theokit 0.2.0 → 0.2.2
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/dist/actions-virtual-module-E6PU47GV.js +175 -0
- package/dist/actions-virtual-module-E6PU47GV.js.map +1 -0
- package/dist/actions-virtual-module-E7IELVHL.js +174 -0
- package/dist/actions-virtual-module-E7IELVHL.js.map +1 -0
- package/dist/app-typed-client-RZMVWHJ3.js +289 -0
- package/dist/app-typed-client-RZMVWHJ3.js.map +1 -0
- package/dist/app-typed-client-YIBP6JWH.js +289 -0
- package/dist/app-typed-client-YIBP6JWH.js.map +1 -0
- package/dist/{aws-lambda-PONVF3UX.js → aws-lambda-XYCGZH3I.js} +2 -2
- package/dist/{build-IQQYCA3B.js → build-NCGIV4LL.js} +41 -14
- package/dist/build-NCGIV4LL.js.map +1 -0
- package/dist/build-request-body-preview-EOP2Y4F3.js +52 -0
- package/dist/build-request-body-preview-EOP2Y4F3.js.map +1 -0
- package/dist/build-request-body-preview-II2235E6.js +54 -0
- package/dist/build-request-body-preview-II2235E6.js.map +1 -0
- package/dist/{bun-DITMZMKL.js → bun-K73TQEWC.js} +2 -2
- package/dist/{chunk-GQSTRXXB.js → chunk-2BQYEBCK.js} +77 -68
- package/dist/chunk-2BQYEBCK.js.map +1 -0
- package/dist/chunk-7DHGV77J.js +140 -0
- package/dist/chunk-7DHGV77J.js.map +1 -0
- package/dist/{chunk-WQCPKA2L.js → chunk-C27AZN46.js} +570 -165
- package/dist/chunk-C27AZN46.js.map +1 -0
- package/dist/chunk-ERAZNHHR.js +73 -0
- package/dist/chunk-ERAZNHHR.js.map +1 -0
- package/dist/{chunk-ZK5JCE5J.js → chunk-EXJWO2HB.js} +201 -43
- package/dist/chunk-EXJWO2HB.js.map +1 -0
- package/dist/chunk-F3TG5FJV.js +97 -0
- package/dist/chunk-F3TG5FJV.js.map +1 -0
- package/dist/{chunk-OK2PJOLK.js → chunk-KJVEJILQ.js} +7 -3
- package/dist/chunk-KJVEJILQ.js.map +1 -0
- package/dist/{chunk-JBC7PFV4.js → chunk-M23FP2AS.js} +2 -2
- package/dist/chunk-M23FP2AS.js.map +1 -0
- package/dist/{chunk-FRBYJANT.js → chunk-M6MYZSD2.js} +195 -41
- package/dist/chunk-M6MYZSD2.js.map +1 -0
- package/dist/{chunk-S3JBIFKS.js → chunk-MG7H3LAW.js} +2 -2
- package/dist/chunk-MOT42NAH.js +165 -0
- package/dist/chunk-MOT42NAH.js.map +1 -0
- package/dist/{chunk-OEB3OIYP.js → chunk-OJHO2X7C.js} +497 -168
- package/dist/chunk-OJHO2X7C.js.map +1 -0
- package/dist/chunk-QLGES2HC.js +189 -0
- package/dist/chunk-QLGES2HC.js.map +1 -0
- package/dist/{chunk-JZ5WEG3S.js → chunk-SEVYARIU.js} +9 -5
- package/dist/chunk-SEVYARIU.js.map +1 -0
- package/dist/chunk-WSJKACWB.js +17 -0
- package/dist/chunk-WSJKACWB.js.map +1 -0
- package/dist/{chunk-3Q36SEDK.js → chunk-ZBWROIIO.js} +43 -2
- package/dist/{chunk-3Q36SEDK.js.map → chunk-ZBWROIIO.js.map} +1 -1
- package/dist/{chunk-Y5KE6YFP.js → chunk-ZJW5FFYJ.js} +3 -3
- package/dist/chunk-ZJW5FFYJ.js.map +1 -0
- package/dist/chunk-ZOXNJV2O.js +358 -0
- package/dist/chunk-ZOXNJV2O.js.map +1 -0
- package/dist/cli/index.js +22 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/client/index.d.ts +31 -1
- package/dist/client/index.js +100 -0
- package/dist/client/index.js.map +1 -1
- package/dist/{cloudflare-RBANU3SE.js → cloudflare-LCAOTRYZ.js} +2 -2
- package/dist/{deno-deploy-J7LI6HNL.js → deno-deploy-ZN4RE5HF.js} +2 -2
- package/dist/{dev-VN6WS3CM.js → dev-WDXC74CK.js} +15 -7
- package/dist/dev-WDXC74CK.js.map +1 -0
- package/dist/dev-emit-3CHM73TV.js +390 -0
- package/dist/dev-emit-3CHM73TV.js.map +1 -0
- package/dist/dev-emit-I3CKOHML.js +44 -0
- package/dist/dev-emit-I3CKOHML.js.map +1 -0
- package/dist/devtools/entry.js +545 -181
- package/dist/devtools/entry.js.map +1 -1
- package/dist/{dispatcher-IK3FVMX5.js → dispatcher-AQJGI3HU.js} +2 -2
- package/dist/dispatcher-E6QV32BO.js +83 -0
- package/dist/dispatcher-E6QV32BO.js.map +1 -0
- package/dist/{dist-Q5VBFWVL.js → dist-R2Q7GVDW.js} +10357 -12068
- package/dist/dist-R2Q7GVDW.js.map +1 -0
- package/dist/{generate-SIO4JSKJ.js → generate-DT4IIAHF.js} +88 -22
- package/dist/generate-DT4IIAHF.js.map +1 -0
- package/dist/index.d.ts +108 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/{info-B3UOXFUF.js → info-VM7EK4JC.js} +3 -3
- package/dist/{netlify-IFY6ZGFV.js → netlify-5VQ22Z2P.js} +2 -2
- package/dist/{node-KWZI3CKV.js → node-3APTLGWB.js} +2 -2
- package/dist/openapi-CBASKC7W.js +84 -0
- package/dist/openapi-CBASKC7W.js.map +1 -0
- package/dist/registry-5QFTLOL2.js +25 -0
- package/dist/{routes-CAGFZKUK.js → routes-7O4NUDYS.js} +9 -7
- package/dist/{routes-CAGFZKUK.js.map → routes-7O4NUDYS.js.map} +1 -1
- package/dist/{schema-Cxq2qqwh.d.ts → schema-CjVly9c_.d.ts} +43 -1
- package/dist/{schema-MVB2FVK6.js → schema-JAKF23GR.js} +2 -2
- package/dist/server/cost/index.js +1 -1
- package/dist/server/index.d.ts +219 -47
- package/dist/server/index.js +35 -116
- package/dist/server/index.js.map +1 -1
- package/dist/{start-ZGWW5MOL.js → start-5JTFWF4T.js} +20 -14
- package/dist/start-5JTFWF4T.js.map +1 -0
- package/dist/{static-BH6JQNM2.js → static-TZBVBDTB.js} +4 -4
- package/dist/{upgrade-readiness-OHL36FQL.js → upgrade-readiness-Q46KTG2J.js} +6 -1
- package/dist/upgrade-readiness-Q46KTG2J.js.map +1 -0
- package/dist/{vercel-RHADE5IP.js → vercel-MWW24ABC.js} +2 -2
- package/dist/vite-plugin/index.d.ts +25 -1
- package/dist/vite-plugin/index.js +5 -2
- package/dist/{vite-plugin-WA3G45YY.js → vite-plugin-4WZ3C2AC.js} +7 -6
- package/package.json +4 -2
- package/dist/build-IQQYCA3B.js.map +0 -1
- package/dist/chunk-FRBYJANT.js.map +0 -1
- package/dist/chunk-GQSTRXXB.js.map +0 -1
- package/dist/chunk-JBC7PFV4.js.map +0 -1
- package/dist/chunk-JZ5WEG3S.js.map +0 -1
- package/dist/chunk-OEB3OIYP.js.map +0 -1
- package/dist/chunk-OK2PJOLK.js.map +0 -1
- package/dist/chunk-WQCPKA2L.js.map +0 -1
- package/dist/chunk-Y5KE6YFP.js.map +0 -1
- package/dist/chunk-ZK5JCE5J.js.map +0 -1
- package/dist/dev-VN6WS3CM.js.map +0 -1
- package/dist/dist-Q5VBFWVL.js.map +0 -1
- package/dist/generate-SIO4JSKJ.js.map +0 -1
- package/dist/registry-BB3UYAOG.js +0 -25
- package/dist/start-ZGWW5MOL.js.map +0 -1
- package/dist/upgrade-readiness-OHL36FQL.js.map +0 -1
- /package/dist/{aws-lambda-PONVF3UX.js.map → aws-lambda-XYCGZH3I.js.map} +0 -0
- /package/dist/{bun-DITMZMKL.js.map → bun-K73TQEWC.js.map} +0 -0
- /package/dist/{chunk-S3JBIFKS.js.map → chunk-MG7H3LAW.js.map} +0 -0
- /package/dist/{cloudflare-RBANU3SE.js.map → cloudflare-LCAOTRYZ.js.map} +0 -0
- /package/dist/{deno-deploy-J7LI6HNL.js.map → deno-deploy-ZN4RE5HF.js.map} +0 -0
- /package/dist/{dispatcher-IK3FVMX5.js.map → dispatcher-AQJGI3HU.js.map} +0 -0
- /package/dist/{info-B3UOXFUF.js.map → info-VM7EK4JC.js.map} +0 -0
- /package/dist/{netlify-IFY6ZGFV.js.map → netlify-5VQ22Z2P.js.map} +0 -0
- /package/dist/{node-KWZI3CKV.js.map → node-3APTLGWB.js.map} +0 -0
- /package/dist/{registry-BB3UYAOG.js.map → registry-5QFTLOL2.js.map} +0 -0
- /package/dist/{schema-MVB2FVK6.js.map → schema-JAKF23GR.js.map} +0 -0
- /package/dist/{static-BH6JQNM2.js.map → static-TZBVBDTB.js.map} +0 -0
- /package/dist/{vercel-RHADE5IP.js.map → vercel-MWW24ABC.js.map} +0 -0
- /package/dist/{vite-plugin-WA3G45YY.js.map → vite-plugin-4WZ3C2AC.js.map} +0 -0
|
@@ -7,9 +7,6 @@ import {
|
|
|
7
7
|
createOutboxDispatcher,
|
|
8
8
|
createQueueClient
|
|
9
9
|
} from "./chunk-GY5Q27BJ.js";
|
|
10
|
-
import {
|
|
11
|
-
walkSourceFiles
|
|
12
|
-
} from "./chunk-X2VVCJ4V.js";
|
|
13
10
|
|
|
14
11
|
// src/config/load-env.ts
|
|
15
12
|
import { lstatSync, readFileSync, realpathSync, statSync } from "fs";
|
|
@@ -108,6 +105,185 @@ function loadEnv(options = {}) {
|
|
|
108
105
|
return result;
|
|
109
106
|
}
|
|
110
107
|
|
|
108
|
+
// src/server/security/csrf-readiness-store.ts
|
|
109
|
+
var CsrfReadinessStore = class _CsrfReadinessStore {
|
|
110
|
+
static MAX_ENTRIES = 1e3;
|
|
111
|
+
entries = /* @__PURE__ */ new Map();
|
|
112
|
+
keyFor(event) {
|
|
113
|
+
return `${event.method}\0${event.path}\0${event.reason}`;
|
|
114
|
+
}
|
|
115
|
+
record(event) {
|
|
116
|
+
const key = this.keyFor(event);
|
|
117
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
118
|
+
const existing = this.entries.get(key);
|
|
119
|
+
if (existing) {
|
|
120
|
+
existing.count++;
|
|
121
|
+
existing.lastSeen = now;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (this.entries.size >= _CsrfReadinessStore.MAX_ENTRIES) {
|
|
125
|
+
const firstKey = this.entries.keys().next().value;
|
|
126
|
+
if (firstKey !== void 0) this.entries.delete(firstKey);
|
|
127
|
+
}
|
|
128
|
+
this.entries.set(key, { count: 1, firstSeen: now, lastSeen: now });
|
|
129
|
+
}
|
|
130
|
+
summary() {
|
|
131
|
+
let total = 0;
|
|
132
|
+
const routes = [];
|
|
133
|
+
for (const [key, entry] of this.entries) {
|
|
134
|
+
const [method, path, reason] = key.split("\0");
|
|
135
|
+
routes.push({
|
|
136
|
+
method,
|
|
137
|
+
path,
|
|
138
|
+
reason,
|
|
139
|
+
count: entry.count,
|
|
140
|
+
firstSeen: entry.firstSeen,
|
|
141
|
+
lastSeen: entry.lastSeen
|
|
142
|
+
});
|
|
143
|
+
total += entry.count;
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
147
|
+
totalEvents: total,
|
|
148
|
+
routes
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
reset() {
|
|
152
|
+
this.entries.clear();
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// src/core/contracts/action-protocol.ts
|
|
157
|
+
var CODE_TO_STATUS = {
|
|
158
|
+
VALIDATION_ERROR: 422,
|
|
159
|
+
BAD_REQUEST: 400,
|
|
160
|
+
UNAUTHORIZED: 401,
|
|
161
|
+
FORBIDDEN: 403,
|
|
162
|
+
NOT_FOUND: 404,
|
|
163
|
+
METHOD_NOT_ALLOWED: 405,
|
|
164
|
+
CONFLICT: 409,
|
|
165
|
+
CONTENT_TOO_LARGE: 413,
|
|
166
|
+
PAYLOAD_TOO_LARGE: 413,
|
|
167
|
+
UNSUPPORTED_MEDIA_TYPE: 415,
|
|
168
|
+
TOO_MANY_REQUESTS: 429,
|
|
169
|
+
INTERNAL_SERVER_ERROR: 500
|
|
170
|
+
};
|
|
171
|
+
var STATUS_TO_CODE = {
|
|
172
|
+
400: "BAD_REQUEST",
|
|
173
|
+
401: "UNAUTHORIZED",
|
|
174
|
+
403: "FORBIDDEN",
|
|
175
|
+
404: "NOT_FOUND",
|
|
176
|
+
405: "METHOD_NOT_ALLOWED",
|
|
177
|
+
409: "CONFLICT",
|
|
178
|
+
413: "PAYLOAD_TOO_LARGE",
|
|
179
|
+
415: "UNSUPPORTED_MEDIA_TYPE",
|
|
180
|
+
422: "VALIDATION_ERROR",
|
|
181
|
+
429: "TOO_MANY_REQUESTS",
|
|
182
|
+
500: "INTERNAL_SERVER_ERROR"
|
|
183
|
+
};
|
|
184
|
+
var ActionError = class _ActionError extends Error {
|
|
185
|
+
// Discriminator widened to the full union so subclasses can narrow to their
|
|
186
|
+
// specific literal (TypeScript would otherwise reject the override). Concrete
|
|
187
|
+
// values are still always exact literals at runtime.
|
|
188
|
+
type = "TheoActionError";
|
|
189
|
+
code;
|
|
190
|
+
status;
|
|
191
|
+
constructor(params) {
|
|
192
|
+
super(params.message ?? params.code);
|
|
193
|
+
this.code = params.code;
|
|
194
|
+
this.status = _ActionError.codeToStatus(params.code);
|
|
195
|
+
if (params.stack) {
|
|
196
|
+
this.stack = params.stack;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
static codeToStatus(code) {
|
|
200
|
+
return CODE_TO_STATUS[code];
|
|
201
|
+
}
|
|
202
|
+
static statusToCode(status) {
|
|
203
|
+
return STATUS_TO_CODE[status] ?? "INTERNAL_SERVER_ERROR";
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Parse a serialized error JSON back into the typed class hierarchy.
|
|
207
|
+
* Distinguishes `TheoActionInputError` (with `issues` array) from
|
|
208
|
+
* `TheoActionError` via the `type` discriminator. Falls back to
|
|
209
|
+
* `INTERNAL_SERVER_ERROR` for malformed bodies (non-object, missing
|
|
210
|
+
* `type`, unknown `code`).
|
|
211
|
+
*/
|
|
212
|
+
static fromJson(body) {
|
|
213
|
+
if (typeof body !== "object" || body === null) {
|
|
214
|
+
return new _ActionError({ code: "INTERNAL_SERVER_ERROR" });
|
|
215
|
+
}
|
|
216
|
+
const obj = body;
|
|
217
|
+
if (obj.type === "TheoActionInputError" && Array.isArray(obj.issues)) {
|
|
218
|
+
return new ActionInputError(obj.issues);
|
|
219
|
+
}
|
|
220
|
+
if (obj.type === "TheoActionError" && typeof obj.code === "string" && obj.code in CODE_TO_STATUS) {
|
|
221
|
+
return new _ActionError({
|
|
222
|
+
code: obj.code,
|
|
223
|
+
message: typeof obj.message === "string" ? obj.message : void 0
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return new _ActionError({ code: "INTERNAL_SERVER_ERROR" });
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
var ActionInputError = class extends ActionError {
|
|
230
|
+
type = "TheoActionInputError";
|
|
231
|
+
issues;
|
|
232
|
+
fields;
|
|
233
|
+
constructor(rawIssues) {
|
|
234
|
+
super({ code: "VALIDATION_ERROR", message: "Validation failed" });
|
|
235
|
+
this.issues = extractUniversalIssues(rawIssues);
|
|
236
|
+
this.fields = buildFieldsMap(this.issues);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
function buildFieldsMap(issues) {
|
|
240
|
+
const fields = {};
|
|
241
|
+
const seen = /* @__PURE__ */ new Set();
|
|
242
|
+
for (const issue of issues) {
|
|
243
|
+
const key = issue.path.length === 0 ? "" : issue.path.join(".");
|
|
244
|
+
const dedupeKey = `${key}\0${issue.message}`;
|
|
245
|
+
if (seen.has(dedupeKey)) continue;
|
|
246
|
+
seen.add(dedupeKey);
|
|
247
|
+
const bucket = fields[key] ?? [];
|
|
248
|
+
bucket.push(issue.message);
|
|
249
|
+
fields[key] = bucket;
|
|
250
|
+
}
|
|
251
|
+
return fields;
|
|
252
|
+
}
|
|
253
|
+
function extractUniversalIssues(raw) {
|
|
254
|
+
if (!Array.isArray(raw)) return [];
|
|
255
|
+
const out = [];
|
|
256
|
+
for (const entry of raw) {
|
|
257
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
258
|
+
const obj = entry;
|
|
259
|
+
if (!Array.isArray(obj.path)) continue;
|
|
260
|
+
if (typeof obj.message !== "string") continue;
|
|
261
|
+
const path = [];
|
|
262
|
+
let pathValid = true;
|
|
263
|
+
for (const seg of obj.path) {
|
|
264
|
+
if (typeof seg === "string" || typeof seg === "number") {
|
|
265
|
+
path.push(seg);
|
|
266
|
+
} else {
|
|
267
|
+
pathValid = false;
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (!pathValid) continue;
|
|
272
|
+
out.push({
|
|
273
|
+
path,
|
|
274
|
+
message: obj.message,
|
|
275
|
+
code: typeof obj.code === "string" ? obj.code : void 0
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return out;
|
|
279
|
+
}
|
|
280
|
+
function isActionError(value) {
|
|
281
|
+
return value instanceof ActionError;
|
|
282
|
+
}
|
|
283
|
+
function isInputError(value) {
|
|
284
|
+
return value instanceof ActionInputError;
|
|
285
|
+
}
|
|
286
|
+
|
|
111
287
|
// src/server/body-parser.ts
|
|
112
288
|
import { basename } from "path";
|
|
113
289
|
import Busboy from "busboy";
|
|
@@ -127,6 +303,13 @@ var FileTooLargeError = class extends Error {
|
|
|
127
303
|
code = "FILE_TOO_LARGE";
|
|
128
304
|
status = 413;
|
|
129
305
|
};
|
|
306
|
+
function stashBodyPreview(req, body) {
|
|
307
|
+
try {
|
|
308
|
+
;
|
|
309
|
+
req[DEVTOOLS_BODY_PREVIEW] = body;
|
|
310
|
+
} catch {
|
|
311
|
+
}
|
|
312
|
+
}
|
|
130
313
|
function parseJsonBody(req) {
|
|
131
314
|
return new Promise((resolve2, reject) => {
|
|
132
315
|
const chunks = [];
|
|
@@ -226,6 +409,7 @@ function parseMultipartBody(req, contentType, options) {
|
|
|
226
409
|
req.pipe(bb);
|
|
227
410
|
});
|
|
228
411
|
}
|
|
412
|
+
var DEVTOOLS_BODY_PREVIEW = /* @__PURE__ */ Symbol("theo:devtools:bodyPreview");
|
|
229
413
|
async function parseRequestBody(req, options) {
|
|
230
414
|
const method = req.method?.toUpperCase() ?? "GET";
|
|
231
415
|
if (!METHODS_WITH_BODY.has(method)) {
|
|
@@ -234,6 +418,7 @@ async function parseRequestBody(req, options) {
|
|
|
234
418
|
const contentType = req.headers["content-type"] ?? "";
|
|
235
419
|
if (contentType.includes("application/json")) {
|
|
236
420
|
const json = await parseJsonBody(req);
|
|
421
|
+
stashBodyPreview(req, json);
|
|
237
422
|
return { fields: {}, files: [], json };
|
|
238
423
|
}
|
|
239
424
|
if (contentType.includes("multipart/form-data")) {
|
|
@@ -243,6 +428,15 @@ async function parseRequestBody(req, options) {
|
|
|
243
428
|
maxFieldSize: options?.maxFieldSize ?? DEFAULT_MAX_FIELD_SIZE
|
|
244
429
|
};
|
|
245
430
|
const { fields, files } = await parseMultipartBody(req, contentType, limits);
|
|
431
|
+
stashBodyPreview(req, {
|
|
432
|
+
fields,
|
|
433
|
+
files: files.map((f) => ({
|
|
434
|
+
fieldname: f.fieldname,
|
|
435
|
+
filename: f.filename,
|
|
436
|
+
mimeType: f.mimeType,
|
|
437
|
+
size: f.size
|
|
438
|
+
}))
|
|
439
|
+
});
|
|
246
440
|
return { fields, files };
|
|
247
441
|
}
|
|
248
442
|
if (!contentType) {
|
|
@@ -255,7 +449,7 @@ async function parseRequestBody(req, options) {
|
|
|
255
449
|
var defaultLoggerFn = (log) => {
|
|
256
450
|
console.log(JSON.stringify(log));
|
|
257
451
|
};
|
|
258
|
-
function logRequest(info, customLogger) {
|
|
452
|
+
function logRequest(info, customLogger, req) {
|
|
259
453
|
const log = {
|
|
260
454
|
level: "info",
|
|
261
455
|
...info,
|
|
@@ -263,13 +457,24 @@ function logRequest(info, customLogger) {
|
|
|
263
457
|
};
|
|
264
458
|
const logger = customLogger ?? defaultLoggerFn;
|
|
265
459
|
logger(log);
|
|
266
|
-
broadcastRequestToDevtools(info);
|
|
460
|
+
broadcastRequestToDevtools(info, req);
|
|
267
461
|
}
|
|
268
462
|
function randomRequestId() {
|
|
269
463
|
return `req-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
|
270
464
|
}
|
|
271
|
-
function broadcastRequestToDevtools(info) {
|
|
272
|
-
|
|
465
|
+
function broadcastRequestToDevtools(info, req) {
|
|
466
|
+
const headers = req?.headers ? Object.fromEntries(
|
|
467
|
+
Object.entries(req.headers).map(([k, v]) => [
|
|
468
|
+
k,
|
|
469
|
+
Array.isArray(v) ? v.join(", ") : v ?? ""
|
|
470
|
+
])
|
|
471
|
+
) : void 0;
|
|
472
|
+
const stashedBody = req ? req[DEVTOOLS_BODY_PREVIEW] : void 0;
|
|
473
|
+
void Promise.all([
|
|
474
|
+
import("./broadcast-4SX7R5XE.js"),
|
|
475
|
+
import("./build-request-body-preview-EOP2Y4F3.js")
|
|
476
|
+
]).then(([{ broadcastRequest }, { buildRequestBodyPreview }]) => {
|
|
477
|
+
const bodyInfo = buildRequestBodyPreview(stashedBody);
|
|
273
478
|
broadcastRequest({
|
|
274
479
|
id: randomRequestId(),
|
|
275
480
|
traceId: info.requestId,
|
|
@@ -277,7 +482,13 @@ function broadcastRequestToDevtools(info) {
|
|
|
277
482
|
path: info.url,
|
|
278
483
|
status: info.status,
|
|
279
484
|
durationMs: info.duration,
|
|
280
|
-
startedAt: Date.now() - info.duration
|
|
485
|
+
startedAt: Date.now() - info.duration,
|
|
486
|
+
...headers ? { headers } : {},
|
|
487
|
+
...bodyInfo ? {
|
|
488
|
+
bodyPreview: bodyInfo.preview,
|
|
489
|
+
bodyLength: bodyInfo.length,
|
|
490
|
+
bodyTruncated: bodyInfo.truncated
|
|
491
|
+
} : {}
|
|
281
492
|
});
|
|
282
493
|
}).catch(() => {
|
|
283
494
|
});
|
|
@@ -954,6 +1165,90 @@ async function executeRoute(ctx) {
|
|
|
954
1165
|
}
|
|
955
1166
|
}
|
|
956
1167
|
|
|
1168
|
+
// src/server/http/form-data-to-object.ts
|
|
1169
|
+
import { z } from "zod";
|
|
1170
|
+
function formDataToObject(formData, schema, prefix = "") {
|
|
1171
|
+
const shape = schema._def.shape();
|
|
1172
|
+
const out = {};
|
|
1173
|
+
for (const [key, rawValidator] of Object.entries(shape)) {
|
|
1174
|
+
const fullKey = prefix + key;
|
|
1175
|
+
const validator = unwrapWrappers(rawValidator);
|
|
1176
|
+
if (validator instanceof z.ZodObject) {
|
|
1177
|
+
const nestedPrefix = `${fullKey}.`;
|
|
1178
|
+
const hasNestedKeys = [...formData.keys()].some((k) => k.startsWith(nestedPrefix));
|
|
1179
|
+
if (hasNestedKeys) {
|
|
1180
|
+
out[key] = formDataToObject(formData, validator, nestedPrefix);
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
out[key] = unwrapMissingDefault(rawValidator);
|
|
1184
|
+
continue;
|
|
1185
|
+
}
|
|
1186
|
+
if (validator instanceof z.ZodArray) {
|
|
1187
|
+
const values = formData.getAll(fullKey);
|
|
1188
|
+
out[key] = coerceArrayElements(values, validator);
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
if (validator instanceof z.ZodBoolean) {
|
|
1192
|
+
out[key] = coerceBoolean(formData, fullKey);
|
|
1193
|
+
continue;
|
|
1194
|
+
}
|
|
1195
|
+
if (formData.has(fullKey)) {
|
|
1196
|
+
const raw = formData.get(fullKey);
|
|
1197
|
+
out[key] = coerceScalar(raw, validator);
|
|
1198
|
+
} else {
|
|
1199
|
+
out[key] = unwrapMissingDefault(rawValidator);
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
return out;
|
|
1203
|
+
}
|
|
1204
|
+
function unwrapWrappers(validator) {
|
|
1205
|
+
let inner = validator;
|
|
1206
|
+
while (inner instanceof z.ZodOptional || inner instanceof z.ZodNullable || inner instanceof z.ZodDefault) {
|
|
1207
|
+
inner = inner._def.innerType;
|
|
1208
|
+
}
|
|
1209
|
+
return inner;
|
|
1210
|
+
}
|
|
1211
|
+
function unwrapMissingDefault(validator) {
|
|
1212
|
+
let cursor = validator;
|
|
1213
|
+
while (cursor instanceof z.ZodOptional || cursor instanceof z.ZodNullable || cursor instanceof z.ZodDefault) {
|
|
1214
|
+
if (cursor instanceof z.ZodDefault) {
|
|
1215
|
+
const def = cursor._def.defaultValue;
|
|
1216
|
+
return typeof def === "function" ? def() : def;
|
|
1217
|
+
}
|
|
1218
|
+
if (cursor instanceof z.ZodNullable) return null;
|
|
1219
|
+
cursor = cursor._def.innerType;
|
|
1220
|
+
}
|
|
1221
|
+
return void 0;
|
|
1222
|
+
}
|
|
1223
|
+
function coerceScalar(raw, validator) {
|
|
1224
|
+
if (raw === null) return void 0;
|
|
1225
|
+
if (validator instanceof z.ZodNumber) {
|
|
1226
|
+
return typeof raw === "string" ? Number(raw) : raw;
|
|
1227
|
+
}
|
|
1228
|
+
return raw;
|
|
1229
|
+
}
|
|
1230
|
+
function coerceArrayElements(values, arrayValidator) {
|
|
1231
|
+
const elementType = unwrapWrappers(arrayValidator._def.type);
|
|
1232
|
+
if (elementType instanceof z.ZodNumber) {
|
|
1233
|
+
return values.map((v) => typeof v === "string" ? Number(v) : v);
|
|
1234
|
+
}
|
|
1235
|
+
if (elementType instanceof z.ZodBoolean) {
|
|
1236
|
+
return values.map((v) => {
|
|
1237
|
+
if (v === "true") return true;
|
|
1238
|
+
if (v === "false") return false;
|
|
1239
|
+
return Boolean(v);
|
|
1240
|
+
});
|
|
1241
|
+
}
|
|
1242
|
+
return values;
|
|
1243
|
+
}
|
|
1244
|
+
function coerceBoolean(formData, key) {
|
|
1245
|
+
if (!formData.has(key)) return void 0;
|
|
1246
|
+
const val = formData.get(key);
|
|
1247
|
+
if (val === "true") return true;
|
|
1248
|
+
if (val === "false") return false;
|
|
1249
|
+
return Boolean(val);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
957
1252
|
// src/server/http/handle-request-error.ts
|
|
958
1253
|
async function handleRequestError(err, c) {
|
|
959
1254
|
if (c.pluginRunner) {
|
|
@@ -999,7 +1294,78 @@ async function handleRequestError(err, c) {
|
|
|
999
1294
|
}
|
|
1000
1295
|
}
|
|
1001
1296
|
|
|
1297
|
+
// src/server/http/serialize-action-result.ts
|
|
1298
|
+
import { stringify as devalueStringify } from "devalue";
|
|
1299
|
+
var DEFAULT_RESPONSE_BODY_SIZE_LIMIT = 5 * 1024 * 1024;
|
|
1300
|
+
function serializeActionResult(result, options = {}) {
|
|
1301
|
+
const limit = options.responseBodySizeLimit ?? DEFAULT_RESPONSE_BODY_SIZE_LIMIT;
|
|
1302
|
+
if (result.error !== void 0) {
|
|
1303
|
+
const body2 = result.error instanceof ActionInputError ? JSON.stringify({
|
|
1304
|
+
type: result.error.type,
|
|
1305
|
+
code: result.error.code,
|
|
1306
|
+
message: result.error.message,
|
|
1307
|
+
issues: result.error.issues,
|
|
1308
|
+
fields: result.error.fields
|
|
1309
|
+
}) : JSON.stringify({
|
|
1310
|
+
type: result.error.type,
|
|
1311
|
+
code: result.error.code,
|
|
1312
|
+
message: result.error.message
|
|
1313
|
+
});
|
|
1314
|
+
if (Buffer.byteLength(body2, "utf8") > limit) {
|
|
1315
|
+
throw new ActionError({
|
|
1316
|
+
code: "PAYLOAD_TOO_LARGE",
|
|
1317
|
+
message: `Serialized error body exceeds limit (${limit} bytes)`
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
return {
|
|
1321
|
+
type: "error",
|
|
1322
|
+
status: result.error.status,
|
|
1323
|
+
contentType: "application/json",
|
|
1324
|
+
body: body2
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
if (result.data === void 0) {
|
|
1328
|
+
return { type: "empty", status: 204 };
|
|
1329
|
+
}
|
|
1330
|
+
if (result.data instanceof Response) {
|
|
1331
|
+
throw new ActionError({
|
|
1332
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
1333
|
+
message: "Action handler cannot serialize Response objects \u2014 return plain data instead"
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1336
|
+
let body;
|
|
1337
|
+
try {
|
|
1338
|
+
body = devalueStringify(result.data, {
|
|
1339
|
+
URL: (value) => value instanceof URL && value.href
|
|
1340
|
+
});
|
|
1341
|
+
} catch (e) {
|
|
1342
|
+
throw new ActionError({
|
|
1343
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
1344
|
+
message: `Action data not serializable: ${e instanceof Error ? e.message : String(e)}`
|
|
1345
|
+
});
|
|
1346
|
+
}
|
|
1347
|
+
if (Buffer.byteLength(body, "utf8") > limit) {
|
|
1348
|
+
throw new ActionError({
|
|
1349
|
+
code: "PAYLOAD_TOO_LARGE",
|
|
1350
|
+
message: `Serialized response body exceeds limit (${limit} bytes)`
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
return {
|
|
1354
|
+
type: "data",
|
|
1355
|
+
status: 200,
|
|
1356
|
+
contentType: "application/json+devalue",
|
|
1357
|
+
body
|
|
1358
|
+
};
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1002
1361
|
// src/server/http/action-execute.ts
|
|
1362
|
+
var __IS_DEV = (() => {
|
|
1363
|
+
try {
|
|
1364
|
+
return import.meta.env?.DEV === true;
|
|
1365
|
+
} catch {
|
|
1366
|
+
return process.env.NODE_ENV !== "production";
|
|
1367
|
+
}
|
|
1368
|
+
})();
|
|
1003
1369
|
function isActionConfig(value) {
|
|
1004
1370
|
if (typeof value !== "object" || value === null) return false;
|
|
1005
1371
|
const candidate = value;
|
|
@@ -1066,16 +1432,72 @@ async function runPreHandlerPipeline(p) {
|
|
|
1066
1432
|
}
|
|
1067
1433
|
return true;
|
|
1068
1434
|
}
|
|
1069
|
-
async function readActionBody(req, res, requestId) {
|
|
1435
|
+
async function readActionBody(req, res, requestId, actionConfig) {
|
|
1070
1436
|
try {
|
|
1071
1437
|
const parsed = await parseRequestBody(req);
|
|
1072
|
-
const
|
|
1438
|
+
const accept = actionConfig.accept ?? "json";
|
|
1439
|
+
let body;
|
|
1440
|
+
if (accept === "form") {
|
|
1441
|
+
body = formDataToObject(
|
|
1442
|
+
synthesizeFormData(parsed),
|
|
1443
|
+
actionConfig.input
|
|
1444
|
+
);
|
|
1445
|
+
} else if (parsed.json !== void 0) {
|
|
1446
|
+
body = parsed.json;
|
|
1447
|
+
} else {
|
|
1448
|
+
body = parsed.fields;
|
|
1449
|
+
}
|
|
1073
1450
|
return { ok: true, body };
|
|
1074
1451
|
} catch (err) {
|
|
1075
1452
|
sendError(res, "VALIDATION_ERROR", err.message, 400, void 0, requestId);
|
|
1076
1453
|
return { ok: false };
|
|
1077
1454
|
}
|
|
1078
1455
|
}
|
|
1456
|
+
function synthesizeFormData(parsed) {
|
|
1457
|
+
const fd = new FormData();
|
|
1458
|
+
for (const [name, value] of Object.entries(parsed.fields)) {
|
|
1459
|
+
fd.append(name, value);
|
|
1460
|
+
}
|
|
1461
|
+
for (const file of parsed.files) {
|
|
1462
|
+
const blob = new Blob([file.buffer], {
|
|
1463
|
+
type: file.mimeType
|
|
1464
|
+
});
|
|
1465
|
+
fd.append(file.fieldname, blob, file.filename);
|
|
1466
|
+
}
|
|
1467
|
+
return fd;
|
|
1468
|
+
}
|
|
1469
|
+
function writeSerialized(res, serialized) {
|
|
1470
|
+
if (serialized.type === "empty") {
|
|
1471
|
+
res.statusCode = serialized.status;
|
|
1472
|
+
res.end();
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
res.statusCode = serialized.status;
|
|
1476
|
+
res.setHeader("Content-Type", serialized.contentType);
|
|
1477
|
+
res.end(serialized.body);
|
|
1478
|
+
}
|
|
1479
|
+
async function emitActionCallTelemetry(name, startedAt, input, outcome) {
|
|
1480
|
+
if (!__IS_DEV) return;
|
|
1481
|
+
try {
|
|
1482
|
+
const mod = await import("./dispatcher-AQJGI3HU.js");
|
|
1483
|
+
mod.dispatcher.onActionCall({
|
|
1484
|
+
// eslint-disable-next-line sonarjs/pseudo-random -- non-secret correlation id
|
|
1485
|
+
id: `act-${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`,
|
|
1486
|
+
timestamp: startedAt,
|
|
1487
|
+
name,
|
|
1488
|
+
input,
|
|
1489
|
+
output: outcome.status === "success" ? outcome.output : void 0,
|
|
1490
|
+
error: outcome.status === "error" ? {
|
|
1491
|
+
code: outcome.error.code,
|
|
1492
|
+
message: outcome.error.message,
|
|
1493
|
+
fields: outcome.error instanceof ActionInputError ? outcome.error.fields : void 0
|
|
1494
|
+
} : void 0,
|
|
1495
|
+
durationMs: Date.now() - startedAt,
|
|
1496
|
+
status: outcome.status
|
|
1497
|
+
});
|
|
1498
|
+
} catch {
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1079
1501
|
async function executeActionWithOptions(opts) {
|
|
1080
1502
|
const {
|
|
1081
1503
|
filePath,
|
|
@@ -1122,22 +1544,24 @@ async function executeActionWithOptions(opts) {
|
|
|
1122
1544
|
};
|
|
1123
1545
|
if (!await runPreHandlerPipeline(pipeline)) return;
|
|
1124
1546
|
ctx = pipeline.ctx;
|
|
1125
|
-
const bodyOutcome = await readActionBody(req, res, requestId);
|
|
1547
|
+
const bodyOutcome = await readActionBody(req, res, requestId, actionConfig);
|
|
1126
1548
|
if (!bodyOutcome.ok) return;
|
|
1549
|
+
const actionName = exportName === "default" ? deriveActionNameFromPath(filePath) : exportName;
|
|
1550
|
+
const startedAt = Date.now();
|
|
1127
1551
|
const result = actionConfig.input.safeParse(bodyOutcome.body);
|
|
1128
1552
|
if (!result.success) {
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
requestId
|
|
1136
|
-
);
|
|
1553
|
+
const inputErr = new ActionInputError(result.error?.issues ?? []);
|
|
1554
|
+
writeSerialized(res, serializeActionResult({ data: void 0, error: inputErr }));
|
|
1555
|
+
await emitActionCallTelemetry(actionName, startedAt, bodyOutcome.body, {
|
|
1556
|
+
status: "error",
|
|
1557
|
+
error: inputErr
|
|
1558
|
+
});
|
|
1137
1559
|
return;
|
|
1138
1560
|
}
|
|
1139
1561
|
await runActionHandler({
|
|
1140
1562
|
actionConfig,
|
|
1563
|
+
actionName,
|
|
1564
|
+
startedAt,
|
|
1141
1565
|
input: result.data,
|
|
1142
1566
|
ctx,
|
|
1143
1567
|
res,
|
|
@@ -1148,12 +1572,44 @@ async function executeActionWithOptions(opts) {
|
|
|
1148
1572
|
await handleActionError(err, { req, res, ctx, requestId, pluginRunner, buildPluginCtx });
|
|
1149
1573
|
}
|
|
1150
1574
|
}
|
|
1575
|
+
function deriveActionNameFromPath(filePath) {
|
|
1576
|
+
const base = filePath.split(/[\\/]/).pop() ?? "unknown";
|
|
1577
|
+
return base.replace(/\.[jt]sx?$/, "");
|
|
1578
|
+
}
|
|
1579
|
+
function isActionErrorLike(err) {
|
|
1580
|
+
if (err === null || typeof err !== "object") return false;
|
|
1581
|
+
const obj = err;
|
|
1582
|
+
return (obj.type === "TheoActionError" || obj.type === "TheoActionInputError") && typeof obj.code === "string" && typeof obj.status === "number";
|
|
1583
|
+
}
|
|
1151
1584
|
async function runActionHandler(args) {
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1585
|
+
try {
|
|
1586
|
+
const handlerResult = await args.actionConfig.handler({ input: args.input, ctx: args.ctx });
|
|
1587
|
+
writeSerialized(args.res, serializeActionResult({ data: handlerResult, error: void 0 }));
|
|
1588
|
+
if (args.pluginRunner) {
|
|
1589
|
+
await args.pluginRunner.runOnResponse(args.buildPluginCtx(args.ctx));
|
|
1590
|
+
}
|
|
1591
|
+
await emitActionCallTelemetry(args.actionName, args.startedAt, args.input, {
|
|
1592
|
+
status: "success",
|
|
1593
|
+
output: handlerResult
|
|
1594
|
+
});
|
|
1595
|
+
} catch (err) {
|
|
1596
|
+
if (isActionErrorLike(err)) {
|
|
1597
|
+
writeSerialized(args.res, serializeActionResult({ data: void 0, error: err }));
|
|
1598
|
+
await emitActionCallTelemetry(args.actionName, args.startedAt, args.input, {
|
|
1599
|
+
status: "error",
|
|
1600
|
+
error: err
|
|
1601
|
+
});
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
const wrapped = new ActionError({
|
|
1605
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
1606
|
+
message: err instanceof Error ? err.message : "Action handler threw"
|
|
1607
|
+
});
|
|
1608
|
+
await emitActionCallTelemetry(args.actionName, args.startedAt, args.input, {
|
|
1609
|
+
status: "error",
|
|
1610
|
+
error: wrapped
|
|
1611
|
+
});
|
|
1612
|
+
throw err;
|
|
1157
1613
|
}
|
|
1158
1614
|
}
|
|
1159
1615
|
async function handleActionError(err, c) {
|
|
@@ -1197,28 +1653,6 @@ function findSuggestion(input, candidates, maxDistance = 3) {
|
|
|
1197
1653
|
return best;
|
|
1198
1654
|
}
|
|
1199
1655
|
|
|
1200
|
-
// src/server/scan/action-scan.ts
|
|
1201
|
-
import { existsSync as existsSync3, statSync as statSync3 } from "fs";
|
|
1202
|
-
import { extname as extname2, join as join3, relative } from "path";
|
|
1203
|
-
var ACTION_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
1204
|
-
function scanServerActions(serverDir) {
|
|
1205
|
-
const actionsDir = join3(serverDir, "actions");
|
|
1206
|
-
if (!existsSync3(actionsDir) || !statSync3(actionsDir).isDirectory()) {
|
|
1207
|
-
return [];
|
|
1208
|
-
}
|
|
1209
|
-
const results = [];
|
|
1210
|
-
walkSourceFiles(actionsDir, { extensions: ACTION_EXTENSIONS }, (absPath) => {
|
|
1211
|
-
let rel = relative(actionsDir, absPath);
|
|
1212
|
-
rel = rel.replace(/\\/g, "/");
|
|
1213
|
-
rel = rel.slice(0, -extname2(rel).length);
|
|
1214
|
-
results.push({
|
|
1215
|
-
filePath: absPath,
|
|
1216
|
-
actionPath: rel
|
|
1217
|
-
});
|
|
1218
|
-
});
|
|
1219
|
-
return results;
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
1656
|
// src/server/scan/module-loader.ts
|
|
1223
1657
|
import { pathToFileURL } from "url";
|
|
1224
1658
|
function createViteLoader(vite) {
|
|
@@ -1232,7 +1666,7 @@ function createProductionLoader() {
|
|
|
1232
1666
|
}
|
|
1233
1667
|
|
|
1234
1668
|
// src/server/http/batch-handler.ts
|
|
1235
|
-
import { z } from "zod";
|
|
1669
|
+
import { z as z2 } from "zod";
|
|
1236
1670
|
var STRIPPED_HEADERS = [
|
|
1237
1671
|
"authorization",
|
|
1238
1672
|
"cookie",
|
|
@@ -1252,15 +1686,15 @@ var BatchPathConflictError = class extends Error {
|
|
|
1252
1686
|
this.name = "BatchPathConflictError";
|
|
1253
1687
|
}
|
|
1254
1688
|
};
|
|
1255
|
-
var batchRequestSchema =
|
|
1256
|
-
path:
|
|
1257
|
-
method:
|
|
1258
|
-
query:
|
|
1259
|
-
body:
|
|
1260
|
-
headers:
|
|
1689
|
+
var batchRequestSchema = z2.object({
|
|
1690
|
+
path: z2.string().min(1),
|
|
1691
|
+
method: z2.string().min(1),
|
|
1692
|
+
query: z2.record(z2.unknown()).optional(),
|
|
1693
|
+
body: z2.unknown().optional(),
|
|
1694
|
+
headers: z2.record(z2.string()).optional()
|
|
1261
1695
|
});
|
|
1262
|
-
var batchPayloadSchema =
|
|
1263
|
-
requests:
|
|
1696
|
+
var batchPayloadSchema = z2.object({
|
|
1697
|
+
requests: z2.array(batchRequestSchema).min(1)
|
|
1264
1698
|
});
|
|
1265
1699
|
function sanitizeItemHeaders(itemHeaders, outerHeaders) {
|
|
1266
1700
|
const out = {};
|
|
@@ -1535,88 +1969,6 @@ function resultFromState(state, config) {
|
|
|
1535
1969
|
};
|
|
1536
1970
|
}
|
|
1537
1971
|
|
|
1538
|
-
// src/server/scan/match.ts
|
|
1539
|
-
function compilePattern(routePath) {
|
|
1540
|
-
const paramNames = [];
|
|
1541
|
-
const regexStr = routePath.replace(/:(?:\.\.\.)?([^/]+)/g, (match, name) => {
|
|
1542
|
-
paramNames.push(name);
|
|
1543
|
-
return match.startsWith(":...") ? "(.+)" : "([^/]+)";
|
|
1544
|
-
});
|
|
1545
|
-
return { pattern: new RegExp(`^${regexStr}$`), paramNames };
|
|
1546
|
-
}
|
|
1547
|
-
function matchRoute(url, routes) {
|
|
1548
|
-
let path = url.split("?")[0];
|
|
1549
|
-
if (path.length > 1 && path.endsWith("/")) {
|
|
1550
|
-
path = path.slice(0, -1);
|
|
1551
|
-
}
|
|
1552
|
-
for (const route of routes) {
|
|
1553
|
-
const match = route.pattern.exec(path);
|
|
1554
|
-
if (match) {
|
|
1555
|
-
const params = {};
|
|
1556
|
-
route.paramNames.forEach((name, i) => {
|
|
1557
|
-
params[name] = match[i + 1];
|
|
1558
|
-
});
|
|
1559
|
-
return { route, params };
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
return null;
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
// src/server/scan/scan.ts
|
|
1566
|
-
import { existsSync as existsSync4, statSync as statSync4 } from "fs";
|
|
1567
|
-
import { extname as extname3, join as join4, relative as relative2 } from "path";
|
|
1568
|
-
var ROUTE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
1569
|
-
function fileToRoutePath(filePath, routesDir) {
|
|
1570
|
-
let rel = relative2(routesDir, filePath);
|
|
1571
|
-
const ext = extname3(rel);
|
|
1572
|
-
rel = rel.slice(0, -ext.length);
|
|
1573
|
-
rel = rel.replace(/\\/g, "/");
|
|
1574
|
-
if (rel.endsWith("/index")) {
|
|
1575
|
-
rel = rel.slice(0, -6);
|
|
1576
|
-
} else if (rel === "index") {
|
|
1577
|
-
rel = "";
|
|
1578
|
-
}
|
|
1579
|
-
rel = rel.replace(/\[\.\.\.([^\]]+)\]/g, ":...$1");
|
|
1580
|
-
rel = rel.replace(/\[([^\]]+)\]/g, ":$1");
|
|
1581
|
-
return `/api/${rel}`;
|
|
1582
|
-
}
|
|
1583
|
-
function scanServerRoutes(serverDir) {
|
|
1584
|
-
const routesDir = join4(serverDir, "routes");
|
|
1585
|
-
if (!existsSync4(routesDir) || !statSync4(routesDir).isDirectory()) {
|
|
1586
|
-
return [];
|
|
1587
|
-
}
|
|
1588
|
-
const results = [];
|
|
1589
|
-
walkSourceFiles(routesDir, { extensions: ROUTE_EXTENSIONS }, (absPath) => {
|
|
1590
|
-
const routePath = fileToRoutePath(absPath, routesDir);
|
|
1591
|
-
const { pattern, paramNames } = compilePattern(routePath);
|
|
1592
|
-
results.push({
|
|
1593
|
-
filePath: absPath,
|
|
1594
|
-
routePath,
|
|
1595
|
-
paramNames,
|
|
1596
|
-
pattern
|
|
1597
|
-
});
|
|
1598
|
-
});
|
|
1599
|
-
const conflicting = results.find((r) => r.routePath === "/api/__theo_batch__");
|
|
1600
|
-
if (conflicting) {
|
|
1601
|
-
throw new Error(
|
|
1602
|
-
`Server route ${conflicting.filePath} resolves to '/api/__theo_batch__' which is reserved for the batch endpoint. Rename the route or disable batching in theo.config.ts.`
|
|
1603
|
-
);
|
|
1604
|
-
}
|
|
1605
|
-
const isCatchAll = (route) => route.routePath.includes(":...");
|
|
1606
|
-
results.sort((a, b) => {
|
|
1607
|
-
const aStatic = a.paramNames.length === 0;
|
|
1608
|
-
const bStatic = b.paramNames.length === 0;
|
|
1609
|
-
const aCatchAll = isCatchAll(a);
|
|
1610
|
-
const bCatchAll = isCatchAll(b);
|
|
1611
|
-
if (aStatic && !bStatic) return -1;
|
|
1612
|
-
if (!aStatic && bStatic) return 1;
|
|
1613
|
-
if (aCatchAll && !bCatchAll) return 1;
|
|
1614
|
-
if (!aCatchAll && bCatchAll) return -1;
|
|
1615
|
-
return a.routePath.localeCompare(b.routePath);
|
|
1616
|
-
});
|
|
1617
|
-
return results;
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
1972
|
// src/server/security/csp-report.ts
|
|
1621
1973
|
var CSP_REPORT_PATH = "/__theo/csp-report";
|
|
1622
1974
|
var MAX_BODY = 16 * 1024;
|
|
@@ -2029,33 +2381,15 @@ function resolveTransformer(selector) {
|
|
|
2029
2381
|
return selector;
|
|
2030
2382
|
}
|
|
2031
2383
|
|
|
2032
|
-
// src/server/scan/ws-scan.ts
|
|
2033
|
-
import { existsSync as existsSync5, statSync as statSync5 } from "fs";
|
|
2034
|
-
import { extname as extname4, join as join5, relative as relative3 } from "path";
|
|
2035
|
-
var WS_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
2036
|
-
function scanWebSocketRoutes(serverDir) {
|
|
2037
|
-
const wsDir = join5(serverDir, "ws");
|
|
2038
|
-
if (!existsSync5(wsDir) || !statSync5(wsDir).isDirectory()) {
|
|
2039
|
-
return [];
|
|
2040
|
-
}
|
|
2041
|
-
const results = [];
|
|
2042
|
-
walkSourceFiles(wsDir, { extensions: WS_EXTENSIONS }, (absPath) => {
|
|
2043
|
-
let rel = relative3(wsDir, absPath);
|
|
2044
|
-
rel = rel.replace(/\\/g, "/");
|
|
2045
|
-
rel = rel.slice(0, -extname4(rel).length);
|
|
2046
|
-
if (rel.endsWith("/index")) rel = rel.slice(0, -6);
|
|
2047
|
-
else if (rel === "index") rel = "";
|
|
2048
|
-
results.push({
|
|
2049
|
-
filePath: absPath,
|
|
2050
|
-
wsPath: `/ws/${rel}`
|
|
2051
|
-
});
|
|
2052
|
-
});
|
|
2053
|
-
return results;
|
|
2054
|
-
}
|
|
2055
|
-
|
|
2056
2384
|
export {
|
|
2057
2385
|
_resetEnvCache,
|
|
2058
2386
|
loadEnv,
|
|
2387
|
+
CsrfReadinessStore,
|
|
2388
|
+
ActionError,
|
|
2389
|
+
ActionInputError,
|
|
2390
|
+
extractUniversalIssues,
|
|
2391
|
+
isActionError,
|
|
2392
|
+
isInputError,
|
|
2059
2393
|
FileTooLargeError,
|
|
2060
2394
|
parseRequestBody,
|
|
2061
2395
|
logRequest,
|
|
@@ -2079,7 +2413,6 @@ export {
|
|
|
2079
2413
|
executeAction,
|
|
2080
2414
|
levenshtein,
|
|
2081
2415
|
findSuggestion,
|
|
2082
|
-
scanServerActions,
|
|
2083
2416
|
createViteLoader,
|
|
2084
2417
|
createProductionLoader,
|
|
2085
2418
|
STRIPPED_HEADERS,
|
|
@@ -2094,9 +2427,6 @@ export {
|
|
|
2094
2427
|
extractTraceId,
|
|
2095
2428
|
InMemoryStore,
|
|
2096
2429
|
createRateLimiter,
|
|
2097
|
-
compilePattern,
|
|
2098
|
-
matchRoute,
|
|
2099
|
-
scanServerRoutes,
|
|
2100
2430
|
CSP_REPORT_PATH,
|
|
2101
2431
|
normalizeLegacy,
|
|
2102
2432
|
normalizeNew,
|
|
@@ -2115,7 +2445,6 @@ export {
|
|
|
2115
2445
|
createPluginRunnerFromConfig,
|
|
2116
2446
|
superjsonTransformer,
|
|
2117
2447
|
jsonTransformer,
|
|
2118
|
-
resolveTransformer
|
|
2119
|
-
scanWebSocketRoutes
|
|
2448
|
+
resolveTransformer
|
|
2120
2449
|
};
|
|
2121
|
-
//# sourceMappingURL=chunk-
|
|
2450
|
+
//# sourceMappingURL=chunk-OJHO2X7C.js.map
|