@xylex-group/athena 3.3.0 → 3.5.1
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 +17 -1
- package/dist/{athena-request-headers-Lr_m8xCV.d.cts → athena-request-headers-BnFN2mbd.d.cts} +1 -1
- package/dist/{athena-request-headers-DzimbvOA.d.ts → athena-request-headers-Dwdkq2WR.d.ts} +1 -1
- package/dist/billing.cjs +1 -1
- package/dist/billing.cjs.map +1 -1
- package/dist/billing.js +1 -1
- package/dist/billing.js.map +1 -1
- package/dist/browser.cjs +3064 -163
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +10 -10
- package/dist/browser.d.ts +10 -10
- package/dist/browser.js +3064 -163
- package/dist/browser.js.map +1 -1
- package/dist/cli/index.cjs +2990 -83
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.d.cts +4 -4
- package/dist/cli/index.d.ts +4 -4
- package/dist/cli/index.js +2990 -83
- package/dist/cli/index.js.map +1 -1
- package/dist/cloudflare.cjs +12914 -0
- package/dist/cloudflare.cjs.map +1 -0
- package/dist/cloudflare.d.cts +368 -0
- package/dist/cloudflare.d.ts +368 -0
- package/dist/cloudflare.js +12885 -0
- package/dist/cloudflare.js.map +1 -0
- package/dist/cookies.cjs +4 -3
- package/dist/cookies.cjs.map +1 -1
- package/dist/cookies.js +4 -3
- package/dist/cookies.js.map +1 -1
- package/dist/env.cjs +1 -1
- package/dist/env.cjs.map +1 -1
- package/dist/env.js +1 -1
- package/dist/env.js.map +1 -1
- package/dist/index.cjs +2989 -86
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -10
- package/dist/index.d.ts +10 -10
- package/dist/index.js +2989 -87
- package/dist/index.js.map +1 -1
- package/dist/{model-form-3Gr8rkdT.d.cts → model-form-D0ykVVOs.d.cts} +3 -3
- package/dist/{model-form-CotpEL7z.d.ts → model-form-U8NVtfSA.d.ts} +3 -3
- package/dist/{module-CQmOaDzP.d.cts → module-CMWlJ1z3.d.cts} +4 -4
- package/dist/{module-CWkUTcUc.d.ts → module-CZ-wPcf2.d.ts} +4 -4
- package/dist/next/client.cjs +3025 -125
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.d.cts +4 -4
- package/dist/next/client.d.ts +4 -4
- package/dist/next/client.js +3025 -125
- package/dist/next/client.js.map +1 -1
- package/dist/next/server.cjs +3108 -194
- package/dist/next/server.cjs.map +1 -1
- package/dist/next/server.d.cts +22 -9
- package/dist/next/server.d.ts +22 -9
- package/dist/next/server.js +3108 -194
- package/dist/next/server.js.map +1 -1
- package/dist/{pipeline-BnRKO_Pd.d.cts → pipeline-BCpb4BX1.d.cts} +1 -1
- package/dist/{pipeline-sWCmlFqu.d.ts → pipeline-CgmXwMeX.d.ts} +1 -1
- package/dist/react.cjs +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +6 -6
- package/dist/react.d.ts +6 -6
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/{schema-handlers-VjFDhIMw.d.ts → schema-handlers-CvWqF-dW.d.ts} +1 -1
- package/dist/{schema-handlers-BtT_IQFi.d.cts → schema-handlers-Dr8h9Jg-.d.cts} +1 -1
- package/dist/social-providers.cjs +1 -1
- package/dist/social-providers.cjs.map +1 -1
- package/dist/social-providers.js +1 -1
- package/dist/social-providers.js.map +1 -1
- package/dist/{types-Bltx6iL6.d.cts → types-B2RZPivM.d.cts} +8 -1
- package/dist/{types-Bltx6iL6.d.ts → types-B2RZPivM.d.ts} +8 -1
- package/dist/{types-BDX8zS7w.d.ts → types-BKklrX4J.d.ts} +2 -2
- package/dist/{types-CgCv5o6R.d.cts → types-D5dj1v5Z.d.cts} +1 -1
- package/dist/{types-D-Am7YSd.d.cts → types-DqBFwfhn.d.cts} +2 -2
- package/dist/{types-B75PdABb.d.ts → types-S-bTMPiM.d.ts} +1 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +2 -2
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js.map +1 -1
- package/dist/{v3-client-G1SLeHRa.d.cts → v3-client-DP-qg82X.d.cts} +300 -4
- package/dist/{v3-client-ao8BWQ8v.d.ts → v3-client-OAai_JNs.d.ts} +300 -4
- package/package.json +10 -3
package/dist/cli/index.cjs
CHANGED
|
@@ -2371,6 +2371,8 @@ function buildUpdateDebugSql(payload) {
|
|
|
2371
2371
|
sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
|
|
2372
2372
|
}
|
|
2373
2373
|
const pagination = resolvePagination({
|
|
2374
|
+
limit: payload.limit,
|
|
2375
|
+
offset: payload.offset,
|
|
2374
2376
|
currentPage: payload.current_page,
|
|
2375
2377
|
pageSize: payload.page_size
|
|
2376
2378
|
});
|
|
@@ -2393,6 +2395,8 @@ function buildDeleteDebugSql(payload) {
|
|
|
2393
2395
|
sqlParts.push(`WHERE ${whereClauses.join(" AND ")}`);
|
|
2394
2396
|
}
|
|
2395
2397
|
const pagination = resolvePagination({
|
|
2398
|
+
limit: payload.limit,
|
|
2399
|
+
offset: payload.offset,
|
|
2396
2400
|
currentPage: payload.current_page,
|
|
2397
2401
|
pageSize: payload.page_size
|
|
2398
2402
|
});
|
|
@@ -2536,7 +2540,7 @@ function buildAthenaGatewayUrl(baseUrl, path) {
|
|
|
2536
2540
|
|
|
2537
2541
|
// package.json
|
|
2538
2542
|
var package_default = {
|
|
2539
|
-
version: "3.
|
|
2543
|
+
version: "3.5.1"
|
|
2540
2544
|
};
|
|
2541
2545
|
|
|
2542
2546
|
// src/sdk-version.ts
|
|
@@ -5716,9 +5720,9 @@ function readHeadersField(value) {
|
|
|
5716
5720
|
const headers = value.headers;
|
|
5717
5721
|
if (!isRecord8(headers)) return {};
|
|
5718
5722
|
const result = {};
|
|
5719
|
-
for (const [key,
|
|
5720
|
-
if (typeof
|
|
5721
|
-
result[key] =
|
|
5723
|
+
for (const [key, headerValue3] of Object.entries(headers)) {
|
|
5724
|
+
if (typeof headerValue3 === "string") {
|
|
5725
|
+
result[key] = headerValue3;
|
|
5722
5726
|
}
|
|
5723
5727
|
}
|
|
5724
5728
|
return result;
|
|
@@ -9343,6 +9347,8 @@ function createTableBuilder(tableName, client, formatGatewayResult, tracer, beha
|
|
|
9343
9347
|
strip_nulls: mergedOptions?.stripNulls ?? true
|
|
9344
9348
|
};
|
|
9345
9349
|
if (executionState.order) payload.sort_by = executionState.order;
|
|
9350
|
+
if (executionState.limit !== void 0) payload.limit = executionState.limit;
|
|
9351
|
+
if (executionState.offset !== void 0) payload.offset = executionState.offset;
|
|
9346
9352
|
if (executionState.currentPage !== void 0) payload.current_page = executionState.currentPage;
|
|
9347
9353
|
if (executionState.pageSize !== void 0) payload.page_size = executionState.pageSize;
|
|
9348
9354
|
if (executionState.totalPages !== void 0) payload.total_pages = executionState.totalPages;
|
|
@@ -9397,6 +9403,8 @@ function createTableBuilder(tableName, client, formatGatewayResult, tracer, beha
|
|
|
9397
9403
|
conditions: filters
|
|
9398
9404
|
};
|
|
9399
9405
|
if (executionState.order) payload.sort_by = executionState.order;
|
|
9406
|
+
if (executionState.limit !== void 0) payload.limit = executionState.limit;
|
|
9407
|
+
if (executionState.offset !== void 0) payload.offset = executionState.offset;
|
|
9400
9408
|
if (executionState.currentPage !== void 0) payload.current_page = executionState.currentPage;
|
|
9401
9409
|
if (executionState.pageSize !== void 0) payload.page_size = executionState.pageSize;
|
|
9402
9410
|
if (executionState.totalPages !== void 0) payload.total_pages = executionState.totalPages;
|
|
@@ -9448,7 +9456,11 @@ function createQueryBuilder(client, formatGatewayResult, behavior, tracer) {
|
|
|
9448
9456
|
if (!normalizedQuery) {
|
|
9449
9457
|
throw new Error("query requires a non-empty string");
|
|
9450
9458
|
}
|
|
9451
|
-
const
|
|
9459
|
+
const params = options?.params;
|
|
9460
|
+
const payload = {
|
|
9461
|
+
query: normalizedQuery,
|
|
9462
|
+
...Array.isArray(params) ? { params } : {}
|
|
9463
|
+
};
|
|
9452
9464
|
const callsite = captureTraceCallsite(tracer);
|
|
9453
9465
|
return executeRead(
|
|
9454
9466
|
behavior,
|
|
@@ -9497,7 +9509,7 @@ function normalizeAuthClientConfig(auth, defaultBaseUrl) {
|
|
|
9497
9509
|
}
|
|
9498
9510
|
function createInternalClientCore(config) {
|
|
9499
9511
|
const normalizedAuthConfig = normalizeAuthClientConfig(config.auth, config.authUrl);
|
|
9500
|
-
const gatewayTransport = createAthenaGatewayClient({
|
|
9512
|
+
const gatewayTransport = config.gatewayTransport ?? createAthenaGatewayClient({
|
|
9501
9513
|
baseUrl: config.baseUrl,
|
|
9502
9514
|
apiKey: config.apiKey,
|
|
9503
9515
|
client: config.client ?? void 0,
|
|
@@ -9595,61 +9607,2699 @@ function createInternalClientView(core, resolveContext) {
|
|
|
9595
9607
|
if (first && typeof first === "object" && !Array.isArray(first)) {
|
|
9596
9608
|
return untypedDbFrom(table).select(void 0, first);
|
|
9597
9609
|
}
|
|
9598
|
-
return untypedDbFrom(table).select(first, second);
|
|
9610
|
+
return untypedDbFrom(table).select(first, second);
|
|
9611
|
+
},
|
|
9612
|
+
insert(table, values, options) {
|
|
9613
|
+
return Array.isArray(values) ? untypedDbFrom(table).insert(values, options) : untypedDbFrom(table).insert(values, options);
|
|
9614
|
+
},
|
|
9615
|
+
upsert(table, values, options) {
|
|
9616
|
+
return Array.isArray(values) ? untypedDbFrom(table).upsert(values, options) : untypedDbFrom(table).upsert(values, options);
|
|
9617
|
+
},
|
|
9618
|
+
update(table, values, options) {
|
|
9619
|
+
return untypedDbFrom(table).update(values, options);
|
|
9620
|
+
},
|
|
9621
|
+
delete(table, options) {
|
|
9622
|
+
return untypedDbFrom(table).delete(options);
|
|
9623
|
+
},
|
|
9624
|
+
rpc,
|
|
9625
|
+
query
|
|
9626
|
+
};
|
|
9627
|
+
const chat = createChatModule({
|
|
9628
|
+
baseUrl: config.chatUrl,
|
|
9629
|
+
apiKey: config.apiKey,
|
|
9630
|
+
client: config.client ?? void 0,
|
|
9631
|
+
headers: config.headers,
|
|
9632
|
+
wsUrl: config.chatWsUrl,
|
|
9633
|
+
webSocketFactory: config.chat?.webSocketFactory ?? void 0
|
|
9634
|
+
}, {
|
|
9635
|
+
async resolveCallOptions() {
|
|
9636
|
+
const context = await resolveContext();
|
|
9637
|
+
return context ? {
|
|
9638
|
+
headers: buildContextHeaders(context),
|
|
9639
|
+
bearerToken: context.bearerToken,
|
|
9640
|
+
cookie: context.cookie,
|
|
9641
|
+
sessionToken: context.sessionToken,
|
|
9642
|
+
forceNoCache: context.forceNoCache
|
|
9643
|
+
} : void 0;
|
|
9644
|
+
}
|
|
9645
|
+
});
|
|
9646
|
+
const request2 = createAthenaRequest(config, resolveContext);
|
|
9647
|
+
const storage = createStorageModule(gateway, {
|
|
9648
|
+
...config.storage,
|
|
9649
|
+
...config.storageUrl ? {
|
|
9650
|
+
baseUrl: config.storageUrl,
|
|
9651
|
+
stripBasePath: true
|
|
9652
|
+
} : {}
|
|
9653
|
+
});
|
|
9654
|
+
return {
|
|
9655
|
+
from,
|
|
9656
|
+
db,
|
|
9657
|
+
rpc,
|
|
9658
|
+
query,
|
|
9659
|
+
request: request2,
|
|
9660
|
+
verifyConnection: gateway.verifyConnection,
|
|
9661
|
+
auth: auth.auth,
|
|
9662
|
+
chat,
|
|
9663
|
+
storage
|
|
9664
|
+
};
|
|
9665
|
+
}
|
|
9666
|
+
|
|
9667
|
+
// src/cloudflare/types.ts
|
|
9668
|
+
var CLOUDFLARE_EDGE_BASE_URL = "https://athena.local/cloudflare-edge";
|
|
9669
|
+
var CLOUDFLARE_EDGE_API_KEY = "cloudflare-edge-local";
|
|
9670
|
+
|
|
9671
|
+
// src/cloudflare/capabilities.ts
|
|
9672
|
+
function createGatewayCapabilities(options) {
|
|
9673
|
+
const storageConfigured = options?.storageConfigured ?? true;
|
|
9674
|
+
return {
|
|
9675
|
+
mode: "gateway",
|
|
9676
|
+
db: {
|
|
9677
|
+
engine: options?.engine ?? "postgresql",
|
|
9678
|
+
local: false,
|
|
9679
|
+
layers: {
|
|
9680
|
+
query: true,
|
|
9681
|
+
flatCrud: true,
|
|
9682
|
+
findManyAst: true,
|
|
9683
|
+
rpc: true,
|
|
9684
|
+
relations: true
|
|
9685
|
+
}
|
|
9686
|
+
},
|
|
9687
|
+
storage: {
|
|
9688
|
+
local: false,
|
|
9689
|
+
objects: storageConfigured,
|
|
9690
|
+
catalogs: storageConfigured ? options?.storageCatalogs ?? true : false,
|
|
9691
|
+
backups: storageConfigured ? options?.storageBackups ?? true : false
|
|
9692
|
+
},
|
|
9693
|
+
auth: {
|
|
9694
|
+
remote: options?.authRemote ?? true
|
|
9695
|
+
}
|
|
9696
|
+
};
|
|
9697
|
+
}
|
|
9698
|
+
function createCloudflareEdgeCapabilities(options) {
|
|
9699
|
+
const hasObjects = options.hasR2 || Boolean(options.hasRemoteStorage);
|
|
9700
|
+
return {
|
|
9701
|
+
mode: "cloudflare-edge",
|
|
9702
|
+
db: {
|
|
9703
|
+
engine: "cloudflare-d1",
|
|
9704
|
+
local: true,
|
|
9705
|
+
layers: {
|
|
9706
|
+
query: true,
|
|
9707
|
+
flatCrud: true,
|
|
9708
|
+
findManyAst: options.findManyAst ?? false,
|
|
9709
|
+
rpc: false,
|
|
9710
|
+
relations: false
|
|
9711
|
+
}
|
|
9712
|
+
},
|
|
9713
|
+
storage: {
|
|
9714
|
+
// Local objects only when an R2 binding is present.
|
|
9715
|
+
local: options.hasR2,
|
|
9716
|
+
// Remote hybrid root can still expose storage.objects via the HTTP API.
|
|
9717
|
+
objects: hasObjects,
|
|
9718
|
+
catalogs: false,
|
|
9719
|
+
backups: false
|
|
9720
|
+
},
|
|
9721
|
+
auth: {
|
|
9722
|
+
remote: options.authRemote
|
|
9723
|
+
}
|
|
9724
|
+
};
|
|
9725
|
+
}
|
|
9726
|
+
|
|
9727
|
+
// src/cloudflare/d1/runner.ts
|
|
9728
|
+
function resultColumns(rows) {
|
|
9729
|
+
const first = rows.find((value) => value && typeof value === "object" && !Array.isArray(value));
|
|
9730
|
+
if (!first) return [];
|
|
9731
|
+
return Object.keys(first);
|
|
9732
|
+
}
|
|
9733
|
+
function isIdentChar(ch) {
|
|
9734
|
+
if (!ch) return false;
|
|
9735
|
+
return /[A-Za-z0-9_]/.test(ch);
|
|
9736
|
+
}
|
|
9737
|
+
function scanSqlKeywordsOutsideLiterals(sql, onKeyword) {
|
|
9738
|
+
let inSingle = false;
|
|
9739
|
+
let inDouble = false;
|
|
9740
|
+
let inBacktick = false;
|
|
9741
|
+
let inBracket = false;
|
|
9742
|
+
let inLineComment = false;
|
|
9743
|
+
let inBlockComment = false;
|
|
9744
|
+
for (let i = 0; i < sql.length; i += 1) {
|
|
9745
|
+
const ch = sql[i];
|
|
9746
|
+
const next = sql[i + 1];
|
|
9747
|
+
if (inLineComment) {
|
|
9748
|
+
if (ch === "\n" || ch === "\r") inLineComment = false;
|
|
9749
|
+
continue;
|
|
9750
|
+
}
|
|
9751
|
+
if (inBlockComment) {
|
|
9752
|
+
if (ch === "*" && next === "/") {
|
|
9753
|
+
i += 1;
|
|
9754
|
+
inBlockComment = false;
|
|
9755
|
+
}
|
|
9756
|
+
continue;
|
|
9757
|
+
}
|
|
9758
|
+
if (inSingle) {
|
|
9759
|
+
if (ch === "'" && next === "'") {
|
|
9760
|
+
i += 1;
|
|
9761
|
+
continue;
|
|
9762
|
+
}
|
|
9763
|
+
if (ch === "'") inSingle = false;
|
|
9764
|
+
continue;
|
|
9765
|
+
}
|
|
9766
|
+
if (inDouble) {
|
|
9767
|
+
if (ch === '"' && next === '"') {
|
|
9768
|
+
i += 1;
|
|
9769
|
+
continue;
|
|
9770
|
+
}
|
|
9771
|
+
if (ch === '"') inDouble = false;
|
|
9772
|
+
continue;
|
|
9773
|
+
}
|
|
9774
|
+
if (inBacktick) {
|
|
9775
|
+
if (ch === "`" && next === "`") {
|
|
9776
|
+
i += 1;
|
|
9777
|
+
continue;
|
|
9778
|
+
}
|
|
9779
|
+
if (ch === "`") inBacktick = false;
|
|
9780
|
+
continue;
|
|
9781
|
+
}
|
|
9782
|
+
if (inBracket) {
|
|
9783
|
+
if (ch === "]") inBracket = false;
|
|
9784
|
+
continue;
|
|
9785
|
+
}
|
|
9786
|
+
if (ch === "-" && next === "-") {
|
|
9787
|
+
inLineComment = true;
|
|
9788
|
+
i += 1;
|
|
9789
|
+
continue;
|
|
9790
|
+
}
|
|
9791
|
+
if (ch === "/" && next === "*") {
|
|
9792
|
+
inBlockComment = true;
|
|
9793
|
+
i += 1;
|
|
9794
|
+
continue;
|
|
9795
|
+
}
|
|
9796
|
+
if (ch === "'") {
|
|
9797
|
+
inSingle = true;
|
|
9798
|
+
continue;
|
|
9799
|
+
}
|
|
9800
|
+
if (ch === '"') {
|
|
9801
|
+
inDouble = true;
|
|
9802
|
+
continue;
|
|
9803
|
+
}
|
|
9804
|
+
if (ch === "`") {
|
|
9805
|
+
inBacktick = true;
|
|
9806
|
+
continue;
|
|
9807
|
+
}
|
|
9808
|
+
if (ch === "[") {
|
|
9809
|
+
inBracket = true;
|
|
9810
|
+
continue;
|
|
9811
|
+
}
|
|
9812
|
+
if (!isIdentChar(ch) || isIdentChar(sql[i - 1])) {
|
|
9813
|
+
continue;
|
|
9814
|
+
}
|
|
9815
|
+
let end = i + 1;
|
|
9816
|
+
while (end < sql.length && isIdentChar(sql[end])) {
|
|
9817
|
+
end += 1;
|
|
9818
|
+
}
|
|
9819
|
+
const keyword = sql.slice(i, end);
|
|
9820
|
+
if (onKeyword(keyword, i)) {
|
|
9821
|
+
return;
|
|
9822
|
+
}
|
|
9823
|
+
i = end - 1;
|
|
9824
|
+
}
|
|
9825
|
+
}
|
|
9826
|
+
function sqlContainsKeywordOutsideLiterals(sql, keyword) {
|
|
9827
|
+
const target = keyword.toUpperCase();
|
|
9828
|
+
let found = false;
|
|
9829
|
+
scanSqlKeywordsOutsideLiterals(sql, (token) => {
|
|
9830
|
+
if (token.toUpperCase() === target) {
|
|
9831
|
+
found = true;
|
|
9832
|
+
return true;
|
|
9833
|
+
}
|
|
9834
|
+
return false;
|
|
9835
|
+
});
|
|
9836
|
+
return found;
|
|
9837
|
+
}
|
|
9838
|
+
function sqlFirstKeywordOutsideLiterals(sql) {
|
|
9839
|
+
let first = null;
|
|
9840
|
+
scanSqlKeywordsOutsideLiterals(sql, (token) => {
|
|
9841
|
+
first = token;
|
|
9842
|
+
return true;
|
|
9843
|
+
});
|
|
9844
|
+
return first;
|
|
9845
|
+
}
|
|
9846
|
+
var ROW_PRODUCING_LEAD_KEYWORDS = /* @__PURE__ */ new Set(["SELECT", "PRAGMA", "EXPLAIN", "VALUES"]);
|
|
9847
|
+
var MUTATION_LEAD_KEYWORDS = /* @__PURE__ */ new Set(["INSERT", "UPDATE", "DELETE", "REPLACE"]);
|
|
9848
|
+
var WITH_TERMINAL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
9849
|
+
"SELECT",
|
|
9850
|
+
"INSERT",
|
|
9851
|
+
"UPDATE",
|
|
9852
|
+
"DELETE",
|
|
9853
|
+
"REPLACE",
|
|
9854
|
+
"VALUES",
|
|
9855
|
+
"PRAGMA",
|
|
9856
|
+
"EXPLAIN"
|
|
9857
|
+
]);
|
|
9858
|
+
function sqlTerminalKeywordAfterWith(sql) {
|
|
9859
|
+
let inSingle = false;
|
|
9860
|
+
let inDouble = false;
|
|
9861
|
+
let inBacktick = false;
|
|
9862
|
+
let inBracket = false;
|
|
9863
|
+
let inLineComment = false;
|
|
9864
|
+
let inBlockComment = false;
|
|
9865
|
+
let depth = 0;
|
|
9866
|
+
let sawWith = false;
|
|
9867
|
+
for (let i = 0; i < sql.length; i += 1) {
|
|
9868
|
+
const ch = sql[i];
|
|
9869
|
+
const next = sql[i + 1];
|
|
9870
|
+
if (inLineComment) {
|
|
9871
|
+
if (ch === "\n" || ch === "\r") inLineComment = false;
|
|
9872
|
+
continue;
|
|
9873
|
+
}
|
|
9874
|
+
if (inBlockComment) {
|
|
9875
|
+
if (ch === "*" && next === "/") {
|
|
9876
|
+
i += 1;
|
|
9877
|
+
inBlockComment = false;
|
|
9878
|
+
}
|
|
9879
|
+
continue;
|
|
9880
|
+
}
|
|
9881
|
+
if (inSingle) {
|
|
9882
|
+
if (ch === "'" && next === "'") {
|
|
9883
|
+
i += 1;
|
|
9884
|
+
continue;
|
|
9885
|
+
}
|
|
9886
|
+
if (ch === "'") inSingle = false;
|
|
9887
|
+
continue;
|
|
9888
|
+
}
|
|
9889
|
+
if (inDouble) {
|
|
9890
|
+
if (ch === '"' && next === '"') {
|
|
9891
|
+
i += 1;
|
|
9892
|
+
continue;
|
|
9893
|
+
}
|
|
9894
|
+
if (ch === '"') inDouble = false;
|
|
9895
|
+
continue;
|
|
9896
|
+
}
|
|
9897
|
+
if (inBacktick) {
|
|
9898
|
+
if (ch === "`" && next === "`") {
|
|
9899
|
+
i += 1;
|
|
9900
|
+
continue;
|
|
9901
|
+
}
|
|
9902
|
+
if (ch === "`") inBacktick = false;
|
|
9903
|
+
continue;
|
|
9904
|
+
}
|
|
9905
|
+
if (inBracket) {
|
|
9906
|
+
if (ch === "]") inBracket = false;
|
|
9907
|
+
continue;
|
|
9908
|
+
}
|
|
9909
|
+
if (ch === "-" && next === "-") {
|
|
9910
|
+
inLineComment = true;
|
|
9911
|
+
i += 1;
|
|
9912
|
+
continue;
|
|
9913
|
+
}
|
|
9914
|
+
if (ch === "/" && next === "*") {
|
|
9915
|
+
inBlockComment = true;
|
|
9916
|
+
i += 1;
|
|
9917
|
+
continue;
|
|
9918
|
+
}
|
|
9919
|
+
if (ch === "'") {
|
|
9920
|
+
inSingle = true;
|
|
9921
|
+
continue;
|
|
9922
|
+
}
|
|
9923
|
+
if (ch === '"') {
|
|
9924
|
+
inDouble = true;
|
|
9925
|
+
continue;
|
|
9926
|
+
}
|
|
9927
|
+
if (ch === "`") {
|
|
9928
|
+
inBacktick = true;
|
|
9929
|
+
continue;
|
|
9930
|
+
}
|
|
9931
|
+
if (ch === "[") {
|
|
9932
|
+
inBracket = true;
|
|
9933
|
+
continue;
|
|
9934
|
+
}
|
|
9935
|
+
if (ch === "(") {
|
|
9936
|
+
depth += 1;
|
|
9937
|
+
continue;
|
|
9938
|
+
}
|
|
9939
|
+
if (ch === ")") {
|
|
9940
|
+
if (depth > 0) depth -= 1;
|
|
9941
|
+
continue;
|
|
9942
|
+
}
|
|
9943
|
+
if (depth !== 0 || !isIdentChar(ch) || isIdentChar(sql[i - 1])) {
|
|
9944
|
+
continue;
|
|
9945
|
+
}
|
|
9946
|
+
let end = i + 1;
|
|
9947
|
+
while (end < sql.length && isIdentChar(sql[end])) {
|
|
9948
|
+
end += 1;
|
|
9949
|
+
}
|
|
9950
|
+
const keyword = sql.slice(i, end);
|
|
9951
|
+
const upper = keyword.toUpperCase();
|
|
9952
|
+
if (!sawWith) {
|
|
9953
|
+
if (upper === "WITH") {
|
|
9954
|
+
sawWith = true;
|
|
9955
|
+
}
|
|
9956
|
+
i = end - 1;
|
|
9957
|
+
continue;
|
|
9958
|
+
}
|
|
9959
|
+
if (WITH_TERMINAL_KEYWORDS.has(upper)) {
|
|
9960
|
+
return keyword;
|
|
9961
|
+
}
|
|
9962
|
+
i = end - 1;
|
|
9963
|
+
}
|
|
9964
|
+
return null;
|
|
9965
|
+
}
|
|
9966
|
+
function sqlLeadStatementKeyword(sql) {
|
|
9967
|
+
const first = sqlFirstKeywordOutsideLiterals(sql);
|
|
9968
|
+
if (!first) return null;
|
|
9969
|
+
if (first.toUpperCase() !== "WITH") return first;
|
|
9970
|
+
return sqlTerminalKeywordAfterWith(sql) ?? first;
|
|
9971
|
+
}
|
|
9972
|
+
function statementExpectsResultRows(sql) {
|
|
9973
|
+
const lead = sqlLeadStatementKeyword(sql);
|
|
9974
|
+
if (lead) {
|
|
9975
|
+
const upper = lead.toUpperCase();
|
|
9976
|
+
if (ROW_PRODUCING_LEAD_KEYWORDS.has(upper)) {
|
|
9977
|
+
return true;
|
|
9978
|
+
}
|
|
9979
|
+
if (MUTATION_LEAD_KEYWORDS.has(upper)) {
|
|
9980
|
+
return sqlContainsKeywordOutsideLiterals(sql, "RETURNING");
|
|
9981
|
+
}
|
|
9982
|
+
}
|
|
9983
|
+
return sqlContainsKeywordOutsideLiterals(sql, "RETURNING");
|
|
9984
|
+
}
|
|
9985
|
+
function collectRowsFromBatchResults(results) {
|
|
9986
|
+
const rows = [];
|
|
9987
|
+
for (const item of results) {
|
|
9988
|
+
if (Array.isArray(item.results) && item.results.length > 0) {
|
|
9989
|
+
rows.push(...item.results);
|
|
9990
|
+
}
|
|
9991
|
+
}
|
|
9992
|
+
return rows;
|
|
9993
|
+
}
|
|
9994
|
+
function sumBatchAffectedRows(results, statementCount) {
|
|
9995
|
+
let sum2 = 0;
|
|
9996
|
+
let sawChanges = false;
|
|
9997
|
+
for (const item of results) {
|
|
9998
|
+
const changes = item.meta?.changes;
|
|
9999
|
+
if (typeof changes === "number") {
|
|
10000
|
+
sum2 += changes;
|
|
10001
|
+
sawChanges = true;
|
|
10002
|
+
}
|
|
10003
|
+
}
|
|
10004
|
+
return sawChanges ? sum2 : statementCount;
|
|
10005
|
+
}
|
|
10006
|
+
function matchSqlKeywordAt(query, index, keyword) {
|
|
10007
|
+
const len = keyword.length;
|
|
10008
|
+
if (index + len > query.length) {
|
|
10009
|
+
return null;
|
|
10010
|
+
}
|
|
10011
|
+
const slice = query.slice(index, index + len);
|
|
10012
|
+
if (slice.toLowerCase() !== keyword.toLowerCase()) {
|
|
10013
|
+
return null;
|
|
10014
|
+
}
|
|
10015
|
+
const before = index === 0 ? "" : query[index - 1];
|
|
10016
|
+
const after = index + len >= query.length ? "" : query[index + len];
|
|
10017
|
+
const isIdent = (c) => /[A-Za-z0-9_]/.test(c);
|
|
10018
|
+
if (before && isIdent(before)) {
|
|
10019
|
+
return null;
|
|
10020
|
+
}
|
|
10021
|
+
if (after && isIdent(after)) {
|
|
10022
|
+
return null;
|
|
10023
|
+
}
|
|
10024
|
+
return len;
|
|
10025
|
+
}
|
|
10026
|
+
function splitSqlStatements(query) {
|
|
10027
|
+
const statements = [];
|
|
10028
|
+
let inSingle = false;
|
|
10029
|
+
let inDouble = false;
|
|
10030
|
+
let inBacktick = false;
|
|
10031
|
+
let inBracket = false;
|
|
10032
|
+
let inLineComment = false;
|
|
10033
|
+
let inBlockComment = false;
|
|
10034
|
+
let segmentStart = 0;
|
|
10035
|
+
let segmentHasContent = false;
|
|
10036
|
+
let awaitTriggerBegin = false;
|
|
10037
|
+
let triggerBodyDepth = 0;
|
|
10038
|
+
let caseDepth = 0;
|
|
10039
|
+
const flushSegment = (end) => {
|
|
10040
|
+
if (!segmentHasContent) {
|
|
10041
|
+
segmentStart = end + 1;
|
|
10042
|
+
return;
|
|
10043
|
+
}
|
|
10044
|
+
const sql = query.slice(segmentStart, end).trim();
|
|
10045
|
+
if (sql) {
|
|
10046
|
+
statements.push(sql);
|
|
10047
|
+
}
|
|
10048
|
+
segmentHasContent = false;
|
|
10049
|
+
segmentStart = end + 1;
|
|
10050
|
+
awaitTriggerBegin = false;
|
|
10051
|
+
triggerBodyDepth = 0;
|
|
10052
|
+
caseDepth = 0;
|
|
10053
|
+
};
|
|
10054
|
+
for (let i = 0; i < query.length; i += 1) {
|
|
10055
|
+
const ch = query[i];
|
|
10056
|
+
const next = query[i + 1];
|
|
10057
|
+
if (inLineComment) {
|
|
10058
|
+
if (ch === "\n" || ch === "\r") {
|
|
10059
|
+
inLineComment = false;
|
|
10060
|
+
}
|
|
10061
|
+
continue;
|
|
10062
|
+
}
|
|
10063
|
+
if (inBlockComment) {
|
|
10064
|
+
if (ch === "*" && next === "/") {
|
|
10065
|
+
inBlockComment = false;
|
|
10066
|
+
i += 1;
|
|
10067
|
+
}
|
|
10068
|
+
continue;
|
|
10069
|
+
}
|
|
10070
|
+
if (inSingle) {
|
|
10071
|
+
if (ch === "'" && next === "'") {
|
|
10072
|
+
i += 1;
|
|
10073
|
+
continue;
|
|
10074
|
+
}
|
|
10075
|
+
if (ch === "'") {
|
|
10076
|
+
inSingle = false;
|
|
10077
|
+
}
|
|
10078
|
+
continue;
|
|
10079
|
+
}
|
|
10080
|
+
if (inDouble) {
|
|
10081
|
+
if (ch === '"' && next === '"') {
|
|
10082
|
+
i += 1;
|
|
10083
|
+
continue;
|
|
10084
|
+
}
|
|
10085
|
+
if (ch === '"') {
|
|
10086
|
+
inDouble = false;
|
|
10087
|
+
}
|
|
10088
|
+
continue;
|
|
10089
|
+
}
|
|
10090
|
+
if (inBacktick) {
|
|
10091
|
+
if (ch === "`" && next === "`") {
|
|
10092
|
+
i += 1;
|
|
10093
|
+
continue;
|
|
10094
|
+
}
|
|
10095
|
+
if (ch === "`") {
|
|
10096
|
+
inBacktick = false;
|
|
10097
|
+
}
|
|
10098
|
+
continue;
|
|
10099
|
+
}
|
|
10100
|
+
if (inBracket) {
|
|
10101
|
+
if (ch === "]") {
|
|
10102
|
+
inBracket = false;
|
|
10103
|
+
}
|
|
10104
|
+
continue;
|
|
10105
|
+
}
|
|
10106
|
+
if (ch === "-" && next === "-") {
|
|
10107
|
+
inLineComment = true;
|
|
10108
|
+
i += 1;
|
|
10109
|
+
continue;
|
|
10110
|
+
}
|
|
10111
|
+
if (ch === "/" && next === "*") {
|
|
10112
|
+
inBlockComment = true;
|
|
10113
|
+
i += 1;
|
|
10114
|
+
continue;
|
|
10115
|
+
}
|
|
10116
|
+
if (ch === "'") {
|
|
10117
|
+
inSingle = true;
|
|
10118
|
+
segmentHasContent = true;
|
|
10119
|
+
continue;
|
|
10120
|
+
}
|
|
10121
|
+
if (ch === '"') {
|
|
10122
|
+
inDouble = true;
|
|
10123
|
+
segmentHasContent = true;
|
|
10124
|
+
continue;
|
|
10125
|
+
}
|
|
10126
|
+
if (ch === "`") {
|
|
10127
|
+
inBacktick = true;
|
|
10128
|
+
segmentHasContent = true;
|
|
10129
|
+
continue;
|
|
10130
|
+
}
|
|
10131
|
+
if (ch === "[") {
|
|
10132
|
+
inBracket = true;
|
|
10133
|
+
segmentHasContent = true;
|
|
10134
|
+
continue;
|
|
10135
|
+
}
|
|
10136
|
+
if (/[A-Za-z_]/.test(ch)) {
|
|
10137
|
+
const createLen = matchSqlKeywordAt(query, i, "CREATE");
|
|
10138
|
+
if (createLen != null) {
|
|
10139
|
+
let j2 = i + createLen;
|
|
10140
|
+
const skipWsAndComments = () => {
|
|
10141
|
+
while (j2 < query.length) {
|
|
10142
|
+
const c = query[j2];
|
|
10143
|
+
const n = query[j2 + 1];
|
|
10144
|
+
if (/\s/.test(c)) {
|
|
10145
|
+
j2 += 1;
|
|
10146
|
+
continue;
|
|
10147
|
+
}
|
|
10148
|
+
if (c === "-" && n === "-") {
|
|
10149
|
+
j2 += 2;
|
|
10150
|
+
while (j2 < query.length && query[j2] !== "\n" && query[j2] !== "\r") {
|
|
10151
|
+
j2 += 1;
|
|
10152
|
+
}
|
|
10153
|
+
continue;
|
|
10154
|
+
}
|
|
10155
|
+
if (c === "/" && n === "*") {
|
|
10156
|
+
j2 += 2;
|
|
10157
|
+
while (j2 < query.length) {
|
|
10158
|
+
if (query[j2] === "*" && query[j2 + 1] === "/") {
|
|
10159
|
+
j2 += 2;
|
|
10160
|
+
break;
|
|
10161
|
+
}
|
|
10162
|
+
j2 += 1;
|
|
10163
|
+
}
|
|
10164
|
+
continue;
|
|
10165
|
+
}
|
|
10166
|
+
break;
|
|
10167
|
+
}
|
|
10168
|
+
};
|
|
10169
|
+
skipWsAndComments();
|
|
10170
|
+
const tempLen = matchSqlKeywordAt(query, j2, "TEMPORARY") ?? matchSqlKeywordAt(query, j2, "TEMP");
|
|
10171
|
+
if (tempLen != null) {
|
|
10172
|
+
j2 += tempLen;
|
|
10173
|
+
skipWsAndComments();
|
|
10174
|
+
}
|
|
10175
|
+
const triggerLen = matchSqlKeywordAt(query, j2, "TRIGGER");
|
|
10176
|
+
if (triggerLen != null) {
|
|
10177
|
+
awaitTriggerBegin = true;
|
|
10178
|
+
segmentHasContent = true;
|
|
10179
|
+
i += createLen - 1;
|
|
10180
|
+
continue;
|
|
10181
|
+
}
|
|
10182
|
+
segmentHasContent = true;
|
|
10183
|
+
i += createLen - 1;
|
|
10184
|
+
continue;
|
|
10185
|
+
}
|
|
10186
|
+
const caseLen = matchSqlKeywordAt(query, i, "CASE");
|
|
10187
|
+
if (caseLen != null) {
|
|
10188
|
+
caseDepth += 1;
|
|
10189
|
+
segmentHasContent = true;
|
|
10190
|
+
i += caseLen - 1;
|
|
10191
|
+
continue;
|
|
10192
|
+
}
|
|
10193
|
+
const beginLen = matchSqlKeywordAt(query, i, "BEGIN");
|
|
10194
|
+
if (beginLen != null) {
|
|
10195
|
+
if (awaitTriggerBegin) {
|
|
10196
|
+
triggerBodyDepth += 1;
|
|
10197
|
+
awaitTriggerBegin = false;
|
|
10198
|
+
}
|
|
10199
|
+
segmentHasContent = true;
|
|
10200
|
+
i += beginLen - 1;
|
|
10201
|
+
continue;
|
|
10202
|
+
}
|
|
10203
|
+
const endLen = matchSqlKeywordAt(query, i, "END");
|
|
10204
|
+
if (endLen != null) {
|
|
10205
|
+
if (caseDepth > 0) {
|
|
10206
|
+
caseDepth -= 1;
|
|
10207
|
+
} else if (triggerBodyDepth > 0) {
|
|
10208
|
+
triggerBodyDepth -= 1;
|
|
10209
|
+
}
|
|
10210
|
+
segmentHasContent = true;
|
|
10211
|
+
i += endLen - 1;
|
|
10212
|
+
continue;
|
|
10213
|
+
}
|
|
10214
|
+
let j = i + 1;
|
|
10215
|
+
while (j < query.length && /[A-Za-z0-9_]/.test(query[j])) {
|
|
10216
|
+
j += 1;
|
|
10217
|
+
}
|
|
10218
|
+
segmentHasContent = true;
|
|
10219
|
+
i = j - 1;
|
|
10220
|
+
continue;
|
|
10221
|
+
}
|
|
10222
|
+
if (ch === ";") {
|
|
10223
|
+
if (triggerBodyDepth > 0 || caseDepth > 0) {
|
|
10224
|
+
segmentHasContent = true;
|
|
10225
|
+
continue;
|
|
10226
|
+
}
|
|
10227
|
+
flushSegment(i);
|
|
10228
|
+
continue;
|
|
10229
|
+
}
|
|
10230
|
+
if (!/\s/.test(ch)) {
|
|
10231
|
+
segmentHasContent = true;
|
|
10232
|
+
}
|
|
10233
|
+
}
|
|
10234
|
+
flushSegment(query.length);
|
|
10235
|
+
return statements;
|
|
10236
|
+
}
|
|
10237
|
+
function isMultiStatement(query) {
|
|
10238
|
+
return splitSqlStatements(query).length > 1;
|
|
10239
|
+
}
|
|
10240
|
+
function resolveSessionTarget(db, input) {
|
|
10241
|
+
const sessionValue = input.bookmark?.trim() || input.sessionMode?.trim();
|
|
10242
|
+
if (!sessionValue) {
|
|
10243
|
+
return { target: db, session: null };
|
|
10244
|
+
}
|
|
10245
|
+
if (typeof db.withSession !== "function") {
|
|
10246
|
+
throw new Error("D1 session mode/bookmark requested but withSession is not available");
|
|
10247
|
+
}
|
|
10248
|
+
const session = db.withSession(sessionValue);
|
|
10249
|
+
return { target: session, session };
|
|
10250
|
+
}
|
|
10251
|
+
function normalizeD1BindParams(params) {
|
|
10252
|
+
return params.map((value) => {
|
|
10253
|
+
if (typeof value === "boolean") {
|
|
10254
|
+
return value ? 1 : 0;
|
|
10255
|
+
}
|
|
10256
|
+
return value;
|
|
10257
|
+
});
|
|
10258
|
+
}
|
|
10259
|
+
async function executeD1Query(db, input) {
|
|
10260
|
+
const query = input.query?.trim();
|
|
10261
|
+
if (!query) {
|
|
10262
|
+
return { ok: false, error: "invalid_query", message: "query is required" };
|
|
10263
|
+
}
|
|
10264
|
+
const params = normalizeD1BindParams(Array.isArray(input.params) ? input.params : []);
|
|
10265
|
+
const { target, session } = resolveSessionTarget(db, input);
|
|
10266
|
+
if (isMultiStatement(query)) {
|
|
10267
|
+
if (params.length > 0) {
|
|
10268
|
+
return {
|
|
10269
|
+
ok: false,
|
|
10270
|
+
error: "invalid_query",
|
|
10271
|
+
message: "params are only supported for single prepared statements"
|
|
10272
|
+
};
|
|
10273
|
+
}
|
|
10274
|
+
const resolveBookmark = () => session?.getBookmark() ?? null;
|
|
10275
|
+
const parts = splitSqlStatements(query);
|
|
10276
|
+
if (typeof target.batch === "function") {
|
|
10277
|
+
const prepared = parts.map((part) => target.prepare(part));
|
|
10278
|
+
const results = await target.batch(prepared);
|
|
10279
|
+
const rows2 = collectRowsFromBatchResults(results);
|
|
10280
|
+
return {
|
|
10281
|
+
ok: true,
|
|
10282
|
+
rows: rows2,
|
|
10283
|
+
columns: resultColumns(rows2),
|
|
10284
|
+
// Prefer returned rows; otherwise sum mutation meta.changes (not statement count).
|
|
10285
|
+
count: rows2.length > 0 ? rows2.length : sumBatchAffectedRows(results, parts.length),
|
|
10286
|
+
bookmark: resolveBookmark(),
|
|
10287
|
+
meta: { statementCount: parts.length, results },
|
|
10288
|
+
statementCount: parts.length
|
|
10289
|
+
};
|
|
10290
|
+
}
|
|
10291
|
+
const canExec = typeof target.exec === "function" && !session;
|
|
10292
|
+
if (canExec) {
|
|
10293
|
+
const execResult = await target.exec(query);
|
|
10294
|
+
return {
|
|
10295
|
+
ok: true,
|
|
10296
|
+
rows: [],
|
|
10297
|
+
columns: [],
|
|
10298
|
+
durationMs: execResult.duration,
|
|
10299
|
+
count: execResult.count,
|
|
10300
|
+
bookmark: resolveBookmark(),
|
|
10301
|
+
meta: { statementCount: execResult.count },
|
|
10302
|
+
statementCount: execResult.count
|
|
10303
|
+
};
|
|
10304
|
+
}
|
|
10305
|
+
return {
|
|
10306
|
+
ok: false,
|
|
10307
|
+
error: "invalid_query",
|
|
10308
|
+
message: "multi-statement query requires D1 batch() or exec() support"
|
|
10309
|
+
};
|
|
10310
|
+
}
|
|
10311
|
+
let statement = target.prepare(query);
|
|
10312
|
+
if (params.length > 0) {
|
|
10313
|
+
statement = statement.bind(...params);
|
|
10314
|
+
}
|
|
10315
|
+
const expectsRows = statementExpectsResultRows(query);
|
|
10316
|
+
const result = expectsRows ? await statement.all() : await statement.run();
|
|
10317
|
+
if (result.success === false) {
|
|
10318
|
+
const errorField = result.error;
|
|
10319
|
+
const message = typeof errorField === "string" && errorField.trim() ? errorField : "D1 query failed";
|
|
10320
|
+
return {
|
|
10321
|
+
ok: false,
|
|
10322
|
+
error: "query_failed",
|
|
10323
|
+
message
|
|
10324
|
+
};
|
|
10325
|
+
}
|
|
10326
|
+
const rows = Array.isArray(result.results) ? result.results : [];
|
|
10327
|
+
const bookmark = session?.getBookmark() ?? null;
|
|
10328
|
+
const meta = result.meta ?? {};
|
|
10329
|
+
const changes = typeof meta.changes === "number" ? meta.changes : typeof result.meta?.changes === "number" ? result.meta.changes : void 0;
|
|
10330
|
+
return {
|
|
10331
|
+
ok: true,
|
|
10332
|
+
rows,
|
|
10333
|
+
columns: resultColumns(rows),
|
|
10334
|
+
durationMs: typeof result.meta?.duration === "number" ? result.meta.duration : void 0,
|
|
10335
|
+
count: expectsRows ? rows.length : changes ?? rows.length,
|
|
10336
|
+
bookmark,
|
|
10337
|
+
meta
|
|
10338
|
+
};
|
|
10339
|
+
}
|
|
10340
|
+
async function executeD1Batch(db, input) {
|
|
10341
|
+
const statements = Array.isArray(input.statements) ? input.statements : [];
|
|
10342
|
+
if (statements.length === 0) {
|
|
10343
|
+
return {
|
|
10344
|
+
ok: false,
|
|
10345
|
+
error: "invalid_batch",
|
|
10346
|
+
message: "statements must contain at least one item"
|
|
10347
|
+
};
|
|
10348
|
+
}
|
|
10349
|
+
const { target, session } = resolveSessionTarget(db, input);
|
|
10350
|
+
const prepared = statements.map((item) => {
|
|
10351
|
+
const query = item.query?.trim();
|
|
10352
|
+
if (!query) {
|
|
10353
|
+
throw new Error("statements[].query is required");
|
|
10354
|
+
}
|
|
10355
|
+
const params = normalizeD1BindParams(Array.isArray(item.params) ? item.params : []);
|
|
10356
|
+
let statement = target.prepare(query);
|
|
10357
|
+
if (params.length > 0) {
|
|
10358
|
+
statement = statement.bind(...params);
|
|
10359
|
+
}
|
|
10360
|
+
return statement;
|
|
10361
|
+
});
|
|
10362
|
+
const results = await target.batch(prepared);
|
|
10363
|
+
const bookmark = session?.getBookmark() ?? null;
|
|
10364
|
+
return {
|
|
10365
|
+
ok: true,
|
|
10366
|
+
results,
|
|
10367
|
+
bookmark
|
|
10368
|
+
};
|
|
10369
|
+
}
|
|
10370
|
+
|
|
10371
|
+
// src/cloudflare/d1/sql.ts
|
|
10372
|
+
function asSingle(sql, params) {
|
|
10373
|
+
return { sql, params };
|
|
10374
|
+
}
|
|
10375
|
+
function asBatch(statements) {
|
|
10376
|
+
if (statements.length === 0) {
|
|
10377
|
+
throw new D1SqlCompileError("empty_insert", "batch requires at least one statement");
|
|
10378
|
+
}
|
|
10379
|
+
return {
|
|
10380
|
+
sql: statements[0].sql,
|
|
10381
|
+
params: statements[0].params,
|
|
10382
|
+
statements
|
|
10383
|
+
};
|
|
10384
|
+
}
|
|
10385
|
+
function hasPaginationBounds(payload) {
|
|
10386
|
+
return payload.limit !== void 0 || payload.offset !== void 0 || payload.page_size !== void 0 || payload.current_page !== void 0;
|
|
10387
|
+
}
|
|
10388
|
+
function assertEffectiveMutationBounds(payload) {
|
|
10389
|
+
if (payload.current_page !== void 0 && payload.page_size === void 0 && payload.limit === void 0) {
|
|
10390
|
+
throw new D1SqlCompileError(
|
|
10391
|
+
"page_without_size",
|
|
10392
|
+
"current_page requires page_size (or limit) for bounded update/delete on D1 edge-local"
|
|
10393
|
+
);
|
|
10394
|
+
}
|
|
10395
|
+
const clause = limitOffsetClause(payload);
|
|
10396
|
+
if (!clause.trim()) {
|
|
10397
|
+
throw new D1SqlCompileError(
|
|
10398
|
+
"invalid_pagination_bounds",
|
|
10399
|
+
"Bounded update/delete requires limit, offset, and/or page_size on D1 edge-local"
|
|
10400
|
+
);
|
|
10401
|
+
}
|
|
10402
|
+
}
|
|
10403
|
+
var SQLITE_ROWID_ALIASES = /* @__PURE__ */ new Set(["rowid", "oid", "_rowid_"]);
|
|
10404
|
+
function referencedMutationColumns(payload) {
|
|
10405
|
+
const names = [];
|
|
10406
|
+
for (const condition of payload.conditions ?? []) {
|
|
10407
|
+
const column = (condition.column ?? condition.eq_column ?? "").trim();
|
|
10408
|
+
if (column) names.push(column);
|
|
10409
|
+
}
|
|
10410
|
+
if (payload.sort_by?.field?.trim()) {
|
|
10411
|
+
names.push(payload.sort_by.field.trim());
|
|
10412
|
+
}
|
|
10413
|
+
return names;
|
|
10414
|
+
}
|
|
10415
|
+
function resolveBoundedIdentityColumn(payload, options) {
|
|
10416
|
+
const refs = referencedMutationColumns(payload);
|
|
10417
|
+
const lower = refs.map((name) => name.toLowerCase());
|
|
10418
|
+
if (lower.some((name) => SQLITE_ROWID_ALIASES.has(name))) {
|
|
10419
|
+
throw new D1SqlCompileError(
|
|
10420
|
+
"bounded_mutation_unsafe_identity",
|
|
10421
|
+
"Bounded update/delete cannot use rowid/oid/_rowid_ in filters or order \u2014 those names may be user columns that shadow SQLite row identity. Resolve the table primary key (or a single-column unique index) instead."
|
|
10422
|
+
);
|
|
10423
|
+
}
|
|
10424
|
+
const identity = options?.identityColumn?.trim();
|
|
10425
|
+
if (!identity) {
|
|
10426
|
+
throw new D1SqlCompileError(
|
|
10427
|
+
"bounded_mutation_no_unique_identity",
|
|
10428
|
+
"Bounded update/delete requires a proven unique identity column from the table schema (single-column PRIMARY KEY or UNIQUE index). Cannot assume a column named id."
|
|
10429
|
+
);
|
|
10430
|
+
}
|
|
10431
|
+
if (SQLITE_ROWID_ALIASES.has(identity.toLowerCase())) {
|
|
10432
|
+
throw new D1SqlCompileError(
|
|
10433
|
+
"bounded_mutation_unsafe_identity",
|
|
10434
|
+
`Bounded update/delete cannot use identity column "${identity}" \u2014 it may shadow SQLite row identity.`
|
|
10435
|
+
);
|
|
10436
|
+
}
|
|
10437
|
+
return identity;
|
|
10438
|
+
}
|
|
10439
|
+
function boundedRowidInClause(table, whereSql, whereParams, payload, options) {
|
|
10440
|
+
assertEffectiveMutationBounds(payload);
|
|
10441
|
+
const identity = resolveBoundedIdentityColumn(payload, options);
|
|
10442
|
+
const identitySql = quoteIdent(identity);
|
|
10443
|
+
const innerWhere = whereSql.replace(/^WHERE\s+/i, "");
|
|
10444
|
+
const parts = [
|
|
10445
|
+
`SELECT ${identitySql} FROM ${quoteIdent(table)}`,
|
|
10446
|
+
innerWhere ? `WHERE ${innerWhere}` : "",
|
|
10447
|
+
orderClause(payload.sort_by),
|
|
10448
|
+
limitOffsetClause(payload)
|
|
10449
|
+
].filter(Boolean);
|
|
10450
|
+
return {
|
|
10451
|
+
sql: `${identitySql} IN (${parts.join(" ")})`,
|
|
10452
|
+
params: [...whereParams]
|
|
10453
|
+
};
|
|
10454
|
+
}
|
|
10455
|
+
var D1SqlCompileError = class extends Error {
|
|
10456
|
+
code;
|
|
10457
|
+
constructor(code, message) {
|
|
10458
|
+
super(message);
|
|
10459
|
+
this.name = "D1SqlCompileError";
|
|
10460
|
+
this.code = code;
|
|
10461
|
+
}
|
|
10462
|
+
};
|
|
10463
|
+
var SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
10464
|
+
function quoteIdent(name) {
|
|
10465
|
+
const trimmed = name.trim();
|
|
10466
|
+
if (!trimmed) {
|
|
10467
|
+
throw new D1SqlCompileError("invalid_identifier", "Identifier is required");
|
|
10468
|
+
}
|
|
10469
|
+
if (trimmed.includes(".")) {
|
|
10470
|
+
throw new D1SqlCompileError(
|
|
10471
|
+
"schema_qualified_name",
|
|
10472
|
+
`Schema-qualified names are unsupported on D1 edge-local: ${trimmed}`
|
|
10473
|
+
);
|
|
10474
|
+
}
|
|
10475
|
+
if (!SAFE_IDENTIFIER.test(trimmed)) {
|
|
10476
|
+
throw new D1SqlCompileError(
|
|
10477
|
+
"invalid_identifier",
|
|
10478
|
+
`Invalid SQL identifier for D1 edge-local: ${trimmed}`
|
|
10479
|
+
);
|
|
10480
|
+
}
|
|
10481
|
+
return quoteQualifiedIdentifier(trimmed);
|
|
10482
|
+
}
|
|
10483
|
+
var RESPONSE_ALIAS_PATTERN2 = /^([A-Za-z_][A-Za-z0-9_]*)\s*:\s*([A-Za-z_][A-Za-z0-9_]*)$/;
|
|
10484
|
+
var SQL_ALIAS_PATTERN2 = /^([A-Za-z_][A-Za-z0-9_]*)\s+(?:as\s+)?([A-Za-z_][A-Za-z0-9_]*)$/i;
|
|
10485
|
+
function quoteSelectTokenStrict(token) {
|
|
10486
|
+
const trimmed = token.trim();
|
|
10487
|
+
if (!trimmed || trimmed === "*") return trimmed || "*";
|
|
10488
|
+
if (SAFE_IDENTIFIER.test(trimmed)) {
|
|
10489
|
+
return quoteQualifiedIdentifier(trimmed);
|
|
10490
|
+
}
|
|
10491
|
+
const responseAlias = RESPONSE_ALIAS_PATTERN2.exec(trimmed);
|
|
10492
|
+
if (responseAlias) {
|
|
10493
|
+
const [, aliasIdentifier, baseIdentifier] = responseAlias;
|
|
10494
|
+
return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteQualifiedIdentifier(aliasIdentifier)}`;
|
|
10495
|
+
}
|
|
10496
|
+
const sqlAlias = SQL_ALIAS_PATTERN2.exec(trimmed);
|
|
10497
|
+
if (sqlAlias) {
|
|
10498
|
+
const [, baseIdentifier, aliasIdentifier] = sqlAlias;
|
|
10499
|
+
return `${quoteQualifiedIdentifier(baseIdentifier)} AS ${quoteQualifiedIdentifier(aliasIdentifier)}`;
|
|
10500
|
+
}
|
|
10501
|
+
throw new D1SqlCompileError(
|
|
10502
|
+
"unsafe_select",
|
|
10503
|
+
`SELECT/RETURNING expressions are not supported on D1 edge-local; use column names or aliases only: ${token}`
|
|
10504
|
+
);
|
|
10505
|
+
}
|
|
10506
|
+
function selectClause(columns) {
|
|
10507
|
+
if (columns === void 0 || columns === "*" || columns === "") return "*";
|
|
10508
|
+
if (typeof columns === "string") {
|
|
10509
|
+
const trimmed = columns.trim();
|
|
10510
|
+
if (!trimmed || trimmed === "*") return "*";
|
|
10511
|
+
return trimmed.split(",").map((part) => quoteSelectTokenStrict(part)).join(", ");
|
|
10512
|
+
}
|
|
10513
|
+
if (columns.length === 0) return "*";
|
|
10514
|
+
return columns.map((column) => quoteSelectTokenStrict(column)).join(", ");
|
|
10515
|
+
}
|
|
10516
|
+
function wantsReturning(payload) {
|
|
10517
|
+
return Boolean(payload.columns) && payload.head !== true;
|
|
10518
|
+
}
|
|
10519
|
+
function conditionToWhere(condition) {
|
|
10520
|
+
const column = condition.column?.trim() || condition.eq_column?.trim() || "";
|
|
10521
|
+
if (!column) {
|
|
10522
|
+
throw new D1SqlCompileError("invalid_condition", "Condition requires a column");
|
|
10523
|
+
}
|
|
10524
|
+
const col = quoteIdent(column);
|
|
10525
|
+
const operator = (condition.operator || "eq").toLowerCase();
|
|
10526
|
+
const value = condition.value !== void 0 ? condition.value : condition.eq_value;
|
|
10527
|
+
switch (operator) {
|
|
10528
|
+
case "eq": {
|
|
10529
|
+
if (Array.isArray(value)) {
|
|
10530
|
+
if (value.length === 0) {
|
|
10531
|
+
return { sql: "1 = 0", params: [] };
|
|
10532
|
+
}
|
|
10533
|
+
const placeholders = value.map(() => "?").join(", ");
|
|
10534
|
+
return { sql: `${col} IN (${placeholders})`, params: [...value] };
|
|
10535
|
+
}
|
|
10536
|
+
if (value === null) {
|
|
10537
|
+
return { sql: `${col} IS NULL`, params: [] };
|
|
10538
|
+
}
|
|
10539
|
+
return { sql: `${col} = ?`, params: [value ?? null] };
|
|
10540
|
+
}
|
|
10541
|
+
case "neq":
|
|
10542
|
+
if (value === null) {
|
|
10543
|
+
return { sql: `${col} IS NOT NULL`, params: [] };
|
|
10544
|
+
}
|
|
10545
|
+
return { sql: `${col} != ?`, params: [value ?? null] };
|
|
10546
|
+
case "gt":
|
|
10547
|
+
return { sql: `${col} > ?`, params: [value ?? null] };
|
|
10548
|
+
case "gte":
|
|
10549
|
+
return { sql: `${col} >= ?`, params: [value ?? null] };
|
|
10550
|
+
case "lt":
|
|
10551
|
+
return { sql: `${col} < ?`, params: [value ?? null] };
|
|
10552
|
+
case "lte":
|
|
10553
|
+
return { sql: `${col} <= ?`, params: [value ?? null] };
|
|
10554
|
+
case "like":
|
|
10555
|
+
return { sql: `${col} LIKE ?`, params: [value ?? null] };
|
|
10556
|
+
case "ilike":
|
|
10557
|
+
return { sql: `${col} LIKE ? COLLATE NOCASE`, params: [value ?? null] };
|
|
10558
|
+
case "in": {
|
|
10559
|
+
const items = Array.isArray(value) ? value : value === void 0 ? [] : [value];
|
|
10560
|
+
if (items.length === 0) {
|
|
10561
|
+
return { sql: "1 = 0", params: [] };
|
|
10562
|
+
}
|
|
10563
|
+
const placeholders = items.map(() => "?").join(", ");
|
|
10564
|
+
return { sql: `${col} IN (${placeholders})`, params: [...items] };
|
|
10565
|
+
}
|
|
10566
|
+
case "is": {
|
|
10567
|
+
if (value === null || value === "null") {
|
|
10568
|
+
return { sql: `${col} IS NULL`, params: [] };
|
|
10569
|
+
}
|
|
10570
|
+
if (value === true || value === "true") {
|
|
10571
|
+
return { sql: `${col} IS TRUE`, params: [] };
|
|
10572
|
+
}
|
|
10573
|
+
if (value === false || value === "false") {
|
|
10574
|
+
return { sql: `${col} IS FALSE`, params: [] };
|
|
10575
|
+
}
|
|
10576
|
+
return { sql: `${col} IS ?`, params: [value] };
|
|
10577
|
+
}
|
|
10578
|
+
case "not":
|
|
10579
|
+
case "or":
|
|
10580
|
+
case "contains":
|
|
10581
|
+
case "containedby":
|
|
10582
|
+
throw new D1SqlCompileError(
|
|
10583
|
+
"unsupported_operator",
|
|
10584
|
+
`Condition operator "${operator}" is not supported on D1 edge-local L1`
|
|
10585
|
+
);
|
|
10586
|
+
default:
|
|
10587
|
+
throw new D1SqlCompileError(
|
|
10588
|
+
"unsupported_operator",
|
|
10589
|
+
`Unknown condition operator "${operator}"`
|
|
10590
|
+
);
|
|
10591
|
+
}
|
|
10592
|
+
}
|
|
10593
|
+
function buildWhere(conditions) {
|
|
10594
|
+
if (!conditions?.length) {
|
|
10595
|
+
return { sql: "", params: [] };
|
|
10596
|
+
}
|
|
10597
|
+
const parts = [];
|
|
10598
|
+
const params = [];
|
|
10599
|
+
for (const condition of conditions) {
|
|
10600
|
+
if (!condition.operator && condition.eq_column) {
|
|
10601
|
+
const clause2 = conditionToWhere({
|
|
10602
|
+
...condition,
|
|
10603
|
+
operator: "eq",
|
|
10604
|
+
column: condition.eq_column,
|
|
10605
|
+
value: condition.eq_value
|
|
10606
|
+
});
|
|
10607
|
+
parts.push(clause2.sql);
|
|
10608
|
+
params.push(...clause2.params);
|
|
10609
|
+
continue;
|
|
10610
|
+
}
|
|
10611
|
+
const clause = conditionToWhere(condition);
|
|
10612
|
+
parts.push(clause.sql);
|
|
10613
|
+
params.push(...clause.params);
|
|
10614
|
+
}
|
|
10615
|
+
return {
|
|
10616
|
+
sql: parts.length ? `WHERE ${parts.join(" AND ")}` : "",
|
|
10617
|
+
params
|
|
10618
|
+
};
|
|
10619
|
+
}
|
|
10620
|
+
function orderClause(order) {
|
|
10621
|
+
if (!order?.field?.trim()) return "";
|
|
10622
|
+
const direction = order.direction === "descending" ? "DESC" : "ASC";
|
|
10623
|
+
return `ORDER BY ${quoteIdent(order.field)} ${direction}`;
|
|
10624
|
+
}
|
|
10625
|
+
function limitOffsetClause(payload) {
|
|
10626
|
+
let limit = payload.limit;
|
|
10627
|
+
let offset = payload.offset;
|
|
10628
|
+
if (limit === void 0 && payload.page_size !== void 0) {
|
|
10629
|
+
limit = payload.page_size;
|
|
10630
|
+
}
|
|
10631
|
+
if (payload.current_page !== void 0 && offset === void 0) {
|
|
10632
|
+
const pageSize = payload.page_size ?? payload.limit;
|
|
10633
|
+
if (pageSize !== void 0) {
|
|
10634
|
+
offset = Math.max(0, (payload.current_page - 1) * pageSize);
|
|
10635
|
+
}
|
|
10636
|
+
}
|
|
10637
|
+
const parts = [];
|
|
10638
|
+
if (offset !== void 0 && limit === void 0) {
|
|
10639
|
+
parts.push("LIMIT -1");
|
|
10640
|
+
parts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
|
|
10641
|
+
} else {
|
|
10642
|
+
if (limit !== void 0) {
|
|
10643
|
+
parts.push(`LIMIT ${Math.max(0, Math.trunc(limit))}`);
|
|
10644
|
+
}
|
|
10645
|
+
if (offset !== void 0) {
|
|
10646
|
+
parts.push(`OFFSET ${Math.max(0, Math.trunc(offset))}`);
|
|
10647
|
+
}
|
|
10648
|
+
}
|
|
10649
|
+
return parts.join(" ");
|
|
10650
|
+
}
|
|
10651
|
+
function rejectStructuredRelations(payload) {
|
|
10652
|
+
const select = payload.select;
|
|
10653
|
+
if (select && typeof select === "object" && !Array.isArray(select)) {
|
|
10654
|
+
for (const value of Object.values(select)) {
|
|
10655
|
+
if (value && typeof value === "object") {
|
|
10656
|
+
throw new D1SqlCompileError(
|
|
10657
|
+
"relations_unsupported",
|
|
10658
|
+
"Nested relation selects are unsupported on D1 edge-local"
|
|
10659
|
+
);
|
|
10660
|
+
}
|
|
10661
|
+
}
|
|
10662
|
+
}
|
|
10663
|
+
if (payload.view_name) {
|
|
10664
|
+
throw new D1SqlCompileError(
|
|
10665
|
+
"view_unsupported",
|
|
10666
|
+
"view_name is unsupported on D1 edge-local L1"
|
|
10667
|
+
);
|
|
10668
|
+
}
|
|
10669
|
+
}
|
|
10670
|
+
function jsonValue(value) {
|
|
10671
|
+
if (value === void 0) return null;
|
|
10672
|
+
if (value !== null && typeof value === "object") {
|
|
10673
|
+
return JSON.stringify(value);
|
|
10674
|
+
}
|
|
10675
|
+
return value;
|
|
10676
|
+
}
|
|
10677
|
+
function compileD1Fetch(payload) {
|
|
10678
|
+
rejectStructuredRelations(payload);
|
|
10679
|
+
const table = payload.table_name?.trim();
|
|
10680
|
+
if (!table) {
|
|
10681
|
+
throw new D1SqlCompileError("missing_table", "table_name is required for fetch");
|
|
10682
|
+
}
|
|
10683
|
+
let columns = payload.columns;
|
|
10684
|
+
if (payload.select && typeof payload.select === "object" && !Array.isArray(payload.select)) {
|
|
10685
|
+
const keys = Object.entries(payload.select).filter(([, enabled]) => enabled === true || enabled === 1).map(([key]) => key);
|
|
10686
|
+
if (keys.length > 0) {
|
|
10687
|
+
columns = keys;
|
|
10688
|
+
}
|
|
10689
|
+
} else if (typeof payload.select === "string" && payload.select.trim()) {
|
|
10690
|
+
columns = payload.select;
|
|
10691
|
+
}
|
|
10692
|
+
let where = buildWhere(payload.conditions);
|
|
10693
|
+
if (!where.sql && payload.where && typeof payload.where === "object") {
|
|
10694
|
+
const eqConditions = Object.entries(
|
|
10695
|
+
payload.where
|
|
10696
|
+
).map(([column, value]) => ({
|
|
10697
|
+
column,
|
|
10698
|
+
operator: "eq",
|
|
10699
|
+
value
|
|
10700
|
+
}));
|
|
10701
|
+
where = buildWhere(eqConditions);
|
|
10702
|
+
}
|
|
10703
|
+
if (payload.head === true) {
|
|
10704
|
+
return compileD1CountFromWhere(table, where);
|
|
10705
|
+
}
|
|
10706
|
+
const parts = [
|
|
10707
|
+
`SELECT ${selectClause(columns)} FROM ${quoteIdent(table)}`,
|
|
10708
|
+
where.sql,
|
|
10709
|
+
orderClause(payload.sort_by),
|
|
10710
|
+
limitOffsetClause(payload)
|
|
10711
|
+
].filter(Boolean);
|
|
10712
|
+
return asSingle(parts.join(" "), where.params);
|
|
10713
|
+
}
|
|
10714
|
+
function compileD1Count(payload) {
|
|
10715
|
+
rejectStructuredRelations(payload);
|
|
10716
|
+
const table = payload.table_name?.trim();
|
|
10717
|
+
if (!table) {
|
|
10718
|
+
throw new D1SqlCompileError("missing_table", "table_name is required for count");
|
|
10719
|
+
}
|
|
10720
|
+
let where = buildWhere(payload.conditions);
|
|
10721
|
+
if (!where.sql && payload.where && typeof payload.where === "object") {
|
|
10722
|
+
const eqConditions = Object.entries(
|
|
10723
|
+
payload.where
|
|
10724
|
+
).map(([column, value]) => ({
|
|
10725
|
+
column,
|
|
10726
|
+
operator: "eq",
|
|
10727
|
+
value
|
|
10728
|
+
}));
|
|
10729
|
+
where = buildWhere(eqConditions);
|
|
10730
|
+
}
|
|
10731
|
+
return compileD1CountFromWhere(table, where);
|
|
10732
|
+
}
|
|
10733
|
+
function compileD1CountFromWhere(table, where) {
|
|
10734
|
+
const parts = [
|
|
10735
|
+
`SELECT COUNT(*) AS __athena_count FROM ${quoteIdent(table)}`,
|
|
10736
|
+
where.sql
|
|
10737
|
+
].filter(Boolean);
|
|
10738
|
+
return asSingle(parts.join(" "), where.params);
|
|
10739
|
+
}
|
|
10740
|
+
function extractAthenaCount(rows) {
|
|
10741
|
+
const first = rows[0];
|
|
10742
|
+
const rawCount = first && typeof first === "object" && !Array.isArray(first) ? first.__athena_count : void 0;
|
|
10743
|
+
const count = typeof rawCount === "number" ? rawCount : typeof rawCount === "bigint" ? Number(rawCount) : Number(rawCount ?? 0);
|
|
10744
|
+
return Number.isFinite(count) ? count : 0;
|
|
10745
|
+
}
|
|
10746
|
+
function compileSingleRowInsert(table, row, payload) {
|
|
10747
|
+
const columns = Object.keys(row);
|
|
10748
|
+
if (columns.length === 0) {
|
|
10749
|
+
if (payload.on_conflict) {
|
|
10750
|
+
throw new D1SqlCompileError(
|
|
10751
|
+
"default_values_upsert_unsupported",
|
|
10752
|
+
"DEFAULT VALUES upsert is unsupported on D1 edge-local; provide insert columns or omit onConflict"
|
|
10753
|
+
);
|
|
10754
|
+
}
|
|
10755
|
+
const parts2 = [`INSERT INTO ${quoteIdent(table)} DEFAULT VALUES`];
|
|
10756
|
+
if (wantsReturning(payload)) {
|
|
10757
|
+
parts2.push(`RETURNING ${selectClause(payload.columns)}`);
|
|
10758
|
+
}
|
|
10759
|
+
return { sql: parts2.join(" "), params: [] };
|
|
10760
|
+
}
|
|
10761
|
+
const params = columns.map((column) => jsonValue(row[column]));
|
|
10762
|
+
const parts = [
|
|
10763
|
+
`INSERT INTO ${quoteIdent(table)} (${columns.map(quoteIdent).join(", ")})`,
|
|
10764
|
+
`VALUES (${columns.map(() => "?").join(", ")})`
|
|
10765
|
+
];
|
|
10766
|
+
appendOnConflict(parts, params, payload, columns);
|
|
10767
|
+
if (wantsReturning(payload)) {
|
|
10768
|
+
parts.push(`RETURNING ${selectClause(payload.columns)}`);
|
|
10769
|
+
}
|
|
10770
|
+
return { sql: parts.join(" "), params };
|
|
10771
|
+
}
|
|
10772
|
+
function parseOnConflictColumns(onConflict) {
|
|
10773
|
+
const raw = Array.isArray(onConflict) ? onConflict : [onConflict];
|
|
10774
|
+
const columns = [];
|
|
10775
|
+
for (const item of raw) {
|
|
10776
|
+
for (const part of String(item).split(",")) {
|
|
10777
|
+
const trimmed = part.trim();
|
|
10778
|
+
if (trimmed) columns.push(trimmed);
|
|
10779
|
+
}
|
|
10780
|
+
}
|
|
10781
|
+
if (columns.length === 0) {
|
|
10782
|
+
throw new D1SqlCompileError(
|
|
10783
|
+
"invalid_on_conflict",
|
|
10784
|
+
"on_conflict requires at least one column"
|
|
10785
|
+
);
|
|
10786
|
+
}
|
|
10787
|
+
return columns;
|
|
10788
|
+
}
|
|
10789
|
+
function appendOnConflict(parts, params, payload, insertColumns) {
|
|
10790
|
+
if (!payload.on_conflict) return;
|
|
10791
|
+
const conflictCols = parseOnConflictColumns(payload.on_conflict);
|
|
10792
|
+
const conflictSql = conflictCols.map(quoteIdent).join(", ");
|
|
10793
|
+
if (payload.update_body !== void 0 && payload.update_body !== null) {
|
|
10794
|
+
const entries = Object.entries(payload.update_body);
|
|
10795
|
+
if (entries.length === 0) {
|
|
10796
|
+
parts.push(`ON CONFLICT (${conflictSql}) DO NOTHING`);
|
|
10797
|
+
return;
|
|
10798
|
+
}
|
|
10799
|
+
const assignments2 = entries.map(([column, value]) => {
|
|
10800
|
+
params.push(jsonValue(value));
|
|
10801
|
+
return `${quoteIdent(column)} = ?`;
|
|
10802
|
+
});
|
|
10803
|
+
parts.push(`ON CONFLICT (${conflictSql}) DO UPDATE SET ${assignments2.join(", ")}`);
|
|
10804
|
+
return;
|
|
10805
|
+
}
|
|
10806
|
+
const conflictSet = new Set(conflictCols);
|
|
10807
|
+
const assignments = insertColumns.filter((column) => !conflictSet.has(column)).map((column) => {
|
|
10808
|
+
const quoted = quoteIdent(column);
|
|
10809
|
+
return `${quoted} = excluded.${quoted}`;
|
|
10810
|
+
});
|
|
10811
|
+
if (assignments.length === 0) {
|
|
10812
|
+
parts.push(`ON CONFLICT (${conflictSql}) DO NOTHING`);
|
|
10813
|
+
} else {
|
|
10814
|
+
parts.push(`ON CONFLICT (${conflictSql}) DO UPDATE SET ${assignments.join(", ")}`);
|
|
10815
|
+
}
|
|
10816
|
+
}
|
|
10817
|
+
function compileD1Insert(payload) {
|
|
10818
|
+
const table = payload.table_name?.trim();
|
|
10819
|
+
if (!table) {
|
|
10820
|
+
throw new D1SqlCompileError("missing_table", "table_name is required for insert");
|
|
10821
|
+
}
|
|
10822
|
+
const rows = Array.isArray(payload.insert_body) ? payload.insert_body : [payload.insert_body];
|
|
10823
|
+
if (rows.length === 0) {
|
|
10824
|
+
throw new D1SqlCompileError("empty_insert", "insert_body must not be empty");
|
|
10825
|
+
}
|
|
10826
|
+
const columns = [];
|
|
10827
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10828
|
+
for (const row of rows) {
|
|
10829
|
+
for (const key of Object.keys(row ?? {})) {
|
|
10830
|
+
if (seen.has(key)) continue;
|
|
10831
|
+
seen.add(key);
|
|
10832
|
+
columns.push(key);
|
|
10833
|
+
}
|
|
10834
|
+
}
|
|
10835
|
+
if (columns.length === 0) {
|
|
10836
|
+
if (rows.length === 1) {
|
|
10837
|
+
const compiled = compileSingleRowInsert(table, {}, payload);
|
|
10838
|
+
return asSingle(compiled.sql, compiled.params);
|
|
10839
|
+
}
|
|
10840
|
+
throw new D1SqlCompileError(
|
|
10841
|
+
"empty_insert",
|
|
10842
|
+
"Multi-row insert_body cannot be empty objects; use a single {} for DEFAULT VALUES"
|
|
10843
|
+
);
|
|
10844
|
+
}
|
|
10845
|
+
const forceNullForMissing = payload.default_to_null === true;
|
|
10846
|
+
const isSparse = rows.some(
|
|
10847
|
+
(row) => columns.some((column) => !Object.prototype.hasOwnProperty.call(row, column))
|
|
10848
|
+
);
|
|
10849
|
+
if (isSparse && !forceNullForMissing && rows.length > 1) {
|
|
10850
|
+
return asBatch(
|
|
10851
|
+
rows.map((row) => compileSingleRowInsert(table, row, payload))
|
|
10852
|
+
);
|
|
10853
|
+
}
|
|
10854
|
+
if (isSparse && !forceNullForMissing && rows.length === 1) {
|
|
10855
|
+
const compiled = compileSingleRowInsert(table, rows[0], payload);
|
|
10856
|
+
return asSingle(compiled.sql, compiled.params);
|
|
10857
|
+
}
|
|
10858
|
+
const params = [];
|
|
10859
|
+
const valueGroups = rows.map((row) => {
|
|
10860
|
+
const placeholders = columns.map((column) => {
|
|
10861
|
+
const has = Object.prototype.hasOwnProperty.call(row, column);
|
|
10862
|
+
if (!has) {
|
|
10863
|
+
params.push(null);
|
|
10864
|
+
return "?";
|
|
10865
|
+
}
|
|
10866
|
+
params.push(jsonValue(row[column]));
|
|
10867
|
+
return "?";
|
|
10868
|
+
});
|
|
10869
|
+
return `(${placeholders.join(", ")})`;
|
|
10870
|
+
});
|
|
10871
|
+
const parts = [
|
|
10872
|
+
`INSERT INTO ${quoteIdent(table)} (${columns.map(quoteIdent).join(", ")})`,
|
|
10873
|
+
`VALUES ${valueGroups.join(", ")}`
|
|
10874
|
+
];
|
|
10875
|
+
appendOnConflict(parts, params, payload, columns);
|
|
10876
|
+
if (wantsReturning(payload)) {
|
|
10877
|
+
parts.push(`RETURNING ${selectClause(payload.columns)}`);
|
|
10878
|
+
}
|
|
10879
|
+
return asSingle(parts.join(" "), params);
|
|
10880
|
+
}
|
|
10881
|
+
function compileD1Update(payload, options) {
|
|
10882
|
+
const table = payload.table_name?.trim();
|
|
10883
|
+
if (!table) {
|
|
10884
|
+
throw new D1SqlCompileError("missing_table", "table_name is required for update");
|
|
10885
|
+
}
|
|
10886
|
+
const entries = Object.entries(payload.update_body ?? {});
|
|
10887
|
+
if (entries.length === 0) {
|
|
10888
|
+
throw new D1SqlCompileError("empty_update", "update_body must not be empty");
|
|
10889
|
+
}
|
|
10890
|
+
const params = [];
|
|
10891
|
+
const assignments = entries.map(([column, value]) => {
|
|
10892
|
+
params.push(jsonValue(value));
|
|
10893
|
+
return `${quoteIdent(column)} = ?`;
|
|
10894
|
+
});
|
|
10895
|
+
const where = buildWhere(payload.conditions);
|
|
10896
|
+
if (!where.sql) {
|
|
10897
|
+
throw new D1SqlCompileError(
|
|
10898
|
+
"unfiltered_update",
|
|
10899
|
+
"UPDATE requires at least one condition on D1 edge-local"
|
|
10900
|
+
);
|
|
10901
|
+
}
|
|
10902
|
+
if (hasPaginationBounds(payload) || payload.sort_by) {
|
|
10903
|
+
if (!hasPaginationBounds(payload)) {
|
|
10904
|
+
throw new D1SqlCompileError(
|
|
10905
|
+
"order_without_bounds",
|
|
10906
|
+
"UPDATE with sort_by requires limit/offset or page_size/current_page on D1 edge-local"
|
|
10907
|
+
);
|
|
10908
|
+
}
|
|
10909
|
+
const bounded = boundedRowidInClause(
|
|
10910
|
+
table,
|
|
10911
|
+
where.sql,
|
|
10912
|
+
where.params,
|
|
10913
|
+
{
|
|
10914
|
+
...payload,
|
|
10915
|
+
conditions: payload.conditions
|
|
10916
|
+
},
|
|
10917
|
+
options
|
|
10918
|
+
);
|
|
10919
|
+
params.push(...bounded.params);
|
|
10920
|
+
const parts2 = [
|
|
10921
|
+
`UPDATE ${quoteIdent(table)} SET ${assignments.join(", ")}`,
|
|
10922
|
+
`WHERE ${bounded.sql}`
|
|
10923
|
+
];
|
|
10924
|
+
if (wantsReturning(payload)) {
|
|
10925
|
+
parts2.push(`RETURNING ${selectClause(payload.columns)}`);
|
|
10926
|
+
}
|
|
10927
|
+
return asSingle(parts2.join(" "), params);
|
|
10928
|
+
}
|
|
10929
|
+
params.push(...where.params);
|
|
10930
|
+
const parts = [
|
|
10931
|
+
`UPDATE ${quoteIdent(table)} SET ${assignments.join(", ")}`,
|
|
10932
|
+
where.sql
|
|
10933
|
+
];
|
|
10934
|
+
if (wantsReturning(payload)) {
|
|
10935
|
+
parts.push(`RETURNING ${selectClause(payload.columns)}`);
|
|
10936
|
+
}
|
|
10937
|
+
return asSingle(parts.join(" "), params);
|
|
10938
|
+
}
|
|
10939
|
+
function compileD1Delete(payload, options) {
|
|
10940
|
+
const table = payload.table_name?.trim();
|
|
10941
|
+
if (!table) {
|
|
10942
|
+
throw new D1SqlCompileError("missing_table", "table_name is required for delete");
|
|
10943
|
+
}
|
|
10944
|
+
const conditions = [...payload.conditions ?? []];
|
|
10945
|
+
if (payload.resource_id !== void 0 && payload.resource_id !== null) {
|
|
10946
|
+
const hasResourceIdFilter = conditions.some((condition) => {
|
|
10947
|
+
const column = (condition.column ?? condition.eq_column ?? "").trim();
|
|
10948
|
+
return column === "resource_id";
|
|
10949
|
+
});
|
|
10950
|
+
const hasIdFilter = conditions.some((condition) => {
|
|
10951
|
+
const column = (condition.column ?? condition.eq_column ?? "").trim();
|
|
10952
|
+
return column === "id";
|
|
10953
|
+
});
|
|
10954
|
+
if (!hasResourceIdFilter && !hasIdFilter) {
|
|
10955
|
+
conditions.unshift({
|
|
10956
|
+
column: "id",
|
|
10957
|
+
operator: "eq",
|
|
10958
|
+
value: payload.resource_id
|
|
10959
|
+
});
|
|
10960
|
+
}
|
|
10961
|
+
}
|
|
10962
|
+
const where = buildWhere(conditions);
|
|
10963
|
+
if (!where.sql) {
|
|
10964
|
+
throw new D1SqlCompileError(
|
|
10965
|
+
"unfiltered_delete",
|
|
10966
|
+
"DELETE requires at least one condition or resource_id on D1 edge-local"
|
|
10967
|
+
);
|
|
10968
|
+
}
|
|
10969
|
+
if (hasPaginationBounds(payload) || payload.sort_by) {
|
|
10970
|
+
if (!hasPaginationBounds(payload)) {
|
|
10971
|
+
throw new D1SqlCompileError(
|
|
10972
|
+
"order_without_bounds",
|
|
10973
|
+
"DELETE with sort_by requires limit/offset or page_size/current_page on D1 edge-local"
|
|
10974
|
+
);
|
|
10975
|
+
}
|
|
10976
|
+
const bounded = boundedRowidInClause(
|
|
10977
|
+
table,
|
|
10978
|
+
where.sql,
|
|
10979
|
+
where.params,
|
|
10980
|
+
{
|
|
10981
|
+
...payload,
|
|
10982
|
+
conditions
|
|
10983
|
+
},
|
|
10984
|
+
options
|
|
10985
|
+
);
|
|
10986
|
+
const parts2 = [`DELETE FROM ${quoteIdent(table)}`, `WHERE ${bounded.sql}`];
|
|
10987
|
+
if (wantsReturning(payload)) {
|
|
10988
|
+
parts2.push(`RETURNING ${selectClause(payload.columns)}`);
|
|
10989
|
+
}
|
|
10990
|
+
return asSingle(parts2.join(" "), bounded.params);
|
|
10991
|
+
}
|
|
10992
|
+
const parts = [`DELETE FROM ${quoteIdent(table)}`, where.sql];
|
|
10993
|
+
if (wantsReturning(payload)) {
|
|
10994
|
+
parts.push(`RETURNING ${selectClause(payload.columns)}`);
|
|
10995
|
+
}
|
|
10996
|
+
return asSingle(parts.join(" "), where.params);
|
|
10997
|
+
}
|
|
10998
|
+
|
|
10999
|
+
// src/cloudflare/d1/sql-rewrite.ts
|
|
11000
|
+
var PG_CAST_TYPES = "text|uuid|citext|varchar|bpchar|char|integer|int4|int8|bigint|smallint|boolean|bool|jsonb|json|numeric|decimal|real|float|double precision|timestamptz|timestamp|date|bytea";
|
|
11001
|
+
function mapSqlOutsideLiterals(sql, transform) {
|
|
11002
|
+
let out = "";
|
|
11003
|
+
let buffer = "";
|
|
11004
|
+
let inSingle = false;
|
|
11005
|
+
let inDouble = false;
|
|
11006
|
+
let inBacktick = false;
|
|
11007
|
+
let inBracket = false;
|
|
11008
|
+
let inLineComment = false;
|
|
11009
|
+
let inBlockComment = false;
|
|
11010
|
+
const flushCode = () => {
|
|
11011
|
+
if (buffer) {
|
|
11012
|
+
out += transform(buffer);
|
|
11013
|
+
buffer = "";
|
|
11014
|
+
}
|
|
11015
|
+
};
|
|
11016
|
+
for (let i = 0; i < sql.length; i += 1) {
|
|
11017
|
+
const ch = sql[i];
|
|
11018
|
+
const next = sql[i + 1];
|
|
11019
|
+
if (inLineComment) {
|
|
11020
|
+
out += ch;
|
|
11021
|
+
if (ch === "\n" || ch === "\r") inLineComment = false;
|
|
11022
|
+
continue;
|
|
11023
|
+
}
|
|
11024
|
+
if (inBlockComment) {
|
|
11025
|
+
out += ch;
|
|
11026
|
+
if (ch === "*" && next === "/") {
|
|
11027
|
+
out += next;
|
|
11028
|
+
i += 1;
|
|
11029
|
+
inBlockComment = false;
|
|
11030
|
+
}
|
|
11031
|
+
continue;
|
|
11032
|
+
}
|
|
11033
|
+
if (inSingle) {
|
|
11034
|
+
out += ch;
|
|
11035
|
+
if (ch === "'" && next === "'") {
|
|
11036
|
+
out += next;
|
|
11037
|
+
i += 1;
|
|
11038
|
+
continue;
|
|
11039
|
+
}
|
|
11040
|
+
if (ch === "'") inSingle = false;
|
|
11041
|
+
continue;
|
|
11042
|
+
}
|
|
11043
|
+
if (inDouble) {
|
|
11044
|
+
out += ch;
|
|
11045
|
+
if (ch === '"' && next === '"') {
|
|
11046
|
+
out += next;
|
|
11047
|
+
i += 1;
|
|
11048
|
+
continue;
|
|
11049
|
+
}
|
|
11050
|
+
if (ch === '"') inDouble = false;
|
|
11051
|
+
continue;
|
|
11052
|
+
}
|
|
11053
|
+
if (inBacktick) {
|
|
11054
|
+
out += ch;
|
|
11055
|
+
if (ch === "`" && next === "`") {
|
|
11056
|
+
out += next;
|
|
11057
|
+
i += 1;
|
|
11058
|
+
continue;
|
|
11059
|
+
}
|
|
11060
|
+
if (ch === "`") inBacktick = false;
|
|
11061
|
+
continue;
|
|
11062
|
+
}
|
|
11063
|
+
if (inBracket) {
|
|
11064
|
+
out += ch;
|
|
11065
|
+
if (ch === "]") inBracket = false;
|
|
11066
|
+
continue;
|
|
11067
|
+
}
|
|
11068
|
+
if (ch === "-" && next === "-") {
|
|
11069
|
+
flushCode();
|
|
11070
|
+
out += ch;
|
|
11071
|
+
out += next;
|
|
11072
|
+
i += 1;
|
|
11073
|
+
inLineComment = true;
|
|
11074
|
+
continue;
|
|
11075
|
+
}
|
|
11076
|
+
if (ch === "/" && next === "*") {
|
|
11077
|
+
flushCode();
|
|
11078
|
+
out += ch;
|
|
11079
|
+
out += next;
|
|
11080
|
+
i += 1;
|
|
11081
|
+
inBlockComment = true;
|
|
11082
|
+
continue;
|
|
11083
|
+
}
|
|
11084
|
+
if (ch === "'") {
|
|
11085
|
+
flushCode();
|
|
11086
|
+
out += ch;
|
|
11087
|
+
inSingle = true;
|
|
11088
|
+
continue;
|
|
11089
|
+
}
|
|
11090
|
+
if (ch === '"') {
|
|
11091
|
+
flushCode();
|
|
11092
|
+
out += ch;
|
|
11093
|
+
inDouble = true;
|
|
11094
|
+
continue;
|
|
11095
|
+
}
|
|
11096
|
+
if (ch === "`") {
|
|
11097
|
+
flushCode();
|
|
11098
|
+
out += ch;
|
|
11099
|
+
inBacktick = true;
|
|
11100
|
+
continue;
|
|
11101
|
+
}
|
|
11102
|
+
if (ch === "[") {
|
|
11103
|
+
flushCode();
|
|
11104
|
+
out += ch;
|
|
11105
|
+
inBracket = true;
|
|
11106
|
+
continue;
|
|
11107
|
+
}
|
|
11108
|
+
buffer += ch;
|
|
11109
|
+
}
|
|
11110
|
+
flushCode();
|
|
11111
|
+
return out;
|
|
11112
|
+
}
|
|
11113
|
+
function isIdentChar2(ch) {
|
|
11114
|
+
if (!ch) return false;
|
|
11115
|
+
return /[A-Za-z0-9_]/.test(ch);
|
|
11116
|
+
}
|
|
11117
|
+
function collectLimitOffsetKeywords(sql) {
|
|
11118
|
+
const hits = [];
|
|
11119
|
+
let depth = 0;
|
|
11120
|
+
let statement = 0;
|
|
11121
|
+
let inSingle = false;
|
|
11122
|
+
let inDouble = false;
|
|
11123
|
+
let inBacktick = false;
|
|
11124
|
+
let inBracket = false;
|
|
11125
|
+
let inLineComment = false;
|
|
11126
|
+
let inBlockComment = false;
|
|
11127
|
+
for (let i = 0; i < sql.length; i += 1) {
|
|
11128
|
+
const ch = sql[i];
|
|
11129
|
+
const next = sql[i + 1];
|
|
11130
|
+
if (inLineComment) {
|
|
11131
|
+
if (ch === "\n" || ch === "\r") inLineComment = false;
|
|
11132
|
+
continue;
|
|
11133
|
+
}
|
|
11134
|
+
if (inBlockComment) {
|
|
11135
|
+
if (ch === "*" && next === "/") {
|
|
11136
|
+
i += 1;
|
|
11137
|
+
inBlockComment = false;
|
|
11138
|
+
}
|
|
11139
|
+
continue;
|
|
11140
|
+
}
|
|
11141
|
+
if (inSingle) {
|
|
11142
|
+
if (ch === "'" && next === "'") {
|
|
11143
|
+
i += 1;
|
|
11144
|
+
continue;
|
|
11145
|
+
}
|
|
11146
|
+
if (ch === "'") inSingle = false;
|
|
11147
|
+
continue;
|
|
11148
|
+
}
|
|
11149
|
+
if (inDouble) {
|
|
11150
|
+
if (ch === '"' && next === '"') {
|
|
11151
|
+
i += 1;
|
|
11152
|
+
continue;
|
|
11153
|
+
}
|
|
11154
|
+
if (ch === '"') inDouble = false;
|
|
11155
|
+
continue;
|
|
11156
|
+
}
|
|
11157
|
+
if (inBacktick) {
|
|
11158
|
+
if (ch === "`" && next === "`") {
|
|
11159
|
+
i += 1;
|
|
11160
|
+
continue;
|
|
11161
|
+
}
|
|
11162
|
+
if (ch === "`") inBacktick = false;
|
|
11163
|
+
continue;
|
|
11164
|
+
}
|
|
11165
|
+
if (inBracket) {
|
|
11166
|
+
if (ch === "]") inBracket = false;
|
|
11167
|
+
continue;
|
|
11168
|
+
}
|
|
11169
|
+
if (ch === "-" && next === "-") {
|
|
11170
|
+
inLineComment = true;
|
|
11171
|
+
i += 1;
|
|
11172
|
+
continue;
|
|
11173
|
+
}
|
|
11174
|
+
if (ch === "/" && next === "*") {
|
|
11175
|
+
inBlockComment = true;
|
|
11176
|
+
i += 1;
|
|
11177
|
+
continue;
|
|
11178
|
+
}
|
|
11179
|
+
if (ch === "'") {
|
|
11180
|
+
inSingle = true;
|
|
11181
|
+
continue;
|
|
11182
|
+
}
|
|
11183
|
+
if (ch === '"') {
|
|
11184
|
+
inDouble = true;
|
|
11185
|
+
continue;
|
|
11186
|
+
}
|
|
11187
|
+
if (ch === "`") {
|
|
11188
|
+
inBacktick = true;
|
|
11189
|
+
continue;
|
|
11190
|
+
}
|
|
11191
|
+
if (ch === "[") {
|
|
11192
|
+
inBracket = true;
|
|
11193
|
+
continue;
|
|
11194
|
+
}
|
|
11195
|
+
if (ch === "(") {
|
|
11196
|
+
depth += 1;
|
|
11197
|
+
continue;
|
|
11198
|
+
}
|
|
11199
|
+
if (ch === ")") {
|
|
11200
|
+
depth = Math.max(0, depth - 1);
|
|
11201
|
+
continue;
|
|
11202
|
+
}
|
|
11203
|
+
if (ch === ";" && depth === 0) {
|
|
11204
|
+
statement += 1;
|
|
11205
|
+
continue;
|
|
11206
|
+
}
|
|
11207
|
+
if (!isIdentChar2(ch) || isIdentChar2(sql[i - 1])) {
|
|
11208
|
+
continue;
|
|
11209
|
+
}
|
|
11210
|
+
const rest = sql.slice(i);
|
|
11211
|
+
if (/^LIMIT\b/i.test(rest)) {
|
|
11212
|
+
hits.push({ kind: "limit", start: i, depth, statement });
|
|
11213
|
+
i += 4;
|
|
11214
|
+
continue;
|
|
11215
|
+
}
|
|
11216
|
+
if (/^OFFSET\b/i.test(rest)) {
|
|
11217
|
+
hits.push({ kind: "offset", start: i, depth, statement });
|
|
11218
|
+
i += 5;
|
|
11219
|
+
}
|
|
11220
|
+
}
|
|
11221
|
+
return hits;
|
|
11222
|
+
}
|
|
11223
|
+
function injectLimitBeforeBareOffsets(sql) {
|
|
11224
|
+
const hits = collectLimitOffsetKeywords(sql);
|
|
11225
|
+
if (hits.length === 0) return sql;
|
|
11226
|
+
const injectAt = [];
|
|
11227
|
+
for (const hit of hits) {
|
|
11228
|
+
if (hit.kind !== "offset") continue;
|
|
11229
|
+
let hasLimit = false;
|
|
11230
|
+
for (let i = hits.length - 1; i >= 0; i -= 1) {
|
|
11231
|
+
const prev = hits[i];
|
|
11232
|
+
if (prev.start >= hit.start) continue;
|
|
11233
|
+
if (prev.statement !== hit.statement) continue;
|
|
11234
|
+
if (prev.depth !== hit.depth) continue;
|
|
11235
|
+
if (prev.kind === "limit") {
|
|
11236
|
+
hasLimit = true;
|
|
11237
|
+
break;
|
|
11238
|
+
}
|
|
11239
|
+
if (prev.kind === "offset") {
|
|
11240
|
+
break;
|
|
11241
|
+
}
|
|
11242
|
+
}
|
|
11243
|
+
if (!hasLimit) {
|
|
11244
|
+
injectAt.push(hit.start);
|
|
11245
|
+
}
|
|
11246
|
+
}
|
|
11247
|
+
if (injectAt.length === 0) return sql;
|
|
11248
|
+
let out = sql;
|
|
11249
|
+
for (let i = injectAt.length - 1; i >= 0; i -= 1) {
|
|
11250
|
+
const at = injectAt[i];
|
|
11251
|
+
out = `${out.slice(0, at)}LIMIT -1 ${out.slice(at)}`;
|
|
11252
|
+
}
|
|
11253
|
+
return out;
|
|
11254
|
+
}
|
|
11255
|
+
function rewritePostgresSqlForSqlite(sql) {
|
|
11256
|
+
const castPattern = new RegExp(`::(?:${PG_CAST_TYPES})\\b`, "gi");
|
|
11257
|
+
const withoutCastsAndIlike = mapSqlOutsideLiterals(sql, (chunk) => {
|
|
11258
|
+
let rewritten = chunk.replace(castPattern, "");
|
|
11259
|
+
rewritten = rewritten.replace(/\bILIKE\b/gi, "LIKE");
|
|
11260
|
+
return rewritten;
|
|
11261
|
+
});
|
|
11262
|
+
return injectLimitBeforeBareOffsets(withoutCastsAndIlike);
|
|
11263
|
+
}
|
|
11264
|
+
|
|
11265
|
+
// src/cloudflare/d1/transport.ts
|
|
11266
|
+
function hasPaginationBounds2(payload) {
|
|
11267
|
+
return payload.limit !== void 0 || payload.offset !== void 0 || payload.page_size !== void 0 || payload.current_page !== void 0;
|
|
11268
|
+
}
|
|
11269
|
+
function normalizeSqliteCollation(name) {
|
|
11270
|
+
const raw = String(name ?? "").trim().replace(/^["'`[]|["'`\]]$/g, "");
|
|
11271
|
+
if (!raw) return "BINARY";
|
|
11272
|
+
return raw.toUpperCase();
|
|
11273
|
+
}
|
|
11274
|
+
function findCreateTableColumnListOpen(createSql) {
|
|
11275
|
+
const m = /\bCREATE\s+(?:(?:TEMP|TEMPORARY|VIRTUAL)\s+)?TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?/i.exec(
|
|
11276
|
+
createSql
|
|
11277
|
+
);
|
|
11278
|
+
if (!m) return -1;
|
|
11279
|
+
let i = m.index + m[0].length;
|
|
11280
|
+
let inSingle = false;
|
|
11281
|
+
let inDouble = false;
|
|
11282
|
+
let inBacktick = false;
|
|
11283
|
+
let inBracket = false;
|
|
11284
|
+
while (i < createSql.length) {
|
|
11285
|
+
const ch = createSql[i];
|
|
11286
|
+
if (inSingle) {
|
|
11287
|
+
if (ch === "'" && createSql[i + 1] === "'") {
|
|
11288
|
+
i += 2;
|
|
11289
|
+
continue;
|
|
11290
|
+
}
|
|
11291
|
+
if (ch === "'") inSingle = false;
|
|
11292
|
+
i++;
|
|
11293
|
+
continue;
|
|
11294
|
+
}
|
|
11295
|
+
if (inDouble) {
|
|
11296
|
+
if (ch === '"') inDouble = false;
|
|
11297
|
+
i++;
|
|
11298
|
+
continue;
|
|
11299
|
+
}
|
|
11300
|
+
if (inBacktick) {
|
|
11301
|
+
if (ch === "`") inBacktick = false;
|
|
11302
|
+
i++;
|
|
11303
|
+
continue;
|
|
11304
|
+
}
|
|
11305
|
+
if (inBracket) {
|
|
11306
|
+
if (ch === "]") inBracket = false;
|
|
11307
|
+
i++;
|
|
11308
|
+
continue;
|
|
11309
|
+
}
|
|
11310
|
+
if (ch === "'") {
|
|
11311
|
+
inSingle = true;
|
|
11312
|
+
i++;
|
|
11313
|
+
continue;
|
|
11314
|
+
}
|
|
11315
|
+
if (ch === '"') {
|
|
11316
|
+
inDouble = true;
|
|
11317
|
+
i++;
|
|
11318
|
+
continue;
|
|
11319
|
+
}
|
|
11320
|
+
if (ch === "`") {
|
|
11321
|
+
inBacktick = true;
|
|
11322
|
+
i++;
|
|
11323
|
+
continue;
|
|
11324
|
+
}
|
|
11325
|
+
if (ch === "[") {
|
|
11326
|
+
inBracket = true;
|
|
11327
|
+
i++;
|
|
11328
|
+
continue;
|
|
11329
|
+
}
|
|
11330
|
+
if (ch === "(") return i;
|
|
11331
|
+
i++;
|
|
11332
|
+
}
|
|
11333
|
+
return -1;
|
|
11334
|
+
}
|
|
11335
|
+
function parseColumnCollationFromCreateSql(createSql, columnName) {
|
|
11336
|
+
if (!createSql || !columnName) return null;
|
|
11337
|
+
const listOpen = findCreateTableColumnListOpen(createSql);
|
|
11338
|
+
if (listOpen < 0) return null;
|
|
11339
|
+
const listStart = listOpen + 1;
|
|
11340
|
+
const escaped = columnName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11341
|
+
const nameRe = new RegExp(
|
|
11342
|
+
`(?:^|[^\\w])(?:"${escaped}"|\`${escaped}\`|\\[${escaped}\\]|${escaped})\\s+`,
|
|
11343
|
+
"i"
|
|
11344
|
+
);
|
|
11345
|
+
const listSlice = createSql.slice(listStart);
|
|
11346
|
+
let nameMatchAbs = -1;
|
|
11347
|
+
let nameMatchLen = 0;
|
|
11348
|
+
{
|
|
11349
|
+
let i2 = 0;
|
|
11350
|
+
let depth2 = 1;
|
|
11351
|
+
let inSingle2 = false;
|
|
11352
|
+
let inDouble2 = false;
|
|
11353
|
+
let inBacktick2 = false;
|
|
11354
|
+
let inBracket2 = false;
|
|
11355
|
+
let segmentStart = 0;
|
|
11356
|
+
const trySegment = (from, to) => {
|
|
11357
|
+
if (nameMatchAbs >= 0) return;
|
|
11358
|
+
const segment = listSlice.slice(from, to);
|
|
11359
|
+
const m = nameRe.exec(segment);
|
|
11360
|
+
if (!m) return;
|
|
11361
|
+
const leading = segment.slice(0, m.index).trim();
|
|
11362
|
+
if (leading.length > 0) return;
|
|
11363
|
+
nameMatchAbs = listStart + from + m.index;
|
|
11364
|
+
nameMatchLen = m[0].length;
|
|
11365
|
+
};
|
|
11366
|
+
while (i2 < listSlice.length && depth2 > 0) {
|
|
11367
|
+
const ch = listSlice[i2];
|
|
11368
|
+
if (inSingle2) {
|
|
11369
|
+
if (ch === "'" && listSlice[i2 + 1] === "'") {
|
|
11370
|
+
i2 += 2;
|
|
11371
|
+
continue;
|
|
11372
|
+
}
|
|
11373
|
+
if (ch === "'") inSingle2 = false;
|
|
11374
|
+
i2++;
|
|
11375
|
+
continue;
|
|
11376
|
+
}
|
|
11377
|
+
if (inDouble2) {
|
|
11378
|
+
if (ch === '"') inDouble2 = false;
|
|
11379
|
+
i2++;
|
|
11380
|
+
continue;
|
|
11381
|
+
}
|
|
11382
|
+
if (inBacktick2) {
|
|
11383
|
+
if (ch === "`") inBacktick2 = false;
|
|
11384
|
+
i2++;
|
|
11385
|
+
continue;
|
|
11386
|
+
}
|
|
11387
|
+
if (inBracket2) {
|
|
11388
|
+
if (ch === "]") inBracket2 = false;
|
|
11389
|
+
i2++;
|
|
11390
|
+
continue;
|
|
11391
|
+
}
|
|
11392
|
+
if (ch === "'") {
|
|
11393
|
+
inSingle2 = true;
|
|
11394
|
+
i2++;
|
|
11395
|
+
continue;
|
|
11396
|
+
}
|
|
11397
|
+
if (ch === '"') {
|
|
11398
|
+
inDouble2 = true;
|
|
11399
|
+
i2++;
|
|
11400
|
+
continue;
|
|
11401
|
+
}
|
|
11402
|
+
if (ch === "`") {
|
|
11403
|
+
inBacktick2 = true;
|
|
11404
|
+
i2++;
|
|
11405
|
+
continue;
|
|
11406
|
+
}
|
|
11407
|
+
if (ch === "[") {
|
|
11408
|
+
inBracket2 = true;
|
|
11409
|
+
i2++;
|
|
11410
|
+
continue;
|
|
11411
|
+
}
|
|
11412
|
+
if (ch === "(") {
|
|
11413
|
+
depth2++;
|
|
11414
|
+
i2++;
|
|
11415
|
+
continue;
|
|
11416
|
+
}
|
|
11417
|
+
if (ch === ")") {
|
|
11418
|
+
depth2--;
|
|
11419
|
+
if (depth2 === 0) {
|
|
11420
|
+
trySegment(segmentStart, i2);
|
|
11421
|
+
break;
|
|
11422
|
+
}
|
|
11423
|
+
i2++;
|
|
11424
|
+
continue;
|
|
11425
|
+
}
|
|
11426
|
+
if (ch === "," && depth2 === 1) {
|
|
11427
|
+
trySegment(segmentStart, i2);
|
|
11428
|
+
segmentStart = i2 + 1;
|
|
11429
|
+
i2++;
|
|
11430
|
+
continue;
|
|
11431
|
+
}
|
|
11432
|
+
i2++;
|
|
11433
|
+
}
|
|
11434
|
+
}
|
|
11435
|
+
if (nameMatchAbs < 0 || nameMatchLen <= 0) return null;
|
|
11436
|
+
const start = nameMatchAbs + nameMatchLen;
|
|
11437
|
+
let i = start;
|
|
11438
|
+
let depth = 0;
|
|
11439
|
+
let inSingle = false;
|
|
11440
|
+
let inDouble = false;
|
|
11441
|
+
let inBacktick = false;
|
|
11442
|
+
let inBracket = false;
|
|
11443
|
+
while (i < createSql.length) {
|
|
11444
|
+
const ch = createSql[i];
|
|
11445
|
+
if (inSingle) {
|
|
11446
|
+
if (ch === "'" && createSql[i + 1] === "'") {
|
|
11447
|
+
i += 2;
|
|
11448
|
+
continue;
|
|
11449
|
+
}
|
|
11450
|
+
if (ch === "'") inSingle = false;
|
|
11451
|
+
i++;
|
|
11452
|
+
continue;
|
|
11453
|
+
}
|
|
11454
|
+
if (inDouble) {
|
|
11455
|
+
if (ch === '"') inDouble = false;
|
|
11456
|
+
i++;
|
|
11457
|
+
continue;
|
|
11458
|
+
}
|
|
11459
|
+
if (inBacktick) {
|
|
11460
|
+
if (ch === "`") inBacktick = false;
|
|
11461
|
+
i++;
|
|
11462
|
+
continue;
|
|
11463
|
+
}
|
|
11464
|
+
if (inBracket) {
|
|
11465
|
+
if (ch === "]") inBracket = false;
|
|
11466
|
+
i++;
|
|
11467
|
+
continue;
|
|
11468
|
+
}
|
|
11469
|
+
if (ch === "'") {
|
|
11470
|
+
inSingle = true;
|
|
11471
|
+
i++;
|
|
11472
|
+
continue;
|
|
11473
|
+
}
|
|
11474
|
+
if (ch === '"') {
|
|
11475
|
+
inDouble = true;
|
|
11476
|
+
i++;
|
|
11477
|
+
continue;
|
|
11478
|
+
}
|
|
11479
|
+
if (ch === "`") {
|
|
11480
|
+
inBacktick = true;
|
|
11481
|
+
i++;
|
|
11482
|
+
continue;
|
|
11483
|
+
}
|
|
11484
|
+
if (ch === "[") {
|
|
11485
|
+
inBracket = true;
|
|
11486
|
+
i++;
|
|
11487
|
+
continue;
|
|
11488
|
+
}
|
|
11489
|
+
if (ch === "(") {
|
|
11490
|
+
depth++;
|
|
11491
|
+
i++;
|
|
11492
|
+
continue;
|
|
11493
|
+
}
|
|
11494
|
+
if (ch === ")") {
|
|
11495
|
+
if (depth === 0) {
|
|
11496
|
+
break;
|
|
11497
|
+
}
|
|
11498
|
+
depth--;
|
|
11499
|
+
i++;
|
|
11500
|
+
continue;
|
|
11501
|
+
}
|
|
11502
|
+
if (ch === "," && depth === 0) {
|
|
11503
|
+
break;
|
|
11504
|
+
}
|
|
11505
|
+
i++;
|
|
11506
|
+
}
|
|
11507
|
+
const colDef = createSql.slice(start, i);
|
|
11508
|
+
const collRe = /\bCOLLATE\s+("([^"]+)"|'([^']+)'|`([^`]+)`|\[([^\]]+)\]|([A-Za-z_][\w$]*))/i;
|
|
11509
|
+
const match = collRe.exec(colDef);
|
|
11510
|
+
if (!match) return null;
|
|
11511
|
+
return match[2] ?? match[3] ?? match[4] ?? match[5] ?? match[6] ?? match[1] ?? null;
|
|
11512
|
+
}
|
|
11513
|
+
async function indexCollationMatchesColumn(d1, table, tableSafe, indexSafe, columnName, session) {
|
|
11514
|
+
const xinfo = await executeD1Query(d1, {
|
|
11515
|
+
query: `PRAGMA index_xinfo("${indexSafe}")`,
|
|
11516
|
+
params: [],
|
|
11517
|
+
...session
|
|
11518
|
+
});
|
|
11519
|
+
if (!xinfo.ok) {
|
|
11520
|
+
return false;
|
|
11521
|
+
}
|
|
11522
|
+
const xRows = xinfo.rows ?? [];
|
|
11523
|
+
const keyRows = xRows.filter((row) => {
|
|
11524
|
+
const isKey = row.key === 1 || row.key === true || Number(row.key) === 1;
|
|
11525
|
+
if (!isKey) return false;
|
|
11526
|
+
return typeof row.name === "string" && row.name === columnName;
|
|
11527
|
+
});
|
|
11528
|
+
if (keyRows.length === 0) {
|
|
11529
|
+
const byName = xRows.find(
|
|
11530
|
+
(row) => typeof row.name === "string" && row.name === columnName
|
|
11531
|
+
);
|
|
11532
|
+
if (!byName) return false;
|
|
11533
|
+
keyRows.push(byName);
|
|
11534
|
+
}
|
|
11535
|
+
const indexColl = normalizeSqliteCollation(keyRows[0]?.coll);
|
|
11536
|
+
const master = await executeD1Query(d1, {
|
|
11537
|
+
query: `SELECT sql AS create_sql FROM sqlite_master WHERE type = 'table' AND name = ?`,
|
|
11538
|
+
params: [table],
|
|
11539
|
+
...session
|
|
11540
|
+
});
|
|
11541
|
+
let createSql = "";
|
|
11542
|
+
if (master.ok && Array.isArray(master.rows) && master.rows.length > 0) {
|
|
11543
|
+
createSql = String(
|
|
11544
|
+
master.rows[0]?.create_sql ?? ""
|
|
11545
|
+
);
|
|
11546
|
+
}
|
|
11547
|
+
const columnCollRaw = parseColumnCollationFromCreateSql(createSql, columnName);
|
|
11548
|
+
const columnColl = normalizeSqliteCollation(columnCollRaw);
|
|
11549
|
+
return indexColl === columnColl;
|
|
11550
|
+
}
|
|
11551
|
+
async function resolveD1BoundedIdentityColumn(d1, tableName, session) {
|
|
11552
|
+
const table = tableName.trim();
|
|
11553
|
+
if (!table) {
|
|
11554
|
+
throw new D1SqlCompileError("missing_table", "table_name is required for bounded mutation");
|
|
11555
|
+
}
|
|
11556
|
+
const safe = table.replace(/"/g, '""');
|
|
11557
|
+
const info = await executeD1Query(d1, {
|
|
11558
|
+
query: `PRAGMA table_info("${safe}")`,
|
|
11559
|
+
params: [],
|
|
11560
|
+
...session
|
|
11561
|
+
});
|
|
11562
|
+
if (!info.ok) {
|
|
11563
|
+
throw new D1SqlCompileError(
|
|
11564
|
+
"bounded_mutation_no_unique_identity",
|
|
11565
|
+
`Unable to introspect table "${table}" for bounded mutation identity: ${info.message}`
|
|
11566
|
+
);
|
|
11567
|
+
}
|
|
11568
|
+
const cols = info.rows.filter(
|
|
11569
|
+
(row) => typeof row?.name === "string" && row.name.length > 0
|
|
11570
|
+
);
|
|
11571
|
+
const colByName = new Map(cols.map((col) => [col.name, col]));
|
|
11572
|
+
const isNotNullColumn = (name) => {
|
|
11573
|
+
const col = colByName.get(name);
|
|
11574
|
+
if (!col) return false;
|
|
11575
|
+
return col.notnull === 1 || col.notnull === true || Number(col.notnull) === 1;
|
|
11576
|
+
};
|
|
11577
|
+
const isIntegerPrimaryKeyType = (col) => {
|
|
11578
|
+
if (typeof col.pk !== "number" || col.pk <= 0) return false;
|
|
11579
|
+
const declared = String(col.type ?? "").trim().toUpperCase();
|
|
11580
|
+
return declared === "INTEGER";
|
|
11581
|
+
};
|
|
11582
|
+
const loadIndexList = async () => {
|
|
11583
|
+
const indexList = await executeD1Query(d1, {
|
|
11584
|
+
query: `PRAGMA index_list("${safe}")`,
|
|
11585
|
+
params: [],
|
|
11586
|
+
...session
|
|
11587
|
+
});
|
|
11588
|
+
if (!indexList.ok) return null;
|
|
11589
|
+
return indexList.rows;
|
|
11590
|
+
};
|
|
11591
|
+
const isDescendingIntegerPrimaryKeyException = async (colName, indexes) => {
|
|
11592
|
+
if (indexes) {
|
|
11593
|
+
const hasPkOrigin = indexes.some(
|
|
11594
|
+
(index) => String(index.origin ?? "").toLowerCase() === "pk"
|
|
11595
|
+
);
|
|
11596
|
+
if (hasPkOrigin) return true;
|
|
11597
|
+
const originReported = indexes.some(
|
|
11598
|
+
(index) => index.origin !== void 0 && index.origin !== null && String(index.origin).length > 0
|
|
11599
|
+
);
|
|
11600
|
+
if (indexes.length === 0 || originReported) {
|
|
11601
|
+
return false;
|
|
11602
|
+
}
|
|
11603
|
+
}
|
|
11604
|
+
const master = await executeD1Query(d1, {
|
|
11605
|
+
query: `SELECT sql AS create_sql FROM sqlite_master WHERE type = 'table' AND name = ?`,
|
|
11606
|
+
params: [table],
|
|
11607
|
+
...session
|
|
11608
|
+
});
|
|
11609
|
+
if (!master.ok || !Array.isArray(master.rows) || master.rows.length === 0) {
|
|
11610
|
+
return false;
|
|
11611
|
+
}
|
|
11612
|
+
const createSql = String(
|
|
11613
|
+
master.rows[0]?.create_sql ?? ""
|
|
11614
|
+
);
|
|
11615
|
+
if (!createSql) return false;
|
|
11616
|
+
const escaped = colName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11617
|
+
const inlineDesc = new RegExp(
|
|
11618
|
+
`(?:^|[^\\w])(?:"${escaped}"|${escaped})\\s+INTEGER\\s+PRIMARY\\s+KEY\\s+DESC\\b`,
|
|
11619
|
+
"i"
|
|
11620
|
+
);
|
|
11621
|
+
const tableDesc = new RegExp(
|
|
11622
|
+
`PRIMARY\\s+KEY\\s*\\(\\s*(?:"${escaped}"|${escaped})\\s+DESC\\b`,
|
|
11623
|
+
"i"
|
|
11624
|
+
);
|
|
11625
|
+
return inlineDesc.test(createSql) || tableDesc.test(createSql);
|
|
11626
|
+
};
|
|
11627
|
+
const pkCols = cols.filter((row) => typeof row.pk === "number" && row.pk > 0).sort((a, b) => (a.pk ?? 0) - (b.pk ?? 0));
|
|
11628
|
+
let indexRows;
|
|
11629
|
+
if (pkCols.length === 1 && pkCols[0]?.name) {
|
|
11630
|
+
if (isIntegerPrimaryKeyType(pkCols[0])) {
|
|
11631
|
+
indexRows = await loadIndexList();
|
|
11632
|
+
const isDescException = await isDescendingIntegerPrimaryKeyException(
|
|
11633
|
+
pkCols[0].name,
|
|
11634
|
+
indexRows
|
|
11635
|
+
);
|
|
11636
|
+
if (!isDescException) {
|
|
11637
|
+
return pkCols[0].name;
|
|
11638
|
+
}
|
|
11639
|
+
} else if (isNotNullColumn(pkCols[0].name)) {
|
|
11640
|
+
indexRows = await loadIndexList();
|
|
11641
|
+
const pkIndex = (indexRows ?? []).find(
|
|
11642
|
+
(index) => String(index.origin ?? "").toLowerCase() === "pk"
|
|
11643
|
+
);
|
|
11644
|
+
if (pkIndex && typeof pkIndex.name === "string" && pkIndex.name) {
|
|
11645
|
+
const indexSafe = pkIndex.name.replace(/"/g, '""');
|
|
11646
|
+
const collMatch = await indexCollationMatchesColumn(
|
|
11647
|
+
d1,
|
|
11648
|
+
table,
|
|
11649
|
+
safe,
|
|
11650
|
+
indexSafe,
|
|
11651
|
+
pkCols[0].name,
|
|
11652
|
+
session
|
|
11653
|
+
);
|
|
11654
|
+
if (collMatch) {
|
|
11655
|
+
return pkCols[0].name;
|
|
11656
|
+
}
|
|
11657
|
+
}
|
|
11658
|
+
}
|
|
11659
|
+
}
|
|
11660
|
+
if (indexRows === void 0) {
|
|
11661
|
+
indexRows = await loadIndexList();
|
|
11662
|
+
}
|
|
11663
|
+
if (indexRows) {
|
|
11664
|
+
for (const index of indexRows) {
|
|
11665
|
+
const unique = index.unique === 1 || index.unique === true || Number(index.unique) === 1;
|
|
11666
|
+
if (!unique || typeof index.name !== "string" || !index.name) continue;
|
|
11667
|
+
const isPartial = index.partial === 1 || index.partial === true || Number(index.partial) === 1;
|
|
11668
|
+
if (isPartial) {
|
|
11669
|
+
continue;
|
|
11670
|
+
}
|
|
11671
|
+
const indexSafe = index.name.replace(/"/g, '""');
|
|
11672
|
+
const indexInfo = await executeD1Query(d1, {
|
|
11673
|
+
query: `PRAGMA index_info("${indexSafe}")`,
|
|
11674
|
+
params: [],
|
|
11675
|
+
...session
|
|
11676
|
+
});
|
|
11677
|
+
if (!indexInfo.ok) continue;
|
|
11678
|
+
const rawIndexCols = indexInfo.rows ?? [];
|
|
11679
|
+
const hasExpressionTerm = rawIndexCols.some((row) => {
|
|
11680
|
+
const nameOk = typeof row?.name === "string" && row.name.length > 0;
|
|
11681
|
+
if (!nameOk) return true;
|
|
11682
|
+
if (typeof row.cid === "number" && row.cid < 0) return true;
|
|
11683
|
+
return false;
|
|
11684
|
+
});
|
|
11685
|
+
if (hasExpressionTerm || rawIndexCols.length === 0) {
|
|
11686
|
+
continue;
|
|
11687
|
+
}
|
|
11688
|
+
const indexCols = rawIndexCols.filter(
|
|
11689
|
+
(row) => typeof row?.name === "string" && row.name.length > 0
|
|
11690
|
+
);
|
|
11691
|
+
if (indexCols.length === 1 && indexCols[0]?.name) {
|
|
11692
|
+
const identity = indexCols[0].name;
|
|
11693
|
+
if (!isNotNullColumn(identity)) {
|
|
11694
|
+
continue;
|
|
11695
|
+
}
|
|
11696
|
+
const collMatch = await indexCollationMatchesColumn(
|
|
11697
|
+
d1,
|
|
11698
|
+
table,
|
|
11699
|
+
safe,
|
|
11700
|
+
indexSafe,
|
|
11701
|
+
identity,
|
|
11702
|
+
session
|
|
11703
|
+
);
|
|
11704
|
+
if (!collMatch) {
|
|
11705
|
+
continue;
|
|
11706
|
+
}
|
|
11707
|
+
return identity;
|
|
11708
|
+
}
|
|
11709
|
+
}
|
|
11710
|
+
}
|
|
11711
|
+
throw new D1SqlCompileError(
|
|
11712
|
+
"bounded_mutation_no_unique_identity",
|
|
11713
|
+
`Bounded update/delete on "${table}" requires a proven single-column PRIMARY KEY or UNIQUE index`
|
|
11714
|
+
);
|
|
11715
|
+
}
|
|
11716
|
+
async function compileOptionsForBoundedMutation(d1, payload, session) {
|
|
11717
|
+
if (!hasPaginationBounds2(payload) && !payload.sort_by) {
|
|
11718
|
+
return void 0;
|
|
11719
|
+
}
|
|
11720
|
+
if (!hasPaginationBounds2(payload)) {
|
|
11721
|
+
return void 0;
|
|
11722
|
+
}
|
|
11723
|
+
const identityColumn = await resolveD1BoundedIdentityColumn(
|
|
11724
|
+
d1,
|
|
11725
|
+
payload.table_name ?? "",
|
|
11726
|
+
session
|
|
11727
|
+
);
|
|
11728
|
+
return { identityColumn };
|
|
11729
|
+
}
|
|
11730
|
+
function headerValue2(options, name) {
|
|
11731
|
+
const headers = options?.headers;
|
|
11732
|
+
if (!headers) return void 0;
|
|
11733
|
+
const lower = name.toLowerCase();
|
|
11734
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
11735
|
+
if (key.toLowerCase() === lower && value?.trim()) {
|
|
11736
|
+
return value.trim();
|
|
11737
|
+
}
|
|
11738
|
+
}
|
|
11739
|
+
return void 0;
|
|
11740
|
+
}
|
|
11741
|
+
function sessionFromOptions(options, defaults) {
|
|
11742
|
+
return {
|
|
11743
|
+
bookmark: headerValue2(options, "x-athena-d1-bookmark") ?? null,
|
|
11744
|
+
sessionMode: headerValue2(options, "x-athena-d1-session-mode") ?? defaults.defaultSessionMode ?? null
|
|
11745
|
+
};
|
|
11746
|
+
}
|
|
11747
|
+
function resolveStripNulls(payloadStrip, callOptions, defaultValue = true) {
|
|
11748
|
+
if (typeof payloadStrip === "boolean") return payloadStrip;
|
|
11749
|
+
if (typeof callOptions?.stripNulls === "boolean") return callOptions.stripNulls;
|
|
11750
|
+
return defaultValue;
|
|
11751
|
+
}
|
|
11752
|
+
function stripNullPropertiesFromRows(rows) {
|
|
11753
|
+
return rows.map((row) => {
|
|
11754
|
+
if (row == null || typeof row !== "object" || Array.isArray(row)) {
|
|
11755
|
+
return row;
|
|
11756
|
+
}
|
|
11757
|
+
const out = {};
|
|
11758
|
+
for (const [key, value] of Object.entries(row)) {
|
|
11759
|
+
if (value !== null) {
|
|
11760
|
+
out[key] = value;
|
|
11761
|
+
}
|
|
11762
|
+
}
|
|
11763
|
+
return out;
|
|
11764
|
+
});
|
|
11765
|
+
}
|
|
11766
|
+
function maybeStripNullRows(rows, strip) {
|
|
11767
|
+
return strip ? stripNullPropertiesFromRows(rows) : rows;
|
|
11768
|
+
}
|
|
11769
|
+
function successResponse(data, count, raw, bookmark) {
|
|
11770
|
+
const response = {
|
|
11771
|
+
ok: true,
|
|
11772
|
+
status: 200,
|
|
11773
|
+
statusText: "OK",
|
|
11774
|
+
data,
|
|
11775
|
+
count: count ?? null,
|
|
11776
|
+
error: void 0,
|
|
11777
|
+
errorDetails: null,
|
|
11778
|
+
raw
|
|
11779
|
+
};
|
|
11780
|
+
if (bookmark) {
|
|
11781
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
11782
|
+
const rawRecord = raw;
|
|
11783
|
+
rawRecord.bookmark = bookmark;
|
|
11784
|
+
}
|
|
11785
|
+
}
|
|
11786
|
+
return response;
|
|
11787
|
+
}
|
|
11788
|
+
function errorResponse(status, code, message, endpoint, method, hint) {
|
|
11789
|
+
const error = new AthenaGatewayError({
|
|
11790
|
+
code,
|
|
11791
|
+
message,
|
|
11792
|
+
status,
|
|
11793
|
+
endpoint,
|
|
11794
|
+
method,
|
|
11795
|
+
hint
|
|
11796
|
+
});
|
|
11797
|
+
return {
|
|
11798
|
+
ok: false,
|
|
11799
|
+
status,
|
|
11800
|
+
statusText: null,
|
|
11801
|
+
data: null,
|
|
11802
|
+
error: error.message,
|
|
11803
|
+
errorDetails: error.toDetails(),
|
|
11804
|
+
raw: { error: message, code }
|
|
11805
|
+
};
|
|
11806
|
+
}
|
|
11807
|
+
function compileErrorResponse(error, endpoint, method) {
|
|
11808
|
+
if (error instanceof D1SqlCompileError) {
|
|
11809
|
+
return errorResponse(
|
|
11810
|
+
400,
|
|
11811
|
+
"HTTP_ERROR",
|
|
11812
|
+
error.message,
|
|
11813
|
+
endpoint,
|
|
11814
|
+
method,
|
|
11815
|
+
`D1 SQL compile failed (${error.code})`
|
|
11816
|
+
);
|
|
11817
|
+
}
|
|
11818
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11819
|
+
return errorResponse(500, "UNKNOWN_ERROR", message, endpoint, method);
|
|
11820
|
+
}
|
|
11821
|
+
function createCloudflareD1GatewayTransport(options) {
|
|
11822
|
+
const { d1 } = options;
|
|
11823
|
+
async function runCompiled(compiled, callOptions, endpoint, method, mapResult, stripNulls = true) {
|
|
11824
|
+
try {
|
|
11825
|
+
const session = sessionFromOptions(callOptions, options);
|
|
11826
|
+
if (compiled.statements && compiled.statements.length > 1) {
|
|
11827
|
+
const batch = await executeD1Batch(d1, {
|
|
11828
|
+
statements: compiled.statements.map((statement) => ({
|
|
11829
|
+
query: statement.sql,
|
|
11830
|
+
params: statement.params
|
|
11831
|
+
})),
|
|
11832
|
+
...session
|
|
11833
|
+
});
|
|
11834
|
+
if (!batch.ok) {
|
|
11835
|
+
return errorResponse(400, "HTTP_ERROR", batch.message, endpoint, method);
|
|
11836
|
+
}
|
|
11837
|
+
const rows2 = maybeStripNullRows(
|
|
11838
|
+
batch.results.flatMap(
|
|
11839
|
+
(item) => Array.isArray(item.results) ? item.results : []
|
|
11840
|
+
),
|
|
11841
|
+
stripNulls
|
|
11842
|
+
);
|
|
11843
|
+
const changes = batch.results.reduce((sum2, item) => {
|
|
11844
|
+
const meta = item?.meta;
|
|
11845
|
+
const n = meta && typeof meta === "object" && typeof meta.changes === "number" ? meta.changes : 0;
|
|
11846
|
+
return sum2 + n;
|
|
11847
|
+
}, 0);
|
|
11848
|
+
const count = rows2.length > 0 ? rows2.length : changes;
|
|
11849
|
+
const mapped2 = mapResult?.(rows2, count) ?? {
|
|
11850
|
+
data: rows2,
|
|
11851
|
+
count
|
|
11852
|
+
};
|
|
11853
|
+
return successResponse(mapped2.data, mapped2.count, batch, batch.bookmark);
|
|
11854
|
+
}
|
|
11855
|
+
const result = await executeD1Query(d1, {
|
|
11856
|
+
query: compiled.sql,
|
|
11857
|
+
params: compiled.params,
|
|
11858
|
+
...session
|
|
11859
|
+
});
|
|
11860
|
+
if (!result.ok) {
|
|
11861
|
+
return errorResponse(400, "HTTP_ERROR", result.message, endpoint, method);
|
|
11862
|
+
}
|
|
11863
|
+
const rows = maybeStripNullRows(result.rows, stripNulls);
|
|
11864
|
+
const mapped = mapResult?.(rows, result.count) ?? {
|
|
11865
|
+
data: rows,
|
|
11866
|
+
count: result.count
|
|
11867
|
+
};
|
|
11868
|
+
return successResponse(mapped.data, mapped.count, result, result.bookmark);
|
|
11869
|
+
} catch (error) {
|
|
11870
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11871
|
+
return errorResponse(500, "UNKNOWN_ERROR", message, endpoint, method);
|
|
11872
|
+
}
|
|
11873
|
+
}
|
|
11874
|
+
return {
|
|
11875
|
+
baseUrl: CLOUDFLARE_EDGE_BASE_URL,
|
|
11876
|
+
buildHeaders(callOptions) {
|
|
11877
|
+
return { ...callOptions?.headers ?? {} };
|
|
11878
|
+
},
|
|
11879
|
+
async resolveCallOptions(callOptions) {
|
|
11880
|
+
return callOptions;
|
|
11881
|
+
},
|
|
11882
|
+
async verifyConnection(connectionOptions) {
|
|
11883
|
+
const baseUrl = CLOUDFLARE_EDGE_BASE_URL;
|
|
11884
|
+
try {
|
|
11885
|
+
const result = await executeD1Query(d1, { query: "SELECT 1 AS ok" });
|
|
11886
|
+
if (!result.ok) {
|
|
11887
|
+
return {
|
|
11888
|
+
ok: false,
|
|
11889
|
+
reachable: false,
|
|
11890
|
+
status: 500,
|
|
11891
|
+
statusText: null,
|
|
11892
|
+
baseUrl,
|
|
11893
|
+
url: `${baseUrl}/d1`,
|
|
11894
|
+
error: result.message,
|
|
11895
|
+
errorDetails: null,
|
|
11896
|
+
raw: result
|
|
11897
|
+
};
|
|
11898
|
+
}
|
|
11899
|
+
return {
|
|
11900
|
+
ok: true,
|
|
11901
|
+
reachable: true,
|
|
11902
|
+
status: 200,
|
|
11903
|
+
statusText: "OK",
|
|
11904
|
+
baseUrl,
|
|
11905
|
+
url: `${baseUrl}/d1`,
|
|
11906
|
+
error: void 0,
|
|
11907
|
+
errorDetails: null,
|
|
11908
|
+
raw: result
|
|
11909
|
+
};
|
|
11910
|
+
} catch (error) {
|
|
11911
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11912
|
+
return {
|
|
11913
|
+
ok: false,
|
|
11914
|
+
reachable: false,
|
|
11915
|
+
status: 0,
|
|
11916
|
+
statusText: null,
|
|
11917
|
+
baseUrl,
|
|
11918
|
+
url: `${baseUrl}/d1`,
|
|
11919
|
+
error: message,
|
|
11920
|
+
errorDetails: null,
|
|
11921
|
+
raw: null
|
|
11922
|
+
};
|
|
11923
|
+
}
|
|
11924
|
+
},
|
|
11925
|
+
async fetchGateway(payload, callOptions) {
|
|
11926
|
+
try {
|
|
11927
|
+
if (payload && typeof payload === "object" && "operation" in payload) {
|
|
11928
|
+
return errorResponse(
|
|
11929
|
+
400,
|
|
11930
|
+
"HTTP_ERROR",
|
|
11931
|
+
"Direct AST operation payloads are unsupported on D1 edge-local",
|
|
11932
|
+
"/gateway/fetch",
|
|
11933
|
+
"POST"
|
|
11934
|
+
);
|
|
11935
|
+
}
|
|
11936
|
+
const compiled = compileD1Fetch(payload);
|
|
11937
|
+
const stripNulls = resolveStripNulls(payload.strip_nulls, callOptions, true);
|
|
11938
|
+
if (payload.head === true) {
|
|
11939
|
+
return runCompiled(
|
|
11940
|
+
compiled,
|
|
11941
|
+
callOptions,
|
|
11942
|
+
"/gateway/fetch",
|
|
11943
|
+
"POST",
|
|
11944
|
+
(rows) => ({ data: [], count: extractAthenaCount(rows) }),
|
|
11945
|
+
// COUNT rows are meta, not user-facing object rows
|
|
11946
|
+
false
|
|
11947
|
+
);
|
|
11948
|
+
}
|
|
11949
|
+
const wantsTotalCount = payload.count === "exact" || payload.count === "planned" || payload.count === "estimated";
|
|
11950
|
+
if (wantsTotalCount) {
|
|
11951
|
+
const session = sessionFromOptions(callOptions, options);
|
|
11952
|
+
const countCompiled = compileD1Count(payload);
|
|
11953
|
+
const batch = await executeD1Batch(d1, {
|
|
11954
|
+
statements: [
|
|
11955
|
+
{
|
|
11956
|
+
query: compiled.sql,
|
|
11957
|
+
params: compiled.params
|
|
11958
|
+
},
|
|
11959
|
+
{
|
|
11960
|
+
query: countCompiled.sql,
|
|
11961
|
+
params: countCompiled.params
|
|
11962
|
+
}
|
|
11963
|
+
],
|
|
11964
|
+
...session
|
|
11965
|
+
});
|
|
11966
|
+
if (!batch.ok) {
|
|
11967
|
+
return errorResponse(400, "HTTP_ERROR", batch.message, "/gateway/fetch", "POST");
|
|
11968
|
+
}
|
|
11969
|
+
const dataItem = batch.results[0];
|
|
11970
|
+
const countItem = batch.results[1];
|
|
11971
|
+
if (!dataItem || dataItem.success === false) {
|
|
11972
|
+
const message = dataItem && typeof dataItem.error === "string" && dataItem.error.trim() ? dataItem.error : "D1 page query failed";
|
|
11973
|
+
return errorResponse(400, "HTTP_ERROR", message, "/gateway/fetch", "POST");
|
|
11974
|
+
}
|
|
11975
|
+
if (!countItem || countItem.success === false) {
|
|
11976
|
+
const message = countItem && typeof countItem.error === "string" && countItem.error.trim() ? countItem.error : "D1 count query failed";
|
|
11977
|
+
return errorResponse(400, "HTTP_ERROR", message, "/gateway/fetch", "POST");
|
|
11978
|
+
}
|
|
11979
|
+
const dataRows = Array.isArray(dataItem.results) ? dataItem.results : [];
|
|
11980
|
+
const countRows = Array.isArray(countItem.results) ? countItem.results : [];
|
|
11981
|
+
const total = extractAthenaCount(countRows);
|
|
11982
|
+
const rows = maybeStripNullRows(dataRows, stripNulls);
|
|
11983
|
+
return successResponse(
|
|
11984
|
+
rows,
|
|
11985
|
+
total,
|
|
11986
|
+
{ data: dataItem, count: countItem, batch },
|
|
11987
|
+
batch.bookmark
|
|
11988
|
+
);
|
|
11989
|
+
}
|
|
11990
|
+
return runCompiled(compiled, callOptions, "/gateway/fetch", "POST", void 0, stripNulls);
|
|
11991
|
+
} catch (error) {
|
|
11992
|
+
return compileErrorResponse(error, "/gateway/fetch", "POST");
|
|
11993
|
+
}
|
|
9599
11994
|
},
|
|
9600
|
-
|
|
9601
|
-
|
|
11995
|
+
async insertGateway(payload, callOptions) {
|
|
11996
|
+
try {
|
|
11997
|
+
const compiled = compileD1Insert(payload);
|
|
11998
|
+
const stripNulls = resolveStripNulls(void 0, callOptions, true);
|
|
11999
|
+
return runCompiled(compiled, callOptions, "/gateway/insert", "PUT", void 0, stripNulls);
|
|
12000
|
+
} catch (error) {
|
|
12001
|
+
return compileErrorResponse(error, "/gateway/insert", "PUT");
|
|
12002
|
+
}
|
|
9602
12003
|
},
|
|
9603
|
-
|
|
9604
|
-
|
|
12004
|
+
async updateGateway(payload, callOptions) {
|
|
12005
|
+
try {
|
|
12006
|
+
const session = sessionFromOptions(callOptions, options);
|
|
12007
|
+
const compileOptions = await compileOptionsForBoundedMutation(d1, payload, session);
|
|
12008
|
+
const compiled = compileD1Update(payload, compileOptions);
|
|
12009
|
+
const stripNulls = resolveStripNulls(payload.strip_nulls, callOptions, true);
|
|
12010
|
+
return runCompiled(compiled, callOptions, "/gateway/update", "POST", void 0, stripNulls);
|
|
12011
|
+
} catch (error) {
|
|
12012
|
+
return compileErrorResponse(error, "/gateway/update", "POST");
|
|
12013
|
+
}
|
|
9605
12014
|
},
|
|
9606
|
-
|
|
9607
|
-
|
|
12015
|
+
async deleteGateway(payload, callOptions) {
|
|
12016
|
+
try {
|
|
12017
|
+
const session = sessionFromOptions(callOptions, options);
|
|
12018
|
+
const compileOptions = await compileOptionsForBoundedMutation(d1, payload, session);
|
|
12019
|
+
const compiled = compileD1Delete(payload, compileOptions);
|
|
12020
|
+
const stripNulls = resolveStripNulls(void 0, callOptions, true);
|
|
12021
|
+
return runCompiled(compiled, callOptions, "/gateway/delete", "DELETE", void 0, stripNulls);
|
|
12022
|
+
} catch (error) {
|
|
12023
|
+
return compileErrorResponse(error, "/gateway/delete", "DELETE");
|
|
12024
|
+
}
|
|
9608
12025
|
},
|
|
9609
|
-
|
|
9610
|
-
return
|
|
12026
|
+
async rpcGateway(payload, callOptions) {
|
|
12027
|
+
return errorResponse(
|
|
12028
|
+
400,
|
|
12029
|
+
"HTTP_ERROR",
|
|
12030
|
+
"RPC is unsupported on D1 edge-local mode",
|
|
12031
|
+
"/gateway/rpc",
|
|
12032
|
+
"POST",
|
|
12033
|
+
"Use flat CRUD or raw query SQL instead"
|
|
12034
|
+
);
|
|
9611
12035
|
},
|
|
9612
|
-
|
|
9613
|
-
|
|
12036
|
+
async queryGateway(payload, callOptions) {
|
|
12037
|
+
try {
|
|
12038
|
+
const params = payload.params ?? callOptions?.params;
|
|
12039
|
+
const session = sessionFromOptions(callOptions, options);
|
|
12040
|
+
const query = rewritePostgresSqlForSqlite(payload.query);
|
|
12041
|
+
const result = await executeD1Query(d1, {
|
|
12042
|
+
query,
|
|
12043
|
+
params: Array.isArray(params) ? params : [],
|
|
12044
|
+
...session
|
|
12045
|
+
});
|
|
12046
|
+
if (!result.ok) {
|
|
12047
|
+
return errorResponse(400, "HTTP_ERROR", result.message, "/gateway/query", "POST");
|
|
12048
|
+
}
|
|
12049
|
+
const stripNulls = resolveStripNulls(void 0, callOptions, true);
|
|
12050
|
+
const rows = maybeStripNullRows(result.rows, stripNulls);
|
|
12051
|
+
return successResponse(
|
|
12052
|
+
rows,
|
|
12053
|
+
result.count,
|
|
12054
|
+
{ ...result, query },
|
|
12055
|
+
result.bookmark
|
|
12056
|
+
);
|
|
12057
|
+
} catch (error) {
|
|
12058
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
12059
|
+
return errorResponse(500, "UNKNOWN_ERROR", message, "/gateway/query", "POST");
|
|
12060
|
+
}
|
|
12061
|
+
}
|
|
9614
12062
|
};
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
12063
|
+
}
|
|
12064
|
+
|
|
12065
|
+
// src/config/errors.ts
|
|
12066
|
+
var AthenaConfigurationError = class extends Error {
|
|
12067
|
+
code;
|
|
12068
|
+
service;
|
|
12069
|
+
constructor(code, message, service) {
|
|
12070
|
+
super(message);
|
|
12071
|
+
this.name = "AthenaConfigurationError";
|
|
12072
|
+
this.code = code;
|
|
12073
|
+
this.service = service;
|
|
12074
|
+
}
|
|
12075
|
+
};
|
|
12076
|
+
|
|
12077
|
+
// src/cloudflare/execution-mode.ts
|
|
12078
|
+
var ATHENA_EXECUTION_MODE_ENV_KEY = "ATHENA_EXECUTION_MODE";
|
|
12079
|
+
var ATHENA_EXECUTION_PREFER_ENV_KEY = "ATHENA_EXECUTION_PREFER";
|
|
12080
|
+
var MODE_ALIASES = {
|
|
12081
|
+
gateway: "gateway",
|
|
12082
|
+
server: "gateway",
|
|
12083
|
+
remote: "gateway",
|
|
12084
|
+
http: "gateway",
|
|
12085
|
+
edge: "edge",
|
|
12086
|
+
cloudflare: "edge",
|
|
12087
|
+
"cloudflare-edge": "edge",
|
|
12088
|
+
d1: "edge",
|
|
12089
|
+
local: "edge",
|
|
12090
|
+
auto: "auto"
|
|
12091
|
+
};
|
|
12092
|
+
function normalizeOptional(value) {
|
|
12093
|
+
const trimmed = value?.trim();
|
|
12094
|
+
return trimmed || void 0;
|
|
12095
|
+
}
|
|
12096
|
+
function normalizeModeString(value) {
|
|
12097
|
+
if (value == null) return void 0;
|
|
12098
|
+
const key = value.trim().toLowerCase();
|
|
12099
|
+
if (!key) return void 0;
|
|
12100
|
+
const mapped = MODE_ALIASES[key];
|
|
12101
|
+
if (!mapped) {
|
|
12102
|
+
throw new AthenaConfigurationError(
|
|
12103
|
+
"ATHENA_NO_SERVICE_CONFIGURED",
|
|
12104
|
+
`Unknown Athena execution mode "${value}". Use gateway | edge | auto (aliases: server, remote, cloudflare, d1).`,
|
|
12105
|
+
"db"
|
|
12106
|
+
);
|
|
12107
|
+
}
|
|
12108
|
+
return mapped;
|
|
12109
|
+
}
|
|
12110
|
+
function isD1Binding(value) {
|
|
12111
|
+
return Boolean(
|
|
12112
|
+
value && typeof value === "object" && typeof value.prepare === "function"
|
|
12113
|
+
);
|
|
12114
|
+
}
|
|
12115
|
+
var GATEWAY_URL_ENV_KEYS2 = [
|
|
12116
|
+
"ATHENA_URL",
|
|
12117
|
+
"NEXT_PUBLIC_ATHENA_URL",
|
|
12118
|
+
"ATHENA_DB_URL",
|
|
12119
|
+
"ATHENA_GATEWAY_URL",
|
|
12120
|
+
"NEXT_PUBLIC_ATHENA_DB_API_URL"
|
|
12121
|
+
];
|
|
12122
|
+
function resolveGatewayUrl(url, env) {
|
|
12123
|
+
if (normalizeOptional(url)) {
|
|
12124
|
+
return normalizeOptional(url);
|
|
12125
|
+
}
|
|
12126
|
+
for (const key of GATEWAY_URL_ENV_KEYS2) {
|
|
12127
|
+
const value = normalizeOptional(env[key]);
|
|
12128
|
+
if (value) return value;
|
|
12129
|
+
}
|
|
12130
|
+
return void 0;
|
|
12131
|
+
}
|
|
12132
|
+
function resolvePrefer(prefer, env) {
|
|
12133
|
+
const raw = normalizeOptional(typeof prefer === "string" ? prefer : prefer ?? void 0) ?? normalizeOptional(env[ATHENA_EXECUTION_PREFER_ENV_KEY]) ?? "edge";
|
|
12134
|
+
const key = raw.toLowerCase();
|
|
12135
|
+
if (key === "gateway" || key === "server" || key === "remote" || key === "http") {
|
|
12136
|
+
return "gateway";
|
|
12137
|
+
}
|
|
12138
|
+
if (key === "edge" || key === "d1" || key === "cloudflare" || key === "local") {
|
|
12139
|
+
return "edge";
|
|
12140
|
+
}
|
|
12141
|
+
throw new AthenaConfigurationError(
|
|
12142
|
+
"ATHENA_NO_SERVICE_CONFIGURED",
|
|
12143
|
+
`Unknown Athena execution prefer "${raw}". Use edge | gateway.`,
|
|
12144
|
+
"db"
|
|
12145
|
+
);
|
|
12146
|
+
}
|
|
12147
|
+
function resolveAthenaExecutionMode(input = {}) {
|
|
12148
|
+
const env = input.env ?? {};
|
|
12149
|
+
const fromEnv = normalizeModeString(env[ATHENA_EXECUTION_MODE_ENV_KEY]);
|
|
12150
|
+
const requested = normalizeModeString(input.mode) ?? fromEnv ?? "auto";
|
|
12151
|
+
const url = resolveGatewayUrl(input.url, env);
|
|
12152
|
+
const hasD1 = isD1Binding(input.d1);
|
|
12153
|
+
const hasGateway = Boolean(url);
|
|
12154
|
+
if (requested === "gateway") {
|
|
12155
|
+
if (!hasGateway) {
|
|
12156
|
+
throw new AthenaConfigurationError(
|
|
12157
|
+
"ATHENA_NO_SERVICE_CONFIGURED",
|
|
12158
|
+
'Athena execution mode is "gateway" but no url / ATHENA_URL / ATHENA_DB_URL / ATHENA_GATEWAY_URL was provided.',
|
|
12159
|
+
"db"
|
|
12160
|
+
);
|
|
9632
12161
|
}
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
|
|
9644
|
-
|
|
9645
|
-
|
|
9646
|
-
|
|
9647
|
-
|
|
9648
|
-
|
|
9649
|
-
|
|
9650
|
-
|
|
9651
|
-
|
|
12162
|
+
return "gateway";
|
|
12163
|
+
}
|
|
12164
|
+
if (requested === "edge") {
|
|
12165
|
+
if (!hasD1) {
|
|
12166
|
+
throw new AthenaConfigurationError(
|
|
12167
|
+
"ATHENA_NO_SERVICE_CONFIGURED",
|
|
12168
|
+
'Athena execution mode is "edge" but no D1 binding was provided (db.d1 / config.d1 / env.DB).',
|
|
12169
|
+
"db"
|
|
12170
|
+
);
|
|
12171
|
+
}
|
|
12172
|
+
return "edge";
|
|
12173
|
+
}
|
|
12174
|
+
if (hasD1 && hasGateway) {
|
|
12175
|
+
return resolvePrefer(input.prefer, env);
|
|
12176
|
+
}
|
|
12177
|
+
if (hasD1) return "edge";
|
|
12178
|
+
if (hasGateway) return "gateway";
|
|
12179
|
+
throw new AthenaConfigurationError(
|
|
12180
|
+
"ATHENA_NO_SERVICE_CONFIGURED",
|
|
12181
|
+
'Cannot resolve Athena execution mode: provide mode "gateway" with url/key, mode "edge" with d1, or mode "auto" with either a D1 binding or a gateway URL (ATHENA_URL / ATHENA_DB_URL / ATHENA_GATEWAY_URL).',
|
|
12182
|
+
"db"
|
|
12183
|
+
);
|
|
12184
|
+
}
|
|
12185
|
+
|
|
12186
|
+
// src/cloudflare/r2/storage.ts
|
|
12187
|
+
function normalizePrefix(prefix) {
|
|
12188
|
+
if (!prefix?.trim()) return "";
|
|
12189
|
+
const normalized = prefix.replace(/^\/+/, "").replace(/\/?$/, "/");
|
|
12190
|
+
if (normalized.includes("\0")) {
|
|
12191
|
+
throw new Error("storage prefix must not contain null bytes");
|
|
12192
|
+
}
|
|
12193
|
+
if (normalized.split("/").some((segment) => segment === "..")) {
|
|
12194
|
+
throw new Error('storage prefix must not contain ".." path segments');
|
|
12195
|
+
}
|
|
12196
|
+
return normalized;
|
|
12197
|
+
}
|
|
12198
|
+
function assertSafeObjectKey(key) {
|
|
12199
|
+
if (typeof key !== "string" || !key.trim()) {
|
|
12200
|
+
throw new Error("Object key is required");
|
|
12201
|
+
}
|
|
12202
|
+
if (key.includes("\0")) {
|
|
12203
|
+
throw new Error("Object key must not contain null bytes");
|
|
12204
|
+
}
|
|
12205
|
+
const cleaned = key.replace(/^\/+/, "");
|
|
12206
|
+
if (!cleaned) {
|
|
12207
|
+
throw new Error("Object key is required");
|
|
12208
|
+
}
|
|
12209
|
+
const segments = cleaned.split("/");
|
|
12210
|
+
if (segments.some((segment) => segment === "..")) {
|
|
12211
|
+
throw new Error('Object key must not contain ".." path segments');
|
|
12212
|
+
}
|
|
12213
|
+
return cleaned;
|
|
12214
|
+
}
|
|
12215
|
+
function joinKey(prefix, key) {
|
|
12216
|
+
return `${prefix}${assertSafeObjectKey(key)}`;
|
|
12217
|
+
}
|
|
12218
|
+
function toLogicalKey(prefix, physicalKey) {
|
|
12219
|
+
if (!prefix) return physicalKey;
|
|
12220
|
+
if (physicalKey.startsWith(prefix)) {
|
|
12221
|
+
return physicalKey.slice(prefix.length);
|
|
12222
|
+
}
|
|
12223
|
+
return physicalKey;
|
|
12224
|
+
}
|
|
12225
|
+
function toPhysicalKey(prefix, key) {
|
|
12226
|
+
return joinKey(prefix, key);
|
|
12227
|
+
}
|
|
12228
|
+
function unsupported(method) {
|
|
12229
|
+
throw new Error(
|
|
12230
|
+
`storage.${method} is not available in Cloudflare edge-local R2 object mode. Only putObject / getObject / deleteObject / listObjects are supported (L3a).`
|
|
12231
|
+
);
|
|
12232
|
+
}
|
|
12233
|
+
function createCloudflareR2StorageModule(options) {
|
|
12234
|
+
const prefix = normalizePrefix(options.prefix);
|
|
12235
|
+
const { r2 } = options;
|
|
12236
|
+
const putObject = async (input) => {
|
|
12237
|
+
const logical = assertSafeObjectKey(input.key);
|
|
12238
|
+
const physical = toPhysicalKey(prefix, logical);
|
|
12239
|
+
await r2.put(physical, input.body, {
|
|
12240
|
+
httpMetadata: input.contentType ? { contentType: input.contentType } : void 0,
|
|
12241
|
+
customMetadata: input.metadata
|
|
12242
|
+
});
|
|
12243
|
+
return { key: logical };
|
|
12244
|
+
};
|
|
12245
|
+
const getObject = async (input) => {
|
|
12246
|
+
const logical = assertSafeObjectKey(input.key);
|
|
12247
|
+
const physical = toPhysicalKey(prefix, logical);
|
|
12248
|
+
const object = await r2.get(physical);
|
|
12249
|
+
if (!object) return null;
|
|
12250
|
+
return {
|
|
12251
|
+
key: toLogicalKey(prefix, object.key),
|
|
12252
|
+
size: object.size,
|
|
12253
|
+
contentType: object.httpMetadata?.contentType,
|
|
12254
|
+
body: object
|
|
12255
|
+
};
|
|
12256
|
+
};
|
|
12257
|
+
const deleteObject = async (input) => {
|
|
12258
|
+
const logicalKeys = (Array.isArray(input.key) ? input.key : [input.key]).map(
|
|
12259
|
+
(key) => assertSafeObjectKey(key)
|
|
12260
|
+
);
|
|
12261
|
+
const physicalKeys = logicalKeys.map((key) => toPhysicalKey(prefix, key));
|
|
12262
|
+
await r2.delete(physicalKeys.length === 1 ? physicalKeys[0] : physicalKeys);
|
|
12263
|
+
return { deleted: logicalKeys };
|
|
12264
|
+
};
|
|
12265
|
+
const listObjects = async (input) => {
|
|
12266
|
+
const relative = input?.prefix?.trim() ? assertSafeObjectKey(input.prefix) : "";
|
|
12267
|
+
const listPrefix = `${prefix}${relative}`;
|
|
12268
|
+
const listed = await r2.list({
|
|
12269
|
+
prefix: listPrefix || void 0,
|
|
12270
|
+
limit: input?.limit,
|
|
12271
|
+
cursor: input?.cursor
|
|
12272
|
+
});
|
|
12273
|
+
return {
|
|
12274
|
+
objects: listed.objects.map((object) => ({
|
|
12275
|
+
key: toLogicalKey(prefix, object.key),
|
|
12276
|
+
size: object.size,
|
|
12277
|
+
etag: object.etag,
|
|
12278
|
+
uploaded: object.uploaded
|
|
12279
|
+
})),
|
|
12280
|
+
truncated: listed.truncated,
|
|
12281
|
+
cursor: listed.cursor
|
|
12282
|
+
};
|
|
9652
12283
|
};
|
|
12284
|
+
const base = {
|
|
12285
|
+
putObject,
|
|
12286
|
+
getObject,
|
|
12287
|
+
deleteObject,
|
|
12288
|
+
listObjects
|
|
12289
|
+
};
|
|
12290
|
+
return new Proxy(base, {
|
|
12291
|
+
get(target, property, receiver) {
|
|
12292
|
+
if (property in target) {
|
|
12293
|
+
return Reflect.get(target, property, receiver);
|
|
12294
|
+
}
|
|
12295
|
+
if (typeof property === "string") {
|
|
12296
|
+
return (...args) => {
|
|
12297
|
+
return unsupported(property);
|
|
12298
|
+
};
|
|
12299
|
+
}
|
|
12300
|
+
return void 0;
|
|
12301
|
+
}
|
|
12302
|
+
});
|
|
9653
12303
|
}
|
|
9654
12304
|
|
|
9655
12305
|
// src/billing/module.ts
|
|
@@ -10248,18 +12898,6 @@ function createBillingModule(config) {
|
|
|
10248
12898
|
};
|
|
10249
12899
|
}
|
|
10250
12900
|
|
|
10251
|
-
// src/config/errors.ts
|
|
10252
|
-
var AthenaConfigurationError = class extends Error {
|
|
10253
|
-
code;
|
|
10254
|
-
service;
|
|
10255
|
-
constructor(code, message, service) {
|
|
10256
|
-
super(message);
|
|
10257
|
-
this.name = "AthenaConfigurationError";
|
|
10258
|
-
this.code = code;
|
|
10259
|
-
this.service = service;
|
|
10260
|
-
}
|
|
10261
|
-
};
|
|
10262
|
-
|
|
10263
12901
|
// src/context/merge.ts
|
|
10264
12902
|
function mergeAthenaRequestContexts(base, override) {
|
|
10265
12903
|
if (!base && !override) return void 0;
|
|
@@ -10372,14 +13010,30 @@ function readFirstEnv(env, keys) {
|
|
|
10372
13010
|
}
|
|
10373
13011
|
return void 0;
|
|
10374
13012
|
}
|
|
10375
|
-
function
|
|
13013
|
+
function readFirstEnvHttpUrl(env, keys) {
|
|
13014
|
+
for (const key of keys) {
|
|
13015
|
+
const value = env?.[key]?.trim();
|
|
13016
|
+
if (value && isAbsoluteHttpUrl(value)) return value;
|
|
13017
|
+
}
|
|
13018
|
+
return void 0;
|
|
13019
|
+
}
|
|
13020
|
+
function normalizeOptional2(value) {
|
|
10376
13021
|
const normalized = value?.trim();
|
|
10377
13022
|
return normalized || void 0;
|
|
10378
13023
|
}
|
|
13024
|
+
function isAbsoluteHttpUrl(value) {
|
|
13025
|
+
try {
|
|
13026
|
+
const url = new URL(value);
|
|
13027
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
13028
|
+
} catch {
|
|
13029
|
+
return false;
|
|
13030
|
+
}
|
|
13031
|
+
}
|
|
10379
13032
|
function appendPath(baseUrl, path) {
|
|
10380
13033
|
return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
|
|
10381
13034
|
}
|
|
10382
13035
|
function toWebSocketUrl(baseUrl) {
|
|
13036
|
+
if (!isAbsoluteHttpUrl(baseUrl)) return void 0;
|
|
10383
13037
|
const url = new URL(baseUrl);
|
|
10384
13038
|
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
10385
13039
|
url.pathname = `${url.pathname.replace(/\/+$/, "")}/wss/gateway`;
|
|
@@ -10389,11 +13043,11 @@ function toWebSocketUrl(baseUrl) {
|
|
|
10389
13043
|
}
|
|
10390
13044
|
function resolveCore(config) {
|
|
10391
13045
|
const env = config.env;
|
|
10392
|
-
const explicitRoot =
|
|
10393
|
-
const envRoot =
|
|
10394
|
-
const root = explicitRoot
|
|
10395
|
-
const explicitRootWinsOverEnvServices = explicitRoot
|
|
10396
|
-
const resolveService = (explicit, envKeys, path) =>
|
|
13046
|
+
const explicitRoot = normalizeOptional2(config.url);
|
|
13047
|
+
const envRoot = readFirstEnvHttpUrl(env, ENV_URL_KEYS);
|
|
13048
|
+
const root = explicitRoot && isAbsoluteHttpUrl(explicitRoot) ? explicitRoot : explicitRoot ? void 0 : envRoot;
|
|
13049
|
+
const explicitRootWinsOverEnvServices = Boolean(root && explicitRoot && isAbsoluteHttpUrl(explicitRoot));
|
|
13050
|
+
const resolveService = (explicit, envKeys, path) => normalizeOptional2(explicit) ?? (explicitRootWinsOverEnvServices && root ? appendPath(root, path) : void 0) ?? readFirstEnvHttpUrl(env, envKeys) ?? (root ? appendPath(root, path) : void 0);
|
|
10397
13051
|
const dbUrl = resolveService(config.db?.url, ENV_DB_URL_KEYS, "db");
|
|
10398
13052
|
const urls = {
|
|
10399
13053
|
db: dbUrl,
|
|
@@ -10401,8 +13055,8 @@ function resolveCore(config) {
|
|
|
10401
13055
|
chat: resolveService(config.chat?.url, ENV_CHAT_URL_KEYS, "chat"),
|
|
10402
13056
|
storage: resolveService(config.storage?.url, ENV_STORAGE_URL_KEYS, "storage"),
|
|
10403
13057
|
// Billing lives on the main Athena HTTP surface (same host as gateway/db).
|
|
10404
|
-
billing:
|
|
10405
|
-
chatWs:
|
|
13058
|
+
billing: normalizeOptional2(config.billing?.url) ?? dbUrl ?? root ?? void 0,
|
|
13059
|
+
chatWs: normalizeOptional2(config.chat?.wsUrl) ?? (explicitRootWinsOverEnvServices && root ? toWebSocketUrl(root) : void 0) ?? readFirstEnvHttpUrl(env, ENV_CHAT_WS_URL_KEYS) ?? (root ? toWebSocketUrl(root) : void 0)
|
|
10406
13060
|
};
|
|
10407
13061
|
if (!urls.db && !urls.auth && !urls.chat && !urls.storage && !urls.billing) {
|
|
10408
13062
|
throw new AthenaConfigurationError(
|
|
@@ -10411,7 +13065,7 @@ function resolveCore(config) {
|
|
|
10411
13065
|
"db"
|
|
10412
13066
|
);
|
|
10413
13067
|
}
|
|
10414
|
-
const key =
|
|
13068
|
+
const key = normalizeOptional2(config.key) ?? readFirstEnv(env, ENV_KEY_KEYS);
|
|
10415
13069
|
if (!key) {
|
|
10416
13070
|
throw new AthenaConfigurationError(
|
|
10417
13071
|
"ATHENA_API_KEY_REQUIRED",
|
|
@@ -10422,7 +13076,7 @@ function resolveCore(config) {
|
|
|
10422
13076
|
config,
|
|
10423
13077
|
urls,
|
|
10424
13078
|
key,
|
|
10425
|
-
client:
|
|
13079
|
+
client: normalizeOptional2(config.client) ?? readFirstEnv(env, ENV_CLIENT_KEYS)
|
|
10426
13080
|
};
|
|
10427
13081
|
return {
|
|
10428
13082
|
...base,
|
|
@@ -10437,7 +13091,13 @@ async function resolveConfiguredContext(core, viewContext) {
|
|
|
10437
13091
|
function createInternalConfig(core) {
|
|
10438
13092
|
const { config, urls } = core;
|
|
10439
13093
|
const { url: _authUrl, ...authOptions } = config.auth ?? {};
|
|
10440
|
-
const {
|
|
13094
|
+
const {
|
|
13095
|
+
url: _storageUrl,
|
|
13096
|
+
directUpload,
|
|
13097
|
+
r2: _r2,
|
|
13098
|
+
prefix: _r2Prefix,
|
|
13099
|
+
...storageOptions
|
|
13100
|
+
} = config.storage ?? {};
|
|
10441
13101
|
const diagnostics = resolveAthenaClientDiagnostics({
|
|
10442
13102
|
diagnostics: config.diagnostics,
|
|
10443
13103
|
debugAst: config.debugAst,
|
|
@@ -10468,7 +13128,8 @@ function createInternalConfig(core) {
|
|
|
10468
13128
|
storage: {
|
|
10469
13129
|
...storageOptions,
|
|
10470
13130
|
...directUpload ? { directUpload } : {}
|
|
10471
|
-
}
|
|
13131
|
+
},
|
|
13132
|
+
gatewayTransport: config.gatewayTransport
|
|
10472
13133
|
};
|
|
10473
13134
|
}
|
|
10474
13135
|
function serviceGuard(urls, service) {
|
|
@@ -10491,6 +13152,232 @@ function createUnavailableNamespace(service, urls, path = []) {
|
|
|
10491
13152
|
}
|
|
10492
13153
|
});
|
|
10493
13154
|
}
|
|
13155
|
+
function isD1Binding2(value) {
|
|
13156
|
+
return Boolean(
|
|
13157
|
+
value && typeof value === "object" && typeof value.prepare === "function"
|
|
13158
|
+
);
|
|
13159
|
+
}
|
|
13160
|
+
function isR2Binding(value) {
|
|
13161
|
+
return Boolean(
|
|
13162
|
+
value && typeof value === "object" && typeof value.put === "function" && typeof value.get === "function"
|
|
13163
|
+
);
|
|
13164
|
+
}
|
|
13165
|
+
function foldBindingAliases(config) {
|
|
13166
|
+
const d1 = config.db?.d1 ?? config.d1;
|
|
13167
|
+
const r2 = config.storage?.r2 ?? config.r2;
|
|
13168
|
+
const sessionMode = config.db?.sessionMode ?? config.sessionMode;
|
|
13169
|
+
const prefix = config.storage?.prefix ?? config.storagePrefix;
|
|
13170
|
+
let next = { ...config };
|
|
13171
|
+
if (d1 !== void 0 || sessionMode !== void 0) {
|
|
13172
|
+
next = {
|
|
13173
|
+
...next,
|
|
13174
|
+
db: {
|
|
13175
|
+
...next.db,
|
|
13176
|
+
...d1 !== void 0 ? { d1 } : {},
|
|
13177
|
+
...sessionMode !== void 0 ? { sessionMode } : {}
|
|
13178
|
+
}
|
|
13179
|
+
};
|
|
13180
|
+
}
|
|
13181
|
+
if (r2 !== void 0 || prefix !== void 0) {
|
|
13182
|
+
next = {
|
|
13183
|
+
...next,
|
|
13184
|
+
storage: {
|
|
13185
|
+
...next.storage,
|
|
13186
|
+
...r2 !== void 0 ? { r2 } : {},
|
|
13187
|
+
...prefix !== void 0 ? { prefix } : {}
|
|
13188
|
+
}
|
|
13189
|
+
};
|
|
13190
|
+
}
|
|
13191
|
+
const {
|
|
13192
|
+
d1: _d1,
|
|
13193
|
+
r2: _r2,
|
|
13194
|
+
storagePrefix: _storagePrefix,
|
|
13195
|
+
sessionMode: _sessionMode,
|
|
13196
|
+
...rest
|
|
13197
|
+
} = next;
|
|
13198
|
+
return rest;
|
|
13199
|
+
}
|
|
13200
|
+
function applyExecutionMode(config) {
|
|
13201
|
+
const d1 = config.db?.d1;
|
|
13202
|
+
const hasD1 = isD1Binding2(d1);
|
|
13203
|
+
const hasUrl = Boolean(
|
|
13204
|
+
normalizeOptional2(config.url) ?? normalizeOptional2(config.db?.url) ?? (config.env ? normalizeOptional2(config.env.ATHENA_URL) ?? normalizeOptional2(config.env.NEXT_PUBLIC_ATHENA_URL) : void 0)
|
|
13205
|
+
);
|
|
13206
|
+
const hasModeHint = config.mode != null || config.prefer != null || Boolean(config.env?.ATHENA_EXECUTION_MODE) || Boolean(config.env?.ATHENA_EXECUTION_PREFER);
|
|
13207
|
+
if (!hasD1 && !hasModeHint) {
|
|
13208
|
+
return config;
|
|
13209
|
+
}
|
|
13210
|
+
if (hasD1 && !hasUrl && !hasModeHint) {
|
|
13211
|
+
return config;
|
|
13212
|
+
}
|
|
13213
|
+
let resolved;
|
|
13214
|
+
try {
|
|
13215
|
+
resolved = resolveAthenaExecutionMode({
|
|
13216
|
+
mode: config.mode,
|
|
13217
|
+
prefer: config.prefer,
|
|
13218
|
+
d1: hasD1 ? d1 : null,
|
|
13219
|
+
url: config.url ?? config.db?.url,
|
|
13220
|
+
env: config.env
|
|
13221
|
+
});
|
|
13222
|
+
} catch (error) {
|
|
13223
|
+
if (!hasModeHint) return config;
|
|
13224
|
+
throw error;
|
|
13225
|
+
}
|
|
13226
|
+
if (resolved === "gateway" && hasD1) {
|
|
13227
|
+
const { d1: _drop, sessionMode: _sm, ...dbRest } = config.db ?? {};
|
|
13228
|
+
return {
|
|
13229
|
+
...config,
|
|
13230
|
+
db: Object.keys(dbRest).length > 0 ? dbRest : void 0,
|
|
13231
|
+
gatewayTransport: void 0
|
|
13232
|
+
};
|
|
13233
|
+
}
|
|
13234
|
+
return config;
|
|
13235
|
+
}
|
|
13236
|
+
function resolveUnifiedRemoteRoot(config) {
|
|
13237
|
+
return normalizeOptional2(config.url) ?? readFirstEnv(config.env, ENV_URL_KEYS);
|
|
13238
|
+
}
|
|
13239
|
+
function isEdgeLocalSentinelUrl(url) {
|
|
13240
|
+
if (!url) return false;
|
|
13241
|
+
return url === CLOUDFLARE_EDGE_BASE_URL || url.startsWith("https://athena.local/") || url.startsWith("http://athena.local/");
|
|
13242
|
+
}
|
|
13243
|
+
function hasRemoteDbGatewayUrl(config) {
|
|
13244
|
+
const dbUrl = normalizeOptional2(config.db?.url) ?? readFirstEnv(config.env, ENV_DB_URL_KEYS);
|
|
13245
|
+
return Boolean(dbUrl && !isEdgeLocalSentinelUrl(dbUrl));
|
|
13246
|
+
}
|
|
13247
|
+
function hasRemoteHttpServices(config) {
|
|
13248
|
+
return Boolean(
|
|
13249
|
+
resolveUnifiedRemoteRoot(config) || hasRemoteDbGatewayUrl(config) || normalizeOptional2(config.auth?.url) || readFirstEnv(config.env, ENV_AUTH_URL_KEYS) || normalizeOptional2(config.storage?.url) || readFirstEnv(config.env, ENV_STORAGE_URL_KEYS) || normalizeOptional2(config.chat?.url) || readFirstEnv(config.env, ENV_CHAT_URL_KEYS) || normalizeOptional2(config.billing?.url)
|
|
13250
|
+
);
|
|
13251
|
+
}
|
|
13252
|
+
function hasRemoteHttpStorage(config) {
|
|
13253
|
+
return Boolean(
|
|
13254
|
+
normalizeOptional2(config.storage?.url) || readFirstEnv(config.env, ENV_STORAGE_URL_KEYS) || resolveUnifiedRemoteRoot(config)
|
|
13255
|
+
);
|
|
13256
|
+
}
|
|
13257
|
+
function hasRemoteAuthService(config) {
|
|
13258
|
+
return Boolean(
|
|
13259
|
+
normalizeOptional2(config.auth?.url) || readFirstEnv(config.env, ENV_AUTH_URL_KEYS) || resolveUnifiedRemoteRoot(config)
|
|
13260
|
+
);
|
|
13261
|
+
}
|
|
13262
|
+
function materializeEdgeBindings(config) {
|
|
13263
|
+
const d1 = config.db?.d1;
|
|
13264
|
+
const r2 = config.storage?.r2;
|
|
13265
|
+
const hasD1 = isD1Binding2(d1);
|
|
13266
|
+
const hasR2 = isR2Binding(r2);
|
|
13267
|
+
if (!hasD1 && !hasR2) {
|
|
13268
|
+
return config;
|
|
13269
|
+
}
|
|
13270
|
+
if (hasD1 && d1 && typeof d1.prepare !== "function") {
|
|
13271
|
+
throw new AthenaConfigurationError(
|
|
13272
|
+
"ATHENA_NO_SERVICE_CONFIGURED",
|
|
13273
|
+
"db.d1 must be a D1 binding with prepare().",
|
|
13274
|
+
"db"
|
|
13275
|
+
);
|
|
13276
|
+
}
|
|
13277
|
+
const remoteRoot = resolveUnifiedRemoteRoot(config);
|
|
13278
|
+
const remoteServices = hasRemoteHttpServices(config);
|
|
13279
|
+
const remoteStorage = hasRemoteHttpStorage(config);
|
|
13280
|
+
const remoteAuth = hasRemoteAuthService(config);
|
|
13281
|
+
const next = { ...config };
|
|
13282
|
+
if (hasD1 && d1) {
|
|
13283
|
+
if (!config.gatewayTransport) {
|
|
13284
|
+
next.gatewayTransport = createCloudflareD1GatewayTransport({
|
|
13285
|
+
d1,
|
|
13286
|
+
defaultSessionMode: config.db?.sessionMode
|
|
13287
|
+
});
|
|
13288
|
+
}
|
|
13289
|
+
const explicitDbUrl = normalizeOptional2(config.db?.url);
|
|
13290
|
+
const remoteDbGateway = hasRemoteDbGatewayUrl(config);
|
|
13291
|
+
next.db = {
|
|
13292
|
+
...config.db,
|
|
13293
|
+
d1,
|
|
13294
|
+
url: explicitDbUrl ?? (remoteDbGateway ? void 0 : CLOUDFLARE_EDGE_BASE_URL)
|
|
13295
|
+
};
|
|
13296
|
+
const explicitKey = normalizeOptional2(config.key);
|
|
13297
|
+
if (explicitKey) {
|
|
13298
|
+
next.key = explicitKey;
|
|
13299
|
+
} else if (!remoteServices) {
|
|
13300
|
+
next.key = CLOUDFLARE_EDGE_API_KEY;
|
|
13301
|
+
} else {
|
|
13302
|
+
next.key = void 0;
|
|
13303
|
+
}
|
|
13304
|
+
if (remoteRoot) {
|
|
13305
|
+
next.billing = {
|
|
13306
|
+
...config.billing ?? {},
|
|
13307
|
+
url: normalizeOptional2(config.billing?.url) ?? remoteRoot
|
|
13308
|
+
};
|
|
13309
|
+
}
|
|
13310
|
+
if (!config.capabilities) {
|
|
13311
|
+
next.capabilities = createCloudflareEdgeCapabilities({
|
|
13312
|
+
hasR2,
|
|
13313
|
+
// Explicit storage.url / env storage URL / unified remote root all count.
|
|
13314
|
+
hasRemoteStorage: remoteStorage,
|
|
13315
|
+
authRemote: remoteAuth,
|
|
13316
|
+
findManyAst: false
|
|
13317
|
+
});
|
|
13318
|
+
}
|
|
13319
|
+
} else if (hasR2 && r2) {
|
|
13320
|
+
if (!remoteRoot && !normalizeOptional2(config.db?.url) && !normalizeOptional2(config.storage?.url) && !readFirstEnv(config.env, ENV_STORAGE_URL_KEYS)) {
|
|
13321
|
+
next.storage = {
|
|
13322
|
+
...config.storage,
|
|
13323
|
+
r2,
|
|
13324
|
+
// Local sentinel so resolveCore accepts a storage-only binding client.
|
|
13325
|
+
url: CLOUDFLARE_EDGE_BASE_URL
|
|
13326
|
+
};
|
|
13327
|
+
if (!remoteServices) {
|
|
13328
|
+
next.key = normalizeOptional2(config.key) ?? CLOUDFLARE_EDGE_API_KEY;
|
|
13329
|
+
} else if (normalizeOptional2(config.key)) {
|
|
13330
|
+
next.key = normalizeOptional2(config.key);
|
|
13331
|
+
} else {
|
|
13332
|
+
next.key = void 0;
|
|
13333
|
+
}
|
|
13334
|
+
}
|
|
13335
|
+
if (!config.capabilities) {
|
|
13336
|
+
if (remoteRoot || normalizeOptional2(config.db?.url) || readFirstEnv(config.env, ENV_DB_URL_KEYS)) {
|
|
13337
|
+
const base = createGatewayCapabilities({
|
|
13338
|
+
authRemote: remoteAuth,
|
|
13339
|
+
storageConfigured: true
|
|
13340
|
+
});
|
|
13341
|
+
next.capabilities = {
|
|
13342
|
+
...base,
|
|
13343
|
+
storage: {
|
|
13344
|
+
...base.storage,
|
|
13345
|
+
local: true,
|
|
13346
|
+
objects: true
|
|
13347
|
+
}
|
|
13348
|
+
};
|
|
13349
|
+
} else {
|
|
13350
|
+
next.capabilities = {
|
|
13351
|
+
mode: "cloudflare-edge",
|
|
13352
|
+
db: {
|
|
13353
|
+
engine: "unknown",
|
|
13354
|
+
local: false,
|
|
13355
|
+
layers: {
|
|
13356
|
+
query: false,
|
|
13357
|
+
flatCrud: false,
|
|
13358
|
+
findManyAst: false,
|
|
13359
|
+
rpc: false,
|
|
13360
|
+
relations: false
|
|
13361
|
+
}
|
|
13362
|
+
},
|
|
13363
|
+
storage: {
|
|
13364
|
+
local: true,
|
|
13365
|
+
objects: true,
|
|
13366
|
+
catalogs: false,
|
|
13367
|
+
backups: false
|
|
13368
|
+
},
|
|
13369
|
+
auth: {
|
|
13370
|
+
remote: remoteAuth
|
|
13371
|
+
}
|
|
13372
|
+
};
|
|
13373
|
+
}
|
|
13374
|
+
}
|
|
13375
|
+
}
|
|
13376
|
+
return next;
|
|
13377
|
+
}
|
|
13378
|
+
function normalizeCreateClientConfig(config) {
|
|
13379
|
+
return materializeEdgeBindings(applyExecutionMode(foldBindingAliases(config)));
|
|
13380
|
+
}
|
|
10494
13381
|
function createClientView(core, viewContext) {
|
|
10495
13382
|
const resolved = createInternalClientView(
|
|
10496
13383
|
core.internalCore,
|
|
@@ -10508,12 +13395,23 @@ function createClientView(core, viewContext) {
|
|
|
10508
13395
|
},
|
|
10509
13396
|
...billingOptions
|
|
10510
13397
|
}) : createUnavailableNamespace("billing", core.urls);
|
|
13398
|
+
const capabilities = core.config.capabilities ?? createGatewayCapabilities({
|
|
13399
|
+
authRemote: Boolean(core.urls.auth),
|
|
13400
|
+
// Match unavailable storage proxy when no storage URL was resolved.
|
|
13401
|
+
storageConfigured: Boolean(core.urls.storage)
|
|
13402
|
+
});
|
|
13403
|
+
const r2 = core.config.storage?.r2;
|
|
13404
|
+
const storageModule = isR2Binding(r2) ? createCloudflareR2StorageModule({
|
|
13405
|
+
r2,
|
|
13406
|
+
prefix: core.config.storage?.prefix ?? void 0
|
|
13407
|
+
}) : core.urls.storage ? resolved.storage : createUnavailableNamespace("storage", core.urls);
|
|
10511
13408
|
const client = {
|
|
10512
13409
|
db: core.urls.db ? resolved.db : createUnavailableNamespace("db", core.urls),
|
|
10513
13410
|
auth: core.urls.auth ? resolved.auth : createUnavailableNamespace("auth", core.urls),
|
|
10514
13411
|
chat: core.urls.chat ? resolved.chat : createUnavailableNamespace("chat", core.urls),
|
|
10515
|
-
storage:
|
|
13412
|
+
storage: storageModule,
|
|
10516
13413
|
billing: billingModule,
|
|
13414
|
+
capabilities,
|
|
10517
13415
|
from: ((...args) => {
|
|
10518
13416
|
requireDb();
|
|
10519
13417
|
return Reflect.apply(resolved.from, resolved, args);
|
|
@@ -10554,7 +13452,8 @@ function normalizeOptionalContextString(value) {
|
|
|
10554
13452
|
}
|
|
10555
13453
|
function warnIfEmptyRequestContext(context) {
|
|
10556
13454
|
if (!isEmptyAthenaRequestContext(context)) return;
|
|
10557
|
-
|
|
13455
|
+
const nodeEnv = globalThis.process?.env?.NODE_ENV;
|
|
13456
|
+
if (nodeEnv === "production") {
|
|
10558
13457
|
return;
|
|
10559
13458
|
}
|
|
10560
13459
|
console.warn(
|
|
@@ -10562,7 +13461,8 @@ function warnIfEmptyRequestContext(context) {
|
|
|
10562
13461
|
);
|
|
10563
13462
|
}
|
|
10564
13463
|
function createClient(config) {
|
|
10565
|
-
|
|
13464
|
+
const normalized = normalizeCreateClientConfig(config);
|
|
13465
|
+
return createClientView(resolveCore(normalized));
|
|
10566
13466
|
}
|
|
10567
13467
|
|
|
10568
13468
|
// src/generator/schema-discovery.ts
|
|
@@ -13064,6 +15964,7 @@ var AthenaGatewayPostgresIntrospectionProvider = class {
|
|
|
13064
15964
|
constructor(config) {
|
|
13065
15965
|
this.config = config;
|
|
13066
15966
|
this.client = createClient({
|
|
15967
|
+
url: this.config.gatewayUrl,
|
|
13067
15968
|
db: { url: this.config.gatewayUrl },
|
|
13068
15969
|
key: this.config.apiKey,
|
|
13069
15970
|
// Multi-tenant gateways reject DEFAULT_CLIENT; prefer explicit config.
|
|
@@ -13302,6 +16203,12 @@ async function runSchemaGenerator(options = {}) {
|
|
|
13302
16203
|
}
|
|
13303
16204
|
|
|
13304
16205
|
// src/cli/index.ts
|
|
16206
|
+
function setCliExitCode(code) {
|
|
16207
|
+
const proc = globalThis.process;
|
|
16208
|
+
if (proc) {
|
|
16209
|
+
proc.exitCode = code;
|
|
16210
|
+
}
|
|
16211
|
+
}
|
|
13305
16212
|
function isDebugEnabled() {
|
|
13306
16213
|
const value = globalThis.process?.env?.ATHENA_JS_DEBUG;
|
|
13307
16214
|
return value === "1" || value === "true";
|
|
@@ -13773,7 +16680,7 @@ async function runCLI(argv, runtime = {}) {
|
|
|
13773
16680
|
parsed = parseCommand(argv);
|
|
13774
16681
|
} catch (error) {
|
|
13775
16682
|
logCliError(formatGeneratorError(error), errorLog);
|
|
13776
|
-
|
|
16683
|
+
setCliExitCode(1);
|
|
13777
16684
|
return;
|
|
13778
16685
|
}
|
|
13779
16686
|
if (parsed.command === "help") {
|
|
@@ -13811,7 +16718,7 @@ async function runCLI(argv, runtime = {}) {
|
|
|
13811
16718
|
} catch (error) {
|
|
13812
16719
|
const formatted = formatGeneratorError(error, parsed.configPath);
|
|
13813
16720
|
logCliError(formatted, errorLog);
|
|
13814
|
-
|
|
16721
|
+
setCliExitCode(1);
|
|
13815
16722
|
}
|
|
13816
16723
|
return;
|
|
13817
16724
|
}
|
|
@@ -13831,7 +16738,7 @@ async function runCLI(argv, runtime = {}) {
|
|
|
13831
16738
|
} catch (error) {
|
|
13832
16739
|
const formatted = formatGeneratorError(error, parsed.configPath);
|
|
13833
16740
|
logCliError(formatted, errorLog);
|
|
13834
|
-
|
|
16741
|
+
setCliExitCode(1);
|
|
13835
16742
|
return;
|
|
13836
16743
|
}
|
|
13837
16744
|
if (parsed.dryRun) {
|