@xylex-group/athena 2.1.2 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +306 -117
- package/dist/browser.cjs +2151 -194
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +14 -14
- package/dist/browser.d.ts +14 -14
- package/dist/browser.js +2146 -194
- package/dist/browser.js.map +1 -1
- package/dist/cli/index.cjs +2035 -172
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +3 -3
- package/dist/cli/index.d.ts +3 -3
- package/dist/cli/index.js +2036 -173
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +2166 -190
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +2162 -191
- package/dist/index.js.map +1 -1
- package/dist/{model-form-2hqmoOUX.d.ts → model-form-C0FAbOaf.d.ts} +97 -2
- package/dist/{model-form-Cy-zaO0u.d.cts → model-form-GzTqhEzM.d.cts} +97 -2
- package/dist/{pipeline-BOPszLsL.d.ts → pipeline-CR4V15jF.d.ts} +1 -1
- package/dist/{pipeline-E3FDbs4W.d.cts → pipeline-DZeExYMA.d.cts} +1 -1
- package/dist/{client-BX0NQqOn.d.ts → react-email-BuApZuyG.d.ts} +362 -174
- package/dist/{client-dpAp-NZK.d.cts → react-email-CQJq92zQ.d.cts} +362 -174
- package/dist/react.cjs +279 -21
- 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 +279 -21
- package/dist/react.js.map +1 -1
- package/dist/{types-BaBzjwXr.d.cts → types-09Q4D86N.d.cts} +24 -2
- package/dist/{types-BaBzjwXr.d.ts → types-09Q4D86N.d.ts} +24 -2
- package/dist/{types-CpqL-pZx.d.cts → types-D1JvL21V.d.cts} +1 -1
- package/dist/{types-CeBPrnGj.d.ts → types-DU3gNdFv.d.ts} +1 -1
- 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 +74 -16
package/dist/cli/index.cjs
CHANGED
|
@@ -7,6 +7,11 @@ var url = require('url');
|
|
|
7
7
|
|
|
8
8
|
// src/generator/pipeline.ts
|
|
9
9
|
|
|
10
|
+
// src/utils/slugify.ts
|
|
11
|
+
function slugify(input) {
|
|
12
|
+
return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
// src/generator/naming.ts
|
|
11
16
|
var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
12
17
|
var RESERVED_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
@@ -102,7 +107,7 @@ function applyNamingStyle(input, style) {
|
|
|
102
107
|
case "snake":
|
|
103
108
|
return words.map((word) => word.toLowerCase()).join("_");
|
|
104
109
|
case "kebab":
|
|
105
|
-
return words.
|
|
110
|
+
return slugify(words.join("-"));
|
|
106
111
|
default:
|
|
107
112
|
return input;
|
|
108
113
|
}
|
|
@@ -355,6 +360,19 @@ function isAthenaGatewayError(error) {
|
|
|
355
360
|
return error instanceof AthenaGatewayError;
|
|
356
361
|
}
|
|
357
362
|
|
|
363
|
+
// src/utils/parse-boolean-flag.ts
|
|
364
|
+
function parseBooleanFlag(rawValue, fallback) {
|
|
365
|
+
if (!rawValue) return fallback;
|
|
366
|
+
const normalized = rawValue.trim().toLowerCase();
|
|
367
|
+
if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
return fallback;
|
|
374
|
+
}
|
|
375
|
+
|
|
358
376
|
// src/auxiliaries.ts
|
|
359
377
|
var AthenaErrorCode = {
|
|
360
378
|
UniqueViolation: "UNIQUE_VIOLATION",
|
|
@@ -375,20 +393,41 @@ var AthenaErrorCategory = {
|
|
|
375
393
|
Database: "database",
|
|
376
394
|
Unknown: "unknown"
|
|
377
395
|
};
|
|
378
|
-
function
|
|
379
|
-
|
|
380
|
-
const normalized = rawValue.trim().toLowerCase();
|
|
381
|
-
if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
|
|
382
|
-
return true;
|
|
383
|
-
}
|
|
384
|
-
if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
|
|
385
|
-
return false;
|
|
386
|
-
}
|
|
387
|
-
return fallback;
|
|
396
|
+
function parseBooleanFlag2(rawValue, fallback) {
|
|
397
|
+
return parseBooleanFlag(rawValue, fallback);
|
|
388
398
|
}
|
|
389
399
|
function isRecord(value) {
|
|
390
400
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
391
401
|
}
|
|
402
|
+
function firstNonEmptyString(...values) {
|
|
403
|
+
for (const value of values) {
|
|
404
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
405
|
+
return value.trim();
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return void 0;
|
|
409
|
+
}
|
|
410
|
+
function messageFromUnknownError(error) {
|
|
411
|
+
if (typeof error === "string" && error.trim().length > 0) {
|
|
412
|
+
return error.trim();
|
|
413
|
+
}
|
|
414
|
+
if (error instanceof Error && error.message.trim().length > 0) {
|
|
415
|
+
return error.message.trim();
|
|
416
|
+
}
|
|
417
|
+
if (!isRecord(error)) {
|
|
418
|
+
return void 0;
|
|
419
|
+
}
|
|
420
|
+
return firstNonEmptyString(error.message, error.error, error.details);
|
|
421
|
+
}
|
|
422
|
+
function gatewayCodeFromUnknownError(error) {
|
|
423
|
+
if (!isRecord(error) || typeof error.gatewayCode !== "string") {
|
|
424
|
+
return void 0;
|
|
425
|
+
}
|
|
426
|
+
return error.gatewayCode;
|
|
427
|
+
}
|
|
428
|
+
function isAthenaResultErrorLike(value) {
|
|
429
|
+
return isRecord(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");
|
|
430
|
+
}
|
|
392
431
|
function isAthenaErrorKind(value) {
|
|
393
432
|
return value === "unique_violation" || value === "not_found" || value === "validation" || value === "auth" || value === "rate_limit" || value === "transient" || value === "unknown";
|
|
394
433
|
}
|
|
@@ -460,6 +499,7 @@ function classifyKind(status, code, message) {
|
|
|
460
499
|
if (status === 404 || hasNotFoundPattern) return "not_found";
|
|
461
500
|
if (status === 401 || status === 403 || hasAuthPattern) return "auth";
|
|
462
501
|
if (status === 429 || hasRateLimitPattern) return "rate_limit";
|
|
502
|
+
if (code === "INVALID_URL") return "validation";
|
|
463
503
|
if (status === 400 || status === 422 || hasValidationPattern) return "validation";
|
|
464
504
|
if (code === "NETWORK_ERROR" || status === 0 || status !== void 0 && status >= 500 || hasTransientPattern) {
|
|
465
505
|
return "transient";
|
|
@@ -467,6 +507,9 @@ function classifyKind(status, code, message) {
|
|
|
467
507
|
return "unknown";
|
|
468
508
|
}
|
|
469
509
|
function toAthenaErrorCode(kind, status, gatewayCode) {
|
|
510
|
+
if (gatewayCode === "INVALID_URL") {
|
|
511
|
+
return AthenaErrorCode.ValidationFailed;
|
|
512
|
+
}
|
|
470
513
|
if (gatewayCode === "NETWORK_ERROR" || kind === "transient" && status === 0) {
|
|
471
514
|
return AthenaErrorCode.NetworkUnavailable;
|
|
472
515
|
}
|
|
@@ -510,13 +553,50 @@ function normalizeAthenaError(resultOrError, context) {
|
|
|
510
553
|
return withContextOverrides(attached, context);
|
|
511
554
|
}
|
|
512
555
|
if (isAthenaResultLike(resultOrError)) {
|
|
556
|
+
if (isAthenaResultErrorLike(resultOrError.error)) {
|
|
557
|
+
return {
|
|
558
|
+
kind: resultOrError.error.kind ?? classifyKind(resultOrError.status, gatewayCodeFromUnknownError(resultOrError.error), resultOrError.error.message),
|
|
559
|
+
code: resultOrError.error.athenaCode ?? toAthenaErrorCode(
|
|
560
|
+
resultOrError.error.kind ?? classifyKind(
|
|
561
|
+
resultOrError.status,
|
|
562
|
+
gatewayCodeFromUnknownError(resultOrError.error),
|
|
563
|
+
resultOrError.error.message
|
|
564
|
+
),
|
|
565
|
+
resultOrError.error.status ?? resultOrError.status,
|
|
566
|
+
gatewayCodeFromUnknownError(resultOrError.error)
|
|
567
|
+
),
|
|
568
|
+
category: resultOrError.error.category ?? toAthenaErrorCategory(
|
|
569
|
+
resultOrError.error.kind ?? classifyKind(
|
|
570
|
+
resultOrError.status,
|
|
571
|
+
gatewayCodeFromUnknownError(resultOrError.error),
|
|
572
|
+
resultOrError.error.message
|
|
573
|
+
),
|
|
574
|
+
resultOrError.error.status ?? resultOrError.status
|
|
575
|
+
),
|
|
576
|
+
retryable: resultOrError.error.retryable ?? isRetryable(
|
|
577
|
+
resultOrError.error.kind ?? classifyKind(
|
|
578
|
+
resultOrError.status,
|
|
579
|
+
gatewayCodeFromUnknownError(resultOrError.error),
|
|
580
|
+
resultOrError.error.message
|
|
581
|
+
),
|
|
582
|
+
resultOrError.error.status ?? resultOrError.status
|
|
583
|
+
),
|
|
584
|
+
status: resultOrError.error.status ?? resultOrError.status,
|
|
585
|
+
constraint: resultOrError.error.constraint,
|
|
586
|
+
table: context?.table ?? resultOrError.error.table,
|
|
587
|
+
operation: context?.operation ?? resultOrError.error.operation,
|
|
588
|
+
message: resultOrError.error.message,
|
|
589
|
+
raw: resultOrError.error.raw ?? resultOrError.raw
|
|
590
|
+
};
|
|
591
|
+
}
|
|
513
592
|
const details = resultOrError.errorDetails;
|
|
514
|
-
const message2 = resultOrError.error ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
|
|
593
|
+
const message2 = messageFromUnknownError(resultOrError.error) ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
|
|
515
594
|
const operation = context?.operation ?? operationFromDetails(details);
|
|
516
595
|
const table = context?.table ?? extractTable(message2);
|
|
517
596
|
const constraint = extractConstraint(message2);
|
|
518
|
-
const
|
|
519
|
-
const
|
|
597
|
+
const gatewayCode = details?.code ?? gatewayCodeFromUnknownError(resultOrError.error);
|
|
598
|
+
const kind2 = classifyKind(resultOrError.status, gatewayCode, message2);
|
|
599
|
+
const code = toAthenaErrorCode(kind2, resultOrError.status, gatewayCode);
|
|
520
600
|
const category = toAthenaErrorCategory(kind2, resultOrError.status);
|
|
521
601
|
return {
|
|
522
602
|
kind: kind2,
|
|
@@ -794,7 +874,7 @@ function normalizeBooleanFlag(rawValue, fallback) {
|
|
|
794
874
|
return rawValue;
|
|
795
875
|
}
|
|
796
876
|
if (typeof rawValue === "string") {
|
|
797
|
-
return
|
|
877
|
+
return parseBooleanFlag2(rawValue, fallback);
|
|
798
878
|
}
|
|
799
879
|
return fallback;
|
|
800
880
|
}
|
|
@@ -1283,8 +1363,74 @@ function generateArtifactsFromSnapshot(snapshot, config) {
|
|
|
1283
1363
|
return new ArtifactComposer(snapshot, normalizedConfig).compose();
|
|
1284
1364
|
}
|
|
1285
1365
|
|
|
1366
|
+
// src/gateway/url.ts
|
|
1367
|
+
var ATHENA_DEFAULT_BASE_URL = "https://athena-db.com";
|
|
1368
|
+
function describeReceivedValue(value) {
|
|
1369
|
+
if (value === void 0) return "undefined";
|
|
1370
|
+
if (value === null) return "null";
|
|
1371
|
+
if (typeof value === "string") {
|
|
1372
|
+
return value.trim().length > 0 ? JSON.stringify(value) : "an empty string";
|
|
1373
|
+
}
|
|
1374
|
+
return `${typeof value} ${JSON.stringify(value)}`;
|
|
1375
|
+
}
|
|
1376
|
+
function invalidBaseUrlError(message, hint) {
|
|
1377
|
+
return new AthenaGatewayError({
|
|
1378
|
+
code: "INVALID_URL",
|
|
1379
|
+
message,
|
|
1380
|
+
status: 0,
|
|
1381
|
+
hint
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
function normalizeAthenaGatewayBaseUrl(input, options = {}) {
|
|
1385
|
+
const label = options.label ?? "Athena gateway base URL";
|
|
1386
|
+
const candidate = input ?? options.defaultBaseUrl;
|
|
1387
|
+
if (candidate === void 0 || candidate === null) {
|
|
1388
|
+
throw invalidBaseUrlError(
|
|
1389
|
+
`${label} must be a non-empty absolute http(s) URL. Received ${describeReceivedValue(input)}.`,
|
|
1390
|
+
'Set ATHENA_URL (or pass createClient(url, ...)) to a full URL such as "https://mirror3.athena-db.com".'
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
const trimmed = candidate.trim();
|
|
1394
|
+
if (!trimmed) {
|
|
1395
|
+
throw invalidBaseUrlError(
|
|
1396
|
+
`${label} must be a non-empty absolute http(s) URL. Received ${describeReceivedValue(candidate)}.`,
|
|
1397
|
+
'Set ATHENA_URL (or pass createClient(url, ...)) to a full URL such as "https://mirror3.athena-db.com".'
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
let parsed;
|
|
1401
|
+
try {
|
|
1402
|
+
parsed = new URL(trimmed);
|
|
1403
|
+
} catch {
|
|
1404
|
+
throw invalidBaseUrlError(
|
|
1405
|
+
`${label} must be a valid absolute http(s) URL. Received ${describeReceivedValue(candidate)}.`,
|
|
1406
|
+
'Use a full URL including the protocol, for example "https://mirror3.athena-db.com".'
|
|
1407
|
+
);
|
|
1408
|
+
}
|
|
1409
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
1410
|
+
throw invalidBaseUrlError(
|
|
1411
|
+
`${label} must use http or https. Received ${JSON.stringify(trimmed)}.`,
|
|
1412
|
+
'Use an Athena gateway URL such as "https://mirror3.athena-db.com".'
|
|
1413
|
+
);
|
|
1414
|
+
}
|
|
1415
|
+
if (parsed.search || parsed.hash) {
|
|
1416
|
+
throw invalidBaseUrlError(
|
|
1417
|
+
`${label} must not include query parameters or hash fragments. Received ${JSON.stringify(trimmed)}.`,
|
|
1418
|
+
'Pass only the base URL. Endpoint paths such as "/gateway/fetch" are appended by the SDK.'
|
|
1419
|
+
);
|
|
1420
|
+
}
|
|
1421
|
+
return parsed.toString().replace(/\/+$/, "");
|
|
1422
|
+
}
|
|
1423
|
+
function buildAthenaGatewayUrl(baseUrl, path) {
|
|
1424
|
+
if (!path.startsWith("/")) {
|
|
1425
|
+
throw invalidBaseUrlError(
|
|
1426
|
+
`Athena gateway path must start with "/". Received ${JSON.stringify(path)}.`,
|
|
1427
|
+
'Use a leading slash such as "/gateway/fetch" or "/".'
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
return `${baseUrl}${path}`;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1286
1433
|
// src/gateway/client.ts
|
|
1287
|
-
var DEFAULT_BASE_URL = "https://athena-db.com";
|
|
1288
1434
|
var DEFAULT_CLIENT = "railway_direct";
|
|
1289
1435
|
var FALLBACK_SDK_VERSION = "1.3.0";
|
|
1290
1436
|
var SDK_NAME = "xylex-group/athena";
|
|
@@ -1311,23 +1457,39 @@ function normalizeHeaderValue(value) {
|
|
|
1311
1457
|
function isRecord2(value) {
|
|
1312
1458
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1313
1459
|
}
|
|
1460
|
+
function nonEmptyString(value) {
|
|
1461
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
1462
|
+
}
|
|
1463
|
+
function resolveStructuredErrorPayload(payload) {
|
|
1464
|
+
if (!isRecord2(payload)) return null;
|
|
1465
|
+
return isRecord2(payload.error) ? payload.error : payload;
|
|
1466
|
+
}
|
|
1314
1467
|
function resolveRequestId(headers) {
|
|
1315
1468
|
return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
|
|
1316
1469
|
}
|
|
1317
1470
|
function resolveErrorMessage(payload, fallback) {
|
|
1318
|
-
|
|
1319
|
-
|
|
1471
|
+
const structuredPayload = resolveStructuredErrorPayload(payload);
|
|
1472
|
+
if (structuredPayload) {
|
|
1473
|
+
const messageCandidates = [structuredPayload.message, structuredPayload.error, structuredPayload.details];
|
|
1320
1474
|
for (const candidate of messageCandidates) {
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
}
|
|
1475
|
+
const resolved = nonEmptyString(candidate);
|
|
1476
|
+
if (resolved) return resolved;
|
|
1324
1477
|
}
|
|
1325
1478
|
}
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
}
|
|
1479
|
+
const rawMessage = nonEmptyString(payload);
|
|
1480
|
+
if (rawMessage) return rawMessage;
|
|
1329
1481
|
return fallback;
|
|
1330
1482
|
}
|
|
1483
|
+
function resolveErrorHint(payload) {
|
|
1484
|
+
const structuredPayload = resolveStructuredErrorPayload(payload);
|
|
1485
|
+
return structuredPayload ? nonEmptyString(structuredPayload.hint) : void 0;
|
|
1486
|
+
}
|
|
1487
|
+
function resolveStatusText(response, payload) {
|
|
1488
|
+
const rawStatusText = nonEmptyString(response.statusText);
|
|
1489
|
+
if (rawStatusText) return rawStatusText;
|
|
1490
|
+
const payloadRecord = isRecord2(payload) ? payload : null;
|
|
1491
|
+
return payloadRecord ? nonEmptyString(payloadRecord.statusText) ?? null : null;
|
|
1492
|
+
}
|
|
1331
1493
|
function detailsFromError(error) {
|
|
1332
1494
|
return error.toDetails();
|
|
1333
1495
|
}
|
|
@@ -1465,9 +1627,168 @@ function buildHeaders(config, options) {
|
|
|
1465
1627
|
});
|
|
1466
1628
|
return headers;
|
|
1467
1629
|
}
|
|
1630
|
+
function toInvalidUrlResponse(error, endpoint, method) {
|
|
1631
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1632
|
+
const gatewayError = error instanceof AthenaGatewayError ? error : new AthenaGatewayError({
|
|
1633
|
+
code: "INVALID_URL",
|
|
1634
|
+
message,
|
|
1635
|
+
status: 0,
|
|
1636
|
+
endpoint,
|
|
1637
|
+
method,
|
|
1638
|
+
cause: message,
|
|
1639
|
+
hint: "Set ATHENA_URL to a full http(s) URL before running queries."
|
|
1640
|
+
});
|
|
1641
|
+
return {
|
|
1642
|
+
ok: false,
|
|
1643
|
+
status: 0,
|
|
1644
|
+
statusText: null,
|
|
1645
|
+
data: null,
|
|
1646
|
+
error: gatewayError.message,
|
|
1647
|
+
errorDetails: detailsFromError(
|
|
1648
|
+
new AthenaGatewayError({
|
|
1649
|
+
code: gatewayError.code,
|
|
1650
|
+
message: gatewayError.message,
|
|
1651
|
+
status: gatewayError.status,
|
|
1652
|
+
endpoint,
|
|
1653
|
+
method,
|
|
1654
|
+
requestId: gatewayError.requestId,
|
|
1655
|
+
hint: gatewayError.hint,
|
|
1656
|
+
cause: gatewayError.causeDetail
|
|
1657
|
+
})
|
|
1658
|
+
),
|
|
1659
|
+
raw: null
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1662
|
+
function resolveGatewayBaseUrl(input) {
|
|
1663
|
+
return normalizeAthenaGatewayBaseUrl(input, {
|
|
1664
|
+
defaultBaseUrl: ATHENA_DEFAULT_BASE_URL
|
|
1665
|
+
});
|
|
1666
|
+
}
|
|
1667
|
+
function resolveProbePath(path) {
|
|
1668
|
+
if (!path) return "/";
|
|
1669
|
+
if (!path.startsWith("/")) {
|
|
1670
|
+
throw new AthenaGatewayError({
|
|
1671
|
+
code: "INVALID_URL",
|
|
1672
|
+
message: `Athena gateway probe path must start with "/". Received ${JSON.stringify(path)}.`,
|
|
1673
|
+
status: 0,
|
|
1674
|
+
hint: 'Use a leading slash such as "/" or "/health".'
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1677
|
+
return path;
|
|
1678
|
+
}
|
|
1679
|
+
function mergeConnectionHeaders(baseHeaders, headers) {
|
|
1680
|
+
const merged = {
|
|
1681
|
+
...baseHeaders,
|
|
1682
|
+
...headers ?? {}
|
|
1683
|
+
};
|
|
1684
|
+
if (!merged["X-Athena-Sdk"] && !merged["x-athena-sdk"]) {
|
|
1685
|
+
merged["X-Athena-Sdk"] = SDK_HEADER_VALUE;
|
|
1686
|
+
}
|
|
1687
|
+
return merged;
|
|
1688
|
+
}
|
|
1689
|
+
async function performConnectionCheck(baseUrl, requestHeaders, options) {
|
|
1690
|
+
const path = resolveProbePath(options?.path);
|
|
1691
|
+
const url = buildAthenaGatewayUrl(baseUrl, path);
|
|
1692
|
+
try {
|
|
1693
|
+
const response = await fetch(url, {
|
|
1694
|
+
method: "GET",
|
|
1695
|
+
headers: mergeConnectionHeaders(requestHeaders, options?.headers),
|
|
1696
|
+
signal: options?.signal
|
|
1697
|
+
});
|
|
1698
|
+
const rawText = await response.text();
|
|
1699
|
+
const requestId = resolveRequestId(response.headers);
|
|
1700
|
+
const parsedBody = parseResponseBody(
|
|
1701
|
+
rawText ?? "",
|
|
1702
|
+
response.headers.get("content-type")
|
|
1703
|
+
);
|
|
1704
|
+
if (parsedBody.parseFailed) {
|
|
1705
|
+
const invalidJsonError = new AthenaGatewayError({
|
|
1706
|
+
code: "INVALID_JSON",
|
|
1707
|
+
message: "Gateway probe returned malformed JSON",
|
|
1708
|
+
status: response.status,
|
|
1709
|
+
method: "GET",
|
|
1710
|
+
requestId,
|
|
1711
|
+
hint: "Verify the gateway response body is valid JSON.",
|
|
1712
|
+
cause: rawText.slice(0, 300)
|
|
1713
|
+
});
|
|
1714
|
+
return {
|
|
1715
|
+
ok: false,
|
|
1716
|
+
reachable: true,
|
|
1717
|
+
status: response.status,
|
|
1718
|
+
statusText: resolveStatusText(response, parsedBody.parsed),
|
|
1719
|
+
baseUrl,
|
|
1720
|
+
url,
|
|
1721
|
+
error: invalidJsonError.message,
|
|
1722
|
+
errorDetails: detailsFromError(invalidJsonError),
|
|
1723
|
+
raw: parsedBody.parsed
|
|
1724
|
+
};
|
|
1725
|
+
}
|
|
1726
|
+
const parsed = parsedBody.parsed;
|
|
1727
|
+
if (!response.ok) {
|
|
1728
|
+
const httpError = new AthenaGatewayError({
|
|
1729
|
+
code: "HTTP_ERROR",
|
|
1730
|
+
message: resolveErrorMessage(
|
|
1731
|
+
parsed,
|
|
1732
|
+
`Athena gateway GET ${path} failed with status ${response.status}`
|
|
1733
|
+
),
|
|
1734
|
+
status: response.status,
|
|
1735
|
+
method: "GET",
|
|
1736
|
+
requestId,
|
|
1737
|
+
hint: resolveErrorHint(parsed)
|
|
1738
|
+
});
|
|
1739
|
+
return {
|
|
1740
|
+
ok: false,
|
|
1741
|
+
reachable: true,
|
|
1742
|
+
status: response.status,
|
|
1743
|
+
statusText: resolveStatusText(response, parsed),
|
|
1744
|
+
baseUrl,
|
|
1745
|
+
url,
|
|
1746
|
+
error: httpError.message,
|
|
1747
|
+
errorDetails: detailsFromError(httpError),
|
|
1748
|
+
raw: parsed
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
return {
|
|
1752
|
+
ok: true,
|
|
1753
|
+
reachable: true,
|
|
1754
|
+
status: response.status,
|
|
1755
|
+
statusText: resolveStatusText(response, parsed),
|
|
1756
|
+
baseUrl,
|
|
1757
|
+
url,
|
|
1758
|
+
error: void 0,
|
|
1759
|
+
errorDetails: null,
|
|
1760
|
+
raw: parsed
|
|
1761
|
+
};
|
|
1762
|
+
} catch (callError) {
|
|
1763
|
+
const message = callError instanceof Error ? callError.message : String(callError);
|
|
1764
|
+
const networkError = new AthenaGatewayError({
|
|
1765
|
+
code: "NETWORK_ERROR",
|
|
1766
|
+
message: `Network error while probing Athena gateway ${url}: ${message}`,
|
|
1767
|
+
method: "GET",
|
|
1768
|
+
cause: message,
|
|
1769
|
+
hint: "Check gateway URL, DNS, and network reachability."
|
|
1770
|
+
});
|
|
1771
|
+
return {
|
|
1772
|
+
ok: false,
|
|
1773
|
+
reachable: false,
|
|
1774
|
+
status: 0,
|
|
1775
|
+
statusText: null,
|
|
1776
|
+
baseUrl,
|
|
1777
|
+
url,
|
|
1778
|
+
error: networkError.message,
|
|
1779
|
+
errorDetails: detailsFromError(networkError),
|
|
1780
|
+
raw: null
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1468
1784
|
async function callAthena(config, endpoint, method, payload, options) {
|
|
1469
|
-
|
|
1470
|
-
|
|
1785
|
+
let baseUrl;
|
|
1786
|
+
try {
|
|
1787
|
+
baseUrl = resolveGatewayBaseUrl(options?.baseUrl ?? config.baseUrl);
|
|
1788
|
+
} catch (error) {
|
|
1789
|
+
return toInvalidUrlResponse(error, endpoint, method);
|
|
1790
|
+
}
|
|
1791
|
+
const url = buildAthenaGatewayUrl(baseUrl, endpoint);
|
|
1471
1792
|
const headers = buildHeaders(config, options);
|
|
1472
1793
|
try {
|
|
1473
1794
|
const requestInit = {
|
|
@@ -1498,6 +1819,7 @@ async function callAthena(config, endpoint, method, payload, options) {
|
|
|
1498
1819
|
return {
|
|
1499
1820
|
ok: false,
|
|
1500
1821
|
status: response.status,
|
|
1822
|
+
statusText: resolveStatusText(response, parsedBody.parsed),
|
|
1501
1823
|
data: null,
|
|
1502
1824
|
error: invalidJsonError.message,
|
|
1503
1825
|
errorDetails: detailsFromError(invalidJsonError),
|
|
@@ -1516,11 +1838,13 @@ async function callAthena(config, endpoint, method, payload, options) {
|
|
|
1516
1838
|
status: response.status,
|
|
1517
1839
|
endpoint,
|
|
1518
1840
|
method,
|
|
1519
|
-
requestId
|
|
1841
|
+
requestId,
|
|
1842
|
+
hint: resolveErrorHint(parsed)
|
|
1520
1843
|
});
|
|
1521
1844
|
return {
|
|
1522
1845
|
ok: false,
|
|
1523
1846
|
status: response.status,
|
|
1847
|
+
statusText: resolveStatusText(response, parsed),
|
|
1524
1848
|
data: null,
|
|
1525
1849
|
error: httpError.message,
|
|
1526
1850
|
errorDetails: detailsFromError(httpError),
|
|
@@ -1532,6 +1856,7 @@ async function callAthena(config, endpoint, method, payload, options) {
|
|
|
1532
1856
|
return {
|
|
1533
1857
|
ok: true,
|
|
1534
1858
|
status: response.status,
|
|
1859
|
+
statusText: resolveStatusText(response, parsed),
|
|
1535
1860
|
data: payloadData ?? null,
|
|
1536
1861
|
count: payloadCount,
|
|
1537
1862
|
error: void 0,
|
|
@@ -1551,6 +1876,7 @@ async function callAthena(config, endpoint, method, payload, options) {
|
|
|
1551
1876
|
return {
|
|
1552
1877
|
ok: false,
|
|
1553
1878
|
status: 0,
|
|
1879
|
+
statusText: null,
|
|
1554
1880
|
data: null,
|
|
1555
1881
|
error: networkError.message,
|
|
1556
1882
|
errorDetails: detailsFromError(networkError),
|
|
@@ -1559,32 +1885,44 @@ async function callAthena(config, endpoint, method, payload, options) {
|
|
|
1559
1885
|
}
|
|
1560
1886
|
}
|
|
1561
1887
|
function createAthenaGatewayClient(config = {}) {
|
|
1888
|
+
const normalizedBaseUrl = resolveGatewayBaseUrl(config.baseUrl);
|
|
1889
|
+
const normalizedConfig = {
|
|
1890
|
+
...config,
|
|
1891
|
+
baseUrl: normalizedBaseUrl
|
|
1892
|
+
};
|
|
1562
1893
|
return {
|
|
1563
|
-
baseUrl:
|
|
1894
|
+
baseUrl: normalizedBaseUrl,
|
|
1564
1895
|
buildHeaders(options) {
|
|
1565
|
-
return buildHeaders(
|
|
1896
|
+
return buildHeaders(normalizedConfig, options);
|
|
1897
|
+
},
|
|
1898
|
+
verifyConnection(options) {
|
|
1899
|
+
return performConnectionCheck(
|
|
1900
|
+
normalizedBaseUrl,
|
|
1901
|
+
buildHeaders(normalizedConfig),
|
|
1902
|
+
options
|
|
1903
|
+
);
|
|
1566
1904
|
},
|
|
1567
1905
|
fetchGateway(payload, options) {
|
|
1568
|
-
return callAthena(
|
|
1906
|
+
return callAthena(normalizedConfig, "/gateway/fetch", "POST", payload, options);
|
|
1569
1907
|
},
|
|
1570
1908
|
insertGateway(payload, options) {
|
|
1571
|
-
return callAthena(
|
|
1909
|
+
return callAthena(normalizedConfig, "/gateway/insert", "PUT", payload, options);
|
|
1572
1910
|
},
|
|
1573
1911
|
updateGateway(payload, options) {
|
|
1574
|
-
return callAthena(
|
|
1912
|
+
return callAthena(normalizedConfig, "/gateway/update", "POST", payload, options);
|
|
1575
1913
|
},
|
|
1576
1914
|
deleteGateway(payload, options) {
|
|
1577
|
-
return callAthena(
|
|
1915
|
+
return callAthena(normalizedConfig, "/gateway/delete", "DELETE", payload, options);
|
|
1578
1916
|
},
|
|
1579
1917
|
rpcGateway(payload, options) {
|
|
1580
1918
|
if (options?.get) {
|
|
1581
1919
|
const endpoint = buildRpcGetEndpoint(payload);
|
|
1582
|
-
return callAthena(
|
|
1920
|
+
return callAthena(normalizedConfig, endpoint, "GET", null, options);
|
|
1583
1921
|
}
|
|
1584
|
-
return callAthena(
|
|
1922
|
+
return callAthena(normalizedConfig, "/gateway/rpc", "POST", payload, options);
|
|
1585
1923
|
},
|
|
1586
1924
|
queryGateway(payload, options) {
|
|
1587
|
-
return callAthena(
|
|
1925
|
+
return callAthena(normalizedConfig, "/gateway/query", "POST", payload, options);
|
|
1588
1926
|
}
|
|
1589
1927
|
};
|
|
1590
1928
|
}
|
|
@@ -1592,10 +1930,29 @@ function createAthenaGatewayClient(config = {}) {
|
|
|
1592
1930
|
// src/sql-identifiers.ts
|
|
1593
1931
|
var SIMPLE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1594
1932
|
var COMPOSITE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
1595
|
-
var
|
|
1933
|
+
var SQL_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
|
|
1934
|
+
var RESPONSE_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([A-Za-z_][A-Za-z0-9_.]*)$/i;
|
|
1596
1935
|
function quoteIdentifierSegment(identifier) {
|
|
1597
1936
|
return `"${identifier.replace(/"/g, '""')}"`;
|
|
1598
1937
|
}
|
|
1938
|
+
function parseAliasedIdentifierToken(token) {
|
|
1939
|
+
const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(token);
|
|
1940
|
+
if (responseAliasMatch) {
|
|
1941
|
+
const [, aliasIdentifier2, baseIdentifier2] = responseAliasMatch;
|
|
1942
|
+
if (COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier2) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier2)) {
|
|
1943
|
+
return { baseIdentifier: baseIdentifier2, aliasIdentifier: aliasIdentifier2 };
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
const sqlAliasMatch = SQL_ALIAS_PATTERN.exec(token);
|
|
1947
|
+
if (!sqlAliasMatch) {
|
|
1948
|
+
return null;
|
|
1949
|
+
}
|
|
1950
|
+
const [, baseIdentifier, aliasIdentifier] = sqlAliasMatch;
|
|
1951
|
+
if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
|
|
1952
|
+
return null;
|
|
1953
|
+
}
|
|
1954
|
+
return { baseIdentifier, aliasIdentifier };
|
|
1955
|
+
}
|
|
1599
1956
|
function quoteQualifiedIdentifier(identifier) {
|
|
1600
1957
|
return identifier.split(".").map((segment) => quoteIdentifierSegment(segment)).join(".");
|
|
1601
1958
|
}
|
|
@@ -1604,23 +1961,27 @@ function quoteSelectToken(token) {
|
|
|
1604
1961
|
if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) {
|
|
1605
1962
|
return quoteQualifiedIdentifier(token);
|
|
1606
1963
|
}
|
|
1607
|
-
const
|
|
1608
|
-
if (!
|
|
1609
|
-
return token;
|
|
1610
|
-
}
|
|
1611
|
-
const [, baseIdentifier, aliasIdentifier] = aliasMatch;
|
|
1612
|
-
if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
|
|
1964
|
+
const aliasedIdentifier = parseAliasedIdentifierToken(token);
|
|
1965
|
+
if (!aliasedIdentifier) {
|
|
1613
1966
|
return token;
|
|
1614
1967
|
}
|
|
1968
|
+
const { baseIdentifier, aliasIdentifier } = aliasedIdentifier;
|
|
1615
1969
|
return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
|
|
1616
1970
|
}
|
|
1971
|
+
function quoteSelectColumnToken(token) {
|
|
1972
|
+
const trimmed = token.trim();
|
|
1973
|
+
if (!trimmed || trimmed === "*") return trimmed || "*";
|
|
1974
|
+
const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(trimmed);
|
|
1975
|
+
if (responseAliasMatch) {
|
|
1976
|
+
const [, aliasIdentifier, baseIdentifier] = responseAliasMatch;
|
|
1977
|
+
return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
|
|
1978
|
+
}
|
|
1979
|
+
return quoteQualifiedIdentifier(trimmed);
|
|
1980
|
+
}
|
|
1617
1981
|
function canAutoQuoteToken(token) {
|
|
1618
1982
|
if (token === "*") return true;
|
|
1619
1983
|
if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) return true;
|
|
1620
|
-
|
|
1621
|
-
if (!aliasMatch) return false;
|
|
1622
|
-
const [, baseIdentifier, aliasIdentifier] = aliasMatch;
|
|
1623
|
-
return COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier);
|
|
1984
|
+
return parseAliasedIdentifierToken(token) != null;
|
|
1624
1985
|
}
|
|
1625
1986
|
function splitTopLevelCommaSeparated(input) {
|
|
1626
1987
|
const parts = [];
|
|
@@ -1704,6 +2065,190 @@ function quoteSelectColumnsExpression(columns) {
|
|
|
1704
2065
|
return tokens.map(quoteSelectToken).join(", ");
|
|
1705
2066
|
}
|
|
1706
2067
|
|
|
2068
|
+
// src/auth/react-email.ts
|
|
2069
|
+
var reactEmailRenderModulePromise;
|
|
2070
|
+
function isRecord3(value) {
|
|
2071
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2072
|
+
}
|
|
2073
|
+
function isFunction(value) {
|
|
2074
|
+
return typeof value === "function";
|
|
2075
|
+
}
|
|
2076
|
+
function toStringOrUndefined(value) {
|
|
2077
|
+
if (typeof value !== "string") return void 0;
|
|
2078
|
+
return value;
|
|
2079
|
+
}
|
|
2080
|
+
function nowIsoString() {
|
|
2081
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
2082
|
+
}
|
|
2083
|
+
function emitReactEmailEvent(observe, phase, input = {}) {
|
|
2084
|
+
if (!observe) return;
|
|
2085
|
+
try {
|
|
2086
|
+
observe({
|
|
2087
|
+
phase,
|
|
2088
|
+
timestamp: nowIsoString(),
|
|
2089
|
+
...input
|
|
2090
|
+
});
|
|
2091
|
+
} catch {
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
function mergeRenderDefaults(input, defaults) {
|
|
2095
|
+
return {
|
|
2096
|
+
...input,
|
|
2097
|
+
pretty: input.pretty ?? defaults?.pretty,
|
|
2098
|
+
includePlainText: input.includePlainText ?? defaults?.includePlainText
|
|
2099
|
+
};
|
|
2100
|
+
}
|
|
2101
|
+
function mergeRuntimeOptions(options) {
|
|
2102
|
+
if (!options) return void 0;
|
|
2103
|
+
return {
|
|
2104
|
+
defaults: options.defaults,
|
|
2105
|
+
observe: options.observe,
|
|
2106
|
+
route: "route" in options ? options.route : void 0
|
|
2107
|
+
};
|
|
2108
|
+
}
|
|
2109
|
+
async function resolveReactEmailRenderModule() {
|
|
2110
|
+
if (!reactEmailRenderModulePromise) {
|
|
2111
|
+
reactEmailRenderModulePromise = (async () => {
|
|
2112
|
+
try {
|
|
2113
|
+
const loaded = await import('@react-email/render');
|
|
2114
|
+
if (!isFunction(loaded.render)) {
|
|
2115
|
+
throw new Error("missing render(...) export");
|
|
2116
|
+
}
|
|
2117
|
+
return {
|
|
2118
|
+
render: loaded.render,
|
|
2119
|
+
toPlainText: isFunction(loaded.toPlainText) ? loaded.toPlainText : void 0,
|
|
2120
|
+
pretty: isFunction(loaded.pretty) ? loaded.pretty : void 0
|
|
2121
|
+
};
|
|
2122
|
+
} catch (error) {
|
|
2123
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2124
|
+
throw new Error(
|
|
2125
|
+
`React Email rendering requires @react-email/render. Install it in your project (for example: pnpm add @react-email/render). Loader error: ${message}`
|
|
2126
|
+
);
|
|
2127
|
+
}
|
|
2128
|
+
})();
|
|
2129
|
+
}
|
|
2130
|
+
if (!reactEmailRenderModulePromise) {
|
|
2131
|
+
throw new Error("React Email renderer module failed to initialize");
|
|
2132
|
+
}
|
|
2133
|
+
return reactEmailRenderModulePromise;
|
|
2134
|
+
}
|
|
2135
|
+
async function resolveReactEmailElement(input) {
|
|
2136
|
+
if (input.element != null) {
|
|
2137
|
+
return input.element;
|
|
2138
|
+
}
|
|
2139
|
+
if (!input.component) {
|
|
2140
|
+
throw new Error("react email payload requires either `element` or `component`");
|
|
2141
|
+
}
|
|
2142
|
+
try {
|
|
2143
|
+
const reactModule = await import('react');
|
|
2144
|
+
if (typeof reactModule.createElement !== "function") {
|
|
2145
|
+
throw new Error("react createElement(...) export is unavailable");
|
|
2146
|
+
}
|
|
2147
|
+
return reactModule.createElement(
|
|
2148
|
+
input.component,
|
|
2149
|
+
input.props ?? {}
|
|
2150
|
+
);
|
|
2151
|
+
} catch (error) {
|
|
2152
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2153
|
+
throw new Error(
|
|
2154
|
+
`React Email component rendering requires react runtime support. Install react in your project. Loader error: ${message}`
|
|
2155
|
+
);
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
async function renderAthenaReactEmail(input, options) {
|
|
2159
|
+
if (!isRecord3(input)) {
|
|
2160
|
+
throw new Error("react email payload must be an object");
|
|
2161
|
+
}
|
|
2162
|
+
const runtimeOptions = mergeRuntimeOptions(options);
|
|
2163
|
+
const start = Date.now();
|
|
2164
|
+
emitReactEmailEvent(runtimeOptions?.observe, "render:start", {
|
|
2165
|
+
route: runtimeOptions?.route,
|
|
2166
|
+
message: "Rendering react email payload"
|
|
2167
|
+
});
|
|
2168
|
+
try {
|
|
2169
|
+
const normalizedInput = mergeRenderDefaults(input, runtimeOptions?.defaults);
|
|
2170
|
+
const element = await resolveReactEmailElement(normalizedInput);
|
|
2171
|
+
const renderModule = await resolveReactEmailRenderModule();
|
|
2172
|
+
const htmlValue = await renderModule.render(element);
|
|
2173
|
+
const renderedHtml = typeof htmlValue === "string" ? htmlValue : String(htmlValue ?? "");
|
|
2174
|
+
if (!renderedHtml.trim()) {
|
|
2175
|
+
throw new Error("react email renderer returned an empty HTML string");
|
|
2176
|
+
}
|
|
2177
|
+
let html = renderedHtml;
|
|
2178
|
+
if (normalizedInput.pretty && renderModule.pretty) {
|
|
2179
|
+
const prettyValue = await renderModule.pretty(renderedHtml);
|
|
2180
|
+
if (typeof prettyValue === "string" && prettyValue.trim().length > 0) {
|
|
2181
|
+
html = prettyValue;
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
const explicitText = toStringOrUndefined(normalizedInput.text);
|
|
2185
|
+
if (explicitText !== void 0) {
|
|
2186
|
+
emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
|
|
2187
|
+
route: runtimeOptions?.route,
|
|
2188
|
+
durationMs: Date.now() - start,
|
|
2189
|
+
message: "Rendered react email with explicit text"
|
|
2190
|
+
});
|
|
2191
|
+
return {
|
|
2192
|
+
html,
|
|
2193
|
+
text: explicitText
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
if (normalizedInput.includePlainText === false || !renderModule.toPlainText) {
|
|
2197
|
+
emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
|
|
2198
|
+
route: runtimeOptions?.route,
|
|
2199
|
+
durationMs: Date.now() - start,
|
|
2200
|
+
message: "Rendered react email without plain-text derivation"
|
|
2201
|
+
});
|
|
2202
|
+
return { html };
|
|
2203
|
+
}
|
|
2204
|
+
const plainTextValue = await renderModule.toPlainText(html);
|
|
2205
|
+
const plainText = toStringOrUndefined(plainTextValue);
|
|
2206
|
+
emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
|
|
2207
|
+
route: runtimeOptions?.route,
|
|
2208
|
+
durationMs: Date.now() - start,
|
|
2209
|
+
message: plainText ? "Rendered react email with derived plain text" : "Rendered react email HTML only"
|
|
2210
|
+
});
|
|
2211
|
+
if (plainText === void 0) {
|
|
2212
|
+
return { html };
|
|
2213
|
+
}
|
|
2214
|
+
return {
|
|
2215
|
+
html,
|
|
2216
|
+
text: plainText
|
|
2217
|
+
};
|
|
2218
|
+
} catch (error) {
|
|
2219
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2220
|
+
emitReactEmailEvent(runtimeOptions?.observe, "render:error", {
|
|
2221
|
+
route: runtimeOptions?.route,
|
|
2222
|
+
durationMs: Date.now() - start,
|
|
2223
|
+
error: message,
|
|
2224
|
+
message: "Failed to render react email payload"
|
|
2225
|
+
});
|
|
2226
|
+
throw error;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
async function resolveReactEmailPayloadFields(input, fields, options) {
|
|
2230
|
+
const { react, ...payloadWithoutReact } = input;
|
|
2231
|
+
if (!react) {
|
|
2232
|
+
return payloadWithoutReact;
|
|
2233
|
+
}
|
|
2234
|
+
const rendered = await renderAthenaReactEmail(react, options);
|
|
2235
|
+
const payload = {
|
|
2236
|
+
...payloadWithoutReact
|
|
2237
|
+
};
|
|
2238
|
+
payload[fields.htmlField] = rendered.html;
|
|
2239
|
+
const currentTextValue = payload[fields.textField];
|
|
2240
|
+
if (rendered.text !== void 0 && (currentTextValue === void 0 || currentTextValue === null || currentTextValue === "")) {
|
|
2241
|
+
payload[fields.textField] = rendered.text;
|
|
2242
|
+
}
|
|
2243
|
+
if (fields.variablesField && (payload[fields.variablesField] === void 0 || payload[fields.variablesField] === null) && isRecord3(react.props)) {
|
|
2244
|
+
const derivedVariables = Object.keys(react.props);
|
|
2245
|
+
if (derivedVariables.length > 0) {
|
|
2246
|
+
payload[fields.variablesField] = derivedVariables;
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
return payload;
|
|
2250
|
+
}
|
|
2251
|
+
|
|
1707
2252
|
// src/auth/client.ts
|
|
1708
2253
|
var DEFAULT_AUTH_BASE_URL = "http://localhost:3001/api/auth";
|
|
1709
2254
|
var FALLBACK_SDK_VERSION2 = "1.0.0";
|
|
@@ -1713,7 +2258,7 @@ var SDK_HEADER_VALUE2 = `${SDK_NAME2} ${SDK_VERSION2}`;
|
|
|
1713
2258
|
function normalizeBaseUrl(baseUrl) {
|
|
1714
2259
|
return (baseUrl ?? DEFAULT_AUTH_BASE_URL).replace(/\/$/, "");
|
|
1715
2260
|
}
|
|
1716
|
-
function
|
|
2261
|
+
function isRecord4(value) {
|
|
1717
2262
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1718
2263
|
}
|
|
1719
2264
|
function normalizeHeaderValue2(value) {
|
|
@@ -1738,7 +2283,7 @@ function resolveRequestId2(headers) {
|
|
|
1738
2283
|
return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
|
|
1739
2284
|
}
|
|
1740
2285
|
function resolveErrorMessage2(payload, fallback) {
|
|
1741
|
-
if (
|
|
2286
|
+
if (isRecord4(payload)) {
|
|
1742
2287
|
const messageCandidates = [payload.error, payload.message, payload.details];
|
|
1743
2288
|
for (const candidate of messageCandidates) {
|
|
1744
2289
|
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
@@ -2064,6 +2609,19 @@ function createAuthClient(config = {}) {
|
|
|
2064
2609
|
options
|
|
2065
2610
|
);
|
|
2066
2611
|
};
|
|
2612
|
+
const withReactEmailRoute = (route) => ({
|
|
2613
|
+
...resolvedConfig.reactEmail,
|
|
2614
|
+
route
|
|
2615
|
+
});
|
|
2616
|
+
const resolveAdminEmailPayload = (route, input) => resolveReactEmailPayloadFields(input, {
|
|
2617
|
+
htmlField: "htmlBody",
|
|
2618
|
+
textField: "textBody"
|
|
2619
|
+
}, withReactEmailRoute(route));
|
|
2620
|
+
const resolveAdminEmailTemplatePayload = (route, input) => resolveReactEmailPayloadFields(input, {
|
|
2621
|
+
htmlField: "htmlTemplate",
|
|
2622
|
+
textField: "textTemplate",
|
|
2623
|
+
variablesField: "variables"
|
|
2624
|
+
}, withReactEmailRoute(route));
|
|
2067
2625
|
const listUserEmailsWithFallback = async (input, options) => {
|
|
2068
2626
|
const primary = await getWithQuery(
|
|
2069
2627
|
"/email/list",
|
|
@@ -2091,7 +2649,7 @@ function createAuthClient(config = {}) {
|
|
|
2091
2649
|
data: null
|
|
2092
2650
|
};
|
|
2093
2651
|
}
|
|
2094
|
-
const fallbackStatus =
|
|
2652
|
+
const fallbackStatus = isRecord4(fallback.data) && typeof fallback.data.ok === "boolean" ? fallback.data.ok ? "ok" : "error" : "ok";
|
|
2095
2653
|
return {
|
|
2096
2654
|
...fallback,
|
|
2097
2655
|
data: {
|
|
@@ -2527,9 +3085,17 @@ function createAuthClient(config = {}) {
|
|
|
2527
3085
|
email: {
|
|
2528
3086
|
list: (input, options) => getWithQuery("/admin/email/list", input, options),
|
|
2529
3087
|
get: (input, options) => getWithQuery("/admin/email/get", input, options),
|
|
2530
|
-
create: (input, options) => postGeneric(
|
|
2531
|
-
|
|
2532
|
-
|
|
3088
|
+
create: async (input, options) => postGeneric(
|
|
3089
|
+
"/admin/email/create",
|
|
3090
|
+
await resolveAdminEmailPayload("/admin/email/create", input),
|
|
3091
|
+
options
|
|
3092
|
+
),
|
|
3093
|
+
update: async (input, options) => postGeneric(
|
|
3094
|
+
"/admin/email/update",
|
|
3095
|
+
await resolveAdminEmailPayload("/admin/email/update", input),
|
|
3096
|
+
options
|
|
3097
|
+
),
|
|
3098
|
+
delete: (input, options) => postGeneric("/admin/email/delete", input, options),
|
|
2533
3099
|
failure: {
|
|
2534
3100
|
list: (input, options) => getWithQuery("/admin/email-failure/list", input, options),
|
|
2535
3101
|
get: (input, options) => getWithQuery("/admin/email-failure/get", input, options),
|
|
@@ -2540,17 +3106,33 @@ function createAuthClient(config = {}) {
|
|
|
2540
3106
|
template: {
|
|
2541
3107
|
list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
|
|
2542
3108
|
get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
|
|
2543
|
-
create: (input, options) => postGeneric(
|
|
2544
|
-
|
|
3109
|
+
create: async (input, options) => postGeneric(
|
|
3110
|
+
"/admin/email-template/create",
|
|
3111
|
+
await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
|
|
3112
|
+
options
|
|
3113
|
+
),
|
|
3114
|
+
update: async (input, options) => postGeneric(
|
|
3115
|
+
"/admin/email-template/update",
|
|
3116
|
+
await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
|
|
3117
|
+
options
|
|
3118
|
+
),
|
|
2545
3119
|
delete: (input, options) => postGeneric("/admin/email-template/delete", input, options)
|
|
2546
3120
|
}
|
|
2547
3121
|
},
|
|
2548
3122
|
emailTemplate: {
|
|
2549
3123
|
get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
|
|
2550
|
-
create: (input, options) => postGeneric(
|
|
3124
|
+
create: async (input, options) => postGeneric(
|
|
3125
|
+
"/admin/email-template/create",
|
|
3126
|
+
await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
|
|
3127
|
+
options
|
|
3128
|
+
),
|
|
2551
3129
|
delete: (input, options) => postGeneric("/admin/email-template/delete", input, options),
|
|
2552
3130
|
list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
|
|
2553
|
-
update: (input, options) => postGeneric(
|
|
3131
|
+
update: async (input, options) => postGeneric(
|
|
3132
|
+
"/admin/email-template/update",
|
|
3133
|
+
await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
|
|
3134
|
+
options
|
|
3135
|
+
)
|
|
2554
3136
|
}
|
|
2555
3137
|
},
|
|
2556
3138
|
apiKey: {
|
|
@@ -2745,8 +3327,8 @@ function createAuthClient(config = {}) {
|
|
|
2745
3327
|
// src/db/module.ts
|
|
2746
3328
|
function createDbModule(input) {
|
|
2747
3329
|
const db = {
|
|
2748
|
-
from(table) {
|
|
2749
|
-
return input.from(table);
|
|
3330
|
+
from(table, options) {
|
|
3331
|
+
return input.from(table, options);
|
|
2750
3332
|
},
|
|
2751
3333
|
select(table, columns, options) {
|
|
2752
3334
|
return input.from(table).select(columns, options);
|
|
@@ -2773,17 +3355,551 @@ function createDbModule(input) {
|
|
|
2773
3355
|
return db;
|
|
2774
3356
|
}
|
|
2775
3357
|
|
|
3358
|
+
// src/query-ast.ts
|
|
3359
|
+
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;
|
|
3360
|
+
var FILTER_OPERATORS = /* @__PURE__ */ new Set([
|
|
3361
|
+
"eq",
|
|
3362
|
+
"neq",
|
|
3363
|
+
"gt",
|
|
3364
|
+
"gte",
|
|
3365
|
+
"lt",
|
|
3366
|
+
"lte",
|
|
3367
|
+
"like",
|
|
3368
|
+
"ilike",
|
|
3369
|
+
"is",
|
|
3370
|
+
"in",
|
|
3371
|
+
"contains",
|
|
3372
|
+
"containedBy"
|
|
3373
|
+
]);
|
|
3374
|
+
var BOOLEAN_SAFE_OPERATORS = /* @__PURE__ */ new Set([
|
|
3375
|
+
"eq",
|
|
3376
|
+
"neq",
|
|
3377
|
+
"gt",
|
|
3378
|
+
"gte",
|
|
3379
|
+
"lt",
|
|
3380
|
+
"lte",
|
|
3381
|
+
"like",
|
|
3382
|
+
"ilike",
|
|
3383
|
+
"is"
|
|
3384
|
+
]);
|
|
3385
|
+
function isRecord5(value) {
|
|
3386
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3387
|
+
}
|
|
3388
|
+
function isUuidString(value) {
|
|
3389
|
+
return UUID_PATTERN.test(value.trim());
|
|
3390
|
+
}
|
|
3391
|
+
function isUuidIdentifierColumn(column) {
|
|
3392
|
+
return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
|
|
3393
|
+
}
|
|
3394
|
+
function shouldUseUuidTextComparison(column, value) {
|
|
3395
|
+
return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
|
|
3396
|
+
}
|
|
3397
|
+
function isRelationSelectNode(value) {
|
|
3398
|
+
return isRecord5(value) && isRecord5(value.select);
|
|
3399
|
+
}
|
|
3400
|
+
function normalizeIdentifier(value, label) {
|
|
3401
|
+
const normalized = value.trim();
|
|
3402
|
+
if (!normalized) {
|
|
3403
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
3404
|
+
}
|
|
3405
|
+
return normalized;
|
|
3406
|
+
}
|
|
3407
|
+
function stringifyFilterValue(value) {
|
|
3408
|
+
if (Array.isArray(value)) {
|
|
3409
|
+
return value.join(",");
|
|
3410
|
+
}
|
|
3411
|
+
return String(value);
|
|
3412
|
+
}
|
|
3413
|
+
function buildGatewayCondition(operator, column, value) {
|
|
3414
|
+
const condition = { operator };
|
|
3415
|
+
if (column) {
|
|
3416
|
+
condition.column = column;
|
|
3417
|
+
if (operator === "eq") {
|
|
3418
|
+
condition.eq_column = column;
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
if (value !== void 0) {
|
|
3422
|
+
condition.value = value;
|
|
3423
|
+
if (operator === "eq") {
|
|
3424
|
+
condition.eq_value = value;
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
if (operator === "eq" && column && value !== void 0 && shouldUseUuidTextComparison(column, value)) {
|
|
3428
|
+
condition.column_cast = "text";
|
|
3429
|
+
condition.eq_column_cast = "text";
|
|
3430
|
+
}
|
|
3431
|
+
return condition;
|
|
3432
|
+
}
|
|
3433
|
+
function compileRelationToken(key, node) {
|
|
3434
|
+
const nested = compileSelectShape(node.select);
|
|
3435
|
+
const propertyKey = normalizeIdentifier(key, "select relation key");
|
|
3436
|
+
if (node.schema && node.via) {
|
|
3437
|
+
throw new Error(
|
|
3438
|
+
`findMany relation "${propertyKey}" cannot combine schema and via yet; use schema with the relation key, or use via without schema`
|
|
3439
|
+
);
|
|
3440
|
+
}
|
|
3441
|
+
const relationTokenBase = normalizeIdentifier(node.via ?? propertyKey, "select relation token");
|
|
3442
|
+
if (node.schema && relationTokenBase.includes(".")) {
|
|
3443
|
+
throw new Error(
|
|
3444
|
+
`findMany relation "${propertyKey}" already resolves to a qualified relation token; do not also set schema`
|
|
3445
|
+
);
|
|
3446
|
+
}
|
|
3447
|
+
const relationSchema = node.schema?.trim();
|
|
3448
|
+
const relationToken = relationSchema ? `${normalizeIdentifier(relationSchema, "select relation schema")}.${relationTokenBase}` : relationTokenBase;
|
|
3449
|
+
const alias = node.as?.trim() || (relationToken !== propertyKey ? propertyKey : "");
|
|
3450
|
+
const prefix = alias ? `${alias}:` : "";
|
|
3451
|
+
return `${prefix}${relationToken}(${nested})`;
|
|
3452
|
+
}
|
|
3453
|
+
function compileSelectShape(select) {
|
|
3454
|
+
if (!isRecord5(select)) {
|
|
3455
|
+
throw new Error("findMany select must be an object");
|
|
3456
|
+
}
|
|
3457
|
+
const tokens = [];
|
|
3458
|
+
for (const [rawKey, rawValue] of Object.entries(select)) {
|
|
3459
|
+
if (rawValue === void 0) {
|
|
3460
|
+
continue;
|
|
3461
|
+
}
|
|
3462
|
+
if (rawValue === true) {
|
|
3463
|
+
tokens.push(normalizeIdentifier(rawKey, "select column"));
|
|
3464
|
+
continue;
|
|
3465
|
+
}
|
|
3466
|
+
if (isRelationSelectNode(rawValue)) {
|
|
3467
|
+
tokens.push(compileRelationToken(rawKey, rawValue));
|
|
3468
|
+
continue;
|
|
3469
|
+
}
|
|
3470
|
+
throw new Error(`Unsupported select node for "${rawKey}"`);
|
|
3471
|
+
}
|
|
3472
|
+
if (tokens.length === 0) {
|
|
3473
|
+
throw new Error("findMany select requires at least one field");
|
|
3474
|
+
}
|
|
3475
|
+
return tokens.join(",");
|
|
3476
|
+
}
|
|
3477
|
+
function selectShapeUsesRelationSchema(select) {
|
|
3478
|
+
if (!isRecord5(select)) {
|
|
3479
|
+
return false;
|
|
3480
|
+
}
|
|
3481
|
+
for (const rawValue of Object.values(select)) {
|
|
3482
|
+
if (!isRelationSelectNode(rawValue)) {
|
|
3483
|
+
continue;
|
|
3484
|
+
}
|
|
3485
|
+
if (typeof rawValue.schema === "string" && rawValue.schema.trim().length > 0) {
|
|
3486
|
+
return true;
|
|
3487
|
+
}
|
|
3488
|
+
if (selectShapeUsesRelationSchema(rawValue.select)) {
|
|
3489
|
+
return true;
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
return false;
|
|
3493
|
+
}
|
|
3494
|
+
function compileColumnWhere(column, input) {
|
|
3495
|
+
const normalizedColumn = normalizeIdentifier(column, "where column");
|
|
3496
|
+
if (!isRecord5(input)) {
|
|
3497
|
+
return [buildGatewayCondition("eq", normalizedColumn, input)];
|
|
3498
|
+
}
|
|
3499
|
+
const conditions = [];
|
|
3500
|
+
for (const [rawOperator, rawValue] of Object.entries(input)) {
|
|
3501
|
+
if (rawValue === void 0) {
|
|
3502
|
+
continue;
|
|
3503
|
+
}
|
|
3504
|
+
if (!FILTER_OPERATORS.has(rawOperator)) {
|
|
3505
|
+
throw new Error(`Unsupported where operator "${rawOperator}" on "${normalizedColumn}"`);
|
|
3506
|
+
}
|
|
3507
|
+
if ((rawOperator === "in" || rawOperator === "contains" || rawOperator === "containedBy") && !Array.isArray(rawValue)) {
|
|
3508
|
+
throw new Error(`where.${normalizedColumn}.${rawOperator} requires an array value`);
|
|
3509
|
+
}
|
|
3510
|
+
conditions.push(
|
|
3511
|
+
buildGatewayCondition(
|
|
3512
|
+
rawOperator,
|
|
3513
|
+
normalizedColumn,
|
|
3514
|
+
rawValue
|
|
3515
|
+
)
|
|
3516
|
+
);
|
|
3517
|
+
}
|
|
3518
|
+
if (conditions.length === 0) {
|
|
3519
|
+
throw new Error(`where.${normalizedColumn} requires at least one operator`);
|
|
3520
|
+
}
|
|
3521
|
+
return conditions;
|
|
3522
|
+
}
|
|
3523
|
+
function compileBooleanExpressionTerms(clause, label) {
|
|
3524
|
+
if (!isRecord5(clause)) {
|
|
3525
|
+
throw new Error(`findMany where.${label} clauses must be objects`);
|
|
3526
|
+
}
|
|
3527
|
+
const entries = Object.entries(clause).filter(([, value]) => value !== void 0);
|
|
3528
|
+
if (entries.length !== 1) {
|
|
3529
|
+
throw new Error(`findMany where.${label} clauses must target exactly one column`);
|
|
3530
|
+
}
|
|
3531
|
+
const [rawColumn, rawValue] = entries[0];
|
|
3532
|
+
const column = normalizeIdentifier(rawColumn, `where.${label} column`);
|
|
3533
|
+
if (!isRecord5(rawValue)) {
|
|
3534
|
+
return [`${column}.eq.${stringifyFilterValue(rawValue)}`];
|
|
3535
|
+
}
|
|
3536
|
+
const operatorEntries = Object.entries(rawValue).filter(([, value]) => value !== void 0);
|
|
3537
|
+
if (operatorEntries.length === 0) {
|
|
3538
|
+
throw new Error(`findMany where.${label}.${column} requires at least one operator`);
|
|
3539
|
+
}
|
|
3540
|
+
if (label === "not" && operatorEntries.length > 1) {
|
|
3541
|
+
throw new Error("findMany where.not only supports a single lossless operator expression");
|
|
3542
|
+
}
|
|
3543
|
+
return operatorEntries.map(([rawOperator, rawOperand]) => {
|
|
3544
|
+
if (!BOOLEAN_SAFE_OPERATORS.has(rawOperator)) {
|
|
3545
|
+
throw new Error(`findMany where.${label} only supports lossless scalar operators`);
|
|
3546
|
+
}
|
|
3547
|
+
if (Array.isArray(rawOperand)) {
|
|
3548
|
+
throw new Error(`findMany where.${label} does not support array-valued operators`);
|
|
3549
|
+
}
|
|
3550
|
+
return `${column}.${rawOperator}.${stringifyFilterValue(rawOperand)}`;
|
|
3551
|
+
});
|
|
3552
|
+
}
|
|
3553
|
+
function compileWhere(where) {
|
|
3554
|
+
if (where === void 0) {
|
|
3555
|
+
return void 0;
|
|
3556
|
+
}
|
|
3557
|
+
if (!isRecord5(where)) {
|
|
3558
|
+
throw new Error("findMany where must be an object");
|
|
3559
|
+
}
|
|
3560
|
+
const conditions = [];
|
|
3561
|
+
for (const [rawKey, rawValue] of Object.entries(where)) {
|
|
3562
|
+
if (rawValue === void 0) {
|
|
3563
|
+
continue;
|
|
3564
|
+
}
|
|
3565
|
+
if (rawKey === "or") {
|
|
3566
|
+
if (!Array.isArray(rawValue) || rawValue.length === 0) {
|
|
3567
|
+
throw new Error("findMany where.or must be a non-empty array");
|
|
3568
|
+
}
|
|
3569
|
+
const expressions = rawValue.flatMap(
|
|
3570
|
+
(value) => compileBooleanExpressionTerms(value, "or")
|
|
3571
|
+
);
|
|
3572
|
+
conditions.push(buildGatewayCondition("or", void 0, expressions.join(",")));
|
|
3573
|
+
continue;
|
|
3574
|
+
}
|
|
3575
|
+
if (rawKey === "not") {
|
|
3576
|
+
const expressions = compileBooleanExpressionTerms(rawValue, "not");
|
|
3577
|
+
if (expressions.length !== 1) {
|
|
3578
|
+
throw new Error("findMany where.not must compile to exactly one lossless expression");
|
|
3579
|
+
}
|
|
3580
|
+
conditions.push(buildGatewayCondition("not", void 0, expressions[0]));
|
|
3581
|
+
continue;
|
|
3582
|
+
}
|
|
3583
|
+
conditions.push(...compileColumnWhere(rawKey, rawValue));
|
|
3584
|
+
}
|
|
3585
|
+
return conditions.length > 0 ? conditions : void 0;
|
|
3586
|
+
}
|
|
3587
|
+
function resolveOrderDirection(input) {
|
|
3588
|
+
if (typeof input === "boolean") {
|
|
3589
|
+
return input === false ? "descending" : "ascending";
|
|
3590
|
+
}
|
|
3591
|
+
if (typeof input === "string") {
|
|
3592
|
+
const normalized = input.trim().toLowerCase();
|
|
3593
|
+
if (normalized === "asc" || normalized === "ascending") {
|
|
3594
|
+
return "ascending";
|
|
3595
|
+
}
|
|
3596
|
+
if (normalized === "desc" || normalized === "descending") {
|
|
3597
|
+
return "descending";
|
|
3598
|
+
}
|
|
3599
|
+
throw new Error(`Unsupported orderBy direction "${input}"`);
|
|
3600
|
+
}
|
|
3601
|
+
return input.ascending === false ? "descending" : "ascending";
|
|
3602
|
+
}
|
|
3603
|
+
function compileOrderBy(orderBy) {
|
|
3604
|
+
if (orderBy === void 0) {
|
|
3605
|
+
return void 0;
|
|
3606
|
+
}
|
|
3607
|
+
if (!isRecord5(orderBy)) {
|
|
3608
|
+
throw new Error("findMany orderBy must be an object");
|
|
3609
|
+
}
|
|
3610
|
+
if ("column" in orderBy) {
|
|
3611
|
+
return {
|
|
3612
|
+
field: normalizeIdentifier(String(orderBy.column), "orderBy column"),
|
|
3613
|
+
direction: orderBy.ascending === false ? "descending" : "ascending"
|
|
3614
|
+
};
|
|
3615
|
+
}
|
|
3616
|
+
const entries = Object.entries(orderBy).filter(([, value]) => value !== void 0);
|
|
3617
|
+
if (entries.length === 0) {
|
|
3618
|
+
return void 0;
|
|
3619
|
+
}
|
|
3620
|
+
if (entries.length > 1) {
|
|
3621
|
+
throw new Error("findMany orderBy only supports a single column in v1");
|
|
3622
|
+
}
|
|
3623
|
+
const [column, input] = entries[0];
|
|
3624
|
+
return {
|
|
3625
|
+
field: normalizeIdentifier(column, "orderBy column"),
|
|
3626
|
+
direction: resolveOrderDirection(input)
|
|
3627
|
+
};
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
// src/gateway/structured-select.ts
|
|
3631
|
+
var IDENTIFIER_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3632
|
+
function isIdentifierPath(value) {
|
|
3633
|
+
const segments = value.split(".").map((segment) => segment.trim());
|
|
3634
|
+
return segments.length > 1 && segments.every((segment) => IDENTIFIER_SEGMENT_PATTERN.test(segment));
|
|
3635
|
+
}
|
|
3636
|
+
function extractRelationHead(raw) {
|
|
3637
|
+
const trimmed = raw.trim();
|
|
3638
|
+
if (!trimmed) return "";
|
|
3639
|
+
const aliasIndex = trimmed.indexOf(":");
|
|
3640
|
+
const withoutAlias = aliasIndex >= 0 ? trimmed.slice(aliasIndex + 1).trim() : trimmed;
|
|
3641
|
+
const modifierIndex = withoutAlias.indexOf("!");
|
|
3642
|
+
return (modifierIndex >= 0 ? withoutAlias.slice(0, modifierIndex) : withoutAlias).trim();
|
|
3643
|
+
}
|
|
3644
|
+
function hasSchemaQualifiedRelationToken(select) {
|
|
3645
|
+
let singleQuoted = false;
|
|
3646
|
+
let doubleQuoted = false;
|
|
3647
|
+
let tokenStart = 0;
|
|
3648
|
+
for (let index = 0; index < select.length; index += 1) {
|
|
3649
|
+
const char = select[index];
|
|
3650
|
+
const next = index + 1 < select.length ? select[index + 1] : "";
|
|
3651
|
+
if (singleQuoted) {
|
|
3652
|
+
if (char === "'" && next === "'") {
|
|
3653
|
+
index += 1;
|
|
3654
|
+
continue;
|
|
3655
|
+
}
|
|
3656
|
+
if (char === "'") {
|
|
3657
|
+
singleQuoted = false;
|
|
3658
|
+
}
|
|
3659
|
+
continue;
|
|
3660
|
+
}
|
|
3661
|
+
if (doubleQuoted) {
|
|
3662
|
+
if (char === '"' && next === '"') {
|
|
3663
|
+
index += 1;
|
|
3664
|
+
continue;
|
|
3665
|
+
}
|
|
3666
|
+
if (char === '"') {
|
|
3667
|
+
doubleQuoted = false;
|
|
3668
|
+
}
|
|
3669
|
+
continue;
|
|
3670
|
+
}
|
|
3671
|
+
if (char === "'") {
|
|
3672
|
+
singleQuoted = true;
|
|
3673
|
+
continue;
|
|
3674
|
+
}
|
|
3675
|
+
if (char === '"') {
|
|
3676
|
+
doubleQuoted = true;
|
|
3677
|
+
continue;
|
|
3678
|
+
}
|
|
3679
|
+
if (char === "(") {
|
|
3680
|
+
const relationHead = extractRelationHead(select.slice(tokenStart, index));
|
|
3681
|
+
if (isIdentifierPath(relationHead)) {
|
|
3682
|
+
return true;
|
|
3683
|
+
}
|
|
3684
|
+
tokenStart = index + 1;
|
|
3685
|
+
continue;
|
|
3686
|
+
}
|
|
3687
|
+
if (char === "," || char === ")") {
|
|
3688
|
+
tokenStart = index + 1;
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
return false;
|
|
3692
|
+
}
|
|
3693
|
+
function toStructuredSelectString(columns) {
|
|
3694
|
+
return Array.isArray(columns) ? columns.join(",") : columns;
|
|
3695
|
+
}
|
|
3696
|
+
function buildStructuredWhere(conditions) {
|
|
3697
|
+
if (!conditions?.length) return void 0;
|
|
3698
|
+
const where = {};
|
|
3699
|
+
for (const condition of conditions) {
|
|
3700
|
+
if (!condition.column) {
|
|
3701
|
+
return null;
|
|
3702
|
+
}
|
|
3703
|
+
if (condition.column_cast !== void 0 || condition.value_cast !== void 0) {
|
|
3704
|
+
return null;
|
|
3705
|
+
}
|
|
3706
|
+
const operand = condition.value;
|
|
3707
|
+
const operator = condition.operator;
|
|
3708
|
+
if (operator === "eq") {
|
|
3709
|
+
if (operand === void 0) return null;
|
|
3710
|
+
} else if (operator === "neq" || operator === "gt" || operator === "lt") {
|
|
3711
|
+
if (operand === void 0) return null;
|
|
3712
|
+
} else if (operator === "in") {
|
|
3713
|
+
if (!Array.isArray(operand)) return null;
|
|
3714
|
+
} else {
|
|
3715
|
+
return null;
|
|
3716
|
+
}
|
|
3717
|
+
const existing = where[condition.column];
|
|
3718
|
+
if (existing !== void 0 && (typeof existing !== "object" || existing === null || Array.isArray(existing))) {
|
|
3719
|
+
return null;
|
|
3720
|
+
}
|
|
3721
|
+
const next = existing ?? {};
|
|
3722
|
+
if (Object.prototype.hasOwnProperty.call(next, operator)) {
|
|
3723
|
+
return null;
|
|
3724
|
+
}
|
|
3725
|
+
next[operator] = operand;
|
|
3726
|
+
where[condition.column] = next;
|
|
3727
|
+
}
|
|
3728
|
+
return where;
|
|
3729
|
+
}
|
|
3730
|
+
function buildStructuredOrderBy(order) {
|
|
3731
|
+
if (!order?.field) return void 0;
|
|
3732
|
+
return {
|
|
3733
|
+
[order.field]: order.direction === "descending" ? "desc" : "asc"
|
|
3734
|
+
};
|
|
3735
|
+
}
|
|
3736
|
+
function buildStructuredSelectTransport(input) {
|
|
3737
|
+
const select = toStructuredSelectString(input.columns).trim();
|
|
3738
|
+
if (!select || select === "*") {
|
|
3739
|
+
return null;
|
|
3740
|
+
}
|
|
3741
|
+
if (!hasSchemaQualifiedRelationToken(select)) {
|
|
3742
|
+
return null;
|
|
3743
|
+
}
|
|
3744
|
+
if (input.count !== void 0 || input.head !== void 0) {
|
|
3745
|
+
return {
|
|
3746
|
+
error: "Schema-qualified nested select strings require structured select transport, which does not support count/head options in athena-js yet."
|
|
3747
|
+
};
|
|
3748
|
+
}
|
|
3749
|
+
const where = buildStructuredWhere(input.conditions);
|
|
3750
|
+
if (where === null) {
|
|
3751
|
+
return {
|
|
3752
|
+
error: "Schema-qualified nested select strings only support eq, neq, gt, lt, and in filters in athena-js structured select transport."
|
|
3753
|
+
};
|
|
3754
|
+
}
|
|
3755
|
+
return {
|
|
3756
|
+
select,
|
|
3757
|
+
payload: {
|
|
3758
|
+
table_name: input.tableName,
|
|
3759
|
+
select,
|
|
3760
|
+
where,
|
|
3761
|
+
orderBy: buildStructuredOrderBy(input.order),
|
|
3762
|
+
limit: input.limit,
|
|
3763
|
+
offset: input.offset,
|
|
3764
|
+
strip_nulls: input.stripNulls
|
|
3765
|
+
}
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3769
|
+
// src/query-transport.ts
|
|
3770
|
+
function canUseFindManyAstTransport(state) {
|
|
3771
|
+
return state.conditions.length === 0 && state.offset === void 0 && state.currentPage === void 0 && state.pageSize === void 0 && state.totalPages === void 0;
|
|
3772
|
+
}
|
|
3773
|
+
function toFindManyAstOrder(order) {
|
|
3774
|
+
if (!order) {
|
|
3775
|
+
return void 0;
|
|
3776
|
+
}
|
|
3777
|
+
return {
|
|
3778
|
+
column: order.field,
|
|
3779
|
+
ascending: order.direction !== "descending"
|
|
3780
|
+
};
|
|
3781
|
+
}
|
|
3782
|
+
function resolvePagination(input) {
|
|
3783
|
+
let limit = input.limit;
|
|
3784
|
+
let offset = input.offset;
|
|
3785
|
+
if (limit === void 0 && input.pageSize !== void 0) {
|
|
3786
|
+
limit = Math.max(0, Math.trunc(input.pageSize));
|
|
3787
|
+
}
|
|
3788
|
+
if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
|
|
3789
|
+
offset = (Math.trunc(input.currentPage) - 1) * Math.max(0, Math.trunc(input.pageSize));
|
|
3790
|
+
}
|
|
3791
|
+
return { limit, offset };
|
|
3792
|
+
}
|
|
3793
|
+
function hasTypedEqualityComparison(conditions) {
|
|
3794
|
+
return conditions?.some(
|
|
3795
|
+
(condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
|
|
3796
|
+
) ?? false;
|
|
3797
|
+
}
|
|
3798
|
+
function createSelectTransportPlan(input) {
|
|
3799
|
+
const conditions = input.state.conditions.length ? input.state.conditions.map((condition) => ({ ...condition })) : void 0;
|
|
3800
|
+
if (hasTypedEqualityComparison(conditions) && !input.options?.head && !input.options?.count && conditions) {
|
|
3801
|
+
const query = input.buildTypedSelectQuery({
|
|
3802
|
+
tableName: input.tableName,
|
|
3803
|
+
columns: input.columns,
|
|
3804
|
+
conditions,
|
|
3805
|
+
limit: input.state.limit,
|
|
3806
|
+
offset: input.state.offset,
|
|
3807
|
+
currentPage: input.state.currentPage,
|
|
3808
|
+
pageSize: input.state.pageSize,
|
|
3809
|
+
order: input.state.order
|
|
3810
|
+
});
|
|
3811
|
+
if (query) {
|
|
3812
|
+
return {
|
|
3813
|
+
kind: "query",
|
|
3814
|
+
query,
|
|
3815
|
+
payload: { query }
|
|
3816
|
+
};
|
|
3817
|
+
}
|
|
3818
|
+
}
|
|
3819
|
+
const pagination = resolvePagination({
|
|
3820
|
+
limit: input.state.limit,
|
|
3821
|
+
offset: input.state.offset,
|
|
3822
|
+
currentPage: input.state.currentPage,
|
|
3823
|
+
pageSize: input.state.pageSize
|
|
3824
|
+
});
|
|
3825
|
+
const stripNulls = input.options?.stripNulls ?? true;
|
|
3826
|
+
const structuredSelectTransport = buildStructuredSelectTransport({
|
|
3827
|
+
tableName: input.tableName,
|
|
3828
|
+
columns: input.columns,
|
|
3829
|
+
conditions,
|
|
3830
|
+
limit: pagination.limit,
|
|
3831
|
+
offset: pagination.offset,
|
|
3832
|
+
order: input.state.order,
|
|
3833
|
+
stripNulls,
|
|
3834
|
+
count: input.options?.count,
|
|
3835
|
+
head: input.options?.head
|
|
3836
|
+
});
|
|
3837
|
+
if (structuredSelectTransport && "error" in structuredSelectTransport) {
|
|
3838
|
+
throw new Error(structuredSelectTransport.error);
|
|
3839
|
+
}
|
|
3840
|
+
if (structuredSelectTransport) {
|
|
3841
|
+
const { payload, select } = structuredSelectTransport;
|
|
3842
|
+
return {
|
|
3843
|
+
kind: "fetch",
|
|
3844
|
+
payload,
|
|
3845
|
+
debug: {
|
|
3846
|
+
columns: select,
|
|
3847
|
+
conditions,
|
|
3848
|
+
limit: pagination.limit,
|
|
3849
|
+
offset: pagination.offset,
|
|
3850
|
+
order: input.state.order
|
|
3851
|
+
}
|
|
3852
|
+
};
|
|
3853
|
+
}
|
|
3854
|
+
return {
|
|
3855
|
+
kind: "fetch",
|
|
3856
|
+
payload: {
|
|
3857
|
+
table_name: input.tableName,
|
|
3858
|
+
columns: input.columns,
|
|
3859
|
+
conditions,
|
|
3860
|
+
limit: input.state.limit,
|
|
3861
|
+
offset: input.state.offset,
|
|
3862
|
+
current_page: input.state.currentPage,
|
|
3863
|
+
page_size: input.state.pageSize,
|
|
3864
|
+
total_pages: input.state.totalPages,
|
|
3865
|
+
sort_by: input.state.order,
|
|
3866
|
+
strip_nulls: stripNulls,
|
|
3867
|
+
count: input.options?.count,
|
|
3868
|
+
head: input.options?.head
|
|
3869
|
+
},
|
|
3870
|
+
debug: {
|
|
3871
|
+
columns: input.columns,
|
|
3872
|
+
conditions,
|
|
3873
|
+
limit: input.state.limit,
|
|
3874
|
+
offset: input.state.offset,
|
|
3875
|
+
currentPage: input.state.currentPage,
|
|
3876
|
+
pageSize: input.state.pageSize,
|
|
3877
|
+
order: input.state.order
|
|
3878
|
+
}
|
|
3879
|
+
};
|
|
3880
|
+
}
|
|
3881
|
+
|
|
2776
3882
|
// src/client.ts
|
|
2777
3883
|
var DEFAULT_COLUMNS = "*";
|
|
2778
|
-
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;
|
|
2779
3884
|
var SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_]*(?:\[\])?$/i;
|
|
2780
3885
|
var ATHENA_NORMALIZED_ERROR_KEY = "__athenaNormalizedError";
|
|
3886
|
+
var QUERY_TRACE_STACK_SKIP_PATTERNS = [
|
|
3887
|
+
"src\\client.ts",
|
|
3888
|
+
"src/client.ts",
|
|
3889
|
+
"dist\\client.",
|
|
3890
|
+
"dist/client.",
|
|
3891
|
+
"node_modules\\@xylex-group\\athena",
|
|
3892
|
+
"node_modules/@xylex-group/athena",
|
|
3893
|
+
"node:internal",
|
|
3894
|
+
"internal/process"
|
|
3895
|
+
];
|
|
2781
3896
|
function formatResult(response) {
|
|
2782
3897
|
const result = {
|
|
2783
3898
|
data: response.data ?? null,
|
|
2784
|
-
error:
|
|
3899
|
+
error: null,
|
|
2785
3900
|
errorDetails: response.errorDetails ?? null,
|
|
2786
3901
|
status: response.status,
|
|
3902
|
+
statusText: response.statusText ?? null,
|
|
2787
3903
|
raw: response.raw
|
|
2788
3904
|
};
|
|
2789
3905
|
if (response.count !== void 0) {
|
|
@@ -2800,19 +3916,236 @@ function attachNormalizedError(result, normalizedError) {
|
|
|
2800
3916
|
});
|
|
2801
3917
|
}
|
|
2802
3918
|
function createResultFormatter(experimental) {
|
|
2803
|
-
if (!experimental?.enableErrorNormalization) {
|
|
2804
|
-
return formatResult;
|
|
2805
|
-
}
|
|
2806
3919
|
return (response, context) => {
|
|
2807
3920
|
const result = formatResult(response);
|
|
2808
|
-
if (
|
|
3921
|
+
if (response.error == null && response.errorDetails == null) {
|
|
2809
3922
|
return result;
|
|
2810
3923
|
}
|
|
2811
|
-
const normalizedError = normalizeAthenaError(
|
|
3924
|
+
const normalizedError = normalizeAthenaError(
|
|
3925
|
+
{
|
|
3926
|
+
...result,
|
|
3927
|
+
error: response.error ?? response.errorDetails?.message ?? null
|
|
3928
|
+
},
|
|
3929
|
+
context
|
|
3930
|
+
);
|
|
3931
|
+
result.error = createResultError(response, result, normalizedError);
|
|
2812
3932
|
attachNormalizedError(result, normalizedError);
|
|
2813
3933
|
return result;
|
|
2814
3934
|
};
|
|
2815
3935
|
}
|
|
3936
|
+
function isRecord6(value) {
|
|
3937
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3938
|
+
}
|
|
3939
|
+
function firstNonEmptyString2(...values) {
|
|
3940
|
+
for (const value of values) {
|
|
3941
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
3942
|
+
return value.trim();
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
return void 0;
|
|
3946
|
+
}
|
|
3947
|
+
function resolveStructuredErrorPayload2(raw) {
|
|
3948
|
+
if (!isRecord6(raw)) return null;
|
|
3949
|
+
return isRecord6(raw.error) ? raw.error : raw;
|
|
3950
|
+
}
|
|
3951
|
+
function resolveStructuredErrorDetails(payload, message) {
|
|
3952
|
+
if (!payload || !("details" in payload)) {
|
|
3953
|
+
return null;
|
|
3954
|
+
}
|
|
3955
|
+
const details = payload.details;
|
|
3956
|
+
if (details == null) {
|
|
3957
|
+
return null;
|
|
3958
|
+
}
|
|
3959
|
+
if (typeof details === "string" && details.trim() === message.trim()) {
|
|
3960
|
+
return null;
|
|
3961
|
+
}
|
|
3962
|
+
return details;
|
|
3963
|
+
}
|
|
3964
|
+
function createResultError(response, result, normalized) {
|
|
3965
|
+
const rawRecord = isRecord6(response.raw) ? response.raw : null;
|
|
3966
|
+
const payload = resolveStructuredErrorPayload2(response.raw);
|
|
3967
|
+
const message = firstNonEmptyString2(
|
|
3968
|
+
response.error,
|
|
3969
|
+
payload?.message,
|
|
3970
|
+
payload?.error,
|
|
3971
|
+
payload?.details,
|
|
3972
|
+
response.errorDetails?.message,
|
|
3973
|
+
normalized.message
|
|
3974
|
+
) ?? normalized.message;
|
|
3975
|
+
const statusText = firstNonEmptyString2(response.statusText, rawRecord?.statusText) ?? null;
|
|
3976
|
+
const hint = firstNonEmptyString2(payload?.hint, response.errorDetails?.hint) ?? null;
|
|
3977
|
+
const code = firstNonEmptyString2(payload?.code) ?? normalized.code;
|
|
3978
|
+
const details = resolveStructuredErrorDetails(payload, message) ?? response.errorDetails?.cause ?? null;
|
|
3979
|
+
return {
|
|
3980
|
+
message,
|
|
3981
|
+
code,
|
|
3982
|
+
athenaCode: normalized.code,
|
|
3983
|
+
gatewayCode: response.errorDetails?.code ?? null,
|
|
3984
|
+
kind: normalized.kind,
|
|
3985
|
+
category: normalized.category,
|
|
3986
|
+
retryable: normalized.retryable,
|
|
3987
|
+
details,
|
|
3988
|
+
hint,
|
|
3989
|
+
status: result.status,
|
|
3990
|
+
statusText,
|
|
3991
|
+
constraint: normalized.constraint,
|
|
3992
|
+
table: normalized.table,
|
|
3993
|
+
operation: normalized.operation,
|
|
3994
|
+
endpoint: response.errorDetails?.endpoint,
|
|
3995
|
+
method: response.errorDetails?.method,
|
|
3996
|
+
requestId: response.errorDetails?.requestId,
|
|
3997
|
+
cause: response.errorDetails?.cause,
|
|
3998
|
+
raw: result.raw
|
|
3999
|
+
};
|
|
4000
|
+
}
|
|
4001
|
+
function parseQueryTraceCallsiteFrame(frame) {
|
|
4002
|
+
const trimmed = frame.trim();
|
|
4003
|
+
if (!trimmed) {
|
|
4004
|
+
return null;
|
|
4005
|
+
}
|
|
4006
|
+
let body = trimmed.replace(/^at\s+/, "");
|
|
4007
|
+
if (body.startsWith("async ")) {
|
|
4008
|
+
body = body.slice(6);
|
|
4009
|
+
}
|
|
4010
|
+
let functionName;
|
|
4011
|
+
let location = body;
|
|
4012
|
+
const wrappedMatch = body.match(/^(.*?)\s+\((.*)\)$/);
|
|
4013
|
+
if (wrappedMatch) {
|
|
4014
|
+
functionName = wrappedMatch[1].trim() || void 0;
|
|
4015
|
+
location = wrappedMatch[2].trim();
|
|
4016
|
+
}
|
|
4017
|
+
const locationMatch = location.match(/^(.*):(\d+):(\d+)$/);
|
|
4018
|
+
if (!locationMatch) {
|
|
4019
|
+
return null;
|
|
4020
|
+
}
|
|
4021
|
+
const filePath = locationMatch[1].replace(/^file:\/\//, "");
|
|
4022
|
+
const line = Number(locationMatch[2]);
|
|
4023
|
+
const column = Number(locationMatch[3]);
|
|
4024
|
+
if (!Number.isFinite(line) || !Number.isFinite(column)) {
|
|
4025
|
+
return null;
|
|
4026
|
+
}
|
|
4027
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
4028
|
+
const fileName = normalizedPath.split("/").at(-1) ?? filePath;
|
|
4029
|
+
return {
|
|
4030
|
+
filePath,
|
|
4031
|
+
fileName,
|
|
4032
|
+
line,
|
|
4033
|
+
column,
|
|
4034
|
+
frame: trimmed,
|
|
4035
|
+
functionName
|
|
4036
|
+
};
|
|
4037
|
+
}
|
|
4038
|
+
function captureQueryTraceCallsite() {
|
|
4039
|
+
const stack = new Error().stack;
|
|
4040
|
+
if (!stack) return null;
|
|
4041
|
+
const frames = stack.split("\n").slice(2).map((frame) => frame.trim()).filter(Boolean);
|
|
4042
|
+
for (const frame of frames) {
|
|
4043
|
+
if (QUERY_TRACE_STACK_SKIP_PATTERNS.some((pattern) => frame.includes(pattern))) {
|
|
4044
|
+
continue;
|
|
4045
|
+
}
|
|
4046
|
+
const callsite = parseQueryTraceCallsiteFrame(frame);
|
|
4047
|
+
if (callsite) return callsite;
|
|
4048
|
+
}
|
|
4049
|
+
const fallback = frames.find((frame) => !frame.includes("captureQueryTraceCallsite"));
|
|
4050
|
+
return fallback ? parseQueryTraceCallsiteFrame(fallback) : null;
|
|
4051
|
+
}
|
|
4052
|
+
function defaultQueryTraceLogger(event) {
|
|
4053
|
+
const target = event.table ?? event.functionName ?? "gateway";
|
|
4054
|
+
const outcomeState = event.outcome?.error ? "error" : "ok";
|
|
4055
|
+
const banner = `[athena-js][trace] ${event.operation.toUpperCase()} ${event.endpoint} ${target} ${event.durationMs}ms ${outcomeState}`;
|
|
4056
|
+
console.info(banner, event);
|
|
4057
|
+
}
|
|
4058
|
+
function captureTraceCallsite(tracer) {
|
|
4059
|
+
return tracer?.captureCallsite() ?? null;
|
|
4060
|
+
}
|
|
4061
|
+
function createTraceCallsiteStore(tracer, initialCallsite) {
|
|
4062
|
+
let storedCallsite = initialCallsite ?? void 0;
|
|
4063
|
+
return {
|
|
4064
|
+
resolve(callsite) {
|
|
4065
|
+
if (callsite) {
|
|
4066
|
+
storedCallsite = callsite;
|
|
4067
|
+
return callsite;
|
|
4068
|
+
}
|
|
4069
|
+
if (storedCallsite !== void 0) {
|
|
4070
|
+
return storedCallsite;
|
|
4071
|
+
}
|
|
4072
|
+
const capturedCallsite = captureTraceCallsite(tracer);
|
|
4073
|
+
if (capturedCallsite) {
|
|
4074
|
+
storedCallsite = capturedCallsite;
|
|
4075
|
+
}
|
|
4076
|
+
return capturedCallsite;
|
|
4077
|
+
}
|
|
4078
|
+
};
|
|
4079
|
+
}
|
|
4080
|
+
function createQueryTracer(experimental) {
|
|
4081
|
+
const traceOption = experimental?.traceQueries;
|
|
4082
|
+
if (!traceOption) {
|
|
4083
|
+
return void 0;
|
|
4084
|
+
}
|
|
4085
|
+
const logger = typeof traceOption === "object" && traceOption.logger ? traceOption.logger : defaultQueryTraceLogger;
|
|
4086
|
+
const emit = (event) => {
|
|
4087
|
+
try {
|
|
4088
|
+
logger(event);
|
|
4089
|
+
} catch (error) {
|
|
4090
|
+
console.warn("[athena-js][trace] logger failed", error);
|
|
4091
|
+
}
|
|
4092
|
+
};
|
|
4093
|
+
return {
|
|
4094
|
+
captureCallsite: captureQueryTraceCallsite,
|
|
4095
|
+
publishSuccess(context, result, durationMs, callsite) {
|
|
4096
|
+
emit({
|
|
4097
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4098
|
+
durationMs,
|
|
4099
|
+
operation: context.operation,
|
|
4100
|
+
endpoint: context.endpoint,
|
|
4101
|
+
table: context.table,
|
|
4102
|
+
functionName: context.functionName,
|
|
4103
|
+
sql: context.sql,
|
|
4104
|
+
payload: context.payload,
|
|
4105
|
+
options: context.options,
|
|
4106
|
+
callsite,
|
|
4107
|
+
outcome: {
|
|
4108
|
+
status: result.status,
|
|
4109
|
+
error: result.error,
|
|
4110
|
+
errorDetails: result.errorDetails ?? null,
|
|
4111
|
+
count: result.count ?? null,
|
|
4112
|
+
data: result.data,
|
|
4113
|
+
raw: result.raw
|
|
4114
|
+
}
|
|
4115
|
+
});
|
|
4116
|
+
},
|
|
4117
|
+
publishFailure(context, error, durationMs, callsite) {
|
|
4118
|
+
emit({
|
|
4119
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4120
|
+
durationMs,
|
|
4121
|
+
operation: context.operation,
|
|
4122
|
+
endpoint: context.endpoint,
|
|
4123
|
+
table: context.table,
|
|
4124
|
+
functionName: context.functionName,
|
|
4125
|
+
sql: context.sql,
|
|
4126
|
+
payload: context.payload,
|
|
4127
|
+
options: context.options,
|
|
4128
|
+
callsite,
|
|
4129
|
+
thrownError: error
|
|
4130
|
+
});
|
|
4131
|
+
}
|
|
4132
|
+
};
|
|
4133
|
+
}
|
|
4134
|
+
async function executeWithQueryTrace(tracer, context, runner, callsiteOverride) {
|
|
4135
|
+
if (!tracer) {
|
|
4136
|
+
return runner();
|
|
4137
|
+
}
|
|
4138
|
+
const callsite = callsiteOverride ?? tracer.captureCallsite();
|
|
4139
|
+
const startedAt = Date.now();
|
|
4140
|
+
try {
|
|
4141
|
+
const result = await runner();
|
|
4142
|
+
tracer.publishSuccess(context, result, Date.now() - startedAt, callsite);
|
|
4143
|
+
return result;
|
|
4144
|
+
} catch (error) {
|
|
4145
|
+
tracer.publishFailure(context, error, Date.now() - startedAt, callsite);
|
|
4146
|
+
throw error;
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
2816
4149
|
function toSingleResult(response) {
|
|
2817
4150
|
const payload = response.data;
|
|
2818
4151
|
const singleData = Array.isArray(payload) ? payload.length ? payload[0] : null : payload ?? null;
|
|
@@ -2833,15 +4166,16 @@ function asAthenaJsonObject(value) {
|
|
|
2833
4166
|
function asAthenaJsonObjectArray(values) {
|
|
2834
4167
|
return values;
|
|
2835
4168
|
}
|
|
2836
|
-
function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
|
|
4169
|
+
function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS, tracer, initialCallsite) {
|
|
2837
4170
|
let selectedColumns = defaultColumns === null ? void 0 : defaultColumns;
|
|
2838
4171
|
let selectedOptions;
|
|
2839
4172
|
let promise = null;
|
|
2840
|
-
const
|
|
4173
|
+
const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
|
|
4174
|
+
const run = (columns, options, callsite) => {
|
|
2841
4175
|
const payloadColumns = columns ?? selectedColumns;
|
|
2842
4176
|
const payloadOptions = options ?? selectedOptions;
|
|
2843
4177
|
if (!promise) {
|
|
2844
|
-
promise = executor(payloadColumns, payloadOptions);
|
|
4178
|
+
promise = executor(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
|
|
2845
4179
|
}
|
|
2846
4180
|
return promise;
|
|
2847
4181
|
};
|
|
@@ -2849,7 +4183,7 @@ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
|
|
|
2849
4183
|
select(columns = selectedColumns, options) {
|
|
2850
4184
|
selectedColumns = columns;
|
|
2851
4185
|
selectedOptions = options ?? selectedOptions;
|
|
2852
|
-
return run(columns, options);
|
|
4186
|
+
return run(columns, options, captureTraceCallsite(tracer));
|
|
2853
4187
|
},
|
|
2854
4188
|
returning(columns = selectedColumns, options) {
|
|
2855
4189
|
return mutationQuery.select(columns, options);
|
|
@@ -2857,7 +4191,7 @@ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
|
|
|
2857
4191
|
single(columns = selectedColumns, options) {
|
|
2858
4192
|
selectedColumns = columns;
|
|
2859
4193
|
selectedOptions = options ?? selectedOptions;
|
|
2860
|
-
return run(columns, options).then(toSingleResult);
|
|
4194
|
+
return run(columns, options, captureTraceCallsite(tracer)).then(toSingleResult);
|
|
2861
4195
|
},
|
|
2862
4196
|
maybeSingle(columns = selectedColumns, options) {
|
|
2863
4197
|
return mutationQuery.single(columns, options);
|
|
@@ -2880,21 +4214,12 @@ function getResourceId(state) {
|
|
|
2880
4214
|
);
|
|
2881
4215
|
return candidate?.value?.toString();
|
|
2882
4216
|
}
|
|
2883
|
-
function
|
|
4217
|
+
function stringifyFilterValue2(value) {
|
|
2884
4218
|
if (Array.isArray(value)) {
|
|
2885
4219
|
return value.join(",");
|
|
2886
4220
|
}
|
|
2887
4221
|
return String(value);
|
|
2888
4222
|
}
|
|
2889
|
-
function isUuidString(value) {
|
|
2890
|
-
return UUID_PATTERN.test(value.trim());
|
|
2891
|
-
}
|
|
2892
|
-
function isUuidIdentifierColumn(column) {
|
|
2893
|
-
return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
|
|
2894
|
-
}
|
|
2895
|
-
function shouldUseUuidTextComparison(column, value) {
|
|
2896
|
-
return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
|
|
2897
|
-
}
|
|
2898
4223
|
function normalizeCast(cast) {
|
|
2899
4224
|
const normalized = cast.trim().toLowerCase();
|
|
2900
4225
|
if (!SAFE_CAST_PATTERN.test(normalized)) {
|
|
@@ -2917,25 +4242,92 @@ function withCast(expression, cast) {
|
|
|
2917
4242
|
}
|
|
2918
4243
|
function buildSelectColumnsClause(columns) {
|
|
2919
4244
|
if (Array.isArray(columns)) {
|
|
2920
|
-
return columns.map((column) =>
|
|
4245
|
+
return columns.map((column) => quoteSelectColumnToken(column)).join(", ");
|
|
2921
4246
|
}
|
|
2922
4247
|
return quoteSelectColumnsExpression(columns);
|
|
2923
4248
|
}
|
|
4249
|
+
function parseIdentifierSegment(input) {
|
|
4250
|
+
const trimmed = input.trim();
|
|
4251
|
+
if (!trimmed) return null;
|
|
4252
|
+
if (!trimmed.startsWith('"')) {
|
|
4253
|
+
return {
|
|
4254
|
+
normalizedValue: trimmed.toLowerCase()
|
|
4255
|
+
};
|
|
4256
|
+
}
|
|
4257
|
+
let value = "";
|
|
4258
|
+
let index = 1;
|
|
4259
|
+
let closed = false;
|
|
4260
|
+
while (index < trimmed.length) {
|
|
4261
|
+
const char = trimmed[index];
|
|
4262
|
+
const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
|
|
4263
|
+
if (char === '"' && next === '"') {
|
|
4264
|
+
value += '"';
|
|
4265
|
+
index += 2;
|
|
4266
|
+
continue;
|
|
4267
|
+
}
|
|
4268
|
+
if (char === '"') {
|
|
4269
|
+
closed = true;
|
|
4270
|
+
index += 1;
|
|
4271
|
+
break;
|
|
4272
|
+
}
|
|
4273
|
+
value += char;
|
|
4274
|
+
index += 1;
|
|
4275
|
+
}
|
|
4276
|
+
if (!closed || trimmed.slice(index).trim().length > 0 || !value.trim()) {
|
|
4277
|
+
return null;
|
|
4278
|
+
}
|
|
4279
|
+
return {
|
|
4280
|
+
normalizedValue: value
|
|
4281
|
+
};
|
|
4282
|
+
}
|
|
4283
|
+
function splitQualifiedTableName(tableName) {
|
|
4284
|
+
const trimmed = tableName.trim();
|
|
4285
|
+
let inQuotes = false;
|
|
4286
|
+
for (let index = 0; index < trimmed.length; index += 1) {
|
|
4287
|
+
const char = trimmed[index];
|
|
4288
|
+
const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
|
|
4289
|
+
if (char === '"') {
|
|
4290
|
+
if (inQuotes && next === '"') {
|
|
4291
|
+
index += 1;
|
|
4292
|
+
continue;
|
|
4293
|
+
}
|
|
4294
|
+
inQuotes = !inQuotes;
|
|
4295
|
+
continue;
|
|
4296
|
+
}
|
|
4297
|
+
if (char === "." && !inQuotes) {
|
|
4298
|
+
const schemaSegment = trimmed.slice(0, index).trim();
|
|
4299
|
+
const tableSegment = trimmed.slice(index + 1).trim();
|
|
4300
|
+
if (!schemaSegment || !tableSegment) {
|
|
4301
|
+
return null;
|
|
4302
|
+
}
|
|
4303
|
+
return { schemaSegment };
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
return null;
|
|
4307
|
+
}
|
|
2924
4308
|
function resolveTableNameForCall(tableName, schema) {
|
|
2925
4309
|
if (!schema) return tableName;
|
|
2926
4310
|
const normalizedSchema = schema.trim();
|
|
2927
4311
|
if (!normalizedSchema) {
|
|
2928
4312
|
throw new Error("schema option must be a non-empty string");
|
|
2929
4313
|
}
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
4314
|
+
const normalizedTableName = tableName.trim();
|
|
4315
|
+
const parsedSchema = parseIdentifierSegment(normalizedSchema);
|
|
4316
|
+
if (!parsedSchema) {
|
|
4317
|
+
throw new Error("schema option must be a non-empty string");
|
|
4318
|
+
}
|
|
4319
|
+
const qualified = splitQualifiedTableName(normalizedTableName);
|
|
4320
|
+
if (qualified) {
|
|
4321
|
+
const parsedTableSchema = parseIdentifierSegment(qualified.schemaSegment);
|
|
4322
|
+
const sameSchema = parsedTableSchema ? parsedTableSchema.normalizedValue === parsedSchema.normalizedValue : normalizedTableName.startsWith(`${normalizedSchema}.`);
|
|
4323
|
+
if (sameSchema) {
|
|
4324
|
+
return normalizedTableName;
|
|
2933
4325
|
}
|
|
2934
4326
|
throw new Error(
|
|
2935
|
-
`schema option "${normalizedSchema}" conflicts with schema-qualified table "${
|
|
4327
|
+
`schema option "${normalizedSchema}" conflicts with schema-qualified table "${normalizedTableName}"`
|
|
2936
4328
|
);
|
|
2937
4329
|
}
|
|
2938
|
-
return `${normalizedSchema}.${
|
|
4330
|
+
return `${normalizedSchema}.${normalizedTableName}`;
|
|
2939
4331
|
}
|
|
2940
4332
|
function conditionToSqlClause(condition) {
|
|
2941
4333
|
if (!condition.column) return null;
|
|
@@ -3013,6 +4405,226 @@ function buildTypedSelectQuery(input) {
|
|
|
3013
4405
|
}
|
|
3014
4406
|
return `${sqlParts.join(" ")};`;
|
|
3015
4407
|
}
|
|
4408
|
+
function sanitizeSqlComment(comment) {
|
|
4409
|
+
return comment.replace(/\*\//g, "* /");
|
|
4410
|
+
}
|
|
4411
|
+
function toSqlJsonLiteral(value) {
|
|
4412
|
+
if (value === void 0) return "DEFAULT";
|
|
4413
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
4414
|
+
return toSqlLiteral(value);
|
|
4415
|
+
}
|
|
4416
|
+
return `'${escapeSqlStringLiteral(JSON.stringify(value))}'::jsonb`;
|
|
4417
|
+
}
|
|
4418
|
+
function conditionToDebugSqlClause(condition) {
|
|
4419
|
+
const exact = conditionToSqlClause(condition);
|
|
4420
|
+
if (exact) return exact;
|
|
4421
|
+
const rawCondition = sanitizeSqlComment(JSON.stringify(condition));
|
|
4422
|
+
if (!condition.column) {
|
|
4423
|
+
return `TRUE /* unsupported condition: ${rawCondition} */`;
|
|
4424
|
+
}
|
|
4425
|
+
const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
|
|
4426
|
+
const value = condition.value;
|
|
4427
|
+
const rhs = withCast(toSqlJsonLiteral(value), condition.value_cast);
|
|
4428
|
+
switch (condition.operator) {
|
|
4429
|
+
case "contains":
|
|
4430
|
+
return `${column} @> ${rhs}`;
|
|
4431
|
+
case "containedBy":
|
|
4432
|
+
return `${column} <@ ${rhs}`;
|
|
4433
|
+
case "not":
|
|
4434
|
+
return `TRUE /* NOT expression passthrough: ${rawCondition} */`;
|
|
4435
|
+
case "or":
|
|
4436
|
+
return `TRUE /* OR expression passthrough: ${rawCondition} */`;
|
|
4437
|
+
default:
|
|
4438
|
+
return `TRUE /* unsupported condition: ${rawCondition} */`;
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
function appendOrderLimitOffset(sqlParts, order, limit, offset) {
|
|
4442
|
+
if (order?.field) {
|
|
4443
|
+
const direction = order.direction === "descending" ? "DESC" : "ASC";
|
|
4444
|
+
sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(order.field)} ${direction}`);
|
|
4445
|
+
}
|
|
4446
|
+
if (limit !== void 0) {
|
|
4447
|
+
sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
|
|
4448
|
+
}
|
|
4449
|
+
if (offset !== void 0) {
|
|
4450
|
+
sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4453
|
+
function buildDebugSelectQuery(input) {
|
|
4454
|
+
const sqlParts = [
|
|
4455
|
+
`SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
|
|
4456
|
+
];
|
|
4457
|
+
if (input.conditions?.length) {
|
|
4458
|
+
const whereClauses = input.conditions.map(conditionToDebugSqlClause);
|
|
4459
|
+
sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
|
|
4460
|
+
}
|
|
4461
|
+
const pagination = resolvePagination(input);
|
|
4462
|
+
appendOrderLimitOffset(sqlParts, input.order, pagination.limit, pagination.offset);
|
|
4463
|
+
return `${sqlParts.join(" ")};`;
|
|
4464
|
+
}
|
|
4465
|
+
function resolveDebugTableIdentifier(tableName) {
|
|
4466
|
+
if (!tableName?.trim()) {
|
|
4467
|
+
return '"__unknown_table__"';
|
|
4468
|
+
}
|
|
4469
|
+
return quoteQualifiedIdentifier(tableName);
|
|
4470
|
+
}
|
|
4471
|
+
function buildInsertDebugSql(payload) {
|
|
4472
|
+
const rows = Array.isArray(payload.insert_body) ? payload.insert_body : [payload.insert_body];
|
|
4473
|
+
const columns = [];
|
|
4474
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4475
|
+
for (const row of rows) {
|
|
4476
|
+
for (const column of Object.keys(row)) {
|
|
4477
|
+
if (seen.has(column)) continue;
|
|
4478
|
+
seen.add(column);
|
|
4479
|
+
columns.push(column);
|
|
4480
|
+
}
|
|
4481
|
+
}
|
|
4482
|
+
const sqlParts = [`INSERT INTO ${quoteQualifiedIdentifier(payload.table_name)}`];
|
|
4483
|
+
if (!rows.length || !columns.length) {
|
|
4484
|
+
sqlParts.push("DEFAULT VALUES");
|
|
4485
|
+
if (rows.length > 1) {
|
|
4486
|
+
sqlParts.push(`/* trace: ${rows.length} rows collapsed to DEFAULT VALUES */`);
|
|
4487
|
+
}
|
|
4488
|
+
} else {
|
|
4489
|
+
const valuesClause = rows.map((row) => {
|
|
4490
|
+
const values = columns.map((column) => {
|
|
4491
|
+
const hasColumn = Object.prototype.hasOwnProperty.call(row, column);
|
|
4492
|
+
if (!hasColumn) {
|
|
4493
|
+
return payload.default_to_null ? "NULL" : "DEFAULT";
|
|
4494
|
+
}
|
|
4495
|
+
const rowValue = row[column];
|
|
4496
|
+
return toSqlJsonLiteral(rowValue);
|
|
4497
|
+
});
|
|
4498
|
+
return `(${values.join(", ")})`;
|
|
4499
|
+
}).join(", ");
|
|
4500
|
+
const columnClause = columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
|
|
4501
|
+
sqlParts.push(`(${columnClause})`);
|
|
4502
|
+
sqlParts.push(`VALUES ${valuesClause}`);
|
|
4503
|
+
}
|
|
4504
|
+
if (payload.on_conflict) {
|
|
4505
|
+
const conflictColumns = Array.isArray(payload.on_conflict) ? payload.on_conflict.map((column) => quoteQualifiedIdentifier(column)).join(", ") : payload.on_conflict;
|
|
4506
|
+
if (payload.update_body && Object.keys(payload.update_body).length > 0) {
|
|
4507
|
+
const assignments = Object.entries(payload.update_body).map(
|
|
4508
|
+
([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
|
|
4509
|
+
);
|
|
4510
|
+
sqlParts.push(`ON CONFLICT (${conflictColumns}) DO UPDATE SET ${assignments.join(", ")}`);
|
|
4511
|
+
} else {
|
|
4512
|
+
sqlParts.push(`ON CONFLICT (${conflictColumns}) DO NOTHING`);
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
if (payload.columns) {
|
|
4516
|
+
sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
|
|
4517
|
+
}
|
|
4518
|
+
return `${sqlParts.join(" ")};`;
|
|
4519
|
+
}
|
|
4520
|
+
function buildUpdateDebugSql(payload) {
|
|
4521
|
+
const set = payload.set ?? payload.data ?? {};
|
|
4522
|
+
const assignments = Object.entries(set).map(
|
|
4523
|
+
([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
|
|
4524
|
+
);
|
|
4525
|
+
const sqlParts = [
|
|
4526
|
+
`UPDATE ${resolveDebugTableIdentifier(payload.table_name)} SET ${assignments.length ? assignments.join(", ") : "/* empty set */"}`
|
|
4527
|
+
];
|
|
4528
|
+
if (payload.conditions?.length) {
|
|
4529
|
+
const whereClauses = payload.conditions.map(conditionToDebugSqlClause);
|
|
4530
|
+
sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
|
|
4531
|
+
}
|
|
4532
|
+
const pagination = resolvePagination({
|
|
4533
|
+
currentPage: payload.current_page,
|
|
4534
|
+
pageSize: payload.page_size
|
|
4535
|
+
});
|
|
4536
|
+
appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
|
|
4537
|
+
if (payload.columns) {
|
|
4538
|
+
sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
|
|
4539
|
+
}
|
|
4540
|
+
return `${sqlParts.join(" ")};`;
|
|
4541
|
+
}
|
|
4542
|
+
function buildDeleteDebugSql(payload) {
|
|
4543
|
+
const sqlParts = [`DELETE FROM ${quoteQualifiedIdentifier(payload.table_name)}`];
|
|
4544
|
+
const whereClauses = [];
|
|
4545
|
+
if (payload.resource_id) {
|
|
4546
|
+
whereClauses.push(`"resource_id" = ${toSqlLiteral(payload.resource_id)}`);
|
|
4547
|
+
}
|
|
4548
|
+
if (payload.conditions?.length) {
|
|
4549
|
+
whereClauses.push(...payload.conditions.map(conditionToDebugSqlClause));
|
|
4550
|
+
}
|
|
4551
|
+
if (whereClauses.length) {
|
|
4552
|
+
sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
|
|
4553
|
+
}
|
|
4554
|
+
const pagination = resolvePagination({
|
|
4555
|
+
currentPage: payload.current_page,
|
|
4556
|
+
pageSize: payload.page_size
|
|
4557
|
+
});
|
|
4558
|
+
appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
|
|
4559
|
+
if (payload.columns) {
|
|
4560
|
+
sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
|
|
4561
|
+
}
|
|
4562
|
+
return `${sqlParts.join(" ")};`;
|
|
4563
|
+
}
|
|
4564
|
+
function rpcFilterToSqlClause(filter) {
|
|
4565
|
+
const column = quoteQualifiedIdentifier(filter.column);
|
|
4566
|
+
const value = filter.value;
|
|
4567
|
+
switch (filter.operator) {
|
|
4568
|
+
case "eq":
|
|
4569
|
+
case "neq":
|
|
4570
|
+
case "gt":
|
|
4571
|
+
case "gte":
|
|
4572
|
+
case "lt":
|
|
4573
|
+
case "lte":
|
|
4574
|
+
case "like":
|
|
4575
|
+
case "ilike": {
|
|
4576
|
+
if (value === void 0 || Array.isArray(value)) {
|
|
4577
|
+
return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
|
|
4578
|
+
}
|
|
4579
|
+
const operatorMap = {
|
|
4580
|
+
eq: "=",
|
|
4581
|
+
neq: "!=",
|
|
4582
|
+
gt: ">",
|
|
4583
|
+
gte: ">=",
|
|
4584
|
+
lt: "<",
|
|
4585
|
+
lte: "<=",
|
|
4586
|
+
like: "LIKE",
|
|
4587
|
+
ilike: "ILIKE"
|
|
4588
|
+
};
|
|
4589
|
+
return `${column} ${operatorMap[filter.operator]} ${toSqlLiteral(value)}`;
|
|
4590
|
+
}
|
|
4591
|
+
case "is":
|
|
4592
|
+
if (value === null) return `${column} IS NULL`;
|
|
4593
|
+
if (value === true) return `${column} IS TRUE`;
|
|
4594
|
+
if (value === false) return `${column} IS FALSE`;
|
|
4595
|
+
return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
|
|
4596
|
+
case "in":
|
|
4597
|
+
if (!Array.isArray(value)) {
|
|
4598
|
+
return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
|
|
4599
|
+
}
|
|
4600
|
+
if (value.length === 0) return "FALSE";
|
|
4601
|
+
return `${column} IN (${value.map((item) => toSqlLiteral(item)).join(", ")})`;
|
|
4602
|
+
default:
|
|
4603
|
+
return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
|
|
4604
|
+
}
|
|
4605
|
+
}
|
|
4606
|
+
function buildRpcDebugSql(payload) {
|
|
4607
|
+
const argsEntries = payload.args ? Object.entries(payload.args) : [];
|
|
4608
|
+
const argsClause = argsEntries.map(([key, value]) => `${quoteQualifiedIdentifier(key)} => ${toSqlJsonLiteral(value)}`).join(", ");
|
|
4609
|
+
const functionRef = payload.schema ? `${quoteQualifiedIdentifier(payload.schema)}.${quoteQualifiedIdentifier(payload.function)}` : quoteQualifiedIdentifier(payload.function);
|
|
4610
|
+
const sqlParts = [
|
|
4611
|
+
`SELECT ${payload.select ? quoteSelectColumnsExpression(payload.select) : "*"} FROM ${functionRef}(${argsClause})`
|
|
4612
|
+
];
|
|
4613
|
+
if (payload.filters?.length) {
|
|
4614
|
+
sqlParts.push(`WHERE ${payload.filters.map(rpcFilterToSqlClause).join(" AND ")}`);
|
|
4615
|
+
}
|
|
4616
|
+
if (payload.order?.column) {
|
|
4617
|
+
const direction = payload.order.ascending === false ? "DESC" : "ASC";
|
|
4618
|
+
sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(payload.order.column)} ${direction}`);
|
|
4619
|
+
}
|
|
4620
|
+
if (payload.limit !== void 0) {
|
|
4621
|
+
sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(payload.limit))}`);
|
|
4622
|
+
}
|
|
4623
|
+
if (payload.offset !== void 0) {
|
|
4624
|
+
sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(payload.offset))}`);
|
|
4625
|
+
}
|
|
4626
|
+
return `${sqlParts.join(" ")};`;
|
|
4627
|
+
}
|
|
3016
4628
|
function createFilterMethods(state, addCondition, self) {
|
|
3017
4629
|
return {
|
|
3018
4630
|
eq(column, value) {
|
|
@@ -3124,7 +4736,7 @@ function createFilterMethods(state, addCondition, self) {
|
|
|
3124
4736
|
not(columnOrExpression, operator, value) {
|
|
3125
4737
|
const expression = String(columnOrExpression);
|
|
3126
4738
|
if (operator != null && value !== void 0) {
|
|
3127
|
-
addCondition("not", void 0, `${expression}.${operator}.${
|
|
4739
|
+
addCondition("not", void 0, `${expression}.${operator}.${stringifyFilterValue2(value)}`);
|
|
3128
4740
|
} else {
|
|
3129
4741
|
addCondition("not", void 0, expression);
|
|
3130
4742
|
}
|
|
@@ -3187,14 +4799,15 @@ function createRpcFilterMethods(filters, self) {
|
|
|
3187
4799
|
}
|
|
3188
4800
|
};
|
|
3189
4801
|
}
|
|
3190
|
-
function createRpcBuilder(functionName, args, baseOptions, client, formatGatewayResult) {
|
|
4802
|
+
function createRpcBuilder(functionName, args, baseOptions, client, formatGatewayResult, tracer, initialCallsite) {
|
|
3191
4803
|
const state = {
|
|
3192
4804
|
filters: []
|
|
3193
4805
|
};
|
|
3194
4806
|
let selectedColumns;
|
|
3195
4807
|
let selectedOptions;
|
|
3196
4808
|
let promise = null;
|
|
3197
|
-
const
|
|
4809
|
+
const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
|
|
4810
|
+
const executeRpc = async (columns, options, callsite) => {
|
|
3198
4811
|
const mergedOptions = mergeOptions(baseOptions, options);
|
|
3199
4812
|
const payload = {
|
|
3200
4813
|
function: functionName,
|
|
@@ -3208,14 +4821,30 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
|
|
|
3208
4821
|
offset: state.offset,
|
|
3209
4822
|
order: state.order
|
|
3210
4823
|
};
|
|
3211
|
-
const
|
|
3212
|
-
|
|
4824
|
+
const endpoint = mergedOptions?.get ? `/rpc/${functionName}` : "/gateway/rpc";
|
|
4825
|
+
const sql = buildRpcDebugSql(payload);
|
|
4826
|
+
return executeWithQueryTrace(
|
|
4827
|
+
tracer,
|
|
4828
|
+
{
|
|
4829
|
+
operation: "rpc",
|
|
4830
|
+
endpoint,
|
|
4831
|
+
functionName,
|
|
4832
|
+
sql,
|
|
4833
|
+
payload,
|
|
4834
|
+
options: mergedOptions
|
|
4835
|
+
},
|
|
4836
|
+
async () => {
|
|
4837
|
+
const response = await client.rpcGateway(payload, mergedOptions);
|
|
4838
|
+
return formatGatewayResult(response, { operation: "rpc" });
|
|
4839
|
+
},
|
|
4840
|
+
callsite
|
|
4841
|
+
);
|
|
3213
4842
|
};
|
|
3214
|
-
const run = (columns, options) => {
|
|
4843
|
+
const run = (columns, options, callsite) => {
|
|
3215
4844
|
const payloadColumns = columns ?? selectedColumns;
|
|
3216
4845
|
const payloadOptions = options ?? selectedOptions;
|
|
3217
4846
|
if (!promise) {
|
|
3218
|
-
promise = executeRpc(payloadColumns, payloadOptions);
|
|
4847
|
+
promise = executeRpc(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
|
|
3219
4848
|
}
|
|
3220
4849
|
return promise;
|
|
3221
4850
|
};
|
|
@@ -3225,10 +4854,10 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
|
|
|
3225
4854
|
select(columns = selectedColumns, options) {
|
|
3226
4855
|
selectedColumns = columns;
|
|
3227
4856
|
selectedOptions = options ?? selectedOptions;
|
|
3228
|
-
return run(columns, options);
|
|
4857
|
+
return run(columns, options, captureTraceCallsite(tracer));
|
|
3229
4858
|
},
|
|
3230
4859
|
async single(columns, options) {
|
|
3231
|
-
const result = await run(columns, options);
|
|
4860
|
+
const result = await run(columns, options, captureTraceCallsite(tracer));
|
|
3232
4861
|
return toSingleResult(result);
|
|
3233
4862
|
},
|
|
3234
4863
|
maybeSingle(columns, options) {
|
|
@@ -3263,7 +4892,7 @@ function createRpcBuilder(functionName, args, baseOptions, client, formatGateway
|
|
|
3263
4892
|
});
|
|
3264
4893
|
return builder;
|
|
3265
4894
|
}
|
|
3266
|
-
function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
4895
|
+
function createTableBuilder(tableName, client, formatGatewayResult, tracer, experimental) {
|
|
3267
4896
|
const state = {
|
|
3268
4897
|
conditions: []
|
|
3269
4898
|
};
|
|
@@ -3295,70 +4924,103 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
|
3295
4924
|
}
|
|
3296
4925
|
state.conditions.push(condition);
|
|
3297
4926
|
};
|
|
4927
|
+
const snapshotState = () => ({
|
|
4928
|
+
conditions: state.conditions.map((condition) => ({ ...condition })),
|
|
4929
|
+
limit: state.limit,
|
|
4930
|
+
offset: state.offset,
|
|
4931
|
+
order: state.order ? { ...state.order } : void 0,
|
|
4932
|
+
currentPage: state.currentPage,
|
|
4933
|
+
pageSize: state.pageSize,
|
|
4934
|
+
totalPages: state.totalPages
|
|
4935
|
+
});
|
|
3298
4936
|
const builder = {};
|
|
3299
4937
|
const filterMethods = createFilterMethods(
|
|
3300
4938
|
state,
|
|
3301
4939
|
addCondition,
|
|
3302
4940
|
builder
|
|
3303
4941
|
);
|
|
3304
|
-
const runSelect = async (columns = DEFAULT_COLUMNS, options) => {
|
|
4942
|
+
const runSelect = async (columns = DEFAULT_COLUMNS, options, executionState = snapshotState(), callsite) => {
|
|
3305
4943
|
const resolvedTableName = resolveTableNameForCall(tableName, options?.schema);
|
|
3306
|
-
const
|
|
3307
|
-
|
|
3308
|
-
(condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
|
|
3309
|
-
) ?? false;
|
|
3310
|
-
if (hasTypedEqualityComparison && !options?.head && !options?.count && conditions) {
|
|
3311
|
-
const query = buildTypedSelectQuery({
|
|
3312
|
-
tableName: resolvedTableName,
|
|
3313
|
-
columns,
|
|
3314
|
-
conditions,
|
|
3315
|
-
limit: state.limit,
|
|
3316
|
-
offset: state.offset,
|
|
3317
|
-
currentPage: state.currentPage,
|
|
3318
|
-
pageSize: state.pageSize,
|
|
3319
|
-
order: state.order
|
|
3320
|
-
});
|
|
3321
|
-
if (query) {
|
|
3322
|
-
const queryResponse = await client.queryGateway({ query }, options);
|
|
3323
|
-
return formatGatewayResult(queryResponse, { table: resolvedTableName, operation: "select" });
|
|
3324
|
-
}
|
|
3325
|
-
}
|
|
3326
|
-
const payload = {
|
|
3327
|
-
table_name: resolvedTableName,
|
|
4944
|
+
const plan = createSelectTransportPlan({
|
|
4945
|
+
tableName: resolvedTableName,
|
|
3328
4946
|
columns,
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
4947
|
+
state: executionState,
|
|
4948
|
+
options,
|
|
4949
|
+
buildTypedSelectQuery
|
|
4950
|
+
});
|
|
4951
|
+
if (plan.kind === "query") {
|
|
4952
|
+
return executeWithQueryTrace(
|
|
4953
|
+
tracer,
|
|
4954
|
+
{
|
|
4955
|
+
operation: "select",
|
|
4956
|
+
endpoint: "/gateway/query",
|
|
4957
|
+
table: resolvedTableName,
|
|
4958
|
+
sql: plan.query,
|
|
4959
|
+
payload: plan.payload,
|
|
4960
|
+
options
|
|
4961
|
+
},
|
|
4962
|
+
async () => {
|
|
4963
|
+
const queryResponse = await client.queryGateway(plan.payload, options);
|
|
4964
|
+
return formatGatewayResult(queryResponse, { table: resolvedTableName, operation: "select" });
|
|
4965
|
+
},
|
|
4966
|
+
callsite
|
|
4967
|
+
);
|
|
4968
|
+
}
|
|
4969
|
+
const sql = buildDebugSelectQuery({
|
|
4970
|
+
tableName: resolvedTableName,
|
|
4971
|
+
...plan.debug
|
|
4972
|
+
});
|
|
4973
|
+
return executeWithQueryTrace(
|
|
4974
|
+
tracer,
|
|
4975
|
+
{
|
|
4976
|
+
operation: "select",
|
|
4977
|
+
endpoint: "/gateway/fetch",
|
|
4978
|
+
table: resolvedTableName,
|
|
4979
|
+
sql,
|
|
4980
|
+
payload: plan.payload,
|
|
4981
|
+
options
|
|
4982
|
+
},
|
|
4983
|
+
async () => {
|
|
4984
|
+
const response = await client.fetchGateway(plan.payload, options);
|
|
4985
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
|
|
4986
|
+
},
|
|
4987
|
+
callsite
|
|
4988
|
+
);
|
|
3342
4989
|
};
|
|
3343
|
-
const createSelectChain = (columns, options) => {
|
|
4990
|
+
const createSelectChain = (columns, options, initialCallsite) => {
|
|
3344
4991
|
const chain = {};
|
|
4992
|
+
const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
|
|
3345
4993
|
const filterMethods2 = createFilterMethods(state, addCondition, chain);
|
|
3346
4994
|
Object.assign(chain, filterMethods2, {
|
|
3347
4995
|
async single(cols, opts) {
|
|
3348
|
-
const r = await runSelect(
|
|
4996
|
+
const r = await runSelect(
|
|
4997
|
+
cols ?? columns,
|
|
4998
|
+
opts ?? options,
|
|
4999
|
+
snapshotState(),
|
|
5000
|
+
callsiteStore.resolve(captureTraceCallsite(tracer))
|
|
5001
|
+
);
|
|
3349
5002
|
return toSingleResult(r);
|
|
3350
5003
|
},
|
|
3351
5004
|
maybeSingle(cols, opts) {
|
|
3352
5005
|
return chain.single(cols, opts);
|
|
3353
5006
|
},
|
|
3354
5007
|
then(onfulfilled, onrejected) {
|
|
3355
|
-
return runSelect(
|
|
5008
|
+
return runSelect(
|
|
5009
|
+
columns,
|
|
5010
|
+
options,
|
|
5011
|
+
snapshotState(),
|
|
5012
|
+
callsiteStore.resolve()
|
|
5013
|
+
).then(onfulfilled, onrejected);
|
|
3356
5014
|
},
|
|
3357
5015
|
catch(onrejected) {
|
|
3358
|
-
return runSelect(columns, options).catch(
|
|
5016
|
+
return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).catch(
|
|
5017
|
+
onrejected
|
|
5018
|
+
);
|
|
3359
5019
|
},
|
|
3360
5020
|
finally(onfinally) {
|
|
3361
|
-
return runSelect(columns, options).finally(
|
|
5021
|
+
return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).finally(
|
|
5022
|
+
onfinally
|
|
5023
|
+
);
|
|
3362
5024
|
}
|
|
3363
5025
|
});
|
|
3364
5026
|
return chain;
|
|
@@ -3375,11 +5037,75 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
|
3375
5037
|
return builder;
|
|
3376
5038
|
},
|
|
3377
5039
|
select(columns = DEFAULT_COLUMNS, options) {
|
|
3378
|
-
return createSelectChain(columns, options);
|
|
5040
|
+
return createSelectChain(columns, options, captureTraceCallsite(tracer));
|
|
5041
|
+
},
|
|
5042
|
+
async findMany(options) {
|
|
5043
|
+
const columns = compileSelectShape(options.select);
|
|
5044
|
+
const baseState = snapshotState();
|
|
5045
|
+
const executionState = snapshotState();
|
|
5046
|
+
const callsite = captureTraceCallsite(tracer);
|
|
5047
|
+
const compiledWhere = compileWhere(options.where);
|
|
5048
|
+
if (compiledWhere?.length) {
|
|
5049
|
+
executionState.conditions.push(...compiledWhere);
|
|
5050
|
+
}
|
|
5051
|
+
if (options.orderBy !== void 0) {
|
|
5052
|
+
executionState.order = compileOrderBy(options.orderBy);
|
|
5053
|
+
}
|
|
5054
|
+
if (options.limit !== void 0) {
|
|
5055
|
+
executionState.limit = options.limit;
|
|
5056
|
+
}
|
|
5057
|
+
if (experimental?.findManyAst && canUseFindManyAstTransport(baseState) && !selectShapeUsesRelationSchema(options.select)) {
|
|
5058
|
+
const resolvedTableName = resolveTableNameForCall(tableName, void 0);
|
|
5059
|
+
const payload = {
|
|
5060
|
+
table_name: resolvedTableName,
|
|
5061
|
+
select: options.select
|
|
5062
|
+
};
|
|
5063
|
+
if (options.where !== void 0) {
|
|
5064
|
+
payload.where = options.where;
|
|
5065
|
+
}
|
|
5066
|
+
const astOrder = toFindManyAstOrder(executionState.order);
|
|
5067
|
+
if (astOrder !== void 0) {
|
|
5068
|
+
payload.orderBy = astOrder;
|
|
5069
|
+
}
|
|
5070
|
+
if (executionState.limit !== void 0) {
|
|
5071
|
+
payload.limit = executionState.limit;
|
|
5072
|
+
}
|
|
5073
|
+
const sql = buildDebugSelectQuery({
|
|
5074
|
+
tableName: resolvedTableName,
|
|
5075
|
+
columns,
|
|
5076
|
+
conditions: executionState.conditions,
|
|
5077
|
+
limit: executionState.limit,
|
|
5078
|
+
order: executionState.order
|
|
5079
|
+
});
|
|
5080
|
+
return executeWithQueryTrace(
|
|
5081
|
+
tracer,
|
|
5082
|
+
{
|
|
5083
|
+
operation: "select",
|
|
5084
|
+
endpoint: "/gateway/fetch",
|
|
5085
|
+
table: resolvedTableName,
|
|
5086
|
+
sql,
|
|
5087
|
+
payload
|
|
5088
|
+
},
|
|
5089
|
+
async () => {
|
|
5090
|
+
const response = await client.fetchGateway(
|
|
5091
|
+
payload
|
|
5092
|
+
);
|
|
5093
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
|
|
5094
|
+
},
|
|
5095
|
+
callsite
|
|
5096
|
+
);
|
|
5097
|
+
}
|
|
5098
|
+
return runSelect(
|
|
5099
|
+
columns,
|
|
5100
|
+
void 0,
|
|
5101
|
+
executionState,
|
|
5102
|
+
callsite
|
|
5103
|
+
);
|
|
3379
5104
|
},
|
|
3380
5105
|
insert(values, options) {
|
|
5106
|
+
const mutationCallsite = captureTraceCallsite(tracer);
|
|
3381
5107
|
if (Array.isArray(values)) {
|
|
3382
|
-
const executeInsertMany = async (columns, selectOptions) => {
|
|
5108
|
+
const executeInsertMany = async (columns, selectOptions, callsite) => {
|
|
3383
5109
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
3384
5110
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
3385
5111
|
const payload = {
|
|
@@ -3392,12 +5118,27 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
|
3392
5118
|
if (mergedOptions?.defaultToNull !== void 0) {
|
|
3393
5119
|
payload.default_to_null = mergedOptions.defaultToNull;
|
|
3394
5120
|
}
|
|
3395
|
-
const
|
|
3396
|
-
return
|
|
5121
|
+
const sql = buildInsertDebugSql(payload);
|
|
5122
|
+
return executeWithQueryTrace(
|
|
5123
|
+
tracer,
|
|
5124
|
+
{
|
|
5125
|
+
operation: "insert",
|
|
5126
|
+
endpoint: "/gateway/insert",
|
|
5127
|
+
table: resolvedTableName,
|
|
5128
|
+
sql,
|
|
5129
|
+
payload,
|
|
5130
|
+
options: mergedOptions
|
|
5131
|
+
},
|
|
5132
|
+
async () => {
|
|
5133
|
+
const response = await client.insertGateway(payload, mergedOptions);
|
|
5134
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
|
|
5135
|
+
},
|
|
5136
|
+
callsite
|
|
5137
|
+
);
|
|
3397
5138
|
};
|
|
3398
|
-
return createMutationQuery(executeInsertMany);
|
|
5139
|
+
return createMutationQuery(executeInsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
|
|
3399
5140
|
}
|
|
3400
|
-
const executeInsertOne = async (columns, selectOptions) => {
|
|
5141
|
+
const executeInsertOne = async (columns, selectOptions, callsite) => {
|
|
3401
5142
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
3402
5143
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
3403
5144
|
const payload = {
|
|
@@ -3410,14 +5151,30 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
|
3410
5151
|
if (mergedOptions?.defaultToNull !== void 0) {
|
|
3411
5152
|
payload.default_to_null = mergedOptions.defaultToNull;
|
|
3412
5153
|
}
|
|
3413
|
-
const
|
|
3414
|
-
return
|
|
5154
|
+
const sql = buildInsertDebugSql(payload);
|
|
5155
|
+
return executeWithQueryTrace(
|
|
5156
|
+
tracer,
|
|
5157
|
+
{
|
|
5158
|
+
operation: "insert",
|
|
5159
|
+
endpoint: "/gateway/insert",
|
|
5160
|
+
table: resolvedTableName,
|
|
5161
|
+
sql,
|
|
5162
|
+
payload,
|
|
5163
|
+
options: mergedOptions
|
|
5164
|
+
},
|
|
5165
|
+
async () => {
|
|
5166
|
+
const response = await client.insertGateway(payload, mergedOptions);
|
|
5167
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
|
|
5168
|
+
},
|
|
5169
|
+
callsite
|
|
5170
|
+
);
|
|
3415
5171
|
};
|
|
3416
|
-
return createMutationQuery(executeInsertOne);
|
|
5172
|
+
return createMutationQuery(executeInsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
|
|
3417
5173
|
},
|
|
3418
5174
|
upsert(values, options) {
|
|
5175
|
+
const mutationCallsite = captureTraceCallsite(tracer);
|
|
3419
5176
|
if (Array.isArray(values)) {
|
|
3420
|
-
const executeUpsertMany = async (columns, selectOptions) => {
|
|
5177
|
+
const executeUpsertMany = async (columns, selectOptions, callsite) => {
|
|
3421
5178
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
3422
5179
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
3423
5180
|
const payload = {
|
|
@@ -3432,12 +5189,27 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
|
3432
5189
|
if (mergedOptions?.defaultToNull !== void 0) {
|
|
3433
5190
|
payload.default_to_null = mergedOptions.defaultToNull;
|
|
3434
5191
|
}
|
|
3435
|
-
const
|
|
3436
|
-
return
|
|
5192
|
+
const sql = buildInsertDebugSql(payload);
|
|
5193
|
+
return executeWithQueryTrace(
|
|
5194
|
+
tracer,
|
|
5195
|
+
{
|
|
5196
|
+
operation: "upsert",
|
|
5197
|
+
endpoint: "/gateway/insert",
|
|
5198
|
+
table: resolvedTableName,
|
|
5199
|
+
sql,
|
|
5200
|
+
payload,
|
|
5201
|
+
options: mergedOptions
|
|
5202
|
+
},
|
|
5203
|
+
async () => {
|
|
5204
|
+
const response = await client.insertGateway(payload, mergedOptions);
|
|
5205
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
|
|
5206
|
+
},
|
|
5207
|
+
callsite
|
|
5208
|
+
);
|
|
3437
5209
|
};
|
|
3438
|
-
return createMutationQuery(executeUpsertMany);
|
|
5210
|
+
return createMutationQuery(executeUpsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
|
|
3439
5211
|
}
|
|
3440
|
-
const executeUpsertOne = async (columns, selectOptions) => {
|
|
5212
|
+
const executeUpsertOne = async (columns, selectOptions, callsite) => {
|
|
3441
5213
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
3442
5214
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
3443
5215
|
const payload = {
|
|
@@ -3452,13 +5224,29 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
|
3452
5224
|
if (mergedOptions?.defaultToNull !== void 0) {
|
|
3453
5225
|
payload.default_to_null = mergedOptions.defaultToNull;
|
|
3454
5226
|
}
|
|
3455
|
-
const
|
|
3456
|
-
return
|
|
5227
|
+
const sql = buildInsertDebugSql(payload);
|
|
5228
|
+
return executeWithQueryTrace(
|
|
5229
|
+
tracer,
|
|
5230
|
+
{
|
|
5231
|
+
operation: "upsert",
|
|
5232
|
+
endpoint: "/gateway/insert",
|
|
5233
|
+
table: resolvedTableName,
|
|
5234
|
+
sql,
|
|
5235
|
+
payload,
|
|
5236
|
+
options: mergedOptions
|
|
5237
|
+
},
|
|
5238
|
+
async () => {
|
|
5239
|
+
const response = await client.insertGateway(payload, mergedOptions);
|
|
5240
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
|
|
5241
|
+
},
|
|
5242
|
+
callsite
|
|
5243
|
+
);
|
|
3457
5244
|
};
|
|
3458
|
-
return createMutationQuery(executeUpsertOne);
|
|
5245
|
+
return createMutationQuery(executeUpsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
|
|
3459
5246
|
},
|
|
3460
5247
|
update(values, options) {
|
|
3461
|
-
const
|
|
5248
|
+
const mutationCallsite = captureTraceCallsite(tracer);
|
|
5249
|
+
const executeUpdate = async (columns, selectOptions, callsite) => {
|
|
3462
5250
|
const filters = state.conditions.length ? [...state.conditions] : void 0;
|
|
3463
5251
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
3464
5252
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
@@ -3473,10 +5261,25 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
|
3473
5261
|
if (state.pageSize !== void 0) payload.page_size = state.pageSize;
|
|
3474
5262
|
if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
|
|
3475
5263
|
if (columns) payload.columns = columns;
|
|
3476
|
-
const
|
|
3477
|
-
return
|
|
5264
|
+
const sql = buildUpdateDebugSql(payload);
|
|
5265
|
+
return executeWithQueryTrace(
|
|
5266
|
+
tracer,
|
|
5267
|
+
{
|
|
5268
|
+
operation: "update",
|
|
5269
|
+
endpoint: "/gateway/update",
|
|
5270
|
+
table: resolvedTableName,
|
|
5271
|
+
sql,
|
|
5272
|
+
payload,
|
|
5273
|
+
options: mergedOptions
|
|
5274
|
+
},
|
|
5275
|
+
async () => {
|
|
5276
|
+
const response = await client.updateGateway(payload, mergedOptions);
|
|
5277
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "update" });
|
|
5278
|
+
},
|
|
5279
|
+
callsite
|
|
5280
|
+
);
|
|
3478
5281
|
};
|
|
3479
|
-
const mutation = createMutationQuery(executeUpdate, null);
|
|
5282
|
+
const mutation = createMutationQuery(executeUpdate, null, tracer, mutationCallsite);
|
|
3480
5283
|
const updateChain = {};
|
|
3481
5284
|
const filterMethods2 = createFilterMethods(state, addCondition, updateChain);
|
|
3482
5285
|
Object.assign(updateChain, filterMethods2, mutation);
|
|
@@ -3488,7 +5291,8 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
|
3488
5291
|
if (!resourceId && !filters?.length) {
|
|
3489
5292
|
throw new Error('delete requires a resource_id either via eq("resource_id", ...) or options.resourceId');
|
|
3490
5293
|
}
|
|
3491
|
-
const
|
|
5294
|
+
const mutationCallsite = captureTraceCallsite(tracer);
|
|
5295
|
+
const executeDelete = async (columns, selectOptions, callsite) => {
|
|
3492
5296
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
3493
5297
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
3494
5298
|
const payload = {
|
|
@@ -3501,13 +5305,33 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
|
3501
5305
|
if (state.pageSize !== void 0) payload.page_size = state.pageSize;
|
|
3502
5306
|
if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
|
|
3503
5307
|
if (columns) payload.columns = columns;
|
|
3504
|
-
const
|
|
3505
|
-
return
|
|
5308
|
+
const sql = buildDeleteDebugSql(payload);
|
|
5309
|
+
return executeWithQueryTrace(
|
|
5310
|
+
tracer,
|
|
5311
|
+
{
|
|
5312
|
+
operation: "delete",
|
|
5313
|
+
endpoint: "/gateway/delete",
|
|
5314
|
+
table: resolvedTableName,
|
|
5315
|
+
sql,
|
|
5316
|
+
payload,
|
|
5317
|
+
options: mergedOptions
|
|
5318
|
+
},
|
|
5319
|
+
async () => {
|
|
5320
|
+
const response = await client.deleteGateway(payload, mergedOptions);
|
|
5321
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "delete" });
|
|
5322
|
+
},
|
|
5323
|
+
callsite
|
|
5324
|
+
);
|
|
3506
5325
|
};
|
|
3507
|
-
return createMutationQuery(executeDelete, null);
|
|
5326
|
+
return createMutationQuery(executeDelete, null, tracer, mutationCallsite);
|
|
3508
5327
|
},
|
|
3509
5328
|
async single(columns, options) {
|
|
3510
|
-
const response = await
|
|
5329
|
+
const response = await runSelect(
|
|
5330
|
+
columns ?? DEFAULT_COLUMNS,
|
|
5331
|
+
options,
|
|
5332
|
+
snapshotState(),
|
|
5333
|
+
captureTraceCallsite(tracer)
|
|
5334
|
+
);
|
|
3511
5335
|
return toSingleResult(response);
|
|
3512
5336
|
},
|
|
3513
5337
|
async maybeSingle(columns, options) {
|
|
@@ -3516,14 +5340,29 @@ function createTableBuilder(tableName, client, formatGatewayResult) {
|
|
|
3516
5340
|
});
|
|
3517
5341
|
return builder;
|
|
3518
5342
|
}
|
|
3519
|
-
function createQueryBuilder(client, formatGatewayResult) {
|
|
5343
|
+
function createQueryBuilder(client, formatGatewayResult, tracer) {
|
|
3520
5344
|
return async function query(query, options) {
|
|
3521
5345
|
const normalizedQuery = query.trim();
|
|
3522
5346
|
if (!normalizedQuery) {
|
|
3523
5347
|
throw new Error("query requires a non-empty string");
|
|
3524
5348
|
}
|
|
3525
|
-
const
|
|
3526
|
-
|
|
5349
|
+
const payload = { query: normalizedQuery };
|
|
5350
|
+
const callsite = captureTraceCallsite(tracer);
|
|
5351
|
+
return executeWithQueryTrace(
|
|
5352
|
+
tracer,
|
|
5353
|
+
{
|
|
5354
|
+
operation: "query",
|
|
5355
|
+
endpoint: "/gateway/query",
|
|
5356
|
+
sql: normalizedQuery,
|
|
5357
|
+
payload,
|
|
5358
|
+
options
|
|
5359
|
+
},
|
|
5360
|
+
async () => {
|
|
5361
|
+
const response = await client.queryGateway(payload, options);
|
|
5362
|
+
return formatGatewayResult(response, { operation: "query" });
|
|
5363
|
+
},
|
|
5364
|
+
callsite
|
|
5365
|
+
);
|
|
3527
5366
|
};
|
|
3528
5367
|
}
|
|
3529
5368
|
function createClientFromConfig(config) {
|
|
@@ -3534,9 +5373,16 @@ function createClientFromConfig(config) {
|
|
|
3534
5373
|
backend: config.backend,
|
|
3535
5374
|
headers: config.headers
|
|
3536
5375
|
});
|
|
3537
|
-
const formatGatewayResult = createResultFormatter(
|
|
5376
|
+
const formatGatewayResult = createResultFormatter();
|
|
5377
|
+
const queryTracer = createQueryTracer(config.experimental);
|
|
3538
5378
|
const auth = createAuthClient(config.auth);
|
|
3539
|
-
const from = (table) => createTableBuilder(
|
|
5379
|
+
const from = (table, options) => createTableBuilder(
|
|
5380
|
+
resolveTableNameForCall(table, options?.schema),
|
|
5381
|
+
gateway,
|
|
5382
|
+
formatGatewayResult,
|
|
5383
|
+
queryTracer,
|
|
5384
|
+
config.experimental
|
|
5385
|
+
);
|
|
3540
5386
|
const rpc = (fn, args, options) => {
|
|
3541
5387
|
const normalizedFn = fn.trim();
|
|
3542
5388
|
if (!normalizedFn) {
|
|
@@ -3547,16 +5393,19 @@ function createClientFromConfig(config) {
|
|
|
3547
5393
|
args,
|
|
3548
5394
|
options,
|
|
3549
5395
|
gateway,
|
|
3550
|
-
formatGatewayResult
|
|
5396
|
+
formatGatewayResult,
|
|
5397
|
+
queryTracer,
|
|
5398
|
+
captureTraceCallsite(queryTracer)
|
|
3551
5399
|
);
|
|
3552
5400
|
};
|
|
3553
|
-
const query = createQueryBuilder(gateway, formatGatewayResult);
|
|
5401
|
+
const query = createQueryBuilder(gateway, formatGatewayResult, queryTracer);
|
|
3554
5402
|
const db = createDbModule({ from, rpc, query });
|
|
3555
5403
|
return {
|
|
3556
5404
|
from,
|
|
3557
5405
|
db,
|
|
3558
5406
|
rpc,
|
|
3559
5407
|
query,
|
|
5408
|
+
verifyConnection: gateway.verifyConnection,
|
|
3560
5409
|
auth: auth.auth
|
|
3561
5410
|
};
|
|
3562
5411
|
}
|
|
@@ -4020,7 +5869,7 @@ var AthenaGatewayCatalogClient = class {
|
|
|
4020
5869
|
async queryRows(query) {
|
|
4021
5870
|
const result = await this.client.query(query);
|
|
4022
5871
|
if (result.error || result.status < 200 || result.status >= 300) {
|
|
4023
|
-
throw new Error(result.error ?? `Gateway query failed with status ${result.status}`);
|
|
5872
|
+
throw new Error(result.error?.message ?? `Gateway query failed with status ${result.status}`);
|
|
4024
5873
|
}
|
|
4025
5874
|
return result.data ?? [];
|
|
4026
5875
|
}
|
|
@@ -4117,10 +5966,24 @@ function resolveGeneratorProvider(providerConfig, experimentalFlags) {
|
|
|
4117
5966
|
}
|
|
4118
5967
|
|
|
4119
5968
|
// src/generator/pipeline.ts
|
|
5969
|
+
function canOverwriteArtifact(file) {
|
|
5970
|
+
return file.kind === "model" || file.kind === "schema";
|
|
5971
|
+
}
|
|
5972
|
+
async function fileExists(path) {
|
|
5973
|
+
try {
|
|
5974
|
+
await promises.stat(path);
|
|
5975
|
+
return true;
|
|
5976
|
+
} catch {
|
|
5977
|
+
return false;
|
|
5978
|
+
}
|
|
5979
|
+
}
|
|
4120
5980
|
async function writeArtifacts(files, cwd) {
|
|
4121
5981
|
const writtenFiles = [];
|
|
4122
5982
|
for (const file of files) {
|
|
4123
5983
|
const absolutePath = path.resolve(cwd, file.path);
|
|
5984
|
+
if (!canOverwriteArtifact(file) && await fileExists(absolutePath)) {
|
|
5985
|
+
continue;
|
|
5986
|
+
}
|
|
4124
5987
|
await promises.mkdir(path.dirname(absolutePath), { recursive: true });
|
|
4125
5988
|
await promises.writeFile(absolutePath, file.content, "utf8");
|
|
4126
5989
|
writtenFiles.push(file.path);
|