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