@boon4681/giri 0.0.3-alpha-9 → 0.0.3-alpha-11

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/cli.js CHANGED
@@ -833,39 +833,37 @@ function isGiriBodySchema(value) {
833
833
  value && typeof value === "object" && value[bodySchemaBrand] === true
834
834
  );
835
835
  }
836
+ var RouteInputError = class extends Error {
837
+ name = "RouteInputError";
838
+ };
836
839
  function resolveRouteInput(sources) {
837
- const input = {};
838
- const owners = {};
840
+ const body = [];
841
+ const query = [];
839
842
  for (const source of sources) {
840
843
  if (source.body !== void 0) {
841
844
  if (!isGiriBodySchema(source.body)) {
842
- throw new Error(
845
+ throw new RouteInputError(
843
846
  `${source.label}: "body" must be wrapped with a validator, e.g. \`zod.body({ json: ... })\` from @boon4681/giri/validators/zod.`
844
847
  );
845
848
  }
846
- if (input.body) {
847
- throw new Error(
848
- `${source.label}: body validator conflicts with the body validator from ${owners.body}.`
849
- );
850
- }
851
- input.body = source.body;
852
- owners.body = source.label;
849
+ body.push(source.body);
853
850
  }
854
851
  if (source.query !== void 0) {
855
852
  if (!isGiriInputSchema(source.query)) {
856
- throw new Error(
853
+ throw new RouteInputError(
857
854
  `${source.label}: "query" must be wrapped with a validator, e.g. \`zod.query(...)\` from @boon4681/giri/validators/zod.`
858
855
  );
859
856
  }
860
- if (input.query) {
861
- throw new Error(
862
- `${source.label}: query validator conflicts with the query validator from ${owners.query}.`
863
- );
864
- }
865
- input.query = source.query;
866
- owners.query = source.label;
857
+ query.push(source.query);
867
858
  }
868
859
  }
860
+ const input = {};
861
+ if (body.length > 0) {
862
+ input.body = body;
863
+ }
864
+ if (query.length > 0) {
865
+ input.query = query;
866
+ }
869
867
  return input.body || input.query ? input : void 0;
870
868
  }
871
869
 
@@ -1527,6 +1525,63 @@ function bodyToJsonSchemas(value) {
1527
1525
  }
1528
1526
  return Object.keys(out).length > 0 ? out : void 0;
1529
1527
  }
1528
+ function mergeObjectJsonSchemas(schemas) {
1529
+ const properties = {};
1530
+ const required = /* @__PURE__ */ new Set();
1531
+ let mergedAny = false;
1532
+ for (const schema of schemas) {
1533
+ if (schema.type === "object" && schema.properties && typeof schema.properties === "object") {
1534
+ mergedAny = true;
1535
+ Object.assign(properties, schema.properties);
1536
+ if (Array.isArray(schema.required)) {
1537
+ for (const name of schema.required) {
1538
+ required.add(name);
1539
+ }
1540
+ }
1541
+ }
1542
+ }
1543
+ if (!mergedAny) {
1544
+ return schemas[schemas.length - 1];
1545
+ }
1546
+ const merged = { type: "object", properties };
1547
+ if (required.size > 0) {
1548
+ merged.required = [...required];
1549
+ }
1550
+ return merged;
1551
+ }
1552
+ function queryToJsonSchema(schemas) {
1553
+ if (!schemas || schemas.length === 0) {
1554
+ return void 0;
1555
+ }
1556
+ const jsons = schemas.map((schema) => inputToJsonSchema(schema)).filter((json) => json !== void 0);
1557
+ if (jsons.length === 0) {
1558
+ return void 0;
1559
+ }
1560
+ return jsons.length === 1 ? jsons[0] : mergeObjectJsonSchemas(jsons);
1561
+ }
1562
+ function bodiesToJsonSchemas(schemas) {
1563
+ if (!schemas || schemas.length === 0) {
1564
+ return void 0;
1565
+ }
1566
+ const perType = /* @__PURE__ */ new Map();
1567
+ for (const schema of schemas) {
1568
+ const single = bodyToJsonSchemas(schema);
1569
+ if (!single) {
1570
+ continue;
1571
+ }
1572
+ for (const [contentType, json] of Object.entries(single)) {
1573
+ const key = contentType;
1574
+ const list = perType.get(key) ?? [];
1575
+ list.push(json);
1576
+ perType.set(key, list);
1577
+ }
1578
+ }
1579
+ const out = {};
1580
+ for (const [contentType, jsons] of perType) {
1581
+ out[contentType] = jsons.length === 1 ? jsons[0] : mergeObjectJsonSchemas(jsons);
1582
+ }
1583
+ return Object.keys(out).length > 0 ? out : void 0;
1584
+ }
1530
1585
 
1531
1586
  // src/generator/schema/route-openapi.ts
1532
1587
  var import_typebox2 = require("@sinclair/typebox");
@@ -1600,8 +1655,8 @@ function readInput(routeModule, middleware, label) {
1600
1655
  { label, body: routeModule.body, query: routeModule.query }
1601
1656
  ]);
1602
1657
  const input = {};
1603
- const body = bodyToJsonSchemas(resolved?.body);
1604
- const query = inputToJsonSchema(resolved?.query);
1658
+ const body = bodiesToJsonSchemas(resolved?.body);
1659
+ const query = queryToJsonSchema(resolved?.query);
1605
1660
  if (body) {
1606
1661
  input.body = body;
1607
1662
  }
@@ -2029,7 +2084,10 @@ async function extractRouteMeta(config, paths, routes) {
2029
2084
  if (meta.input || meta.security || meta.hidden || meta.openapi) {
2030
2085
  byFile.set(route.file, meta);
2031
2086
  }
2032
- } catch {
2087
+ } catch (error) {
2088
+ if (error instanceof RouteInputError) {
2089
+ throw error;
2090
+ }
2033
2091
  }
2034
2092
  }
2035
2093
  } finally {
@@ -2257,6 +2315,9 @@ async function extractMeta(config, paths, routes) {
2257
2315
  }
2258
2316
  }
2259
2317
  } catch (error) {
2318
+ if (error instanceof RouteInputError) {
2319
+ throw error;
2320
+ }
2260
2321
  console.warn(`giri: skipped input/security generation (${error.message}).`);
2261
2322
  }
2262
2323
  return { inputsByFile, securityByFile, hiddenFiles, openapiByFile };