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 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 typebox = require('@sinclair/typebox');
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 validate2 = ajv.compile(inputSchema);
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
- descriptor.value = async function(...args2) {
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 = originalMethod[SERIALIZE_METADATA];
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
- descriptor.value[SERIALIZE_WRAPPED] = true;
1017
- descriptor.value[SERIALIZE_METADATA] = originalMethod[SERIALIZE_METADATA];
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(typebox.Type);
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(typebox.Type);
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(typebox.Type);
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(typebox.Type);
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(typebox.Type);
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(typebox.Type);
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.properties) {
4264
+ if (swaggerOptions.query.type === "object" && swaggerOptions.query.shape) {
4195
4265
  for (const [name, schema] of Object.entries(
4196
- swaggerOptions.query.properties
4266
+ swaggerOptions.query.shape
4197
4267
  )) {
4198
4268
  parameters.push({
4199
4269
  name,
4200
4270
  in: "query",
4201
- required: Array.isArray(swaggerOptions.query.required) ? swaggerOptions.query.required.includes(name) : false,
4202
- schema: typeboxToOpenAPI(schema)
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: typeboxToOpenAPI(swaggerOptions.requestBody)
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: typeboxToOpenAPI(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: typeboxToOpenAPI(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.type === "object" && paramSchema.properties && paramSchema.properties[name]) {
4474
- schema = typeboxToOpenAPI(paramSchema.properties[name]) || {
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 = await executeMiddlewareChain(
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
- let body = response.getBody();
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 (response.headers["Content-Type"] === "application/json") {
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(response.responseStatus, response.headers);
4753
+ httpResponse.writeHead(
4754
+ responseResult.responseStatus,
4755
+ responseResult.headers
4756
+ );
4678
4757
  httpResponse.end(body);
4679
4758
  }
4680
4759
  );