balda-js 0.0.39 → 0.0.41
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/lib/index.cjs +134 -55
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +42 -31
- package/lib/index.d.ts +42 -31
- package/lib/index.js +134 -55
- package/lib/index.js.map +1 -1
- package/package.json +3 -3
package/lib/index.cjs
CHANGED
|
@@ -5,7 +5,7 @@ var pino = require('pino');
|
|
|
5
5
|
var path = require('path');
|
|
6
6
|
var Ajv = require('ajv');
|
|
7
7
|
var addFormats = require('ajv-formats');
|
|
8
|
-
var
|
|
8
|
+
var zod = require('zod');
|
|
9
9
|
var fs = require('fs/promises');
|
|
10
10
|
var http = require('http');
|
|
11
11
|
|
|
@@ -948,7 +948,9 @@ var ajv = addFormats__default.default(new Ajv__default.default(), [
|
|
|
948
948
|
"iso-time"
|
|
949
949
|
]);
|
|
950
950
|
var validateSchema = (inputSchema, data, safe = false) => {
|
|
951
|
-
const
|
|
951
|
+
const jsonSchema = zod.z.toJSONSchema(inputSchema);
|
|
952
|
+
const { $schema, ...schemaWithoutMeta } = jsonSchema;
|
|
953
|
+
const validate2 = ajv.compile(schemaWithoutMeta);
|
|
952
954
|
if (!validate2(data)) {
|
|
953
955
|
if (safe) {
|
|
954
956
|
return data;
|
|
@@ -984,16 +986,17 @@ var serialize = (schema, options) => {
|
|
|
984
986
|
descriptor.value[SERIALIZE_METADATA] = {};
|
|
985
987
|
}
|
|
986
988
|
descriptor.value[SERIALIZE_METADATA][status] = {
|
|
989
|
+
name: propertyKey,
|
|
987
990
|
schema,
|
|
988
991
|
safe: options?.safe ?? true
|
|
989
992
|
};
|
|
990
993
|
if (!descriptor.value[SERIALIZE_WRAPPED]) {
|
|
991
994
|
const originalMethod = descriptor.value;
|
|
992
|
-
|
|
995
|
+
const wrappedFunction = async function(...args2) {
|
|
993
996
|
const res = args2[1];
|
|
994
997
|
await originalMethod.apply(this, args2);
|
|
995
998
|
const actualStatus = res.responseStatus;
|
|
996
|
-
const serializeMetadata =
|
|
999
|
+
const serializeMetadata = wrappedFunction[SERIALIZE_METADATA];
|
|
997
1000
|
const schema2 = serializeMetadata?.[actualStatus]?.schema;
|
|
998
1001
|
const safe = serializeMetadata?.[actualStatus]?.safe ?? true;
|
|
999
1002
|
if (schema2 && !safe) {
|
|
@@ -1013,8 +1016,9 @@ var serialize = (schema, options) => {
|
|
|
1013
1016
|
}
|
|
1014
1017
|
}
|
|
1015
1018
|
};
|
|
1016
|
-
|
|
1017
|
-
|
|
1019
|
+
wrappedFunction[SERIALIZE_WRAPPED] = true;
|
|
1020
|
+
wrappedFunction[SERIALIZE_METADATA] = originalMethod[SERIALIZE_METADATA];
|
|
1021
|
+
descriptor.value = wrappedFunction;
|
|
1018
1022
|
}
|
|
1019
1023
|
};
|
|
1020
1024
|
};
|
|
@@ -2220,8 +2224,6 @@ var commandRegistry = CommandRegistry.getInstance();
|
|
|
2220
2224
|
// src/runtime/native_request.ts
|
|
2221
2225
|
var NativeRequest = class extends Request {
|
|
2222
2226
|
};
|
|
2223
|
-
|
|
2224
|
-
// src/server/http/request.ts
|
|
2225
2227
|
var Request2 = class _Request extends NativeRequest {
|
|
2226
2228
|
static fromRequest(request) {
|
|
2227
2229
|
return new _Request(request.url, {
|
|
@@ -2236,19 +2238,19 @@ var Request2 = class _Request extends NativeRequest {
|
|
|
2236
2238
|
static enrichRequest(request) {
|
|
2237
2239
|
request.validate = (inputSchema, safe = false) => {
|
|
2238
2240
|
if (typeof inputSchema === "function") {
|
|
2239
|
-
inputSchema = inputSchema(
|
|
2241
|
+
inputSchema = inputSchema(zod.z);
|
|
2240
2242
|
}
|
|
2241
2243
|
return validateSchema(inputSchema, request.body || {}, safe);
|
|
2242
2244
|
};
|
|
2243
2245
|
request.validateQuery = (inputSchema, safe = false) => {
|
|
2244
2246
|
if (typeof inputSchema === "function") {
|
|
2245
|
-
inputSchema = inputSchema(
|
|
2247
|
+
inputSchema = inputSchema(zod.z);
|
|
2246
2248
|
}
|
|
2247
2249
|
return validateSchema(inputSchema, request.query || {}, safe);
|
|
2248
2250
|
};
|
|
2249
2251
|
request.validateAll = (inputSchema, safe = false) => {
|
|
2250
2252
|
if (typeof inputSchema === "function") {
|
|
2251
|
-
inputSchema = inputSchema(
|
|
2253
|
+
inputSchema = inputSchema(zod.z);
|
|
2252
2254
|
}
|
|
2253
2255
|
return validateSchema(
|
|
2254
2256
|
inputSchema,
|
|
@@ -2355,7 +2357,7 @@ var Request2 = class _Request extends NativeRequest {
|
|
|
2355
2357
|
*/
|
|
2356
2358
|
validate(inputSchema, safe = false) {
|
|
2357
2359
|
if (typeof inputSchema === "function") {
|
|
2358
|
-
inputSchema = inputSchema(
|
|
2360
|
+
inputSchema = inputSchema(zod.z);
|
|
2359
2361
|
}
|
|
2360
2362
|
return validateSchema(inputSchema, this.body || {}, safe);
|
|
2361
2363
|
}
|
|
@@ -2364,7 +2366,7 @@ var Request2 = class _Request extends NativeRequest {
|
|
|
2364
2366
|
*/
|
|
2365
2367
|
validateQuery(inputSchema, safe = false) {
|
|
2366
2368
|
if (typeof inputSchema === "function") {
|
|
2367
|
-
inputSchema = inputSchema(
|
|
2369
|
+
inputSchema = inputSchema(zod.z);
|
|
2368
2370
|
}
|
|
2369
2371
|
return validateSchema(inputSchema, this.query || {}, safe);
|
|
2370
2372
|
}
|
|
@@ -2373,7 +2375,7 @@ var Request2 = class _Request extends NativeRequest {
|
|
|
2373
2375
|
*/
|
|
2374
2376
|
validateAll(inputSchema, safe = false) {
|
|
2375
2377
|
if (typeof inputSchema === "function") {
|
|
2376
|
-
inputSchema = inputSchema(
|
|
2378
|
+
inputSchema = inputSchema(zod.z);
|
|
2377
2379
|
}
|
|
2378
2380
|
return validateSchema(
|
|
2379
2381
|
inputSchema,
|
|
@@ -2385,21 +2387,6 @@ var Request2 = class _Request extends NativeRequest {
|
|
|
2385
2387
|
);
|
|
2386
2388
|
}
|
|
2387
2389
|
};
|
|
2388
|
-
var NativeFile = class {
|
|
2389
|
-
async file(path2) {
|
|
2390
|
-
switch (runtime.type) {
|
|
2391
|
-
case "bun":
|
|
2392
|
-
return Bun.file(path2).arrayBuffer();
|
|
2393
|
-
case "node":
|
|
2394
|
-
return fs__default.default.readFile(path2);
|
|
2395
|
-
case "deno":
|
|
2396
|
-
return Deno.readFile(path2);
|
|
2397
|
-
default:
|
|
2398
|
-
throw new Error("Unsupported runtime");
|
|
2399
|
-
}
|
|
2400
|
-
}
|
|
2401
|
-
};
|
|
2402
|
-
var nativeFile = new NativeFile();
|
|
2403
2390
|
|
|
2404
2391
|
// src/errors/error_factory.ts
|
|
2405
2392
|
var errorFactory = (error) => {
|
|
@@ -2423,6 +2410,21 @@ var RouteNotFoundError = class extends BaldaError {
|
|
|
2423
2410
|
super(`ROUTE_NOT_FOUND: Cannot ${method} ${path2}`);
|
|
2424
2411
|
}
|
|
2425
2412
|
};
|
|
2413
|
+
var NativeFile = class {
|
|
2414
|
+
async file(path2) {
|
|
2415
|
+
switch (runtime.type) {
|
|
2416
|
+
case "bun":
|
|
2417
|
+
return Bun.file(path2).arrayBuffer();
|
|
2418
|
+
case "node":
|
|
2419
|
+
return fs__default.default.readFile(path2);
|
|
2420
|
+
case "deno":
|
|
2421
|
+
return Deno.readFile(path2);
|
|
2422
|
+
default:
|
|
2423
|
+
throw new Error("Unsupported runtime");
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
};
|
|
2427
|
+
var nativeFile = new NativeFile();
|
|
2426
2428
|
|
|
2427
2429
|
// src/plugins/static/static_constants.ts
|
|
2428
2430
|
var mimeTypes = /* @__PURE__ */ new Map([
|
|
@@ -2629,7 +2631,7 @@ var Response2 = class {
|
|
|
2629
2631
|
*/
|
|
2630
2632
|
file(pathToFile) {
|
|
2631
2633
|
const mimeType = getContentType(pathToFile);
|
|
2632
|
-
nativeFile.file(pathToFile);
|
|
2634
|
+
this.body = nativeFile.file(pathToFile);
|
|
2633
2635
|
this.headers = {
|
|
2634
2636
|
...this.headers,
|
|
2635
2637
|
"Content-Type": mimeType
|
|
@@ -2827,6 +2829,31 @@ var Response2 = class {
|
|
|
2827
2829
|
httpVersionNotSupported(body) {
|
|
2828
2830
|
this.status(505).send(body);
|
|
2829
2831
|
}
|
|
2832
|
+
/**
|
|
2833
|
+
* Stream a response using an async generator or ReadableStream
|
|
2834
|
+
* Sets appropriate headers for Server-Sent Events by default
|
|
2835
|
+
*/
|
|
2836
|
+
stream(source, options) {
|
|
2837
|
+
const contentType = options?.contentType ?? "text/event-stream";
|
|
2838
|
+
this.headers = {
|
|
2839
|
+
...this.headers,
|
|
2840
|
+
"Content-Type": contentType,
|
|
2841
|
+
"Cache-Control": "no-cache",
|
|
2842
|
+
Connection: "keep-alive"
|
|
2843
|
+
};
|
|
2844
|
+
if (source instanceof ReadableStream) {
|
|
2845
|
+
this.body = source;
|
|
2846
|
+
return;
|
|
2847
|
+
}
|
|
2848
|
+
this.body = new ReadableStream({
|
|
2849
|
+
async start(controller2) {
|
|
2850
|
+
for await (const chunk of source) {
|
|
2851
|
+
controller2.enqueue(new TextEncoder().encode(chunk));
|
|
2852
|
+
}
|
|
2853
|
+
controller2.close();
|
|
2854
|
+
}
|
|
2855
|
+
});
|
|
2856
|
+
}
|
|
2830
2857
|
/**
|
|
2831
2858
|
* Get the body of the response
|
|
2832
2859
|
*/
|
|
@@ -4134,8 +4161,6 @@ function parseMimeType(contentType) {
|
|
|
4134
4161
|
}
|
|
4135
4162
|
return trimmed.substring(0, semicolonIndex).trim().toLowerCase();
|
|
4136
4163
|
}
|
|
4137
|
-
|
|
4138
|
-
// src/plugins/swagger/swagger.ts
|
|
4139
4164
|
var swagger = (globalOptions) => {
|
|
4140
4165
|
let swaggerOptions = {
|
|
4141
4166
|
type: "standard",
|
|
@@ -4167,9 +4192,54 @@ var swagger = (globalOptions) => {
|
|
|
4167
4192
|
res.json(spec);
|
|
4168
4193
|
});
|
|
4169
4194
|
};
|
|
4195
|
+
function safeToJSONSchema(schema) {
|
|
4196
|
+
try {
|
|
4197
|
+
return zod.z.toJSONSchema(schema);
|
|
4198
|
+
} catch (error) {
|
|
4199
|
+
if (error instanceof Error && error.message.includes(
|
|
4200
|
+
"Custom types cannot be represented in JSON Schema"
|
|
4201
|
+
)) {
|
|
4202
|
+
const def = schema._def;
|
|
4203
|
+
if (def?.typeName === "ZodInstanceof") {
|
|
4204
|
+
const testFile = new File([], "test");
|
|
4205
|
+
if (schema.safeParse(testFile).success) {
|
|
4206
|
+
return { type: "string", format: "binary" };
|
|
4207
|
+
}
|
|
4208
|
+
}
|
|
4209
|
+
if (def?.typeName === "ZodObject" && def?.shape) {
|
|
4210
|
+
const properties = {};
|
|
4211
|
+
const required = [];
|
|
4212
|
+
for (const [key, fieldSchema] of Object.entries(def.shape)) {
|
|
4213
|
+
try {
|
|
4214
|
+
properties[key] = safeToJSONSchema(fieldSchema);
|
|
4215
|
+
const fieldDef = fieldSchema._def;
|
|
4216
|
+
if (fieldDef?.typeName !== "ZodOptional" && fieldDef?.typeName !== "ZodDefault") {
|
|
4217
|
+
required.push(key);
|
|
4218
|
+
}
|
|
4219
|
+
} catch {
|
|
4220
|
+
properties[key] = { type: "string", format: "binary" };
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
return {
|
|
4224
|
+
type: "object",
|
|
4225
|
+
properties,
|
|
4226
|
+
...required.length > 0 ? { required } : {}
|
|
4227
|
+
};
|
|
4228
|
+
}
|
|
4229
|
+
return { type: "object", description: "Custom type" };
|
|
4230
|
+
}
|
|
4231
|
+
throw error;
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4170
4234
|
function generateOpenAPISpec(globalOptions) {
|
|
4171
4235
|
const routes = router.getRoutes();
|
|
4172
4236
|
const paths = {};
|
|
4237
|
+
if (Array.isArray(globalOptions.models)) {
|
|
4238
|
+
globalOptions.models = globalOptions.models.reduce((acc, model) => {
|
|
4239
|
+
acc[model.$id || "name"] = model;
|
|
4240
|
+
return acc;
|
|
4241
|
+
}, {});
|
|
4242
|
+
}
|
|
4173
4243
|
const components = {
|
|
4174
4244
|
...globalOptions.components,
|
|
4175
4245
|
securitySchemes: globalOptions.securitySchemes || {},
|
|
@@ -4191,15 +4261,17 @@ function generateOpenAPISpec(globalOptions) {
|
|
|
4191
4261
|
};
|
|
4192
4262
|
let parameters = [];
|
|
4193
4263
|
if (swaggerOptions?.query) {
|
|
4194
|
-
if (swaggerOptions.query.type === "object" && swaggerOptions.query.
|
|
4264
|
+
if (swaggerOptions.query.type === "object" && swaggerOptions.query.shape) {
|
|
4195
4265
|
for (const [name, schema] of Object.entries(
|
|
4196
|
-
swaggerOptions.query.
|
|
4266
|
+
swaggerOptions.query.shape
|
|
4197
4267
|
)) {
|
|
4198
4268
|
parameters.push({
|
|
4199
4269
|
name,
|
|
4200
4270
|
in: "query",
|
|
4201
|
-
required: Array.isArray(
|
|
4202
|
-
|
|
4271
|
+
required: Array.isArray(
|
|
4272
|
+
swaggerOptions.query.shape[name].required
|
|
4273
|
+
) ? swaggerOptions.query.shape[name].required.includes(name) : false,
|
|
4274
|
+
schema: safeToJSONSchema(schema)
|
|
4203
4275
|
});
|
|
4204
4276
|
}
|
|
4205
4277
|
}
|
|
@@ -4224,7 +4296,7 @@ function generateOpenAPISpec(globalOptions) {
|
|
|
4224
4296
|
operation.requestBody = {
|
|
4225
4297
|
content: {
|
|
4226
4298
|
[routeBodyContentType]: {
|
|
4227
|
-
schema:
|
|
4299
|
+
schema: safeToJSONSchema(swaggerOptions.requestBody)
|
|
4228
4300
|
}
|
|
4229
4301
|
},
|
|
4230
4302
|
required: true
|
|
@@ -4248,7 +4320,7 @@ function generateOpenAPISpec(globalOptions) {
|
|
|
4248
4320
|
description: `Response for ${statusCode}`,
|
|
4249
4321
|
content: {
|
|
4250
4322
|
"application/json": {
|
|
4251
|
-
schema:
|
|
4323
|
+
schema: safeToJSONSchema(schema)
|
|
4252
4324
|
}
|
|
4253
4325
|
}
|
|
4254
4326
|
};
|
|
@@ -4262,7 +4334,7 @@ function generateOpenAPISpec(globalOptions) {
|
|
|
4262
4334
|
description: `Error response for ${statusCode}`,
|
|
4263
4335
|
content: {
|
|
4264
4336
|
"application/json": {
|
|
4265
|
-
schema:
|
|
4337
|
+
schema: safeToJSONSchema(schema)
|
|
4266
4338
|
}
|
|
4267
4339
|
}
|
|
4268
4340
|
};
|
|
@@ -4456,13 +4528,6 @@ function generateRapiDocUI(specUrl, globalOptions) {
|
|
|
4456
4528
|
</html>
|
|
4457
4529
|
`;
|
|
4458
4530
|
}
|
|
4459
|
-
function typeboxToOpenAPI(schema) {
|
|
4460
|
-
if (!schema) {
|
|
4461
|
-
return void 0;
|
|
4462
|
-
}
|
|
4463
|
-
const { $id, $schema, ...rest } = schema;
|
|
4464
|
-
return rest;
|
|
4465
|
-
}
|
|
4466
4531
|
function extractPathParams(path2, paramSchema) {
|
|
4467
4532
|
const params = [];
|
|
4468
4533
|
const regex = /:([a-zA-Z0-9_]+)/g;
|
|
@@ -4470,8 +4535,10 @@ function extractPathParams(path2, paramSchema) {
|
|
|
4470
4535
|
while ((match = regex.exec(path2)) !== null) {
|
|
4471
4536
|
const name = match[1];
|
|
4472
4537
|
let schema = { type: "string" };
|
|
4473
|
-
if (paramSchema && paramSchema.
|
|
4474
|
-
schema =
|
|
4538
|
+
if (paramSchema && paramSchema.shape && paramSchema.shape[name]) {
|
|
4539
|
+
schema = safeToJSONSchema(
|
|
4540
|
+
paramSchema.shape[name]
|
|
4541
|
+
) || {
|
|
4475
4542
|
type: "string"
|
|
4476
4543
|
};
|
|
4477
4544
|
}
|
|
@@ -4570,7 +4637,10 @@ var ServerDeno = class {
|
|
|
4570
4637
|
req.params = match?.params ?? {};
|
|
4571
4638
|
req.query = Object.fromEntries(url.searchParams.entries());
|
|
4572
4639
|
req.ip = req.headers.get("x-forwarded-for")?.split(",")[0] ?? info.remoteAddr?.hostname;
|
|
4573
|
-
await handler?.(req, info);
|
|
4640
|
+
const handlerResponse = await handler?.(req, info);
|
|
4641
|
+
if (handlerResponse) {
|
|
4642
|
+
return new Response(null, { status: 426 });
|
|
4643
|
+
}
|
|
4574
4644
|
const res = await executeMiddlewareChain(
|
|
4575
4645
|
match?.middleware ?? [],
|
|
4576
4646
|
match?.handler ?? ((req2, res2) => {
|
|
@@ -4633,7 +4703,7 @@ var ServerNode = class {
|
|
|
4633
4703
|
this.tapOptions = input?.tapOptions;
|
|
4634
4704
|
this.runtimeServer = http.createServer(
|
|
4635
4705
|
async (req, httpResponse) => {
|
|
4636
|
-
if (this.tapOptions) {
|
|
4706
|
+
if (this.tapOptions && this.tapOptions.type === "node") {
|
|
4637
4707
|
const { options } = this.tapOptions;
|
|
4638
4708
|
await options?.(req);
|
|
4639
4709
|
}
|
|
@@ -4651,16 +4721,22 @@ var ServerNode = class {
|
|
|
4651
4721
|
const [_, search = ""] = req.url?.split("?", 2) ?? [];
|
|
4652
4722
|
request.query = Object.fromEntries(new URLSearchParams(search));
|
|
4653
4723
|
request.params = match?.params ?? {};
|
|
4654
|
-
const response =
|
|
4724
|
+
const response = new Response2();
|
|
4725
|
+
response.nodeResponse = httpResponse;
|
|
4726
|
+
const responseResult = await executeMiddlewareChain(
|
|
4655
4727
|
match?.middleware ?? [],
|
|
4656
4728
|
match?.handler ?? ((req2, res) => {
|
|
4657
4729
|
res.notFound({
|
|
4658
4730
|
...errorFactory(new RouteNotFoundError(req2.url, req2.method))
|
|
4659
4731
|
});
|
|
4660
4732
|
}),
|
|
4661
|
-
request
|
|
4733
|
+
request,
|
|
4734
|
+
response
|
|
4662
4735
|
);
|
|
4663
|
-
|
|
4736
|
+
if (httpResponse.headersSent || httpResponse.writableEnded) {
|
|
4737
|
+
return;
|
|
4738
|
+
}
|
|
4739
|
+
let body = responseResult.getBody();
|
|
4664
4740
|
if (body instanceof ReadableStream) {
|
|
4665
4741
|
pipeReadableStreamToNodeResponse(body, httpResponse);
|
|
4666
4742
|
return;
|
|
@@ -4669,12 +4745,15 @@ var ServerNode = class {
|
|
|
4669
4745
|
body = body;
|
|
4670
4746
|
} else if (typeof body === "string") {
|
|
4671
4747
|
body = body;
|
|
4672
|
-
} else if (
|
|
4748
|
+
} else if (responseResult.headers["Content-Type"] === "application/json") {
|
|
4673
4749
|
body = JSON.stringify(body);
|
|
4674
4750
|
} else {
|
|
4675
4751
|
body = String(body);
|
|
4676
4752
|
}
|
|
4677
|
-
httpResponse.writeHead(
|
|
4753
|
+
httpResponse.writeHead(
|
|
4754
|
+
responseResult.responseStatus,
|
|
4755
|
+
responseResult.headers
|
|
4756
|
+
);
|
|
4678
4757
|
httpResponse.end(body);
|
|
4679
4758
|
}
|
|
4680
4759
|
);
|