deepspace 0.23.2 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +6 -2
- package/dist/build.js +11 -0
- package/dist/build.js.map +1 -1
- package/dist/chunk-OFDOJGFS.js +1957 -0
- package/dist/chunk-OFDOJGFS.js.map +1 -0
- package/dist/cli.js +1583 -1688
- package/dist/cli.js.map +1 -1
- package/dist/documentation-client-core.js +1 -1
- package/dist/documentation-default-renderer.cjs +1 -1
- package/dist/documentation-react.js +1 -1
- package/dist/documentation-runtime.js +1 -1
- package/dist/documentation-server-core.js +1 -1
- package/dist/documentation.js +1 -1
- package/dist/documentation.js.map +1 -1
- package/dist/index.d.ts +30 -4
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +3 -19
- package/dist/schema.js +1 -207
- package/dist/schema.js.map +1 -1
- package/dist/server.d.ts +153 -48
- package/dist/server.js +501 -381
- package/dist/server.js.map +1 -1
- package/dist/worker.d.ts +178 -48
- package/dist/worker.js +561 -435
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -181,7 +181,7 @@ var BUILT_IN_TOOLS = [
|
|
|
181
181
|
},
|
|
182
182
|
{
|
|
183
183
|
name: "user.list",
|
|
184
|
-
description: "List
|
|
184
|
+
description: "List the users this caller may see, exactly as the room's roster does: an admin gets whole rows (including email and any app-defined column) through the users collection's read policy; everyone else gets the public identity projection (id, name, imageUrl, role, lastSeenAt) of the roster the users policy allows them \u2014 nothing if that policy denies reads, and only their own scope if the schema sets `roster: 'read-policy'`. A caller with no identity gets an empty list. Called from a server action (X-App-Action), it returns whole rows like the other RBAC-off tools.",
|
|
185
185
|
params: {}
|
|
186
186
|
},
|
|
187
187
|
// Yjs tools (collaborative document access)
|
|
@@ -762,8 +762,8 @@ function overPartBound(bytes) {
|
|
|
762
762
|
if (bytes <= UPLOAD_PART_BYTES) return null;
|
|
763
763
|
return fail2(413, oversizePartMessage(bytes), "too_large", { maxBytes: UPLOAD_PART_BYTES });
|
|
764
764
|
}
|
|
765
|
-
function declaredLength(
|
|
766
|
-
const raw =
|
|
765
|
+
function declaredLength(request2, what) {
|
|
766
|
+
const raw = request2.headers.get("content-length");
|
|
767
767
|
const declared = raw === null ? NaN : Number(raw);
|
|
768
768
|
if (!Number.isSafeInteger(declared) || declared < 0) {
|
|
769
769
|
return fail2(
|
|
@@ -841,7 +841,7 @@ function multipartFailure(error) {
|
|
|
841
841
|
}
|
|
842
842
|
function createScopedR2Handler(config) {
|
|
843
843
|
const requireAuthForMutations = config.requireAuthForMutations ?? true;
|
|
844
|
-
return async (
|
|
844
|
+
return async (request2, url, bucket, auth) => {
|
|
845
845
|
let subpathRaw;
|
|
846
846
|
try {
|
|
847
847
|
subpathRaw = decodeURIComponent(url.pathname.replace("/api/files", "").replace(/^\//, ""));
|
|
@@ -870,26 +870,26 @@ function createScopedR2Handler(config) {
|
|
|
870
870
|
}
|
|
871
871
|
const prefix = result.prefix;
|
|
872
872
|
const excludedPrefixes = result.excludedPrefixes ?? [];
|
|
873
|
-
if (isFilesVerbPath(
|
|
873
|
+
if (isFilesVerbPath(request2.method, subpath)) {
|
|
874
874
|
if (requireAuthForMutations && !auth.userId) {
|
|
875
|
-
await
|
|
875
|
+
await request2.body?.cancel().catch(() => void 0);
|
|
876
876
|
return fail2(401, "Authentication required", "unauthorized");
|
|
877
877
|
}
|
|
878
|
-
return subpath === "upload" ? handleUpload(
|
|
878
|
+
return subpath === "upload" ? handleUpload(request2, url, bucket, prefix, excludedPrefixes, auth, scope) : handleMultipart(request2, url, bucket, prefix, excludedPrefixes, auth, scope, subpath);
|
|
879
879
|
}
|
|
880
|
-
if (!subpath &&
|
|
880
|
+
if (!subpath && request2.method === "GET") {
|
|
881
881
|
return handleList(bucket, prefix, excludedPrefixes, url, scope, auth);
|
|
882
882
|
}
|
|
883
|
-
if (subpath &&
|
|
883
|
+
if (subpath && request2.method === "GET") {
|
|
884
884
|
if (!isKeyWithinScope(subpath, prefix, excludedPrefixes)) {
|
|
885
885
|
return Response.json(
|
|
886
886
|
{ error: "Access denied: key outside scope" },
|
|
887
887
|
{ status: 403, headers: CORS_HEADERS2 }
|
|
888
888
|
);
|
|
889
889
|
}
|
|
890
|
-
return handleDownload(
|
|
890
|
+
return handleDownload(request2, bucket, subpath, scope);
|
|
891
891
|
}
|
|
892
|
-
if (subpath &&
|
|
892
|
+
if (subpath && request2.method === "DELETE") {
|
|
893
893
|
if (requireAuthForMutations && !auth.userId) {
|
|
894
894
|
return Response.json(
|
|
895
895
|
{ error: "Authentication required" },
|
|
@@ -915,18 +915,18 @@ function createScopedR2Handler(config) {
|
|
|
915
915
|
return Response.json({ error: "Method not allowed" }, { status: 405, headers: CORS_HEADERS2 });
|
|
916
916
|
};
|
|
917
917
|
}
|
|
918
|
-
async function handleUpload(
|
|
918
|
+
async function handleUpload(request2, url, bucket, prefix, excludedPrefixes, auth, scope) {
|
|
919
919
|
try {
|
|
920
|
-
const declaredBytes = declaredLength(
|
|
920
|
+
const declaredBytes = declaredLength(request2, "File uploads");
|
|
921
921
|
if (declaredBytes instanceof Response) return declaredBytes;
|
|
922
922
|
const tooLargeRequest = overRequestBound(declaredBytes, 8 * 1024);
|
|
923
923
|
if (tooLargeRequest) return tooLargeRequest;
|
|
924
|
-
const contentType =
|
|
924
|
+
const contentType = request2.headers.get("content-type") || "";
|
|
925
925
|
let fileData;
|
|
926
926
|
let fileName;
|
|
927
927
|
let rawMimeType;
|
|
928
928
|
if (contentType.includes("multipart/form-data")) {
|
|
929
|
-
const formData = await
|
|
929
|
+
const formData = await request2.formData();
|
|
930
930
|
const file = formData.get("file");
|
|
931
931
|
if (!file) return fail2(400, "No file provided", "bad_request");
|
|
932
932
|
const tooBig = overRequestBound(file.size);
|
|
@@ -935,7 +935,7 @@ async function handleUpload(request, url, bucket, prefix, excludedPrefixes, auth
|
|
|
935
935
|
fileName = formData.get("name")?.toString() || file.name;
|
|
936
936
|
rawMimeType = file.type || "application/octet-stream";
|
|
937
937
|
} else {
|
|
938
|
-
const body = await
|
|
938
|
+
const body = await request2.json();
|
|
939
939
|
if (!body.data || !body.name) return fail2(400, "Missing data or name", "bad_request");
|
|
940
940
|
const base64Data = body.data.replace(/^data:[^;]+;base64,/, "");
|
|
941
941
|
const tooBig = overRequestBound(Math.floor(base64Data.length * 3 / 4));
|
|
@@ -1016,15 +1016,15 @@ function storedFileJson(key, prefix, name, url, scope) {
|
|
|
1016
1016
|
);
|
|
1017
1017
|
}
|
|
1018
1018
|
var MAX_CONTROL_BODY_BYTES = 64 * 1024;
|
|
1019
|
-
async function readControlJson(
|
|
1020
|
-
const declared = declaredLength(
|
|
1019
|
+
async function readControlJson(request2) {
|
|
1020
|
+
const declared = declaredLength(request2, "This request");
|
|
1021
1021
|
if (declared instanceof Response) return declared;
|
|
1022
1022
|
if (declared > MAX_CONTROL_BODY_BYTES) {
|
|
1023
1023
|
return fail2(413, `Request body exceeds ${MAX_CONTROL_BODY_BYTES} bytes`, "too_large");
|
|
1024
1024
|
}
|
|
1025
1025
|
let parsed;
|
|
1026
1026
|
try {
|
|
1027
|
-
parsed = await
|
|
1027
|
+
parsed = await request2.json();
|
|
1028
1028
|
} catch {
|
|
1029
1029
|
return fail2(400, "Body must be JSON", "bad_request");
|
|
1030
1030
|
}
|
|
@@ -1076,7 +1076,7 @@ function multipartLayoutError(declaredBytes) {
|
|
|
1076
1076
|
{ partSize: UPLOAD_PART_BYTES, partCount: multipartPartCount(declaredBytes) }
|
|
1077
1077
|
);
|
|
1078
1078
|
}
|
|
1079
|
-
async function streamPart(
|
|
1079
|
+
async function streamPart(upload2, partNumber, body, expectedBytes) {
|
|
1080
1080
|
let observedBytes = 0;
|
|
1081
1081
|
const sized = new FixedLengthStream(expectedBytes);
|
|
1082
1082
|
const pumped = body.pipeThrough(
|
|
@@ -1088,7 +1088,7 @@ async function streamPart(upload, partNumber, body, expectedBytes) {
|
|
|
1088
1088
|
})
|
|
1089
1089
|
).pipeTo(sized.writable);
|
|
1090
1090
|
const [uploaded, drained] = await Promise.allSettled([
|
|
1091
|
-
|
|
1091
|
+
upload2.uploadPart(partNumber, sized.readable),
|
|
1092
1092
|
pumped
|
|
1093
1093
|
]);
|
|
1094
1094
|
if (uploaded.status === "rejected" || drained.status === "rejected") {
|
|
@@ -1097,20 +1097,20 @@ async function streamPart(upload, partNumber, body, expectedBytes) {
|
|
|
1097
1097
|
}
|
|
1098
1098
|
return { ok: true, part: uploaded.value };
|
|
1099
1099
|
}
|
|
1100
|
-
async function handleMultipart(
|
|
1101
|
-
if (subpath === "multipart" &&
|
|
1102
|
-
return multipartInit(
|
|
1100
|
+
async function handleMultipart(request2, url, bucket, prefix, excludedPrefixes, auth, scope, subpath) {
|
|
1101
|
+
if (subpath === "multipart" && request2.method === "POST") {
|
|
1102
|
+
return multipartInit(request2, url, bucket, prefix, excludedPrefixes, auth);
|
|
1103
1103
|
}
|
|
1104
1104
|
if (subpath === "multipart/part") {
|
|
1105
|
-
return multipartPart(
|
|
1105
|
+
return multipartPart(request2, url, bucket, prefix, excludedPrefixes, auth);
|
|
1106
1106
|
}
|
|
1107
1107
|
if (subpath === "multipart/complete") {
|
|
1108
|
-
return multipartComplete(
|
|
1108
|
+
return multipartComplete(request2, url, bucket, prefix, excludedPrefixes, auth, scope);
|
|
1109
1109
|
}
|
|
1110
1110
|
return multipartAbort(url, bucket, prefix, excludedPrefixes, auth);
|
|
1111
1111
|
}
|
|
1112
|
-
async function multipartInit(
|
|
1113
|
-
const body = await readControlJson(
|
|
1112
|
+
async function multipartInit(request2, url, bucket, prefix, excludedPrefixes, auth) {
|
|
1113
|
+
const body = await readControlJson(request2);
|
|
1114
1114
|
if (body instanceof Response) return body;
|
|
1115
1115
|
const size = body.size;
|
|
1116
1116
|
if (!Number.isSafeInteger(size) || size <= 0) {
|
|
@@ -1151,9 +1151,9 @@ async function multipartInit(request, url, bucket, prefix, excludedPrefixes, aut
|
|
|
1151
1151
|
"request_id_conflict"
|
|
1152
1152
|
);
|
|
1153
1153
|
}
|
|
1154
|
-
let
|
|
1154
|
+
let upload2;
|
|
1155
1155
|
try {
|
|
1156
|
-
|
|
1156
|
+
upload2 = await bucket.createMultipartUpload(located.key, {
|
|
1157
1157
|
httpMetadata: { contentType: mimeType },
|
|
1158
1158
|
customMetadata: fileMetadata(name, auth.userId, requestId, size)
|
|
1159
1159
|
});
|
|
@@ -1165,7 +1165,7 @@ async function multipartInit(request, url, bucket, prefix, excludedPrefixes, aut
|
|
|
1165
1165
|
body: JSON.stringify({
|
|
1166
1166
|
version: 1,
|
|
1167
1167
|
reservationId: requestId,
|
|
1168
|
-
uploadId:
|
|
1168
|
+
uploadId: upload2.uploadId,
|
|
1169
1169
|
objectKey: located.key,
|
|
1170
1170
|
uploadKey: located.subkey,
|
|
1171
1171
|
admitted: false,
|
|
@@ -1179,7 +1179,7 @@ async function multipartInit(request, url, bucket, prefix, excludedPrefixes, aut
|
|
|
1179
1179
|
const reservedBytes = Math.max(size, 5 * 1024 * 1024);
|
|
1180
1180
|
const admission = await admitStorage(bucket, auth.storage, reservedBytes, 0, marker);
|
|
1181
1181
|
if (admission instanceof Response) {
|
|
1182
|
-
await
|
|
1182
|
+
await upload2.abort().catch(() => void 0);
|
|
1183
1183
|
if (admission.status === 409 && auth.storage) {
|
|
1184
1184
|
const winner = await readMultipartReservation(bucket, auth.storage, requestId);
|
|
1185
1185
|
if (winner && winner !== "invalid" && winner.objectKey === located.key && winner.uploadKey === located.subkey && winner.declaredBytes === size && winner.admitted) {
|
|
@@ -1189,11 +1189,11 @@ async function multipartInit(request, url, bucket, prefix, excludedPrefixes, aut
|
|
|
1189
1189
|
return admission;
|
|
1190
1190
|
}
|
|
1191
1191
|
if (auth.storage && !await activateMultipartReservation(bucket, auth.storage, requestId)) {
|
|
1192
|
-
await
|
|
1192
|
+
await upload2.abort().catch(() => void 0);
|
|
1193
1193
|
await releaseQuotaMarker(bucket, auth.storage, admission.markerKey, true);
|
|
1194
1194
|
return quotaUnavailable("This upload reservation could not be finalized. Retry shortly.");
|
|
1195
1195
|
}
|
|
1196
|
-
return multipartSessionJson(
|
|
1196
|
+
return multipartSessionJson(upload2.uploadId, located.subkey, requestId);
|
|
1197
1197
|
}
|
|
1198
1198
|
async function abandonMultipart(bucket, session, auth, reservation) {
|
|
1199
1199
|
let result = "aborted";
|
|
@@ -1211,12 +1211,12 @@ async function abandonMultipart(bucket, session, auth, reservation) {
|
|
|
1211
1211
|
}
|
|
1212
1212
|
return result;
|
|
1213
1213
|
}
|
|
1214
|
-
async function multipartPart(
|
|
1214
|
+
async function multipartPart(request2, url, bucket, prefix, excludedPrefixes, auth) {
|
|
1215
1215
|
const refuse = async (response) => {
|
|
1216
|
-
await
|
|
1216
|
+
await request2.body?.cancel().catch(() => void 0);
|
|
1217
1217
|
return response;
|
|
1218
1218
|
};
|
|
1219
|
-
const declaredBytes = declaredLength(
|
|
1219
|
+
const declaredBytes = declaredLength(request2, "Part uploads");
|
|
1220
1220
|
if (declaredBytes instanceof Response) return refuse(declaredBytes);
|
|
1221
1221
|
const tooBig = overPartBound(declaredBytes);
|
|
1222
1222
|
if (tooBig) return refuse(tooBig);
|
|
@@ -1228,7 +1228,7 @@ async function multipartPart(request, url, bucket, prefix, excludedPrefixes, aut
|
|
|
1228
1228
|
}
|
|
1229
1229
|
const session = resolveSession(url, prefix, excludedPrefixes);
|
|
1230
1230
|
if (session instanceof Response) return refuse(session);
|
|
1231
|
-
if (!
|
|
1231
|
+
if (!request2.body) return fail2(400, "A part body is required", "bad_request");
|
|
1232
1232
|
const reservation = await readMultipartReservation(bucket, auth.storage, session.reservationId);
|
|
1233
1233
|
if (reservation === "invalid") {
|
|
1234
1234
|
return refuse(quotaUnavailable("This upload reservation could not be read. Retry shortly."));
|
|
@@ -1270,8 +1270,8 @@ async function multipartPart(request, url, bucket, prefix, excludedPrefixes, aut
|
|
|
1270
1270
|
if (refreshed === "busy") {
|
|
1271
1271
|
return refuse(quotaUnavailable("This upload reservation is busy. Retry the part shortly."));
|
|
1272
1272
|
}
|
|
1273
|
-
const
|
|
1274
|
-
const stored = await streamPart(
|
|
1273
|
+
const upload2 = bucket.resumeMultipartUpload(session.key, session.uploadId);
|
|
1274
|
+
const stored = await streamPart(upload2, partNumber, request2.body, declaredBytes);
|
|
1275
1275
|
if (!stored.ok) {
|
|
1276
1276
|
if (r2ErrorCode(stored.error) === 10024 && reservation) {
|
|
1277
1277
|
await releaseQuotaMarker(bucket, auth.storage, reservation.key, true);
|
|
@@ -1327,8 +1327,8 @@ async function finishCompletedMultipart(bucket, session, reservation, object, au
|
|
|
1327
1327
|
const name = object.customMetadata?.originalName ?? session.uploadKey.split("/").pop() ?? "file";
|
|
1328
1328
|
return storedFileJson(session.key, prefix, name, url, scope);
|
|
1329
1329
|
}
|
|
1330
|
-
async function multipartComplete(
|
|
1331
|
-
const body = await readControlJson(
|
|
1330
|
+
async function multipartComplete(request2, url, bucket, prefix, excludedPrefixes, auth, scope) {
|
|
1331
|
+
const body = await readControlJson(request2);
|
|
1332
1332
|
if (body instanceof Response) return body;
|
|
1333
1333
|
const session = resolveSession(url, prefix, excludedPrefixes);
|
|
1334
1334
|
if (session instanceof Response) return session;
|
|
@@ -1487,7 +1487,7 @@ async function handleList(bucket, prefix, excludedPrefixes, url, scope, auth) {
|
|
|
1487
1487
|
{ headers: CORS_HEADERS2 }
|
|
1488
1488
|
);
|
|
1489
1489
|
}
|
|
1490
|
-
async function handleDownload(
|
|
1490
|
+
async function handleDownload(request2, bucket, key, scope) {
|
|
1491
1491
|
const object = await bucket.get(key);
|
|
1492
1492
|
if (!object) {
|
|
1493
1493
|
return Response.json({ error: "File not found" }, { status: 404, headers: CORS_HEADERS2 });
|
|
@@ -1509,7 +1509,7 @@ async function handleDownload(request, bucket, key, scope) {
|
|
|
1509
1509
|
contentDisposition(originalName, mustDownload ? "attachment" : "inline")
|
|
1510
1510
|
);
|
|
1511
1511
|
if (mustDownload) headers.set("Content-Security-Policy", "sandbox; default-src 'none'");
|
|
1512
|
-
if (scope === "app" &&
|
|
1512
|
+
if (scope === "app" && request2.headers.get("if-none-match") === object.httpEtag) {
|
|
1513
1513
|
return new Response(null, { status: 304, headers });
|
|
1514
1514
|
}
|
|
1515
1515
|
return new Response(object.body, { headers });
|
|
@@ -2075,8 +2075,8 @@ async function composioTools(env, options) {
|
|
|
2075
2075
|
if (!list.ok || !list.success) {
|
|
2076
2076
|
throw new Error(`composioTools: failed to list Composio tools: ${list.detail}`);
|
|
2077
2077
|
}
|
|
2078
|
-
const
|
|
2079
|
-
const items = Array.isArray(
|
|
2078
|
+
const listItems2 = list.data?.items;
|
|
2079
|
+
const items = Array.isArray(listItems2) ? listItems2 : [];
|
|
2080
2080
|
const tools = {};
|
|
2081
2081
|
for (const item of items) {
|
|
2082
2082
|
if (!item?.slug) continue;
|
|
@@ -2739,18 +2739,272 @@ var COST_RATES = {
|
|
|
2739
2739
|
queriedPerDim: VECTORIZE_QUERIED_USD_PER_M_DIMS / 1e6,
|
|
2740
2740
|
/** USD per stored dimension per month. */
|
|
2741
2741
|
storedPerDimPerMonth: VECTORIZE_STORED_USD_PER_100M_DIMS / 1e8
|
|
2742
|
+
},
|
|
2743
|
+
aiSearch: {
|
|
2744
|
+
ingestPerToken: 0.75 / 1e6,
|
|
2745
|
+
ingestImagePerToken: 0.5 / 1e6,
|
|
2746
|
+
storagePerByteMonth: 2 / 1e9,
|
|
2747
|
+
hybridOrSemanticPerQuery: 0.75 / 1e3,
|
|
2748
|
+
fulltextPerQuery: 0.1 / 1e3
|
|
2742
2749
|
}
|
|
2743
2750
|
};
|
|
2744
2751
|
function priceBindingUsageEvent(kind, op, units) {
|
|
2752
|
+
if (!Number.isFinite(units) || units < 0) return 0;
|
|
2745
2753
|
if (kind === "ai" && op === "input") {
|
|
2746
2754
|
return units * COST_RATES.ai.embedInputPerChar;
|
|
2747
2755
|
}
|
|
2748
2756
|
if (kind === "vectorize" && op === "query") {
|
|
2749
2757
|
return units * COST_RATES.vectorize.queriedPerDim;
|
|
2750
2758
|
}
|
|
2759
|
+
if (kind === "ai_search") {
|
|
2760
|
+
if (op === "ingest") return units * COST_RATES.aiSearch.ingestPerToken;
|
|
2761
|
+
if (op === "ingest-image") return units * COST_RATES.aiSearch.ingestImagePerToken;
|
|
2762
|
+
if (op === "storage") return units * COST_RATES.aiSearch.storagePerByteMonth;
|
|
2763
|
+
if (op === "search-hybrid" || op === "search-semantic")
|
|
2764
|
+
return units * COST_RATES.aiSearch.hybridOrSemanticPerQuery;
|
|
2765
|
+
if (op === "search-fulltext") return units * COST_RATES.aiSearch.fulltextPerQuery;
|
|
2766
|
+
}
|
|
2751
2767
|
return 0;
|
|
2752
2768
|
}
|
|
2753
2769
|
|
|
2770
|
+
// src/server/utils/knowledge-base.ts
|
|
2771
|
+
var MAX_UPLOAD_BYTES = 4 * 1024 * 1024;
|
|
2772
|
+
var TEXT_MIME = /^(text\/|application\/(json|xml|javascript|x-(javascript|typescript)))/iu;
|
|
2773
|
+
var MAX_QUERY_CHARS = 4096;
|
|
2774
|
+
function hasControlChar(value) {
|
|
2775
|
+
for (let i = 0; i < value.length; i++) {
|
|
2776
|
+
const code = value.charCodeAt(i);
|
|
2777
|
+
if (code < 32 || code === 127) return true;
|
|
2778
|
+
}
|
|
2779
|
+
return false;
|
|
2780
|
+
}
|
|
2781
|
+
var KnowledgeError = class extends Error {
|
|
2782
|
+
constructor(status, code, message, uploadedItems) {
|
|
2783
|
+
super(message);
|
|
2784
|
+
this.status = status;
|
|
2785
|
+
this.code = code;
|
|
2786
|
+
this.uploadedItems = uploadedItems;
|
|
2787
|
+
this.name = "KnowledgeError";
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
function normalizeKnowledgeFolder(folder) {
|
|
2791
|
+
if (!folder || folder.includes("\\") || hasControlChar(folder) || folder.startsWith("/"))
|
|
2792
|
+
throw new KnowledgeError(400, "invalid_folder", "Folder must be a relative forward-slash path");
|
|
2793
|
+
const withoutTrailingSlash = folder.endsWith("/") ? folder.slice(0, -1) : folder;
|
|
2794
|
+
if (!withoutTrailingSlash || withoutTrailingSlash.endsWith("/"))
|
|
2795
|
+
throw new KnowledgeError(400, "invalid_folder", "Folder cannot contain empty segments");
|
|
2796
|
+
const parts = withoutTrailingSlash.split("/");
|
|
2797
|
+
if (parts.some((part) => !part || part === "." || part === ".."))
|
|
2798
|
+
throw new KnowledgeError(
|
|
2799
|
+
400,
|
|
2800
|
+
"invalid_folder",
|
|
2801
|
+
"Folder cannot contain empty, dot, or parent segments"
|
|
2802
|
+
);
|
|
2803
|
+
const normalized = `${parts.join("/")}/`;
|
|
2804
|
+
if (normalized.length > 128)
|
|
2805
|
+
throw new KnowledgeError(400, "invalid_folder", "Folder is too long for an AI Search item key");
|
|
2806
|
+
return normalized;
|
|
2807
|
+
}
|
|
2808
|
+
function validateKnowledgeFilename(name) {
|
|
2809
|
+
if (!name || name === "." || name === ".." || name.includes("/") || name.includes("\\") || hasControlChar(name)) {
|
|
2810
|
+
throw new KnowledgeError(
|
|
2811
|
+
400,
|
|
2812
|
+
"invalid_filename",
|
|
2813
|
+
"File name must be a non-empty base name without path separators"
|
|
2814
|
+
);
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
function folderFilter(folder) {
|
|
2818
|
+
const value = normalizeKnowledgeFolder(folder);
|
|
2819
|
+
return { folder: { $gte: value, $lt: `${value.slice(0, -1)}0` } };
|
|
2820
|
+
}
|
|
2821
|
+
function knowledge(env) {
|
|
2822
|
+
const add = (file, options = {}) => addFile(env, file, options);
|
|
2823
|
+
const list = (options = {}) => listItems(env, options);
|
|
2824
|
+
const search = (query, options = {}) => searchItems(env, query, options);
|
|
2825
|
+
return {
|
|
2826
|
+
add,
|
|
2827
|
+
list,
|
|
2828
|
+
search,
|
|
2829
|
+
async remove(itemId) {
|
|
2830
|
+
if (!/^[A-Za-z0-9_-]{1,256}$/u.test(itemId))
|
|
2831
|
+
throw new KnowledgeError(400, "invalid_item_id", "Invalid knowledge item id");
|
|
2832
|
+
await request(env, `/api/knowledge/items/${encodeURIComponent(itemId)}`, { method: "DELETE" });
|
|
2833
|
+
},
|
|
2834
|
+
scoped({ folder }) {
|
|
2835
|
+
const fixed = normalizeKnowledgeFolder(folder);
|
|
2836
|
+
return {
|
|
2837
|
+
add: (file) => add(file, { folder: fixed }),
|
|
2838
|
+
list: (options = {}) => list({ ...options, folder: fixed }),
|
|
2839
|
+
search: (query, options = {}) => search(query, { ...options, folder: fixed })
|
|
2840
|
+
};
|
|
2841
|
+
}
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
async function addFile(env, file, options) {
|
|
2845
|
+
validateKnowledgeFilename(file.name);
|
|
2846
|
+
const folder = options.folder === void 0 ? void 0 : normalizeKnowledgeFolder(options.folder);
|
|
2847
|
+
if ((folder?.length ?? 0) + file.name.length > 128)
|
|
2848
|
+
throw new KnowledgeError(
|
|
2849
|
+
400,
|
|
2850
|
+
"invalid_key",
|
|
2851
|
+
"Folder and filename together exceed the 128-character AI Search key limit"
|
|
2852
|
+
);
|
|
2853
|
+
if (file.size <= MAX_UPLOAD_BYTES) return upload(env, file, folder);
|
|
2854
|
+
if (!TEXT_MIME.test(file.type) && !/\.(txt|md|mdx|csv|json|xml|html?|ya?ml|tsx?|jsx?|css)$/iu.test(file.name))
|
|
2855
|
+
throw new KnowledgeError(
|
|
2856
|
+
413,
|
|
2857
|
+
"file_too_large",
|
|
2858
|
+
"This file exceeds 4 MB. Split the source document with a format-aware tool before uploading."
|
|
2859
|
+
);
|
|
2860
|
+
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
2861
|
+
const parts = splitUtf8(bytes, MAX_UPLOAD_BYTES);
|
|
2862
|
+
const uploaded = [];
|
|
2863
|
+
try {
|
|
2864
|
+
for (let i = 0; i < parts.length; i++) {
|
|
2865
|
+
const bytes2 = parts[i];
|
|
2866
|
+
const contents = bytes2.buffer.slice(
|
|
2867
|
+
bytes2.byteOffset,
|
|
2868
|
+
bytes2.byteOffset + bytes2.byteLength
|
|
2869
|
+
);
|
|
2870
|
+
const part = new File([contents], partName(file.name, i + 1), {
|
|
2871
|
+
type: file.type || "text/plain"
|
|
2872
|
+
});
|
|
2873
|
+
if ((folder?.length ?? 0) + part.name.length > 128)
|
|
2874
|
+
throw new KnowledgeError(
|
|
2875
|
+
400,
|
|
2876
|
+
"invalid_key",
|
|
2877
|
+
"Folder and split filename together exceed the 128-character AI Search key limit"
|
|
2878
|
+
);
|
|
2879
|
+
uploaded.push(...(await upload(env, part, folder)).items);
|
|
2880
|
+
}
|
|
2881
|
+
} catch (error) {
|
|
2882
|
+
if (error instanceof KnowledgeError)
|
|
2883
|
+
throw new KnowledgeError(error.status, error.code, error.message, uploaded);
|
|
2884
|
+
throw new KnowledgeError(502, "knowledge_upload_failed", "Knowledge upload failed", uploaded);
|
|
2885
|
+
}
|
|
2886
|
+
return { items: uploaded };
|
|
2887
|
+
}
|
|
2888
|
+
function splitUtf8(bytes, max) {
|
|
2889
|
+
const out = [];
|
|
2890
|
+
for (let start = 0; start < bytes.length; ) {
|
|
2891
|
+
let end = Math.min(start + max, bytes.length);
|
|
2892
|
+
if (end < bytes.length) {
|
|
2893
|
+
let cut = end;
|
|
2894
|
+
while (cut > start && (bytes[cut] & 192) === 128) cut--;
|
|
2895
|
+
for (let i = cut - 1; i > Math.max(start, cut - 8192); i--)
|
|
2896
|
+
if (bytes[i] === 10 && (i === start || bytes[i - 1] === 10)) {
|
|
2897
|
+
cut = i + 1;
|
|
2898
|
+
break;
|
|
2899
|
+
}
|
|
2900
|
+
end = cut > start ? cut : end;
|
|
2901
|
+
}
|
|
2902
|
+
out.push(bytes.slice(start, end));
|
|
2903
|
+
start = end;
|
|
2904
|
+
}
|
|
2905
|
+
return out;
|
|
2906
|
+
}
|
|
2907
|
+
function partName(name, n) {
|
|
2908
|
+
const dot = name.lastIndexOf(".");
|
|
2909
|
+
return dot > 0 ? `${name.slice(0, dot)}.part-${n}${name.slice(dot)}` : `${name}.part-${n}`;
|
|
2910
|
+
}
|
|
2911
|
+
async function upload(env, file, folder) {
|
|
2912
|
+
const body = new FormData();
|
|
2913
|
+
body.append("file", file);
|
|
2914
|
+
if (folder) body.append("folder", folder);
|
|
2915
|
+
return await request(env, "/api/knowledge/items", {
|
|
2916
|
+
method: "POST",
|
|
2917
|
+
body
|
|
2918
|
+
});
|
|
2919
|
+
}
|
|
2920
|
+
async function listItems(env, options) {
|
|
2921
|
+
if (options.page !== void 0 && (!Number.isInteger(options.page) || options.page < 1))
|
|
2922
|
+
throw new KnowledgeError(400, "invalid_page", "List page must be a positive integer");
|
|
2923
|
+
if (options.perPage !== void 0 && (!Number.isInteger(options.perPage) || options.perPage < 1 || options.perPage > 50))
|
|
2924
|
+
throw new KnowledgeError(
|
|
2925
|
+
400,
|
|
2926
|
+
"invalid_per_page",
|
|
2927
|
+
"List perPage must be an integer from 1 to 50"
|
|
2928
|
+
);
|
|
2929
|
+
if (options.search !== void 0 && options.search.length > 256)
|
|
2930
|
+
throw new KnowledgeError(400, "invalid_search", "List search cannot exceed 256 characters");
|
|
2931
|
+
if (options.status !== void 0 && !["queued", "running", "completed", "error", "skipped", "outdated"].includes(options.status))
|
|
2932
|
+
throw new KnowledgeError(400, "invalid_status", "Invalid knowledge item status");
|
|
2933
|
+
const q = new URLSearchParams();
|
|
2934
|
+
if (options.folder) q.set("folder", normalizeKnowledgeFolder(options.folder));
|
|
2935
|
+
if (options.page !== void 0) q.set("page", String(options.page));
|
|
2936
|
+
if (options.perPage !== void 0) q.set("perPage", String(options.perPage));
|
|
2937
|
+
if (options.status) q.set("status", options.status);
|
|
2938
|
+
if (options.search) q.set("search", options.search);
|
|
2939
|
+
return await request(env, `/api/knowledge/items${q.size ? `?${q}` : ""}`);
|
|
2940
|
+
}
|
|
2941
|
+
async function searchItems(env, query, options) {
|
|
2942
|
+
const normalizedQuery = query.trim();
|
|
2943
|
+
if (!normalizedQuery) throw new KnowledgeError(400, "invalid_query", "Search query is required");
|
|
2944
|
+
if (normalizedQuery.length > MAX_QUERY_CHARS)
|
|
2945
|
+
throw new KnowledgeError(
|
|
2946
|
+
400,
|
|
2947
|
+
"invalid_query",
|
|
2948
|
+
`Search query cannot exceed ${MAX_QUERY_CHARS} characters`
|
|
2949
|
+
);
|
|
2950
|
+
if (options.mode !== void 0 && options.mode !== "hybrid" && options.mode !== "semantic" && options.mode !== "fulltext")
|
|
2951
|
+
throw new KnowledgeError(400, "invalid_mode", "Invalid knowledge search mode");
|
|
2952
|
+
if (options.limit !== void 0 && (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 50))
|
|
2953
|
+
throw new KnowledgeError(
|
|
2954
|
+
400,
|
|
2955
|
+
"invalid_limit",
|
|
2956
|
+
"Search limit must be an integer from 1 through 50"
|
|
2957
|
+
);
|
|
2958
|
+
if (options.matchThreshold !== void 0 && (!Number.isFinite(options.matchThreshold) || options.matchThreshold < 0 || options.matchThreshold > 1))
|
|
2959
|
+
throw new KnowledgeError(
|
|
2960
|
+
400,
|
|
2961
|
+
"invalid_match_threshold",
|
|
2962
|
+
"Match threshold must be between 0 and 1"
|
|
2963
|
+
);
|
|
2964
|
+
if (options.queryRewrite !== void 0 && typeof options.queryRewrite !== "boolean")
|
|
2965
|
+
throw new KnowledgeError(
|
|
2966
|
+
400,
|
|
2967
|
+
"invalid_query_rewrite",
|
|
2968
|
+
"queryRewrite must be a boolean when provided"
|
|
2969
|
+
);
|
|
2970
|
+
const folder = options.folder === void 0 ? void 0 : normalizeKnowledgeFolder(options.folder);
|
|
2971
|
+
return await request(env, "/api/knowledge/search", {
|
|
2972
|
+
method: "POST",
|
|
2973
|
+
headers: { "content-type": "application/json" },
|
|
2974
|
+
body: JSON.stringify({
|
|
2975
|
+
query: normalizedQuery,
|
|
2976
|
+
mode: options.mode ?? "hybrid",
|
|
2977
|
+
limit: options.limit ?? 10,
|
|
2978
|
+
queryRewrite: options.queryRewrite ?? false,
|
|
2979
|
+
...options.matchThreshold !== void 0 && {
|
|
2980
|
+
matchThreshold: options.matchThreshold
|
|
2981
|
+
},
|
|
2982
|
+
...folder && { folder }
|
|
2983
|
+
})
|
|
2984
|
+
});
|
|
2985
|
+
}
|
|
2986
|
+
async function request(env, path, init = {}) {
|
|
2987
|
+
const headers = new Headers(init.headers);
|
|
2988
|
+
appendAppIdentity(headers, env);
|
|
2989
|
+
const response = await apiWorkerFetch(env, path, { ...init, headers });
|
|
2990
|
+
const text = await response.text();
|
|
2991
|
+
let data;
|
|
2992
|
+
try {
|
|
2993
|
+
data = text ? JSON.parse(text) : void 0;
|
|
2994
|
+
} catch {
|
|
2995
|
+
data = void 0;
|
|
2996
|
+
}
|
|
2997
|
+
if (!response.ok) {
|
|
2998
|
+
const body = data && typeof data === "object" ? data : {};
|
|
2999
|
+
throw new KnowledgeError(
|
|
3000
|
+
response.status,
|
|
3001
|
+
typeof body.code === "string" ? body.code : "knowledge_request_failed",
|
|
3002
|
+
typeof body.error === "string" ? body.error : "Knowledge request failed"
|
|
3003
|
+
);
|
|
3004
|
+
}
|
|
3005
|
+
return data;
|
|
3006
|
+
}
|
|
3007
|
+
|
|
2754
3008
|
// src/server/utils/d1-migrations.ts
|
|
2755
3009
|
var META_TABLE = "_dpc_migrations";
|
|
2756
3010
|
var META_BOOTSTRAP_SQL = `CREATE TABLE IF NOT EXISTS ${META_TABLE} (idx INTEGER PRIMARY KEY, applied_at TEXT NOT NULL)`;
|
|
@@ -2796,13 +3050,18 @@ function decodeRoomIdentityHeader(value) {
|
|
|
2796
3050
|
}
|
|
2797
3051
|
|
|
2798
3052
|
// src/server/rooms/base-room.ts
|
|
2799
|
-
function connectionAttachmentFromRequest(
|
|
3053
|
+
function connectionAttachmentFromRequest(request2) {
|
|
3054
|
+
if (!request2.headers.has("x-user-id") && new URL(request2.url).searchParams.has("userId")) {
|
|
3055
|
+
console.warn(
|
|
3056
|
+
"[BaseRoom] Connection carries identity in the URL (?userId=) but no x-user-id header, so this user joins as anonymous. The app worker's room proxy predates 0.19 or forwards the client's URL unstripped: forward with authenticatedRoomRequest() from deepspace/worker."
|
|
3057
|
+
);
|
|
3058
|
+
}
|
|
2800
3059
|
return {
|
|
2801
|
-
userId: decodeRoomIdentityHeader(
|
|
2802
|
-
userName: decodeRoomIdentityHeader(
|
|
2803
|
-
userEmail: decodeRoomIdentityHeader(
|
|
2804
|
-
userImageUrl: decodeRoomIdentityHeader(
|
|
2805
|
-
role: decodeRoomIdentityHeader(
|
|
3060
|
+
userId: decodeRoomIdentityHeader(request2.headers.get("x-user-id")) || `${ANONYMOUS_USER_ID_PREFIX}${crypto.randomUUID()}`,
|
|
3061
|
+
userName: decodeRoomIdentityHeader(request2.headers.get("x-user-name")) || "Anonymous",
|
|
3062
|
+
userEmail: decodeRoomIdentityHeader(request2.headers.get("x-user-email")) || "",
|
|
3063
|
+
userImageUrl: decodeRoomIdentityHeader(request2.headers.get("x-user-image-url")),
|
|
3064
|
+
role: decodeRoomIdentityHeader(request2.headers.get("x-user-role"))
|
|
2806
3065
|
};
|
|
2807
3066
|
}
|
|
2808
3067
|
var BaseRoom = class {
|
|
@@ -2818,15 +3077,15 @@ var BaseRoom = class {
|
|
|
2818
3077
|
// ==========================================================================
|
|
2819
3078
|
// HTTP Entry Point
|
|
2820
3079
|
// ==========================================================================
|
|
2821
|
-
async fetch(
|
|
2822
|
-
const url = new URL(
|
|
2823
|
-
const internal = await this.handleInternalRequest(
|
|
3080
|
+
async fetch(request2) {
|
|
3081
|
+
const url = new URL(request2.url);
|
|
3082
|
+
const internal = await this.handleInternalRequest(request2, url);
|
|
2824
3083
|
if (internal) return internal;
|
|
2825
|
-
if (
|
|
2826
|
-
return this.handleWebSocketUpgrade(
|
|
3084
|
+
if (request2.headers.get("Upgrade") === "websocket") {
|
|
3085
|
+
return this.handleWebSocketUpgrade(request2, url);
|
|
2827
3086
|
}
|
|
2828
3087
|
if (this.onRequest) {
|
|
2829
|
-
return this.onRequest(
|
|
3088
|
+
return this.onRequest(request2);
|
|
2830
3089
|
}
|
|
2831
3090
|
return new Response("Not Found", { status: 404 });
|
|
2832
3091
|
}
|
|
@@ -2847,11 +3106,11 @@ var BaseRoom = class {
|
|
|
2847
3106
|
* See the security note on `fetch()`: this path is only reachable via DO
|
|
2848
3107
|
* stub fetch from the app worker, never from the public internet.
|
|
2849
3108
|
*/
|
|
2850
|
-
async handleInternalRequest(
|
|
2851
|
-
if (
|
|
3109
|
+
async handleInternalRequest(request2, url) {
|
|
3110
|
+
if (request2.method === "POST" && url.pathname === "/internal/disconnect-sockets") {
|
|
2852
3111
|
let options;
|
|
2853
3112
|
try {
|
|
2854
|
-
const text = await
|
|
3113
|
+
const text = await request2.text();
|
|
2855
3114
|
if (text) options = JSON.parse(text);
|
|
2856
3115
|
} catch {
|
|
2857
3116
|
}
|
|
@@ -2863,11 +3122,11 @@ var BaseRoom = class {
|
|
|
2863
3122
|
// ==========================================================================
|
|
2864
3123
|
// WebSocket Connection
|
|
2865
3124
|
// ==========================================================================
|
|
2866
|
-
async handleWebSocketUpgrade(
|
|
3125
|
+
async handleWebSocketUpgrade(request2, _url) {
|
|
2867
3126
|
const pair = new WebSocketPair();
|
|
2868
3127
|
const [client, server] = Object.values(pair);
|
|
2869
3128
|
this.state.acceptWebSocket(server);
|
|
2870
|
-
const attachment = connectionAttachmentFromRequest(
|
|
3129
|
+
const attachment = connectionAttachmentFromRequest(request2);
|
|
2871
3130
|
const augmented = await this.onConnect(server, attachment) ?? attachment;
|
|
2872
3131
|
server.serializeAttachment(augmented);
|
|
2873
3132
|
return new Response(null, { status: 101, webSocket: client });
|
|
@@ -3103,7 +3362,8 @@ function dataToColumnValues(data, columns) {
|
|
|
3103
3362
|
return values;
|
|
3104
3363
|
}
|
|
3105
3364
|
function coerceValue(value, storage, interpretation) {
|
|
3106
|
-
if (value === null || value === void 0
|
|
3365
|
+
if (value === null || value === void 0) return null;
|
|
3366
|
+
if (value === "") return storage === "text" ? "" : null;
|
|
3107
3367
|
if (storage === "text") {
|
|
3108
3368
|
if (interpretation.kind === "json") {
|
|
3109
3369
|
if (typeof value === "string") return value;
|
|
@@ -3137,7 +3397,7 @@ function coerceValue(value, storage, interpretation) {
|
|
|
3137
3397
|
}
|
|
3138
3398
|
if (interpretation.kind === "date" || interpretation.kind === "datetime") {
|
|
3139
3399
|
const d = new Date(s);
|
|
3140
|
-
if (!isNaN(d.getTime())) return
|
|
3400
|
+
if (!isNaN(d.getTime())) return epochSeconds(d);
|
|
3141
3401
|
return s;
|
|
3142
3402
|
}
|
|
3143
3403
|
const cleaned = s.replace(/[$€£¥,\s]/g, "");
|
|
@@ -3145,6 +3405,9 @@ function coerceValue(value, storage, interpretation) {
|
|
|
3145
3405
|
if (!isNaN(n) && isFinite(n)) return n;
|
|
3146
3406
|
return s;
|
|
3147
3407
|
}
|
|
3408
|
+
function epochSeconds(date = /* @__PURE__ */ new Date()) {
|
|
3409
|
+
return Math.floor(date.getTime() / 1e3);
|
|
3410
|
+
}
|
|
3148
3411
|
function buildTableSelect(collectionName, columns) {
|
|
3149
3412
|
const tbl = collectionTableName(collectionName);
|
|
3150
3413
|
const parts = ["_row_id", "_created_by", "_created_at", "_updated_at"];
|
|
@@ -3579,6 +3842,9 @@ var serverBuild = {
|
|
|
3579
3842
|
function getResolvedColumns(schema) {
|
|
3580
3843
|
return (schema.columns ?? []).map(resolveColumn);
|
|
3581
3844
|
}
|
|
3845
|
+
function triggerTimestamp(col) {
|
|
3846
|
+
return col.storage === "number" ? epochSeconds() : (/* @__PURE__ */ new Date()).toISOString();
|
|
3847
|
+
}
|
|
3582
3848
|
function systemManagedColumnError(column, isUpdate) {
|
|
3583
3849
|
const roleHint = column === "role" ? ", or the admin-gated useUsers().setRole() to change a role" : "";
|
|
3584
3850
|
const remedy = isUpdate ? `Re-sending the current value of '${column}' unchanged is accepted and ignored.` : `A users row is created by registration, so no value for '${column}' is accepted here \u2014 omit it.`;
|
|
@@ -3697,7 +3963,7 @@ function putRecord(ctx, collection, recordId, data, userId, userRole, skipUserRb
|
|
|
3697
3963
|
if (!isUpdate) {
|
|
3698
3964
|
if (mergedData[triggerField] !== void 0 && mergedData[triggerField] !== null) {
|
|
3699
3965
|
if (triggerValue === void 0 || mergedData[triggerField] === triggerValue) {
|
|
3700
|
-
mergedData[col.name] = (
|
|
3966
|
+
mergedData[col.name] = triggerTimestamp(col);
|
|
3701
3967
|
}
|
|
3702
3968
|
}
|
|
3703
3969
|
} else {
|
|
@@ -3705,7 +3971,7 @@ function putRecord(ctx, collection, recordId, data, userId, userRole, skipUserRb
|
|
|
3705
3971
|
const newVal = mergedData[triggerField];
|
|
3706
3972
|
if (newVal !== oldVal) {
|
|
3707
3973
|
if (triggerValue === void 0 || newVal === triggerValue) {
|
|
3708
|
-
mergedData[col.name] = (
|
|
3974
|
+
mergedData[col.name] = triggerTimestamp(col);
|
|
3709
3975
|
}
|
|
3710
3976
|
}
|
|
3711
3977
|
}
|
|
@@ -3750,27 +4016,27 @@ function putRecord(ctx, collection, recordId, data, userId, userRole, skipUserRb
|
|
|
3750
4016
|
if (SYSTEM_ASSIGNED_COLUMNS.has(col.name) && Object.hasOwn(data, col.name) && coerceValue(data[col.name], col.storage, col.interpretation) !== coerceValue(current, col.storage, col.interpretation)) {
|
|
3751
4017
|
return { success: false, error: systemManagedColumnError(col.name, isUpdate) };
|
|
3752
4018
|
}
|
|
3753
|
-
if (current !== void 0)
|
|
4019
|
+
if (current !== void 0)
|
|
4020
|
+
finalData[col.name] = current;
|
|
3754
4021
|
else delete finalData[col.name];
|
|
3755
4022
|
}
|
|
3756
4023
|
}
|
|
3757
4024
|
const colValues = dataToColumnValues(finalData, columns);
|
|
3758
4025
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3759
4026
|
const tbl = collectionTableName(collection);
|
|
3760
|
-
if (
|
|
3761
|
-
const
|
|
3762
|
-
const
|
|
3763
|
-
return
|
|
3764
|
-
});
|
|
3765
|
-
const uniqueParams = schema.uniqueOn.map((fieldName) => {
|
|
3766
|
-
const val = finalData[fieldName];
|
|
3767
|
-
return val !== void 0 ? val : null;
|
|
4027
|
+
if (schema.uniqueOn && schema.uniqueOn.length > 0) {
|
|
4028
|
+
const uniqueColumns = schema.uniqueOn.map((fieldName) => {
|
|
4029
|
+
const id = columns.find((column) => column.name === fieldName)?.id ?? columnId(fieldName);
|
|
4030
|
+
return { id, value: colValues[id] ?? null };
|
|
3768
4031
|
});
|
|
3769
|
-
const
|
|
3770
|
-
|
|
4032
|
+
const uniqueWhere = uniqueColumns.map(({ id }) => `"${id}" = ?`);
|
|
4033
|
+
const uniqueParams = uniqueColumns.map(({ value }) => value);
|
|
4034
|
+
if (isUpdate) uniqueParams.push(recordId);
|
|
4035
|
+
const conflict = ctx.sql.exec(
|
|
4036
|
+
`SELECT _row_id FROM "${tbl}" WHERE ${uniqueWhere.join(" AND ")}${isUpdate ? " AND _row_id <> ?" : ""} LIMIT 1`,
|
|
3771
4037
|
...uniqueParams
|
|
3772
4038
|
);
|
|
3773
|
-
if (
|
|
4039
|
+
if (conflict.toArray().length > 0) {
|
|
3774
4040
|
const fields = schema.uniqueOn.map((f) => `${f}=${finalData[f] ?? "null"}`).join(", ");
|
|
3775
4041
|
return {
|
|
3776
4042
|
success: false,
|
|
@@ -3837,8 +4103,21 @@ function deleteRecord(ctx, collection, recordId, userId, userRole, skipUserRbac
|
|
|
3837
4103
|
return { success: true, data: { deleted: true } };
|
|
3838
4104
|
}
|
|
3839
4105
|
function refuseUnknownWhere(collection, schema, where) {
|
|
3840
|
-
const columns = (schema
|
|
4106
|
+
const columns = (schema?.columns ?? []).map(resolveColumn);
|
|
3841
4107
|
const filterable = ["recordId", "createdBy", ...columns.map((c) => c.name)];
|
|
4108
|
+
if (typeof where !== "object" || where === null || Array.isArray(where)) {
|
|
4109
|
+
const got = where === null ? "null" : Array.isArray(where) ? "an array" : typeof where;
|
|
4110
|
+
return {
|
|
4111
|
+
success: false,
|
|
4112
|
+
error: `where for "${collection}" must be an object of field=value pairs, not ${got} \u2014 filterable fields: ${filterable.join(", ")}`
|
|
4113
|
+
};
|
|
4114
|
+
}
|
|
4115
|
+
if (!schema && Object.keys(where).length > 0) {
|
|
4116
|
+
return {
|
|
4117
|
+
success: false,
|
|
4118
|
+
error: `"${collection}" has no schema, so it has no filterable fields \u2014 query it without \`where\``
|
|
4119
|
+
};
|
|
4120
|
+
}
|
|
3842
4121
|
const unknown = Object.keys(where).filter(
|
|
3843
4122
|
(key) => key !== "recordId" && key !== "createdBy" && !columns.some((c) => c.id === columnId(key) || c.name === key)
|
|
3844
4123
|
);
|
|
@@ -3867,11 +4146,14 @@ function deleteWhere(ctx, collection, where, limit, userId, userRole, skipUserRb
|
|
|
3867
4146
|
error: `records.deleteWhere: collection "${collection}" has no schema, so it has no filterable fields \u2014 delete its rows by recordId with records.delete`
|
|
3868
4147
|
};
|
|
3869
4148
|
}
|
|
3870
|
-
if (!where || typeof where !== "object" || Array.isArray(where) || Object.keys(where).length === 0) {
|
|
3871
|
-
return { success: false, error: "Missing required param: where (must match at least one field)" };
|
|
3872
|
-
}
|
|
3873
4149
|
const whereRefusal = refuseUnknownWhere(collection, schema, where);
|
|
3874
4150
|
if (whereRefusal) return whereRefusal;
|
|
4151
|
+
if (Object.keys(where).length === 0) {
|
|
4152
|
+
return {
|
|
4153
|
+
success: false,
|
|
4154
|
+
error: "Missing required param: where (must match at least one field)"
|
|
4155
|
+
};
|
|
4156
|
+
}
|
|
3875
4157
|
if (limit !== void 0 && !(typeof limit === "number" && Number.isFinite(limit))) {
|
|
3876
4158
|
return { success: false, error: "limit must be a finite number" };
|
|
3877
4159
|
}
|
|
@@ -3879,32 +4161,27 @@ function deleteWhere(ctx, collection, where, limit, userId, userRole, skipUserRb
|
|
|
3879
4161
|
Math.max(1, Math.floor(limit ?? DEFAULT_DELETE_WHERE_LIMIT)),
|
|
3880
4162
|
MAX_DELETE_WHERE_LIMIT
|
|
3881
4163
|
);
|
|
3882
|
-
const scan = { capped: false };
|
|
3883
4164
|
const matched = executeQuery(
|
|
3884
4165
|
ctx,
|
|
3885
4166
|
{ collection, where, limit: page },
|
|
3886
4167
|
userId,
|
|
3887
4168
|
userRole,
|
|
3888
|
-
|
|
3889
|
-
scan
|
|
4169
|
+
true
|
|
3890
4170
|
);
|
|
3891
|
-
if (
|
|
3892
|
-
return {
|
|
3893
|
-
success: false,
|
|
3894
|
-
error: `records.deleteWhere scanned ${MAX_FILTERED_SCAN_ROWS} rows without filling a page of readable matches in "${collection}" \u2014 narrow \`where\` (or delete by recordId) and retry`
|
|
3895
|
-
};
|
|
3896
|
-
}
|
|
3897
|
-
if (!skipUserRbac && schema) {
|
|
4171
|
+
if (!skipUserRbac) {
|
|
3898
4172
|
const permCtx = ctx.getPermissionContext();
|
|
3899
4173
|
for (const record of matched) {
|
|
3900
4174
|
if (!canDelete(schema, userRole, record, userId, permCtx)) {
|
|
3901
|
-
return {
|
|
4175
|
+
return {
|
|
4176
|
+
success: false,
|
|
4177
|
+
error: `DELETE DENIED: role=${userRole}, collection=${collection}`
|
|
4178
|
+
};
|
|
3902
4179
|
}
|
|
3903
4180
|
}
|
|
3904
4181
|
}
|
|
3905
4182
|
let deleted = 0;
|
|
3906
4183
|
for (const record of matched) {
|
|
3907
|
-
if (deleteRecord(ctx, collection, record.recordId, userId, userRole,
|
|
4184
|
+
if (deleteRecord(ctx, collection, record.recordId, userId, userRole, true).success) {
|
|
3908
4185
|
deleted++;
|
|
3909
4186
|
}
|
|
3910
4187
|
}
|
|
@@ -4603,17 +4880,17 @@ async function registerUser(sql, userId, name, email, imageUrl, isAdmin, default
|
|
|
4603
4880
|
);
|
|
4604
4881
|
return { id: userId, email, name, imageUrl, role, createdAt: now, lastSeenAt: now };
|
|
4605
4882
|
}
|
|
4606
|
-
function rosterFor(ctx, schema, records, publicRoster,
|
|
4607
|
-
if (isAnonymousUserId(
|
|
4608
|
-
if (
|
|
4883
|
+
function rosterFor(ctx, schema, records, publicRoster, userId, role) {
|
|
4884
|
+
if (!userId || isAnonymousUserId(userId)) return [];
|
|
4885
|
+
if (role === "admin") {
|
|
4609
4886
|
const permissionContext = ctx.getPermissionContext();
|
|
4610
|
-
return records.filter((record) => canRead(schema, "admin", record,
|
|
4887
|
+
return records.filter((record) => canRead(schema, "admin", record, userId, permissionContext)).map((record) => ({ id: record.recordId, ...record.data }));
|
|
4611
4888
|
}
|
|
4612
|
-
if (!getRolePermissions(schema,
|
|
4889
|
+
if (!getRolePermissions(schema, role).read) return [];
|
|
4613
4890
|
if (schema.roster === "read-policy") {
|
|
4614
4891
|
const permissionContext = ctx.getPermissionContext();
|
|
4615
4892
|
const readable = new Set(
|
|
4616
|
-
records.filter((record) => canRead(schema,
|
|
4893
|
+
records.filter((record) => canRead(schema, role, record, userId, permissionContext)).map((record) => record.recordId)
|
|
4617
4894
|
);
|
|
4618
4895
|
return publicRoster.filter((user) => readable.has(user.id));
|
|
4619
4896
|
}
|
|
@@ -4624,17 +4901,18 @@ function publicIdentities(records) {
|
|
|
4624
4901
|
id: recordId,
|
|
4625
4902
|
name: data.name,
|
|
4626
4903
|
...data.imageUrl ? { imageUrl: data.imageUrl } : {},
|
|
4627
|
-
role: data.role
|
|
4904
|
+
role: data.role,
|
|
4905
|
+
lastSeenAt: data.lastSeenAt
|
|
4628
4906
|
}));
|
|
4629
4907
|
}
|
|
4630
|
-
function
|
|
4908
|
+
function rosterForCaller(ctx, userId, role) {
|
|
4631
4909
|
const schema = ctx.schemaRegistry.get("users");
|
|
4632
|
-
if (!schema)
|
|
4633
|
-
ctx.send(ws, { type: MSG.USER_LIST, payload: { users: [] } });
|
|
4634
|
-
return;
|
|
4635
|
-
}
|
|
4910
|
+
if (!schema) return [];
|
|
4636
4911
|
const records = getAllUserRecords(ctx.sql, ctx.schemaRegistry);
|
|
4637
|
-
|
|
4912
|
+
return rosterFor(ctx, schema, records, publicIdentities(records), userId, role);
|
|
4913
|
+
}
|
|
4914
|
+
function handleUserList(ctx, ws, attachment) {
|
|
4915
|
+
const users = rosterForCaller(ctx, attachment.userId, attachment.role);
|
|
4638
4916
|
ctx.send(ws, { type: MSG.USER_LIST, payload: { users } });
|
|
4639
4917
|
}
|
|
4640
4918
|
function broadcastUserList(ctx, except) {
|
|
@@ -4649,7 +4927,9 @@ function broadcastUserList(ctx, except) {
|
|
|
4649
4927
|
if (!attachment) continue;
|
|
4650
4928
|
ctx.send(other, {
|
|
4651
4929
|
type: MSG.USER_LIST,
|
|
4652
|
-
payload: {
|
|
4930
|
+
payload: {
|
|
4931
|
+
users: rosterFor(ctx, schema, records, publicRoster, attachment.userId, attachment.role)
|
|
4932
|
+
}
|
|
4653
4933
|
});
|
|
4654
4934
|
}
|
|
4655
4935
|
}
|
|
@@ -4702,8 +4982,8 @@ var CORS_HEADERS3 = {
|
|
|
4702
4982
|
"Access-Control-Allow-Headers": "Content-Type, X-User-Id, X-App-Action",
|
|
4703
4983
|
"Content-Type": "application/json"
|
|
4704
4984
|
};
|
|
4705
|
-
async function handleToolsRequest(ctx,
|
|
4706
|
-
if (
|
|
4985
|
+
async function handleToolsRequest(ctx, request2, path) {
|
|
4986
|
+
if (request2.method === "OPTIONS") {
|
|
4707
4987
|
return new Response(null, { headers: CORS_HEADERS3 });
|
|
4708
4988
|
}
|
|
4709
4989
|
if (path === "tools/list") {
|
|
@@ -4720,15 +5000,15 @@ async function handleToolsRequest(ctx, request, path) {
|
|
|
4720
5000
|
}
|
|
4721
5001
|
return Response.json({ tool: tool2 }, { headers: CORS_HEADERS3 });
|
|
4722
5002
|
}
|
|
4723
|
-
if (path.startsWith("tools/execute") &&
|
|
4724
|
-
return handleToolExecute(ctx,
|
|
5003
|
+
if (path.startsWith("tools/execute") && request2.method === "POST") {
|
|
5004
|
+
return handleToolExecute(ctx, request2);
|
|
4725
5005
|
}
|
|
4726
5006
|
return Response.json({ error: "Not found" }, { status: 404, headers: CORS_HEADERS3 });
|
|
4727
5007
|
}
|
|
4728
|
-
async function handleToolExecute(ctx,
|
|
5008
|
+
async function handleToolExecute(ctx, request2) {
|
|
4729
5009
|
let body;
|
|
4730
5010
|
try {
|
|
4731
|
-
body = await
|
|
5011
|
+
body = await request2.json();
|
|
4732
5012
|
} catch {
|
|
4733
5013
|
return Response.json(
|
|
4734
5014
|
{ success: false, error: "Invalid JSON body" },
|
|
@@ -4742,8 +5022,8 @@ async function handleToolExecute(ctx, request) {
|
|
|
4742
5022
|
{ status: 400, headers: CORS_HEADERS3 }
|
|
4743
5023
|
);
|
|
4744
5024
|
}
|
|
4745
|
-
const userId =
|
|
4746
|
-
const isAppAction =
|
|
5025
|
+
const userId = request2.headers.get("x-user-id") ?? "";
|
|
5026
|
+
const isAppAction = request2.headers.get("x-app-action")?.toLowerCase() === "true";
|
|
4747
5027
|
let userRole = "viewer";
|
|
4748
5028
|
if (userId && ctx.ownerUserId && userId === ctx.ownerUserId) {
|
|
4749
5029
|
userRole = "admin";
|
|
@@ -4778,17 +5058,11 @@ async function executeTool(ctx, toolName, params, userId, userRole, skipUserRbac
|
|
|
4778
5058
|
if (!collection) return { success: false, error: "Missing required param: collection" };
|
|
4779
5059
|
const resolved = resolveCollection(ctx, collection);
|
|
4780
5060
|
if (!resolved.ok) return { success: false, error: resolved.error };
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
if (!resolved.schema) {
|
|
4784
|
-
return {
|
|
4785
|
-
success: false,
|
|
4786
|
-
error: `records.query: collection "${collection}" has no schema, so it has no filterable fields \u2014 query it without \`where\``
|
|
4787
|
-
};
|
|
4788
|
-
}
|
|
4789
|
-
const refusal = refuseUnknownWhere(collection, resolved.schema, where);
|
|
5061
|
+
if (params.where !== void 0) {
|
|
5062
|
+
const refusal = refuseUnknownWhere(collection, resolved.schema, params.where);
|
|
4790
5063
|
if (refusal) return refusal;
|
|
4791
5064
|
}
|
|
5065
|
+
const where = params.where;
|
|
4792
5066
|
const query = {
|
|
4793
5067
|
collection,
|
|
4794
5068
|
where,
|
|
@@ -4897,8 +5171,16 @@ async function executeTool(ctx, toolName, params, userId, userRole, skipUserRbac
|
|
|
4897
5171
|
if (!user) return { success: false, error: "User not found" };
|
|
4898
5172
|
return { success: true, data: { user } };
|
|
4899
5173
|
}
|
|
5174
|
+
// The same roster the socket's `user.list` returns, for the same caller.
|
|
5175
|
+
// Reading the users table directly here handed any member every peer's
|
|
5176
|
+
// email regardless of the room's users policy, while their own socket
|
|
5177
|
+
// roster and `useQuery('users')` obeyed it.
|
|
5178
|
+
//
|
|
5179
|
+
// An app action (`X-App-Action`) is the app's own server-side code, which
|
|
5180
|
+
// is already the trust boundary and reads every other collection RBAC-off;
|
|
5181
|
+
// it keeps the full rows.
|
|
4900
5182
|
case "user.list": {
|
|
4901
|
-
const users = getAllUsers(ctx.sql, ctx.schemaRegistry);
|
|
5183
|
+
const users = skipUserRbac ? getAllUsers(ctx.sql, ctx.schemaRegistry) : rosterForCaller(ctx, userId, userRole);
|
|
4902
5184
|
return { success: true, data: { users } };
|
|
4903
5185
|
}
|
|
4904
5186
|
default:
|
|
@@ -5033,25 +5315,25 @@ var CORS_HEADERS4 = {
|
|
|
5033
5315
|
"Access-Control-Allow-Headers": "Content-Type",
|
|
5034
5316
|
"Content-Type": "application/json"
|
|
5035
5317
|
};
|
|
5036
|
-
async function handleApiRequest(ctx,
|
|
5037
|
-
if (
|
|
5318
|
+
async function handleApiRequest(ctx, request2, url) {
|
|
5319
|
+
if (request2.method === "OPTIONS") {
|
|
5038
5320
|
return new Response(null, { headers: CORS_HEADERS4 });
|
|
5039
5321
|
}
|
|
5040
5322
|
const path = url.pathname.replace("/api/", "");
|
|
5041
5323
|
if (path.startsWith("tools/")) {
|
|
5042
|
-
return handleToolsRequest(ctx,
|
|
5324
|
+
return handleToolsRequest(ctx, request2, path);
|
|
5043
5325
|
}
|
|
5044
5326
|
if (path === "debug/status") {
|
|
5045
5327
|
return handleDebugStatus(ctx);
|
|
5046
5328
|
}
|
|
5047
5329
|
if (path === "debug/sql") {
|
|
5048
|
-
return handleDebugSql(ctx,
|
|
5330
|
+
return handleDebugSql(ctx, request2, url);
|
|
5049
5331
|
}
|
|
5050
5332
|
if (path === "debug/user-role") {
|
|
5051
5333
|
return handleDebugUserRole(ctx, url);
|
|
5052
5334
|
}
|
|
5053
|
-
if (path === "debug/set-role" &&
|
|
5054
|
-
return handleDebugSetRole(ctx,
|
|
5335
|
+
if (path === "debug/set-role" && request2.method === "POST") {
|
|
5336
|
+
return handleDebugSetRole(ctx, request2);
|
|
5055
5337
|
}
|
|
5056
5338
|
if (path.startsWith("debug/records/")) {
|
|
5057
5339
|
return handleDebugRecords(ctx, path);
|
|
@@ -5082,8 +5364,8 @@ function handleDebugUserRole(ctx, url) {
|
|
|
5082
5364
|
role: user.role
|
|
5083
5365
|
}, { headers: CORS_HEADERS4 });
|
|
5084
5366
|
}
|
|
5085
|
-
async function handleDebugSetRole(ctx,
|
|
5086
|
-
const body = await
|
|
5367
|
+
async function handleDebugSetRole(ctx, request2) {
|
|
5368
|
+
const body = await request2.json();
|
|
5087
5369
|
if (!body.role) {
|
|
5088
5370
|
return Response.json({ error: "role is required" }, { status: 400, headers: CORS_HEADERS4 });
|
|
5089
5371
|
}
|
|
@@ -5191,11 +5473,11 @@ function handleDebugQuery(ctx, url) {
|
|
|
5191
5473
|
records
|
|
5192
5474
|
}, { headers: CORS_HEADERS4 });
|
|
5193
5475
|
}
|
|
5194
|
-
async function handleDebugSql(ctx,
|
|
5476
|
+
async function handleDebugSql(ctx, request2, url) {
|
|
5195
5477
|
let sql;
|
|
5196
5478
|
let params = [];
|
|
5197
|
-
if (
|
|
5198
|
-
const body = await
|
|
5479
|
+
if (request2.method === "POST") {
|
|
5480
|
+
const body = await request2.json();
|
|
5199
5481
|
sql = body.sql;
|
|
5200
5482
|
params = body.params ?? [];
|
|
5201
5483
|
} else {
|
|
@@ -5315,10 +5597,10 @@ var RecordRoom = class extends BaseRoom {
|
|
|
5315
5597
|
// ============================================================================
|
|
5316
5598
|
// HTTP Entry Point (overrides BaseRoom.fetch)
|
|
5317
5599
|
// ============================================================================
|
|
5318
|
-
async fetch(
|
|
5319
|
-
const url = new URL(
|
|
5600
|
+
async fetch(request2) {
|
|
5601
|
+
const url = new URL(request2.url);
|
|
5320
5602
|
if (url.pathname.startsWith("/internal/")) {
|
|
5321
|
-
const internal = await this.handleInternalRequest(
|
|
5603
|
+
const internal = await this.handleInternalRequest(request2, url);
|
|
5322
5604
|
if (internal) return internal;
|
|
5323
5605
|
}
|
|
5324
5606
|
const fetchStart = Date.now();
|
|
@@ -5331,11 +5613,11 @@ var RecordRoom = class extends BaseRoom {
|
|
|
5331
5613
|
if (url.pathname.startsWith("/api/debug/") && !this.debugRoutesEnabled) {
|
|
5332
5614
|
return new Response("Not Found", { status: 404 });
|
|
5333
5615
|
}
|
|
5334
|
-
return handleApiRequest(this.createHandlerContext(),
|
|
5616
|
+
return handleApiRequest(this.createHandlerContext(), request2, url);
|
|
5335
5617
|
}
|
|
5336
|
-
if (
|
|
5618
|
+
if (request2.headers.get("Upgrade") === "websocket") {
|
|
5337
5619
|
this._fetchTiming = { fetchStart, isColdStart, needsInit, initMs };
|
|
5338
|
-
return super.fetch(
|
|
5620
|
+
return super.fetch(request2);
|
|
5339
5621
|
}
|
|
5340
5622
|
return new Response("Not Found", { status: 404 });
|
|
5341
5623
|
}
|
|
@@ -5917,9 +6199,9 @@ var CanvasRoom = class extends BaseRoom {
|
|
|
5917
6199
|
// --------------------------------------------------------------------------
|
|
5918
6200
|
// BaseRoom Lifecycle
|
|
5919
6201
|
// --------------------------------------------------------------------------
|
|
5920
|
-
async fetch(
|
|
6202
|
+
async fetch(request2) {
|
|
5921
6203
|
this.ensureInitialized();
|
|
5922
|
-
return super.fetch(
|
|
6204
|
+
return super.fetch(request2);
|
|
5923
6205
|
}
|
|
5924
6206
|
onConnect(ws, user) {
|
|
5925
6207
|
this.ensureInitialized();
|
|
@@ -6398,6 +6680,7 @@ function cronMatches(parsed, timezone, date) {
|
|
|
6398
6680
|
}
|
|
6399
6681
|
|
|
6400
6682
|
// src/server/rooms/cron-room.ts
|
|
6683
|
+
var CRON_TASKS_INTERNAL_PATH = "/internal/cron/tasks";
|
|
6401
6684
|
var CRON_WRITE_TYPES = /* @__PURE__ */ new Set([
|
|
6402
6685
|
MSG.CRON_TRIGGER,
|
|
6403
6686
|
MSG.CRON_PAUSE,
|
|
@@ -6472,9 +6755,15 @@ var CronRoom = class extends BaseRoom {
|
|
|
6472
6755
|
// ==========================================================================
|
|
6473
6756
|
// BaseRoom Lifecycle
|
|
6474
6757
|
// ==========================================================================
|
|
6475
|
-
async fetch(
|
|
6758
|
+
async fetch(request2) {
|
|
6476
6759
|
this.ensureInitialized();
|
|
6477
|
-
return super.fetch(
|
|
6760
|
+
return super.fetch(request2);
|
|
6761
|
+
}
|
|
6762
|
+
async handleInternalRequest(request2, url) {
|
|
6763
|
+
if (request2.method === "GET" && url.pathname === CRON_TASKS_INTERNAL_PATH) {
|
|
6764
|
+
return Response.json({ tasks: this.getTaskStates() });
|
|
6765
|
+
}
|
|
6766
|
+
return super.handleInternalRequest(request2, url);
|
|
6478
6767
|
}
|
|
6479
6768
|
onConnect(ws, user) {
|
|
6480
6769
|
this.ensureInitialized();
|
|
@@ -6622,15 +6911,22 @@ var CronRoom = class extends BaseRoom {
|
|
|
6622
6911
|
const start = Date.now();
|
|
6623
6912
|
let success = true;
|
|
6624
6913
|
let error;
|
|
6914
|
+
let thrown;
|
|
6625
6915
|
try {
|
|
6626
6916
|
await this.onTask(taskName);
|
|
6627
6917
|
} catch (e) {
|
|
6628
6918
|
success = false;
|
|
6919
|
+
thrown = e;
|
|
6629
6920
|
error = e instanceof Error ? e.message : String(e);
|
|
6630
|
-
console.error(`[CronRoom] Task "${taskName}" failed:`, e);
|
|
6631
6921
|
}
|
|
6632
6922
|
const durationMs = Date.now() - start;
|
|
6633
6923
|
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6924
|
+
if (success) {
|
|
6925
|
+
console.log(`[cron] ${taskName} ok ${durationMs}ms`);
|
|
6926
|
+
} else {
|
|
6927
|
+
const stack = thrown instanceof Error ? thrown.stack : void 0;
|
|
6928
|
+
console.error(`[cron] ${taskName} failed ${durationMs}ms: ${error}`, ...stack ? [stack] : []);
|
|
6929
|
+
}
|
|
6634
6930
|
this.sql.exec(
|
|
6635
6931
|
`INSERT INTO cron_history (task_name, started_at, completed_at, success, duration_ms, error, created_at)
|
|
6636
6932
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -6739,6 +7035,19 @@ var CronRoom = class extends BaseRoom {
|
|
|
6739
7035
|
);
|
|
6740
7036
|
}
|
|
6741
7037
|
};
|
|
7038
|
+
var armedCronRooms = /* @__PURE__ */ new Set();
|
|
7039
|
+
function armCronRoom(ctx, namespace, roomId, tasks) {
|
|
7040
|
+
if (tasks.length === 0 || armedCronRooms.has(roomId)) return;
|
|
7041
|
+
armedCronRooms.add(roomId);
|
|
7042
|
+
ctx.waitUntil(
|
|
7043
|
+
namespace.get(namespace.idFromName(roomId)).fetch(`https://internal${CRON_TASKS_INTERNAL_PATH}`).then((response) => {
|
|
7044
|
+
if (!response.ok) armedCronRooms.delete(roomId);
|
|
7045
|
+
return response;
|
|
7046
|
+
}).catch(() => {
|
|
7047
|
+
armedCronRooms.delete(roomId);
|
|
7048
|
+
})
|
|
7049
|
+
);
|
|
7050
|
+
}
|
|
6742
7051
|
function computeNextRunAt(task, from) {
|
|
6743
7052
|
if (task.interval_minutes) {
|
|
6744
7053
|
return new Date(from.getTime() + task.interval_minutes * 60 * 1e3).toISOString();
|
|
@@ -6811,9 +7120,9 @@ var JobRoom = class extends BaseRoom {
|
|
|
6811
7120
|
// ==========================================================================
|
|
6812
7121
|
// BaseRoom Lifecycle
|
|
6813
7122
|
// ==========================================================================
|
|
6814
|
-
async fetch(
|
|
7123
|
+
async fetch(request2) {
|
|
6815
7124
|
this.ensureInitialized();
|
|
6816
|
-
return super.fetch(
|
|
7125
|
+
return super.fetch(request2);
|
|
6817
7126
|
}
|
|
6818
7127
|
async onConnect(ws, user) {
|
|
6819
7128
|
this.ensureInitialized();
|
|
@@ -6900,13 +7209,13 @@ var JobRoom = class extends BaseRoom {
|
|
|
6900
7209
|
* Subclasses overriding `onRequest` should call `super.onRequest(req)`
|
|
6901
7210
|
* for paths they don't handle.
|
|
6902
7211
|
*/
|
|
6903
|
-
async onRequest(
|
|
7212
|
+
async onRequest(request2) {
|
|
6904
7213
|
this.ensureInitialized();
|
|
6905
|
-
const url = new URL(
|
|
6906
|
-
if (
|
|
7214
|
+
const url = new URL(request2.url);
|
|
7215
|
+
if (request2.method !== "POST" || !url.pathname.endsWith("/enqueue")) {
|
|
6907
7216
|
return new Response("Not Found", { status: 404 });
|
|
6908
7217
|
}
|
|
6909
|
-
const body = await
|
|
7218
|
+
const body = await request2.json().catch(() => null);
|
|
6910
7219
|
if (!body || typeof body.type !== "string" || !body.type) {
|
|
6911
7220
|
return new Response(JSON.stringify({ error: "type required" }), {
|
|
6912
7221
|
status: 400,
|
|
@@ -7072,7 +7381,8 @@ var JobRoom = class extends BaseRoom {
|
|
|
7072
7381
|
} catch (e) {
|
|
7073
7382
|
outcome = "failed";
|
|
7074
7383
|
errorMessage = e instanceof Error ? e.message : String(e);
|
|
7075
|
-
|
|
7384
|
+
const stack = e instanceof Error ? e.stack : void 0;
|
|
7385
|
+
console.error(`[jobs] ${row.type} (${row.id}) failed: ${errorMessage}`, ...stack ? [stack] : []);
|
|
7076
7386
|
} finally {
|
|
7077
7387
|
this.inFlight.delete(row.id);
|
|
7078
7388
|
}
|
|
@@ -7386,7 +7696,8 @@ var AUTO_PROVISIONABLE_TYPES = /* @__PURE__ */ new Set([
|
|
|
7386
7696
|
"kv_namespace",
|
|
7387
7697
|
"vectorize",
|
|
7388
7698
|
"r2_bucket",
|
|
7389
|
-
"queue"
|
|
7699
|
+
"queue",
|
|
7700
|
+
"ai_search"
|
|
7390
7701
|
]);
|
|
7391
7702
|
var VECTORIZE_METRICS = /* @__PURE__ */ new Set(["cosine", "euclidean", "dot-product"]);
|
|
7392
7703
|
function isAutoProvision(b) {
|
|
@@ -7401,6 +7712,8 @@ function isAutoProvision(b) {
|
|
|
7401
7712
|
return b.bucket_name === AUTO_PROVISION_SENTINEL;
|
|
7402
7713
|
case "queue":
|
|
7403
7714
|
return b.queue_name === AUTO_PROVISION_SENTINEL;
|
|
7715
|
+
case "ai_search":
|
|
7716
|
+
return b.instance_name === AUTO_PROVISION_SENTINEL;
|
|
7404
7717
|
default:
|
|
7405
7718
|
return false;
|
|
7406
7719
|
}
|
|
@@ -7415,7 +7728,8 @@ var ALLOWED_BINDING_TYPES = /* @__PURE__ */ new Set([
|
|
|
7415
7728
|
"browser_rendering",
|
|
7416
7729
|
"analytics_engine",
|
|
7417
7730
|
"hyperdrive",
|
|
7418
|
-
"ratelimit"
|
|
7731
|
+
"ratelimit",
|
|
7732
|
+
"ai_search"
|
|
7419
7733
|
]);
|
|
7420
7734
|
var RESERVED_BINDING_NAMES = /* @__PURE__ */ new Set([
|
|
7421
7735
|
"ASSETS",
|
|
@@ -7439,6 +7753,7 @@ function validateBindingManifest(manifest) {
|
|
|
7439
7753
|
}
|
|
7440
7754
|
const errors = [];
|
|
7441
7755
|
const seenNames = /* @__PURE__ */ new Set();
|
|
7756
|
+
let aiSearchCount = 0;
|
|
7442
7757
|
for (const entry of manifest) {
|
|
7443
7758
|
if (!entry || typeof entry !== "object") {
|
|
7444
7759
|
errors.push({
|
|
@@ -7469,14 +7784,23 @@ function validateBindingManifest(manifest) {
|
|
|
7469
7784
|
}
|
|
7470
7785
|
seenNames.add(name);
|
|
7471
7786
|
const binding = entry;
|
|
7787
|
+
if (binding.type === "ai_search") aiSearchCount++;
|
|
7472
7788
|
const fieldErr = requiredFieldError(binding);
|
|
7473
7789
|
if (fieldErr) errors.push({ binding, reason: fieldErr });
|
|
7474
7790
|
}
|
|
7791
|
+
if (aiSearchCount > 1) {
|
|
7792
|
+
errors.push({ reason: "Only one ai_search binding may be declared per app" });
|
|
7793
|
+
}
|
|
7475
7794
|
if (errors.length) return { valid: false, errors };
|
|
7476
7795
|
return { valid: true, bindings: manifest };
|
|
7477
7796
|
}
|
|
7478
7797
|
function requiredFieldError(b) {
|
|
7479
7798
|
switch (b.type) {
|
|
7799
|
+
case "ai_search":
|
|
7800
|
+
if (b.instance_name !== AUTO_PROVISION_SENTINEL) {
|
|
7801
|
+
return `ai_search binding "${b.name}" must set instance_name = "auto"; managed instances cannot be selected directly`;
|
|
7802
|
+
}
|
|
7803
|
+
return null;
|
|
7480
7804
|
case "vectorize": {
|
|
7481
7805
|
if (!b.index_name) return `vectorize binding "${b.name}" missing index_name (or "auto")`;
|
|
7482
7806
|
if (b.index_name === AUTO_PROVISION_SENTINEL) {
|
|
@@ -7538,6 +7862,13 @@ function requiredFieldError(b) {
|
|
|
7538
7862
|
}
|
|
7539
7863
|
}
|
|
7540
7864
|
function bindingManifestFromOutputConfig(outputConfig) {
|
|
7865
|
+
const aiSearchNamespaces = outputConfig.ai_search_namespaces;
|
|
7866
|
+
const declaresAiSearchNamespace = Array.isArray(aiSearchNamespaces) ? aiSearchNamespaces.length > 0 : aiSearchNamespaces !== void 0 && aiSearchNamespaces !== null;
|
|
7867
|
+
if (declaresAiSearchNamespace) {
|
|
7868
|
+
throw new Error(
|
|
7869
|
+
'ai_search_namespaces is not supported by DeepSpace; declare one [[ai_search]] instance_name = "auto" instead'
|
|
7870
|
+
);
|
|
7871
|
+
}
|
|
7541
7872
|
const out = [];
|
|
7542
7873
|
const aiBinding = pluckString(outputConfig.ai, "binding");
|
|
7543
7874
|
if (aiBinding) out.push({ type: "ai", name: aiBinding });
|
|
@@ -7555,6 +7886,11 @@ function bindingManifestFromOutputConfig(outputConfig) {
|
|
|
7555
7886
|
...metric && (metric === "cosine" || metric === "euclidean" || metric === "dot-product") && { metric }
|
|
7556
7887
|
});
|
|
7557
7888
|
}
|
|
7889
|
+
for (const a of asObjectArray(outputConfig.ai_search)) {
|
|
7890
|
+
const name = pluckString(a, "binding");
|
|
7891
|
+
const instance_name = pluckString(a, "instance_name");
|
|
7892
|
+
if (name && instance_name) out.push({ type: "ai_search", name, instance_name });
|
|
7893
|
+
}
|
|
7558
7894
|
for (const r of asObjectArray(outputConfig.r2_buckets)) {
|
|
7559
7895
|
const name = pluckString(r, "binding");
|
|
7560
7896
|
const bucket_name = pluckString(r, "bucket_name");
|
|
@@ -8090,218 +8426,8 @@ var DIRECTORY_SCHEMAS = [
|
|
|
8090
8426
|
postsSchema
|
|
8091
8427
|
];
|
|
8092
8428
|
|
|
8093
|
-
// src/server/schemas/workspace.ts
|
|
8094
|
-
var workspaceTeamsSchema = {
|
|
8095
|
-
name: "teams",
|
|
8096
|
-
columns: [
|
|
8097
|
-
{ name: "Name", storage: "text", interpretation: "plain" },
|
|
8098
|
-
{ name: "CreatedBy", storage: "text", interpretation: "plain" },
|
|
8099
|
-
{ name: "IsOpen", storage: "number", interpretation: { kind: "boolean" } }
|
|
8100
|
-
],
|
|
8101
|
-
teamField: "_rowId",
|
|
8102
|
-
permissions: {
|
|
8103
|
-
admin: { read: true, create: true, update: true, delete: true },
|
|
8104
|
-
member: { read: "team", create: true, update: "team", delete: "own" }
|
|
8105
|
-
}
|
|
8106
|
-
};
|
|
8107
|
-
var workspaceTeamMembersSchema = {
|
|
8108
|
-
name: "team_members",
|
|
8109
|
-
columns: [
|
|
8110
|
-
{ name: "TeamId", storage: "text", interpretation: "plain" },
|
|
8111
|
-
{ name: "UserId", storage: "text", interpretation: "plain" },
|
|
8112
|
-
{ name: "RoleInTeam", storage: "text", interpretation: { kind: "select", options: ["admin", "member"] } },
|
|
8113
|
-
{ name: "JoinedAt", storage: "text", interpretation: { kind: "datetime" } },
|
|
8114
|
-
{ name: "Email", storage: "text", interpretation: { kind: "email" } },
|
|
8115
|
-
{ name: "Status", storage: "text", interpretation: { kind: "select", options: ["active", "invited"] } }
|
|
8116
|
-
],
|
|
8117
|
-
teamField: "TeamId",
|
|
8118
|
-
permissions: {
|
|
8119
|
-
admin: { read: true, create: true, update: true, delete: true },
|
|
8120
|
-
member: { read: "team", create: true, update: "team", delete: "team" }
|
|
8121
|
-
}
|
|
8122
|
-
};
|
|
8123
|
-
var workspaceTasksSchema = {
|
|
8124
|
-
name: "tasks",
|
|
8125
|
-
columns: [
|
|
8126
|
-
{ name: "TeamId", storage: "text", interpretation: "plain" },
|
|
8127
|
-
{ name: "Title", storage: "text", interpretation: "plain" },
|
|
8128
|
-
{ name: "Notes", storage: "text", interpretation: "plain" },
|
|
8129
|
-
{ name: "Completed", storage: "number", interpretation: { kind: "boolean" } },
|
|
8130
|
-
{ name: "CompletedAt", storage: "number", interpretation: "plain" },
|
|
8131
|
-
{ name: "Deleted", storage: "number", interpretation: { kind: "boolean" } },
|
|
8132
|
-
{ name: "DeletedAt", storage: "number", interpretation: "plain" },
|
|
8133
|
-
{ name: "Priority", storage: "text", interpretation: { kind: "select", options: ["none", "low", "medium", "high"] } },
|
|
8134
|
-
{ name: "DueDate", storage: "text", interpretation: "plain" },
|
|
8135
|
-
{ name: "ProjectId", storage: "text", interpretation: "plain" },
|
|
8136
|
-
{ name: "KanbanStatus", storage: "text", interpretation: { kind: "select", options: ["backlog", "ready", "in_progress", "review", "done"] } },
|
|
8137
|
-
{ name: "Order", storage: "number", interpretation: "plain" },
|
|
8138
|
-
{ name: "AssignedUser", storage: "text", interpretation: { kind: "json" } },
|
|
8139
|
-
{ name: "AssignedBy", storage: "text", interpretation: { kind: "json" } },
|
|
8140
|
-
{ name: "TagIds", storage: "text", interpretation: { kind: "json" } },
|
|
8141
|
-
{ name: "CreatedAt", storage: "number", interpretation: "plain" }
|
|
8142
|
-
],
|
|
8143
|
-
teamField: "TeamId",
|
|
8144
|
-
permissions: {
|
|
8145
|
-
admin: { read: true, create: true, update: true, delete: true },
|
|
8146
|
-
member: { read: "team", create: true, update: "team", delete: "team" },
|
|
8147
|
-
viewer: { read: false, create: false, update: false, delete: false }
|
|
8148
|
-
}
|
|
8149
|
-
};
|
|
8150
|
-
var workspaceProjectsSchema = {
|
|
8151
|
-
name: "projects",
|
|
8152
|
-
columns: [
|
|
8153
|
-
{ name: "TeamId", storage: "text", interpretation: "plain" },
|
|
8154
|
-
{ name: "Title", storage: "text", interpretation: "plain" },
|
|
8155
|
-
{ name: "Notes", storage: "text", interpretation: "plain" },
|
|
8156
|
-
{ name: "Color", storage: "text", interpretation: "plain" },
|
|
8157
|
-
{ name: "ParentId", storage: "text", interpretation: "plain" },
|
|
8158
|
-
{ name: "Order", storage: "number", interpretation: "plain" },
|
|
8159
|
-
{ name: "CreatedAt", storage: "number", interpretation: "plain" }
|
|
8160
|
-
],
|
|
8161
|
-
teamField: "TeamId",
|
|
8162
|
-
permissions: {
|
|
8163
|
-
admin: { read: true, create: true, update: true, delete: true },
|
|
8164
|
-
member: { read: "team", create: true, update: "team", delete: "team" },
|
|
8165
|
-
viewer: { read: false, create: false, update: false, delete: false }
|
|
8166
|
-
}
|
|
8167
|
-
};
|
|
8168
|
-
var workspaceTagsSchema = {
|
|
8169
|
-
name: "tags",
|
|
8170
|
-
columns: [
|
|
8171
|
-
{ name: "TeamId", storage: "text", interpretation: "plain" },
|
|
8172
|
-
{ name: "Name", storage: "text", interpretation: "plain" },
|
|
8173
|
-
{ name: "Color", storage: "text", interpretation: "plain" },
|
|
8174
|
-
{ name: "CreatedAt", storage: "number", interpretation: "plain" }
|
|
8175
|
-
],
|
|
8176
|
-
teamField: "TeamId",
|
|
8177
|
-
permissions: {
|
|
8178
|
-
admin: { read: true, create: true, update: true, delete: true },
|
|
8179
|
-
member: { read: "team", create: true, update: "team", delete: "team" },
|
|
8180
|
-
viewer: { read: false, create: false, update: false, delete: false }
|
|
8181
|
-
}
|
|
8182
|
-
};
|
|
8183
|
-
var workspacePeopleSchema = {
|
|
8184
|
-
name: "people",
|
|
8185
|
-
columns: [
|
|
8186
|
-
{ name: "Name", storage: "text", interpretation: "plain" },
|
|
8187
|
-
{ name: "Email", storage: "text", interpretation: { kind: "email" } },
|
|
8188
|
-
{ name: "Type", storage: "text", interpretation: { kind: "select", options: ["employee", "customer", "vendor", "contact"] } },
|
|
8189
|
-
{ name: "Status", storage: "text", interpretation: { kind: "select", options: ["active", "inactive", "archived"] } },
|
|
8190
|
-
{ name: "CompanyId", storage: "text", interpretation: "plain" },
|
|
8191
|
-
{ name: "Department", storage: "text", interpretation: "plain" },
|
|
8192
|
-
{ name: "Title", storage: "text", interpretation: "plain" },
|
|
8193
|
-
{ name: "LastContactedAt", storage: "text", interpretation: { kind: "datetime" } },
|
|
8194
|
-
{ name: "Metadata", storage: "text", interpretation: { kind: "json" } }
|
|
8195
|
-
],
|
|
8196
|
-
permissions: {
|
|
8197
|
-
"*": { read: true, create: true, update: true, delete: false },
|
|
8198
|
-
admin: { read: true, create: true, update: true, delete: true }
|
|
8199
|
-
}
|
|
8200
|
-
};
|
|
8201
|
-
var workspaceTransactionsSchema = {
|
|
8202
|
-
name: "transactions",
|
|
8203
|
-
columns: [
|
|
8204
|
-
{ name: "Type", storage: "text", interpretation: { kind: "select", options: ["revenue", "expense", "payment", "transfer", "invoice", "refund"] } },
|
|
8205
|
-
{ name: "Amount", storage: "number", interpretation: { kind: "currency", symbol: "$", decimals: 2 } },
|
|
8206
|
-
{ name: "Currency", storage: "text", interpretation: "plain" },
|
|
8207
|
-
{ name: "Description", storage: "text", interpretation: "plain" },
|
|
8208
|
-
{ name: "Category", storage: "text", interpretation: "plain" },
|
|
8209
|
-
{ name: "CounterpartyRef", storage: "text", interpretation: "plain" },
|
|
8210
|
-
{ name: "OrderRef", storage: "text", interpretation: "plain" },
|
|
8211
|
-
{ name: "Account", storage: "text", interpretation: "plain" }
|
|
8212
|
-
],
|
|
8213
|
-
permissions: {
|
|
8214
|
-
"*": { read: true, create: true, update: true, delete: false },
|
|
8215
|
-
admin: { read: true, create: true, update: true, delete: true }
|
|
8216
|
-
}
|
|
8217
|
-
};
|
|
8218
|
-
var workspaceAccountsSchema = {
|
|
8219
|
-
name: "accounts",
|
|
8220
|
-
columns: [
|
|
8221
|
-
{ name: "Name", storage: "text", interpretation: "plain" },
|
|
8222
|
-
{ name: "Balance", storage: "number", interpretation: { kind: "currency", symbol: "$", decimals: 2 } },
|
|
8223
|
-
{ name: "Currency", storage: "text", interpretation: "plain" }
|
|
8224
|
-
],
|
|
8225
|
-
permissions: {
|
|
8226
|
-
"*": { read: true, create: true, update: true, delete: false },
|
|
8227
|
-
admin: { read: true, create: true, update: true, delete: true }
|
|
8228
|
-
}
|
|
8229
|
-
};
|
|
8230
|
-
var workspaceContentSharesSchema = {
|
|
8231
|
-
name: "content_shares",
|
|
8232
|
-
ownerField: "OwnerId",
|
|
8233
|
-
columns: [
|
|
8234
|
-
{ name: "ContentType", storage: "text", interpretation: { kind: "select", options: ["document", "slide", "spreadsheet"] } },
|
|
8235
|
-
{ name: "ContentId", storage: "text", interpretation: "plain" },
|
|
8236
|
-
{ name: "OwnerId", storage: "text", interpretation: "plain" },
|
|
8237
|
-
{ name: "OwnerName", storage: "text", interpretation: "plain" },
|
|
8238
|
-
{ name: "Title", storage: "text", interpretation: "plain" },
|
|
8239
|
-
{ name: "ShareType", storage: "text", interpretation: { kind: "select", options: ["self", "channel", "team", "direct", "link", "org"] } },
|
|
8240
|
-
{ name: "ShareTarget", storage: "text", interpretation: "plain" },
|
|
8241
|
-
{ name: "Permission", storage: "text", interpretation: { kind: "select", options: ["view", "edit"] } },
|
|
8242
|
-
{ name: "SharedAt", storage: "text", interpretation: { kind: "datetime" } },
|
|
8243
|
-
{ name: "SharedBy", storage: "text", interpretation: "plain" },
|
|
8244
|
-
{ name: "SourceApp", storage: "text", interpretation: "plain" },
|
|
8245
|
-
{ name: "WordCount", storage: "number", interpretation: "plain" },
|
|
8246
|
-
{ name: "LastEditedAt", storage: "text", interpretation: { kind: "datetime" } }
|
|
8247
|
-
],
|
|
8248
|
-
permissions: {
|
|
8249
|
-
"*": { read: true, create: true, update: true, delete: "own" },
|
|
8250
|
-
admin: { read: true, create: true, update: true, delete: true }
|
|
8251
|
-
}
|
|
8252
|
-
};
|
|
8253
|
-
var workspaceFormResponsesSchema = {
|
|
8254
|
-
name: "form_responses",
|
|
8255
|
-
columns: [
|
|
8256
|
-
{ name: "FormId", storage: "text", interpretation: "plain" },
|
|
8257
|
-
{ name: "FormTitle", storage: "text", interpretation: "plain" },
|
|
8258
|
-
{ name: "RespondentId", storage: "text", interpretation: "plain" },
|
|
8259
|
-
{ name: "RespondentEmail", storage: "text", interpretation: { kind: "email" } },
|
|
8260
|
-
{ name: "RespondentName", storage: "text", interpretation: "plain" },
|
|
8261
|
-
{ name: "Data", storage: "text", interpretation: { kind: "json" } },
|
|
8262
|
-
{ name: "SubmittedAt", storage: "text", interpretation: { kind: "datetime" } }
|
|
8263
|
-
],
|
|
8264
|
-
permissions: {
|
|
8265
|
-
"*": { read: true, create: true, update: false, delete: false },
|
|
8266
|
-
admin: { read: true, create: true, update: true, delete: true }
|
|
8267
|
-
}
|
|
8268
|
-
};
|
|
8269
|
-
var workspaceEmailHandlesSchema = {
|
|
8270
|
-
name: "email_handles",
|
|
8271
|
-
columns: [
|
|
8272
|
-
{ name: "UserId", storage: "text", interpretation: "plain" },
|
|
8273
|
-
{ name: "Handle", storage: "text", interpretation: "plain" },
|
|
8274
|
-
{ name: "EmailAddress", storage: "text", interpretation: { kind: "email" } },
|
|
8275
|
-
{ name: "Status", storage: "text", interpretation: { kind: "select", options: ["active", "disabled"] } }
|
|
8276
|
-
],
|
|
8277
|
-
uniqueOn: ["Handle"],
|
|
8278
|
-
ownerField: "UserId",
|
|
8279
|
-
permissions: {
|
|
8280
|
-
"*": { read: true, create: true, update: "own", delete: false },
|
|
8281
|
-
admin: { read: true, create: true, update: true, delete: true }
|
|
8282
|
-
}
|
|
8283
|
-
};
|
|
8284
|
-
var WORKSPACE_SCHEMAS = [
|
|
8285
|
-
workspaceTeamsSchema,
|
|
8286
|
-
workspaceTeamMembersSchema,
|
|
8287
|
-
workspaceTasksSchema,
|
|
8288
|
-
workspaceProjectsSchema,
|
|
8289
|
-
workspaceTagsSchema,
|
|
8290
|
-
workspacePeopleSchema,
|
|
8291
|
-
workspaceTransactionsSchema,
|
|
8292
|
-
workspaceAccountsSchema,
|
|
8293
|
-
workspaceContentSharesSchema,
|
|
8294
|
-
workspaceFormResponsesSchema,
|
|
8295
|
-
workspaceEmailHandlesSchema
|
|
8296
|
-
];
|
|
8297
|
-
|
|
8298
8429
|
// src/server/schemas/shared-do.ts
|
|
8299
8430
|
var GLOBAL_DO_TYPES = [
|
|
8300
|
-
{
|
|
8301
|
-
name: "workspace",
|
|
8302
|
-
schemas: WORKSPACE_SCHEMAS,
|
|
8303
|
-
description: "Shared workspace data (teams, tasks, projects, people, ledger)"
|
|
8304
|
-
},
|
|
8305
8431
|
{
|
|
8306
8432
|
name: "conv",
|
|
8307
8433
|
schemas: [...CONVERSATION_SCHEMAS, ...VOTING_SCHEMAS],
|
|
@@ -8811,6 +8937,7 @@ export {
|
|
|
8811
8937
|
GLOBAL_DO_TYPES,
|
|
8812
8938
|
GLOBAL_DO_TYPE_NAMES,
|
|
8813
8939
|
JobRoom,
|
|
8940
|
+
KnowledgeError,
|
|
8814
8941
|
MAX_APP_FILE_BYTES,
|
|
8815
8942
|
MAX_DELETE_WHERE_LIMIT,
|
|
8816
8943
|
MAX_DEPLOY_ASSET_FILE_BYTES,
|
|
@@ -8839,13 +8966,13 @@ export {
|
|
|
8839
8966
|
UPLOAD_PART_BYTES,
|
|
8840
8967
|
USERS_COLUMNS,
|
|
8841
8968
|
VOTING_SCHEMAS,
|
|
8842
|
-
WORKSPACE_SCHEMAS,
|
|
8843
8969
|
YjsRoom,
|
|
8844
8970
|
analyzePermissions,
|
|
8845
8971
|
apiWorkerFetch,
|
|
8846
8972
|
appendMessage,
|
|
8847
8973
|
applyAiToolDefaults,
|
|
8848
8974
|
applySlidingWindow,
|
|
8975
|
+
armCronRoom,
|
|
8849
8976
|
authWorkerFetch,
|
|
8850
8977
|
bindingManifestFromOutputConfig,
|
|
8851
8978
|
broadcastChange,
|
|
@@ -8880,7 +9007,9 @@ export {
|
|
|
8880
9007
|
deleteChatCascade,
|
|
8881
9008
|
deleteRecord,
|
|
8882
9009
|
enqueueJob,
|
|
9010
|
+
epochSeconds,
|
|
8883
9011
|
executeQuery,
|
|
9012
|
+
folderFilter,
|
|
8884
9013
|
formatBytes,
|
|
8885
9014
|
getAllUsers,
|
|
8886
9015
|
getChat,
|
|
@@ -8910,6 +9039,7 @@ export {
|
|
|
8910
9039
|
isTestAccountClaims,
|
|
8911
9040
|
isTestAccountEmail,
|
|
8912
9041
|
isTestAccountTier,
|
|
9042
|
+
knowledge,
|
|
8913
9043
|
lintSchema,
|
|
8914
9044
|
lintSchemaSet,
|
|
8915
9045
|
lintSchemas,
|
|
@@ -8922,6 +9052,7 @@ export {
|
|
|
8922
9052
|
mintAppId,
|
|
8923
9053
|
mintUlid,
|
|
8924
9054
|
noopPermissionContext,
|
|
9055
|
+
normalizeKnowledgeFolder,
|
|
8925
9056
|
parseQuotaLimit,
|
|
8926
9057
|
platformCorsPatterns,
|
|
8927
9058
|
platformWorkerFetch,
|
|
@@ -8954,17 +9085,6 @@ export {
|
|
|
8954
9085
|
validateAppName,
|
|
8955
9086
|
validateBindingManifest,
|
|
8956
9087
|
validateDoManifest,
|
|
8957
|
-
verifyJwt
|
|
8958
|
-
workspaceAccountsSchema,
|
|
8959
|
-
workspaceContentSharesSchema,
|
|
8960
|
-
workspaceEmailHandlesSchema,
|
|
8961
|
-
workspaceFormResponsesSchema,
|
|
8962
|
-
workspacePeopleSchema,
|
|
8963
|
-
workspaceProjectsSchema,
|
|
8964
|
-
workspaceTagsSchema,
|
|
8965
|
-
workspaceTasksSchema,
|
|
8966
|
-
workspaceTeamMembersSchema,
|
|
8967
|
-
workspaceTeamsSchema,
|
|
8968
|
-
workspaceTransactionsSchema
|
|
9088
|
+
verifyJwt
|
|
8969
9089
|
};
|
|
8970
9090
|
//# sourceMappingURL=server.js.map
|