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