@kizunajs/openapi 2.0.0-beta.10 → 2.0.0-beta.12

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/index.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  let zod = require("zod");
3
3
  let kizunajs_plugin = require("kizunajs/plugin");
4
+ let kizunajs_generator = require("kizunajs/generator");
4
5
  let kizunajs_adapter = require("kizunajs/adapter");
5
6
  let yaml = require("yaml");
6
- let kizunajs_generator = require("kizunajs/generator");
7
7
  let kizunajs = require("kizunajs");
8
8
  //#region src/generator.ts
9
9
  const streamJsonSchema = (response, toJsonSchema) => {
@@ -162,10 +162,11 @@ const deriveHeadOperation = (getOperation) => {
162
162
  for (const response of Object.values(operation.responses)) delete response.content;
163
163
  return operation;
164
164
  };
165
- const openApiGenerator = (0, kizunajs_generator.createGenerator)((options, api) => {
165
+ const openApiWalk = (options, api) => {
166
166
  const paths = {};
167
167
  return {
168
- processRoute({ routeKey, route, routeTags, deprecated }) {
168
+ processRoute({ routeKey, route, routeTags, deprecated, hidden }) {
169
+ if (hidden && options.includeHidden !== true) return;
169
170
  const openApiPath = convertPath(route.path);
170
171
  const method = route.method.toLowerCase();
171
172
  let operation = {
@@ -379,7 +380,7 @@ const openApiGenerator = (0, kizunajs_generator.createGenerator)((options, api)
379
380
  return renderer;
380
381
  }
381
382
  };
382
- });
383
+ };
383
384
  /**
384
385
  * The permissions a route requires, as `x-kizuna-requires`: what is acted on,
385
386
  * mapped to the verbs the caller has to hold.
@@ -459,17 +460,17 @@ const tagsFromApi = (api) => {
459
460
  * So a build step does not restate the options and drift from what is served.
460
461
  */
461
462
  const optionsFromInstalledPlugin = (api) => {
462
- for (const declaration of Object.values(api.plugins ?? {})) if (declaration.slug === "openApi") return declaration.props;
463
+ for (const plugin of Object.values(api.plugins ?? {})) if (plugin.definedSlug === "openApi") return plugin.options;
463
464
  throw new Error("generateOpenApi reads its options from the config. Name `openApiPlugin` under `plugins` with the API's `info`.");
464
465
  };
465
466
  /**
466
467
  * Render from options held directly, for the plugin's own routes.
467
468
  */
468
469
  function renderOpenApi(api, options) {
469
- const renderer = openApiGenerator(api, {
470
+ const renderer = (0, kizunajs_generator.walkApi)(api, openApiWalk({
470
471
  ...options,
471
472
  tagLookup: buildTagLookup(api)
472
- });
473
+ }, api));
473
474
  const tags = tagsFromApi(api);
474
475
  if (tags.length > 0) renderer("json").tags = tags;
475
476
  return renderer;
@@ -561,22 +562,6 @@ const renderDocsHtml = (options) => {
561
562
  });
562
563
  };
563
564
  //#endregion
564
- //#region src/document-target.ts
565
- /**
566
- * An OpenAPI document target for `kizuna.config.ts`.
567
- *
568
- * @example
569
- * openApiDocument({ output: './openapi.yaml' });
570
- */
571
- const openApiDocument = ({ output, ...overrides }) => ({
572
- kind: "openapi",
573
- output,
574
- generate: (contract) => {
575
- const render = generateOpenApi(contract, overrides);
576
- return output.endsWith(".json") ? `${JSON.stringify(render("json"), null, 4)}\n` : render("yaml");
577
- }
578
- });
579
- //#endregion
580
565
  //#region src/server.ts
581
566
  const HTML = "text/html; charset=utf-8";
582
567
  const JSON_TYPE = "application/json";
@@ -586,24 +571,129 @@ const sent = (body, contentType) => new Response(body, {
586
571
  headers: { "content-type": contentType }
587
572
  });
588
573
  /**
589
- * What answers the routes `openApiPlugin` declares: the reference UI, and the
590
- * document itself where the declaration published it.
574
+ * The routes `openApiPlugin` serves: the reference UI, and the document itself
575
+ * where the options publish it. The document is rendered on the first request,
576
+ * once the api it describes has assembled.
591
577
  */
592
- const openApiServe = (props, api) => {
593
- const spec = renderOpenApi((0, kizunajs_adapter.contractOf)(api), props);
594
- return { router: {
595
- page: () => (0, kizunajs_adapter.rawResponse)(sent(renderDocsHtml({
596
- specUrl: props.jsonPath,
597
- specContent: props.jsonPath === void 0 ? spec("json") : void 0,
598
- provider: props.provider,
599
- pageTitle: props.pageTitle ?? spec("json").info.title,
600
- cdnUrl: props.cdnUrl,
601
- configuration: props.configuration
602
- }), HTML)),
603
- json: () => (0, kizunajs_adapter.rawResponse)(sent(JSON.stringify(spec("json")), JSON_TYPE)),
604
- yaml: () => (0, kizunajs_adapter.rawResponse)(sent(spec("yaml"), YAML))
605
- } };
578
+ const openApiRoutes = (props, api) => {
579
+ let renderer;
580
+ const spec = ((format) => {
581
+ renderer ??= renderOpenApi((0, kizunajs_adapter.contractOf)(api), props);
582
+ return renderer(format);
583
+ });
584
+ const routes = {};
585
+ if (props.docsPath !== void 0) routes.page = (0, kizunajs_plugin.route)({
586
+ method: "GET",
587
+ path: props.docsPath,
588
+ summary: "API reference",
589
+ responses: { 200: zod.z.string() }
590
+ }).handler(() => (0, kizunajs_adapter.rawResponse)(sent(renderDocsHtml({
591
+ specUrl: props.jsonPath,
592
+ specContent: props.jsonPath === void 0 ? spec("json") : void 0,
593
+ provider: props.provider,
594
+ pageTitle: props.pageTitle ?? spec("json").info.title,
595
+ cdnUrl: props.cdnUrl,
596
+ configuration: props.configuration
597
+ }), HTML)));
598
+ if (props.jsonPath !== void 0) routes.json = (0, kizunajs_plugin.route)({
599
+ method: "GET",
600
+ path: props.jsonPath,
601
+ summary: "OpenAPI document",
602
+ responses: { 200: zod.z.unknown() }
603
+ }).handler(() => (0, kizunajs_adapter.rawResponse)(sent(JSON.stringify(spec("json")), JSON_TYPE)));
604
+ if (props.yamlPath !== void 0) routes.yaml = (0, kizunajs_plugin.route)({
605
+ method: "GET",
606
+ path: props.yamlPath,
607
+ summary: "OpenAPI document, as YAML",
608
+ responses: { 200: zod.z.string() }
609
+ }).handler(() => (0, kizunajs_adapter.rawResponse)(sent(spec("yaml"), YAML)));
610
+ return routes;
606
611
  };
612
+ //#endregion
613
+ //#region src/types.ts
614
+ const GenerateOpenApiOptionsSchema = zod.z.object({
615
+ /**
616
+ * The OpenAPI Specification version to declare in the document's `openapi` field.
617
+ *
618
+ * Defaults to `'3.1.0'`.
619
+ */
620
+ openApiVersion: zod.z.literal("3.1.0").optional(),
621
+ info: zod.z.object({
622
+ title: zod.z.string(),
623
+ version: zod.z.string(),
624
+ description: zod.z.string().optional()
625
+ }),
626
+ servers: zod.z.array(zod.z.object({
627
+ url: zod.z.string(),
628
+ description: zod.z.string().optional()
629
+ })).optional(),
630
+ externalDocs: zod.z.object({
631
+ url: zod.z.string(),
632
+ description: zod.z.string().optional()
633
+ }).optional(),
634
+ setOperationId: zod.z.union([zod.z.boolean(), zod.z.literal("concatenated-path")]).optional(),
635
+ /**
636
+ * Document `hidden` routes too, for a document your own team reads.
637
+ *
638
+ * @default false
639
+ */
640
+ includeHidden: zod.z.boolean().optional(),
641
+ operationMapper: zod.z.custom((value) => typeof value === "function", { error: "must be a function" }).optional(),
642
+ /**
643
+ * Document the derived `head` operation on every `GET` path without a declared one.
644
+ *
645
+ * @default false
646
+ */
647
+ derivedHead: zod.z.boolean().optional()
648
+ });
649
+ //#endregion
650
+ //#region src/plugin.ts
651
+ const OPENAPI_PLUGIN_SLUG = "openApi";
652
+ const OpenApiPluginOptionsSchema = GenerateOpenApiOptionsSchema.extend({
653
+ /**
654
+ * Where `kizuna generate` writes the document. `.json` renders JSON,
655
+ * anything else YAML.
656
+ *
657
+ * @example
658
+ * output: './openapi.yaml',
659
+ */
660
+ output: zod.z.string().optional(),
661
+ /**
662
+ * Where the reference UI is served.
663
+ */
664
+ docsPath: kizunajs_plugin.RoutePathSchema.optional(),
665
+ /**
666
+ * Where the document is served. The UI embeds it, so this is only for
667
+ * publishing the file.
668
+ */
669
+ jsonPath: zod.z.custom((value) => typeof value === "string" && value.startsWith("/") && value.endsWith(".json"), { error: "must start with / and end in .json" }).optional(),
670
+ /**
671
+ * Where the document is served as YAML.
672
+ */
673
+ yamlPath: zod.z.custom((value) => typeof value === "string" && value.startsWith("/") && value.endsWith(".yaml"), { error: "must start with / and end in .yaml" }).optional(),
674
+ /**
675
+ * Which API reference UI to render.
676
+ *
677
+ * @default 'scalar'
678
+ */
679
+ provider: zod.z.enum(["scalar", "swagger"]).optional(),
680
+ /**
681
+ * Where to load the UI's assets from, for a self-hosted copy. The script
682
+ * URL for `'scalar'`; the directory holding `swagger-ui.css` and
683
+ * `swagger-ui-bundle.js` for `'swagger'`.
684
+ */
685
+ cdnUrl: zod.z.string().optional(),
686
+ /**
687
+ * The page's `<title>`.
688
+ *
689
+ * @default the document's `info.title`
690
+ */
691
+ pageTitle: zod.z.string().optional(),
692
+ /**
693
+ * Merged into `Scalar.createApiReference` or `SwaggerUIBundle`.
694
+ */
695
+ configuration: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
696
+ });
607
697
  /**
608
698
  * Serve an API reference UI for the OpenAPI document, and the document itself
609
699
  * if you publish it.
@@ -621,42 +711,42 @@ const openApiServe = (props, api) => {
621
711
  * title: 'My API',
622
712
  * version: '1.0.0',
623
713
  * },
714
+ * docsPath: '/docs',
624
715
  * }),
625
716
  * ],
626
717
  * });
627
718
  * ```
628
719
  */
629
- const declare = (slug, props) => (0, kizunajs_plugin.createPlugin)({
630
- slug,
631
- routes: {
632
- ...props.docsPath === void 0 ? {} : { page: {
633
- method: "GET",
634
- path: props.docsPath,
635
- summary: "API reference",
636
- responses: { 200: zod.z.string() }
637
- } },
638
- ...props.jsonPath === void 0 ? {} : { json: {
639
- method: "GET",
640
- path: props.jsonPath,
641
- summary: "OpenAPI document",
642
- responses: { 200: zod.z.unknown() }
643
- } },
644
- ...props.yamlPath === void 0 ? {} : { yaml: {
645
- method: "GET",
646
- path: props.yamlPath,
647
- summary: "OpenAPI document, as YAML",
648
- responses: { 200: zod.z.string() }
649
- } }
650
- },
651
- props,
652
- serve: (pluginProps, api) => openApiServe(pluginProps, api)
720
+ const openApiPlugin = (0, kizunajs_plugin.definePlugin)({
721
+ slug: OPENAPI_PLUGIN_SLUG,
722
+ options: OpenApiPluginOptionsSchema,
723
+ setup: ({ options, api }) => {
724
+ const { output, ...document } = options;
725
+ return {
726
+ routes: openApiRoutes(options, api),
727
+ generators: output === void 0 ? [] : [openApiDocumentGenerator({
728
+ output,
729
+ json: output.endsWith(".json"),
730
+ document
731
+ })]
732
+ };
733
+ }
734
+ });
735
+ /**
736
+ * Writes one document, in the format its `output` names.
737
+ */
738
+ const openApiDocumentGenerator = (0, kizunajs_generator.defineGenerator)({
739
+ options: zod.z.object({
740
+ json: zod.z.boolean(),
741
+ document: zod.z.custom()
742
+ }),
743
+ generate: ({ options, api }) => ({ finalize: () => {
744
+ const render = renderOpenApi(api, options.document);
745
+ return options.json ? `${JSON.stringify(render("json"), null, 4)}\n` : render("yaml");
746
+ } })
653
747
  });
654
- function openApiPlugin(props) {
655
- return declare(props.slug ?? "openApi", props);
656
- }
657
748
  //#endregion
658
749
  exports.generateOpenApi = generateOpenApi;
659
- exports.openApiDocument = openApiDocument;
660
750
  exports.openApiPlugin = openApiPlugin;
661
751
  exports.renderDocsHtml = renderDocsHtml;
662
752
  exports.renderOpenApi = renderOpenApi;