@xylex-group/athena 2.0.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -95
- package/dist/browser.cjs +4791 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +25 -0
- package/dist/browser.d.ts +25 -0
- package/dist/browser.js +4745 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli/index.cjs +2087 -220
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +3 -2
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.js +2089 -222
- package/dist/cli/index.js.map +1 -1
- package/dist/cookies.cjs +890 -0
- package/dist/cookies.cjs.map +1 -0
- package/dist/cookies.d.cts +174 -0
- package/dist/cookies.d.ts +174 -0
- package/dist/cookies.js +869 -0
- package/dist/cookies.js.map +1 -0
- package/dist/index.cjs +3046 -1200
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -408
- package/dist/index.d.ts +8 -408
- package/dist/index.js +3045 -1202
- package/dist/index.js.map +1 -1
- package/dist/{model-form-CVOtC8jq.d.ts → model-form-BpDXlbxb.d.ts} +97 -2
- package/dist/{model-form-hXkvHS_3.d.cts → model-form-hoE2jHIi.d.cts} +97 -2
- package/dist/pipeline-BNIw8pDQ.d.ts +8 -0
- package/dist/pipeline-DNIpEsN8.d.cts +8 -0
- package/dist/react-email-BvyCZnfW.d.cts +610 -0
- package/dist/react-email-qPA1wjFV.d.ts +610 -0
- package/dist/react.cjs +30 -9
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +4 -4
- package/dist/react.d.ts +4 -4
- package/dist/react.js +30 -9
- package/dist/react.js.map +1 -1
- package/dist/{types-BnzoaNRC.d.cts → types-A5e97acl.d.cts} +2 -1
- package/dist/{types-BnzoaNRC.d.ts → types-A5e97acl.d.ts} +2 -1
- package/dist/{pipeline-CQgV-Yfo.d.ts → types-BnD22-vb.d.ts} +2 -7
- package/dist/{pipeline-C-cN0ACi.d.cts → types-bDlr4u7p.d.cts} +2 -7
- package/dist/utils.cjs +153 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.cts +23 -0
- package/dist/utils.d.ts +23 -0
- package/dist/utils.js +146 -0
- package/dist/utils.js.map +1 -0
- package/package.json +87 -9
package/dist/cli/index.cjs
CHANGED
|
@@ -4,10 +4,14 @@ var promises = require('fs/promises');
|
|
|
4
4
|
var path = require('path');
|
|
5
5
|
var fs = require('fs');
|
|
6
6
|
var url = require('url');
|
|
7
|
-
var pg = require('pg');
|
|
8
7
|
|
|
9
8
|
// src/generator/pipeline.ts
|
|
10
9
|
|
|
10
|
+
// src/utils/slugify.ts
|
|
11
|
+
function slugify(input) {
|
|
12
|
+
return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
// src/generator/naming.ts
|
|
12
16
|
var IDENTIFIER_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
13
17
|
var RESERVED_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
@@ -103,7 +107,7 @@ function applyNamingStyle(input, style) {
|
|
|
103
107
|
case "snake":
|
|
104
108
|
return words.map((word) => word.toLowerCase()).join("_");
|
|
105
109
|
case "kebab":
|
|
106
|
-
return words.
|
|
110
|
+
return slugify(words.join("-"));
|
|
107
111
|
default:
|
|
108
112
|
return input;
|
|
109
113
|
}
|
|
@@ -149,9 +153,13 @@ function resolvePlaceholderMap(baseTokens, outputConfig) {
|
|
|
149
153
|
const resolved = {
|
|
150
154
|
...baseTokens
|
|
151
155
|
};
|
|
156
|
+
const reservedTokenKeys = new Set(Object.keys(baseTokens));
|
|
152
157
|
const entries = Object.entries(outputConfig.placeholderMap ?? {});
|
|
153
158
|
for (let index = 0; index < entries.length; index += 1) {
|
|
154
159
|
const [key, value] = entries[index];
|
|
160
|
+
if (reservedTokenKeys.has(key)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
155
163
|
let current = value;
|
|
156
164
|
for (let depth = 0; depth < 8; depth += 1) {
|
|
157
165
|
const next = renderTemplate(current, resolved);
|
|
@@ -348,8 +356,11 @@ var AthenaGatewayError = class _AthenaGatewayError extends Error {
|
|
|
348
356
|
});
|
|
349
357
|
}
|
|
350
358
|
};
|
|
359
|
+
function isAthenaGatewayError(error) {
|
|
360
|
+
return error instanceof AthenaGatewayError;
|
|
361
|
+
}
|
|
351
362
|
|
|
352
|
-
// src/
|
|
363
|
+
// src/utils/parse-boolean-flag.ts
|
|
353
364
|
function parseBooleanFlag(rawValue, fallback) {
|
|
354
365
|
if (!rawValue) return fallback;
|
|
355
366
|
const normalized = rawValue.trim().toLowerCase();
|
|
@@ -362,6 +373,293 @@ function parseBooleanFlag(rawValue, fallback) {
|
|
|
362
373
|
return fallback;
|
|
363
374
|
}
|
|
364
375
|
|
|
376
|
+
// src/auxiliaries.ts
|
|
377
|
+
var AthenaErrorCode = {
|
|
378
|
+
UniqueViolation: "UNIQUE_VIOLATION",
|
|
379
|
+
NotFound: "NOT_FOUND",
|
|
380
|
+
ValidationFailed: "VALIDATION_FAILED",
|
|
381
|
+
AuthUnauthorized: "AUTH_UNAUTHORIZED",
|
|
382
|
+
AuthForbidden: "AUTH_FORBIDDEN",
|
|
383
|
+
RateLimited: "RATE_LIMITED",
|
|
384
|
+
NetworkUnavailable: "NETWORK_UNAVAILABLE",
|
|
385
|
+
TransientFailure: "TRANSIENT_FAILURE",
|
|
386
|
+
HttpFailure: "HTTP_FAILURE",
|
|
387
|
+
Unknown: "UNKNOWN"
|
|
388
|
+
};
|
|
389
|
+
var AthenaErrorCategory = {
|
|
390
|
+
Transport: "transport",
|
|
391
|
+
Client: "client",
|
|
392
|
+
Server: "server",
|
|
393
|
+
Database: "database",
|
|
394
|
+
Unknown: "unknown"
|
|
395
|
+
};
|
|
396
|
+
function parseBooleanFlag2(rawValue, fallback) {
|
|
397
|
+
return parseBooleanFlag(rawValue, fallback);
|
|
398
|
+
}
|
|
399
|
+
function isRecord(value) {
|
|
400
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
401
|
+
}
|
|
402
|
+
function firstNonEmptyString(...values) {
|
|
403
|
+
for (const value of values) {
|
|
404
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
405
|
+
return value.trim();
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return void 0;
|
|
409
|
+
}
|
|
410
|
+
function messageFromUnknownError(error) {
|
|
411
|
+
if (typeof error === "string" && error.trim().length > 0) {
|
|
412
|
+
return error.trim();
|
|
413
|
+
}
|
|
414
|
+
if (error instanceof Error && error.message.trim().length > 0) {
|
|
415
|
+
return error.message.trim();
|
|
416
|
+
}
|
|
417
|
+
if (!isRecord(error)) {
|
|
418
|
+
return void 0;
|
|
419
|
+
}
|
|
420
|
+
return firstNonEmptyString(error.message, error.error, error.details);
|
|
421
|
+
}
|
|
422
|
+
function gatewayCodeFromUnknownError(error) {
|
|
423
|
+
if (!isRecord(error) || typeof error.gatewayCode !== "string") {
|
|
424
|
+
return void 0;
|
|
425
|
+
}
|
|
426
|
+
return error.gatewayCode;
|
|
427
|
+
}
|
|
428
|
+
function isAthenaResultErrorLike(value) {
|
|
429
|
+
return isRecord(value) && typeof value.message === "string" && (value.athenaCode === void 0 || isAthenaErrorCode(value.athenaCode)) && (value.kind === void 0 || isAthenaErrorKind(value.kind)) && (value.category === void 0 || isAthenaErrorCategory(value.category)) && (value.retryable === void 0 || typeof value.retryable === "boolean") && (value.status === void 0 || typeof value.status === "number");
|
|
430
|
+
}
|
|
431
|
+
function isAthenaErrorKind(value) {
|
|
432
|
+
return value === "unique_violation" || value === "not_found" || value === "validation" || value === "auth" || value === "rate_limit" || value === "transient" || value === "unknown";
|
|
433
|
+
}
|
|
434
|
+
function isAthenaErrorCode(value) {
|
|
435
|
+
return value === "UNIQUE_VIOLATION" || value === "NOT_FOUND" || value === "VALIDATION_FAILED" || value === "AUTH_UNAUTHORIZED" || value === "AUTH_FORBIDDEN" || value === "RATE_LIMITED" || value === "NETWORK_UNAVAILABLE" || value === "TRANSIENT_FAILURE" || value === "HTTP_FAILURE" || value === "UNKNOWN";
|
|
436
|
+
}
|
|
437
|
+
function isAthenaErrorCategory(value) {
|
|
438
|
+
return value === "transport" || value === "client" || value === "server" || value === "database" || value === "unknown";
|
|
439
|
+
}
|
|
440
|
+
function isNormalizedAthenaError(value) {
|
|
441
|
+
return isRecord(value) && isAthenaErrorKind(value.kind) && isAthenaErrorCode(value.code) && isAthenaErrorCategory(value.category) && typeof value.retryable === "boolean" && typeof value.message === "string" && "raw" in value;
|
|
442
|
+
}
|
|
443
|
+
function withContextOverrides(normalized, context) {
|
|
444
|
+
if (!context?.table && !context?.operation) {
|
|
445
|
+
return normalized;
|
|
446
|
+
}
|
|
447
|
+
return {
|
|
448
|
+
...normalized,
|
|
449
|
+
table: context.table ?? normalized.table,
|
|
450
|
+
operation: context.operation ?? normalized.operation
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
function resolveAttachedNormalizedError(resultOrError) {
|
|
454
|
+
if (!isRecord(resultOrError)) return void 0;
|
|
455
|
+
if (!("__athenaNormalizedError" in resultOrError)) return void 0;
|
|
456
|
+
const candidate = resultOrError.__athenaNormalizedError;
|
|
457
|
+
return isNormalizedAthenaError(candidate) ? candidate : void 0;
|
|
458
|
+
}
|
|
459
|
+
function isAthenaResultLike(value) {
|
|
460
|
+
return isRecord(value) && "status" in value && "error" in value && "data" in value && typeof value.status === "number";
|
|
461
|
+
}
|
|
462
|
+
function operationFromDetails(details) {
|
|
463
|
+
if (!details?.endpoint) return void 0;
|
|
464
|
+
if (details.endpoint === "/gateway/fetch" || details.endpoint === "/gateway/query") return "select";
|
|
465
|
+
if (details.endpoint === "/gateway/insert") return "insert";
|
|
466
|
+
if (details.endpoint === "/gateway/update") return "update";
|
|
467
|
+
if (details.endpoint === "/gateway/delete") return "delete";
|
|
468
|
+
if (details.endpoint === "/gateway/rpc" || details.endpoint.startsWith("/rpc/")) return "rpc";
|
|
469
|
+
return void 0;
|
|
470
|
+
}
|
|
471
|
+
function matchRegex(input, patterns) {
|
|
472
|
+
for (const pattern of patterns) {
|
|
473
|
+
const match = pattern.exec(input);
|
|
474
|
+
if (match?.[1]) return match[1];
|
|
475
|
+
}
|
|
476
|
+
return void 0;
|
|
477
|
+
}
|
|
478
|
+
function extractConstraint(message) {
|
|
479
|
+
return matchRegex(message, [
|
|
480
|
+
/unique constraint\s+["'`]([^"'`]+)["'`]/i,
|
|
481
|
+
/constraint\s+["'`]([^"'`]+)["'`]/i
|
|
482
|
+
]);
|
|
483
|
+
}
|
|
484
|
+
function extractTable(message) {
|
|
485
|
+
return matchRegex(message, [
|
|
486
|
+
/(?:table|relation)\s+["'`]([^"'`]+)["'`]/i,
|
|
487
|
+
/on\s+table\s+([a-zA-Z0-9_.]+)/i
|
|
488
|
+
]);
|
|
489
|
+
}
|
|
490
|
+
function classifyKind(status, code, message) {
|
|
491
|
+
const lower = message.toLowerCase();
|
|
492
|
+
const hasUniquePattern = lower.includes("unique constraint") || lower.includes("duplicate key") || lower.includes("already exists") || lower.includes("duplicate");
|
|
493
|
+
const hasNotFoundPattern = lower.includes("not found") || lower.includes("no rows");
|
|
494
|
+
const hasValidationPattern = lower.includes("validation") || lower.includes("invalid") || lower.includes("malformed");
|
|
495
|
+
const hasAuthPattern = lower.includes("unauthorized") || lower.includes("forbidden") || lower.includes("auth");
|
|
496
|
+
const hasRateLimitPattern = lower.includes("rate limit") || lower.includes("too many requests");
|
|
497
|
+
const hasTransientPattern = lower.includes("timeout") || lower.includes("temporar") || lower.includes("connection reset") || lower.includes("socket") || lower.includes("deadlock");
|
|
498
|
+
if (status === 409 || hasUniquePattern) return "unique_violation";
|
|
499
|
+
if (status === 404 || hasNotFoundPattern) return "not_found";
|
|
500
|
+
if (status === 401 || status === 403 || hasAuthPattern) return "auth";
|
|
501
|
+
if (status === 429 || hasRateLimitPattern) return "rate_limit";
|
|
502
|
+
if (status === 400 || status === 422 || hasValidationPattern) return "validation";
|
|
503
|
+
if (code === "NETWORK_ERROR" || status === 0 || status !== void 0 && status >= 500 || hasTransientPattern) {
|
|
504
|
+
return "transient";
|
|
505
|
+
}
|
|
506
|
+
return "unknown";
|
|
507
|
+
}
|
|
508
|
+
function toAthenaErrorCode(kind, status, gatewayCode) {
|
|
509
|
+
if (gatewayCode === "NETWORK_ERROR" || kind === "transient" && status === 0) {
|
|
510
|
+
return AthenaErrorCode.NetworkUnavailable;
|
|
511
|
+
}
|
|
512
|
+
switch (kind) {
|
|
513
|
+
case "unique_violation":
|
|
514
|
+
return AthenaErrorCode.UniqueViolation;
|
|
515
|
+
case "not_found":
|
|
516
|
+
return AthenaErrorCode.NotFound;
|
|
517
|
+
case "validation":
|
|
518
|
+
return AthenaErrorCode.ValidationFailed;
|
|
519
|
+
case "rate_limit":
|
|
520
|
+
return AthenaErrorCode.RateLimited;
|
|
521
|
+
case "auth":
|
|
522
|
+
if (status === 403) {
|
|
523
|
+
return AthenaErrorCode.AuthForbidden;
|
|
524
|
+
}
|
|
525
|
+
return AthenaErrorCode.AuthUnauthorized;
|
|
526
|
+
case "transient":
|
|
527
|
+
return status !== void 0 && status >= 500 ? AthenaErrorCode.HttpFailure : AthenaErrorCode.TransientFailure;
|
|
528
|
+
case "unknown":
|
|
529
|
+
default:
|
|
530
|
+
return status !== void 0 && status >= 400 ? AthenaErrorCode.HttpFailure : AthenaErrorCode.Unknown;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function toAthenaErrorCategory(kind, status) {
|
|
534
|
+
if (kind === "transient" && (status === 0 || status === void 0)) {
|
|
535
|
+
return AthenaErrorCategory.Transport;
|
|
536
|
+
}
|
|
537
|
+
if (kind === "unique_violation") return AthenaErrorCategory.Database;
|
|
538
|
+
if (kind === "validation" || kind === "auth" || kind === "not_found") return AthenaErrorCategory.Client;
|
|
539
|
+
if (kind === "rate_limit" || kind === "transient") return AthenaErrorCategory.Server;
|
|
540
|
+
return AthenaErrorCategory.Unknown;
|
|
541
|
+
}
|
|
542
|
+
function isRetryable(kind, status) {
|
|
543
|
+
if (kind === "rate_limit" || kind === "transient") return true;
|
|
544
|
+
return status !== void 0 && status >= 500;
|
|
545
|
+
}
|
|
546
|
+
function normalizeAthenaError(resultOrError, context) {
|
|
547
|
+
const attached = resolveAttachedNormalizedError(resultOrError);
|
|
548
|
+
if (attached) {
|
|
549
|
+
return withContextOverrides(attached, context);
|
|
550
|
+
}
|
|
551
|
+
if (isAthenaResultLike(resultOrError)) {
|
|
552
|
+
if (isAthenaResultErrorLike(resultOrError.error)) {
|
|
553
|
+
return {
|
|
554
|
+
kind: resultOrError.error.kind ?? classifyKind(resultOrError.status, gatewayCodeFromUnknownError(resultOrError.error), resultOrError.error.message),
|
|
555
|
+
code: resultOrError.error.athenaCode ?? toAthenaErrorCode(
|
|
556
|
+
resultOrError.error.kind ?? classifyKind(
|
|
557
|
+
resultOrError.status,
|
|
558
|
+
gatewayCodeFromUnknownError(resultOrError.error),
|
|
559
|
+
resultOrError.error.message
|
|
560
|
+
),
|
|
561
|
+
resultOrError.error.status ?? resultOrError.status,
|
|
562
|
+
gatewayCodeFromUnknownError(resultOrError.error)
|
|
563
|
+
),
|
|
564
|
+
category: resultOrError.error.category ?? toAthenaErrorCategory(
|
|
565
|
+
resultOrError.error.kind ?? classifyKind(
|
|
566
|
+
resultOrError.status,
|
|
567
|
+
gatewayCodeFromUnknownError(resultOrError.error),
|
|
568
|
+
resultOrError.error.message
|
|
569
|
+
),
|
|
570
|
+
resultOrError.error.status ?? resultOrError.status
|
|
571
|
+
),
|
|
572
|
+
retryable: resultOrError.error.retryable ?? isRetryable(
|
|
573
|
+
resultOrError.error.kind ?? classifyKind(
|
|
574
|
+
resultOrError.status,
|
|
575
|
+
gatewayCodeFromUnknownError(resultOrError.error),
|
|
576
|
+
resultOrError.error.message
|
|
577
|
+
),
|
|
578
|
+
resultOrError.error.status ?? resultOrError.status
|
|
579
|
+
),
|
|
580
|
+
status: resultOrError.error.status ?? resultOrError.status,
|
|
581
|
+
constraint: resultOrError.error.constraint,
|
|
582
|
+
table: context?.table ?? resultOrError.error.table,
|
|
583
|
+
operation: context?.operation ?? resultOrError.error.operation,
|
|
584
|
+
message: resultOrError.error.message,
|
|
585
|
+
raw: resultOrError.error.raw ?? resultOrError.raw
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
const details = resultOrError.errorDetails;
|
|
589
|
+
const message2 = messageFromUnknownError(resultOrError.error) ?? details?.message ?? `Athena ${context?.operation ?? operationFromDetails(details) ?? "request"} failed`;
|
|
590
|
+
const operation = context?.operation ?? operationFromDetails(details);
|
|
591
|
+
const table = context?.table ?? extractTable(message2);
|
|
592
|
+
const constraint = extractConstraint(message2);
|
|
593
|
+
const gatewayCode = details?.code ?? gatewayCodeFromUnknownError(resultOrError.error);
|
|
594
|
+
const kind2 = classifyKind(resultOrError.status, gatewayCode, message2);
|
|
595
|
+
const code = toAthenaErrorCode(kind2, resultOrError.status, gatewayCode);
|
|
596
|
+
const category = toAthenaErrorCategory(kind2, resultOrError.status);
|
|
597
|
+
return {
|
|
598
|
+
kind: kind2,
|
|
599
|
+
code,
|
|
600
|
+
category,
|
|
601
|
+
retryable: isRetryable(kind2, resultOrError.status),
|
|
602
|
+
status: resultOrError.status,
|
|
603
|
+
constraint,
|
|
604
|
+
table,
|
|
605
|
+
operation,
|
|
606
|
+
message: message2,
|
|
607
|
+
raw: resultOrError.raw
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
if (isAthenaGatewayError(resultOrError)) {
|
|
611
|
+
const details = resultOrError.toDetails();
|
|
612
|
+
const operation = context?.operation ?? operationFromDetails(details);
|
|
613
|
+
const table = context?.table ?? extractTable(resultOrError.message);
|
|
614
|
+
const constraint = extractConstraint(resultOrError.message);
|
|
615
|
+
const kind2 = classifyKind(resultOrError.status, resultOrError.code, resultOrError.message);
|
|
616
|
+
const code = toAthenaErrorCode(kind2, resultOrError.status, resultOrError.code);
|
|
617
|
+
const category = toAthenaErrorCategory(kind2, resultOrError.status);
|
|
618
|
+
return {
|
|
619
|
+
kind: kind2,
|
|
620
|
+
code,
|
|
621
|
+
category,
|
|
622
|
+
retryable: isRetryable(kind2, resultOrError.status),
|
|
623
|
+
status: resultOrError.status,
|
|
624
|
+
constraint,
|
|
625
|
+
table,
|
|
626
|
+
operation,
|
|
627
|
+
message: resultOrError.message,
|
|
628
|
+
raw: resultOrError
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
if (resultOrError instanceof Error) {
|
|
632
|
+
const maybeStatus = isRecord(resultOrError) && typeof resultOrError.status === "number" ? resultOrError.status : void 0;
|
|
633
|
+
const kind2 = classifyKind(maybeStatus, void 0, resultOrError.message);
|
|
634
|
+
return {
|
|
635
|
+
kind: kind2,
|
|
636
|
+
code: toAthenaErrorCode(kind2, maybeStatus),
|
|
637
|
+
category: toAthenaErrorCategory(kind2, maybeStatus),
|
|
638
|
+
retryable: isRetryable(kind2, maybeStatus),
|
|
639
|
+
status: maybeStatus,
|
|
640
|
+
constraint: extractConstraint(resultOrError.message),
|
|
641
|
+
table: context?.table ?? extractTable(resultOrError.message),
|
|
642
|
+
operation: context?.operation,
|
|
643
|
+
message: resultOrError.message,
|
|
644
|
+
raw: resultOrError
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
const message = typeof resultOrError === "string" ? resultOrError : "Unknown Athena error";
|
|
648
|
+
const kind = classifyKind(void 0, void 0, message);
|
|
649
|
+
return {
|
|
650
|
+
kind,
|
|
651
|
+
code: toAthenaErrorCode(kind, void 0),
|
|
652
|
+
category: toAthenaErrorCategory(kind, void 0),
|
|
653
|
+
retryable: isRetryable(kind, void 0),
|
|
654
|
+
status: void 0,
|
|
655
|
+
constraint: extractConstraint(message),
|
|
656
|
+
table: context?.table ?? extractTable(message),
|
|
657
|
+
operation: context?.operation,
|
|
658
|
+
message,
|
|
659
|
+
raw: resultOrError
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
|
|
365
663
|
// src/generator/schema-selection.ts
|
|
366
664
|
var DEFAULT_POSTGRES_SCHEMAS = ["public"];
|
|
367
665
|
function collectSchemaNames(input) {
|
|
@@ -392,6 +690,7 @@ function resolveProviderSchemas(providerConfig) {
|
|
|
392
690
|
}
|
|
393
691
|
|
|
394
692
|
// src/generator/config.ts
|
|
693
|
+
var POSTGRES_PROTOCOLS = /* @__PURE__ */ new Set(["postgres:", "postgresql:"]);
|
|
395
694
|
var DEFAULT_CONFIG_CANDIDATES = [
|
|
396
695
|
"athena.config.ts",
|
|
397
696
|
"athena.config.js",
|
|
@@ -421,12 +720,157 @@ var DEFAULT_EXPERIMENTAL_FLAGS = {
|
|
|
421
720
|
postgresGatewayIntrospection: false,
|
|
422
721
|
scyllaProviderContracts: true
|
|
423
722
|
};
|
|
723
|
+
var PROJECT_ENV_FILENAMES = [".env", ".env.local"];
|
|
724
|
+
var DIRECT_CONNECTION_STRING_ENV_KEYS = [
|
|
725
|
+
"ATHENA_GENERATOR_PG_URL",
|
|
726
|
+
"DATABASE_URL",
|
|
727
|
+
"PG_URL",
|
|
728
|
+
"POSTGRES_URL",
|
|
729
|
+
"POSTGRESQL_URL"
|
|
730
|
+
];
|
|
731
|
+
var POSTGRES_DATABASE_ENV_KEYS = ["ATHENA_GENERATOR_DB", "ATHENA_DATABASE", "PGDATABASE"];
|
|
732
|
+
var POSTGRES_PASSWORD_ENV_KEYS = ["ATHENA_GENERATOR_PG_PASSWORD", "PGPASSWORD"];
|
|
733
|
+
var GATEWAY_URL_ENV_KEYS = ["ATHENA_URL", "ATHENA_GATEWAY_URL", "ATHENA_GENERATOR_URL"];
|
|
734
|
+
var GATEWAY_API_KEY_ENV_KEYS = [
|
|
735
|
+
"ATHENA_API_KEY",
|
|
736
|
+
"ATHENA_GATEWAY_API_KEY",
|
|
737
|
+
"ATHENA_GENERATOR_API_KEY"
|
|
738
|
+
];
|
|
739
|
+
function normalizeRawEnvValue(rawValue) {
|
|
740
|
+
if (rawValue.startsWith('"') && rawValue.endsWith('"') && rawValue.length >= 2) {
|
|
741
|
+
const inner = rawValue.slice(1, -1);
|
|
742
|
+
return inner.replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
743
|
+
}
|
|
744
|
+
if (rawValue.startsWith("'") && rawValue.endsWith("'") && rawValue.length >= 2) {
|
|
745
|
+
return rawValue.slice(1, -1);
|
|
746
|
+
}
|
|
747
|
+
const commentIndex = rawValue.search(/\s+#/);
|
|
748
|
+
const withoutComment = commentIndex >= 0 ? rawValue.slice(0, commentIndex) : rawValue;
|
|
749
|
+
return withoutComment.trim();
|
|
750
|
+
}
|
|
751
|
+
function parseEnvLine(line) {
|
|
752
|
+
const trimmed = line.trim();
|
|
753
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
754
|
+
return void 0;
|
|
755
|
+
}
|
|
756
|
+
const match = trimmed.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/);
|
|
757
|
+
if (!match) {
|
|
758
|
+
return void 0;
|
|
759
|
+
}
|
|
760
|
+
const [, key, rawValue] = match;
|
|
761
|
+
return [key, normalizeRawEnvValue(rawValue.trim())];
|
|
762
|
+
}
|
|
763
|
+
function readProjectEnvEntries(cwd) {
|
|
764
|
+
const nodeEnv = process.env.NODE_ENV?.trim();
|
|
765
|
+
const filenames = [
|
|
766
|
+
...PROJECT_ENV_FILENAMES,
|
|
767
|
+
...nodeEnv ? [`.env.${nodeEnv}`, `.env.${nodeEnv}.local`] : []
|
|
768
|
+
];
|
|
769
|
+
const entries = /* @__PURE__ */ new Map();
|
|
770
|
+
for (const filename of filenames) {
|
|
771
|
+
const absolutePath = path.resolve(cwd, filename);
|
|
772
|
+
if (!fs.existsSync(absolutePath)) {
|
|
773
|
+
continue;
|
|
774
|
+
}
|
|
775
|
+
const content = fs.readFileSync(absolutePath, "utf8");
|
|
776
|
+
const lines = content.split(/\r?\n/g);
|
|
777
|
+
for (const line of lines) {
|
|
778
|
+
const parsed = parseEnvLine(line);
|
|
779
|
+
if (!parsed) {
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
const [key, value] = parsed;
|
|
783
|
+
entries.set(key, value);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
return entries;
|
|
787
|
+
}
|
|
788
|
+
function applyProjectEnv(cwd) {
|
|
789
|
+
const envEntries = readProjectEnvEntries(cwd);
|
|
790
|
+
if (envEntries.size === 0) {
|
|
791
|
+
return () => {
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
const initialKeys = new Set(
|
|
795
|
+
Object.keys(process.env).filter((key) => process.env[key] !== void 0)
|
|
796
|
+
);
|
|
797
|
+
const staged = /* @__PURE__ */ new Map();
|
|
798
|
+
for (const [key, value] of envEntries.entries()) {
|
|
799
|
+
if (initialKeys.has(key)) {
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
staged.set(key, value);
|
|
803
|
+
}
|
|
804
|
+
for (const [key, value] of staged.entries()) {
|
|
805
|
+
process.env[key] = value;
|
|
806
|
+
}
|
|
807
|
+
return () => {
|
|
808
|
+
for (const key of staged.keys()) {
|
|
809
|
+
delete process.env[key];
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
function readEnvStringValue(key) {
|
|
814
|
+
const value = process.env[key];
|
|
815
|
+
if (typeof value !== "string") {
|
|
816
|
+
return void 0;
|
|
817
|
+
}
|
|
818
|
+
const trimmed = value.trim();
|
|
819
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
820
|
+
}
|
|
821
|
+
function resolveFallbackValue(fallbackKeys) {
|
|
822
|
+
for (const key of fallbackKeys) {
|
|
823
|
+
const value = readEnvStringValue(key);
|
|
824
|
+
if (value) {
|
|
825
|
+
return value;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return void 0;
|
|
829
|
+
}
|
|
830
|
+
function normalizeOptionalString(value, fallbackKeys) {
|
|
831
|
+
if (typeof value === "string") {
|
|
832
|
+
const trimmed = value.trim();
|
|
833
|
+
if (trimmed.length > 0) {
|
|
834
|
+
return trimmed;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
return resolveFallbackValue(fallbackKeys);
|
|
838
|
+
}
|
|
839
|
+
function normalizeRequiredString(value, fieldLabel, fallbackKeys) {
|
|
840
|
+
const resolved = normalizeOptionalString(value, fallbackKeys);
|
|
841
|
+
if (resolved) {
|
|
842
|
+
return resolved;
|
|
843
|
+
}
|
|
844
|
+
throw new Error(
|
|
845
|
+
`Generator config is missing ${fieldLabel}. Set ${fieldLabel} directly or provide one of: ${fallbackKeys.join(", ")}.`
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
function applyPostgresPasswordFallback(connectionString) {
|
|
849
|
+
let parsedUrl;
|
|
850
|
+
try {
|
|
851
|
+
parsedUrl = new URL(connectionString);
|
|
852
|
+
} catch {
|
|
853
|
+
return connectionString;
|
|
854
|
+
}
|
|
855
|
+
if (!POSTGRES_PROTOCOLS.has(parsedUrl.protocol)) {
|
|
856
|
+
return connectionString;
|
|
857
|
+
}
|
|
858
|
+
if (!parsedUrl.username || parsedUrl.password) {
|
|
859
|
+
return connectionString;
|
|
860
|
+
}
|
|
861
|
+
const fallbackPassword = resolveFallbackValue(POSTGRES_PASSWORD_ENV_KEYS);
|
|
862
|
+
if (!fallbackPassword) {
|
|
863
|
+
return connectionString;
|
|
864
|
+
}
|
|
865
|
+
parsedUrl.password = fallbackPassword;
|
|
866
|
+
return parsedUrl.toString();
|
|
867
|
+
}
|
|
424
868
|
function normalizeBooleanFlag(rawValue, fallback) {
|
|
425
869
|
if (typeof rawValue === "boolean") {
|
|
426
870
|
return rawValue;
|
|
427
871
|
}
|
|
428
872
|
if (typeof rawValue === "string") {
|
|
429
|
-
return
|
|
873
|
+
return parseBooleanFlag2(rawValue, fallback);
|
|
430
874
|
}
|
|
431
875
|
return fallback;
|
|
432
876
|
}
|
|
@@ -466,9 +910,41 @@ function normalizeOutputConfig(output) {
|
|
|
466
910
|
};
|
|
467
911
|
}
|
|
468
912
|
function normalizeProviderConfig(provider) {
|
|
469
|
-
if (provider.kind === "postgres") {
|
|
913
|
+
if (provider.kind === "postgres" && provider.mode === "direct") {
|
|
914
|
+
const connectionString = normalizeRequiredString(
|
|
915
|
+
provider.connectionString,
|
|
916
|
+
"provider.connectionString",
|
|
917
|
+
DIRECT_CONNECTION_STRING_ENV_KEYS
|
|
918
|
+
);
|
|
919
|
+
const database = normalizeOptionalString(provider.database, POSTGRES_DATABASE_ENV_KEYS);
|
|
920
|
+
return {
|
|
921
|
+
...provider,
|
|
922
|
+
connectionString: applyPostgresPasswordFallback(connectionString),
|
|
923
|
+
database,
|
|
924
|
+
schemas: normalizeSchemaSelection(provider.schemas)
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
if (provider.kind === "postgres" && provider.mode === "gateway") {
|
|
928
|
+
const gatewayUrl = normalizeRequiredString(
|
|
929
|
+
provider.gatewayUrl,
|
|
930
|
+
"provider.gatewayUrl",
|
|
931
|
+
GATEWAY_URL_ENV_KEYS
|
|
932
|
+
);
|
|
933
|
+
const apiKey = normalizeRequiredString(
|
|
934
|
+
provider.apiKey,
|
|
935
|
+
"provider.apiKey",
|
|
936
|
+
GATEWAY_API_KEY_ENV_KEYS
|
|
937
|
+
);
|
|
938
|
+
const database = normalizeRequiredString(
|
|
939
|
+
provider.database,
|
|
940
|
+
"provider.database",
|
|
941
|
+
POSTGRES_DATABASE_ENV_KEYS
|
|
942
|
+
);
|
|
470
943
|
return {
|
|
471
944
|
...provider,
|
|
945
|
+
gatewayUrl,
|
|
946
|
+
apiKey,
|
|
947
|
+
database,
|
|
472
948
|
schemas: normalizeSchemaSelection(provider.schemas)
|
|
473
949
|
};
|
|
474
950
|
}
|
|
@@ -527,6 +1003,11 @@ function extractConfigExport(module) {
|
|
|
527
1003
|
if (moduleExports && typeof moduleExports === "object") {
|
|
528
1004
|
queue.push(moduleExports);
|
|
529
1005
|
}
|
|
1006
|
+
for (const value of Object.values(record)) {
|
|
1007
|
+
if (value && typeof value === "object") {
|
|
1008
|
+
queue.push(value);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
530
1011
|
}
|
|
531
1012
|
throw new Error(
|
|
532
1013
|
"Generator config file must export a config object as default export or `config`."
|
|
@@ -541,19 +1022,24 @@ function importConfigModule(moduleSpecifier) {
|
|
|
541
1022
|
}
|
|
542
1023
|
async function loadGeneratorConfig(options = {}) {
|
|
543
1024
|
const cwd = options.cwd ?? process.cwd();
|
|
1025
|
+
const restoreProjectEnv = applyProjectEnv(cwd);
|
|
544
1026
|
const resolvedPath = options.configPath ? path.resolve(cwd, options.configPath) : findGeneratorConfigPath(cwd);
|
|
545
1027
|
if (!resolvedPath) {
|
|
546
1028
|
throw new Error(
|
|
547
1029
|
`No generator config found in ${cwd}. Expected one of: ${DEFAULT_CONFIG_CANDIDATES.join(", ")}`
|
|
548
1030
|
);
|
|
549
1031
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
1032
|
+
try {
|
|
1033
|
+
const moduleUrl = url.pathToFileURL(resolvedPath);
|
|
1034
|
+
const module = await importConfigModule(`${moduleUrl.href}?cacheBust=${Date.now()}`);
|
|
1035
|
+
const rawConfig = extractConfigExport(module);
|
|
1036
|
+
return {
|
|
1037
|
+
configPath: resolvedPath,
|
|
1038
|
+
config: normalizeGeneratorConfig(rawConfig)
|
|
1039
|
+
};
|
|
1040
|
+
} finally {
|
|
1041
|
+
restoreProjectEnv();
|
|
1042
|
+
}
|
|
557
1043
|
}
|
|
558
1044
|
|
|
559
1045
|
// src/generator/renderer.ts
|
|
@@ -695,13 +1181,65 @@ function assertNoDuplicatePaths(files) {
|
|
|
695
1181
|
[
|
|
696
1182
|
`Generator output collision detected for path: ${file.path}`,
|
|
697
1183
|
`Collision: ${existing.kind} and ${file.kind}.`,
|
|
698
|
-
"
|
|
1184
|
+
"Use explicit placeholders such as {model}, {model_kebab}, {schema}, or {schema_kebab} in output targets so each artifact resolves to a unique path."
|
|
699
1185
|
].join(" ")
|
|
700
1186
|
);
|
|
701
1187
|
}
|
|
702
1188
|
seen.set(file.path, file);
|
|
703
1189
|
}
|
|
704
1190
|
}
|
|
1191
|
+
function addSchemaSegmentToPath(pathValue, schemaName) {
|
|
1192
|
+
const normalizedPath = normalizePath(pathValue);
|
|
1193
|
+
const parsedPath = path.posix.parse(normalizedPath);
|
|
1194
|
+
const schemaSegment = applyNamingStyle(schemaName, "kebab");
|
|
1195
|
+
if (!schemaSegment) {
|
|
1196
|
+
return normalizedPath;
|
|
1197
|
+
}
|
|
1198
|
+
const dir = parsedPath.dir.length > 0 ? `${parsedPath.dir}/${schemaSegment}` : schemaSegment;
|
|
1199
|
+
return normalizePath(path.posix.join(dir, parsedPath.base));
|
|
1200
|
+
}
|
|
1201
|
+
function scopeDuplicateDescriptorPathsBySchema(descriptors) {
|
|
1202
|
+
const nextDescriptors = descriptors.map((descriptor) => ({ ...descriptor }));
|
|
1203
|
+
const duplicates = /* @__PURE__ */ new Map();
|
|
1204
|
+
for (let index = 0; index < nextDescriptors.length; index += 1) {
|
|
1205
|
+
const descriptor = nextDescriptors[index];
|
|
1206
|
+
const indexes = duplicates.get(descriptor.filePath) ?? [];
|
|
1207
|
+
indexes.push(index);
|
|
1208
|
+
duplicates.set(descriptor.filePath, indexes);
|
|
1209
|
+
}
|
|
1210
|
+
let appliedSchemaScoping = false;
|
|
1211
|
+
for (const indexes of duplicates.values()) {
|
|
1212
|
+
if (indexes.length <= 1) {
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
const schemaNames = new Set(indexes.map((index) => nextDescriptors[index].schemaName));
|
|
1216
|
+
if (schemaNames.size <= 1) {
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1219
|
+
for (const index of indexes) {
|
|
1220
|
+
const descriptor = nextDescriptors[index];
|
|
1221
|
+
descriptor.filePath = addSchemaSegmentToPath(descriptor.filePath, descriptor.schemaName);
|
|
1222
|
+
}
|
|
1223
|
+
appliedSchemaScoping = true;
|
|
1224
|
+
}
|
|
1225
|
+
if (!appliedSchemaScoping) {
|
|
1226
|
+
return nextDescriptors;
|
|
1227
|
+
}
|
|
1228
|
+
const normalizedPaths = /* @__PURE__ */ new Set();
|
|
1229
|
+
for (const descriptor of nextDescriptors) {
|
|
1230
|
+
if (normalizedPaths.has(descriptor.filePath)) {
|
|
1231
|
+
throw new Error(
|
|
1232
|
+
[
|
|
1233
|
+
`Generator output collision detected for path: ${descriptor.filePath}`,
|
|
1234
|
+
"Automatic schema path scoping was applied but collisions remain.",
|
|
1235
|
+
"Add explicit placeholders such as {model}, {model_kebab}, {schema}, or {schema_kebab} to your output targets."
|
|
1236
|
+
].join(" ")
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
normalizedPaths.add(descriptor.filePath);
|
|
1240
|
+
}
|
|
1241
|
+
return nextDescriptors;
|
|
1242
|
+
}
|
|
705
1243
|
var ArtifactComposer = class {
|
|
706
1244
|
constructor(snapshot, config) {
|
|
707
1245
|
this.snapshot = snapshot;
|
|
@@ -740,7 +1278,8 @@ var ArtifactComposer = class {
|
|
|
740
1278
|
});
|
|
741
1279
|
}
|
|
742
1280
|
}
|
|
743
|
-
const
|
|
1281
|
+
const scopedModelDescriptors = scopeDuplicateDescriptorPathsBySchema(modelDescriptors);
|
|
1282
|
+
let schemaDescriptors = Object.keys(this.snapshot.schemas).sort().map((schemaName) => {
|
|
744
1283
|
const schemaPath = normalizePath(
|
|
745
1284
|
renderOutputPath(this.config.output.targets.schema, {
|
|
746
1285
|
provider: providerName,
|
|
@@ -758,9 +1297,10 @@ var ArtifactComposer = class {
|
|
|
758
1297
|
this.config.naming.schemaConst,
|
|
759
1298
|
"schema"
|
|
760
1299
|
),
|
|
761
|
-
models:
|
|
1300
|
+
models: scopedModelDescriptors.filter((model) => model.schemaName === schemaName)
|
|
762
1301
|
};
|
|
763
1302
|
});
|
|
1303
|
+
schemaDescriptors = scopeDuplicateDescriptorPathsBySchema(schemaDescriptors);
|
|
764
1304
|
const databasePath = normalizePath(
|
|
765
1305
|
renderOutputPath(this.config.output.targets.database, {
|
|
766
1306
|
provider: providerName,
|
|
@@ -780,7 +1320,7 @@ var ArtifactComposer = class {
|
|
|
780
1320
|
schemas: schemaDescriptors
|
|
781
1321
|
};
|
|
782
1322
|
const files = [];
|
|
783
|
-
for (const modelDescriptor of
|
|
1323
|
+
for (const modelDescriptor of scopedModelDescriptors) {
|
|
784
1324
|
files.push(renderModelArtifact(this.snapshot, modelDescriptor, this.config));
|
|
785
1325
|
}
|
|
786
1326
|
for (const schemaDescriptor of schemaDescriptors) {
|
|
@@ -844,26 +1384,42 @@ function parseResponseBody(rawText, contentType) {
|
|
|
844
1384
|
function normalizeHeaderValue(value) {
|
|
845
1385
|
return value ? value : void 0;
|
|
846
1386
|
}
|
|
847
|
-
function
|
|
1387
|
+
function isRecord2(value) {
|
|
848
1388
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
849
1389
|
}
|
|
1390
|
+
function nonEmptyString(value) {
|
|
1391
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
1392
|
+
}
|
|
1393
|
+
function resolveStructuredErrorPayload(payload) {
|
|
1394
|
+
if (!isRecord2(payload)) return null;
|
|
1395
|
+
return isRecord2(payload.error) ? payload.error : payload;
|
|
1396
|
+
}
|
|
850
1397
|
function resolveRequestId(headers) {
|
|
851
1398
|
return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
|
|
852
1399
|
}
|
|
853
1400
|
function resolveErrorMessage(payload, fallback) {
|
|
854
|
-
|
|
855
|
-
|
|
1401
|
+
const structuredPayload = resolveStructuredErrorPayload(payload);
|
|
1402
|
+
if (structuredPayload) {
|
|
1403
|
+
const messageCandidates = [structuredPayload.message, structuredPayload.error, structuredPayload.details];
|
|
856
1404
|
for (const candidate of messageCandidates) {
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
}
|
|
1405
|
+
const resolved = nonEmptyString(candidate);
|
|
1406
|
+
if (resolved) return resolved;
|
|
860
1407
|
}
|
|
861
1408
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
}
|
|
1409
|
+
const rawMessage = nonEmptyString(payload);
|
|
1410
|
+
if (rawMessage) return rawMessage;
|
|
865
1411
|
return fallback;
|
|
866
1412
|
}
|
|
1413
|
+
function resolveErrorHint(payload) {
|
|
1414
|
+
const structuredPayload = resolveStructuredErrorPayload(payload);
|
|
1415
|
+
return structuredPayload ? nonEmptyString(structuredPayload.hint) : void 0;
|
|
1416
|
+
}
|
|
1417
|
+
function resolveStatusText(response, payload) {
|
|
1418
|
+
const rawStatusText = nonEmptyString(response.statusText);
|
|
1419
|
+
if (rawStatusText) return rawStatusText;
|
|
1420
|
+
const payloadRecord = isRecord2(payload) ? payload : null;
|
|
1421
|
+
return payloadRecord ? nonEmptyString(payloadRecord.statusText) ?? null : null;
|
|
1422
|
+
}
|
|
867
1423
|
function detailsFromError(error) {
|
|
868
1424
|
return error.toDetails();
|
|
869
1425
|
}
|
|
@@ -1034,6 +1590,7 @@ async function callAthena(config, endpoint, method, payload, options) {
|
|
|
1034
1590
|
return {
|
|
1035
1591
|
ok: false,
|
|
1036
1592
|
status: response.status,
|
|
1593
|
+
statusText: resolveStatusText(response, parsedBody.parsed),
|
|
1037
1594
|
data: null,
|
|
1038
1595
|
error: invalidJsonError.message,
|
|
1039
1596
|
errorDetails: detailsFromError(invalidJsonError),
|
|
@@ -1041,7 +1598,7 @@ async function callAthena(config, endpoint, method, payload, options) {
|
|
|
1041
1598
|
};
|
|
1042
1599
|
}
|
|
1043
1600
|
const parsed = parsedBody.parsed;
|
|
1044
|
-
const parsedPayload =
|
|
1601
|
+
const parsedPayload = isRecord2(parsed) ? parsed : null;
|
|
1045
1602
|
if (!response.ok) {
|
|
1046
1603
|
const httpError = new AthenaGatewayError({
|
|
1047
1604
|
code: "HTTP_ERROR",
|
|
@@ -1052,11 +1609,13 @@ async function callAthena(config, endpoint, method, payload, options) {
|
|
|
1052
1609
|
status: response.status,
|
|
1053
1610
|
endpoint,
|
|
1054
1611
|
method,
|
|
1055
|
-
requestId
|
|
1612
|
+
requestId,
|
|
1613
|
+
hint: resolveErrorHint(parsed)
|
|
1056
1614
|
});
|
|
1057
1615
|
return {
|
|
1058
1616
|
ok: false,
|
|
1059
1617
|
status: response.status,
|
|
1618
|
+
statusText: resolveStatusText(response, parsed),
|
|
1060
1619
|
data: null,
|
|
1061
1620
|
error: httpError.message,
|
|
1062
1621
|
errorDetails: detailsFromError(httpError),
|
|
@@ -1068,6 +1627,7 @@ async function callAthena(config, endpoint, method, payload, options) {
|
|
|
1068
1627
|
return {
|
|
1069
1628
|
ok: true,
|
|
1070
1629
|
status: response.status,
|
|
1630
|
+
statusText: resolveStatusText(response, parsed),
|
|
1071
1631
|
data: payloadData ?? null,
|
|
1072
1632
|
count: payloadCount,
|
|
1073
1633
|
error: void 0,
|
|
@@ -1087,6 +1647,7 @@ async function callAthena(config, endpoint, method, payload, options) {
|
|
|
1087
1647
|
return {
|
|
1088
1648
|
ok: false,
|
|
1089
1649
|
status: 0,
|
|
1650
|
+
statusText: null,
|
|
1090
1651
|
data: null,
|
|
1091
1652
|
error: networkError.message,
|
|
1092
1653
|
errorDetails: detailsFromError(networkError),
|
|
@@ -1128,10 +1689,29 @@ function createAthenaGatewayClient(config = {}) {
|
|
|
1128
1689
|
// src/sql-identifiers.ts
|
|
1129
1690
|
var SIMPLE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1130
1691
|
var COMPOSITE_IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/;
|
|
1131
|
-
var
|
|
1692
|
+
var SQL_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_.]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
|
|
1693
|
+
var RESPONSE_ALIAS_PATTERN = /^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([A-Za-z_][A-Za-z0-9_.]*)$/i;
|
|
1132
1694
|
function quoteIdentifierSegment(identifier) {
|
|
1133
1695
|
return `"${identifier.replace(/"/g, '""')}"`;
|
|
1134
1696
|
}
|
|
1697
|
+
function parseAliasedIdentifierToken(token) {
|
|
1698
|
+
const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(token);
|
|
1699
|
+
if (responseAliasMatch) {
|
|
1700
|
+
const [, aliasIdentifier2, baseIdentifier2] = responseAliasMatch;
|
|
1701
|
+
if (COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier2) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier2)) {
|
|
1702
|
+
return { baseIdentifier: baseIdentifier2, aliasIdentifier: aliasIdentifier2 };
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
const sqlAliasMatch = SQL_ALIAS_PATTERN.exec(token);
|
|
1706
|
+
if (!sqlAliasMatch) {
|
|
1707
|
+
return null;
|
|
1708
|
+
}
|
|
1709
|
+
const [, baseIdentifier, aliasIdentifier] = sqlAliasMatch;
|
|
1710
|
+
if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
|
|
1711
|
+
return null;
|
|
1712
|
+
}
|
|
1713
|
+
return { baseIdentifier, aliasIdentifier };
|
|
1714
|
+
}
|
|
1135
1715
|
function quoteQualifiedIdentifier(identifier) {
|
|
1136
1716
|
return identifier.split(".").map((segment) => quoteIdentifierSegment(segment)).join(".");
|
|
1137
1717
|
}
|
|
@@ -1140,23 +1720,27 @@ function quoteSelectToken(token) {
|
|
|
1140
1720
|
if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) {
|
|
1141
1721
|
return quoteQualifiedIdentifier(token);
|
|
1142
1722
|
}
|
|
1143
|
-
const
|
|
1144
|
-
if (!
|
|
1145
|
-
return token;
|
|
1146
|
-
}
|
|
1147
|
-
const [, baseIdentifier, aliasIdentifier] = aliasMatch;
|
|
1148
|
-
if (!COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) || !SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier)) {
|
|
1723
|
+
const aliasedIdentifier = parseAliasedIdentifierToken(token);
|
|
1724
|
+
if (!aliasedIdentifier) {
|
|
1149
1725
|
return token;
|
|
1150
1726
|
}
|
|
1727
|
+
const { baseIdentifier, aliasIdentifier } = aliasedIdentifier;
|
|
1151
1728
|
return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
|
|
1152
1729
|
}
|
|
1730
|
+
function quoteSelectColumnToken(token) {
|
|
1731
|
+
const trimmed = token.trim();
|
|
1732
|
+
if (!trimmed || trimmed === "*") return trimmed || "*";
|
|
1733
|
+
const responseAliasMatch = RESPONSE_ALIAS_PATTERN.exec(trimmed);
|
|
1734
|
+
if (responseAliasMatch) {
|
|
1735
|
+
const [, aliasIdentifier, baseIdentifier] = responseAliasMatch;
|
|
1736
|
+
return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteIdentifierSegment(aliasIdentifier)}`;
|
|
1737
|
+
}
|
|
1738
|
+
return quoteQualifiedIdentifier(trimmed);
|
|
1739
|
+
}
|
|
1153
1740
|
function canAutoQuoteToken(token) {
|
|
1154
1741
|
if (token === "*") return true;
|
|
1155
1742
|
if (COMPOSITE_IDENTIFIER_PATTERN.test(token)) return true;
|
|
1156
|
-
|
|
1157
|
-
if (!aliasMatch) return false;
|
|
1158
|
-
const [, baseIdentifier, aliasIdentifier] = aliasMatch;
|
|
1159
|
-
return COMPOSITE_IDENTIFIER_PATTERN.test(baseIdentifier) && SIMPLE_IDENTIFIER_PATTERN.test(aliasIdentifier);
|
|
1743
|
+
return parseAliasedIdentifierToken(token) != null;
|
|
1160
1744
|
}
|
|
1161
1745
|
function splitTopLevelCommaSeparated(input) {
|
|
1162
1746
|
const parts = [];
|
|
@@ -1240,6 +1824,190 @@ function quoteSelectColumnsExpression(columns) {
|
|
|
1240
1824
|
return tokens.map(quoteSelectToken).join(", ");
|
|
1241
1825
|
}
|
|
1242
1826
|
|
|
1827
|
+
// src/auth/react-email.ts
|
|
1828
|
+
var reactEmailRenderModulePromise;
|
|
1829
|
+
function isRecord3(value) {
|
|
1830
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1831
|
+
}
|
|
1832
|
+
function isFunction(value) {
|
|
1833
|
+
return typeof value === "function";
|
|
1834
|
+
}
|
|
1835
|
+
function toStringOrUndefined(value) {
|
|
1836
|
+
if (typeof value !== "string") return void 0;
|
|
1837
|
+
return value;
|
|
1838
|
+
}
|
|
1839
|
+
function nowIsoString() {
|
|
1840
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1841
|
+
}
|
|
1842
|
+
function emitReactEmailEvent(observe, phase, input = {}) {
|
|
1843
|
+
if (!observe) return;
|
|
1844
|
+
try {
|
|
1845
|
+
observe({
|
|
1846
|
+
phase,
|
|
1847
|
+
timestamp: nowIsoString(),
|
|
1848
|
+
...input
|
|
1849
|
+
});
|
|
1850
|
+
} catch {
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
function mergeRenderDefaults(input, defaults) {
|
|
1854
|
+
return {
|
|
1855
|
+
...input,
|
|
1856
|
+
pretty: input.pretty ?? defaults?.pretty,
|
|
1857
|
+
includePlainText: input.includePlainText ?? defaults?.includePlainText
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
function mergeRuntimeOptions(options) {
|
|
1861
|
+
if (!options) return void 0;
|
|
1862
|
+
return {
|
|
1863
|
+
defaults: options.defaults,
|
|
1864
|
+
observe: options.observe,
|
|
1865
|
+
route: "route" in options ? options.route : void 0
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
async function resolveReactEmailRenderModule() {
|
|
1869
|
+
if (!reactEmailRenderModulePromise) {
|
|
1870
|
+
reactEmailRenderModulePromise = (async () => {
|
|
1871
|
+
try {
|
|
1872
|
+
const loaded = await import('@react-email/render');
|
|
1873
|
+
if (!isFunction(loaded.render)) {
|
|
1874
|
+
throw new Error("missing render(...) export");
|
|
1875
|
+
}
|
|
1876
|
+
return {
|
|
1877
|
+
render: loaded.render,
|
|
1878
|
+
toPlainText: isFunction(loaded.toPlainText) ? loaded.toPlainText : void 0,
|
|
1879
|
+
pretty: isFunction(loaded.pretty) ? loaded.pretty : void 0
|
|
1880
|
+
};
|
|
1881
|
+
} catch (error) {
|
|
1882
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1883
|
+
throw new Error(
|
|
1884
|
+
`React Email rendering requires @react-email/render. Install it in your project (for example: pnpm add @react-email/render). Loader error: ${message}`
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
})();
|
|
1888
|
+
}
|
|
1889
|
+
if (!reactEmailRenderModulePromise) {
|
|
1890
|
+
throw new Error("React Email renderer module failed to initialize");
|
|
1891
|
+
}
|
|
1892
|
+
return reactEmailRenderModulePromise;
|
|
1893
|
+
}
|
|
1894
|
+
async function resolveReactEmailElement(input) {
|
|
1895
|
+
if (input.element != null) {
|
|
1896
|
+
return input.element;
|
|
1897
|
+
}
|
|
1898
|
+
if (!input.component) {
|
|
1899
|
+
throw new Error("react email payload requires either `element` or `component`");
|
|
1900
|
+
}
|
|
1901
|
+
try {
|
|
1902
|
+
const reactModule = await import('react');
|
|
1903
|
+
if (typeof reactModule.createElement !== "function") {
|
|
1904
|
+
throw new Error("react createElement(...) export is unavailable");
|
|
1905
|
+
}
|
|
1906
|
+
return reactModule.createElement(
|
|
1907
|
+
input.component,
|
|
1908
|
+
input.props ?? {}
|
|
1909
|
+
);
|
|
1910
|
+
} catch (error) {
|
|
1911
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1912
|
+
throw new Error(
|
|
1913
|
+
`React Email component rendering requires react runtime support. Install react in your project. Loader error: ${message}`
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
async function renderAthenaReactEmail(input, options) {
|
|
1918
|
+
if (!isRecord3(input)) {
|
|
1919
|
+
throw new Error("react email payload must be an object");
|
|
1920
|
+
}
|
|
1921
|
+
const runtimeOptions = mergeRuntimeOptions(options);
|
|
1922
|
+
const start = Date.now();
|
|
1923
|
+
emitReactEmailEvent(runtimeOptions?.observe, "render:start", {
|
|
1924
|
+
route: runtimeOptions?.route,
|
|
1925
|
+
message: "Rendering react email payload"
|
|
1926
|
+
});
|
|
1927
|
+
try {
|
|
1928
|
+
const normalizedInput = mergeRenderDefaults(input, runtimeOptions?.defaults);
|
|
1929
|
+
const element = await resolveReactEmailElement(normalizedInput);
|
|
1930
|
+
const renderModule = await resolveReactEmailRenderModule();
|
|
1931
|
+
const htmlValue = await renderModule.render(element);
|
|
1932
|
+
const renderedHtml = typeof htmlValue === "string" ? htmlValue : String(htmlValue ?? "");
|
|
1933
|
+
if (!renderedHtml.trim()) {
|
|
1934
|
+
throw new Error("react email renderer returned an empty HTML string");
|
|
1935
|
+
}
|
|
1936
|
+
let html = renderedHtml;
|
|
1937
|
+
if (normalizedInput.pretty && renderModule.pretty) {
|
|
1938
|
+
const prettyValue = await renderModule.pretty(renderedHtml);
|
|
1939
|
+
if (typeof prettyValue === "string" && prettyValue.trim().length > 0) {
|
|
1940
|
+
html = prettyValue;
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
const explicitText = toStringOrUndefined(normalizedInput.text);
|
|
1944
|
+
if (explicitText !== void 0) {
|
|
1945
|
+
emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
|
|
1946
|
+
route: runtimeOptions?.route,
|
|
1947
|
+
durationMs: Date.now() - start,
|
|
1948
|
+
message: "Rendered react email with explicit text"
|
|
1949
|
+
});
|
|
1950
|
+
return {
|
|
1951
|
+
html,
|
|
1952
|
+
text: explicitText
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
if (normalizedInput.includePlainText === false || !renderModule.toPlainText) {
|
|
1956
|
+
emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
|
|
1957
|
+
route: runtimeOptions?.route,
|
|
1958
|
+
durationMs: Date.now() - start,
|
|
1959
|
+
message: "Rendered react email without plain-text derivation"
|
|
1960
|
+
});
|
|
1961
|
+
return { html };
|
|
1962
|
+
}
|
|
1963
|
+
const plainTextValue = await renderModule.toPlainText(html);
|
|
1964
|
+
const plainText = toStringOrUndefined(plainTextValue);
|
|
1965
|
+
emitReactEmailEvent(runtimeOptions?.observe, "render:success", {
|
|
1966
|
+
route: runtimeOptions?.route,
|
|
1967
|
+
durationMs: Date.now() - start,
|
|
1968
|
+
message: plainText ? "Rendered react email with derived plain text" : "Rendered react email HTML only"
|
|
1969
|
+
});
|
|
1970
|
+
if (plainText === void 0) {
|
|
1971
|
+
return { html };
|
|
1972
|
+
}
|
|
1973
|
+
return {
|
|
1974
|
+
html,
|
|
1975
|
+
text: plainText
|
|
1976
|
+
};
|
|
1977
|
+
} catch (error) {
|
|
1978
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1979
|
+
emitReactEmailEvent(runtimeOptions?.observe, "render:error", {
|
|
1980
|
+
route: runtimeOptions?.route,
|
|
1981
|
+
durationMs: Date.now() - start,
|
|
1982
|
+
error: message,
|
|
1983
|
+
message: "Failed to render react email payload"
|
|
1984
|
+
});
|
|
1985
|
+
throw error;
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
async function resolveReactEmailPayloadFields(input, fields, options) {
|
|
1989
|
+
const { react, ...payloadWithoutReact } = input;
|
|
1990
|
+
if (!react) {
|
|
1991
|
+
return payloadWithoutReact;
|
|
1992
|
+
}
|
|
1993
|
+
const rendered = await renderAthenaReactEmail(react, options);
|
|
1994
|
+
const payload = {
|
|
1995
|
+
...payloadWithoutReact
|
|
1996
|
+
};
|
|
1997
|
+
payload[fields.htmlField] = rendered.html;
|
|
1998
|
+
const currentTextValue = payload[fields.textField];
|
|
1999
|
+
if (rendered.text !== void 0 && (currentTextValue === void 0 || currentTextValue === null || currentTextValue === "")) {
|
|
2000
|
+
payload[fields.textField] = rendered.text;
|
|
2001
|
+
}
|
|
2002
|
+
if (fields.variablesField && (payload[fields.variablesField] === void 0 || payload[fields.variablesField] === null) && isRecord3(react.props)) {
|
|
2003
|
+
const derivedVariables = Object.keys(react.props);
|
|
2004
|
+
if (derivedVariables.length > 0) {
|
|
2005
|
+
payload[fields.variablesField] = derivedVariables;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
return payload;
|
|
2009
|
+
}
|
|
2010
|
+
|
|
1243
2011
|
// src/auth/client.ts
|
|
1244
2012
|
var DEFAULT_AUTH_BASE_URL = "http://localhost:3001/api/auth";
|
|
1245
2013
|
var FALLBACK_SDK_VERSION2 = "1.0.0";
|
|
@@ -1249,7 +2017,7 @@ var SDK_HEADER_VALUE2 = `${SDK_NAME2} ${SDK_VERSION2}`;
|
|
|
1249
2017
|
function normalizeBaseUrl(baseUrl) {
|
|
1250
2018
|
return (baseUrl ?? DEFAULT_AUTH_BASE_URL).replace(/\/$/, "");
|
|
1251
2019
|
}
|
|
1252
|
-
function
|
|
2020
|
+
function isRecord4(value) {
|
|
1253
2021
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1254
2022
|
}
|
|
1255
2023
|
function normalizeHeaderValue2(value) {
|
|
@@ -1274,7 +2042,7 @@ function resolveRequestId2(headers) {
|
|
|
1274
2042
|
return headers.get("x-request-id") ?? headers.get("x-correlation-id") ?? headers.get("x-athena-request-id") ?? void 0;
|
|
1275
2043
|
}
|
|
1276
2044
|
function resolveErrorMessage2(payload, fallback) {
|
|
1277
|
-
if (
|
|
2045
|
+
if (isRecord4(payload)) {
|
|
1278
2046
|
const messageCandidates = [payload.error, payload.message, payload.details];
|
|
1279
2047
|
for (const candidate of messageCandidates) {
|
|
1280
2048
|
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
@@ -1600,6 +2368,19 @@ function createAuthClient(config = {}) {
|
|
|
1600
2368
|
options
|
|
1601
2369
|
);
|
|
1602
2370
|
};
|
|
2371
|
+
const withReactEmailRoute = (route) => ({
|
|
2372
|
+
...resolvedConfig.reactEmail,
|
|
2373
|
+
route
|
|
2374
|
+
});
|
|
2375
|
+
const resolveAdminEmailPayload = (route, input) => resolveReactEmailPayloadFields(input, {
|
|
2376
|
+
htmlField: "htmlBody",
|
|
2377
|
+
textField: "textBody"
|
|
2378
|
+
}, withReactEmailRoute(route));
|
|
2379
|
+
const resolveAdminEmailTemplatePayload = (route, input) => resolveReactEmailPayloadFields(input, {
|
|
2380
|
+
htmlField: "htmlTemplate",
|
|
2381
|
+
textField: "textTemplate",
|
|
2382
|
+
variablesField: "variables"
|
|
2383
|
+
}, withReactEmailRoute(route));
|
|
1603
2384
|
const listUserEmailsWithFallback = async (input, options) => {
|
|
1604
2385
|
const primary = await getWithQuery(
|
|
1605
2386
|
"/email/list",
|
|
@@ -1627,7 +2408,7 @@ function createAuthClient(config = {}) {
|
|
|
1627
2408
|
data: null
|
|
1628
2409
|
};
|
|
1629
2410
|
}
|
|
1630
|
-
const fallbackStatus =
|
|
2411
|
+
const fallbackStatus = isRecord4(fallback.data) && typeof fallback.data.ok === "boolean" ? fallback.data.ok ? "ok" : "error" : "ok";
|
|
1631
2412
|
return {
|
|
1632
2413
|
...fallback,
|
|
1633
2414
|
data: {
|
|
@@ -2063,8 +2844,16 @@ function createAuthClient(config = {}) {
|
|
|
2063
2844
|
email: {
|
|
2064
2845
|
list: (input, options) => getWithQuery("/admin/email/list", input, options),
|
|
2065
2846
|
get: (input, options) => getWithQuery("/admin/email/get", input, options),
|
|
2066
|
-
create: (input, options) => postGeneric(
|
|
2067
|
-
|
|
2847
|
+
create: async (input, options) => postGeneric(
|
|
2848
|
+
"/admin/email/create",
|
|
2849
|
+
await resolveAdminEmailPayload("/admin/email/create", input),
|
|
2850
|
+
options
|
|
2851
|
+
),
|
|
2852
|
+
update: async (input, options) => postGeneric(
|
|
2853
|
+
"/admin/email/update",
|
|
2854
|
+
await resolveAdminEmailPayload("/admin/email/update", input),
|
|
2855
|
+
options
|
|
2856
|
+
),
|
|
2068
2857
|
delete: (input, options) => postGeneric("/admin/email/delete", input, options),
|
|
2069
2858
|
failure: {
|
|
2070
2859
|
list: (input, options) => getWithQuery("/admin/email-failure/list", input, options),
|
|
@@ -2076,17 +2865,33 @@ function createAuthClient(config = {}) {
|
|
|
2076
2865
|
template: {
|
|
2077
2866
|
list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
|
|
2078
2867
|
get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
|
|
2079
|
-
create: (input, options) => postGeneric(
|
|
2080
|
-
|
|
2868
|
+
create: async (input, options) => postGeneric(
|
|
2869
|
+
"/admin/email-template/create",
|
|
2870
|
+
await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
|
|
2871
|
+
options
|
|
2872
|
+
),
|
|
2873
|
+
update: async (input, options) => postGeneric(
|
|
2874
|
+
"/admin/email-template/update",
|
|
2875
|
+
await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
|
|
2876
|
+
options
|
|
2877
|
+
),
|
|
2081
2878
|
delete: (input, options) => postGeneric("/admin/email-template/delete", input, options)
|
|
2082
2879
|
}
|
|
2083
2880
|
},
|
|
2084
2881
|
emailTemplate: {
|
|
2085
2882
|
get: (input, options) => getWithQuery("/admin/email-template/get", input, options),
|
|
2086
|
-
create: (input, options) => postGeneric(
|
|
2883
|
+
create: async (input, options) => postGeneric(
|
|
2884
|
+
"/admin/email-template/create",
|
|
2885
|
+
await resolveAdminEmailTemplatePayload("/admin/email-template/create", input),
|
|
2886
|
+
options
|
|
2887
|
+
),
|
|
2087
2888
|
delete: (input, options) => postGeneric("/admin/email-template/delete", input, options),
|
|
2088
2889
|
list: (input, options) => getWithQuery("/admin/email-template/list", input, options),
|
|
2089
|
-
update: (input, options) => postGeneric(
|
|
2890
|
+
update: async (input, options) => postGeneric(
|
|
2891
|
+
"/admin/email-template/update",
|
|
2892
|
+
await resolveAdminEmailTemplatePayload("/admin/email-template/update", input),
|
|
2893
|
+
options
|
|
2894
|
+
)
|
|
2090
2895
|
}
|
|
2091
2896
|
},
|
|
2092
2897
|
apiKey: {
|
|
@@ -2278,16 +3083,313 @@ function createAuthClient(config = {}) {
|
|
|
2278
3083
|
};
|
|
2279
3084
|
}
|
|
2280
3085
|
|
|
2281
|
-
// src/
|
|
2282
|
-
|
|
3086
|
+
// src/db/module.ts
|
|
3087
|
+
function createDbModule(input) {
|
|
3088
|
+
const db = {
|
|
3089
|
+
from(table) {
|
|
3090
|
+
return input.from(table);
|
|
3091
|
+
},
|
|
3092
|
+
select(table, columns, options) {
|
|
3093
|
+
return input.from(table).select(columns, options);
|
|
3094
|
+
},
|
|
3095
|
+
insert(table, values, options) {
|
|
3096
|
+
return Array.isArray(values) ? input.from(table).insert(values, options) : input.from(table).insert(values, options);
|
|
3097
|
+
},
|
|
3098
|
+
upsert(table, values, options) {
|
|
3099
|
+
return Array.isArray(values) ? input.from(table).upsert(values, options) : input.from(table).upsert(values, options);
|
|
3100
|
+
},
|
|
3101
|
+
update(table, values, options) {
|
|
3102
|
+
return input.from(table).update(values, options);
|
|
3103
|
+
},
|
|
3104
|
+
delete(table, options) {
|
|
3105
|
+
return input.from(table).delete(options);
|
|
3106
|
+
},
|
|
3107
|
+
rpc(fn, args, options) {
|
|
3108
|
+
return input.rpc(fn, args, options);
|
|
3109
|
+
},
|
|
3110
|
+
query(query, options) {
|
|
3111
|
+
return input.query(query, options);
|
|
3112
|
+
}
|
|
3113
|
+
};
|
|
3114
|
+
return db;
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
// src/query-ast.ts
|
|
2283
3118
|
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;
|
|
3119
|
+
var FILTER_OPERATORS = /* @__PURE__ */ new Set([
|
|
3120
|
+
"eq",
|
|
3121
|
+
"neq",
|
|
3122
|
+
"gt",
|
|
3123
|
+
"gte",
|
|
3124
|
+
"lt",
|
|
3125
|
+
"lte",
|
|
3126
|
+
"like",
|
|
3127
|
+
"ilike",
|
|
3128
|
+
"is",
|
|
3129
|
+
"in",
|
|
3130
|
+
"contains",
|
|
3131
|
+
"containedBy"
|
|
3132
|
+
]);
|
|
3133
|
+
var BOOLEAN_SAFE_OPERATORS = /* @__PURE__ */ new Set([
|
|
3134
|
+
"eq",
|
|
3135
|
+
"neq",
|
|
3136
|
+
"gt",
|
|
3137
|
+
"gte",
|
|
3138
|
+
"lt",
|
|
3139
|
+
"lte",
|
|
3140
|
+
"like",
|
|
3141
|
+
"ilike",
|
|
3142
|
+
"is"
|
|
3143
|
+
]);
|
|
3144
|
+
function isRecord5(value) {
|
|
3145
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3146
|
+
}
|
|
3147
|
+
function isUuidString(value) {
|
|
3148
|
+
return UUID_PATTERN.test(value.trim());
|
|
3149
|
+
}
|
|
3150
|
+
function isUuidIdentifierColumn(column) {
|
|
3151
|
+
return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
|
|
3152
|
+
}
|
|
3153
|
+
function shouldUseUuidTextComparison(column, value) {
|
|
3154
|
+
return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
|
|
3155
|
+
}
|
|
3156
|
+
function isRelationSelectNode(value) {
|
|
3157
|
+
return isRecord5(value) && isRecord5(value.select);
|
|
3158
|
+
}
|
|
3159
|
+
function normalizeIdentifier(value, label) {
|
|
3160
|
+
const normalized = value.trim();
|
|
3161
|
+
if (!normalized) {
|
|
3162
|
+
throw new Error(`${label} must be a non-empty string`);
|
|
3163
|
+
}
|
|
3164
|
+
return normalized;
|
|
3165
|
+
}
|
|
3166
|
+
function stringifyFilterValue(value) {
|
|
3167
|
+
if (Array.isArray(value)) {
|
|
3168
|
+
return value.join(",");
|
|
3169
|
+
}
|
|
3170
|
+
return String(value);
|
|
3171
|
+
}
|
|
3172
|
+
function buildGatewayCondition(operator, column, value) {
|
|
3173
|
+
const condition = { operator };
|
|
3174
|
+
if (column) {
|
|
3175
|
+
condition.column = column;
|
|
3176
|
+
if (operator === "eq") {
|
|
3177
|
+
condition.eq_column = column;
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
if (value !== void 0) {
|
|
3181
|
+
condition.value = value;
|
|
3182
|
+
if (operator === "eq") {
|
|
3183
|
+
condition.eq_value = value;
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
if (operator === "eq" && column && value !== void 0 && shouldUseUuidTextComparison(column, value)) {
|
|
3187
|
+
condition.column_cast = "text";
|
|
3188
|
+
condition.eq_column_cast = "text";
|
|
3189
|
+
}
|
|
3190
|
+
return condition;
|
|
3191
|
+
}
|
|
3192
|
+
function compileRelationToken(key, node) {
|
|
3193
|
+
const nested = compileSelectShape(node.select);
|
|
3194
|
+
const propertyKey = normalizeIdentifier(key, "select relation key");
|
|
3195
|
+
const relationToken = normalizeIdentifier(node.via ?? propertyKey, "select relation token");
|
|
3196
|
+
const alias = node.as?.trim() || (relationToken !== propertyKey ? propertyKey : "");
|
|
3197
|
+
const prefix = alias ? `${alias}:` : "";
|
|
3198
|
+
return `${prefix}${relationToken}(${nested})`;
|
|
3199
|
+
}
|
|
3200
|
+
function compileSelectShape(select) {
|
|
3201
|
+
if (!isRecord5(select)) {
|
|
3202
|
+
throw new Error("findMany select must be an object");
|
|
3203
|
+
}
|
|
3204
|
+
const tokens = [];
|
|
3205
|
+
for (const [rawKey, rawValue] of Object.entries(select)) {
|
|
3206
|
+
if (rawValue === void 0) {
|
|
3207
|
+
continue;
|
|
3208
|
+
}
|
|
3209
|
+
if (rawValue === true) {
|
|
3210
|
+
tokens.push(normalizeIdentifier(rawKey, "select column"));
|
|
3211
|
+
continue;
|
|
3212
|
+
}
|
|
3213
|
+
if (isRelationSelectNode(rawValue)) {
|
|
3214
|
+
tokens.push(compileRelationToken(rawKey, rawValue));
|
|
3215
|
+
continue;
|
|
3216
|
+
}
|
|
3217
|
+
throw new Error(`Unsupported select node for "${rawKey}"`);
|
|
3218
|
+
}
|
|
3219
|
+
if (tokens.length === 0) {
|
|
3220
|
+
throw new Error("findMany select requires at least one field");
|
|
3221
|
+
}
|
|
3222
|
+
return tokens.join(",");
|
|
3223
|
+
}
|
|
3224
|
+
function compileColumnWhere(column, input) {
|
|
3225
|
+
const normalizedColumn = normalizeIdentifier(column, "where column");
|
|
3226
|
+
if (!isRecord5(input)) {
|
|
3227
|
+
return [buildGatewayCondition("eq", normalizedColumn, input)];
|
|
3228
|
+
}
|
|
3229
|
+
const conditions = [];
|
|
3230
|
+
for (const [rawOperator, rawValue] of Object.entries(input)) {
|
|
3231
|
+
if (rawValue === void 0) {
|
|
3232
|
+
continue;
|
|
3233
|
+
}
|
|
3234
|
+
if (!FILTER_OPERATORS.has(rawOperator)) {
|
|
3235
|
+
throw new Error(`Unsupported where operator "${rawOperator}" on "${normalizedColumn}"`);
|
|
3236
|
+
}
|
|
3237
|
+
if ((rawOperator === "in" || rawOperator === "contains" || rawOperator === "containedBy") && !Array.isArray(rawValue)) {
|
|
3238
|
+
throw new Error(`where.${normalizedColumn}.${rawOperator} requires an array value`);
|
|
3239
|
+
}
|
|
3240
|
+
conditions.push(
|
|
3241
|
+
buildGatewayCondition(
|
|
3242
|
+
rawOperator,
|
|
3243
|
+
normalizedColumn,
|
|
3244
|
+
rawValue
|
|
3245
|
+
)
|
|
3246
|
+
);
|
|
3247
|
+
}
|
|
3248
|
+
if (conditions.length === 0) {
|
|
3249
|
+
throw new Error(`where.${normalizedColumn} requires at least one operator`);
|
|
3250
|
+
}
|
|
3251
|
+
return conditions;
|
|
3252
|
+
}
|
|
3253
|
+
function compileBooleanExpressionTerms(clause, label) {
|
|
3254
|
+
if (!isRecord5(clause)) {
|
|
3255
|
+
throw new Error(`findMany where.${label} clauses must be objects`);
|
|
3256
|
+
}
|
|
3257
|
+
const entries = Object.entries(clause).filter(([, value]) => value !== void 0);
|
|
3258
|
+
if (entries.length !== 1) {
|
|
3259
|
+
throw new Error(`findMany where.${label} clauses must target exactly one column`);
|
|
3260
|
+
}
|
|
3261
|
+
const [rawColumn, rawValue] = entries[0];
|
|
3262
|
+
const column = normalizeIdentifier(rawColumn, `where.${label} column`);
|
|
3263
|
+
if (!isRecord5(rawValue)) {
|
|
3264
|
+
return [`${column}.eq.${stringifyFilterValue(rawValue)}`];
|
|
3265
|
+
}
|
|
3266
|
+
const operatorEntries = Object.entries(rawValue).filter(([, value]) => value !== void 0);
|
|
3267
|
+
if (operatorEntries.length === 0) {
|
|
3268
|
+
throw new Error(`findMany where.${label}.${column} requires at least one operator`);
|
|
3269
|
+
}
|
|
3270
|
+
if (label === "not" && operatorEntries.length > 1) {
|
|
3271
|
+
throw new Error("findMany where.not only supports a single lossless operator expression");
|
|
3272
|
+
}
|
|
3273
|
+
return operatorEntries.map(([rawOperator, rawOperand]) => {
|
|
3274
|
+
if (!BOOLEAN_SAFE_OPERATORS.has(rawOperator)) {
|
|
3275
|
+
throw new Error(`findMany where.${label} only supports lossless scalar operators`);
|
|
3276
|
+
}
|
|
3277
|
+
if (Array.isArray(rawOperand)) {
|
|
3278
|
+
throw new Error(`findMany where.${label} does not support array-valued operators`);
|
|
3279
|
+
}
|
|
3280
|
+
return `${column}.${rawOperator}.${stringifyFilterValue(rawOperand)}`;
|
|
3281
|
+
});
|
|
3282
|
+
}
|
|
3283
|
+
function compileWhere(where) {
|
|
3284
|
+
if (where === void 0) {
|
|
3285
|
+
return void 0;
|
|
3286
|
+
}
|
|
3287
|
+
if (!isRecord5(where)) {
|
|
3288
|
+
throw new Error("findMany where must be an object");
|
|
3289
|
+
}
|
|
3290
|
+
const conditions = [];
|
|
3291
|
+
for (const [rawKey, rawValue] of Object.entries(where)) {
|
|
3292
|
+
if (rawValue === void 0) {
|
|
3293
|
+
continue;
|
|
3294
|
+
}
|
|
3295
|
+
if (rawKey === "or") {
|
|
3296
|
+
if (!Array.isArray(rawValue) || rawValue.length === 0) {
|
|
3297
|
+
throw new Error("findMany where.or must be a non-empty array");
|
|
3298
|
+
}
|
|
3299
|
+
const expressions = rawValue.flatMap(
|
|
3300
|
+
(value) => compileBooleanExpressionTerms(value, "or")
|
|
3301
|
+
);
|
|
3302
|
+
conditions.push(buildGatewayCondition("or", void 0, expressions.join(",")));
|
|
3303
|
+
continue;
|
|
3304
|
+
}
|
|
3305
|
+
if (rawKey === "not") {
|
|
3306
|
+
const expressions = compileBooleanExpressionTerms(rawValue, "not");
|
|
3307
|
+
if (expressions.length !== 1) {
|
|
3308
|
+
throw new Error("findMany where.not must compile to exactly one lossless expression");
|
|
3309
|
+
}
|
|
3310
|
+
conditions.push(buildGatewayCondition("not", void 0, expressions[0]));
|
|
3311
|
+
continue;
|
|
3312
|
+
}
|
|
3313
|
+
conditions.push(...compileColumnWhere(rawKey, rawValue));
|
|
3314
|
+
}
|
|
3315
|
+
return conditions.length > 0 ? conditions : void 0;
|
|
3316
|
+
}
|
|
3317
|
+
function resolveOrderDirection(input) {
|
|
3318
|
+
if (typeof input === "boolean") {
|
|
3319
|
+
return input === false ? "descending" : "ascending";
|
|
3320
|
+
}
|
|
3321
|
+
if (typeof input === "string") {
|
|
3322
|
+
const normalized = input.trim().toLowerCase();
|
|
3323
|
+
if (normalized === "asc" || normalized === "ascending") {
|
|
3324
|
+
return "ascending";
|
|
3325
|
+
}
|
|
3326
|
+
if (normalized === "desc" || normalized === "descending") {
|
|
3327
|
+
return "descending";
|
|
3328
|
+
}
|
|
3329
|
+
throw new Error(`Unsupported orderBy direction "${input}"`);
|
|
3330
|
+
}
|
|
3331
|
+
return input.ascending === false ? "descending" : "ascending";
|
|
3332
|
+
}
|
|
3333
|
+
function compileOrderBy(orderBy) {
|
|
3334
|
+
if (orderBy === void 0) {
|
|
3335
|
+
return void 0;
|
|
3336
|
+
}
|
|
3337
|
+
if (!isRecord5(orderBy)) {
|
|
3338
|
+
throw new Error("findMany orderBy must be an object");
|
|
3339
|
+
}
|
|
3340
|
+
if ("column" in orderBy) {
|
|
3341
|
+
return {
|
|
3342
|
+
field: normalizeIdentifier(String(orderBy.column), "orderBy column"),
|
|
3343
|
+
direction: orderBy.ascending === false ? "descending" : "ascending"
|
|
3344
|
+
};
|
|
3345
|
+
}
|
|
3346
|
+
const entries = Object.entries(orderBy).filter(([, value]) => value !== void 0);
|
|
3347
|
+
if (entries.length === 0) {
|
|
3348
|
+
return void 0;
|
|
3349
|
+
}
|
|
3350
|
+
if (entries.length > 1) {
|
|
3351
|
+
throw new Error("findMany orderBy only supports a single column in v1");
|
|
3352
|
+
}
|
|
3353
|
+
const [column, input] = entries[0];
|
|
3354
|
+
return {
|
|
3355
|
+
field: normalizeIdentifier(column, "orderBy column"),
|
|
3356
|
+
direction: resolveOrderDirection(input)
|
|
3357
|
+
};
|
|
3358
|
+
}
|
|
3359
|
+
|
|
3360
|
+
// src/client.ts
|
|
3361
|
+
var DEFAULT_COLUMNS = "*";
|
|
2284
3362
|
var SAFE_CAST_PATTERN = /^[a-z_][a-z0-9_]*(?:\[\])?$/i;
|
|
3363
|
+
var ATHENA_NORMALIZED_ERROR_KEY = "__athenaNormalizedError";
|
|
3364
|
+
var QUERY_TRACE_STACK_SKIP_PATTERNS = [
|
|
3365
|
+
"src\\client.ts",
|
|
3366
|
+
"src/client.ts",
|
|
3367
|
+
"dist\\client.",
|
|
3368
|
+
"dist/client.",
|
|
3369
|
+
"node_modules\\@xylex-group\\athena",
|
|
3370
|
+
"node_modules/@xylex-group/athena",
|
|
3371
|
+
"node:internal",
|
|
3372
|
+
"internal/process"
|
|
3373
|
+
];
|
|
3374
|
+
function canUseFindManyAstTransport(state) {
|
|
3375
|
+
return state.conditions.length === 0 && state.offset === void 0 && state.currentPage === void 0 && state.pageSize === void 0 && state.totalPages === void 0;
|
|
3376
|
+
}
|
|
3377
|
+
function toFindManyAstOrder(order) {
|
|
3378
|
+
if (!order) {
|
|
3379
|
+
return void 0;
|
|
3380
|
+
}
|
|
3381
|
+
return {
|
|
3382
|
+
column: order.field,
|
|
3383
|
+
ascending: order.direction !== "descending"
|
|
3384
|
+
};
|
|
3385
|
+
}
|
|
2285
3386
|
function formatResult(response) {
|
|
2286
3387
|
const result = {
|
|
2287
3388
|
data: response.data ?? null,
|
|
2288
|
-
error:
|
|
3389
|
+
error: null,
|
|
2289
3390
|
errorDetails: response.errorDetails ?? null,
|
|
2290
3391
|
status: response.status,
|
|
3392
|
+
statusText: response.statusText ?? null,
|
|
2291
3393
|
raw: response.raw
|
|
2292
3394
|
};
|
|
2293
3395
|
if (response.count !== void 0) {
|
|
@@ -2295,6 +3397,245 @@ function formatResult(response) {
|
|
|
2295
3397
|
}
|
|
2296
3398
|
return result;
|
|
2297
3399
|
}
|
|
3400
|
+
function attachNormalizedError(result, normalizedError) {
|
|
3401
|
+
Object.defineProperty(result, ATHENA_NORMALIZED_ERROR_KEY, {
|
|
3402
|
+
value: normalizedError,
|
|
3403
|
+
enumerable: false,
|
|
3404
|
+
configurable: true,
|
|
3405
|
+
writable: false
|
|
3406
|
+
});
|
|
3407
|
+
}
|
|
3408
|
+
function createResultFormatter(experimental) {
|
|
3409
|
+
return (response, context) => {
|
|
3410
|
+
const result = formatResult(response);
|
|
3411
|
+
if (response.error == null && response.errorDetails == null) {
|
|
3412
|
+
return result;
|
|
3413
|
+
}
|
|
3414
|
+
const normalizedError = normalizeAthenaError(
|
|
3415
|
+
{
|
|
3416
|
+
...result,
|
|
3417
|
+
error: response.error ?? response.errorDetails?.message ?? null
|
|
3418
|
+
},
|
|
3419
|
+
context
|
|
3420
|
+
);
|
|
3421
|
+
result.error = createResultError(response, result, normalizedError);
|
|
3422
|
+
attachNormalizedError(result, normalizedError);
|
|
3423
|
+
return result;
|
|
3424
|
+
};
|
|
3425
|
+
}
|
|
3426
|
+
function isRecord6(value) {
|
|
3427
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
3428
|
+
}
|
|
3429
|
+
function firstNonEmptyString2(...values) {
|
|
3430
|
+
for (const value of values) {
|
|
3431
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
3432
|
+
return value.trim();
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
return void 0;
|
|
3436
|
+
}
|
|
3437
|
+
function resolveStructuredErrorPayload2(raw) {
|
|
3438
|
+
if (!isRecord6(raw)) return null;
|
|
3439
|
+
return isRecord6(raw.error) ? raw.error : raw;
|
|
3440
|
+
}
|
|
3441
|
+
function resolveStructuredErrorDetails(payload, message) {
|
|
3442
|
+
if (!payload || !("details" in payload)) {
|
|
3443
|
+
return null;
|
|
3444
|
+
}
|
|
3445
|
+
const details = payload.details;
|
|
3446
|
+
if (details == null) {
|
|
3447
|
+
return null;
|
|
3448
|
+
}
|
|
3449
|
+
if (typeof details === "string" && details.trim() === message.trim()) {
|
|
3450
|
+
return null;
|
|
3451
|
+
}
|
|
3452
|
+
return details;
|
|
3453
|
+
}
|
|
3454
|
+
function createResultError(response, result, normalized) {
|
|
3455
|
+
const rawRecord = isRecord6(response.raw) ? response.raw : null;
|
|
3456
|
+
const payload = resolveStructuredErrorPayload2(response.raw);
|
|
3457
|
+
const message = firstNonEmptyString2(
|
|
3458
|
+
response.error,
|
|
3459
|
+
payload?.message,
|
|
3460
|
+
payload?.error,
|
|
3461
|
+
payload?.details,
|
|
3462
|
+
response.errorDetails?.message,
|
|
3463
|
+
normalized.message
|
|
3464
|
+
) ?? normalized.message;
|
|
3465
|
+
const statusText = firstNonEmptyString2(response.statusText, rawRecord?.statusText) ?? null;
|
|
3466
|
+
const hint = firstNonEmptyString2(payload?.hint, response.errorDetails?.hint) ?? null;
|
|
3467
|
+
const code = firstNonEmptyString2(payload?.code) ?? normalized.code;
|
|
3468
|
+
const details = resolveStructuredErrorDetails(payload, message) ?? response.errorDetails?.cause ?? null;
|
|
3469
|
+
return {
|
|
3470
|
+
message,
|
|
3471
|
+
code,
|
|
3472
|
+
athenaCode: normalized.code,
|
|
3473
|
+
gatewayCode: response.errorDetails?.code ?? null,
|
|
3474
|
+
kind: normalized.kind,
|
|
3475
|
+
category: normalized.category,
|
|
3476
|
+
retryable: normalized.retryable,
|
|
3477
|
+
details,
|
|
3478
|
+
hint,
|
|
3479
|
+
status: result.status,
|
|
3480
|
+
statusText,
|
|
3481
|
+
constraint: normalized.constraint,
|
|
3482
|
+
table: normalized.table,
|
|
3483
|
+
operation: normalized.operation,
|
|
3484
|
+
endpoint: response.errorDetails?.endpoint,
|
|
3485
|
+
method: response.errorDetails?.method,
|
|
3486
|
+
requestId: response.errorDetails?.requestId,
|
|
3487
|
+
cause: response.errorDetails?.cause,
|
|
3488
|
+
raw: result.raw
|
|
3489
|
+
};
|
|
3490
|
+
}
|
|
3491
|
+
function parseQueryTraceCallsiteFrame(frame) {
|
|
3492
|
+
const trimmed = frame.trim();
|
|
3493
|
+
if (!trimmed) {
|
|
3494
|
+
return null;
|
|
3495
|
+
}
|
|
3496
|
+
let body = trimmed.replace(/^at\s+/, "");
|
|
3497
|
+
if (body.startsWith("async ")) {
|
|
3498
|
+
body = body.slice(6);
|
|
3499
|
+
}
|
|
3500
|
+
let functionName;
|
|
3501
|
+
let location = body;
|
|
3502
|
+
const wrappedMatch = body.match(/^(.*?)\s+\((.*)\)$/);
|
|
3503
|
+
if (wrappedMatch) {
|
|
3504
|
+
functionName = wrappedMatch[1].trim() || void 0;
|
|
3505
|
+
location = wrappedMatch[2].trim();
|
|
3506
|
+
}
|
|
3507
|
+
const locationMatch = location.match(/^(.*):(\d+):(\d+)$/);
|
|
3508
|
+
if (!locationMatch) {
|
|
3509
|
+
return null;
|
|
3510
|
+
}
|
|
3511
|
+
const filePath = locationMatch[1].replace(/^file:\/\//, "");
|
|
3512
|
+
const line = Number(locationMatch[2]);
|
|
3513
|
+
const column = Number(locationMatch[3]);
|
|
3514
|
+
if (!Number.isFinite(line) || !Number.isFinite(column)) {
|
|
3515
|
+
return null;
|
|
3516
|
+
}
|
|
3517
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
3518
|
+
const fileName = normalizedPath.split("/").at(-1) ?? filePath;
|
|
3519
|
+
return {
|
|
3520
|
+
filePath,
|
|
3521
|
+
fileName,
|
|
3522
|
+
line,
|
|
3523
|
+
column,
|
|
3524
|
+
frame: trimmed,
|
|
3525
|
+
functionName
|
|
3526
|
+
};
|
|
3527
|
+
}
|
|
3528
|
+
function captureQueryTraceCallsite() {
|
|
3529
|
+
const stack = new Error().stack;
|
|
3530
|
+
if (!stack) return null;
|
|
3531
|
+
const frames = stack.split("\n").slice(2).map((frame) => frame.trim()).filter(Boolean);
|
|
3532
|
+
for (const frame of frames) {
|
|
3533
|
+
if (QUERY_TRACE_STACK_SKIP_PATTERNS.some((pattern) => frame.includes(pattern))) {
|
|
3534
|
+
continue;
|
|
3535
|
+
}
|
|
3536
|
+
const callsite = parseQueryTraceCallsiteFrame(frame);
|
|
3537
|
+
if (callsite) return callsite;
|
|
3538
|
+
}
|
|
3539
|
+
const fallback = frames.find((frame) => !frame.includes("captureQueryTraceCallsite"));
|
|
3540
|
+
return fallback ? parseQueryTraceCallsiteFrame(fallback) : null;
|
|
3541
|
+
}
|
|
3542
|
+
function defaultQueryTraceLogger(event) {
|
|
3543
|
+
const target = event.table ?? event.functionName ?? "gateway";
|
|
3544
|
+
const outcomeState = event.outcome?.error ? "error" : "ok";
|
|
3545
|
+
const banner = `[athena-js][trace] ${event.operation.toUpperCase()} ${event.endpoint} ${target} ${event.durationMs}ms ${outcomeState}`;
|
|
3546
|
+
console.info(banner, event);
|
|
3547
|
+
}
|
|
3548
|
+
function captureTraceCallsite(tracer) {
|
|
3549
|
+
return tracer?.captureCallsite() ?? null;
|
|
3550
|
+
}
|
|
3551
|
+
function createTraceCallsiteStore(tracer, initialCallsite) {
|
|
3552
|
+
let storedCallsite = initialCallsite ?? void 0;
|
|
3553
|
+
return {
|
|
3554
|
+
resolve(callsite) {
|
|
3555
|
+
if (callsite) {
|
|
3556
|
+
storedCallsite = callsite;
|
|
3557
|
+
return callsite;
|
|
3558
|
+
}
|
|
3559
|
+
if (storedCallsite !== void 0) {
|
|
3560
|
+
return storedCallsite;
|
|
3561
|
+
}
|
|
3562
|
+
const capturedCallsite = captureTraceCallsite(tracer);
|
|
3563
|
+
if (capturedCallsite) {
|
|
3564
|
+
storedCallsite = capturedCallsite;
|
|
3565
|
+
}
|
|
3566
|
+
return capturedCallsite;
|
|
3567
|
+
}
|
|
3568
|
+
};
|
|
3569
|
+
}
|
|
3570
|
+
function createQueryTracer(experimental) {
|
|
3571
|
+
const traceOption = experimental?.traceQueries;
|
|
3572
|
+
if (!traceOption) {
|
|
3573
|
+
return void 0;
|
|
3574
|
+
}
|
|
3575
|
+
const logger = typeof traceOption === "object" && traceOption.logger ? traceOption.logger : defaultQueryTraceLogger;
|
|
3576
|
+
const emit = (event) => {
|
|
3577
|
+
try {
|
|
3578
|
+
logger(event);
|
|
3579
|
+
} catch (error) {
|
|
3580
|
+
console.warn("[athena-js][trace] logger failed", error);
|
|
3581
|
+
}
|
|
3582
|
+
};
|
|
3583
|
+
return {
|
|
3584
|
+
captureCallsite: captureQueryTraceCallsite,
|
|
3585
|
+
publishSuccess(context, result, durationMs, callsite) {
|
|
3586
|
+
emit({
|
|
3587
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3588
|
+
durationMs,
|
|
3589
|
+
operation: context.operation,
|
|
3590
|
+
endpoint: context.endpoint,
|
|
3591
|
+
table: context.table,
|
|
3592
|
+
functionName: context.functionName,
|
|
3593
|
+
sql: context.sql,
|
|
3594
|
+
payload: context.payload,
|
|
3595
|
+
options: context.options,
|
|
3596
|
+
callsite,
|
|
3597
|
+
outcome: {
|
|
3598
|
+
status: result.status,
|
|
3599
|
+
error: result.error,
|
|
3600
|
+
errorDetails: result.errorDetails ?? null,
|
|
3601
|
+
count: result.count ?? null,
|
|
3602
|
+
data: result.data,
|
|
3603
|
+
raw: result.raw
|
|
3604
|
+
}
|
|
3605
|
+
});
|
|
3606
|
+
},
|
|
3607
|
+
publishFailure(context, error, durationMs, callsite) {
|
|
3608
|
+
emit({
|
|
3609
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3610
|
+
durationMs,
|
|
3611
|
+
operation: context.operation,
|
|
3612
|
+
endpoint: context.endpoint,
|
|
3613
|
+
table: context.table,
|
|
3614
|
+
functionName: context.functionName,
|
|
3615
|
+
sql: context.sql,
|
|
3616
|
+
payload: context.payload,
|
|
3617
|
+
options: context.options,
|
|
3618
|
+
callsite,
|
|
3619
|
+
thrownError: error
|
|
3620
|
+
});
|
|
3621
|
+
}
|
|
3622
|
+
};
|
|
3623
|
+
}
|
|
3624
|
+
async function executeWithQueryTrace(tracer, context, runner, callsiteOverride) {
|
|
3625
|
+
if (!tracer) {
|
|
3626
|
+
return runner();
|
|
3627
|
+
}
|
|
3628
|
+
const callsite = callsiteOverride ?? tracer.captureCallsite();
|
|
3629
|
+
const startedAt = Date.now();
|
|
3630
|
+
try {
|
|
3631
|
+
const result = await runner();
|
|
3632
|
+
tracer.publishSuccess(context, result, Date.now() - startedAt, callsite);
|
|
3633
|
+
return result;
|
|
3634
|
+
} catch (error) {
|
|
3635
|
+
tracer.publishFailure(context, error, Date.now() - startedAt, callsite);
|
|
3636
|
+
throw error;
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
2298
3639
|
function toSingleResult(response) {
|
|
2299
3640
|
const payload = response.data;
|
|
2300
3641
|
const singleData = Array.isArray(payload) ? payload.length ? payload[0] : null : payload ?? null;
|
|
@@ -2315,15 +3656,16 @@ function asAthenaJsonObject(value) {
|
|
|
2315
3656
|
function asAthenaJsonObjectArray(values) {
|
|
2316
3657
|
return values;
|
|
2317
3658
|
}
|
|
2318
|
-
function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
|
|
3659
|
+
function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS, tracer, initialCallsite) {
|
|
2319
3660
|
let selectedColumns = defaultColumns === null ? void 0 : defaultColumns;
|
|
2320
3661
|
let selectedOptions;
|
|
2321
3662
|
let promise = null;
|
|
2322
|
-
const
|
|
3663
|
+
const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
|
|
3664
|
+
const run = (columns, options, callsite) => {
|
|
2323
3665
|
const payloadColumns = columns ?? selectedColumns;
|
|
2324
3666
|
const payloadOptions = options ?? selectedOptions;
|
|
2325
3667
|
if (!promise) {
|
|
2326
|
-
promise = executor(payloadColumns, payloadOptions);
|
|
3668
|
+
promise = executor(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
|
|
2327
3669
|
}
|
|
2328
3670
|
return promise;
|
|
2329
3671
|
};
|
|
@@ -2331,7 +3673,7 @@ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
|
|
|
2331
3673
|
select(columns = selectedColumns, options) {
|
|
2332
3674
|
selectedColumns = columns;
|
|
2333
3675
|
selectedOptions = options ?? selectedOptions;
|
|
2334
|
-
return run(columns, options);
|
|
3676
|
+
return run(columns, options, captureTraceCallsite(tracer));
|
|
2335
3677
|
},
|
|
2336
3678
|
returning(columns = selectedColumns, options) {
|
|
2337
3679
|
return mutationQuery.select(columns, options);
|
|
@@ -2339,7 +3681,7 @@ function createMutationQuery(executor, defaultColumns = DEFAULT_COLUMNS) {
|
|
|
2339
3681
|
single(columns = selectedColumns, options) {
|
|
2340
3682
|
selectedColumns = columns;
|
|
2341
3683
|
selectedOptions = options ?? selectedOptions;
|
|
2342
|
-
return run(columns, options).then(toSingleResult);
|
|
3684
|
+
return run(columns, options, captureTraceCallsite(tracer)).then(toSingleResult);
|
|
2343
3685
|
},
|
|
2344
3686
|
maybeSingle(columns = selectedColumns, options) {
|
|
2345
3687
|
return mutationQuery.single(columns, options);
|
|
@@ -2362,21 +3704,12 @@ function getResourceId(state) {
|
|
|
2362
3704
|
);
|
|
2363
3705
|
return candidate?.value?.toString();
|
|
2364
3706
|
}
|
|
2365
|
-
function
|
|
3707
|
+
function stringifyFilterValue2(value) {
|
|
2366
3708
|
if (Array.isArray(value)) {
|
|
2367
3709
|
return value.join(",");
|
|
2368
3710
|
}
|
|
2369
3711
|
return String(value);
|
|
2370
3712
|
}
|
|
2371
|
-
function isUuidString(value) {
|
|
2372
|
-
return UUID_PATTERN.test(value.trim());
|
|
2373
|
-
}
|
|
2374
|
-
function isUuidIdentifierColumn(column) {
|
|
2375
|
-
return column === "id" || /(?:^|_)uuid(?:_|$)/i.test(column) || /_id$/i.test(column);
|
|
2376
|
-
}
|
|
2377
|
-
function shouldUseUuidTextComparison(column, value) {
|
|
2378
|
-
return typeof value === "string" && isUuidString(value) && isUuidIdentifierColumn(column);
|
|
2379
|
-
}
|
|
2380
3713
|
function normalizeCast(cast) {
|
|
2381
3714
|
const normalized = cast.trim().toLowerCase();
|
|
2382
3715
|
if (!SAFE_CAST_PATTERN.test(normalized)) {
|
|
@@ -2399,25 +3732,92 @@ function withCast(expression, cast) {
|
|
|
2399
3732
|
}
|
|
2400
3733
|
function buildSelectColumnsClause(columns) {
|
|
2401
3734
|
if (Array.isArray(columns)) {
|
|
2402
|
-
return columns.map((column) =>
|
|
3735
|
+
return columns.map((column) => quoteSelectColumnToken(column)).join(", ");
|
|
2403
3736
|
}
|
|
2404
3737
|
return quoteSelectColumnsExpression(columns);
|
|
2405
3738
|
}
|
|
3739
|
+
function parseIdentifierSegment(input) {
|
|
3740
|
+
const trimmed = input.trim();
|
|
3741
|
+
if (!trimmed) return null;
|
|
3742
|
+
if (!trimmed.startsWith('"')) {
|
|
3743
|
+
return {
|
|
3744
|
+
normalizedValue: trimmed.toLowerCase()
|
|
3745
|
+
};
|
|
3746
|
+
}
|
|
3747
|
+
let value = "";
|
|
3748
|
+
let index = 1;
|
|
3749
|
+
let closed = false;
|
|
3750
|
+
while (index < trimmed.length) {
|
|
3751
|
+
const char = trimmed[index];
|
|
3752
|
+
const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
|
|
3753
|
+
if (char === '"' && next === '"') {
|
|
3754
|
+
value += '"';
|
|
3755
|
+
index += 2;
|
|
3756
|
+
continue;
|
|
3757
|
+
}
|
|
3758
|
+
if (char === '"') {
|
|
3759
|
+
closed = true;
|
|
3760
|
+
index += 1;
|
|
3761
|
+
break;
|
|
3762
|
+
}
|
|
3763
|
+
value += char;
|
|
3764
|
+
index += 1;
|
|
3765
|
+
}
|
|
3766
|
+
if (!closed || trimmed.slice(index).trim().length > 0 || !value.trim()) {
|
|
3767
|
+
return null;
|
|
3768
|
+
}
|
|
3769
|
+
return {
|
|
3770
|
+
normalizedValue: value
|
|
3771
|
+
};
|
|
3772
|
+
}
|
|
3773
|
+
function splitQualifiedTableName(tableName) {
|
|
3774
|
+
const trimmed = tableName.trim();
|
|
3775
|
+
let inQuotes = false;
|
|
3776
|
+
for (let index = 0; index < trimmed.length; index += 1) {
|
|
3777
|
+
const char = trimmed[index];
|
|
3778
|
+
const next = index + 1 < trimmed.length ? trimmed[index + 1] : "";
|
|
3779
|
+
if (char === '"') {
|
|
3780
|
+
if (inQuotes && next === '"') {
|
|
3781
|
+
index += 1;
|
|
3782
|
+
continue;
|
|
3783
|
+
}
|
|
3784
|
+
inQuotes = !inQuotes;
|
|
3785
|
+
continue;
|
|
3786
|
+
}
|
|
3787
|
+
if (char === "." && !inQuotes) {
|
|
3788
|
+
const schemaSegment = trimmed.slice(0, index).trim();
|
|
3789
|
+
const tableSegment = trimmed.slice(index + 1).trim();
|
|
3790
|
+
if (!schemaSegment || !tableSegment) {
|
|
3791
|
+
return null;
|
|
3792
|
+
}
|
|
3793
|
+
return { schemaSegment };
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
return null;
|
|
3797
|
+
}
|
|
2406
3798
|
function resolveTableNameForCall(tableName, schema) {
|
|
2407
3799
|
if (!schema) return tableName;
|
|
2408
3800
|
const normalizedSchema = schema.trim();
|
|
2409
3801
|
if (!normalizedSchema) {
|
|
2410
3802
|
throw new Error("schema option must be a non-empty string");
|
|
2411
3803
|
}
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
3804
|
+
const normalizedTableName = tableName.trim();
|
|
3805
|
+
const parsedSchema = parseIdentifierSegment(normalizedSchema);
|
|
3806
|
+
if (!parsedSchema) {
|
|
3807
|
+
throw new Error("schema option must be a non-empty string");
|
|
3808
|
+
}
|
|
3809
|
+
const qualified = splitQualifiedTableName(normalizedTableName);
|
|
3810
|
+
if (qualified) {
|
|
3811
|
+
const parsedTableSchema = parseIdentifierSegment(qualified.schemaSegment);
|
|
3812
|
+
const sameSchema = parsedTableSchema ? parsedTableSchema.normalizedValue === parsedSchema.normalizedValue : normalizedTableName.startsWith(`${normalizedSchema}.`);
|
|
3813
|
+
if (sameSchema) {
|
|
3814
|
+
return normalizedTableName;
|
|
2415
3815
|
}
|
|
2416
3816
|
throw new Error(
|
|
2417
|
-
`schema option "${normalizedSchema}" conflicts with schema-qualified table "${
|
|
3817
|
+
`schema option "${normalizedSchema}" conflicts with schema-qualified table "${normalizedTableName}"`
|
|
2418
3818
|
);
|
|
2419
3819
|
}
|
|
2420
|
-
return `${normalizedSchema}.${
|
|
3820
|
+
return `${normalizedSchema}.${normalizedTableName}`;
|
|
2421
3821
|
}
|
|
2422
3822
|
function conditionToSqlClause(condition) {
|
|
2423
3823
|
if (!condition.column) return null;
|
|
@@ -2495,6 +3895,237 @@ function buildTypedSelectQuery(input) {
|
|
|
2495
3895
|
}
|
|
2496
3896
|
return `${sqlParts.join(" ")};`;
|
|
2497
3897
|
}
|
|
3898
|
+
function sanitizeSqlComment(comment) {
|
|
3899
|
+
return comment.replace(/\*\//g, "* /");
|
|
3900
|
+
}
|
|
3901
|
+
function toSqlJsonLiteral(value) {
|
|
3902
|
+
if (value === void 0) return "DEFAULT";
|
|
3903
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
3904
|
+
return toSqlLiteral(value);
|
|
3905
|
+
}
|
|
3906
|
+
return `'${escapeSqlStringLiteral(JSON.stringify(value))}'::jsonb`;
|
|
3907
|
+
}
|
|
3908
|
+
function conditionToDebugSqlClause(condition) {
|
|
3909
|
+
const exact = conditionToSqlClause(condition);
|
|
3910
|
+
if (exact) return exact;
|
|
3911
|
+
const rawCondition = sanitizeSqlComment(JSON.stringify(condition));
|
|
3912
|
+
if (!condition.column) {
|
|
3913
|
+
return `TRUE /* unsupported condition: ${rawCondition} */`;
|
|
3914
|
+
}
|
|
3915
|
+
const column = withCast(quoteQualifiedIdentifier(condition.column), condition.column_cast);
|
|
3916
|
+
const value = condition.value;
|
|
3917
|
+
const rhs = withCast(toSqlJsonLiteral(value), condition.value_cast);
|
|
3918
|
+
switch (condition.operator) {
|
|
3919
|
+
case "contains":
|
|
3920
|
+
return `${column} @> ${rhs}`;
|
|
3921
|
+
case "containedBy":
|
|
3922
|
+
return `${column} <@ ${rhs}`;
|
|
3923
|
+
case "not":
|
|
3924
|
+
return `TRUE /* NOT expression passthrough: ${rawCondition} */`;
|
|
3925
|
+
case "or":
|
|
3926
|
+
return `TRUE /* OR expression passthrough: ${rawCondition} */`;
|
|
3927
|
+
default:
|
|
3928
|
+
return `TRUE /* unsupported condition: ${rawCondition} */`;
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
function resolvePagination(input) {
|
|
3932
|
+
let limit = input.limit;
|
|
3933
|
+
let offset = input.offset;
|
|
3934
|
+
if (limit === void 0 && input.pageSize !== void 0) {
|
|
3935
|
+
limit = input.pageSize;
|
|
3936
|
+
}
|
|
3937
|
+
if (offset === void 0 && input.pageSize !== void 0 && input.currentPage !== void 0 && input.currentPage > 0) {
|
|
3938
|
+
offset = (input.currentPage - 1) * input.pageSize;
|
|
3939
|
+
}
|
|
3940
|
+
return { limit, offset };
|
|
3941
|
+
}
|
|
3942
|
+
function appendOrderLimitOffset(sqlParts, order, limit, offset) {
|
|
3943
|
+
if (order?.field) {
|
|
3944
|
+
const direction = order.direction === "descending" ? "DESC" : "ASC";
|
|
3945
|
+
sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(order.field)} ${direction}`);
|
|
3946
|
+
}
|
|
3947
|
+
if (limit !== void 0) {
|
|
3948
|
+
sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
|
|
3949
|
+
}
|
|
3950
|
+
if (offset !== void 0) {
|
|
3951
|
+
sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
|
|
3952
|
+
}
|
|
3953
|
+
}
|
|
3954
|
+
function buildDebugSelectQuery(input) {
|
|
3955
|
+
const sqlParts = [
|
|
3956
|
+
`SELECT ${buildSelectColumnsClause(input.columns)} FROM ${quoteQualifiedIdentifier(input.tableName)}`
|
|
3957
|
+
];
|
|
3958
|
+
if (input.conditions?.length) {
|
|
3959
|
+
const whereClauses = input.conditions.map(conditionToDebugSqlClause);
|
|
3960
|
+
sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
|
|
3961
|
+
}
|
|
3962
|
+
const pagination = resolvePagination(input);
|
|
3963
|
+
appendOrderLimitOffset(sqlParts, input.order, pagination.limit, pagination.offset);
|
|
3964
|
+
return `${sqlParts.join(" ")};`;
|
|
3965
|
+
}
|
|
3966
|
+
function resolveDebugTableIdentifier(tableName) {
|
|
3967
|
+
if (!tableName?.trim()) {
|
|
3968
|
+
return '"__unknown_table__"';
|
|
3969
|
+
}
|
|
3970
|
+
return quoteQualifiedIdentifier(tableName);
|
|
3971
|
+
}
|
|
3972
|
+
function buildInsertDebugSql(payload) {
|
|
3973
|
+
const rows = Array.isArray(payload.insert_body) ? payload.insert_body : [payload.insert_body];
|
|
3974
|
+
const columns = [];
|
|
3975
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3976
|
+
for (const row of rows) {
|
|
3977
|
+
for (const column of Object.keys(row)) {
|
|
3978
|
+
if (seen.has(column)) continue;
|
|
3979
|
+
seen.add(column);
|
|
3980
|
+
columns.push(column);
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
const sqlParts = [`INSERT INTO ${quoteQualifiedIdentifier(payload.table_name)}`];
|
|
3984
|
+
if (!rows.length || !columns.length) {
|
|
3985
|
+
sqlParts.push("DEFAULT VALUES");
|
|
3986
|
+
if (rows.length > 1) {
|
|
3987
|
+
sqlParts.push(`/* trace: ${rows.length} rows collapsed to DEFAULT VALUES */`);
|
|
3988
|
+
}
|
|
3989
|
+
} else {
|
|
3990
|
+
const valuesClause = rows.map((row) => {
|
|
3991
|
+
const values = columns.map((column) => {
|
|
3992
|
+
const hasColumn = Object.prototype.hasOwnProperty.call(row, column);
|
|
3993
|
+
if (!hasColumn) {
|
|
3994
|
+
return payload.default_to_null ? "NULL" : "DEFAULT";
|
|
3995
|
+
}
|
|
3996
|
+
const rowValue = row[column];
|
|
3997
|
+
return toSqlJsonLiteral(rowValue);
|
|
3998
|
+
});
|
|
3999
|
+
return `(${values.join(", ")})`;
|
|
4000
|
+
}).join(", ");
|
|
4001
|
+
const columnClause = columns.map((column) => quoteQualifiedIdentifier(column)).join(", ");
|
|
4002
|
+
sqlParts.push(`(${columnClause})`);
|
|
4003
|
+
sqlParts.push(`VALUES ${valuesClause}`);
|
|
4004
|
+
}
|
|
4005
|
+
if (payload.on_conflict) {
|
|
4006
|
+
const conflictColumns = Array.isArray(payload.on_conflict) ? payload.on_conflict.map((column) => quoteQualifiedIdentifier(column)).join(", ") : payload.on_conflict;
|
|
4007
|
+
if (payload.update_body && Object.keys(payload.update_body).length > 0) {
|
|
4008
|
+
const assignments = Object.entries(payload.update_body).map(
|
|
4009
|
+
([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
|
|
4010
|
+
);
|
|
4011
|
+
sqlParts.push(`ON CONFLICT (${conflictColumns}) DO UPDATE SET ${assignments.join(", ")}`);
|
|
4012
|
+
} else {
|
|
4013
|
+
sqlParts.push(`ON CONFLICT (${conflictColumns}) DO NOTHING`);
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
if (payload.columns) {
|
|
4017
|
+
sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
|
|
4018
|
+
}
|
|
4019
|
+
return `${sqlParts.join(" ")};`;
|
|
4020
|
+
}
|
|
4021
|
+
function buildUpdateDebugSql(payload) {
|
|
4022
|
+
const set = payload.set ?? payload.data ?? {};
|
|
4023
|
+
const assignments = Object.entries(set).map(
|
|
4024
|
+
([column, value]) => `${quoteQualifiedIdentifier(column)} = ${toSqlJsonLiteral(value)}`
|
|
4025
|
+
);
|
|
4026
|
+
const sqlParts = [
|
|
4027
|
+
`UPDATE ${resolveDebugTableIdentifier(payload.table_name)} SET ${assignments.length ? assignments.join(", ") : "/* empty set */"}`
|
|
4028
|
+
];
|
|
4029
|
+
if (payload.conditions?.length) {
|
|
4030
|
+
const whereClauses = payload.conditions.map(conditionToDebugSqlClause);
|
|
4031
|
+
sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
|
|
4032
|
+
}
|
|
4033
|
+
const pagination = resolvePagination({
|
|
4034
|
+
currentPage: payload.current_page,
|
|
4035
|
+
pageSize: payload.page_size
|
|
4036
|
+
});
|
|
4037
|
+
appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
|
|
4038
|
+
if (payload.columns) {
|
|
4039
|
+
sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
|
|
4040
|
+
}
|
|
4041
|
+
return `${sqlParts.join(" ")};`;
|
|
4042
|
+
}
|
|
4043
|
+
function buildDeleteDebugSql(payload) {
|
|
4044
|
+
const sqlParts = [`DELETE FROM ${quoteQualifiedIdentifier(payload.table_name)}`];
|
|
4045
|
+
const whereClauses = [];
|
|
4046
|
+
if (payload.resource_id) {
|
|
4047
|
+
whereClauses.push(`"resource_id" = ${toSqlLiteral(payload.resource_id)}`);
|
|
4048
|
+
}
|
|
4049
|
+
if (payload.conditions?.length) {
|
|
4050
|
+
whereClauses.push(...payload.conditions.map(conditionToDebugSqlClause));
|
|
4051
|
+
}
|
|
4052
|
+
if (whereClauses.length) {
|
|
4053
|
+
sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
|
|
4054
|
+
}
|
|
4055
|
+
const pagination = resolvePagination({
|
|
4056
|
+
currentPage: payload.current_page,
|
|
4057
|
+
pageSize: payload.page_size
|
|
4058
|
+
});
|
|
4059
|
+
appendOrderLimitOffset(sqlParts, payload.sort_by, pagination.limit, pagination.offset);
|
|
4060
|
+
if (payload.columns) {
|
|
4061
|
+
sqlParts.push(`RETURNING ${buildSelectColumnsClause(payload.columns)}`);
|
|
4062
|
+
}
|
|
4063
|
+
return `${sqlParts.join(" ")};`;
|
|
4064
|
+
}
|
|
4065
|
+
function rpcFilterToSqlClause(filter) {
|
|
4066
|
+
const column = quoteQualifiedIdentifier(filter.column);
|
|
4067
|
+
const value = filter.value;
|
|
4068
|
+
switch (filter.operator) {
|
|
4069
|
+
case "eq":
|
|
4070
|
+
case "neq":
|
|
4071
|
+
case "gt":
|
|
4072
|
+
case "gte":
|
|
4073
|
+
case "lt":
|
|
4074
|
+
case "lte":
|
|
4075
|
+
case "like":
|
|
4076
|
+
case "ilike": {
|
|
4077
|
+
if (value === void 0 || Array.isArray(value)) {
|
|
4078
|
+
return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
|
|
4079
|
+
}
|
|
4080
|
+
const operatorMap = {
|
|
4081
|
+
eq: "=",
|
|
4082
|
+
neq: "!=",
|
|
4083
|
+
gt: ">",
|
|
4084
|
+
gte: ">=",
|
|
4085
|
+
lt: "<",
|
|
4086
|
+
lte: "<=",
|
|
4087
|
+
like: "LIKE",
|
|
4088
|
+
ilike: "ILIKE"
|
|
4089
|
+
};
|
|
4090
|
+
return `${column} ${operatorMap[filter.operator]} ${toSqlLiteral(value)}`;
|
|
4091
|
+
}
|
|
4092
|
+
case "is":
|
|
4093
|
+
if (value === null) return `${column} IS NULL`;
|
|
4094
|
+
if (value === true) return `${column} IS TRUE`;
|
|
4095
|
+
if (value === false) return `${column} IS FALSE`;
|
|
4096
|
+
return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
|
|
4097
|
+
case "in":
|
|
4098
|
+
if (!Array.isArray(value)) {
|
|
4099
|
+
return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
|
|
4100
|
+
}
|
|
4101
|
+
if (value.length === 0) return "FALSE";
|
|
4102
|
+
return `${column} IN (${value.map((item) => toSqlLiteral(item)).join(", ")})`;
|
|
4103
|
+
default:
|
|
4104
|
+
return `TRUE /* unsupported rpc filter: ${sanitizeSqlComment(JSON.stringify(filter))} */`;
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
function buildRpcDebugSql(payload) {
|
|
4108
|
+
const argsEntries = payload.args ? Object.entries(payload.args) : [];
|
|
4109
|
+
const argsClause = argsEntries.map(([key, value]) => `${quoteQualifiedIdentifier(key)} => ${toSqlJsonLiteral(value)}`).join(", ");
|
|
4110
|
+
const functionRef = payload.schema ? `${quoteQualifiedIdentifier(payload.schema)}.${quoteQualifiedIdentifier(payload.function)}` : quoteQualifiedIdentifier(payload.function);
|
|
4111
|
+
const sqlParts = [
|
|
4112
|
+
`SELECT ${payload.select ? quoteSelectColumnsExpression(payload.select) : "*"} FROM ${functionRef}(${argsClause})`
|
|
4113
|
+
];
|
|
4114
|
+
if (payload.filters?.length) {
|
|
4115
|
+
sqlParts.push(`WHERE ${payload.filters.map(rpcFilterToSqlClause).join(" AND ")}`);
|
|
4116
|
+
}
|
|
4117
|
+
if (payload.order?.column) {
|
|
4118
|
+
const direction = payload.order.ascending === false ? "DESC" : "ASC";
|
|
4119
|
+
sqlParts.push(`ORDER BY ${quoteQualifiedIdentifier(payload.order.column)} ${direction}`);
|
|
4120
|
+
}
|
|
4121
|
+
if (payload.limit !== void 0) {
|
|
4122
|
+
sqlParts.push(`LIMIT ${Math.max(0, Math.trunc(payload.limit))}`);
|
|
4123
|
+
}
|
|
4124
|
+
if (payload.offset !== void 0) {
|
|
4125
|
+
sqlParts.push(`OFFSET ${Math.max(0, Math.trunc(payload.offset))}`);
|
|
4126
|
+
}
|
|
4127
|
+
return `${sqlParts.join(" ")};`;
|
|
4128
|
+
}
|
|
2498
4129
|
function createFilterMethods(state, addCondition, self) {
|
|
2499
4130
|
return {
|
|
2500
4131
|
eq(column, value) {
|
|
@@ -2606,7 +4237,7 @@ function createFilterMethods(state, addCondition, self) {
|
|
|
2606
4237
|
not(columnOrExpression, operator, value) {
|
|
2607
4238
|
const expression = String(columnOrExpression);
|
|
2608
4239
|
if (operator != null && value !== void 0) {
|
|
2609
|
-
addCondition("not", void 0, `${expression}.${operator}.${
|
|
4240
|
+
addCondition("not", void 0, `${expression}.${operator}.${stringifyFilterValue2(value)}`);
|
|
2610
4241
|
} else {
|
|
2611
4242
|
addCondition("not", void 0, expression);
|
|
2612
4243
|
}
|
|
@@ -2669,14 +4300,15 @@ function createRpcFilterMethods(filters, self) {
|
|
|
2669
4300
|
}
|
|
2670
4301
|
};
|
|
2671
4302
|
}
|
|
2672
|
-
function createRpcBuilder(functionName, args, baseOptions, client) {
|
|
4303
|
+
function createRpcBuilder(functionName, args, baseOptions, client, formatGatewayResult, tracer, initialCallsite) {
|
|
2673
4304
|
const state = {
|
|
2674
4305
|
filters: []
|
|
2675
4306
|
};
|
|
2676
4307
|
let selectedColumns;
|
|
2677
4308
|
let selectedOptions;
|
|
2678
4309
|
let promise = null;
|
|
2679
|
-
const
|
|
4310
|
+
const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
|
|
4311
|
+
const executeRpc = async (columns, options, callsite) => {
|
|
2680
4312
|
const mergedOptions = mergeOptions(baseOptions, options);
|
|
2681
4313
|
const payload = {
|
|
2682
4314
|
function: functionName,
|
|
@@ -2690,14 +4322,30 @@ function createRpcBuilder(functionName, args, baseOptions, client) {
|
|
|
2690
4322
|
offset: state.offset,
|
|
2691
4323
|
order: state.order
|
|
2692
4324
|
};
|
|
2693
|
-
const
|
|
2694
|
-
|
|
4325
|
+
const endpoint = mergedOptions?.get ? `/rpc/${functionName}` : "/gateway/rpc";
|
|
4326
|
+
const sql = buildRpcDebugSql(payload);
|
|
4327
|
+
return executeWithQueryTrace(
|
|
4328
|
+
tracer,
|
|
4329
|
+
{
|
|
4330
|
+
operation: "rpc",
|
|
4331
|
+
endpoint,
|
|
4332
|
+
functionName,
|
|
4333
|
+
sql,
|
|
4334
|
+
payload,
|
|
4335
|
+
options: mergedOptions
|
|
4336
|
+
},
|
|
4337
|
+
async () => {
|
|
4338
|
+
const response = await client.rpcGateway(payload, mergedOptions);
|
|
4339
|
+
return formatGatewayResult(response, { operation: "rpc" });
|
|
4340
|
+
},
|
|
4341
|
+
callsite
|
|
4342
|
+
);
|
|
2695
4343
|
};
|
|
2696
|
-
const run = (columns, options) => {
|
|
4344
|
+
const run = (columns, options, callsite) => {
|
|
2697
4345
|
const payloadColumns = columns ?? selectedColumns;
|
|
2698
4346
|
const payloadOptions = options ?? selectedOptions;
|
|
2699
4347
|
if (!promise) {
|
|
2700
|
-
promise = executeRpc(payloadColumns, payloadOptions);
|
|
4348
|
+
promise = executeRpc(payloadColumns, payloadOptions, callsiteStore.resolve(callsite));
|
|
2701
4349
|
}
|
|
2702
4350
|
return promise;
|
|
2703
4351
|
};
|
|
@@ -2707,10 +4355,10 @@ function createRpcBuilder(functionName, args, baseOptions, client) {
|
|
|
2707
4355
|
select(columns = selectedColumns, options) {
|
|
2708
4356
|
selectedColumns = columns;
|
|
2709
4357
|
selectedOptions = options ?? selectedOptions;
|
|
2710
|
-
return run(columns, options);
|
|
4358
|
+
return run(columns, options, captureTraceCallsite(tracer));
|
|
2711
4359
|
},
|
|
2712
4360
|
async single(columns, options) {
|
|
2713
|
-
const result = await run(columns, options);
|
|
4361
|
+
const result = await run(columns, options, captureTraceCallsite(tracer));
|
|
2714
4362
|
return toSingleResult(result);
|
|
2715
4363
|
},
|
|
2716
4364
|
maybeSingle(columns, options) {
|
|
@@ -2745,7 +4393,7 @@ function createRpcBuilder(functionName, args, baseOptions, client) {
|
|
|
2745
4393
|
});
|
|
2746
4394
|
return builder;
|
|
2747
4395
|
}
|
|
2748
|
-
function createTableBuilder(tableName, client) {
|
|
4396
|
+
function createTableBuilder(tableName, client, formatGatewayResult, tracer, experimental) {
|
|
2749
4397
|
const state = {
|
|
2750
4398
|
conditions: []
|
|
2751
4399
|
};
|
|
@@ -2777,15 +4425,24 @@ function createTableBuilder(tableName, client) {
|
|
|
2777
4425
|
}
|
|
2778
4426
|
state.conditions.push(condition);
|
|
2779
4427
|
};
|
|
4428
|
+
const snapshotState = () => ({
|
|
4429
|
+
conditions: state.conditions.map((condition) => ({ ...condition })),
|
|
4430
|
+
limit: state.limit,
|
|
4431
|
+
offset: state.offset,
|
|
4432
|
+
order: state.order ? { ...state.order } : void 0,
|
|
4433
|
+
currentPage: state.currentPage,
|
|
4434
|
+
pageSize: state.pageSize,
|
|
4435
|
+
totalPages: state.totalPages
|
|
4436
|
+
});
|
|
2780
4437
|
const builder = {};
|
|
2781
4438
|
const filterMethods = createFilterMethods(
|
|
2782
4439
|
state,
|
|
2783
4440
|
addCondition,
|
|
2784
4441
|
builder
|
|
2785
4442
|
);
|
|
2786
|
-
const runSelect = async (columns = DEFAULT_COLUMNS, options) => {
|
|
4443
|
+
const runSelect = async (columns = DEFAULT_COLUMNS, options, executionState = snapshotState(), callsite) => {
|
|
2787
4444
|
const resolvedTableName = resolveTableNameForCall(tableName, options?.schema);
|
|
2788
|
-
const conditions =
|
|
4445
|
+
const conditions = executionState.conditions.length ? executionState.conditions.map((condition) => ({ ...condition })) : void 0;
|
|
2789
4446
|
const hasTypedEqualityComparison = conditions?.some(
|
|
2790
4447
|
(condition) => condition.operator === "eq" && (condition.value_cast !== void 0 || condition.column_cast !== void 0)
|
|
2791
4448
|
) ?? false;
|
|
@@ -2794,53 +4451,107 @@ function createTableBuilder(tableName, client) {
|
|
|
2794
4451
|
tableName: resolvedTableName,
|
|
2795
4452
|
columns,
|
|
2796
4453
|
conditions,
|
|
2797
|
-
limit:
|
|
2798
|
-
offset:
|
|
2799
|
-
currentPage:
|
|
2800
|
-
pageSize:
|
|
2801
|
-
order:
|
|
4454
|
+
limit: executionState.limit,
|
|
4455
|
+
offset: executionState.offset,
|
|
4456
|
+
currentPage: executionState.currentPage,
|
|
4457
|
+
pageSize: executionState.pageSize,
|
|
4458
|
+
order: executionState.order
|
|
2802
4459
|
});
|
|
2803
4460
|
if (query) {
|
|
2804
|
-
const
|
|
2805
|
-
return
|
|
4461
|
+
const payload2 = { query };
|
|
4462
|
+
return executeWithQueryTrace(
|
|
4463
|
+
tracer,
|
|
4464
|
+
{
|
|
4465
|
+
operation: "select",
|
|
4466
|
+
endpoint: "/gateway/query",
|
|
4467
|
+
table: resolvedTableName,
|
|
4468
|
+
sql: query,
|
|
4469
|
+
payload: payload2,
|
|
4470
|
+
options
|
|
4471
|
+
},
|
|
4472
|
+
async () => {
|
|
4473
|
+
const queryResponse = await client.queryGateway(payload2, options);
|
|
4474
|
+
return formatGatewayResult(queryResponse, { table: resolvedTableName, operation: "select" });
|
|
4475
|
+
},
|
|
4476
|
+
callsite
|
|
4477
|
+
);
|
|
2806
4478
|
}
|
|
2807
4479
|
}
|
|
2808
4480
|
const payload = {
|
|
2809
4481
|
table_name: resolvedTableName,
|
|
2810
4482
|
columns,
|
|
2811
4483
|
conditions,
|
|
2812
|
-
limit:
|
|
2813
|
-
offset:
|
|
2814
|
-
current_page:
|
|
2815
|
-
page_size:
|
|
2816
|
-
total_pages:
|
|
2817
|
-
sort_by:
|
|
4484
|
+
limit: executionState.limit,
|
|
4485
|
+
offset: executionState.offset,
|
|
4486
|
+
current_page: executionState.currentPage,
|
|
4487
|
+
page_size: executionState.pageSize,
|
|
4488
|
+
total_pages: executionState.totalPages,
|
|
4489
|
+
sort_by: executionState.order,
|
|
2818
4490
|
strip_nulls: options?.stripNulls ?? true,
|
|
2819
4491
|
count: options?.count,
|
|
2820
4492
|
head: options?.head
|
|
2821
4493
|
};
|
|
2822
|
-
const
|
|
2823
|
-
|
|
4494
|
+
const sql = buildDebugSelectQuery({
|
|
4495
|
+
tableName: resolvedTableName,
|
|
4496
|
+
columns,
|
|
4497
|
+
conditions,
|
|
4498
|
+
limit: executionState.limit,
|
|
4499
|
+
offset: executionState.offset,
|
|
4500
|
+
currentPage: executionState.currentPage,
|
|
4501
|
+
pageSize: executionState.pageSize,
|
|
4502
|
+
order: executionState.order
|
|
4503
|
+
});
|
|
4504
|
+
return executeWithQueryTrace(
|
|
4505
|
+
tracer,
|
|
4506
|
+
{
|
|
4507
|
+
operation: "select",
|
|
4508
|
+
endpoint: "/gateway/fetch",
|
|
4509
|
+
table: resolvedTableName,
|
|
4510
|
+
sql,
|
|
4511
|
+
payload,
|
|
4512
|
+
options
|
|
4513
|
+
},
|
|
4514
|
+
async () => {
|
|
4515
|
+
const response = await client.fetchGateway(payload, options);
|
|
4516
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
|
|
4517
|
+
},
|
|
4518
|
+
callsite
|
|
4519
|
+
);
|
|
2824
4520
|
};
|
|
2825
|
-
const createSelectChain = (columns, options) => {
|
|
4521
|
+
const createSelectChain = (columns, options, initialCallsite) => {
|
|
2826
4522
|
const chain = {};
|
|
4523
|
+
const callsiteStore = createTraceCallsiteStore(tracer, initialCallsite);
|
|
2827
4524
|
const filterMethods2 = createFilterMethods(state, addCondition, chain);
|
|
2828
4525
|
Object.assign(chain, filterMethods2, {
|
|
2829
4526
|
async single(cols, opts) {
|
|
2830
|
-
const r = await runSelect(
|
|
4527
|
+
const r = await runSelect(
|
|
4528
|
+
cols ?? columns,
|
|
4529
|
+
opts ?? options,
|
|
4530
|
+
snapshotState(),
|
|
4531
|
+
callsiteStore.resolve(captureTraceCallsite(tracer))
|
|
4532
|
+
);
|
|
2831
4533
|
return toSingleResult(r);
|
|
2832
4534
|
},
|
|
2833
4535
|
maybeSingle(cols, opts) {
|
|
2834
4536
|
return chain.single(cols, opts);
|
|
2835
4537
|
},
|
|
2836
4538
|
then(onfulfilled, onrejected) {
|
|
2837
|
-
return runSelect(
|
|
4539
|
+
return runSelect(
|
|
4540
|
+
columns,
|
|
4541
|
+
options,
|
|
4542
|
+
snapshotState(),
|
|
4543
|
+
callsiteStore.resolve()
|
|
4544
|
+
).then(onfulfilled, onrejected);
|
|
2838
4545
|
},
|
|
2839
4546
|
catch(onrejected) {
|
|
2840
|
-
return runSelect(columns, options).catch(
|
|
4547
|
+
return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).catch(
|
|
4548
|
+
onrejected
|
|
4549
|
+
);
|
|
2841
4550
|
},
|
|
2842
4551
|
finally(onfinally) {
|
|
2843
|
-
return runSelect(columns, options).finally(
|
|
4552
|
+
return runSelect(columns, options, snapshotState(), callsiteStore.resolve()).finally(
|
|
4553
|
+
onfinally
|
|
4554
|
+
);
|
|
2844
4555
|
}
|
|
2845
4556
|
});
|
|
2846
4557
|
return chain;
|
|
@@ -2857,11 +4568,75 @@ function createTableBuilder(tableName, client) {
|
|
|
2857
4568
|
return builder;
|
|
2858
4569
|
},
|
|
2859
4570
|
select(columns = DEFAULT_COLUMNS, options) {
|
|
2860
|
-
return createSelectChain(columns, options);
|
|
4571
|
+
return createSelectChain(columns, options, captureTraceCallsite(tracer));
|
|
4572
|
+
},
|
|
4573
|
+
async findMany(options) {
|
|
4574
|
+
const columns = compileSelectShape(options.select);
|
|
4575
|
+
const baseState = snapshotState();
|
|
4576
|
+
const executionState = snapshotState();
|
|
4577
|
+
const callsite = captureTraceCallsite(tracer);
|
|
4578
|
+
const compiledWhere = compileWhere(options.where);
|
|
4579
|
+
if (compiledWhere?.length) {
|
|
4580
|
+
executionState.conditions.push(...compiledWhere);
|
|
4581
|
+
}
|
|
4582
|
+
if (options.orderBy !== void 0) {
|
|
4583
|
+
executionState.order = compileOrderBy(options.orderBy);
|
|
4584
|
+
}
|
|
4585
|
+
if (options.limit !== void 0) {
|
|
4586
|
+
executionState.limit = options.limit;
|
|
4587
|
+
}
|
|
4588
|
+
if (experimental?.findManyAst && canUseFindManyAstTransport(baseState)) {
|
|
4589
|
+
const resolvedTableName = resolveTableNameForCall(tableName, void 0);
|
|
4590
|
+
const payload = {
|
|
4591
|
+
table_name: resolvedTableName,
|
|
4592
|
+
select: options.select
|
|
4593
|
+
};
|
|
4594
|
+
if (options.where !== void 0) {
|
|
4595
|
+
payload.where = options.where;
|
|
4596
|
+
}
|
|
4597
|
+
const astOrder = toFindManyAstOrder(executionState.order);
|
|
4598
|
+
if (astOrder !== void 0) {
|
|
4599
|
+
payload.orderBy = astOrder;
|
|
4600
|
+
}
|
|
4601
|
+
if (executionState.limit !== void 0) {
|
|
4602
|
+
payload.limit = executionState.limit;
|
|
4603
|
+
}
|
|
4604
|
+
const sql = buildDebugSelectQuery({
|
|
4605
|
+
tableName: resolvedTableName,
|
|
4606
|
+
columns,
|
|
4607
|
+
conditions: executionState.conditions,
|
|
4608
|
+
limit: executionState.limit,
|
|
4609
|
+
order: executionState.order
|
|
4610
|
+
});
|
|
4611
|
+
return executeWithQueryTrace(
|
|
4612
|
+
tracer,
|
|
4613
|
+
{
|
|
4614
|
+
operation: "select",
|
|
4615
|
+
endpoint: "/gateway/fetch",
|
|
4616
|
+
table: resolvedTableName,
|
|
4617
|
+
sql,
|
|
4618
|
+
payload
|
|
4619
|
+
},
|
|
4620
|
+
async () => {
|
|
4621
|
+
const response = await client.fetchGateway(
|
|
4622
|
+
payload
|
|
4623
|
+
);
|
|
4624
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "select" });
|
|
4625
|
+
},
|
|
4626
|
+
callsite
|
|
4627
|
+
);
|
|
4628
|
+
}
|
|
4629
|
+
return runSelect(
|
|
4630
|
+
columns,
|
|
4631
|
+
void 0,
|
|
4632
|
+
executionState,
|
|
4633
|
+
callsite
|
|
4634
|
+
);
|
|
2861
4635
|
},
|
|
2862
4636
|
insert(values, options) {
|
|
4637
|
+
const mutationCallsite = captureTraceCallsite(tracer);
|
|
2863
4638
|
if (Array.isArray(values)) {
|
|
2864
|
-
const executeInsertMany = async (columns, selectOptions) => {
|
|
4639
|
+
const executeInsertMany = async (columns, selectOptions, callsite) => {
|
|
2865
4640
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
2866
4641
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
2867
4642
|
const payload = {
|
|
@@ -2874,12 +4649,27 @@ function createTableBuilder(tableName, client) {
|
|
|
2874
4649
|
if (mergedOptions?.defaultToNull !== void 0) {
|
|
2875
4650
|
payload.default_to_null = mergedOptions.defaultToNull;
|
|
2876
4651
|
}
|
|
2877
|
-
const
|
|
2878
|
-
return
|
|
4652
|
+
const sql = buildInsertDebugSql(payload);
|
|
4653
|
+
return executeWithQueryTrace(
|
|
4654
|
+
tracer,
|
|
4655
|
+
{
|
|
4656
|
+
operation: "insert",
|
|
4657
|
+
endpoint: "/gateway/insert",
|
|
4658
|
+
table: resolvedTableName,
|
|
4659
|
+
sql,
|
|
4660
|
+
payload,
|
|
4661
|
+
options: mergedOptions
|
|
4662
|
+
},
|
|
4663
|
+
async () => {
|
|
4664
|
+
const response = await client.insertGateway(payload, mergedOptions);
|
|
4665
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
|
|
4666
|
+
},
|
|
4667
|
+
callsite
|
|
4668
|
+
);
|
|
2879
4669
|
};
|
|
2880
|
-
return createMutationQuery(executeInsertMany);
|
|
4670
|
+
return createMutationQuery(executeInsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
|
|
2881
4671
|
}
|
|
2882
|
-
const executeInsertOne = async (columns, selectOptions) => {
|
|
4672
|
+
const executeInsertOne = async (columns, selectOptions, callsite) => {
|
|
2883
4673
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
2884
4674
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
2885
4675
|
const payload = {
|
|
@@ -2892,14 +4682,30 @@ function createTableBuilder(tableName, client) {
|
|
|
2892
4682
|
if (mergedOptions?.defaultToNull !== void 0) {
|
|
2893
4683
|
payload.default_to_null = mergedOptions.defaultToNull;
|
|
2894
4684
|
}
|
|
2895
|
-
const
|
|
2896
|
-
return
|
|
4685
|
+
const sql = buildInsertDebugSql(payload);
|
|
4686
|
+
return executeWithQueryTrace(
|
|
4687
|
+
tracer,
|
|
4688
|
+
{
|
|
4689
|
+
operation: "insert",
|
|
4690
|
+
endpoint: "/gateway/insert",
|
|
4691
|
+
table: resolvedTableName,
|
|
4692
|
+
sql,
|
|
4693
|
+
payload,
|
|
4694
|
+
options: mergedOptions
|
|
4695
|
+
},
|
|
4696
|
+
async () => {
|
|
4697
|
+
const response = await client.insertGateway(payload, mergedOptions);
|
|
4698
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
|
|
4699
|
+
},
|
|
4700
|
+
callsite
|
|
4701
|
+
);
|
|
2897
4702
|
};
|
|
2898
|
-
return createMutationQuery(executeInsertOne);
|
|
4703
|
+
return createMutationQuery(executeInsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
|
|
2899
4704
|
},
|
|
2900
4705
|
upsert(values, options) {
|
|
4706
|
+
const mutationCallsite = captureTraceCallsite(tracer);
|
|
2901
4707
|
if (Array.isArray(values)) {
|
|
2902
|
-
const executeUpsertMany = async (columns, selectOptions) => {
|
|
4708
|
+
const executeUpsertMany = async (columns, selectOptions, callsite) => {
|
|
2903
4709
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
2904
4710
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
2905
4711
|
const payload = {
|
|
@@ -2914,12 +4720,27 @@ function createTableBuilder(tableName, client) {
|
|
|
2914
4720
|
if (mergedOptions?.defaultToNull !== void 0) {
|
|
2915
4721
|
payload.default_to_null = mergedOptions.defaultToNull;
|
|
2916
4722
|
}
|
|
2917
|
-
const
|
|
2918
|
-
return
|
|
4723
|
+
const sql = buildInsertDebugSql(payload);
|
|
4724
|
+
return executeWithQueryTrace(
|
|
4725
|
+
tracer,
|
|
4726
|
+
{
|
|
4727
|
+
operation: "upsert",
|
|
4728
|
+
endpoint: "/gateway/insert",
|
|
4729
|
+
table: resolvedTableName,
|
|
4730
|
+
sql,
|
|
4731
|
+
payload,
|
|
4732
|
+
options: mergedOptions
|
|
4733
|
+
},
|
|
4734
|
+
async () => {
|
|
4735
|
+
const response = await client.insertGateway(payload, mergedOptions);
|
|
4736
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
|
|
4737
|
+
},
|
|
4738
|
+
callsite
|
|
4739
|
+
);
|
|
2919
4740
|
};
|
|
2920
|
-
return createMutationQuery(executeUpsertMany);
|
|
4741
|
+
return createMutationQuery(executeUpsertMany, DEFAULT_COLUMNS, tracer, mutationCallsite);
|
|
2921
4742
|
}
|
|
2922
|
-
const executeUpsertOne = async (columns, selectOptions) => {
|
|
4743
|
+
const executeUpsertOne = async (columns, selectOptions, callsite) => {
|
|
2923
4744
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
2924
4745
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
2925
4746
|
const payload = {
|
|
@@ -2934,13 +4755,29 @@ function createTableBuilder(tableName, client) {
|
|
|
2934
4755
|
if (mergedOptions?.defaultToNull !== void 0) {
|
|
2935
4756
|
payload.default_to_null = mergedOptions.defaultToNull;
|
|
2936
4757
|
}
|
|
2937
|
-
const
|
|
2938
|
-
return
|
|
4758
|
+
const sql = buildInsertDebugSql(payload);
|
|
4759
|
+
return executeWithQueryTrace(
|
|
4760
|
+
tracer,
|
|
4761
|
+
{
|
|
4762
|
+
operation: "upsert",
|
|
4763
|
+
endpoint: "/gateway/insert",
|
|
4764
|
+
table: resolvedTableName,
|
|
4765
|
+
sql,
|
|
4766
|
+
payload,
|
|
4767
|
+
options: mergedOptions
|
|
4768
|
+
},
|
|
4769
|
+
async () => {
|
|
4770
|
+
const response = await client.insertGateway(payload, mergedOptions);
|
|
4771
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "insert" });
|
|
4772
|
+
},
|
|
4773
|
+
callsite
|
|
4774
|
+
);
|
|
2939
4775
|
};
|
|
2940
|
-
return createMutationQuery(executeUpsertOne);
|
|
4776
|
+
return createMutationQuery(executeUpsertOne, DEFAULT_COLUMNS, tracer, mutationCallsite);
|
|
2941
4777
|
},
|
|
2942
4778
|
update(values, options) {
|
|
2943
|
-
const
|
|
4779
|
+
const mutationCallsite = captureTraceCallsite(tracer);
|
|
4780
|
+
const executeUpdate = async (columns, selectOptions, callsite) => {
|
|
2944
4781
|
const filters = state.conditions.length ? [...state.conditions] : void 0;
|
|
2945
4782
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
2946
4783
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
@@ -2955,10 +4792,25 @@ function createTableBuilder(tableName, client) {
|
|
|
2955
4792
|
if (state.pageSize !== void 0) payload.page_size = state.pageSize;
|
|
2956
4793
|
if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
|
|
2957
4794
|
if (columns) payload.columns = columns;
|
|
2958
|
-
const
|
|
2959
|
-
return
|
|
4795
|
+
const sql = buildUpdateDebugSql(payload);
|
|
4796
|
+
return executeWithQueryTrace(
|
|
4797
|
+
tracer,
|
|
4798
|
+
{
|
|
4799
|
+
operation: "update",
|
|
4800
|
+
endpoint: "/gateway/update",
|
|
4801
|
+
table: resolvedTableName,
|
|
4802
|
+
sql,
|
|
4803
|
+
payload,
|
|
4804
|
+
options: mergedOptions
|
|
4805
|
+
},
|
|
4806
|
+
async () => {
|
|
4807
|
+
const response = await client.updateGateway(payload, mergedOptions);
|
|
4808
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "update" });
|
|
4809
|
+
},
|
|
4810
|
+
callsite
|
|
4811
|
+
);
|
|
2960
4812
|
};
|
|
2961
|
-
const mutation = createMutationQuery(executeUpdate, null);
|
|
4813
|
+
const mutation = createMutationQuery(executeUpdate, null, tracer, mutationCallsite);
|
|
2962
4814
|
const updateChain = {};
|
|
2963
4815
|
const filterMethods2 = createFilterMethods(state, addCondition, updateChain);
|
|
2964
4816
|
Object.assign(updateChain, filterMethods2, mutation);
|
|
@@ -2970,7 +4822,8 @@ function createTableBuilder(tableName, client) {
|
|
|
2970
4822
|
if (!resourceId && !filters?.length) {
|
|
2971
4823
|
throw new Error('delete requires a resource_id either via eq("resource_id", ...) or options.resourceId');
|
|
2972
4824
|
}
|
|
2973
|
-
const
|
|
4825
|
+
const mutationCallsite = captureTraceCallsite(tracer);
|
|
4826
|
+
const executeDelete = async (columns, selectOptions, callsite) => {
|
|
2974
4827
|
const mergedOptions = mergeOptions(options, selectOptions);
|
|
2975
4828
|
const resolvedTableName = resolveTableNameForCall(tableName, mergedOptions?.schema);
|
|
2976
4829
|
const payload = {
|
|
@@ -2983,13 +4836,33 @@ function createTableBuilder(tableName, client) {
|
|
|
2983
4836
|
if (state.pageSize !== void 0) payload.page_size = state.pageSize;
|
|
2984
4837
|
if (state.totalPages !== void 0) payload.total_pages = state.totalPages;
|
|
2985
4838
|
if (columns) payload.columns = columns;
|
|
2986
|
-
const
|
|
2987
|
-
return
|
|
4839
|
+
const sql = buildDeleteDebugSql(payload);
|
|
4840
|
+
return executeWithQueryTrace(
|
|
4841
|
+
tracer,
|
|
4842
|
+
{
|
|
4843
|
+
operation: "delete",
|
|
4844
|
+
endpoint: "/gateway/delete",
|
|
4845
|
+
table: resolvedTableName,
|
|
4846
|
+
sql,
|
|
4847
|
+
payload,
|
|
4848
|
+
options: mergedOptions
|
|
4849
|
+
},
|
|
4850
|
+
async () => {
|
|
4851
|
+
const response = await client.deleteGateway(payload, mergedOptions);
|
|
4852
|
+
return formatGatewayResult(response, { table: resolvedTableName, operation: "delete" });
|
|
4853
|
+
},
|
|
4854
|
+
callsite
|
|
4855
|
+
);
|
|
2988
4856
|
};
|
|
2989
|
-
return createMutationQuery(executeDelete, null);
|
|
4857
|
+
return createMutationQuery(executeDelete, null, tracer, mutationCallsite);
|
|
2990
4858
|
},
|
|
2991
4859
|
async single(columns, options) {
|
|
2992
|
-
const response = await
|
|
4860
|
+
const response = await runSelect(
|
|
4861
|
+
columns ?? DEFAULT_COLUMNS,
|
|
4862
|
+
options,
|
|
4863
|
+
snapshotState(),
|
|
4864
|
+
captureTraceCallsite(tracer)
|
|
4865
|
+
);
|
|
2993
4866
|
return toSingleResult(response);
|
|
2994
4867
|
},
|
|
2995
4868
|
async maybeSingle(columns, options) {
|
|
@@ -2998,14 +4871,29 @@ function createTableBuilder(tableName, client) {
|
|
|
2998
4871
|
});
|
|
2999
4872
|
return builder;
|
|
3000
4873
|
}
|
|
3001
|
-
function createQueryBuilder(client) {
|
|
4874
|
+
function createQueryBuilder(client, formatGatewayResult, tracer) {
|
|
3002
4875
|
return async function query(query, options) {
|
|
3003
4876
|
const normalizedQuery = query.trim();
|
|
3004
4877
|
if (!normalizedQuery) {
|
|
3005
4878
|
throw new Error("query requires a non-empty string");
|
|
3006
4879
|
}
|
|
3007
|
-
const
|
|
3008
|
-
|
|
4880
|
+
const payload = { query: normalizedQuery };
|
|
4881
|
+
const callsite = captureTraceCallsite(tracer);
|
|
4882
|
+
return executeWithQueryTrace(
|
|
4883
|
+
tracer,
|
|
4884
|
+
{
|
|
4885
|
+
operation: "query",
|
|
4886
|
+
endpoint: "/gateway/query",
|
|
4887
|
+
sql: normalizedQuery,
|
|
4888
|
+
payload,
|
|
4889
|
+
options
|
|
4890
|
+
},
|
|
4891
|
+
async () => {
|
|
4892
|
+
const response = await client.queryGateway(payload, options);
|
|
4893
|
+
return formatGatewayResult(response, { operation: "query" });
|
|
4894
|
+
},
|
|
4895
|
+
callsite
|
|
4896
|
+
);
|
|
3009
4897
|
};
|
|
3010
4898
|
}
|
|
3011
4899
|
function createClientFromConfig(config) {
|
|
@@ -3016,24 +4904,32 @@ function createClientFromConfig(config) {
|
|
|
3016
4904
|
backend: config.backend,
|
|
3017
4905
|
headers: config.headers
|
|
3018
4906
|
});
|
|
4907
|
+
const formatGatewayResult = createResultFormatter();
|
|
4908
|
+
const queryTracer = createQueryTracer(config.experimental);
|
|
3019
4909
|
const auth = createAuthClient(config.auth);
|
|
4910
|
+
const from = (table) => createTableBuilder(table, gateway, formatGatewayResult, queryTracer, config.experimental);
|
|
4911
|
+
const rpc = (fn, args, options) => {
|
|
4912
|
+
const normalizedFn = fn.trim();
|
|
4913
|
+
if (!normalizedFn) {
|
|
4914
|
+
throw new Error("rpc requires a function name");
|
|
4915
|
+
}
|
|
4916
|
+
return createRpcBuilder(
|
|
4917
|
+
normalizedFn,
|
|
4918
|
+
args,
|
|
4919
|
+
options,
|
|
4920
|
+
gateway,
|
|
4921
|
+
formatGatewayResult,
|
|
4922
|
+
queryTracer,
|
|
4923
|
+
captureTraceCallsite(queryTracer)
|
|
4924
|
+
);
|
|
4925
|
+
};
|
|
4926
|
+
const query = createQueryBuilder(gateway, formatGatewayResult, queryTracer);
|
|
4927
|
+
const db = createDbModule({ from, rpc, query });
|
|
3020
4928
|
return {
|
|
3021
|
-
from
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
const normalizedFn = fn.trim();
|
|
3026
|
-
if (!normalizedFn) {
|
|
3027
|
-
throw new Error("rpc requires a function name");
|
|
3028
|
-
}
|
|
3029
|
-
return createRpcBuilder(
|
|
3030
|
-
normalizedFn,
|
|
3031
|
-
args,
|
|
3032
|
-
options,
|
|
3033
|
-
gateway
|
|
3034
|
-
);
|
|
3035
|
-
},
|
|
3036
|
-
query: createQueryBuilder(gateway),
|
|
4929
|
+
from,
|
|
4930
|
+
db,
|
|
4931
|
+
rpc,
|
|
4932
|
+
query,
|
|
3037
4933
|
auth: auth.auth
|
|
3038
4934
|
};
|
|
3039
4935
|
}
|
|
@@ -3042,77 +4938,16 @@ function toBackendConfig(b) {
|
|
|
3042
4938
|
if (!b) return DEFAULT_BACKEND;
|
|
3043
4939
|
return typeof b === "string" ? { type: b } : b;
|
|
3044
4940
|
}
|
|
3045
|
-
var AthenaClientBuilderImpl = class {
|
|
3046
|
-
baseUrl;
|
|
3047
|
-
apiKey;
|
|
3048
|
-
backendConfig = DEFAULT_BACKEND;
|
|
3049
|
-
clientName;
|
|
3050
|
-
defaultHeaders;
|
|
3051
|
-
isHealthTrackingEnabled = false;
|
|
3052
|
-
url(url) {
|
|
3053
|
-
this.baseUrl = url;
|
|
3054
|
-
return this;
|
|
3055
|
-
}
|
|
3056
|
-
key(apiKey) {
|
|
3057
|
-
this.apiKey = apiKey;
|
|
3058
|
-
return this;
|
|
3059
|
-
}
|
|
3060
|
-
backend(backend) {
|
|
3061
|
-
this.backendConfig = toBackendConfig(backend);
|
|
3062
|
-
return this;
|
|
3063
|
-
}
|
|
3064
|
-
client(clientName) {
|
|
3065
|
-
this.clientName = clientName;
|
|
3066
|
-
return this;
|
|
3067
|
-
}
|
|
3068
|
-
headers(headers) {
|
|
3069
|
-
this.defaultHeaders = headers;
|
|
3070
|
-
return this;
|
|
3071
|
-
}
|
|
3072
|
-
healthTracking(enabled) {
|
|
3073
|
-
this.isHealthTrackingEnabled = enabled;
|
|
3074
|
-
return this;
|
|
3075
|
-
}
|
|
3076
|
-
build() {
|
|
3077
|
-
if (!this.baseUrl || !this.apiKey) {
|
|
3078
|
-
throw new Error("AthenaClient requires url and key; call .url() and .key() before .build()");
|
|
3079
|
-
}
|
|
3080
|
-
return createClientFromConfig({
|
|
3081
|
-
baseUrl: this.baseUrl,
|
|
3082
|
-
apiKey: this.apiKey,
|
|
3083
|
-
client: this.clientName,
|
|
3084
|
-
backend: this.backendConfig,
|
|
3085
|
-
headers: this.defaultHeaders,
|
|
3086
|
-
healthTracking: this.isHealthTrackingEnabled
|
|
3087
|
-
});
|
|
3088
|
-
}
|
|
3089
|
-
};
|
|
3090
|
-
var AthenaClient = class _AthenaClient {
|
|
3091
|
-
/** Create a fluent builder for a strongly-typed Athena SDK client. */
|
|
3092
|
-
static builder() {
|
|
3093
|
-
return new AthenaClientBuilderImpl();
|
|
3094
|
-
}
|
|
3095
|
-
/** Build a client from process environment variables. */
|
|
3096
|
-
static fromEnvironment() {
|
|
3097
|
-
const url = process.env.ATHENA_URL ?? process.env.ATHENA_GATEWAY_URL;
|
|
3098
|
-
const key = process.env.ATHENA_API_KEY ?? process.env.ATHENA_GATEWAY_API_KEY;
|
|
3099
|
-
if (!url || !key) {
|
|
3100
|
-
throw new Error(
|
|
3101
|
-
"ATHENA_URL and ATHENA_API_KEY (or ATHENA_GATEWAY_URL and ATHENA_GATEWAY_API_KEY) are required"
|
|
3102
|
-
);
|
|
3103
|
-
}
|
|
3104
|
-
return _AthenaClient.builder().url(url).key(key).build();
|
|
3105
|
-
}
|
|
3106
|
-
};
|
|
3107
4941
|
function createClient(url, apiKey, options) {
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
4942
|
+
return createClientFromConfig({
|
|
4943
|
+
baseUrl: url,
|
|
4944
|
+
apiKey,
|
|
4945
|
+
client: options?.client,
|
|
4946
|
+
backend: toBackendConfig(options?.backend),
|
|
4947
|
+
headers: options?.headers,
|
|
4948
|
+
auth: options?.auth,
|
|
4949
|
+
experimental: options?.experimental
|
|
4950
|
+
});
|
|
3116
4951
|
}
|
|
3117
4952
|
|
|
3118
4953
|
// src/schema/postgres-introspection-core.ts
|
|
@@ -3462,6 +5297,23 @@ var PostgresCatalogSnapshotAssembler = class {
|
|
|
3462
5297
|
table.relations[key] = relation;
|
|
3463
5298
|
}
|
|
3464
5299
|
};
|
|
5300
|
+
|
|
5301
|
+
// src/schema/postgres-provider.ts
|
|
5302
|
+
var pgPoolConstructorPromise;
|
|
5303
|
+
async function loadPgPoolConstructor() {
|
|
5304
|
+
if (!pgPoolConstructorPromise) {
|
|
5305
|
+
pgPoolConstructorPromise = import('pg').then((module) => {
|
|
5306
|
+
const poolConstructor = module.Pool ?? module.default?.Pool;
|
|
5307
|
+
if (!poolConstructor) {
|
|
5308
|
+
throw new Error(
|
|
5309
|
+
'@xylex-group/athena: Unable to load the PostgreSQL driver. Ensure "pg" is installed and this API runs in a Node.js server runtime.'
|
|
5310
|
+
);
|
|
5311
|
+
}
|
|
5312
|
+
return poolConstructor;
|
|
5313
|
+
});
|
|
5314
|
+
}
|
|
5315
|
+
return pgPoolConstructorPromise;
|
|
5316
|
+
}
|
|
3465
5317
|
var PgCatalogClient = class {
|
|
3466
5318
|
constructor(pool) {
|
|
3467
5319
|
this.pool = pool;
|
|
@@ -3501,7 +5353,8 @@ var PostgresIntrospectionProvider = class {
|
|
|
3501
5353
|
}
|
|
3502
5354
|
async inspect(options) {
|
|
3503
5355
|
const schemas = options?.schemas && options.schemas.length > 0 ? normalizePostgresCatalogSchemas(options.schemas) : this.schemas;
|
|
3504
|
-
const
|
|
5356
|
+
const PoolConstructor = await loadPgPoolConstructor();
|
|
5357
|
+
const pool = new PoolConstructor({
|
|
3505
5358
|
connectionString: this.connectionString
|
|
3506
5359
|
});
|
|
3507
5360
|
const catalogClient = new PgCatalogClient(pool);
|
|
@@ -3540,7 +5393,7 @@ var AthenaGatewayCatalogClient = class {
|
|
|
3540
5393
|
async queryRows(query) {
|
|
3541
5394
|
const result = await this.client.query(query);
|
|
3542
5395
|
if (result.error || result.status < 200 || result.status >= 300) {
|
|
3543
|
-
throw new Error(result.error ?? `Gateway query failed with status ${result.status}`);
|
|
5396
|
+
throw new Error(result.error?.message ?? `Gateway query failed with status ${result.status}`);
|
|
3544
5397
|
}
|
|
3545
5398
|
return result.data ?? [];
|
|
3546
5399
|
}
|
|
@@ -3637,10 +5490,24 @@ function resolveGeneratorProvider(providerConfig, experimentalFlags) {
|
|
|
3637
5490
|
}
|
|
3638
5491
|
|
|
3639
5492
|
// src/generator/pipeline.ts
|
|
5493
|
+
function canOverwriteArtifact(file) {
|
|
5494
|
+
return file.kind === "model" || file.kind === "schema";
|
|
5495
|
+
}
|
|
5496
|
+
async function fileExists(path) {
|
|
5497
|
+
try {
|
|
5498
|
+
await promises.stat(path);
|
|
5499
|
+
return true;
|
|
5500
|
+
} catch {
|
|
5501
|
+
return false;
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
3640
5504
|
async function writeArtifacts(files, cwd) {
|
|
3641
5505
|
const writtenFiles = [];
|
|
3642
5506
|
for (const file of files) {
|
|
3643
5507
|
const absolutePath = path.resolve(cwd, file.path);
|
|
5508
|
+
if (!canOverwriteArtifact(file) && await fileExists(absolutePath)) {
|
|
5509
|
+
continue;
|
|
5510
|
+
}
|
|
3644
5511
|
await promises.mkdir(path.dirname(absolutePath), { recursive: true });
|
|
3645
5512
|
await promises.writeFile(absolutePath, file.content, "utf8");
|
|
3646
5513
|
writtenFiles.push(file.path);
|