Compare commits

..

2 Commits

Author SHA1 Message Date
Greg Bergé 5eba878657 wip 2025-04-13 10:02:36 +02:00
Greg Bergé f29d2b8e9c wip 2025-04-13 09:06:01 +02:00
96 changed files with 1189 additions and 2803 deletions
@@ -70,7 +70,7 @@ runs:
apiToken: ${{ inputs.apiToken }}
accountId: ${{ inputs.accountId }}
workingDirectory: ./
wranglerVersion: '4.10.0'
wranglerVersion: '3.112.0'
environment: ${{ inputs.environment }}
command: ${{ inputs.deploy == 'true' && 'deploy' || format('versions upload --tag {0} --message "{1}"', inputs.commitTag, inputs.commitMessage) }} --config ./packages/gitbook-v2/wrangler.jsonc
- name: Outputs
+2 -2
View File
@@ -200,7 +200,7 @@ jobs:
name: Visual Testing v2 (Cloudflare)
needs: deploy-v2-cloudflare
timeout-minutes: 10
if: startsWith(github.head_ref || github.ref_name, 'cloudflare/') || github.ref == 'refs/heads/main'
if: startsWith(github.head_ref || github.ref_name, 'cloudflare/')
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -263,7 +263,7 @@ jobs:
name: Visual Testing Customers v2 (Cloudflare)
needs: deploy-v2-cloudflare
timeout-minutes: 8
if: startsWith(github.head_ref || github.ref_name, 'cloudflare/') || github.ref == 'refs/heads/main'
if: startsWith(github.head_ref || github.ref_name, 'cloudflare/')
steps:
- name: Checkout
uses: actions/checkout@v4
+1 -2
View File
@@ -20,8 +20,7 @@
"**/.wrangler/**/*",
"packages/openapi-parser/src/fixtures/**/*",
"packages/emoji-codepoints/index.ts",
"packages/icons/src/data/*.json",
"packages/cache-do/worker-configuration.d.ts"
"packages/icons/src/data/*.json"
]
},
"formatter": {
+84 -170
View File
@@ -19,14 +19,14 @@
},
"devDependencies": {
"typescript": "^5.5.3",
"wrangler": "^4.10.0",
"wrangler": "^3.112.0",
},
},
"packages/cache-tags": {
"name": "@gitbook/cache-tags",
"version": "0.3.1",
"dependencies": {
"@gitbook/api": "^0.111.0",
"@gitbook/api": "^0.109.0",
"assert-never": "^1.2.1",
},
"devDependencies": {
@@ -155,7 +155,7 @@
"warn-once": "^0.1.1",
},
"devDependencies": {
"@opennextjs/cloudflare": "^1.0.0-beta.3",
"@opennextjs/cloudflare": "https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@8f7ed38",
"@types/rison": "^0.0.9",
"gitbook": "*",
"postcss": "^8",
@@ -234,8 +234,8 @@
"version": "1.1.10",
"dependencies": {
"@gitbook/openapi-parser": "workspace:*",
"@scalar/api-client-react": "^1.2.19",
"@scalar/oas-utils": "^0.2.130",
"@scalar/api-client-react": "^1.2.5",
"@scalar/oas-utils": "^0.2.120",
"clsx": "^2.1.1",
"flatted": "^3.2.9",
"json-xml-parse": "^1.3.0",
@@ -259,7 +259,7 @@
},
"overrides": {
"@codemirror/state": "6.4.1",
"@gitbook/api": "0.111.0",
"@gitbook/api": "0.109.0",
"react": "18.3.1",
"react-dom": "18.3.1",
},
@@ -470,21 +470,19 @@
"@changesets/write": ["@changesets/write@0.3.2", "", { "dependencies": { "@changesets/types": "^6.0.0", "fs-extra": "^7.0.1", "human-id": "^1.0.2", "prettier": "^2.7.1" } }, "sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw=="],
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],
"@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.3.4", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q=="],
"@cloudflare/next-on-pages": ["@cloudflare/next-on-pages@1.13.7", "", { "dependencies": { "acorn": "^8.8.0", "ast-types": "^0.14.2", "chalk": "^5.2.0", "chokidar": "^3.5.3", "commander": "^11.1.0", "cookie": "^0.5.0", "esbuild": "^0.15.3", "js-yaml": "^4.1.0", "miniflare": "^3.20231218.1", "package-manager-manager": "^0.2.0", "pcre-to-regexp": "^1.1.0", "semver": "^7.5.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20240208.0", "vercel": ">=30.0.0", "wrangler": "^3.28.2" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "next-on-pages": "bin/index.js" } }, "sha512-TSMVy+1fmxzeyykOC9guMEj7G2FgENw1T8V1sIFnah6piaJNBmybdyikPdQSdikP5w6v9eQhBt/TrXDPMDw0dw=="],
"@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.15", "workerd": "^1.20250320.0" }, "optionalPeers": ["workerd"] }, "sha512-Xq57Qd+ADpt6hibcVBO0uLG9zzRgyRhfCUgBT9s+g3+3Ivg5zDyVgLFy40ES1VdNcu8rPNSivm9A+kGP5IVaPg=="],
"@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250214.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cDvvedWDc5zrgDnuXe2qYcz/TwBvzmweO55C7XpPuAWJ9Oqxv81PkdekYxD8mH989aQ/GI5YD0Fe6fDYlM+T3Q=="],
"@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250409.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-smA9yq77xsdQ1NMLhFz3JZxMHGd01lg0bE+X3dTFmIUs+hHskJ+HJ/IkMFInkCCeEFlUkoL4yO7ilaU/fin/xA=="],
"@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250214.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NytCvRveVzu0mRKo+tvZo3d/gCUway3B2ZVqSi/TS6NXDGBYIJo7g6s3BnTLS74kgyzeDOjhu9j/RBJBS809qw=="],
"@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250409.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oLVcf+Y5Qun8JHcy1VcR/YnbA5U2ne0czh3XNhDqdHZFK8+vKeC7MnVPX+kEqQA3+uLcMM1/FsIDU1U4Na0h1g=="],
"@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250214.0", "", { "os": "linux", "cpu": "x64" }, "sha512-pQ7+aHNHj8SiYEs4d/6cNoimE5xGeCMfgU1yfDFtA9YGN9Aj2BITZgOWPec+HW7ZkOy9oWlNrO6EvVjGgB4tbQ=="],
"@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250409.0", "", { "os": "linux", "cpu": "x64" }, "sha512-D31B4kdC3a0RD5yfpdIa89//kGHbYsYihZmejm1k4S4NHOho3MUDHAEh4aHtafQNXbZdydGHlSyiVYjTdQ9ILQ=="],
"@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250214.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Vhlfah6Yd9ny1npNQjNgElLIjR6OFdEbuR3LCfbLDCwzWEBFhIf7yC+Tpp/a0Hq7kLz3sLdktaP7xl3PJhyOjA=="],
"@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250409.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Sr59P0TREayil5OQ7kcbjuIn6L6OTSRLI91LKu0D8vi1hss2q9FUwBcwxg0+Yd/x+ty/x7IISiAK5QBkAMeITQ=="],
"@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250409.0", "", { "os": "win32", "cpu": "x64" }, "sha512-dK9I8zBX5rR7MtaaP2AhICQTEw3PVzHcsltN8o46w7JsbYlMvFOj27FfYH5dhs3IahgmIfw2e572QXW2o/dbpg=="],
"@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250214.0", "", { "os": "win32", "cpu": "x64" }, "sha512-GMwMyFbkjBKjYJoKDhGX8nuL4Gqe3IbVnVWf2Q6086CValyIknupk5J6uQWGw2EBU3RGO3x4trDXT5WphQJZDQ=="],
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20241230.0", "", {}, "sha512-dtLD4jY35Lb750cCVyO1i/eIfdZJg2Z0i+B1RYX6BVeRPlgaHx/H18ImKAkYmy0g09Ow8R2jZy3hIxMgXun0WQ=="],
@@ -550,55 +548,49 @@
"@esbuild-plugins/node-modules-polyfill": ["@esbuild-plugins/node-modules-polyfill@0.2.2", "", { "dependencies": { "escape-string-regexp": "^4.0.0", "rollup-plugin-node-polyfills": "^0.2.1" }, "peerDependencies": { "esbuild": "*" } }, "sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA=="],
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.17.19", "", { "os": "android", "cpu": "arm" }, "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A=="],
"@esbuild/android-arm": ["@esbuild/android-arm@0.24.2", "", { "os": "android", "cpu": "arm" }, "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.17.19", "", { "os": "android", "cpu": "arm64" }, "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA=="],
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.24.2", "", { "os": "android", "cpu": "arm64" }, "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.17.19", "", { "os": "android", "cpu": "x64" }, "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww=="],
"@esbuild/android-x64": ["@esbuild/android-x64@0.24.2", "", { "os": "android", "cpu": "x64" }, "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.17.19", "", { "os": "darwin", "cpu": "arm64" }, "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg=="],
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.24.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.17.19", "", { "os": "darwin", "cpu": "x64" }, "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw=="],
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.24.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.17.19", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ=="],
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.24.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.17.19", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ=="],
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.24.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.17.19", "", { "os": "linux", "cpu": "arm" }, "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA=="],
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.24.2", "", { "os": "linux", "cpu": "arm" }, "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.17.19", "", { "os": "linux", "cpu": "arm64" }, "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg=="],
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.24.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.17.19", "", { "os": "linux", "cpu": "ia32" }, "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ=="],
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.24.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ=="],
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A=="],
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.17.19", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg=="],
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.24.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA=="],
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.24.2", "", { "os": "linux", "cpu": "none" }, "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.17.19", "", { "os": "linux", "cpu": "s390x" }, "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q=="],
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.24.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.17.19", "", { "os": "linux", "cpu": "x64" }, "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw=="],
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.24.2", "", { "os": "linux", "cpu": "x64" }, "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.17.19", "", { "os": "none", "cpu": "x64" }, "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q=="],
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.24.2", "", { "os": "none", "cpu": "arm64" }, "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.17.19", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g=="],
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.24.2", "", { "os": "none", "cpu": "x64" }, "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.17.19", "", { "os": "sunos", "cpu": "x64" }, "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg=="],
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.24.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.17.19", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag=="],
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.24.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.17.19", "", { "os": "win32", "cpu": "ia32" }, "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw=="],
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.24.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig=="],
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.24.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ=="],
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.24.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.24.2", "", { "os": "win32", "cpu": "x64" }, "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg=="],
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.17.19", "", { "os": "win32", "cpu": "x64" }, "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA=="],
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
@@ -628,7 +620,7 @@
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
"@gitbook/api": ["@gitbook/api@0.111.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-E5Pk28kPD4p6XNWdwFM9pgDijdByseIZQqcFK+/hoW5tEZa5Yw/plRKJyN1hmwfPL6SKq6Maf0fbIzTQiVXyQQ=="],
"@gitbook/api": ["@gitbook/api@0.109.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-nzhxmiNR2MMHh+HoU7fFdOJ+u4BFyXjtsVquQf3VhZMjeI71yeLx5JEbjkOcijVd/GMAtvpYlYR2qcPqzq27gw=="],
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
@@ -794,9 +786,9 @@
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@opennextjs/aws": ["@opennextjs/aws@3.5.7", "", { "dependencies": { "@ast-grep/napi": "^0.35.0", "@aws-sdk/client-cloudfront": "3.398.0", "@aws-sdk/client-dynamodb": "^3.398.0", "@aws-sdk/client-lambda": "^3.398.0", "@aws-sdk/client-s3": "^3.398.0", "@aws-sdk/client-sqs": "^3.398.0", "@node-minify/core": "^8.0.6", "@node-minify/terser": "^8.0.6", "@tsconfig/node18": "^1.0.1", "aws4fetch": "^1.0.18", "chalk": "^5.3.0", "esbuild": "0.19.2", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0", "yaml": "^2.7.0" }, "bin": { "open-next": "dist/index.js" } }, "sha512-YjyHJrkIHI7YwQRCp8GjDOudu86oOc1RiwxvBBpPHrplsS18H4ZmkzGggAKhK6B4myGsJQ/q9kNP2TraoZiNzg=="],
"@opennextjs/aws": ["@opennextjs/aws@3.5.6", "", { "dependencies": { "@ast-grep/napi": "^0.35.0", "@aws-sdk/client-cloudfront": "3.398.0", "@aws-sdk/client-dynamodb": "^3.398.0", "@aws-sdk/client-lambda": "^3.398.0", "@aws-sdk/client-s3": "^3.398.0", "@aws-sdk/client-sqs": "^3.398.0", "@node-minify/core": "^8.0.6", "@node-minify/terser": "^8.0.6", "@tsconfig/node18": "^1.0.1", "aws4fetch": "^1.0.18", "chalk": "^5.3.0", "esbuild": "0.19.2", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0", "yaml": "^2.7.0" }, "bin": { "open-next": "dist/index.js" } }, "sha512-hYI3uYLVEK15zoxjlGKLSlzOrSf1GvnFWARCmS2gllL6aYaDspw2HIJ2OP+RS3wn+Npqs9iKVV2Dwiejdt5vxQ=="],
"@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.0-beta.3", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.5.7", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^3.114.3 || ^4.7.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-qKBXQZhUeQ+iGvfJeF7PO30g59LHnPOlRVZd77zxwn6Uc9C+c0LSwo8N28XRIWyQPkY007rKk9pSIxOrP4MHtQ=="],
"@opennextjs/cloudflare": ["@opennextjs/cloudflare@https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@8f7ed38", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.5.6", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^3.114.3 || ^4.7.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }],
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
@@ -1076,21 +1068,21 @@
"@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="],
"@scalar/api-client": ["@scalar/api-client@2.3.19", "", { "dependencies": { "@headlessui/tailwindcss": "^0.2.0", "@headlessui/vue": "^1.7.20", "@scalar/components": "0.13.47", "@scalar/draggable": "0.1.11", "@scalar/icons": "0.1.3", "@scalar/import": "0.3.13", "@scalar/oas-utils": "0.2.130", "@scalar/object-utils": "1.1.13", "@scalar/openapi-parser": "0.10.14", "@scalar/openapi-types": "0.2.0", "@scalar/postman-to-openapi": "0.2.3", "@scalar/snippetz": "0.2.19", "@scalar/themes": "0.9.86", "@scalar/types": "0.1.7", "@scalar/use-codemirror": "0.11.92", "@scalar/use-hooks": "0.1.40", "@scalar/use-toasts": "0.7.9", "@scalar/use-tooltip": "1.0.6", "@vueuse/core": "^10.10.0", "@vueuse/integrations": "^11.2.0", "focus-trap": "^7", "fuse.js": "^7.0.0", "microdiff": "^1.4.0", "nanoid": "^5.1.5", "pretty-bytes": "^6.1.1", "pretty-ms": "^8.0.0", "shell-quote": "^1.8.1", "type-fest": "^4.20.0", "vue": "^3.5.12", "vue-router": "^4.3.0", "whatwg-mimetype": "^4.0.0", "yaml": "^2.4.5", "zod": "^3.23.8" } }, "sha512-1Scff4QL6UExxcmSYv5j1dktvQZTXbmDUJp99RqmUROEhneNWEeWaZe+GZWsda5mvDoe4vP9zZKaymkulBDKYQ=="],
"@scalar/api-client": ["@scalar/api-client@2.3.5", "", { "dependencies": { "@headlessui/tailwindcss": "^0.2.0", "@headlessui/vue": "^1.7.20", "@scalar/components": "0.13.37", "@scalar/draggable": "0.1.11", "@scalar/icons": "0.1.3", "@scalar/import": "0.3.2", "@scalar/oas-utils": "0.2.120", "@scalar/object-utils": "1.1.13", "@scalar/openapi-parser": "0.10.10", "@scalar/openapi-types": "0.1.9", "@scalar/postman-to-openapi": "0.1.43", "@scalar/snippetz": "0.2.16", "@scalar/themes": "0.9.79", "@scalar/types": "0.1.1", "@scalar/use-codemirror": "0.11.82", "@scalar/use-hooks": "0.1.33", "@scalar/use-toasts": "0.7.9", "@scalar/use-tooltip": "1.0.6", "@vueuse/core": "^10.10.0", "@vueuse/integrations": "^11.2.0", "focus-trap": "^7", "fuse.js": "^7.0.0", "microdiff": "^1.4.0", "nanoid": "^5.0.9", "pretty-bytes": "^6.1.1", "pretty-ms": "^8.0.0", "shell-quote": "^1.8.1", "vue": "^3.5.12", "vue-router": "^4.3.0", "whatwg-mimetype": "^4.0.0", "yaml": "^2.4.5", "zod": "^3.23.8" } }, "sha512-bVBP8H3laa4VG3dExv4Ak/Kf+q9z8aK/Glpv6CPE0wxgZJWxwkJoSrvwxUE+46JktSObUrhu4xQiqSCmBz8esQ=="],
"@scalar/api-client-react": ["@scalar/api-client-react@1.2.19", "", { "dependencies": { "@scalar/api-client": "2.3.19", "@scalar/types": "0.1.7", "vue": "^3.5.12" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-zWUQvzAgOQ+oAbzswOlN42OJhwKku5RIIfBlukFMT31ceun3cp8e7+RT5DS6qwwQ2FROhSaqqsdM+o7VQ6pXkA=="],
"@scalar/api-client-react": ["@scalar/api-client-react@1.2.5", "", { "dependencies": { "@scalar/api-client": "2.3.5", "@scalar/types": "0.1.1", "vue": "^3.5.12" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-DpVDWb1rtNeU8ZqLX2Duos018q/++b3qIOYs7MzB2cXNg1802ZO7tPywXbYyizxLuy/dziI3UwGkjBuOKKXMGQ=="],
"@scalar/code-highlight": ["@scalar/code-highlight@0.0.27", "", { "dependencies": { "hast-util-to-text": "^4.0.2", "highlight.js": "^11.9.0", "highlightjs-curl": "^1.3.0", "highlightjs-vue": "^1.0.0", "lowlight": "^3.1.0", "rehype-external-links": "^3.0.0", "rehype-format": "^5.0.0", "rehype-parse": "^9.0.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "rehype-stringify": "^10.0.0", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.0", "remark-stringify": "^11.0.0", "unified": "^11.0.4", "unist-util-visit": "^5.0.0" } }, "sha512-A61FUxqD278L+iLtdbMl4+Pg72wtMrnAYft8v1FNY44uf6UfmM47eDVmzWrc7bSvDevg3ho5QA8cKiJBHXZHJA=="],
"@scalar/code-highlight": ["@scalar/code-highlight@0.0.25", "", { "dependencies": { "hast-util-to-text": "^4.0.2", "highlight.js": "^11.9.0", "highlightjs-curl": "^1.3.0", "highlightjs-vue": "^1.0.0", "lowlight": "^3.1.0", "rehype-external-links": "^3.0.0", "rehype-format": "^5.0.0", "rehype-parse": "^9.0.0", "rehype-raw": "^7.0.0", "rehype-sanitize": "^6.0.0", "rehype-stringify": "^10.0.0", "remark-gfm": "^4.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.0", "remark-stringify": "^11.0.0", "unified": "^11.0.4", "unist-util-visit": "^5.0.0" } }, "sha512-rmiXaAoL3Zl+OycIO1CMj8apaeAU/p41EmCpHTxInZiFVW0++iClce2fun1lK6qjTMZneR6UwE4qBKiUUVLCpg=="],
"@scalar/components": ["@scalar/components@0.13.47", "", { "dependencies": { "@floating-ui/utils": "^0.2.2", "@floating-ui/vue": "^1.0.2", "@headlessui/vue": "^1.7.20", "@scalar/code-highlight": "0.0.27", "@scalar/themes": "0.9.86", "@scalar/use-hooks": "0.1.40", "@scalar/use-toasts": "0.7.9", "@vueuse/core": "^10.10.0", "cva": "1.0.0-beta.2", "nanoid": "^5.1.5", "pretty-bytes": "^6.1.1", "radix-vue": "^1.9.3", "tailwind-merge": "^2.5.5", "vue": "^3.5.12" } }, "sha512-e88mKKsCEspd06bpPQPnhtEvCo/jjoFFOX9yUSV9sr0sWFZHi0ihq1zvnpLKpULyS+C5zzyoN/tGVhmaYpXgyg=="],
"@scalar/components": ["@scalar/components@0.13.37", "", { "dependencies": { "@floating-ui/utils": "^0.2.2", "@floating-ui/vue": "^1.0.2", "@headlessui/vue": "^1.7.20", "@scalar/code-highlight": "0.0.25", "@scalar/themes": "0.9.79", "@scalar/use-hooks": "0.1.33", "@scalar/use-toasts": "0.7.9", "@vueuse/core": "^10.10.0", "cva": "1.0.0-beta.2", "nanoid": "^5.0.9", "pretty-bytes": "^6.1.1", "radix-vue": "^1.9.3", "tailwind-merge": "^2.5.5", "vue": "^3.5.12" } }, "sha512-bhJxg0I63nUH0qoZgb8nyHKCSzL8L9widP2WIYymIvXpCFLwCvF64Z0CAbihwgXxq0YblPvNM+g5N3dRtmXqdA=="],
"@scalar/draggable": ["@scalar/draggable@0.1.11", "", { "dependencies": { "vue": "^3.5.12" } }, "sha512-EQW9N1+mDORhsbjdtCI3XDvmUKsuKw1uf6r3kT1Mm2zQKT+rWwA0ChsAkEN6OG62C0YumMuXpH71h1seAWptxw=="],
"@scalar/icons": ["@scalar/icons@0.1.3", "", { "dependencies": { "vue": "^3.5.12" } }, "sha512-Bl46u7WsJ7NYjW1Fva7SMvw9c/92pGBP8B68tvDc+QevQ04DVNxw6+ny1NU/PnLtpuu1rUpPdtSCAkV1OdQGZQ=="],
"@scalar/import": ["@scalar/import@0.3.13", "", { "dependencies": { "@scalar/oas-utils": "0.2.130", "@scalar/openapi-parser": "0.10.14", "yaml": "^2.4.5" } }, "sha512-ooKyRxwtvMpxBnoLt9mSJF8er5rCR6RzGJaIMRCj7ViN776eY4mbLiYcXre/LO8XfLSHb1p7XbNDhfFyTHaUlw=="],
"@scalar/import": ["@scalar/import@0.3.2", "", { "dependencies": { "@scalar/oas-utils": "0.2.120", "@scalar/openapi-parser": "0.10.10", "yaml": "^2.4.5" } }, "sha512-de7IDZgEYOhhgaq8lFFbfiJ4Hx/ITIXvuLA8SfSl5UQKg+LxiOnnn/5PSKF+pnjWmUf7Kz/ds0eHOXpZv5iMkw=="],
"@scalar/oas-utils": ["@scalar/oas-utils@0.2.130", "", { "dependencies": { "@hyperjump/json-schema": "^1.9.6", "@scalar/object-utils": "1.1.13", "@scalar/openapi-types": "0.2.0", "@scalar/themes": "0.9.86", "@scalar/types": "0.1.7", "flatted": "^3.3.1", "microdiff": "^1.4.0", "nanoid": "^5.1.5", "type-fest": "^4.20.0", "yaml": "^2.4.5", "zod": "^3.23.8" } }, "sha512-sVpdc3+3c/WiNrKEIwzJ+ml2ZQBjarMOTDJCM/IrvYhrJE0nHrdkzxlJgNPi++vJbVl0saYt8LhEItALv7NziA=="],
"@scalar/oas-utils": ["@scalar/oas-utils@0.2.120", "", { "dependencies": { "@hyperjump/json-schema": "^1.9.6", "@scalar/object-utils": "1.1.13", "@scalar/openapi-types": "0.1.9", "@scalar/themes": "0.9.79", "@scalar/types": "0.1.1", "flatted": "^3.3.1", "microdiff": "^1.4.0", "nanoid": "^5.0.9", "yaml": "^2.4.5", "zod": "^3.23.8" } }, "sha512-npu0uLClqqXVZfxMdKBWxkWCmONK0jKaUcfmVhGza9Jij5aJyvdfDw6vH/Hh+DghgECwAvQLQeIBZTxjr9ufzg=="],
"@scalar/object-utils": ["@scalar/object-utils@1.1.13", "", { "dependencies": { "flatted": "^3.3.1", "just-clone": "^6.2.0", "ts-deepmerge": "^7.0.1" } }, "sha512-311eTykIXgOtjCs4VTELj9UMT97jHTWc5qkGNoIzZ5nxjCcvOVe7kDQobIkE8dGT+ybOgHz5qly02Eu7nVHeZQ=="],
@@ -1098,17 +1090,17 @@
"@scalar/openapi-types": ["@scalar/openapi-types@0.1.9", "", {}, "sha512-HQQudOSQBU7ewzfnBW9LhDmBE2XOJgSfwrh5PlUB7zJup/kaRkBGNgV2wMjNz9Af/uztiU/xNrO179FysmUT+g=="],
"@scalar/postman-to-openapi": ["@scalar/postman-to-openapi@0.2.3", "", { "dependencies": { "@scalar/oas-utils": "0.2.130", "@scalar/openapi-types": "0.2.0" } }, "sha512-/I5QbDFy+Sh29EIEgub/ztI+1eNtHRn+mln726hR+uWOyVyaDk0FfNC0R4XOn8SsDNyu5eqPbXBb9vceTVG6jQ=="],
"@scalar/postman-to-openapi": ["@scalar/postman-to-openapi@0.1.43", "", { "dependencies": { "@scalar/oas-utils": "0.2.120", "@scalar/openapi-types": "0.1.9" } }, "sha512-gLOkYYPCTKYFBOwyBOKZDc0seZjntmwPTchJUr3oxGQmLB1Y5VBJ+8fXJCTp5TwKiiztjALJs79y9s7jXBdWMA=="],
"@scalar/snippetz": ["@scalar/snippetz@0.2.19", "", { "dependencies": { "stringify-object": "^5.0.0" } }, "sha512-fxC5mL3AZWiXAM21sMe1QU1/mu5KceN8ZmzFaP3xmdK26o/MkPKSLGVWW7w6OQkZi5hNloLHXXQiaI235qomEg=="],
"@scalar/snippetz": ["@scalar/snippetz@0.2.16", "", { "dependencies": { "stringify-object": "^5.0.0" } }, "sha512-xtIY4kvV619IF2uXg6fDw7emtXwuJeWzLunGAaUTIOwNRw5mGSKqJnLcSnIiSdH54YmN8D2CtdJRo2VxPP9/Wg=="],
"@scalar/themes": ["@scalar/themes@0.9.86", "", { "dependencies": { "@scalar/types": "0.1.7" } }, "sha512-QUHo9g5oSWi+0Lm1vJY9TaMZRau8LHg+vte7q5BVTBnu6NuQfigCaN+ouQ73FqIVd96TwMO6Db+dilK1B+9row=="],
"@scalar/themes": ["@scalar/themes@0.9.79", "", { "dependencies": { "@scalar/types": "0.1.1" } }, "sha512-zWiHCZAIjPGa8X9o/NORBPRMTMblLEz2+2RcfW9yIKNO/8H4Gz0rltiGGlJ6vX0o+qHwx7AdgfY+7njmWQR4ng=="],
"@scalar/types": ["@scalar/types@0.1.7", "", { "dependencies": { "@scalar/openapi-types": "0.2.0", "@unhead/schema": "^1.11.11", "nanoid": "^5.1.5", "type-fest": "^4.20.0", "zod": "^3.23.8" } }, "sha512-irIDYzTQG2KLvFbuTI8k2Pz/R4JR+zUUSykVTbEMatkzMmVFnn1VzNSMlODbadycwZunbnL2tA27AXed9URVjw=="],
"@scalar/types": ["@scalar/types@0.1.1", "", { "dependencies": { "@scalar/openapi-types": "0.1.9", "@unhead/schema": "^1.11.11", "zod": "^3.23.8" } }, "sha512-LlUX6AmOOGoRqOMoO835V2FezM1KiO5UlvQC3poT/s7oqD6ranqwRNFxyrPz/IxClPYR+SV1yBUSNKely4ZQhQ=="],
"@scalar/use-codemirror": ["@scalar/use-codemirror@0.11.92", "", { "dependencies": { "@codemirror/autocomplete": "^6.18.3", "@codemirror/commands": "^6.7.1", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.8", "@codemirror/lang-json": "^6.0.0", "@codemirror/lang-xml": "^6.0.0", "@codemirror/lang-yaml": "^6.1.2", "@codemirror/language": "^6.10.7", "@codemirror/lint": "^6.8.4", "@codemirror/state": "^6.5.0", "@codemirror/view": "^6.35.3", "@lezer/common": "^1.2.3", "@lezer/highlight": "^1.2.1", "@lezer/lr": "^1.4.2", "@replit/codemirror-css-color-picker": "^6.3.0", "@scalar/components": "0.13.47", "codemirror": "^6.0.0", "style-mod": "^4.1.2", "vue": "^3.5.12" } }, "sha512-WDd50xGLV+q1T36cKzmhqYP+TyHe4MOW0tIiu09ed9aMXGUk6kQgnf0J3rYPsGeNj9IkVG3znZ7oYT3QplLGVA=="],
"@scalar/use-codemirror": ["@scalar/use-codemirror@0.11.82", "", { "dependencies": { "@codemirror/autocomplete": "^6.18.3", "@codemirror/commands": "^6.7.1", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.8", "@codemirror/lang-json": "^6.0.0", "@codemirror/lang-xml": "^6.0.0", "@codemirror/lang-yaml": "^6.1.2", "@codemirror/language": "^6.10.7", "@codemirror/lint": "^6.8.4", "@codemirror/state": "^6.5.0", "@codemirror/view": "^6.35.3", "@lezer/common": "^1.2.3", "@lezer/highlight": "^1.2.1", "@lezer/lr": "^1.4.2", "@replit/codemirror-css-color-picker": "^6.3.0", "@scalar/components": "0.13.37", "codemirror": "^6.0.0", "style-mod": "^4.1.2", "vue": "^3.5.12" } }, "sha512-zFECln7aWKRf6iJO9oovByD59EsrOMenNLfLhneH6L+K1CrBoHFVr4czSDlom1wlr3HPg3xwpZrukoAteHYILQ=="],
"@scalar/use-hooks": ["@scalar/use-hooks@0.1.40", "", { "dependencies": { "@scalar/themes": "0.9.86", "@scalar/use-toasts": "0.7.9", "@vueuse/core": "^10.10.0", "vue": "^3.5.12", "zod": "^3.23.8" } }, "sha512-z8qtgIcW9Z3PCrP2cbKG+D2EVhpNgl1N0ucGtDg5SMl/fvCyXNfqB9j+u3ygxkouatfQ9zRZuhxreNMkW9/H5g=="],
"@scalar/use-hooks": ["@scalar/use-hooks@0.1.33", "", { "dependencies": { "@scalar/themes": "0.9.79", "@scalar/use-toasts": "0.7.9", "@vueuse/core": "^10.10.0", "vue": "^3.5.12", "zod": "^3.23.8" } }, "sha512-ENm0bWwRdAWWF/S6TbE+fFx0vP2mgEpG5APqQBomm0a41/6L2HJ/TN+9ajAvrJXGi0ULWuxihNS4Jue6tpEssA=="],
"@scalar/use-toasts": ["@scalar/use-toasts@0.7.9", "", { "dependencies": { "nanoid": "^5.0.9", "vue": "^3.5.12", "vue-sonner": "^1.0.3" } }, "sha512-EcUDJY8VozLS9sfoQKvvipStQJ9RuH/nKOzf0BBr+mZDmumi1WFZ1iIJnHVXIN3iSLcSAr5ej6rOqa6jIv4bCQ=="],
@@ -1708,7 +1700,7 @@
"es6-weak-map": ["es6-weak-map@2.0.3", "", { "dependencies": { "d": "1", "es5-ext": "^0.10.46", "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.1" } }, "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA=="],
"esbuild": ["esbuild@0.24.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.24.2", "@esbuild/android-arm": "0.24.2", "@esbuild/android-arm64": "0.24.2", "@esbuild/android-x64": "0.24.2", "@esbuild/darwin-arm64": "0.24.2", "@esbuild/darwin-x64": "0.24.2", "@esbuild/freebsd-arm64": "0.24.2", "@esbuild/freebsd-x64": "0.24.2", "@esbuild/linux-arm": "0.24.2", "@esbuild/linux-arm64": "0.24.2", "@esbuild/linux-ia32": "0.24.2", "@esbuild/linux-loong64": "0.24.2", "@esbuild/linux-mips64el": "0.24.2", "@esbuild/linux-ppc64": "0.24.2", "@esbuild/linux-riscv64": "0.24.2", "@esbuild/linux-s390x": "0.24.2", "@esbuild/linux-x64": "0.24.2", "@esbuild/netbsd-arm64": "0.24.2", "@esbuild/netbsd-x64": "0.24.2", "@esbuild/openbsd-arm64": "0.24.2", "@esbuild/openbsd-x64": "0.24.2", "@esbuild/sunos-x64": "0.24.2", "@esbuild/win32-arm64": "0.24.2", "@esbuild/win32-ia32": "0.24.2", "@esbuild/win32-x64": "0.24.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA=="],
"esbuild": ["esbuild@0.17.19", "", { "optionalDependencies": { "@esbuild/android-arm": "0.17.19", "@esbuild/android-arm64": "0.17.19", "@esbuild/android-x64": "0.17.19", "@esbuild/darwin-arm64": "0.17.19", "@esbuild/darwin-x64": "0.17.19", "@esbuild/freebsd-arm64": "0.17.19", "@esbuild/freebsd-x64": "0.17.19", "@esbuild/linux-arm": "0.17.19", "@esbuild/linux-arm64": "0.17.19", "@esbuild/linux-ia32": "0.17.19", "@esbuild/linux-loong64": "0.17.19", "@esbuild/linux-mips64el": "0.17.19", "@esbuild/linux-ppc64": "0.17.19", "@esbuild/linux-riscv64": "0.17.19", "@esbuild/linux-s390x": "0.17.19", "@esbuild/linux-x64": "0.17.19", "@esbuild/netbsd-x64": "0.17.19", "@esbuild/openbsd-x64": "0.17.19", "@esbuild/sunos-x64": "0.17.19", "@esbuild/win32-arm64": "0.17.19", "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw=="],
"esbuild-android-64": ["esbuild-android-64@0.15.18", "", { "os": "android", "cpu": "x64" }, "sha512-wnpt3OXRhcjfIDSZu9bnzT4/TNTDsOUvip0foZOUBG7QbSt//w3QV4FInVJxNhKc/ErhUxc5z4QjHtMi7/TbgA=="],
@@ -1782,8 +1774,6 @@
"express": ["express@5.0.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.0.1", "content-disposition": "^1.0.0", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "^1.2.1", "debug": "4.3.6", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "^2.0.0", "fresh": "2.0.0", "http-errors": "2.0.0", "merge-descriptors": "^2.0.0", "methods": "~1.1.2", "mime-types": "^3.0.0", "on-finished": "2.4.1", "once": "1.4.0", "parseurl": "~1.3.3", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "router": "^2.0.0", "safe-buffer": "5.2.1", "send": "^1.1.0", "serve-static": "^2.1.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "^2.0.0", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ=="],
"exsolve": ["exsolve@1.0.4", "", {}, "sha512-xsZH6PXaER4XoV+NiT7JHp1bJodJVT+cxeSH1G0f0tlT0lJqYuHUP3bUx2HtfTDvOagMINYp8rsqusxud3RXhw=="],
"ext": ["ext@1.7.0", "", { "dependencies": { "type": "^2.7.2" } }, "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw=="],
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
@@ -2292,7 +2282,7 @@
"min-indent": ["min-indent@1.0.1", "", {}, "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="],
"miniflare": ["miniflare@4.20250409.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250409.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-Hu02dYZvFR+MyrI57O6rSrOUTofcO9EIvcodgq2SAHzAeWSJw2E0oq9lylOrcckFwPMcwxUAb/cQN1LIoCyySw=="],
"miniflare": ["miniflare@3.20250214.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250214.0", "ws": "8.18.0", "youch": "3.2.3", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-t+lT4p2lbOcKv4PS3sx1F/wcDAlbEYZCO2VooLp4H7JErWWYIi9yjD3UillC3CGOpiBahVg5nrPCoFltZf6UlA=="],
"minimatch": ["minimatch@10.0.1", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ=="],
@@ -2320,7 +2310,7 @@
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
"nanoid": ["nanoid@5.1.5", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw=="],
"nanoid": ["nanoid@5.1.2", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-b+CiXQCNMUGe0Ri64S9SXFcP9hogjAJ2Rd6GdVxhPLRm7mhGaM7VgOvCAJ1ZshfHbqVDI3uqTI5C8/GaKuLI7g=="],
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
@@ -2362,7 +2352,7 @@
"obliterator": ["obliterator@1.6.1", "", {}, "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig=="],
"ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="],
"ohash": ["ohash@1.1.4", "", {}, "sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g=="],
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
@@ -2440,7 +2430,7 @@
"path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
"pcre-to-regexp": ["pcre-to-regexp@1.1.0", "", {}, "sha512-KF9XxmUQJ2DIlMj3TqNqY1AWvyvTuIuq11CuuekxyaYMiFuMKGgQrePYMX5bXKLhLG3sDI4CsGAYHPaT7VV7+g=="],
@@ -2812,7 +2802,7 @@
"type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="],
"type-fest": ["type-fest@4.40.0", "", {}, "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw=="],
"type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="],
"type-is": ["type-is@2.0.0", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw=="],
@@ -2828,7 +2818,7 @@
"undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="],
"unenv": ["unenv@2.0.0-rc.15", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.5.4" } }, "sha512-J/rEIZU8w6FOfLNz/hNKsnY+fFHWnu9MH4yRbSZF3xbbGHovcetXPs7sD+9p8L6CeNC//I9bhRYAOsBt2u7/OA=="],
"unenv": ["unenv@2.0.0-rc.1", "", { "dependencies": { "defu": "^6.1.4", "mlly": "^1.7.4", "ohash": "^1.1.4", "pathe": "^1.1.2", "ufo": "^1.5.4" } }, "sha512-PU5fb40H8X149s117aB4ytbORcCvlASdtF97tfls4BPIyj4PeVxvpSuy1jAptqYHqB0vb2w2sHvzM0XWcp2OKg=="],
"unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
@@ -2918,9 +2908,9 @@
"widest-line": ["widest-line@3.1.0", "", { "dependencies": { "string-width": "^4.0.0" } }, "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg=="],
"workerd": ["workerd@1.20250409.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250409.0", "@cloudflare/workerd-darwin-arm64": "1.20250409.0", "@cloudflare/workerd-linux-64": "1.20250409.0", "@cloudflare/workerd-linux-arm64": "1.20250409.0", "@cloudflare/workerd-windows-64": "1.20250409.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-hqjX9swiHvrkOI3jlH9lrZsZRRv9lddUwcMe8Ua76jnyQz+brybWznNjHu8U5oswwcrFwvky1A4CcLjcLY31gQ=="],
"workerd": ["workerd@1.20250214.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250214.0", "@cloudflare/workerd-darwin-arm64": "1.20250214.0", "@cloudflare/workerd-linux-64": "1.20250214.0", "@cloudflare/workerd-linux-arm64": "1.20250214.0", "@cloudflare/workerd-windows-64": "1.20250214.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-QWcqXZLiMpV12wiaVnb3nLmfs/g4ZsFQq2mX85z546r3AX4CTIkXl0VP50W3CwqLADej3PGYiRDOTelDOwVG1g=="],
"wrangler": ["wrangler@4.10.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.1", "blake3-wasm": "2.1.5", "esbuild": "0.24.2", "miniflare": "4.20250409.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.15", "workerd": "1.20250409.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250409.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-fTE4hZ79msEUt8+HEjl/8Q72haCyzPLu4PgrU3L81ysmjrMEdiYfUPqnvCkBUVtJvrDNdctTEimkufT1Y0ipNg=="],
"wrangler": ["wrangler@3.112.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@esbuild-plugins/node-modules-polyfill": "0.2.2", "blake3-wasm": "2.1.5", "esbuild": "0.17.19", "miniflare": "3.20250214.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.1", "workerd": "1.20250214.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250214.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-PNQWGze3ODlWwG33LPr8kNhbht3eB3L9fogv+fapk2fjaqj0kNweRapkwmvtz46ojcqWzsxmTe4nOC0hIVUfPA=="],
"wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
@@ -2952,7 +2942,7 @@
"yn": ["yn@3.1.1", "", {}, "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="],
"youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="],
"youch": ["youch@3.2.3", "", { "dependencies": { "cookie": "^0.5.0", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-ZBcWz/uzZaQVdCvfV4uk616Bbpf2ee+F/AvuKDR5EwX/Y4v06xWdtMluqTD7+KlZdM93lLm9gMZYo0sKBS0pgw=="],
"zhead": ["zhead@2.2.4", "", {}, "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag=="],
@@ -3566,8 +3556,6 @@
"@cloudflare/next-on-pages/miniflare": ["miniflare@3.20241018.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "^8.8.0", "acorn-walk": "^8.2.0", "capnp-ts": "^0.7.0", "exit-hook": "^2.2.1", "glob-to-regexp": "^0.4.1", "stoppable": "^1.1.0", "undici": "^5.28.4", "workerd": "1.20241018.1", "ws": "^8.17.1", "youch": "^3.2.2", "zod": "^3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-g7i5oGAoJOk8+hJp77A5/wAdu7PEvi5hQc+0wzwzjhUNM2I5DHd2Cc29ACPhAe1kIXvCCVkxs3+REF52qnX0aw=="],
"@cloudflare/next-on-pages/wrangler": ["wrangler@3.112.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@esbuild-plugins/node-modules-polyfill": "0.2.2", "blake3-wasm": "2.1.5", "esbuild": "0.17.19", "miniflare": "3.20250214.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.1", "workerd": "1.20250214.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250214.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-PNQWGze3ODlWwG33LPr8kNhbht3eB3L9fogv+fapk2fjaqj0kNweRapkwmvtz46ojcqWzsxmTe4nOC0hIVUfPA=="],
"@codemirror/lang-html/@codemirror/autocomplete": ["@codemirror/autocomplete@6.18.1", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-iWHdj/B1ethnHRTwZj+C1obmmuCzquH29EbcKr0qIjA9NfDeBDJ7vs+WOHsFeLeflE4o+dHfYndJloMKHUkWUA=="],
"@codemirror/lang-html/@codemirror/lang-css": ["@codemirror/lang-css@6.3.0", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@lezer/common": "^1.0.2", "@lezer/css": "^1.1.7" } }, "sha512-CyR4rUNG9OYcXDZwMPvJdtb6PHbBDKUc/6Na2BIwZ6dKab1JQqKa4di+RNRY9Myn7JB81vayKwJeQ7jEdmNVDA=="],
@@ -3714,23 +3702,21 @@
"@rollup/pluginutils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="],
"@scalar/api-client/@scalar/openapi-parser": ["@scalar/openapi-parser@0.10.14", "", { "dependencies": { "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "jsonpointer": "^5.0.1", "leven": "^4.0.0", "yaml": "^2.4.5" } }, "sha512-VXr979NMx6wZ+kpFKor2eyCJZOjyMwcBRc6c4Gc92ZMOC7ZNYqjwbw+Ubh2ELJyP5cWAjOFSrNwtylema0pw5w=="],
"@scalar/api-client/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="],
"@scalar/api-client/pretty-ms": ["pretty-ms@8.0.0", "", { "dependencies": { "parse-ms": "^3.0.0" } }, "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q=="],
"@scalar/import/@scalar/openapi-parser": ["@scalar/openapi-parser@0.10.14", "", { "dependencies": { "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "ajv-formats": "^3.0.1", "jsonpointer": "^5.0.1", "leven": "^4.0.0", "yaml": "^2.4.5" } }, "sha512-VXr979NMx6wZ+kpFKor2eyCJZOjyMwcBRc6c4Gc92ZMOC7ZNYqjwbw+Ubh2ELJyP5cWAjOFSrNwtylema0pw5w=="],
"@scalar/api-client/zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
"@scalar/oas-utils/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="],
"@scalar/code-highlight/remark-gfm": ["remark-gfm@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA=="],
"@scalar/oas-utils/flatted": ["flatted@3.3.1", "", {}, "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="],
"@scalar/oas-utils/zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
"@scalar/object-utils/flatted": ["flatted@3.3.1", "", {}, "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="],
"@scalar/postman-to-openapi/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="],
"@scalar/types/zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
"@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.2.0", "", { "dependencies": { "zod": "^3.23.8" } }, "sha512-waiKk12cRCqyUCWTOX0K1WEVX46+hVUK+zRPzAahDJ7G0TApvbNkuy5wx7aoUyEk++HHde0XuQnshXnt8jsddA=="],
"@scalar/use-toasts/nanoid": ["nanoid@5.1.2", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-b+CiXQCNMUGe0Ri64S9SXFcP9hogjAJ2Rd6GdVxhPLRm7mhGaM7VgOvCAJ1ZshfHbqVDI3uqTI5C8/GaKuLI7g=="],
"@scalar/use-hooks/zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
"@shikijs/core/hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
@@ -4032,7 +4018,7 @@
"gaxios/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"gitbook-v2/next": ["next@15.3.1-canary.8", "", { "dependencies": { "@next/env": "15.3.1-canary.8", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.1-canary.8", "@next/swc-darwin-x64": "15.3.1-canary.8", "@next/swc-linux-arm64-gnu": "15.3.1-canary.8", "@next/swc-linux-arm64-musl": "15.3.1-canary.8", "@next/swc-linux-x64-gnu": "15.3.1-canary.8", "@next/swc-linux-x64-musl": "15.3.1-canary.8", "@next/swc-win32-arm64-msvc": "15.3.1-canary.8", "@next/swc-win32-x64-msvc": "15.3.1-canary.8", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-Of5a3BTTIl/iUvL2a9Jh7m7G/H8z4Pj5Vs54CLvcdadokxSNgLOpjzbDgFR8J4PawLx6+MOMy19m9Cvr6EPGug=="],
"gitbook-v2/next": ["next@15.3.1-canary.7", "", { "dependencies": { "@next/env": "15.3.1-canary.7", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.1-canary.7", "@next/swc-darwin-x64": "15.3.1-canary.7", "@next/swc-linux-arm64-gnu": "15.3.1-canary.7", "@next/swc-linux-arm64-musl": "15.3.1-canary.7", "@next/swc-linux-x64-gnu": "15.3.1-canary.7", "@next/swc-linux-x64-musl": "15.3.1-canary.7", "@next/swc-win32-arm64-msvc": "15.3.1-canary.7", "@next/swc-win32-x64-msvc": "15.3.1-canary.7", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-RblvC7A7k8cKpSa6JSCbaatb6JAncv2niUSH7TqXt6xHUoItnxa3Fu7xkducFJQdcW2Hv2h0+NBu6kaKf0S4zw=="],
"global-dirs/ini": ["ini@1.3.7", "", {}, "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="],
@@ -4082,8 +4068,6 @@
"mdast-util-gfm-task-list-item/mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ=="],
"meow/type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="],
"micro/arg": ["arg@4.1.0", "", {}, "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg=="],
"micro/content-type": ["content-type@1.0.4", "", {}, "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="],
@@ -4104,6 +4088,8 @@
"mlly/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
"mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"next/@swc/helpers": ["@swc/helpers@0.5.5", "", { "dependencies": { "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A=="],
"next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
@@ -4118,6 +4104,8 @@
"path-scurry/lru-cache": ["lru-cache@11.0.2", "", {}, "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA=="],
"pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
"postcss/nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="],
@@ -4218,8 +4206,6 @@
"wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
"youch/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"@aws-crypto/crc32/@aws-sdk/types/@smithy/types": ["@smithy/types@4.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw=="],
"@aws-crypto/crc32c/@aws-sdk/types/@smithy/types": ["@smithy/types@4.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw=="],
@@ -4654,18 +4640,10 @@
"@cloudflare/next-on-pages/miniflare/workerd": ["workerd@1.20241018.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20241018.1", "@cloudflare/workerd-darwin-arm64": "1.20241018.1", "@cloudflare/workerd-linux-64": "1.20241018.1", "@cloudflare/workerd-linux-arm64": "1.20241018.1", "@cloudflare/workerd-windows-64": "1.20241018.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-JPW2oAbYOnJj1c5boyDOdjl/Yvur45jhVE8lf+I9oxR6myyAvuH2tdXO62kye68jRluJOMUeyssLes+JRwLmaA=="],
"@cloudflare/next-on-pages/miniflare/youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="],
"@cloudflare/next-on-pages/miniflare/zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="],
"@cloudflare/next-on-pages/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.3.4", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q=="],
"@cloudflare/next-on-pages/wrangler/esbuild": ["esbuild@0.17.19", "", { "optionalDependencies": { "@esbuild/android-arm": "0.17.19", "@esbuild/android-arm64": "0.17.19", "@esbuild/android-x64": "0.17.19", "@esbuild/darwin-arm64": "0.17.19", "@esbuild/darwin-x64": "0.17.19", "@esbuild/freebsd-arm64": "0.17.19", "@esbuild/freebsd-x64": "0.17.19", "@esbuild/linux-arm": "0.17.19", "@esbuild/linux-arm64": "0.17.19", "@esbuild/linux-ia32": "0.17.19", "@esbuild/linux-loong64": "0.17.19", "@esbuild/linux-mips64el": "0.17.19", "@esbuild/linux-ppc64": "0.17.19", "@esbuild/linux-riscv64": "0.17.19", "@esbuild/linux-s390x": "0.17.19", "@esbuild/linux-x64": "0.17.19", "@esbuild/netbsd-x64": "0.17.19", "@esbuild/openbsd-x64": "0.17.19", "@esbuild/sunos-x64": "0.17.19", "@esbuild/win32-arm64": "0.17.19", "@esbuild/win32-ia32": "0.17.19", "@esbuild/win32-x64": "0.17.19" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw=="],
"@cloudflare/next-on-pages/wrangler/miniflare": ["miniflare@3.20250214.2", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250214.0", "ws": "8.18.0", "youch": "3.2.3", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-t+lT4p2lbOcKv4PS3sx1F/wcDAlbEYZCO2VooLp4H7JErWWYIi9yjD3UillC3CGOpiBahVg5nrPCoFltZf6UlA=="],
"@cloudflare/next-on-pages/wrangler/unenv": ["unenv@2.0.0-rc.1", "", { "dependencies": { "defu": "^6.1.4", "mlly": "^1.7.4", "ohash": "^1.1.4", "pathe": "^1.1.2", "ufo": "^1.5.4" } }, "sha512-PU5fb40H8X149s117aB4ytbORcCvlASdtF97tfls4BPIyj4PeVxvpSuy1jAptqYHqB0vb2w2sHvzM0XWcp2OKg=="],
"@cloudflare/next-on-pages/wrangler/workerd": ["workerd@1.20250214.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250214.0", "@cloudflare/workerd-darwin-arm64": "1.20250214.0", "@cloudflare/workerd-linux-64": "1.20250214.0", "@cloudflare/workerd-linux-arm64": "1.20250214.0", "@cloudflare/workerd-windows-64": "1.20250214.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-QWcqXZLiMpV12wiaVnb3nLmfs/g4ZsFQq2mX85z546r3AX4CTIkXl0VP50W3CwqLADej3PGYiRDOTelDOwVG1g=="],
"@codemirror/lang-json/@codemirror/language/@codemirror/view": ["@codemirror/view@6.34.1", "", { "dependencies": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } }, "sha512-t1zK/l9UiRqwUNPm+pdIT0qzJlzuVckbTEMVNFhfWkGiBQClstzg+78vedCvLSX0xJEZ6lwZbPpnljL7L6iwMQ=="],
"@codemirror/lang-json/@codemirror/language/@lezer/common": ["@lezer/common@1.2.2", "", {}, "sha512-Z+R3hN6kXbgBWAuejUNPihylAL1Z5CaFqnIe0nTX8Ej+XlIy3EGtXxn6WtLMO+os2hRkQvm2yvaGMYliUzlJaw=="],
@@ -4908,23 +4886,23 @@
"gaxios/https-proxy-agent/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="],
"gitbook-v2/next/@next/env": ["@next/env@15.3.1-canary.8", "", {}, "sha512-ShZTo0hNhbTRrp7k6oUDSck4Xx4hhfSeLBp35jvGaw1QMZzWYr5v/oc0kEt0bfMdl+833flwKV7kFR3BnrULfg=="],
"gitbook-v2/next/@next/env": ["@next/env@15.3.1-canary.7", "", {}, "sha512-QSzAlzxaGrQYj3Nzgxd35WM6pmxmNk9lOCn0nsNLPidvc3H6wj/fV3Pl8/tVCYQP61luGuA0Ib0VwZc8ja119w=="],
"gitbook-v2/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.1-canary.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZaDynM+pbrnLLlBAxH/CDGp9KN79OFrLcT1ejlWyo86V3SS9Gyqr4nmTuvTevByTTpr1VHReQel8Zbq0Pttu7Q=="],
"gitbook-v2/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.1-canary.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fs7JYU0MKtpgsDBIvbT/wn1MwKP8C+bqcb6IjWnDtVcciFKzYDs/tPCBW6CtYg1wpHqm9horZDA1ytb/sAghxA=="],
"gitbook-v2/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.3.1-canary.8", "", { "os": "darwin", "cpu": "x64" }, "sha512-BsMR8WqeCDAX8C9RYAO8TI4ttpuqKk2oYMb1+bCrOYi857SMfB4vWD4PWmMvj7mwFXGqrxly4W6CBQlD66A+fg=="],
"gitbook-v2/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.3.1-canary.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-Efl6es/IEXJf5groF78fc1PrTEwK9bOyi6fRjp1bsa/AWOHp8rTc3l0T6EeY5T0SWeYilisZZa9rax7nTgw4bw=="],
"gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.3.1-canary.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-xL+K2SW+/46j/KnKNf1gizM1bxwcEaE56eCEG9RPoYS/lfxHLuHcR9O2MlcPI90g/rIN2HXmHeuKRbXbnVy15g=="],
"gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.3.1-canary.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-IS71I8z2wlecRZRHXqWTtxVozZt1h9IH/2MX1P/WkCjQH98acFmCkw0jo/y70mrZ4lw6tRGTNkSBMun+5P5P+w=="],
"gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.3.1-canary.8", "", { "os": "linux", "cpu": "arm64" }, "sha512-bzXlCUXkjIRsMTb6rr7OsWEQmdO2rZgKijnMGBJzEpa9ROq95VJTF+rdyrLmHuc4fPsAGDn/C18V3E1YOi4ipQ=="],
"gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.3.1-canary.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-uJ4env9fZxIFM+aRBCjNeG91HHZ2cRXiF25xJqGmCC1Y5jT5oYAB/tIBCFRSj6NRzdt4pZAamCMRS41WzzVVUQ=="],
"gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.3.1-canary.8", "", { "os": "linux", "cpu": "x64" }, "sha512-snbPQ9th7eoYYMZpNGhEvX4EbqGjjkiXzTEm2F/oDadlZR2wI6egtfQHiZqeczL7XwG3M66hBJ1/U20wdTDtvA=="],
"gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.3.1-canary.7", "", { "os": "linux", "cpu": "x64" }, "sha512-/+efTL2TxoSAYCxdf+g6WvDKIJDoaETvF3I+lxpixhctfrIwpr2qnG0e6u2M55k1iYiiyFIOA63fpIsGnIMJZA=="],
"gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.3.1-canary.8", "", { "os": "linux", "cpu": "x64" }, "sha512-5hmcaGazc3w6rg/gbQOrmuw0kKShf4Egs0JlUPop/ZiRDfZO5KlyrGY0hUD+2pLG7Yx3B/fTbj86cHKfQZDMBw=="],
"gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.3.1-canary.7", "", { "os": "linux", "cpu": "x64" }, "sha512-Hb+agfULOjEhJAmq/r9OTVAo8KxFlnuCT7BEUbSuVP/Gxn883al6juSG7ZZfk7P4a+ZU6iVYyHKIFAKCarZmUA=="],
"gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.3.1-canary.8", "", { "os": "win32", "cpu": "arm64" }, "sha512-t6uwWC/UbQ8CQGyBVbEmpcJC41yAvz2eZZGec9EoO0ZyQ/fbvjOkqfPnf+WcjWifWNeuiWogjN1UBg7YK9IaVA=="],
"gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.3.1-canary.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-gNd5Vq7k9xIEQj85JoavHGRsyvu1zKOjVoPUYtsZRxXw6kcBg84x2g8NeT/3SmwH1PiFU/sjQXtRzr2Kuc2j3A=="],
"gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.1-canary.8", "", { "os": "win32", "cpu": "x64" }, "sha512-93gNqVYwlr9bz6Pm5dQnqjpuOEQyvDre0+H39UqS7h2KuFh3sf2MejhAEr2uFEux4mb7TN0PlohaihO7YxJ3fw=="],
"gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.1-canary.7", "", { "os": "win32", "cpu": "x64" }, "sha512-D5i+KinvNNmspXfgIvEZ1RFOKVhOgdtoSC7/xEUm55hEkcWFVHOtVVPvgV7WUeo1elNxbgxytzmyKbLaifP03g=="],
"gitbook-v2/next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
@@ -5124,71 +5102,7 @@
"@cloudflare/next-on-pages/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20241018.1", "", { "os": "win32", "cpu": "x64" }, "sha512-qwDVh/KrwEPY82h6tZ1O4BXBAKeGy30BeTr9wvTUVeY9eX/KT73GuEG+ttwiashRfqjOa0Gcqjsfpd913ITFyg=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.17.19", "", { "os": "android", "cpu": "arm" }, "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.17.19", "", { "os": "android", "cpu": "arm64" }, "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.17.19", "", { "os": "android", "cpu": "x64" }, "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.17.19", "", { "os": "darwin", "cpu": "arm64" }, "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.17.19", "", { "os": "darwin", "cpu": "x64" }, "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.17.19", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.17.19", "", { "os": "freebsd", "cpu": "x64" }, "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.17.19", "", { "os": "linux", "cpu": "arm" }, "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.17.19", "", { "os": "linux", "cpu": "arm64" }, "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.17.19", "", { "os": "linux", "cpu": "ia32" }, "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.17.19", "", { "os": "linux", "cpu": "ppc64" }, "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.17.19", "", { "os": "linux", "cpu": "none" }, "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.17.19", "", { "os": "linux", "cpu": "s390x" }, "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.17.19", "", { "os": "linux", "cpu": "x64" }, "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.17.19", "", { "os": "none", "cpu": "x64" }, "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.17.19", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.17.19", "", { "os": "sunos", "cpu": "x64" }, "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.17.19", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.17.19", "", { "os": "win32", "cpu": "ia32" }, "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw=="],
"@cloudflare/next-on-pages/wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.17.19", "", { "os": "win32", "cpu": "x64" }, "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA=="],
"@cloudflare/next-on-pages/wrangler/miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
"@cloudflare/next-on-pages/wrangler/miniflare/undici": ["undici@5.28.5", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA=="],
"@cloudflare/next-on-pages/wrangler/miniflare/youch": ["youch@3.2.3", "", { "dependencies": { "cookie": "^0.5.0", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-ZBcWz/uzZaQVdCvfV4uk616Bbpf2ee+F/AvuKDR5EwX/Y4v06xWdtMluqTD7+KlZdM93lLm9gMZYo0sKBS0pgw=="],
"@cloudflare/next-on-pages/wrangler/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
"@cloudflare/next-on-pages/wrangler/unenv/ohash": ["ohash@1.1.4", "", {}, "sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g=="],
"@cloudflare/next-on-pages/wrangler/unenv/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
"@cloudflare/next-on-pages/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250214.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cDvvedWDc5zrgDnuXe2qYcz/TwBvzmweO55C7XpPuAWJ9Oqxv81PkdekYxD8mH989aQ/GI5YD0Fe6fDYlM+T3Q=="],
"@cloudflare/next-on-pages/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250214.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-NytCvRveVzu0mRKo+tvZo3d/gCUway3B2ZVqSi/TS6NXDGBYIJo7g6s3BnTLS74kgyzeDOjhu9j/RBJBS809qw=="],
"@cloudflare/next-on-pages/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250214.0", "", { "os": "linux", "cpu": "x64" }, "sha512-pQ7+aHNHj8SiYEs4d/6cNoimE5xGeCMfgU1yfDFtA9YGN9Aj2BITZgOWPec+HW7ZkOy9oWlNrO6EvVjGgB4tbQ=="],
"@cloudflare/next-on-pages/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250214.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Vhlfah6Yd9ny1npNQjNgElLIjR6OFdEbuR3LCfbLDCwzWEBFhIf7yC+Tpp/a0Hq7kLz3sLdktaP7xl3PJhyOjA=="],
"@cloudflare/next-on-pages/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250214.0", "", { "os": "win32", "cpu": "x64" }, "sha512-GMwMyFbkjBKjYJoKDhGX8nuL4Gqe3IbVnVWf2Q6086CValyIknupk5J6uQWGw2EBU3RGO3x4trDXT5WphQJZDQ=="],
"@cloudflare/next-on-pages/miniflare/youch/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
"@node-minify/core/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="],
+1 -1
View File
@@ -12,7 +12,7 @@
"@codemirror/state": "6.4.1",
"react": "18.3.1",
"react-dom": "18.3.1",
"@gitbook/api": "0.111.0"
"@gitbook/api": "0.109.0"
},
"private": true,
"scripts": {
+2 -2
View File
@@ -21,10 +21,10 @@
},
"devDependencies": {
"typescript": "^5.5.3",
"wrangler": "^4.10.0"
"wrangler": "^3.112.0"
},
"scripts": {
"generate": "wrangler types",
"generate": "wrangler types --experimental-include-runtime",
"build": "tsc",
"typecheck": "tsc --noEmit",
"dev": "tsc -w",
+1 -1
View File
@@ -14,7 +14,7 @@
"resolveJsonModule": true,
"isolatedModules": true,
"incremental": true,
"types": ["./worker-configuration.d.ts"]
"types": ["./.wrangler/types/runtime.d.ts"]
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
+1 -1
View File
@@ -10,7 +10,7 @@
},
"version": "0.3.1",
"dependencies": {
"@gitbook/api": "^0.111.0",
"@gitbook/api": "^0.109.0",
"assert-never": "^1.2.1"
},
"devDependencies": {
-18
View File
@@ -53,24 +53,6 @@ const nextConfig = {
},
],
},
async headers() {
return [
{
source: '/~gitbook/static/:path*',
headers: [
{
key: 'Cache-Control',
value: 'public, max-age=31536000, immutable',
},
{
key: 'Access-Control-Allow-Origin',
value: '*',
},
],
},
];
},
};
export default nextConfig;
+1 -1
View File
@@ -17,7 +17,7 @@
},
"devDependencies": {
"gitbook": "*",
"@opennextjs/cloudflare": "^1.0.0-beta.3",
"@opennextjs/cloudflare": "https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@8f7ed38",
"@types/rison": "^0.0.9",
"tailwindcss": "^3.4.0",
"postcss": "^8"
+22 -1
View File
@@ -1,7 +1,7 @@
{
"main": ".open-next/worker.js",
"name": "gitbook-open-v2",
"compatibility_date": "2025-04-14",
"compatibility_date": "2025-03-11",
"compatibility_flags": ["nodejs_compat", "allow_importable_env"],
"assets": {
"directory": ".open-next/assets",
@@ -12,6 +12,13 @@
},
"env": {
"preview": {
"d1_databases": [
{
"binding": "NEXT_CACHE_D1",
"database_id": "f59ddb40-ad72-4312-9395-0ac6a129af8e",
"database_name": "gitbook-open-v2-tags-preview"
}
],
"r2_buckets": [
{
"binding": "NEXT_INC_CACHE_R2_BUCKET",
@@ -48,6 +55,13 @@
"bucket_name": "gitbook-open-v2-cache-staging"
}
],
"d1_databases": [
{
"binding": "NEXT_CACHE_D1",
"database_id": "9df62e39-1f35-4066-83aa-e9b8ed3ac8d5",
"database_name": "gitbook-open-v2-tags-staging"
}
],
"services": [
{
"binding": "WORKER_SELF_REFERENCE",
@@ -99,6 +113,13 @@
"bucket_name": "gitbook-open-v2-cache-production"
}
],
"d1_databases": [
{
"binding": "NEXT_CACHE_D1",
"database_id": "a6f16fce-5f45-43a9-89a4-7b83ddf25b77",
"database_name": "gitbook-open-v2-tags-production"
}
],
"services": [
{
"binding": "WORKER_SELF_REFERENCE",
-27
View File
@@ -1,32 +1,5 @@
# gitbook
## 0.11.1
### Patch Changes
- Updated dependencies [ebc39e9]
- Updated dependencies [b6b09d4]
- @gitbook/react-openapi@1.2.1
## 0.11.0
### Minor Changes
- d67699a: Add OpenAPI Webhook block
### Patch Changes
- 4b8a621: Show sections tabs only if there is at least two sections
- 8ed1bda: Translate OpenAPI blocks
- 7588cfe: Improve OpenAPIResponses examples and schemas
- Updated dependencies [eeb977f]
- Updated dependencies [3363a18]
- Updated dependencies [d67699a]
- Updated dependencies [8ed1bda]
- Updated dependencies [7588cfe]
- Updated dependencies [ad1dc0b]
- @gitbook/react-openapi@1.2.0
## 0.10.1
### Patch Changes
+17 -17
View File
@@ -10,18 +10,18 @@ const testCases: TestsCase[] = [
{ name: 'OpenAPI', url: '/snyk-api/reference/apps', run: waitForCookiesDialog },
],
},
// {
// name: 'Nexthink',
// contentBaseURL: 'https://docs.nexthink.com',
// tests: [
// {
// name: 'Home',
// url: '/',
// screenshot: { waitForTOCScrolling: false },
// run: waitForCookiesDialog,
// },
// ],
// },
{
name: 'Nexthink',
contentBaseURL: 'https://docs.nexthink.com',
tests: [
{
name: 'Home',
url: '/',
screenshot: { waitForTOCScrolling: false },
run: waitForCookiesDialog,
},
],
},
{
name: 'asiksupport-stg.dto.kemkes.go.id',
contentBaseURL: 'https://asiksupport-stg.dto.kemkes.go.id',
@@ -157,11 +157,11 @@ const testCases: TestsCase[] = [
contentBaseURL: 'https://wiki.redmodding.org',
tests: [{ name: 'Home', url: '/' }],
},
// {
// name: 'docs.cherry-ai.com',
// contentBaseURL: 'https://docs.cherry-ai.com',
// tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
// },
{
name: 'docs.cherry-ai.com',
contentBaseURL: 'https://docs.cherry-ai.com',
tests: [{ name: 'Home', url: '/', run: waitForCookiesDialog }],
},
{
name: 'docs.snyk.io',
contentBaseURL: 'https://docs.snyk.io',
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "gitbook",
"version": "0.11.1",
"version": "0.10.1",
"private": true,
"scripts": {
"dev": "env-cmd --silent -f ../../.env.local next dev",
-4
View File
@@ -1,7 +1,3 @@
# GitBook immutable static assets
# Duplicated from next.config.mjs until OpenNext supports generating static headers
/~gitbook/static/*
cache-control: public,max-age=31536000,immutable
Access-Control-Allow-Origin: *
/_next/static/*
Access-Control-Allow-Origin: *
@@ -25,7 +25,7 @@ import { IntegrationBlock } from './Integration';
import { List } from './List';
import { ListItem } from './ListItem';
import { BlockMath } from './Math';
import { OpenAPIOperation, OpenAPISchemas, OpenAPIWebhook } from './OpenAPI';
import { OpenAPIOperation, OpenAPISchemas } from './OpenAPI';
import { Paragraph } from './Paragraph';
import { Quote } from './Quote';
import { ReusableContent } from './ReusableContent';
@@ -85,8 +85,6 @@ export function Block<T extends DocumentBlock>(props: BlockProps<T>) {
return <OpenAPIOperation {...props} block={block} />;
case 'openapi-schemas':
return <OpenAPISchemas {...props} block={block} />;
case 'openapi-webhook':
return <OpenAPIWebhook {...props} block={block} />;
case 'embed':
return <Embed {...props} block={block} />;
case 'blockquote':
@@ -161,7 +159,6 @@ export function BlockSkeleton(props: { block: DocumentBlock; style: ClassValue }
case 'swagger':
case 'openapi-operation':
case 'openapi-schemas':
case 'openapi-webhook':
case 'math':
case 'divider':
case 'content-ref':
@@ -51,7 +51,7 @@ async function OpenAPIOperationBody(props: BlockProps<AnyOpenAPIOperationsBlock>
return (
<BaseOpenAPIOperation
data={data}
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
context={getOpenAPIContext({ props, specUrl })}
className="openapi-block"
/>
);
@@ -51,7 +51,7 @@ async function OpenAPISchemasBody(props: BlockProps<OpenAPISchemasBlock>) {
<BaseOpenAPISchemas
schemas={data.schemas}
grouped={block.data.grouped}
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
context={getOpenAPIContext({ props, specUrl })}
className="openapi-block"
/>
);
@@ -1,58 +0,0 @@
import { OpenAPIWebhook as BaseOpenAPIWebhook } from '@gitbook/react-openapi';
import { resolveOpenAPIWebhookBlock } from '@/lib/openapi/resolveOpenAPIWebhookBlock';
import { tcls } from '@/lib/tailwind';
import type { BlockProps } from '../Block';
import './scalar.css';
import './style.css';
import type { OpenAPIWebhookBlock } from '@/lib/openapi/types';
import { getOpenAPIContext } from './context';
/**
* Render an openapi block or an openapi-webhook block.
*/
export async function OpenAPIWebhook(props: BlockProps<OpenAPIWebhookBlock>) {
const { style } = props;
return (
<div className={tcls('flex w-full min-w-0', style, 'max-w-full')}>
<OpenAPIWebhookBody {...props} />
</div>
);
}
async function OpenAPIWebhookBody(props: BlockProps<OpenAPIWebhookBlock>) {
const { block, context } = props;
if (!context.contentContext) {
return null;
}
const { data, specUrl, error } = await resolveOpenAPIWebhookBlock({
block,
context: context.contentContext,
});
if (error) {
return (
<div className="hidden">
<p>
Error with {specUrl}: {error.message}
</p>
</div>
);
}
if (!data || !specUrl) {
return null;
}
return (
<BaseOpenAPIWebhook
data={data}
context={getOpenAPIContext({ props, specUrl, context: context.contentContext })}
className="openapi-block"
/>
);
}
@@ -1,6 +1,6 @@
import type { JSONDocument } from '@gitbook/api';
import { Icon } from '@gitbook/icons';
import { type OpenAPIContextInput, checkIsValidLocale } from '@gitbook/react-openapi';
import type { OpenAPIContext } from '@gitbook/react-openapi';
import { tcls } from '@/lib/tailwind';
@@ -11,31 +11,17 @@ import { Heading } from '../Heading';
import './scalar.css';
import './style.css';
import { DEFAULT_LOCALE, getCustomizationLocale } from '@/intl/server';
import type {
AnyOpenAPIOperationsBlock,
OpenAPISchemasBlock,
OpenAPIWebhookBlock,
} from '@/lib/openapi/types';
import type { GitBookAnyContext } from '@v2/lib/context';
import type { AnyOpenAPIOperationsBlock, OpenAPISchemasBlock } from '@/lib/openapi/types';
/**
* Get the OpenAPI context to render a block.
*/
export function getOpenAPIContext(args: {
props: BlockProps<AnyOpenAPIOperationsBlock | OpenAPISchemasBlock | OpenAPIWebhookBlock>;
props: BlockProps<AnyOpenAPIOperationsBlock | OpenAPISchemasBlock>;
specUrl: string;
context: GitBookAnyContext | undefined;
}): OpenAPIContextInput {
const { props, specUrl, context } = args;
}): OpenAPIContext {
const { props, specUrl } = args;
const { block } = props;
const customization = context && 'customization' in context ? context.customization : null;
const customizationLocale = customization
? getCustomizationLocale(customization)
: DEFAULT_LOCALE;
const locale = checkIsValidLocale(customizationLocale) ? customizationLocale : DEFAULT_LOCALE;
return {
specUrl,
icons: {
@@ -83,6 +69,5 @@ export function getOpenAPIContext(args: {
defaultInteractiveOpened: props.context.mode === 'print',
id: block.meta?.id,
blockKey: block.key,
locale,
};
}
@@ -1,3 +1,2 @@
export * from './OpenAPIOperation';
export * from './OpenAPISchemas';
export * from './OpenAPIWebhook';
@@ -1,7 +1,6 @@
/* Layout Components */
.openapi-operation,
.openapi-schemas,
.openapi-webhook {
.openapi-schemas {
@apply flex-1 flex flex-col gap-8 mb-14 min-w-0;
}
@@ -9,10 +8,6 @@
@apply flex flex-col mb-14 flex-1;
}
.openapi-schemas-title {
@apply tabular-nums text-[0.813rem] leading-4 font-mono shrink-0 font-medium text-tint-strong;
}
.openapi-columns {
@apply grid grid-cols-1 lg:grid-cols-2 gap-6 print-mode:grid-cols-1 justify-stretch;
}
@@ -51,7 +46,10 @@
@apply font-semibold font-mono truncate;
}
.openapi-description.openapi-markdown,
.openapi-description.openapi-markdown {
@apply prose-sm text-[0.938rem];
}
.openapi-description.openapi-markdown code {
@apply prose-sm;
}
@@ -91,23 +89,21 @@
}
/* Method Tags */
.openapi-method,
.openapi-statuscode {
@apply rounded uppercase font-mono items-center shrink-0 font-semibold text-[0.813rem] px-1 py-0.5 mr-2 text-tint-12/8 leading-tight align-middle inline-flex ring-1 ring-inset ring-tint-12/1 dark:ring-tint-1/1 whitespace-nowrap;
.openapi-method {
@apply rounded uppercase font-mono shrink-0 font-bold text-xs px-1 py-0.5 mr-2 text-tint-12/8 leading-tight align-middle inline-flex ring-1 ring-inset ring-tint-12/1 dark:ring-tint-1/1 whitespace-nowrap;
}
.openapi-method-get,
.openapi-statuscode-success {
.openapi-method-get {
/* @apply bg-[hsl(215,54%,86%)] dark:bg-[hsla(215,54%,45%,0.24)] dark:text-[hsl(215,54%,86%)]; */
@apply bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100;
}
.openapi-method-post,
.openapi-statuscode-redirection {
.openapi-method-post {
/* @apply bg-[hsl(120,25%,80%)] dark:bg-[hsla(120,54%,32%,0.24)] dark:text-[hsl(120,25%,80%)]; */
@apply bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-100;
}
.openapi-method-put,
.openapi-statuscode-informational {
.openapi-method-put {
@apply bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-100;
}
@@ -115,9 +111,8 @@
@apply bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-100;
}
.openapi-method-delete,
.openapi-statuscode-error {
@apply bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-100;
.openapi-method-delete {
@apply bg-pomegranate-100 text-pomegranate-800 dark:bg-pomegranate-900 dark:text-pomegranate-100;
}
.openapi-method-head,
@@ -145,7 +140,7 @@
}
.openapi-column-preview-body {
@apply flex flex-col gap-4 sticky top-4 site-header:top-20 site-header-sections:top-32 page-api-block:xl:max-2xl:top-32 print-mode:static;
@apply flex flex-col gap-4 sticky top-4 site-header:top-20 site-header-sections:page-has-toc:top-32 page-api-block:xl:max-2xl:top-32 print-mode:static;
}
.openapi-column-preview pre {
@@ -162,7 +157,7 @@
}
.openapi-section-schemas > .openapi-section-body > .openapi-schema-root-description {
@apply px-2.5 pt-2.5 mt-0 !text-sm;
@apply px-2.5 pt-1 !text-sm;
}
.openapi-schema-properties {
@@ -229,19 +224,19 @@
}
.openapi-schema-required {
@apply text-warning-subtle text-[0.813rem] lowercase;
@apply text-warning-subtle text-[0.813rem];
}
.openapi-schema-optional {
@apply text-info-subtle text-[0.813rem] lowercase;
@apply text-info-subtle text-[0.813rem];
}
.openapi-schema-readonly {
@apply text-primary-subtle/9 text-[0.813rem] lowercase;
@apply text-primary-subtle/9 text-[0.813rem];
}
.openapi-schema-writeonly {
@apply text-success dark:text-success-subtle/9 text-[0.813rem] lowercase;
@apply text-success dark:text-success-subtle/9 text-[0.813rem];
}
.openapi-schema-type {
@@ -365,7 +360,7 @@
/* Responses */
.openapi-responses-header {
@apply py-2 max-w-full flex-1;
@apply py-2 border-b border-tint-subtle max-w-full flex-1;
}
.openapi-responses-header-content {
@@ -373,8 +368,7 @@
}
.openapi-response-tab-content {
@apply overflow-hidden max-w-full flex items-baseline;
@apply text-left text-pretty relative leading-[1.125rem] text-tint !font-normal truncate select-text;
@apply overflow-hidden max-w-full flex items-baseline gap-2;
}
.openapi-response-description.openapi-markdown {
@@ -389,6 +383,10 @@
@apply truncate max-w-full inline pr-1;
}
.openapi-response-statuscode {
@apply tabular-nums text-sm font-normal font-mono shrink-0;
}
.openapi-response-content-type {
@apply text-xs text-tint-8 ml-auto shrink-0;
}
@@ -418,23 +416,15 @@
/* Code Sample */
.openapi-codesample {
@apply border rounded-md straight-corners:rounded-none bg-tint border-tint-subtle;
@apply border rounded bg-tint border-tint-subtle;
}
.openapi-codesample-header {
@apply flex flex-row items-center;
}
.openapi-response-media-types-examples-footer-content {
@apply flex flex-row items-center gap-2.5;
}
.openapi-response-examples-header .openapi-select > button {
@apply max-w-full overflow-hidden shrink pl-0.5 py-0.5;
}
.openapi-response-examples-header .openapi-select > button .openapi-statuscode {
@apply h-full;
.openapi-codesample-header .openapi-select > button {
@apply border-none;
}
.openapi-codesample-header-content {
@@ -442,7 +432,7 @@
}
.openapi-codesample-header-content .openapi-path {
@apply flex items-center font-mono text-[0.813rem] gap-1 h-fit *:truncate overflow-x-auto min-w-0 max-w-full font-normal text-tint-strong;
@apply flex items-center font-mono *:text-[0.813rem] gap-2 h-fit *:truncate overflow-x-auto min-w-0 max-w-full font-normal text-tint-strong;
}
.openapi-codesample-header-content .openapi-path .openapi-path-variable {
@@ -473,7 +463,7 @@
}
.openapi-path .openapi-method {
@apply m-0 mt-0.5 items-center flex px-1;
@apply text-[0.813rem] m-0 mt-0.5 items-center flex px-1;
}
.openapi-path-title {
@@ -504,7 +494,7 @@
/* Panel */
.openapi-panel {
@apply border rounded-md straight-corners:rounded-none bg-tint border-tint-subtle;
@apply border rounded bg-tint border-tint-subtle;
}
.openapi-panel-heading {
@@ -526,41 +516,13 @@
}
/* Example */
.openapi-response-examples {
@apply border rounded-md straight-corners:rounded-none bg-tint border-tint-subtle;
}
.openapi-response-examples-header {
@apply flex flex-row items-center p-2.5;
}
.openapi-response-examples-header-content {
@apply max-w-full overflow-hidden truncate;
}
.openapi-response-examples-statuscode-title {
@apply flex items-center;
}
.openapi-response-examples-header .openapi-select > button,
.openapi-response-examples-header .openapi-markdown,
.openapi-response-examples-statuscode-title {
@apply text-[0.813rem] truncate text-tint font-normal;
}
.openapi-response-examples-panel,
.openapi-codesample-panel {
@apply flex-1 text-sm relative focus-visible:outline-none;
@apply before:w-full before:h-px before:absolute before:bg-tint-6 before:-top-px before:z-10;
}
.openapi-example-empty {
@apply relative text-tint bg-tint min-h-20 flex flex-col justify-center items-center;
}
/* Common Elements */
.openapi-select {
@apply w-auto max-w-full;
/* unstyled */
}
/* Prevent react-aria popover from setting overflow:auto on body */
@@ -569,32 +531,24 @@ body:has(.openapi-select-popover) {
}
.openapi-select > button {
@apply flex items-center font-normal cursor-pointer *:truncate gap-1.5 text-tint-strong max-w-32 rounded text-xs p-1.5 leading-none border border-tint-subtle bg-tint;
@apply flex items-center cursor-pointer gap-1.5 text-tint-strong max-w-60 rounded text-xs leading-6 px-1.5 truncate border border-tint-subtle bg-tint;
@apply hover:bg-tint-hover transition-all;
}
.openapi-select > button[data-focused="true"] {
@apply outline-primary -outline-offset-1 outline outline-1;
}
.openapi-select > button > span.react-aria-SelectValue {
@apply shrink truncate flex items-center;
@apply shrink truncate;
}
.openapi-select > button > span.react-aria-SelectValue span:not(.openapi-statuscode) {
@apply truncate;
.openapi-select > button > .gb-icon {
@apply shrink-0;
}
.openapi-select > button .openapi-markdown {
@apply *:leading-none;
}
.openapi-select > button .gb-icon {
@apply size-2.5 shrink-0;
.openapi-select > button svg {
@apply size-2.5;
}
.openapi-select-popover {
@apply min-w-32 z-10 max-w-[max(20rem,var(--trigger-width))] overflow-x-hidden max-h-52 overflow-y-auto p-1.5 border border-tint-subtle bg-tint-base backdrop-blur-xl rounded-md straight-corners:rounded-none;
@apply min-w-32 max-w-fit w-auto max-h-52 overflow-y-auto p-1.5 border border-tint-subtle bg-tint-base backdrop-blur-xl rounded-md;
@apply shadow-md shadow-tint-12/1 dark:shadow-tint-1/1;
}
@@ -607,17 +561,11 @@ body:has(.openapi-select-popover) {
}
.openapi-select-item {
@apply text-sm flex items-center cursor-pointer px-1.5 overflow-hidden py-1 *:truncate text-tint ring-0 border-none rounded !outline-none;
@apply text-sm cursor-pointer px-1.5 py-0.5 truncate text-tint ring-0 border-none rounded !outline-none;
@apply hover:bg-tint-hover theme-gradient:hover:bg-tint-12/1 hover:text-tint-strong contrast-more:hover:ring-1 contrast-more:hover:ring-inset contrast-more:hover:ring-current;
}
.openapi-select button .openapi-markdown,
.openapi-select-item .openapi-markdown {
@apply text-[0.813rem] *:truncate;
}
.openapi-select-item-selected,
.openapi-select-item-selected .openapi-markdown {
.openapi-select-item-selected {
@apply text-primary-subtle hover:text-primary hover:bg-primary-hover;
@apply theme-muted:hover:bg-primary-active theme-gradient:hover:bg-primary-active tint:font-semibold;
@apply contrast-more:text-primary contrast-more:hover:text-primary-strong contrast-more:font-semibold;
@@ -652,14 +600,6 @@ body:has(.openapi-select-popover) {
@apply text-tint;
}
.openapi-section-footer {
@apply flex flex-row items-center p-2.5 gap-2.5 text-sm text-tint-strong border-t border-tint-subtle;
}
.openapi-section-footer-content {
@apply text-sm text-tint-strong;
}
.openapi-section-toggle {
@apply text-tint-subtle contrast-more:text-tint-strong;
}
@@ -681,7 +621,6 @@ body:has(.openapi-select-popover) {
}
/* Tabs */
.openapi-panel-header,
.openapi-tabs-list {
@apply flex flex-row gap-1.5 py-1.5 px-2.5 w-full overflow-x-scroll;
scrollbar-width: none;
@@ -693,17 +632,18 @@ body:has(.openapi-select-popover) {
}
.openapi-tabs-tab[aria-selected="true"] {
@apply !text-primary-subtle after:absolute after:-bottom-[calc(0.375rem_+_1px)] after:z-20 after:left-0 after:w-full after:h-px after:bg-primary-solid after:transition-all;
@apply text-primary after:absolute after:-bottom-[calc(0.375rem_+_1px)] after:z-20 after:left-0 after:w-full after:h-px after:bg-primary-solid after:transition-all;
}
.openapi-tabs-panel {
.openapi-tabs-panel,
.openapi-codesample-panel {
@apply flex-1 text-sm relative focus-visible:outline-none;
@apply before:w-full before:h-px before:absolute before:bg-tint-6 before:-top-px before:z-10;
}
/* Disclosure group */
.openapi-disclosure-group {
@apply border-tint-subtle border-b border-x overflow-auto last:rounded-b-md straight-corners:last:rounded-none first:rounded-t-md straight-corners:first:rounded-none first:border-t relative;
@apply border-b border-tint-subtle relative;
}
.openapi-disclosure-group-header {
@@ -711,11 +651,16 @@ body:has(.openapi-select-popover) {
}
.openapi-disclosure-group-trigger {
@apply flex items-baseline transition-all hover:bg-tint-subtle relative flex-1 gap-2.5 p-3 truncate -outline-offset-1;
@apply flex items-baseline relative flex-1 gap-2.5 py-2 truncate -outline-offset-1;
}
.openapi-disclosure-group-trigger:disabled {
@apply cursor-default hover:bg-inherit;
@apply cursor-default;
}
.openapi-disclosure-group:only-child,
.openapi-disclosure-group:last-child {
@apply border-b-0;
}
.openapi-disclosure-group-trigger:disabled .openapi-disclosure-group-icon {
@@ -735,42 +680,28 @@ body:has(.openapi-select-popover) {
}
.openapi-disclosure-group-panel {
@apply p-3 pt-1 transition-all;
@apply pb-2.5;
}
.openapi-disclosure-group-trigger[aria-expanded="true"] > .openapi-disclosure-group-icon > svg {
@apply rotate-90;
}
.openapi-disclosure-group:hover .openapi-disclosure-group-mediatype,
.openapi-disclosure-group-mediatype:has(> .openapi-select[data-open="true"]) {
@apply opacity-11 visible flex;
.openapi-disclosure-group:hover .openapi-disclosure-group-mediatype {
@apply opacity-11 flex;
}
.openapi-disclosure-group-mediatype {
@apply opacity-0 invisible text-xs transition-opacity duration-300 shrink-0 absolute right-2.5 top-2.5;
@apply opacity-0 hidden text-xs transition-all duration-200 shrink-0 absolute right-0 top-2.5;
}
.openapi-disclosure-group-mediatype > span {
@apply px-1 bg-tint-6 text-tint-12 rounded-full straight-corners:rounded-md;
@apply px-1 bg-tint-6 text-tint-12 rounded-full;
}
/* Disclosure */
.openapi-schemas-disclosure > .openapi-disclosure-trigger {
@apply flex items-center font-mono !w-full transition-all text-tint-strong !text-sm hover:bg-tint-subtle relative flex-1 gap-2.5 p-3 truncate -outline-offset-1;
}
.openapi-schemas-disclosure > .openapi-disclosure-trigger,
.openapi-schemas-disclosure .openapi-disclosure-panel {
@apply straight-corners:!rounded-none;
}
.openapi-schemas-disclosure > .openapi-disclosure-trigger[aria-expanded="true"] > svg {
@apply rotate-90;
}
.openapi-disclosure-trigger {
@apply transition-all truncate duration-300 max-w-full hover:text-tint-strong rounded-2xl straight-corners:rounded border border-tint-subtle px-2.5 py-1 text-[0.813rem] text-tint flex flex-row items-center gap-1.5 -outline-offset-1;
@apply transition-all truncate duration-300 max-w-full hover:text-tint-strong rounded-2xl border border-tint-subtle px-2.5 py-1 text-[0.813rem] text-tint flex flex-row items-center gap-1.5 -outline-offset-1;
}
.openapi-disclosure-trigger span {
@@ -786,7 +717,7 @@ body:has(.openapi-select-popover) {
}
.openapi-disclosure-trigger[aria-expanded="true"] {
@apply w-full rounded-lg border-b rounded-b-none straight-corners:rounded-b-none;
@apply w-full rounded-lg border-b rounded-b-none;
}
.openapi-disclosure-trigger[aria-expanded="false"] {
@@ -794,7 +725,7 @@ body:has(.openapi-select-popover) {
}
.openapi-disclosure-panel[aria-hidden="false"] {
@apply border-b border-x border-tint-subtle rounded-b-lg straight-corners:rounded-b;
@apply border-b border-x border-tint-subtle rounded-b-lg;
}
.openapi-disclosure-panel .openapi-schema {
@@ -814,13 +745,17 @@ body:has(.openapi-select-popover) {
@apply space-y-2.5;
}
.openapi-section-schemas {
@apply border border-tint-subtle rounded-lg;
}
.openapi-section-schemas > .openapi-section-body > .openapi-schema-properties > .openapi-schema,
.openapi-section-schemas > .openapi-section-body > .openapi-schema-root {
@apply p-2.5;
}
.openapi-tooltip {
@apply flex items-center gap-1 bg-tint-base border border-tint-subtle text-tint-strong rounded-md straight-corners:rounded-none font-medium px-1.5 py-0.5 shadow-sm text-[13px];
@apply flex items-center gap-1 bg-tint-base border border-tint-subtle text-tint-strong rounded-md font-medium px-1.5 py-0.5 shadow-sm text-[13px];
}
.openapi-tooltip svg {
@@ -215,7 +215,7 @@ export function Header(props: { context: GitBookSiteContext; withTopHeader?: boo
/>
</div>
)}
{sections && sections.list.length > 1 && (
{sections && (
<SiteSectionTabs
sections={encodeClientSiteSections(context, sections)}
/>
@@ -1,51 +1,65 @@
'use client';
import { Icon } from '@gitbook/icons';
import { usePathname } from 'next/navigation';
import { useEffect, useRef } from 'react';
import { useRef } from 'react';
import { tString, useLanguage } from '@/intl/client';
import { tcls } from '@/lib/tailwind';
import { usePathnameChange } from '../hooks';
import { useScrollListener } from '../hooks/useScrollListener';
const globalClassName = 'navigation-open';
const SCROLL_DISTANCE = 320;
/**
* Detect if the user has scrolled in the page, meaning the TOC is not visible.
*/
function useHasScrolledRef() {
const hasScrollRef = useRef(false);
const windowRef = useRef(typeof window === 'undefined' ? null : window);
useScrollListener(() => {
hasScrollRef.current = window.scrollY >= 320;
}, windowRef);
return hasScrollRef
}
/**
* Button to show/hide the table of content on mobile.
*/
export function HeaderMobileMenu(props: Partial<React.ButtonHTMLAttributes<HTMLButtonElement>>) {
const language = useLanguage();
const hasScrollRef = useHasScrolledRef();
const pathname = usePathname();
const hasScrollRef = useRef(false);
// Close the navigation when navigating to a page
usePathnameChange(() => {
document.body.classList.remove(globalClassName);
});
const toggleNavigation = () => {
if (!hasScrollRef.current && document.body.classList.contains(globalClassName)) {
document.body.classList.remove(globalClassName);
} else {
const handleNavigationClick = () => {
// If already opened
if (document.body.classList.contains(globalClassName)) {
// If the user has scrolled down, we need to scroll back to the top.
if (hasScrollRef.current) {
window.scrollTo(0, 0);
}
// Else we close the table of contents
else {
document.body.classList.remove(globalClassName);
}
}
// If not opened, we open it an scroll to the top
else {
document.body.classList.add(globalClassName);
window.scrollTo(0, 0);
}
};
const windowRef = useRef(typeof window === 'undefined' ? null : window);
useScrollListener(() => {
hasScrollRef.current = window.scrollY >= SCROLL_DISTANCE;
}, windowRef);
// Close the navigation when navigating to a page
useEffect(() => {
document.body.classList.remove(globalClassName);
}, [pathname]);
};
return (
<button
{...props}
aria-label={tString(language, 'table_of_contents_button_label')}
onClick={toggleNavigation}
onClick={handleNavigationClick}
className={tcls(
'flex flex-row items-center rounded straight-corners:rounded-sm px-2 py-1',
props.className
@@ -1,18 +1,19 @@
'use client';
import { motion } from 'framer-motion';
import React from 'react';
import React, { useRef } from 'react';
import { useScrollActiveId } from '@/components/hooks';
// import { useScrollActiveId } from '@/components/hooks';
import type { DocumentSection } from '@/lib/document-sections';
import { tcls } from '@/lib/tailwind';
import { HEADER_HEIGHT_DESKTOP } from '../layout';
import { usePageActiveSections } from '../hooks/usePageActiveSections';
// import { HEADER_HEIGHT_DESKTOP } from '../layout';
import { AsideSectionHighlight } from './AsideSectionHighlight';
/**
* The threshold at which we consider a section as intersecting the viewport.
*/
const SECTION_INTERSECTING_THRESHOLD = 0.9;
// const SECTION_INTERSECTING_THRESHOLD = 0.9;
const springCurve = {
type: 'spring',
@@ -30,10 +31,13 @@ export function ScrollSectionsList(props: { sections: DocumentSection[] }) {
});
}, [sections]);
const activeId = useScrollActiveId(ids, {
rootMargin: `-${HEADER_HEIGHT_DESKTOP}px 0px -40% 0px`,
threshold: SECTION_INTERSECTING_THRESHOLD,
});
const windowRef = useRef(typeof window === 'undefined' ? null : window);
const activeIds = usePageActiveSections(ids, windowRef);
// const activeId = useScrollActiveId(ids, {
// rootMargin: `-${HEADER_HEIGHT_DESKTOP}px 0px -40% 0px`,
// threshold: SECTION_INTERSECTING_THRESHOLD,
// });
return (
<ul className={tcls('sidebar-list-line:border-l', 'border-tint-subtle')}>
@@ -49,7 +53,7 @@ export function ScrollSectionsList(props: { sections: DocumentSection[] }) {
section.depth > 1 && ['ml-3', 'my-0', 'sidebar-list-line:ml-0']
)}
>
{activeId === section.id ? (
{activeIds.includes(section.id) ? (
<AsideSectionHighlight
transition={springCurve}
className={tcls(
@@ -102,7 +106,7 @@ export function ScrollSectionsList(props: { sections: DocumentSection[] }) {
'sidebar-list-default:border-tint',
],
activeId === section.id && [
activeIds.includes(section.id) && [
'text-primary-subtle',
'hover:text-primary',
'contrast-more:text-primary',
@@ -123,7 +127,7 @@ export function ScrollSectionsList(props: { sections: DocumentSection[] }) {
>
{section.tag ? (
<span
className={`-mt-0.5 openapi-method !text-xs openapi-method-${section.tag.toLowerCase()}`}
className={`-mt-0.5 openapi-method openapi-method-${section.tag.toLowerCase()}`}
>
{section.tag}
</span>
@@ -4,3 +4,5 @@ export * from './useHash';
export * from './useIsMounted';
export * from './useToggleAnimation';
export * from './useCurrentPagePath';
export * from './usePrevious';
export * from './usePathnameChange';
@@ -1,24 +1,26 @@
'use client';
import { useParams } from 'next/navigation';
import React from 'react';
import { useCallback, useEffect, useState } from 'react';
function getHash(): string | null {
if (typeof window === 'undefined') {
return null;
}
return window.location.hash.slice(1);
}
/**
* Hook to get the current hash from the URL.
* @see https://github.com/vercel/next.js/discussions/49465
*/
// How do I get the pathname with hash.
// source: https://github.com/vercel/next.js/discussions/49465
export function useHash() {
const params = useParams();
const [hash, setHash] = React.useState<string | null>(getHash);
React.useEffect(() => {
setHash(getHash());
}, [params]);
const getCurrentHash = useCallback(
() => (typeof window !== 'undefined' ? window.location.hash.replace(/^#!?/, '') : ''),
[]
);
const [hash, setHash] = useState<string>(getCurrentHash());
useEffect(() => {
const handleHashChange = () => {
setHash(getCurrentHash());
};
window.addEventListener('hashchange', handleHashChange);
return () => {
window.removeEventListener('hashchange', handleHashChange);
};
}, [getCurrentHash]);
return hash;
}
@@ -0,0 +1,128 @@
'use client';
import * as React from 'react';
import { useEventCallback } from 'usehooks-ts';
import { useScrollListener } from './useScrollListener';
/**
* Hook that returns the active section key of the page.
* The active section is the section that is currently visible on the viewport.
*
* Here's a diagram: https://user-images.githubusercontent.com/8937991/206921462-14e5b8b7-5a19-4c8b-912d-ed1f17710bc4.png
*/
export function usePageActiveSections(
ids: string[],
scrollRef: React.RefObject<HTMLElement | Window | null>
): string[] {
const [activeSections, setActiveSections] = React.useState<string[]>(noActiveSections);
const update = useEventCallback(() => {
const element = scrollRef.current;
if (element) {
const rect =
element instanceof Window
? new DOMRect(0, 0, window.innerWidth, window.innerHeight)
: element.getBoundingClientRect();
const activeSections = findActiveSectionIds({
ids,
containerRect: rect,
});
setActiveSections((previous) => {
if (activeSections.length !== previous.length) {
return activeSections;
}
if (activeSections.some((key, i) => key !== previous[i])) {
return activeSections;
}
return previous;
});
}
});
useScrollListener(update, scrollRef);
React.useEffect(() => {
update();
}, [update]);
return activeSections;
}
const noActiveSections: string[] = [];
// We consider anything in [-MARGINpx; +MARGINpx] to be at the boundary
const MARGIN = 24;
/**
* findActiveSectionIndex finds the section that is in the window's view
* it uses binary-search to efficiently (minimize DOM queries)
* find the DOM node whose .top is closest to but below `0` (the window's top)
*/
function findActiveSectionIndex(ids: string[], scrollTop: number): number {
const N = ids.length;
// Number of max iterations to find the element (so our search is bounded)
// in theory, at most floor(log(n)+1) steps to find the specific element
const maxIterations = Math.floor(Math.log2(N) + 1);
// Our binary search variables
let start = 0;
let end = N - 1;
let mid = Math.ceil((start + end) / 2);
let idx = -1;
let y: number | null = null;
for (let i = 0; i < maxIterations; i++) {
const id = ids[mid];
// Double check that we have a section, else exit
if (!id) {
return -1;
}
const element = document.getElementById(id);
const sy = element ? element.getBoundingClientRect().top - scrollTop : null;
if (sy === null || sy > MARGIN) {
// Go left
end = mid;
mid = Math.floor((start + end) / 2);
} else if (sy >= -MARGIN && sy <= MARGIN) {
// Hit
return mid;
} else if (sy < MARGIN) {
// If bigger than previous (but still smaller than 0)
// that becomes our new active index
if (y === null || sy > y) {
y = sy;
idx = mid;
}
// Go right
start = mid;
mid = Math.ceil((start + end) / 2);
}
}
return idx;
}
/**
* Find the active sections in the viewport.
*/
function findActiveSectionIds(input: {
ids: string[];
containerRect: DOMRect;
}): string[] {
const activeIndex = findActiveSectionIndex(input.ids, input.containerRect.top);
// Check if the next sections are visible, stop when they are not.
const activeSections: string[] = input.ids[activeIndex] ? [input.ids[activeIndex]] : [];
for (let i = activeIndex + 1; i < input.ids.length; i++) {
const id = input.ids[i];
const element = document.getElementById(id);
if (!element) {
continue;
}
const rect = element.getBoundingClientRect();
if (rect.bottom + MARGIN > input.containerRect.bottom) {
break;
}
activeSections.push(id);
}
return activeSections;
}
@@ -0,0 +1,19 @@
'use client';
import { usePathname } from 'next/navigation';
import * as React from 'react';
import { useEventCallback } from 'usehooks-ts';
import { usePrevious } from './usePrevious';
/**
* Returns the value of the previous render.
*/
export function usePathnameChange(callback: () => void): void {
const pathname = usePathname();
const previousPathname = usePrevious(pathname);
const eventCallback = useEventCallback(callback);
React.useEffect(() => {
if (previousPathname !== undefined && pathname !== previousPathname) {
eventCallback();
}
}, [pathname, previousPathname, eventCallback]);
}
@@ -0,0 +1,13 @@
'use client';
import * as React from 'react';
/**
* Returns the value of the previous render.
*/
export function usePrevious<T>(value: T): T | undefined {
const ref = React.useRef<T | undefined>(undefined);
React.useLayoutEffect(() => {
ref.current = value;
});
return ref.current;
}
@@ -17,7 +17,7 @@ import { type RefObject, useEffect, useLayoutEffect, useRef } from 'react';
*/
export function useScrollListener(
listener: (event: Event) => void,
elementRef: RefObject<HTMLElement | Window> | null
elementRef: RefObject<HTMLElement | Window | null> | null
) {
const listenerRef = useRef(listener);
useLayoutEffect(() => {
@@ -4,6 +4,7 @@ import { usePathname } from 'next/navigation';
import React from 'react';
import { useHash } from './useHash';
import { usePrevious } from './usePrevious';
/**
* Scroll the page to an anchor point or
@@ -13,28 +14,29 @@ import { useHash } from './useHash';
export function useScrollPage(props: { scrollMarginTop?: number }) {
const hash = useHash();
const pathname = usePathname();
const prevPathname = usePrevious(pathname);
const scrollMarginTopRef = React.useRef(props.scrollMarginTop);
React.useLayoutEffect(() => {
if (hash) {
const element = document.getElementById(hash);
if (element) {
if (props.scrollMarginTop) {
element.style.scrollMarginTop = `${props.scrollMarginTop}px`;
const originalScrollMarginTop = element.style.scrollMarginTop;
if (scrollMarginTopRef.current) {
element.style.scrollMarginTop = `${scrollMarginTopRef.current}px`;
}
element.scrollIntoView({
block: 'start',
behavior: 'smooth',
});
element.scrollIntoView({ block: 'start', behavior: 'smooth' });
return () => {
const element = document.getElementById(hash);
if (element) {
element.style.scrollMarginTop = originalScrollMarginTop;
}
};
}
} else {
return;
}
if (prevPathname && pathname !== prevPathname) {
window.scrollTo(0, 0);
}
return () => {
if (hash) {
const element = document.getElementById(hash);
if (element) {
element.style.scrollMarginTop = '';
}
}
};
}, [hash, pathname, props.scrollMarginTop]);
}, [hash, pathname, prevPathname]);
}
+3 -12
View File
@@ -4,26 +4,17 @@ import { type TranslationLanguage, languages } from './translations';
export * from './translate';
export const DEFAULT_LOCALE = 'en';
/**
* Get the locale of the customization.
*/
export function getCustomizationLocale(customization: SiteCustomizationSettings): string {
return customization.internationalization.locale;
}
/**
* Create the translation context for a space to use in the server components.
*/
export function getSpaceLanguage(customization: SiteCustomizationSettings): TranslationLanguage {
const fallback = languages[DEFAULT_LOCALE];
const fallback = languages.en;
const locale = getCustomizationLocale(customization);
const { locale } = customization.internationalization;
let language = fallback;
// @ts-ignore
if (locale !== DEFAULT_LOCALE && languages[locale]) {
if (locale !== 'en' && languages[locale]) {
// @ts-ignore
language = languages[locale];
}
+1 -1
View File
@@ -36,7 +36,7 @@ export const en = {
table_of_contents_button_label: 'Open table of contents',
cookies_title: 'Cookies',
cookies_prompt:
'This site uses cookies to deliver its service and to analyze traffic. By browsing this site, you accept the ${1}.',
'This site uses cookies to deliver its service and to analyse traffic. By browsing this site, you accept the ${1}.',
cookies_prompt_privacy: 'privacy policy',
cookies_accept: 'Accept',
cookies_reject: 'Reject',
+1 -2
View File
@@ -5,7 +5,6 @@ import { type CacheFunctionOptions, cache, noCacheFetchOptions } from '@/lib/cac
import type {
AnyOpenAPIOperationsBlock,
OpenAPISchemasBlock,
OpenAPIWebhookBlock,
ResolveOpenAPIBlockArgs,
} from '@/lib/openapi/types';
import { getCloudflareRequestGlobal } from '@v2/lib/data/cloudflare';
@@ -17,7 +16,7 @@ import { isV2 } from '../v2';
import { enrichFilesystem } from './enrich';
import type { FetchOpenAPIFilesystemResult } from './types';
type AnyOpenAPIBlock = AnyOpenAPIOperationsBlock | OpenAPISchemasBlock | OpenAPIWebhookBlock;
type AnyOpenAPIBlock = AnyOpenAPIOperationsBlock | OpenAPISchemasBlock;
/**
* Fetch OpenAPI block.
@@ -1,61 +0,0 @@
import { fetchOpenAPIFilesystem } from '@/lib/openapi/fetch';
import { OpenAPIParseError } from '@gitbook/openapi-parser';
import { type OpenAPIWebhookData, resolveOpenAPIWebhook } from '@gitbook/react-openapi';
import type {
OpenAPIWebhookBlock,
ResolveOpenAPIBlockArgs,
ResolveOpenAPIBlockResult,
} from './types';
type ResolveOpenAPIWebhookBlockResult = ResolveOpenAPIBlockResult<OpenAPIWebhookData>;
const weakmap = new WeakMap<OpenAPIWebhookBlock, Promise<ResolveOpenAPIWebhookBlockResult>>();
/**
* Cache the result of resolving an OpenAPI block.
* It is important because the resolve is called in sections and in the block itself.
*/
export function resolveOpenAPIWebhookBlock(
args: ResolveOpenAPIBlockArgs<OpenAPIWebhookBlock>
): Promise<ResolveOpenAPIWebhookBlockResult> {
if (weakmap.has(args.block)) {
return weakmap.get(args.block)!;
}
const result = baseResolveOpenAPIWebhookBlock(args);
weakmap.set(args.block, result);
return result;
}
/**
* Resolve OpenAPI webhook block.
*/
async function baseResolveOpenAPIWebhookBlock(
args: ResolveOpenAPIBlockArgs<OpenAPIWebhookBlock>
): Promise<ResolveOpenAPIWebhookBlockResult> {
const { context, block } = args;
if (!block.data.name || !block.data.method) {
return { data: null, specUrl: null };
}
try {
const { filesystem, specUrl } = await fetchOpenAPIFilesystem({ block, context });
if (!filesystem) {
return { data: null, specUrl: null };
}
const data = await resolveOpenAPIWebhook(filesystem, {
name: block.data.name,
method: block.data.method,
});
return { data, specUrl };
} catch (error) {
if (error instanceof OpenAPIParseError) {
return { error };
}
throw error;
}
}
@@ -2,7 +2,6 @@ import type {
DocumentBlockOpenAPI,
DocumentBlockOpenAPIOperation,
DocumentBlockOpenAPISchemas,
DocumentBlockOpenAPIWebhook,
} from '@gitbook/api';
import type { Filesystem, OpenAPIParseError, OpenAPIV3xDocument } from '@gitbook/openapi-parser';
import type { GitBookAnyContext } from '@v2/lib/context';
@@ -17,11 +16,6 @@ export type AnyOpenAPIOperationsBlock = DocumentBlockOpenAPI | DocumentBlockOpen
*/
export type OpenAPISchemasBlock = DocumentBlockOpenAPISchemas;
/**
* Type for OpenAPI Webhook block
*/
export type OpenAPIWebhookBlock = DocumentBlockOpenAPIWebhook;
/**
* Arguments for resolving OpenAPI block.
*/
-21
View File
@@ -1,26 +1,5 @@
# @gitbook/react-openapi
## 1.2.1
### Patch Changes
- ebc39e9: Missing select icon
- b6b09d4: Fix OpenAPI responses select placement and icon
## 1.2.0
### Minor Changes
- d67699a: Add OpenAPI Webhook block
### Patch Changes
- eeb977f: Fix Python code example for JSON payload
- 3363a18: Merge simple alternatives
- 8ed1bda: Translate OpenAPI blocks
- 7588cfe: Improve OpenAPIResponses examples and schemas
- ad1dc0b: Bump scalar packages
## 1.1.10
### Patch Changes
+3 -3
View File
@@ -8,12 +8,12 @@
"default": "./dist/index.js"
}
},
"version": "1.2.1",
"version": "1.1.10",
"sideEffects": false,
"dependencies": {
"@gitbook/openapi-parser": "workspace:*",
"@scalar/api-client-react": "^1.2.19",
"@scalar/oas-utils": "^0.2.130",
"@scalar/api-client-react": "^1.2.5",
"@scalar/oas-utils": "^0.2.120",
"clsx": "^2.1.1",
"flatted": "^3.2.9",
"json-xml-parse": "^1.3.0",
@@ -1,10 +1,10 @@
'use client';
import clsx from 'clsx';
import { useRef } from 'react';
import { useRef, useState } from 'react';
import { mergeProps, useButton, useDisclosure, useFocusRing } from 'react-aria';
import { useDisclosureState } from 'react-stately';
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
import { Section, SectionBody, SectionHeader, SectionHeaderContent } from './StaticSection';
interface InteractiveSectionTab {
@@ -35,10 +35,6 @@ export function InteractiveSection(props: {
header?: React.ReactNode;
/** Children to display within the container */
overlay?: React.ReactNode;
/** State key to use with a store */
stateKey?: string;
/** Icon for the tabs select */
selectIcon?: React.ReactNode;
}) {
const {
id,
@@ -50,9 +46,12 @@ export function InteractiveSection(props: {
header,
overlay,
toggleIcon = '▶',
selectIcon,
stateKey = 'interactive-section',
} = props;
const [selectedTabKey, setSelectedTab] = useState(defaultTab);
const selectedTab: InteractiveSectionTab | undefined =
tabs.find((tab) => tab.key === selectedTabKey) ?? tabs[0];
const state = useDisclosureState({
defaultExpanded: defaultOpened,
});
@@ -61,10 +60,6 @@ export function InteractiveSection(props: {
const { buttonProps: triggerProps, panelProps } = useDisclosure({}, state, panelRef);
const { buttonProps } = useButton(triggerProps, triggerRef);
const { isFocusVisible, focusProps } = useFocusRing();
const store = useSelectState(stateKey, defaultTab);
const selectedTab: InteractiveSectionTab | undefined =
tabs.find((tab) => tab.key === store.key) ?? tabs[0];
return (
<Section
@@ -102,7 +97,6 @@ export function InteractiveSection(props: {
) : null}
{header}
</SectionHeaderContent>
{/* biome-ignore lint/a11y/useKeyWithClickEvents: we prevent default here */}
<div
className={clsx(
'openapi-section-header-controls',
@@ -114,12 +108,16 @@ export function InteractiveSection(props: {
>
{tabs.length > 1 ? (
<OpenAPISelect
stateKey={stateKey}
className={clsx(
'openapi-section-select',
`${className}-tabs-select`
)}
items={tabs}
onSelectionChange={() => {
selectedKey={selectedTab?.key ?? ''}
onSelectionChange={(key) => {
setSelectedTab(String(key));
state.expand();
}}
icon={selectIcon}
placement="bottom end"
>
{tabs.map((tab) => (
@@ -6,10 +6,9 @@ import {
import { OpenAPICodeSampleBody } from './OpenAPICodeSampleSelector';
import { ScalarApiButton } from './ScalarApiButton';
import { type CodeSampleGenerator, codeSampleGenerators } from './code-samples';
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
import { generateMediaTypeExamples, generateSchemaExample } from './generateSchemaExample';
import { stringifyOpenAPI } from './stringifyOpenAPI';
import type { OpenAPIOperationData } from './types';
import type { OpenAPIContext, OpenAPIOperationData } from './types';
import { getDefaultServerURL } from './util/server';
import { checkIsReference } from './utils';
@@ -23,7 +22,7 @@ export function OpenAPICodeSample(props: {
data: OpenAPIOperationData;
context: OpenAPIContext;
}) {
const { data, context } = props;
const { data } = props;
// If code samples are disabled at operation level, we don't display the code samples.
if (data.operation['x-codeSamples'] === false) {
@@ -44,14 +43,7 @@ export function OpenAPICodeSample(props: {
return null;
}
return (
<OpenAPICodeSampleBody
context={getOpenAPIClientContext(context)}
data={data}
items={samples}
selectIcon={context.icons.chevronDown}
/>
);
return <OpenAPICodeSampleBody data={data} items={samples} />;
}
/**
@@ -154,7 +146,6 @@ function generateCodeSamples(props: {
method={data.method}
path={data.path}
renderers={renderers}
blockKey={context.blockKey}
/>
),
footer: (
@@ -215,20 +206,11 @@ function OpenAPICodeSampleFooter(props: {
method={data.method}
path={data.path}
renderers={renderers}
selectIcon={context.icons.chevronDown}
blockKey={context.blockKey}
/>
) : (
<span />
)}
{!hideTryItPanel && (
<ScalarApiButton
context={getOpenAPIClientContext(context)}
method={method}
path={path}
specUrl={specUrl}
/>
)}
{!hideTryItPanel && <ScalarApiButton method={method} path={path} specUrl={specUrl} />}
</div>
);
}
@@ -1,43 +1,70 @@
'use client';
import clsx from 'clsx';
import { useCallback } from 'react';
import { useStore } from 'zustand';
import type { MediaTypeRenderer } from './OpenAPICodeSample';
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
import { createStateKey } from './utils';
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
type MediaTypeState = {
mediaType: string;
setMediaType: (mediaType: string) => void;
};
function useMediaTypeState(
data: { method: string; path: string },
defaultKey: string
): MediaTypeState {
const { method, path } = data;
const store = useStore(getOrCreateStoreByKey(`media-type-${method}-${path}`, defaultKey));
if (typeof store.key !== 'string') {
throw new Error('Media type key is not a string');
}
return {
mediaType: store.key,
setMediaType: useCallback((index: string) => store.setKey(index), [store.setKey]),
};
}
function useMediaTypeSampleIndexState(data: { method: string; path: string }, mediaType: string) {
const { method, path } = data;
const store = useStore(
getOrCreateStoreByKey(`media-type-sample-${mediaType}-${method}-${path}`, 0)
);
if (typeof store.key !== 'number') {
throw new Error('Example key is not a number');
}
return {
index: store.key,
setIndex: useCallback((index: number) => store.setKey(index), [store.setKey]),
};
}
export function OpenAPIMediaTypeExamplesSelector(props: {
method: string;
path: string;
renderers: MediaTypeRenderer[];
selectIcon?: React.ReactNode;
blockKey?: string;
}) {
const { method, path, renderers, selectIcon, blockKey } = props;
const { method, path, renderers } = props;
if (!renderers[0]) {
throw new Error('No renderers provided');
}
const stateKey = createStateKey('request-body-media-type', blockKey);
const state = useSelectState(stateKey, renderers[0].mediaType);
const selected = renderers.find((r) => r.mediaType === state.key) || renderers[0];
const state = useMediaTypeState({ method, path }, renderers[0].mediaType);
const selected = renderers.find((r) => r.mediaType === state.mediaType) || renderers[0];
return (
<div className="openapi-codesample-selectors">
<MediaTypeSelector selectIcon={selectIcon} stateKey={stateKey} renderers={renderers} />
<ExamplesSelector
selectIcon={selectIcon}
method={method}
path={path}
renderer={selected}
/>
<MediaTypeSelector state={state} renderers={renderers} />
<ExamplesSelector method={method} path={path} renderer={selected} />
</div>
);
}
function MediaTypeSelector(props: {
stateKey: string;
state: MediaTypeState;
renderers: MediaTypeRenderer[];
selectIcon?: React.ReactNode;
}) {
const { renderers, stateKey, selectIcon } = props;
const { renderers, state } = props;
if (renderers.length < 2) {
return null;
@@ -51,12 +78,12 @@ function MediaTypeSelector(props: {
return (
<OpenAPISelect
className={clsx('openapi-select')}
selectedKey={state.mediaType}
items={renderers.map((renderer) => ({
key: renderer.mediaType,
label: renderer.mediaType,
}))}
icon={selectIcon}
stateKey={stateKey}
onSelectionChange={(e) => state.setMediaType(String(e))}
placement="bottom start"
>
{items.map((item) => (
@@ -72,9 +99,9 @@ function ExamplesSelector(props: {
method: string;
path: string;
renderer: MediaTypeRenderer;
selectIcon?: React.ReactNode;
}) {
const { method, path, renderer, selectIcon } = props;
const { method, path, renderer } = props;
const state = useMediaTypeSampleIndexState({ method, path }, renderer.mediaType);
if (renderer.examples.length < 2) {
return null;
}
@@ -87,8 +114,8 @@ function ExamplesSelector(props: {
return (
<OpenAPISelect
items={items}
icon={selectIcon}
stateKey={`media-type-sample-${renderer.mediaType}-${method}-${path}`}
selectedKey={state.index}
onSelectionChange={(e) => state.setIndex(Number(e))}
placement="bottom start"
>
{items.map((item) => (
@@ -104,18 +131,14 @@ export function OpenAPIMediaTypeExamplesBody(props: {
method: string;
path: string;
renderers: MediaTypeRenderer[];
blockKey?: string;
}) {
const { renderers, method, path, blockKey } = props;
const { renderers, method, path } = props;
if (!renderers[0]) {
throw new Error('No renderers provided');
}
const mediaTypeState = useSelectState(
createStateKey('request-body-media-type', blockKey),
renderers[0].mediaType
);
const selected = renderers.find((r) => r.mediaType === mediaTypeState.key) ?? renderers[0];
const mediaTypeState = useMediaTypeState({ method, path }, renderers[0].mediaType);
const selected =
renderers.find((r) => r.mediaType === mediaTypeState.mediaType) ?? renderers[0];
if (selected.examples.length === 0) {
return selected.element;
}
@@ -124,13 +147,10 @@ export function OpenAPIMediaTypeExamplesBody(props: {
function ExamplesBody(props: { method: string; path: string; renderer: MediaTypeRenderer }) {
const { method, path, renderer } = props;
const exampleState = useSelectState(
`media-type-sample-${renderer.mediaType}-${method}-${path}`,
renderer.mediaType
);
const example = renderer.examples[Number(exampleState.key)] ?? renderer.examples[0];
const exampleState = useMediaTypeSampleIndexState({ method, path }, renderer.mediaType);
const example = renderer.examples[exampleState.index] ?? renderer.examples[0];
if (!example) {
throw new Error(`No example found for key ${exampleState.key}`);
throw new Error(`No example found for index ${exampleState.index}`);
}
return example.element;
}
@@ -6,7 +6,6 @@ import { useStore } from 'zustand';
import { OpenAPIPath } from './OpenAPIPath';
import { OpenAPISelect, OpenAPISelectItem } from './OpenAPISelect';
import { StaticSection } from './StaticSection';
import type { OpenAPIClientContext } from './context';
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
import type { OpenAPIOperationData } from './types';
@@ -23,22 +22,25 @@ type CodeSampleItem = OpenAPISelectItem & {
footer?: React.ReactNode;
};
function OpenAPICodeSampleHeader(props: {
export function OpenAPICodeSampleHeader(props: {
items: CodeSampleItem[];
data: OpenAPIOperationData;
selectIcon?: React.ReactNode;
context: OpenAPIClientContext;
}) {
const { data, items, selectIcon, context } = props;
const { data, items } = props;
const state = useCodeSampleState(items[0]?.key ?? '');
const selected = items.find((item) => item.key === state.key) || items[0];
return (
<>
<OpenAPIPath context={context} canCopy={false} withServer={false} data={data} />
<OpenAPIPath canCopy={false} withServer={false} data={data} />
{items.length > 1 ? (
<OpenAPISelect
icon={selectIcon}
selectedKey={selected?.key}
onSelectionChange={(key) => {
state.setKey(key);
}}
items={items}
stateKey="codesample"
placement="bottom end"
>
{items.map((item) => (
@@ -57,10 +59,8 @@ function OpenAPICodeSampleHeader(props: {
export function OpenAPICodeSampleBody(props: {
items: CodeSampleItem[];
data: OpenAPIOperationData;
selectIcon?: React.ReactNode;
context: OpenAPIClientContext;
}) {
const { items, data, selectIcon, context } = props;
const { items, data } = props;
if (!items[0]) {
throw new Error('No items provided');
}
@@ -75,14 +75,7 @@ export function OpenAPICodeSampleBody(props: {
return (
<StaticSection
header={
<OpenAPICodeSampleHeader
context={context}
selectIcon={selectIcon}
data={data}
items={items}
/>
}
header={<OpenAPICodeSampleHeader data={data} items={items} />}
className="openapi-codesample"
>
<div id={selected.key as string} className="openapi-codesample-panel">
@@ -2,14 +2,11 @@
import { useState } from 'react';
import { Button, type ButtonProps, Tooltip, TooltipTrigger } from 'react-aria-components';
import type { OpenAPIClientContext } from './context';
import { t } from './translate';
export function OpenAPICopyButton(
props: ButtonProps & {
value: string;
children: React.ReactNode;
context: OpenAPIClientContext;
label?: string;
/**
* Whether to show a tooltip.
@@ -18,7 +15,7 @@ export function OpenAPICopyButton(
withTooltip?: boolean;
}
) {
const { value, label, children, onPress, className, context, withTooltip = true } = props;
const { value, label, children, onPress, className, withTooltip = true } = props;
const [copied, setCopied] = useState(false);
const [isOpen, setIsOpen] = useState(false);
@@ -63,9 +60,7 @@ export function OpenAPICopyButton(
offset={4}
className="openapi-tooltip"
>
{copied
? t(context.translation, 'copied')
: label || t(context.translation, 'copy_to_clipboard')}
{copied ? 'Copied' : label || 'Copy to clipboard'}
</Tooltip>
</TooltipTrigger>
);
@@ -1,39 +1,41 @@
'use client';
import clsx from 'clsx';
import type React from 'react';
import { useState } from 'react';
import { Button, Disclosure, DisclosurePanel } from 'react-aria-components';
import { Button, Disclosure, DisclosurePanel, Heading } from 'react-aria-components';
import type { OpenAPIClientContext } from './types';
/**
* Display an interactive OpenAPI disclosure.
*/
export function OpenAPIDisclosure(props: {
icon: React.ReactNode;
context: OpenAPIClientContext;
children: React.ReactNode;
label: string | ((isExpanded: boolean) => string);
className?: string;
label: string;
}): React.JSX.Element {
const { icon, children, label, className } = props;
const { context, children, label } = props;
const [isExpanded, setIsExpanded] = useState(false);
return (
<Disclosure
className={clsx('openapi-disclosure', className)}
className="openapi-disclosure"
isExpanded={isExpanded}
onExpandedChange={setIsExpanded}
>
<Button
slot="trigger"
className="openapi-disclosure-trigger"
style={({ isFocusVisible }) => ({
outline: isFocusVisible
? '2px solid rgb(var(--primary-color-500) / 0.4)'
: 'none',
})}
>
{icon}
<span>{typeof label === 'function' ? label(isExpanded) : label}</span>
</Button>
<Heading>
<Button
slot="trigger"
className="openapi-disclosure-trigger"
style={({ isFocusVisible }) => ({
outline: isFocusVisible
? '2px solid rgb(var(--primary-color-500) / 0.4)'
: 'none',
})}
>
{context.icons.plus}
<span>
{isExpanded ? 'Hide' : 'Show'} {label}
</span>
</Button>
</Heading>
<DisclosurePanel className="openapi-disclosure-panel">
{isExpanded ? children : null}
</DisclosurePanel>
@@ -1,6 +1,6 @@
'use client';
import { createContext, useContext, useRef } from 'react';
import { createContext, useContext, useRef, useState } from 'react';
import { mergeProps, useButton, useDisclosure, useFocusRing, useId } from 'react-aria';
import {
type DisclosureGroupProps,
@@ -8,23 +8,18 @@ import {
useDisclosureGroupState,
useDisclosureState,
} from 'react-stately';
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
interface Props {
groups: TDisclosureGroup[];
icon?: React.ReactNode;
/** State key to use with a store */
selectStateKey?: string;
/** Icon to display for the select */
selectIcon?: React.ReactNode;
}
type TDisclosureGroup = {
key: string;
id: string;
label: string | React.ReactNode;
tabs?: {
key: string;
label: string | React.ReactNode;
id: string;
label?: string | React.ReactNode;
body?: React.ReactNode;
}[];
};
@@ -35,35 +30,24 @@ const DisclosureGroupStateContext = createContext<DisclosureGroupState | null>(n
* Display an interactive OpenAPI disclosure group.
*/
export function OpenAPIDisclosureGroup(props: DisclosureGroupProps & Props) {
const { icon, groups, selectStateKey, selectIcon } = props;
const { icon, groups } = props;
const state = useDisclosureGroupState(props);
return (
<DisclosureGroupStateContext.Provider value={state}>
{groups.map((group) => (
<DisclosureItem
selectStateKey={selectStateKey}
selectIcon={selectIcon}
icon={icon}
key={group.key}
group={group}
/>
<DisclosureItem icon={icon} key={group.id} group={group} />
))}
</DisclosureGroupStateContext.Provider>
);
}
function DisclosureItem(props: {
group: TDisclosureGroup;
icon?: React.ReactNode;
selectStateKey?: string;
selectIcon?: React.ReactNode;
}) {
const { icon, group, selectStateKey, selectIcon } = props;
function DisclosureItem(props: { group: TDisclosureGroup; icon?: React.ReactNode }) {
const { icon, group } = props;
const defaultId = useId();
const id = group.key || defaultId;
const id = group.id || defaultId;
const groupState = useContext(DisclosureGroupStateContext);
const isExpanded = groupState?.expandedKeys.has(id) || false;
const state = useDisclosureState({
@@ -90,9 +74,9 @@ function DisclosureItem(props: {
const { buttonProps } = useButton(triggerProps, triggerRef);
const { isFocusVisible, focusProps } = useFocusRing();
const defaultTab = group.tabs?.[0]?.key || '';
const store = useSelectState(selectStateKey, defaultTab);
const selectedTab = group.tabs?.find((tab) => tab.key === store.key) || group.tabs?.[0];
const defaultTab = group.tabs?.[0]?.id || '';
const [selectedTabKey, setSelectedTabKey] = useState(defaultTab);
const selectedTab = group.tabs?.find((tab) => tab.id === selectedTabKey);
return (
<div className="openapi-disclosure-group" aria-expanded={state.isExpanded}>
@@ -120,26 +104,23 @@ function DisclosureItem(props: {
{group.label}
</button>
{group.tabs ? (
<div
className="openapi-disclosure-group-mediatype"
onClick={(e) => e.stopPropagation()}
>
<div className="openapi-disclosure-group-mediatype">
{group.tabs?.length > 1 ? (
<OpenAPISelect
icon={selectIcon}
stateKey={selectStateKey}
onSelectionChange={() => {
<select
className="openapi-section-select openapi-select openapi-disclosure-group-tabs-select"
onClick={(event) => event.stopPropagation()}
value={selectedTab?.id}
onChange={(event) => {
setSelectedTabKey(event.target.value);
state.expand();
}}
items={group.tabs}
placement="bottom end"
>
{group.tabs.map((tab) => (
<OpenAPISelectItem key={tab.key} id={tab.key} value={tab}>
<option key={tab.id} value={tab.id}>
{tab.label}
</OpenAPISelectItem>
</option>
))}
</OpenAPISelect>
</select>
) : group.tabs[0]?.label ? (
<span>{group.tabs[0].label}</span>
) : null}
+82 -8
View File
@@ -1,8 +1,9 @@
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
import type { OpenAPIContext, OpenAPIUniversalContext } from './context';
import { generateSchemaExample } from './generateSchemaExample';
import { json2xml } from './json2xml';
import { stringifyOpenAPI } from './stringifyOpenAPI';
import { t } from './translate';
import type { OpenAPIContext } from './types';
import { checkIsReference } from './utils';
/**
* Display an example.
@@ -16,7 +17,7 @@ export function OpenAPIExample(props: {
const code = stringifyExample({ example, xml: syntax === 'xml' });
if (code === null) {
return <OpenAPIEmptyExample context={context} />;
return <OpenAPIEmptyExample />;
}
return context.renderCodeBlock({ code, syntax });
@@ -43,13 +44,86 @@ function stringifyExample(args: { example: OpenAPIV3.ExampleObject; xml: boolean
/**
* Empty response example.
*/
export function OpenAPIEmptyExample(props: {
context: OpenAPIUniversalContext;
}) {
const { context } = props;
export function OpenAPIEmptyExample() {
return (
<pre className="openapi-example-empty">
<p>{t(context.translation, 'no_content')}</p>
<p>No Content</p>
</pre>
);
}
/**
* Generate an example from a reference object.
*/
export function getExampleFromReference(ref: OpenAPIV3.ReferenceObject): OpenAPIV3.ExampleObject {
return { summary: 'Unresolved reference', value: { $ref: ref.$ref } };
}
/**
* Get examples from a media type object.
*/
export function getExamplesFromMediaTypeObject(args: {
mediaType: string;
mediaTypeObject: OpenAPIV3.MediaTypeObject;
}): { key: string; example: OpenAPIV3.ExampleObject }[] {
const { mediaTypeObject, mediaType } = args;
if (mediaTypeObject.examples) {
return Object.entries(mediaTypeObject.examples).map(([key, example]) => {
return {
key,
example: checkIsReference(example) ? getExampleFromReference(example) : example,
};
});
}
if (mediaTypeObject.example) {
return [{ key: 'default', example: { value: mediaTypeObject.example } }];
}
if (mediaTypeObject.schema) {
if (mediaType === 'application/xml') {
// @TODO normally we should use the name of the schema but we don't have it
// fix it when we got the reference name
const root = mediaTypeObject.schema.xml?.name ?? 'object';
return [
{
key: 'default',
example: {
value: {
[root]: generateSchemaExample(mediaTypeObject.schema, {
xml: mediaType === 'application/xml',
mode: 'read',
}),
},
},
},
];
}
return [
{
key: 'default',
example: {
value: generateSchemaExample(mediaTypeObject.schema, {
mode: 'read',
}),
},
},
];
}
return [];
}
/**
* Get example from a schema object.
*/
export function getExampleFromSchema(args: {
schema: OpenAPIV3.SchemaObject;
}): OpenAPIV3.ExampleObject {
const { schema } = args;
if (schema.example) {
return { value: schema.example };
}
return { value: generateSchemaExample(schema, { mode: 'read' }) };
}
@@ -1,139 +0,0 @@
'use client';
import type { Key } from 'react-aria';
import { OpenAPIEmptyExample } from './OpenAPIExample';
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
import { StaticSection } from './StaticSection';
import type { OpenAPIClientContext } from './context';
type OpenAPIMediaTypeItem = OpenAPISelectItem & {
body: React.ReactNode;
examples?: OpenAPIMediaTypeItem[];
};
/**
* Get the state of the response examples select.
*/
export function useMediaTypesState(stateKey: string | undefined, initialKey: Key = 'default') {
return useSelectState(stateKey, initialKey);
}
function useMediaTypeExamplesState(stateKey: string | undefined, initialKey: Key = 'default') {
return useSelectState(stateKey, initialKey);
}
export function OpenAPIMediaTypeContent(props: {
items: OpenAPIMediaTypeItem[];
selectIcon?: React.ReactNode;
stateKey: string;
context: OpenAPIClientContext;
}) {
const { stateKey, items, selectIcon, context } = props;
const state = useMediaTypesState(stateKey, items[0]?.key);
const examples = items.find((item) => item.key === state.key)?.examples ?? [];
if (!items.length && !examples.length) {
return null;
}
return (
<StaticSection
footer={
items.length > 1 || examples.length > 1 ? (
<OpenAPIMediaTypeFooter
items={items}
examples={examples}
selectIcon={selectIcon}
stateKey={stateKey}
/>
) : null
}
className="openapi-response-media-types-examples"
>
<OpenAPIMediaTypeBody
context={context}
stateKey={stateKey}
items={items}
examples={examples}
/>
</StaticSection>
);
}
function OpenAPIMediaTypeFooter(props: {
items: OpenAPIMediaTypeItem[];
examples?: OpenAPIMediaTypeItem[];
selectIcon?: React.ReactNode;
stateKey: string;
}) {
const { items, examples, stateKey, selectIcon } = props;
return (
<>
{items.length > 1 && (
<OpenAPISelect
icon={selectIcon}
items={items}
stateKey={stateKey}
placement="bottom start"
>
{items.map((item) => (
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
<span>{item.label}</span>
</OpenAPISelectItem>
))}
</OpenAPISelect>
)}
{examples && examples.length > 1 ? (
<OpenAPISelect
icon={selectIcon}
items={examples}
stateKey={`${stateKey}-examples`}
placement="bottom start"
>
{examples.map((example) => (
<OpenAPISelectItem key={example.key} id={example.key} value={example}>
<span>{example.label}</span>
</OpenAPISelectItem>
))}
</OpenAPISelect>
) : null}
</>
);
}
function OpenAPIMediaTypeBody(props: {
items: OpenAPIMediaTypeItem[];
examples?: OpenAPIMediaTypeItem[];
stateKey: string;
context: OpenAPIClientContext;
}) {
const { stateKey, items, examples, context } = props;
const state = useMediaTypesState(stateKey, items[0]?.key);
const selectedItem = items.find((item) => item.key === state.key) ?? items[0];
const exampleState = useMediaTypeExamplesState(
`${stateKey}-examples`,
selectedItem?.examples?.[0]?.key
);
if (!selectedItem) {
return null;
}
if (examples) {
const selectedExample =
examples.find((example) => example.key === exampleState.key) ?? examples[0];
if (!selectedExample) {
return <OpenAPIEmptyExample context={context} />;
}
return selectedExample.body;
}
return selectedItem.body;
}
+100 -11
View File
@@ -1,10 +1,18 @@
import clsx from 'clsx';
import type {
OpenAPICustomOperationProperties,
OpenAPIStability,
OpenAPIV3,
} from '@gitbook/openapi-parser';
import { Markdown } from './Markdown';
import { OpenAPICodeSample } from './OpenAPICodeSample';
import { OpenAPIPath } from './OpenAPIPath';
import { OpenAPIResponseExample } from './OpenAPIResponseExample';
import { OpenAPIColumnSpec } from './common/OpenAPIColumnSpec';
import { OpenAPISummary } from './common/OpenAPISummary';
import { type OpenAPIContextInput, resolveOpenAPIContext } from './context';
import type { OpenAPIOperationData } from './types';
import { OpenAPISpec } from './OpenAPISpec';
import { getOpenAPIClientContext } from './context';
import type { OpenAPIContext, OpenAPIOperationData } from './types';
import { resolveDescription } from './utils';
/**
* Display an interactive OpenAPI operation.
@@ -12,24 +20,105 @@ import type { OpenAPIOperationData } from './types';
export function OpenAPIOperation(props: {
className?: string;
data: OpenAPIOperationData;
context: OpenAPIContextInput;
context: OpenAPIContext;
}) {
const { className, data, context: contextInput } = props;
const { className, data, context } = props;
const { operation } = data;
const context = resolveOpenAPIContext(contextInput);
const clientContext = getOpenAPIClientContext(context);
return (
<div className={clsx('openapi-operation', className)}>
<OpenAPISummary data={data} context={context} />
<div className="openapi-summary" id={operation.summary ? undefined : context.id}>
{(operation.deprecated || operation['x-stability']) && (
<div className="openapi-summary-tags">
{operation.deprecated && (
<div className="openapi-deprecated">Deprecated</div>
)}
{operation['x-stability'] && (
<OpenAPIOperationStability stability={operation['x-stability']} />
)}
</div>
)}
{operation.summary
? context.renderHeading({
deprecated: operation.deprecated ?? false,
stability: operation['x-stability'],
title: operation.summary,
})
: null}
<OpenAPIPath data={data} />
</div>
<div className="openapi-columns">
<OpenAPIColumnSpec data={data} context={context} />
<div className="openapi-column-spec">
{operation['x-deprecated-sunset'] ? (
<div className="openapi-deprecated-sunset openapi-description openapi-markdown">
This operation is deprecated and will be sunset on{' '}
<span className="openapi-deprecated-sunset-date">
{operation['x-deprecated-sunset']}
</span>
{'.'}
</div>
) : null}
<OpenAPIOperationDescription operation={operation} context={context} />
<OpenAPISpec data={data} context={clientContext} />
</div>
<div className="openapi-column-preview">
<div className="openapi-column-preview-body">
<OpenAPICodeSample data={data} context={context} />
<OpenAPIResponseExample data={data} context={context} />
<OpenAPICodeSample {...props} />
<OpenAPIResponseExample {...props} />
</div>
</div>
</div>
</div>
);
}
function OpenAPIOperationDescription(props: {
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
context: OpenAPIContext;
}) {
const { operation } = props;
if (operation['x-gitbook-description-document']) {
return (
<div className="openapi-intro">
{props.context.renderDocument({
document: operation['x-gitbook-description-document'],
})}
</div>
);
}
const description = resolveDescription(operation);
if (!description) {
return null;
}
return (
<div className="openapi-intro">
<Markdown className="openapi-description" source={description} />
</div>
);
}
const stabilityEnum = {
experimental: 'Experimental',
alpha: 'Alpha',
beta: 'Beta',
} as const;
function OpenAPIOperationStability(props: { stability: OpenAPIStability }) {
const { stability } = props;
const foundStability = stabilityEnum[stability];
if (!foundStability) {
return null;
}
return (
<div className={`openapi-stability openapi-stability-${foundStability.toLowerCase()}`}>
{foundStability}
</div>
);
}
@@ -1,34 +0,0 @@
import type { OpenAPICustomOperationProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
import { Markdown } from './Markdown';
import type { OpenAPIContext } from './context';
import { resolveDescription } from './utils';
/**
* Display the description of an OpenAPI operation.
*/
export function OpenAPIOperationDescription(props: {
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
context: OpenAPIContext;
}) {
const { operation } = props;
if (operation['x-gitbook-description-document']) {
return (
<div className="openapi-intro">
{props.context.renderDocument({
document: operation['x-gitbook-description-document'],
})}
</div>
);
}
const description = resolveDescription(operation);
if (!description) {
return null;
}
return (
<div className="openapi-intro">
<Markdown className="openapi-description" source={description} />
</div>
);
}
@@ -1,39 +0,0 @@
import type { OpenAPIStability } from '@gitbook/openapi-parser';
import type { OpenAPIContext } from './context';
import { t } from './translate';
/**
* Display the stability of an OpenAPI operation.
*/
export function OpenAPIOperationStability(props: {
stability: OpenAPIStability;
context: OpenAPIContext;
}) {
const { stability, context } = props;
const stabilityLabel = getStabilityLabel(stability, context);
if (!stabilityLabel) {
return null;
}
return (
<div className={`openapi-stability openapi-stability-${stability}`}>{stabilityLabel}</div>
);
}
/**
* Get the stability label for the given stability level.
*/
function getStabilityLabel(stability: OpenAPIStability, context: OpenAPIContext) {
switch (stability) {
case 'experimental':
return t(context.translation, 'stability_experimental');
case 'alpha':
return t(context.translation, 'stability_alpha');
case 'beta':
return t(context.translation, 'stability_beta');
default:
return null;
}
}
+1 -4
View File
@@ -1,5 +1,4 @@
import { OpenAPICopyButton } from './OpenAPICopyButton';
import { type OpenAPIUniversalContext, getOpenAPIClientContext } from './context';
import type { OpenAPIOperationData } from './types';
import { getDefaultServerURL } from './util/server';
@@ -8,7 +7,6 @@ import { getDefaultServerURL } from './util/server';
*/
export function OpenAPIPath(props: {
data: OpenAPIOperationData;
context: OpenAPIUniversalContext;
/** Whether to show the server URL.
* @default true
*/
@@ -19,7 +17,7 @@ export function OpenAPIPath(props: {
*/
canCopy?: boolean;
}) {
const { data, context, withServer = true, canCopy = true } = props;
const { data, withServer = true, canCopy = true } = props;
const { method, path, operation } = data;
const server = getDefaultServerURL(data.servers);
@@ -43,7 +41,6 @@ export function OpenAPIPath(props: {
className="openapi-path-title"
data-deprecated={operation.deprecated}
isDisabled={!canCopy}
context={getOpenAPIClientContext(context)}
>
{element}
</OpenAPICopyButton>
@@ -1,10 +1,8 @@
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
import { InteractiveSection } from './InteractiveSection';
import { OpenAPIRootSchema } from './OpenAPISchemaServer';
import type { OpenAPIClientContext } from './context';
import { t } from './translate';
import type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
import { checkIsReference, createStateKey } from './utils';
import type { OpenAPIClientContext } from './types';
import { checkIsReference } from './utils';
/**
* Display an interactive request body.
@@ -12,9 +10,8 @@ import { checkIsReference, createStateKey } from './utils';
export function OpenAPIRequestBody(props: {
requestBody: OpenAPIV3.RequestBodyObject | OpenAPIV3.ReferenceObject;
context: OpenAPIClientContext;
data: OpenAPIOperationData | OpenAPIWebhookData;
}) {
const { requestBody, context, data } = props;
const { requestBody, context } = props;
if (checkIsReference(requestBody)) {
return null;
@@ -22,10 +19,8 @@ export function OpenAPIRequestBody(props: {
return (
<InteractiveSection
header={t(context.translation, 'name' in data ? 'payload' : 'body')}
header="Body"
className="openapi-requestbody"
stateKey={createStateKey('request-body-media-type', context.blockKey)}
selectIcon={context.icons.chevronDown}
tabs={Object.entries(requestBody.content ?? {}).map(
([contentType, mediaTypeObject]) => {
return {
@@ -1,7 +1,7 @@
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
import { OpenAPIDisclosure } from './OpenAPIDisclosure';
import { OpenAPISchemaProperties } from './OpenAPISchemaServer';
import type { OpenAPIClientContext } from './context';
import type { OpenAPIClientContext } from './types';
import { parameterToProperty, resolveDescription } from './utils';
/**
@@ -27,7 +27,7 @@ export function OpenAPIResponse(props: {
return (
<div className="openapi-response-body">
{headers.length > 0 ? (
<OpenAPIDisclosure icon={context.icons.plus} label="Headers">
<OpenAPIDisclosure context={context} label="Headers">
<OpenAPISchemaProperties
properties={headers.map(([name, header]) =>
parameterToProperty({ name, ...header })
@@ -1,19 +1,21 @@
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
import { Markdown } from './Markdown';
import { OpenAPIEmptyExample, OpenAPIExample } from './OpenAPIExample';
import { OpenAPIMediaTypeContent } from './OpenAPIMediaType';
import { OpenAPIResponseExampleContent } from './OpenAPIResponseExampleContent';
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
import type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
import { getExampleFromReference, getExamples } from './util/example';
import { createStateKey, getStatusCodeDefaultLabel } from './utils';
import { checkIsReference, resolveDescription } from './utils';
import {
OpenAPIEmptyExample,
OpenAPIExample,
getExampleFromReference,
getExamplesFromMediaTypeObject,
} from './OpenAPIExample';
import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
import { StaticSection } from './StaticSection';
import type { OpenAPIContext, OpenAPIOperationData } from './types';
import { checkIsReference, createStateKey, resolveDescription } from './utils';
/**
* Display an example of the response content.
*/
export function OpenAPIResponseExample(props: {
data: OpenAPIOperationData | OpenAPIWebhookData;
data: OpenAPIOperationData;
context: OpenAPIContext;
}) {
const { data, context } = props;
@@ -43,41 +45,36 @@ export function OpenAPIResponseExample(props: {
const tabs = responses.map(([key, responseObject]) => {
const description = resolveDescription(responseObject);
const label = description ? (
<Markdown source={description} />
) : (
getStatusCodeDefaultLabel(key, context)
);
if (checkIsReference(responseObject)) {
return {
key: key,
label,
statusCode: key,
label: key,
body: (
<OpenAPIExample
example={getExampleFromReference(responseObject, context)}
example={getExampleFromReference(responseObject)}
context={context}
syntax="json"
/>
),
footer: description ? <Markdown source={description} /> : undefined,
};
}
if (!responseObject.content || Object.keys(responseObject.content).length === 0) {
return {
key: key,
label,
statusCode: key,
body: <OpenAPIEmptyExample context={context} />,
label: key,
body: <OpenAPIEmptyExample />,
footer: description ? <Markdown source={description} /> : undefined,
};
}
return {
key: key,
label,
statusCode: key,
label: key,
body: <OpenAPIResponse context={context} content={responseObject.content} />,
footer: description ? <Markdown source={description} /> : undefined,
};
});
@@ -86,11 +83,11 @@ export function OpenAPIResponseExample(props: {
}
return (
<OpenAPIResponseExampleContent
selectIcon={context.icons.chevronDown}
blockKey={context.blockKey}
items={tabs}
/>
<OpenAPITabs stateKey={createStateKey('response-example')} items={tabs}>
<StaticSection header={<OpenAPITabsList />} className="openapi-panel">
<OpenAPITabsPanels />
</StaticSection>
</OpenAPITabs>
);
}
@@ -109,26 +106,98 @@ function OpenAPIResponse(props: {
throw new Error('One media type is required');
}
if (entries.length === 1) {
const [mediaType, mediaTypeObject] = firstEntry;
return (
<OpenAPIResponseMediaType
context={context}
mediaType={mediaType}
mediaTypeObject={mediaTypeObject}
/>
);
}
const tabs = entries.map((entry) => {
const [mediaType, mediaTypeObject] = entry;
return {
key: mediaType,
label: mediaType,
body: <></>,
examples: getExamples({
mediaTypeObject,
mediaType,
context,
}),
body: (
<OpenAPIResponseMediaType
context={context}
mediaType={mediaType}
mediaTypeObject={mediaTypeObject}
/>
),
};
});
return (
<OpenAPIMediaTypeContent
selectIcon={context.icons.chevronDown}
stateKey={createStateKey('response-media-types', context.blockKey)}
items={tabs}
context={getOpenAPIClientContext(context)}
/>
<OpenAPITabs stateKey={createStateKey('response-media-types')} items={tabs}>
<StaticSection header={<OpenAPITabsList />} className="openapi-response-media-types">
<OpenAPITabsPanels />
</StaticSection>
</OpenAPITabs>
);
}
function OpenAPIResponseMediaType(props: {
mediaTypeObject: OpenAPIV3.MediaTypeObject;
mediaType: string;
context: OpenAPIContext;
}) {
const { mediaTypeObject, mediaType } = props;
const examples = getExamplesFromMediaTypeObject({ mediaTypeObject, mediaType });
const syntax = getSyntaxFromMediaType(mediaType);
const firstExample = examples[0];
if (!firstExample) {
return <OpenAPIEmptyExample />;
}
if (examples.length === 1) {
return (
<OpenAPIExample
example={firstExample.example}
context={props.context}
syntax={syntax}
/>
);
}
const tabs = examples.map((example) => {
return {
key: example.key,
label: example.example.summary || example.key,
body: (
<OpenAPIExample example={example.example} context={props.context} syntax={syntax} />
),
};
});
return (
<OpenAPITabs stateKey={createStateKey('response-media-type-examples')} items={tabs}>
<StaticSection
header={<OpenAPITabsList />}
className="openapi-response-media-type-examples"
>
<OpenAPITabsPanels />
</StaticSection>
</OpenAPITabs>
);
}
/**
* Get the syntax from a media type.
*/
function getSyntaxFromMediaType(mediaType: string): string {
if (mediaType.includes('json')) {
return 'json';
}
if (mediaType === 'application/xml') {
return 'xml';
}
return 'text';
}
@@ -1,123 +0,0 @@
'use client';
import clsx from 'clsx';
import type { Key } from 'react-aria';
import { OpenAPISelect, OpenAPISelectItem, useSelectState } from './OpenAPISelect';
import { StaticSection } from './StaticSection';
import { createStateKey, getStatusCodeClassName } from './utils';
type OpenAPIResponseExampleItem = OpenAPISelectItem & {
statusCode: string;
body: React.ReactNode;
};
/**
* Get the state of the response examples select.
*/
export function useResponseExamplesState(
blockKey: string | undefined,
initialKey: Key = 'default'
) {
return useSelectState(getResponseExampleStateKey(blockKey), initialKey);
}
export function OpenAPIResponseExampleContent(props: {
items: OpenAPIResponseExampleItem[];
blockKey?: string;
selectIcon?: React.ReactNode;
}) {
const { blockKey, items, selectIcon } = props;
return (
<StaticSection
header={
<OpenAPIResponseExampleHeader
selectIcon={selectIcon}
blockKey={blockKey}
items={items}
/>
}
className="openapi-response-examples"
>
<OpenAPIResponseExampleBody blockKey={blockKey} items={items} />
</StaticSection>
);
}
function OpenAPIResponseExampleHeader(props: {
items: OpenAPIResponseExampleItem[];
blockKey?: string;
selectIcon?: React.ReactNode;
}) {
const { items, blockKey, selectIcon } = props;
if (items.length === 1) {
const item = items[0];
if (!item) {
return null;
}
return (
<span className="openapi-response-examples-statuscode-title">
<span
className={clsx(
'openapi-statuscode',
`openapi-statuscode-${getStatusCodeClassName(item.statusCode)}`,
'openapi-response-examples-statuscode'
)}
>
{item.statusCode}
</span>
<span>{item.label}</span>
</span>
);
}
return (
<OpenAPISelect
items={items}
icon={selectIcon}
stateKey={getResponseExampleStateKey(blockKey)}
placement="bottom start"
>
{items.map((item) => (
<OpenAPISelectItem key={item.key} id={item.key} value={item}>
<span
className={clsx(
'openapi-statuscode',
`openapi-statuscode-${getStatusCodeClassName(item.statusCode)}`,
'openapi-response-examples-statuscode'
)}
>
{item.statusCode}
</span>
<span>{item.label}</span>
</OpenAPISelectItem>
))}
</OpenAPISelect>
);
}
function OpenAPIResponseExampleBody(props: {
items: OpenAPIResponseExampleItem[];
blockKey?: string;
}) {
const { blockKey, items } = props;
const state = useResponseExamplesState(blockKey, items[0]?.key);
const selectedItem = items.find((item) => item.key === state.key) ?? items[0];
if (!selectedItem) {
return null;
}
return <div className="openapi-response-examples-panel">{selectedItem.body}</div>;
}
/**
* Return the state key for the response examples.
*/
function getResponseExampleStateKey(blockKey: string | undefined) {
return createStateKey('openapi-responses', blockKey);
}
+62 -84
View File
@@ -1,15 +1,9 @@
'use client';
import type { OpenAPIV3, OpenAPIV3_1 } from '@gitbook/openapi-parser';
import clsx from 'clsx';
import { Markdown } from './Markdown';
import { OpenAPIDisclosureGroup } from './OpenAPIDisclosureGroup';
import { OpenAPIResponse } from './OpenAPIResponse';
import { useResponseExamplesState } from './OpenAPIResponseExampleContent';
import { StaticSection } from './StaticSection';
import type { OpenAPIClientContext } from './context';
import { t } from './translate';
import { createStateKey, getStatusCodeClassName, getStatusCodeDefaultLabel } from './utils';
import type { OpenAPIClientContext } from './types';
/**
* Display an interactive response body.
@@ -20,86 +14,70 @@ export function OpenAPIResponses(props: {
}) {
const { responses, context } = props;
const groups = Object.entries(responses).map(
([statusCode, response]: [string, OpenAPIV3.ResponseObject]) => {
const tabs = (() => {
// If there is no content, but there are headers, we need to show the headers
if (
(!response.content || !Object.keys(response.content).length) &&
response.headers &&
Object.keys(response.headers).length
) {
return [
{
key: 'default',
label: '',
body: (
<OpenAPIResponse
response={response}
mediaType={{}}
context={context}
/>
),
},
];
}
return Object.entries(response.content ?? {}).map(([contentType, mediaType]) => ({
key: contentType,
label: contentType,
body: (
<OpenAPIResponse
response={response}
mediaType={mediaType}
context={context}
/>
),
}));
})();
const description = response.description;
return {
key: statusCode,
label: (
<div className="openapi-response-tab-content">
<span
className={clsx(
'openapi-statuscode',
`openapi-statuscode-${getStatusCodeClassName(statusCode)}`
)}
>
{statusCode}
</span>
{description ? (
<Markdown
source={description}
className="openapi-response-description"
/>
) : (
getStatusCodeDefaultLabel(statusCode, context)
)}
</div>
),
tabs,
};
}
);
const state = useResponseExamplesState(context.blockKey, groups[0]?.key);
return (
<StaticSection header={t(context.translation, 'responses')} className="openapi-responses">
<StaticSection header="Responses" className="openapi-responses">
<OpenAPIDisclosureGroup
allowsMultipleExpanded
icon={context.icons.chevronRight}
expandedKeys={state.key ? new Set([state.key]) : new Set()}
onExpandedChange={(keys) => {
const key = keys.values().next().value ?? null;
state.setKey(key);
}}
groups={groups}
selectIcon={context.icons.chevronDown}
selectStateKey={createStateKey('response-media-types', context.blockKey)}
groups={Object.entries(responses).map(
([statusCode, response]: [string, OpenAPIV3.ResponseObject]) => {
const tabs = (() => {
// If there is no content, but there are headers, we need to show the headers
if (
(!response.content || !Object.keys(response.content).length) &&
response.headers &&
Object.keys(response.headers).length
) {
return [
{
id: 'default',
body: (
<OpenAPIResponse
response={response}
mediaType={{}}
context={context}
/>
),
},
];
}
return Object.entries(response.content ?? {}).map(
([contentType, mediaType]) => ({
id: contentType,
label: contentType,
body: (
<OpenAPIResponse
response={response}
mediaType={mediaType}
context={context}
/>
),
})
);
})();
const description = response.description;
return {
id: statusCode,
label: (
<div className="openapi-response-tab-content">
<span className="openapi-response-statuscode">
{statusCode}
</span>
{description ? (
<Markdown
source={description}
className="openapi-response-description"
/>
) : null}
</div>
),
tabs,
};
}
)}
/>
</StaticSection>
);
@@ -35,86 +35,6 @@ describe('getSchemaAlternatives', () => {
]);
});
it('merges string enum', () => {
expect(
getSchemaAlternatives({
oneOf: [
{
oneOf: [
{
type: 'string',
enum: ['a', 'b'],
},
{
type: 'string',
enum: ['c', 'd'],
nullable: true,
},
],
},
],
})
).toEqual([
{
type: 'string',
enum: ['a', 'b', 'c', 'd'],
nullable: true,
},
]);
});
it('merges objects with allOf', () => {
expect(
getSchemaAlternatives({
allOf: [
{
type: 'object',
properties: {
name: {
type: 'string',
},
map: {
type: 'string',
},
description: {
type: 'string',
},
},
required: ['name'],
},
{
type: 'object',
properties: {
externalId: {
type: 'string',
},
},
required: ['map', 'externalId'],
},
],
})
).toEqual([
{
type: 'object',
properties: {
name: {
type: 'string',
},
map: {
type: 'string',
},
description: {
type: 'string',
},
externalId: {
type: 'string',
},
},
required: ['name', 'map', 'externalId'],
},
]);
});
it('should not flatten oneOf and allOf', () => {
expect(
getSchemaAlternatives({
+16 -123
View File
@@ -10,10 +10,9 @@ import { Markdown } from './Markdown';
import { OpenAPICopyButton } from './OpenAPICopyButton';
import { OpenAPIDisclosure } from './OpenAPIDisclosure';
import { OpenAPISchemaName } from './OpenAPISchemaName';
import type { OpenAPIClientContext } from './context';
import { retrocycle } from './decycle';
import { stringifyOpenAPI } from './stringifyOpenAPI';
import { tString } from './translate';
import type { OpenAPIClientContext } from './types';
import { checkIsReference, resolveDescription, resolveFirstExample } from './utils';
type CircularRefsIds = Map<OpenAPIV3.SchemaObject, string>;
@@ -41,7 +40,7 @@ function OpenAPISchemaProperty(props: {
return (
<div id={id} className={clsx('openapi-schema', className)}>
<OpenAPISchemaPresentation context={context} property={property} />
<OpenAPISchemaPresentation property={property} />
{(() => {
const circularRefId = parentCircularRefs.get(schema);
// Avoid recursing infinitely, and instead render a link to the parent schema
@@ -55,12 +54,7 @@ function OpenAPISchemaProperty(props: {
const properties = getSchemaProperties(schema);
if (properties?.length) {
return (
<OpenAPIDisclosure
icon={context.icons.plus}
label={(isExpanded) =>
getDisclosureLabel({ schema, isExpanded, context })
}
>
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
<OpenAPISchemaProperties
properties={properties}
circularRefs={circularRefs}
@@ -214,10 +208,7 @@ function OpenAPISchemaAlternative(props: {
{description ? (
<Markdown source={description} className="openapi-schema-description" />
) : null}
<OpenAPIDisclosure
icon={context.icons.plus}
label={(isExpanded) => getDisclosureLabel({ schema, isExpanded, context })}
>
<OpenAPIDisclosure context={context} label={getDisclosureLabel(schema)}>
{properties?.length ? (
<OpenAPISchemaProperties
properties={properties}
@@ -255,9 +246,8 @@ function OpenAPISchemaCircularRef(props: { id: string; schema: OpenAPIV3.SchemaO
*/
function OpenAPISchemaEnum(props: {
schema: OpenAPIV3.SchemaObject & OpenAPICustomOperationProperties;
context: OpenAPIClientContext;
}) {
const { schema, context } = props;
const { schema } = props;
const enumValues = (() => {
// Render x-gitbook-enum first, as it has a different format
@@ -300,7 +290,6 @@ function OpenAPISchemaEnum(props: {
value={item.value}
label={item.description}
withTooltip={!!item.description}
context={context}
>
<code>{`${item.value}`}</code>
</OpenAPICopyButton>
@@ -313,13 +302,9 @@ function OpenAPISchemaEnum(props: {
/**
* Render the top row of a schema. e.g: name, type, and required status.
*/
function OpenAPISchemaPresentation(props: {
property: OpenAPISchemaPropertyEntry;
context: OpenAPIClientContext;
}) {
function OpenAPISchemaPresentation(props: { property: OpenAPISchemaPropertyEntry }) {
const {
property: { schema, propertyName, required },
context,
} = props;
const description = resolveDescription(schema);
@@ -332,7 +317,6 @@ function OpenAPISchemaPresentation(props: {
type={getSchemaTitle(schema)}
propertyName={propertyName}
required={required}
context={context}
/>
{typeof schema['x-deprecated-sunset'] === 'string' ? (
<div className="openapi-deprecated-sunset openapi-schema-description openapi-markdown">
@@ -365,7 +349,7 @@ function OpenAPISchemaPresentation(props: {
Pattern: <code>{schema.pattern}</code>
</span>
) : null}
<OpenAPISchemaEnum schema={schema} context={context} />
<OpenAPISchemaEnum schema={schema} />
</div>
);
}
@@ -460,91 +444,7 @@ export function getSchemaAlternatives(
}
const [type, schemas] = alternatives;
return mergeAlternatives(
type,
flattenAlternatives(type, schemas, new Set(ancestors).add(schema))
);
}
/**
* Merge alternatives of the same type into a single schema.
* - Merge string enums
*/
function mergeAlternatives(
alternativeType: AlternativeType,
schemasOrRefs: OpenAPIV3.SchemaObject[]
): OpenAPIV3.SchemaObject[] | null {
switch (alternativeType) {
case 'oneOf': {
return schemasOrRefs.reduce<OpenAPIV3.SchemaObject[]>((acc, schemaOrRef) => {
const latest = acc.at(-1);
if (
latest &&
latest.type === 'string' &&
latest.enum &&
schemaOrRef.type === 'string' &&
schemaOrRef.enum
) {
latest.enum = Array.from(new Set([...latest.enum, ...schemaOrRef.enum]));
latest.nullable = latest.nullable || schemaOrRef.nullable;
return acc;
}
acc.push(schemaOrRef);
return acc;
}, []);
}
case 'allOf': {
return schemasOrRefs.reduce<OpenAPIV3.SchemaObject[]>((acc, schemaOrRef) => {
const latest = acc.at(-1);
if (
latest &&
latest.type === 'string' &&
latest.enum &&
schemaOrRef.type === 'string' &&
schemaOrRef.enum
) {
const keys = Object.keys(schemaOrRef);
if (keys.every((key) => ['type', 'enum', 'nullable'].includes(key))) {
latest.enum = Array.from(new Set([...latest.enum, ...schemaOrRef.enum]));
latest.nullable = latest.nullable || schemaOrRef.nullable;
return acc;
}
}
if (latest && latest.type === 'object' && schemaOrRef.type === 'object') {
const keys = Object.keys(schemaOrRef);
if (
keys.every((key) =>
['type', 'properties', 'required', 'nullable'].includes(key)
)
) {
latest.properties = {
...latest.properties,
...schemaOrRef.properties,
};
latest.required = Array.from(
new Set([
...(Array.isArray(latest.required) ? latest.required : []),
...(Array.isArray(schemaOrRef.required)
? schemaOrRef.required
: []),
])
);
latest.nullable = latest.nullable || schemaOrRef.nullable;
return acc;
}
}
acc.push(schemaOrRef);
return acc;
}, []);
}
default:
return schemasOrRefs;
}
return flattenAlternatives(type, schemas, new Set(ancestors).add(schema));
}
function flattenAlternatives(
@@ -602,26 +502,19 @@ function getSchemaTitle(schema: OpenAPIV3.SchemaObject): string {
return type;
}
function getDisclosureLabel(props: {
schema: OpenAPIV3.SchemaObject;
isExpanded: boolean;
context: OpenAPIClientContext;
}) {
const { schema, isExpanded, context } = props;
let label: string;
function getDisclosureLabel(schema: OpenAPIV3.SchemaObject): string {
if (schema.type === 'array' && !!schema.items) {
if (schema.items.oneOf) {
label = tString(context.translation, 'available_items').toLowerCase();
return 'available items';
}
// Fallback to "child attributes" for enums and objects
else if (schema.items.enum || schema.items.type === 'object') {
label = tString(context.translation, 'child_attributes').toLowerCase();
} else {
label = schema.items.title ?? schema.title ?? getSchemaTitle(schema.items);
if (schema.items.enum || schema.items.type === 'object') {
return 'child attributes';
}
} else {
label = schema.title || tString(context.translation, 'child_attributes').toLowerCase();
return schema.items.title ?? schema.title ?? getSchemaTitle(schema.items);
}
return `${isExpanded ? tString(context.translation, 'hide') : tString(context.translation, 'show')} ${label}`;
return schema.title || 'child attributes';
}
@@ -1,14 +1,11 @@
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
import type React from 'react';
import type { OpenAPIClientContext } from './context';
import { t, tString } from './translate';
interface OpenAPISchemaNameProps {
schema?: OpenAPIV3.SchemaObject;
propertyName?: string | React.JSX.Element;
required?: boolean;
type?: string;
context: OpenAPIClientContext;
}
/**
@@ -16,9 +13,9 @@ interface OpenAPISchemaNameProps {
* It includes the property name, type, required and deprecated status.
*/
export function OpenAPISchemaName(props: OpenAPISchemaNameProps) {
const { schema, type, propertyName, required, context } = props;
const { schema, type, propertyName, required } = props;
const additionalItems = schema && getAdditionalItems(schema, context);
const additionalItems = schema && getAdditionalItems(schema);
return (
<span className="openapi-schema-name">
@@ -33,45 +30,33 @@ export function OpenAPISchemaName(props: OpenAPISchemaNameProps) {
<span className="openapi-schema-type">{additionalItems}</span>
) : null}
</span>
{schema?.readOnly ? (
<span className="openapi-schema-readonly">
{t(context.translation, 'read_only')}
</span>
) : null}
{schema?.readOnly ? <span className="openapi-schema-readonly">read-only</span> : null}
{schema?.writeOnly ? (
<span className="openapi-schema-writeonly">
{t(context.translation, 'write_only')}
</span>
<span className="openapi-schema-writeonly">write-only</span>
) : null}
{required ? (
<span className="openapi-schema-required">
{t(context.translation, 'required')}
</span>
<span className="openapi-schema-required">required</span>
) : (
<span className="openapi-schema-optional">
{t(context.translation, 'optional')}
</span>
<span className="openapi-schema-optional">optional</span>
)}
{schema?.deprecated ? (
<span className="openapi-deprecated">{t(context.translation, 'deprecated')}</span>
) : null}
{schema?.deprecated ? <span className="openapi-deprecated">Deprecated</span> : null}
</span>
);
}
function getAdditionalItems(schema: OpenAPIV3.SchemaObject, context: OpenAPIClientContext): string {
function getAdditionalItems(schema: OpenAPIV3.SchemaObject): string {
let additionalItems = '';
if (schema.minimum || schema.minLength || schema.minItems) {
additionalItems += ` · ${tString(context.translation, 'min').toLowerCase()}: ${schema.minimum || schema.minLength || schema.minItems}`;
additionalItems += ` · min: ${schema.minimum || schema.minLength || schema.minItems}`;
}
if (schema.maximum || schema.maxLength || schema.maxItems) {
additionalItems += ` · ${tString(context.translation, 'max').toLowerCase()}: ${schema.maximum || schema.maxLength || schema.maxItems}`;
additionalItems += ` · max: ${schema.maximum || schema.maxLength || schema.maxItems}`;
}
if (schema.nullable) {
additionalItems = ` | ${tString(context.translation, 'nullable').toLowerCase()}`;
additionalItems = ' | nullable';
}
return additionalItems;
@@ -4,8 +4,8 @@ import {
OpenAPISchemaPropertiesFromServer,
type OpenAPISchemaPropertyEntry,
} from './OpenAPISchema';
import type { OpenAPIClientContext } from './context';
import { decycle } from './decycle';
import type { OpenAPIClientContext } from './types';
export function OpenAPISchemaProperties(props: {
id?: string;
@@ -1,10 +1,12 @@
import { InteractiveSection } from './InteractiveSection';
import { Markdown } from './Markdown';
import { OpenAPISchemaName } from './OpenAPISchemaName';
import type { OpenAPIClientContext } from './context';
import { t } from './translate';
import type { OpenAPIOperationData, OpenAPISecurityWithRequired } from './types';
import { createStateKey, resolveDescription } from './utils';
import type {
OpenAPIClientContext,
OpenAPIOperationData,
OpenAPISecurityWithRequired,
} from './types';
import { resolveDescription } from './utils';
/**
* Present securities authorization that can be used for this operation.
@@ -21,12 +23,10 @@ export function OpenAPISecurities(props: {
return (
<InteractiveSection
header={t(context.translation, 'authorizations')}
stateKey={createStateKey('securities', context.blockKey)}
header="Authorizations"
toggeable
defaultOpened={false}
toggleIcon={context.icons.chevronRight}
selectIcon={context.icons.chevronDown}
className="openapi-securities"
tabs={securities.map(([key, security]) => {
const description = resolveDescription(security);
@@ -36,7 +36,7 @@ export function OpenAPISecurities(props: {
body: (
<div className="openapi-schema">
<div className="openapi-schema-presentation">
{getLabelForType(security, context)}
{getLabelForType(security)}
{description ? (
<Markdown
@@ -53,12 +53,11 @@ export function OpenAPISecurities(props: {
);
}
function getLabelForType(security: OpenAPISecurityWithRequired, context: OpenAPIClientContext) {
function getLabelForType(security: OpenAPISecurityWithRequired) {
switch (security.type) {
case 'apiKey':
return (
<OpenAPISchemaName
context={context}
propertyName={security.name ?? 'apiKey'}
type="string"
required={security.required}
@@ -68,7 +67,6 @@ function getLabelForType(security: OpenAPISecurityWithRequired, context: OpenAPI
if (security.scheme === 'basic') {
return (
<OpenAPISchemaName
context={context}
propertyName="Authorization"
type="string"
required={security.required}
@@ -81,7 +79,6 @@ function getLabelForType(security: OpenAPISecurityWithRequired, context: OpenAPI
return (
<>
<OpenAPISchemaName
context={context}
propertyName="Authorization"
type="string"
required={security.required}
@@ -97,29 +94,11 @@ function getLabelForType(security: OpenAPISecurityWithRequired, context: OpenAPI
);
}
return (
<OpenAPISchemaName
context={context}
propertyName="HTTP"
required={security.required}
/>
);
return <OpenAPISchemaName propertyName="HTTP" required={security.required} />;
case 'oauth2':
return (
<OpenAPISchemaName
context={context}
propertyName="OAuth2"
required={security.required}
/>
);
return <OpenAPISchemaName propertyName="OAuth2" required={security.required} />;
case 'openIdConnect':
return (
<OpenAPISchemaName
context={context}
propertyName="OpenID Connect"
required={security.required}
/>
);
return <OpenAPISchemaName propertyName="OpenID Connect" required={security.required} />;
default:
// @ts-ignore
return security.type;
+16 -42
View File
@@ -1,7 +1,6 @@
'use client';
import clsx from 'clsx';
import { useCallback } from 'react';
import {
Button,
type Key,
@@ -14,63 +13,38 @@ import {
type SelectProps,
SelectValue,
} from 'react-aria-components';
import { useStore } from 'zustand';
import { getOrCreateStoreByKey } from './getOrCreateStoreByKey';
export type OpenAPISelectItem = {
key: Key;
label: string | React.ReactNode;
label: string;
};
interface OpenAPISelectProps<T extends OpenAPISelectItem> extends Omit<SelectProps<T>, 'children'> {
items: T[];
children: React.ReactNode | ((item: T) => React.ReactNode);
selectedKey?: Key;
onChange?: (key: string | number) => void;
placement?: PopoverProps['placement'];
stateKey?: string;
/**
* Icon to display in the select button.
*/
icon?: React.ReactNode;
}
export function useSelectState(stateKey = 'select-state', initialKey?: Key) {
const store = useStore(getOrCreateStoreByKey(stateKey, initialKey));
return {
key: store.key,
setKey: useCallback((key: Key | null) => store.setKey(key), [store.setKey]),
};
}
export function OpenAPISelect<T extends OpenAPISelectItem>(props: OpenAPISelectProps<T>) {
const {
icon = '▼',
items,
children,
className,
placement,
stateKey,
selectedKey,
onSelectionChange,
} = props;
const state = useSelectState(stateKey, items[0]?.key);
const selected = items.find((item) => item.key === state.key) || items[0];
const { items, children, className, placement } = props;
return (
<Select
aria-label="OpenAPI Select"
{...props}
selectedKey={selectedKey || selected?.key}
onSelectionChange={(key) => {
onSelectionChange?.(key);
state.setKey(key);
}}
className={clsx('openapi-select', className)}
>
<Select {...props} className={clsx('openapi-select', className)}>
<Button>
<SelectValue />
{icon}
<span aria-hidden="true">
<svg
className="gb-icon"
style={{
maskImage:
"url('https://ka-p.fontawesome.com/releases/v6.6.0/svgs/regular/chevron-down.svg?v=2&token=a463935e93')",
maskRepeat: 'no-repeat',
maskPosition: 'center center',
}}
/>
</span>
</Button>
<Popover placement={placement} className="openapi-select-popover">
<ListBox className="openapi-select-listbox" items={items}>
+10 -21
View File
@@ -5,23 +5,16 @@ import { OpenAPIResponses } from './OpenAPIResponses';
import { OpenAPISchemaProperties } from './OpenAPISchemaServer';
import { OpenAPISecurities } from './OpenAPISecurities';
import { StaticSection } from './StaticSection';
import type { OpenAPIClientContext } from './context';
import { tString } from './translate';
import type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
import type { OpenAPIClientContext, OpenAPIOperationData } from './types';
import { parameterToProperty } from './utils';
export function OpenAPISpec(props: {
data: OpenAPIOperationData | OpenAPIWebhookData;
context: OpenAPIClientContext;
}) {
export function OpenAPISpec(props: { data: OpenAPIOperationData; context: OpenAPIClientContext }) {
const { data, context } = props;
const { operation } = data;
const { operation, securities } = data;
const parameters = operation.parameters ?? [];
const parameterGroups = groupParameters(parameters, context);
const securities = 'securities' in data ? data.securities : [];
const parameterGroups = groupParameters(parameters);
return (
<>
@@ -49,7 +42,6 @@ export function OpenAPISpec(props: {
key="body"
requestBody={operation.requestBody}
context={context}
data={data}
/>
) : null}
{operation.responses ? (
@@ -63,10 +55,7 @@ export function OpenAPISpec(props: {
);
}
function groupParameters(
parameters: OpenAPI.Parameters,
context: OpenAPIClientContext
): Array<{
function groupParameters(parameters: OpenAPI.Parameters): Array<{
key: string;
label: string;
parameters: OpenAPI.Parameters;
@@ -83,7 +72,7 @@ function groupParameters(
.filter((parameter) => parameter.in)
.forEach((parameter) => {
const key = parameter.in;
const label = getParameterGroupName(parameter.in, context);
const label = getParameterGroupName(parameter.in);
const group = groups.find((group) => group.key === key);
if (group) {
group.parameters.push(parameter);
@@ -101,14 +90,14 @@ function groupParameters(
return groups;
}
function getParameterGroupName(paramIn: string, context: OpenAPIClientContext): string {
function getParameterGroupName(paramIn: string): string {
switch (paramIn) {
case 'path':
return tString(context.translation, 'path_parameters');
return 'Path parameters';
case 'query':
return tString(context.translation, 'query_parameters');
return 'Query parameters';
case 'header':
return tString(context.translation, 'header_parameters');
return 'Header parameters';
default:
return paramIn;
}
@@ -1,33 +0,0 @@
import clsx from 'clsx';
import { OpenAPIWebhookExample } from './OpenAPIWebhookExample';
import { OpenAPIColumnSpec } from './common/OpenAPIColumnSpec';
import { OpenAPISummary } from './common/OpenAPISummary';
import { type OpenAPIContextInput, resolveOpenAPIContext } from './context';
import type { OpenAPIWebhookData } from './types';
/**
* Display an interactive OpenAPI webhook.
*/
export function OpenAPIWebhook(props: {
className?: string;
data: OpenAPIWebhookData;
context: OpenAPIContextInput;
}) {
const { className, data, context: contextInput } = props;
const context = resolveOpenAPIContext(contextInput);
return (
<div className={clsx('openapi-webhook', className)}>
<OpenAPISummary data={data} context={context} />
<div className="openapi-columns">
<OpenAPIColumnSpec data={data} context={context} />
<div className="openapi-column-preview">
<div className="openapi-column-preview-body">
<OpenAPIWebhookExample data={data} context={context} />
</div>
</div>
</div>
</div>
);
}
@@ -1,60 +0,0 @@
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
import { OpenAPIEmptyExample } from './OpenAPIExample';
import { OpenAPIMediaTypeContent } from './OpenAPIMediaType';
import { type OpenAPIContext, getOpenAPIClientContext } from './context';
import type { OpenAPIWebhookData } from './types';
import { getExamples } from './util/example';
import { createStateKey } from './utils';
export function OpenAPIWebhookExample(props: {
data: OpenAPIWebhookData;
context: OpenAPIContext;
}) {
const { data, context } = props;
const { operation } = data;
const items = (() => {
if (!operation.requestBody) {
return [];
}
return Object.entries(
operation.requestBody.content as Record<string, OpenAPIV3.MediaTypeObject>
).map(([key, value]) => {
const schema = value.schema;
if (!schema) {
return {
key,
label: key,
body: <OpenAPIEmptyExample context={context} />,
};
}
return {
key,
label: key,
body: <></>,
examples: getExamples({
mediaTypeObject: value,
mediaType: key,
context,
}),
};
});
})();
return (
<div className="openapi-panel">
<h4 className="openapi-panel-heading">Payload</h4>
<div className="openapi-panel-body">
<OpenAPIMediaTypeContent
selectIcon={context.icons.chevronDown}
stateKey={createStateKey('request-body-media-type', context.blockKey)}
items={items}
context={getOpenAPIClientContext(context)}
/>
</div>
</div>
);
}
@@ -6,8 +6,6 @@ import { createPortal } from 'react-dom';
import type { OpenAPIV3_1 } from '@gitbook/openapi-parser';
import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
import type { OpenAPIClientContext } from './context';
import { t } from './translate';
/**
* Button which launches the Scalar API Client
@@ -16,9 +14,8 @@ export function ScalarApiButton(props: {
method: OpenAPIV3_1.HttpMethods;
path: string;
specUrl: string;
context: OpenAPIClientContext;
}) {
const { method, path, specUrl, context } = props;
const { method, path, specUrl } = props;
const [isOpen, setIsOpen] = useState(false);
const controllerRef = useRef<ScalarModalControllerRef>(null);
return (
@@ -30,7 +27,7 @@ export function ScalarApiButton(props: {
setIsOpen(true);
}}
>
{t(context.translation, 'test_it')}
Test it
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 12" fill="currentColor">
<path
stroke="currentColor"
@@ -62,7 +59,10 @@ function ScalarModal(props: {
}) {
const { method, path, specUrl, controllerRef } = props;
return (
<ApiClientModalProvider configuration={{ url: specUrl }} initialRequest={{ method, path }}>
<ApiClientModalProvider
configuration={{ spec: { url: specUrl } }}
initialRequest={{ method, path }}
>
<ScalarModalController method={method} path={path} controllerRef={controllerRef} />
</ApiClientModalProvider>
);
+5 -37
View File
@@ -42,50 +42,18 @@ export const SectionBody = forwardRef(function SectionBody(
);
});
export function SectionFooter(props: ComponentPropsWithoutRef<'div'>) {
return (
<div
{...props}
className={clsx(
'openapi-section-footer',
props.className && `${props.className}-footer`
)}
/>
);
}
export function SectionFooterContent(props: ComponentPropsWithoutRef<'div'>) {
return (
<div
{...props}
className={clsx(
'openapi-section-footer-content',
props.className && `${props.className}-footer-content`
)}
/>
);
}
export function StaticSection(props: {
className: string;
header?: React.ReactNode;
header: React.ReactNode;
children: React.ReactNode;
footer?: React.ReactNode;
}) {
const { className, header, children, footer } = props;
const { className, header, children } = props;
return (
<Section className={className}>
{header ? (
<SectionHeader className={className}>
<SectionHeaderContent className={className}>{header}</SectionHeaderContent>
</SectionHeader>
) : null}
<SectionHeader className={className}>
<SectionHeaderContent className={className}>{header}</SectionHeaderContent>
</SectionHeader>
<SectionBody className={className}>{children}</SectionBody>
{footer ? (
<SectionFooter className={className}>
<SectionFooterContent className={className}>{footer}</SectionFooterContent>
</SectionFooter>
) : null}
</Section>
);
}
@@ -413,15 +413,13 @@ describe('python code sample generator', () => {
},
body: {
key: 'value',
truethy: true,
falsey: false,
},
};
const output = generator?.generate(input);
expect(output).toBe(
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({"key":"value","truethy":True,"falsey":False})\n)\n\ndata = response.json()'
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data={"key":"value"}\n)\n\ndata = response.json()'
);
});
+54 -67
View File
@@ -3,7 +3,6 @@ import {
isFormData,
isFormUrlEncoded,
isGraphQL,
isJSON,
isPDF,
isPlainObject,
isText,
@@ -27,52 +26,6 @@ export interface CodeSampleGenerator {
}
export const codeSampleGenerators: CodeSampleGenerator[] = [
{
id: 'http',
label: 'HTTP',
syntax: 'bash',
generate: ({ method, url, headers = {}, body }: CodeSampleInput) => {
const { host, path } = parseHostAndPath(url);
if (body) {
// if we had a body add a content length header
const bodyContent = body ? stringifyOpenAPI(body) : '';
// handle unicode chars with a text encoder
const encoder = new TextEncoder();
const bodyString = BodyGenerators.getHTTPBody(body, headers);
if (bodyString) {
body = bodyString;
}
headers = {
...headers,
'Content-Length': encoder.encode(bodyContent).length.toString(),
};
}
if (!headers.hasOwnProperty('Accept')) {
headers.Accept = '*/*';
}
const headerString = headers
? `${Object.entries(headers)
.map(([key, value]) =>
key.toLowerCase() !== 'host' ? `${key}: ${value}` : ''
)
.join('\n')}\n`
: '';
const bodyString = body ? `\n${body}` : '';
const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
Host: ${host}
${headerString}${bodyString}`;
return httpRequest;
},
},
{
id: 'curl',
label: 'cURL',
@@ -174,14 +127,11 @@ ${headerString}${bodyString}`;
code += indent(`headers=${stringifyOpenAPI(headers)},\n`, 4);
}
const contentType = headers?.['Content-Type'];
if (body) {
if (body === 'files') {
code += indent(`files=${body}\n`, 4);
} else if (isJSON(contentType)) {
code += indent(`data=json.dumps(${body})\n`, 4);
} else {
code += indent(`data=${body}\n`, 4);
code += indent(`data=${stringifyOpenAPI(body)}\n`, 4);
}
}
@@ -190,6 +140,52 @@ ${headerString}${bodyString}`;
return code;
},
},
{
id: 'http',
label: 'HTTP',
syntax: 'bash',
generate: ({ method, url, headers = {}, body }: CodeSampleInput) => {
const { host, path } = parseHostAndPath(url);
if (body) {
// if we had a body add a content length header
const bodyContent = body ? stringifyOpenAPI(body) : '';
// handle unicode chars with a text encoder
const encoder = new TextEncoder();
const bodyString = BodyGenerators.getHTTPBody(body, headers);
if (bodyString) {
body = bodyString;
}
headers = {
...headers,
'Content-Length': encoder.encode(bodyContent).length.toString(),
};
}
if (!headers.hasOwnProperty('Accept')) {
headers.Accept = '*/*';
}
const headerString = headers
? `${Object.entries(headers)
.map(([key, value]) =>
key.toLowerCase() !== 'host' ? `${key}: ${value}` : ''
)
.join('\n')}\n`
: '';
const bodyString = body ? `\n${body}` : '';
const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
Host: ${host}
${headerString}${bodyString}`;
return httpRequest;
},
},
];
function indent(code: string, spaces: number) {
@@ -347,27 +343,18 @@ const BodyGenerators = {
}
code += '}\n\n';
body = 'files';
} else if (isPDF(contentType)) {
}
if (isPDF(contentType)) {
code += 'files = {\n';
code += `${indent(`"file": "${body}",`, 4)}\n`;
code += '}\n\n';
body = 'files';
} else if (isXML(contentType)) {
}
if (isXML(contentType)) {
// Convert JSON to XML if needed
body = JSON.stringify(convertBodyToXML(body));
} else {
body = stringifyOpenAPI(body, (_key, value) => {
switch (value) {
case true:
return '$$__TRUE__$$';
case false:
return '$$__FALSE__$$';
default:
return value;
}
})
.replaceAll('"$$__TRUE__$$"', 'True')
.replaceAll('"$$__FALSE__$$"', 'False');
body = convertBodyToXML(body);
}
return { body, code, headers };
@@ -1,31 +0,0 @@
import { OpenAPISpec } from '../OpenAPISpec';
import { type OpenAPIContext, getOpenAPIClientContext } from '../context';
import { t } from '../translate';
import type { OpenAPIOperationData, OpenAPIWebhookData } from '../types';
import { OpenAPIOperationDescription } from './OpenAPIOperationDescription';
export function OpenAPIColumnSpec(props: {
data: OpenAPIOperationData | OpenAPIWebhookData;
context: OpenAPIContext;
}) {
const { data, context } = props;
const { operation } = data;
const clientContext = getOpenAPIClientContext(context);
return (
<div className="openapi-column-spec">
{operation['x-deprecated-sunset'] ? (
<div className="openapi-deprecated-sunset openapi-description openapi-markdown">
{t(context.translation, 'deprecated_and_sunset_on', [
<span key="date" className="openapi-deprecated-sunset-date">
{operation['x-deprecated-sunset']}
</span>,
])}
</div>
) : null}
<OpenAPIOperationDescription operation={operation} context={context} />
<OpenAPISpec data={data} context={clientContext} />
</div>
);
}
@@ -1,31 +0,0 @@
import type { OpenAPICustomOperationProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
import { Markdown } from '../Markdown';
import type { OpenAPIContext } from '../context';
import { resolveDescription } from '../utils';
export function OpenAPIOperationDescription(props: {
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
context: OpenAPIContext;
}) {
const { operation } = props;
if (operation['x-gitbook-description-document']) {
return (
<div className="openapi-intro">
{props.context.renderDocument({
document: operation['x-gitbook-description-document'],
})}
</div>
);
}
const description = resolveDescription(operation);
if (!description) {
return null;
}
return (
<div className="openapi-intro">
<Markdown className="openapi-description" source={description} />
</div>
);
}
@@ -1,23 +0,0 @@
import type { OpenAPIStability as OpenAPIStabilityType } from '@gitbook/openapi-parser';
const stabilityEnum: Record<OpenAPIStabilityType, string> = {
experimental: 'Experimental',
alpha: 'Alpha',
beta: 'Beta',
} as const;
export function OpenAPIStability(props: { stability: OpenAPIStabilityType }) {
const { stability } = props;
const foundStability = stabilityEnum[stability];
if (!foundStability) {
return null;
}
return (
<div className={`openapi-stability openapi-stability-${foundStability.toLowerCase()}`}>
{foundStability}
</div>
);
}
@@ -1,45 +0,0 @@
import { OpenAPIPath } from '../OpenAPIPath';
import type { OpenAPIContext } from '../context';
import type { OpenAPIOperationData, OpenAPIWebhookData } from '../types';
import { OpenAPIStability } from './OpenAPIStability';
export function OpenAPISummary(props: {
data: OpenAPIOperationData | OpenAPIWebhookData;
context: OpenAPIContext;
}) {
const { data, context } = props;
const { operation } = data;
const title = (() => {
if (operation.summary) {
return operation.summary;
}
if ('name' in data) {
return data.name;
}
return undefined;
})();
return (
<div className="openapi-summary" id={operation.summary ? undefined : context.id}>
{(operation.deprecated || operation['x-stability']) && (
<div className="openapi-summary-tags">
{operation.deprecated && <div className="openapi-deprecated">Deprecated</div>}
{operation['x-stability'] && (
<OpenAPIStability stability={operation['x-stability']} />
)}
</div>
)}
{title
? context.renderHeading({
deprecated: operation.deprecated ?? false,
stability: operation['x-stability'],
title,
})
: null}
{'path' in data ? <OpenAPIPath data={data} context={context} /> : null}
</div>
);
}
+2 -37
View File
@@ -1,11 +1,4 @@
import { type Translation, type TranslationLocale, translations } from './translations';
export interface OpenAPIClientContext {
/**
* The translation language to use.
*/
translation: Translation;
/**
* Icons used in the block.
*/
@@ -30,14 +23,9 @@ export interface OpenAPIClientContext {
* Optional id attached to the heading and used as an anchor.
*/
id?: string;
/**
* Mark the context as a client context.
*/
$$isClientContext$$: true;
}
export interface OpenAPIContext extends Omit<OpenAPIClientContext, '$$isClientContext$$'> {
export interface OpenAPIContext extends OpenAPIClientContext {
/**
* Render a code block.
*/
@@ -63,37 +51,14 @@ export interface OpenAPIContext extends Omit<OpenAPIClientContext, '$$isClientCo
specUrl: string;
}
export type OpenAPIUniversalContext = OpenAPIClientContext | OpenAPIContext;
export interface OpenAPIContextInput extends Omit<OpenAPIContext, 'translation'> {
/**
* The translation language to use.
* @default 'en'
*/
locale?: TranslationLocale | undefined;
}
/**
* Resolve OpenAPI context from the input.
*/
export function resolveOpenAPIContext(context: OpenAPIContextInput): OpenAPIContext {
const { locale, ...rest } = context;
return {
...rest,
translation: translations[locale ?? 'en'],
};
}
/**
* Get the client context from the OpenAPI context.
*/
export function getOpenAPIClientContext(context: OpenAPIUniversalContext): OpenAPIClientContext {
export function getOpenAPIClientContext(context: OpenAPIContext): OpenAPIClientContext {
return {
translation: context.translation,
icons: context.icons,
defaultInteractiveOpened: context.defaultInteractiveOpened,
blockKey: context.blockKey,
id: context.id,
$$isClientContext$$: true,
};
}
@@ -1,3 +1,5 @@
'use client';
import { createStore } from 'zustand';
type Key = string | number;
@@ -8,7 +10,7 @@ type State = {
type Actions = { setKey: (key: Key | null) => void };
export type Store = State & Actions;
type Store = State & Actions;
const createStateStore = (initial?: Key) => {
return createStore<Store>()((set) => ({
+1 -5
View File
@@ -1,9 +1,5 @@
export * from './schemas';
export * from './OpenAPIOperation';
export * from './OpenAPIWebhook';
export * from './OpenAPIOperationContext';
export * from './resolveOpenAPIOperation';
export * from './resolveOpenAPIWebhook';
export type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
export type { OpenAPIContextInput } from './context';
export { checkIsValidLocale } from './translations';
export type { OpenAPIOperationData, OpenAPIContext } from './types';
@@ -1,99 +0,0 @@
import { fromJSON, toJSON } from 'flatted';
import type {
Filesystem,
OpenAPIV3,
OpenAPIV3_1,
OpenAPIV3xDocument,
} from '@gitbook/openapi-parser';
import { dereferenceFilesystem } from './dereference';
import type { OpenAPIWebhookData } from './types';
export { fromJSON, toJSON };
/**
* Resolve an OpenAPI webhook in a file and compile it to a more usable format.
*/
export async function resolveOpenAPIWebhook(
filesystem: Filesystem<OpenAPIV3xDocument>,
webhookDescriptor: {
name: string;
method: string;
}
): Promise<OpenAPIWebhookData | null> {
const { name, method } = webhookDescriptor;
const schema = await dereferenceFilesystem(filesystem);
let operation = getWebhookByNameAndMethod(schema, name, method);
if (!operation) {
return null;
}
// Resolve common parameters
const commonParameters = getPathObjectParameter(schema, name);
if (commonParameters) {
operation = {
...operation,
parameters: [...commonParameters, ...(operation.parameters ?? [])],
};
}
const servers = 'servers' in schema ? (schema.servers ?? []) : [];
return {
servers,
operation,
method,
name,
};
}
/**
* Get a path object from its path.
*/
function getPathObject(
schema: OpenAPIV3.Document | OpenAPIV3_1.Document,
name: string
): OpenAPIV3.PathItemObject | OpenAPIV3_1.PathItemObject | null {
if (schema.webhooks?.[name]) {
return schema.webhooks[name];
}
return null;
}
/**
* Resolve parameters from a path in an OpenAPI schema.
*/
function getPathObjectParameter(
schema: OpenAPIV3.Document | OpenAPIV3_1.Document,
path: string
):
| (OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject)[]
| (OpenAPIV3.ParameterObject | OpenAPIV3_1.ReferenceObject)[]
| null {
const pathObject = getPathObject(schema, path);
if (pathObject?.parameters) {
return pathObject.parameters;
}
return null;
}
/**
* Get an operation by its path and method.
*/
function getWebhookByNameAndMethod(
schema: OpenAPIV3.Document | OpenAPIV3_1.Document,
name: string,
method: string
): OpenAPIV3.OperationObject | null {
// Types are buffy for OpenAPIV3_1.OperationObject, so we use v3
const pathObject = getPathObject(schema, name);
if (!pathObject) {
return null;
}
const normalizedMethod = method.toLowerCase();
if (!pathObject[normalizedMethod]) {
return null;
}
return pathObject[normalizedMethod];
}
@@ -1,16 +1,11 @@
import type { OpenAPISchema } from '@gitbook/openapi-parser';
import clsx from 'clsx';
import { OpenAPIDisclosure } from '../OpenAPIDisclosure';
import { OpenAPIExample } from '../OpenAPIExample';
import { OpenAPIDisclosureGroup } from '../OpenAPIDisclosureGroup';
import { OpenAPIExample, getExampleFromSchema } from '../OpenAPIExample';
import { OpenAPIRootSchema } from '../OpenAPISchemaServer';
import { Section, SectionBody, StaticSection } from '../StaticSection';
import {
type OpenAPIContextInput,
getOpenAPIClientContext,
resolveOpenAPIContext,
} from '../context';
import { t } from '../translate';
import { getExampleFromSchema } from '../util/example';
import { getOpenAPIClientContext } from '../context';
import type { OpenAPIContext } from '../types';
/**
* OpenAPI Schemas component.
@@ -18,13 +13,13 @@ import { getExampleFromSchema } from '../util/example';
export function OpenAPISchemas(props: {
className?: string;
schemas: OpenAPISchema[];
context: OpenAPIContextInput;
context: OpenAPIContext;
/**
* Whether to show the schema directly if there is only one.
*/
grouped?: boolean;
}) {
const { schemas, context: contextInput, grouped, className } = props;
const { schemas, context, grouped, className } = props;
const firstSchema = schemas[0];
@@ -32,7 +27,6 @@ export function OpenAPISchemas(props: {
return null;
}
const context = resolveOpenAPIContext(contextInput);
const clientContext = getOpenAPIClientContext(context);
// If there is only one model and we are not grouping, we show it directly.
@@ -43,16 +37,11 @@ export function OpenAPISchemas(props: {
<div className="openapi-summary" id={context.id}>
{context.renderHeading({
title,
deprecated: Boolean(firstSchema.schema.deprecated),
stability: firstSchema.schema['x-stability'],
})}
</div>
<div className="openapi-columns">
<div className="openapi-column-spec">
<StaticSection
className="openapi-parameters"
header={t(context.translation, 'attributes')}
>
<StaticSection className="openapi-parameters" header="Attributes">
<OpenAPIRootSchema
schema={firstSchema.schema}
context={clientContext}
@@ -83,22 +72,33 @@ export function OpenAPISchemas(props: {
// If there are multiple schemas, we use a disclosure group to show them all.
return (
<div className={clsx('openapi-schemas', className)}>
{schemas.map(({ name, schema }) => {
return (
<OpenAPIDisclosure
className="openapi-schemas-disclosure"
key={name}
icon={context.icons.chevronRight}
label={name}
>
<Section className="openapi-section-schemas">
<SectionBody>
<OpenAPIRootSchema schema={schema} context={clientContext} />
</SectionBody>
</Section>
</OpenAPIDisclosure>
);
})}
<OpenAPIDisclosureGroup
allowsMultipleExpanded
icon={context.icons.chevronRight}
groups={schemas.map(({ name, schema }) => ({
id: name,
label: (
<div className="openapi-response-tab-content" key={`model-${name}`}>
<span className="openapi-response-statuscode">{name}</span>
</div>
),
tabs: [
{
id: 'model',
body: (
<Section className="openapi-section-schemas">
<SectionBody>
<OpenAPIRootSchema
schema={schema}
context={clientContext}
/>
</SectionBody>
</Section>
),
},
],
}))}
/>
</div>
);
}
+3 -11
View File
@@ -1,25 +1,17 @@
/**
* Stringify an OpenAPI object. Same API as JSON.stringify.
*/
export function stringifyOpenAPI(
value: any,
replacer?: ((this: any, key: string, value: any) => any) | null,
space?: string | number
): string {
export function stringifyOpenAPI(body: unknown, _?: null, indent?: number): string {
return JSON.stringify(
value,
body,
(key, value) => {
// Ignore internal keys
if (key.startsWith('x-gitbook-')) {
return undefined;
}
if (replacer) {
return replacer(key, value);
}
return value;
},
space
indent
);
}
-80
View File
@@ -1,80 +0,0 @@
import React from 'react';
import type { Translation, TranslationKey } from './translations';
/**
* Translate a string.
*/
export function t(
translation: Translation,
id: TranslationKey,
...args: React.ReactNode[]
): React.ReactNode {
const string = translation[id];
if (!string) {
throw new Error(`Translation not found for "${id}"`);
}
// Now we are going to replace the arguments
// but we want to return a string as long as it's possible
// (eg. if there isn't any argument that is a ReactNode)
const parts: React.ReactNode[] = [];
let currentStringToReplace: string = string;
args.forEach((arg, i) => {
if (typeof arg === 'string') {
currentStringToReplace = currentStringToReplace.replace(`\${${i + 1}}`, arg);
} else {
const [partToPush, partToReplace] = currentStringToReplace.split(`\${${i + 1}}`);
if (partToPush === undefined || partToReplace === undefined) {
throw new Error(`Invalid translation "${id}"`);
}
parts.push(<React.Fragment key={`string-${i}`}>{partToPush}</React.Fragment>);
parts.push(<React.Fragment key={`arg-${i}`}>{arg}</React.Fragment>);
currentStringToReplace = partToReplace;
}
});
if (!parts.length) {
return currentStringToReplace;
}
return (
<>
{parts}
{currentStringToReplace}
</>
);
}
/**
* Version of `t` that returns a string.
*/
export function tString(
translation: Translation,
id: TranslationKey,
...args: React.ReactNode[]
): string {
const result = t(translation, id, ...args);
return reactToString(result);
}
function reactToString(el: React.ReactNode): string {
if (typeof el === 'string' || typeof el === 'number' || typeof el === 'boolean') {
return `${el}`;
}
if (el === null || el === undefined) {
return '';
}
if (Array.isArray(el)) {
return el.map(reactToString).join('');
}
if (typeof el === 'object' && 'props' in el) {
return el.props.children.map(reactToString).join('');
}
throw new Error(`Unsupported type ${typeof el}`);
}
@@ -1,37 +0,0 @@
export const de = {
required: 'Erforderlich',
deprecated: 'Veraltet',
deprecated_and_sunset_on: 'Diese Operation ist veraltet und wird am ${1} eingestellt.',
stability_experimental: 'Experimentell',
stability_alpha: 'Alpha',
stability_beta: 'Beta',
copy_to_clipboard: 'In die Zwischenablage kopieren',
copied: 'Kopiert',
no_content: 'Kein Inhalt',
unresolved_reference: 'Nicht aufgelöste Referenz',
circular_reference: 'Zirkuläre Referenz',
read_only: 'Nur lesen',
write_only: 'Nur schreiben',
optional: 'Optional',
min: 'Min',
max: 'Max',
nullable: 'Nullfähig',
body: 'Rumpf',
payload: 'Nutzlast',
headers: 'Kopfzeilen',
authorizations: 'Autorisierungen',
responses: 'Antworten',
path_parameters: 'Pfadparameter',
query_parameters: 'Abfrageparameter',
header_parameters: 'Header-Parameter',
attributes: 'Attribute',
test_it: 'Teste es',
information: 'Information',
success: 'Erfolg',
redirect: 'Umleitung',
error: 'Fehler',
show: 'Anzeigen',
hide: 'Verstecken',
available_items: 'Verfügbare Elemente',
child_attributes: 'Unterattribute',
};
@@ -1,37 +0,0 @@
export const en = {
required: 'Required',
deprecated: 'Deprecated',
deprecated_and_sunset_on: 'This operation is deprecated and will be sunset on ${1}.',
stability_experimental: 'Experimental',
stability_alpha: 'Alpha',
stability_beta: 'Beta',
copy_to_clipboard: 'Copy to clipboard',
copied: 'Copied',
no_content: 'No content',
unresolved_reference: 'Unresolved reference',
circular_reference: 'Circular reference',
read_only: 'Read-only',
write_only: 'Write-only',
optional: 'Optional',
min: 'Min',
max: 'Max',
nullable: 'Nullable',
body: 'Body',
payload: 'Payload',
headers: 'Headers',
authorizations: 'Authorizations',
responses: 'Responses',
path_parameters: 'Path parameters',
query_parameters: 'Query parameters',
header_parameters: 'Header parameters',
attributes: 'Attributes',
test_it: 'Test it',
information: 'Information',
success: 'Success',
redirect: 'Redirect',
error: 'Error',
show: 'Show',
hide: 'Hide',
available_items: 'Available items',
child_attributes: 'Child attributes',
};
@@ -1,37 +0,0 @@
export const es = {
required: 'Requerido',
deprecated: 'Obsoleto',
deprecated_and_sunset_on: 'Esta operación está obsoleta y se retirará el ${1}.',
stability_experimental: 'Experimental',
stability_alpha: 'Alfa',
stability_beta: 'Beta',
copy_to_clipboard: 'Copiar al portapapeles',
copied: 'Copiado',
no_content: 'Sin contenido',
unresolved_reference: 'Referencia no resuelta',
circular_reference: 'Referencia circular',
read_only: 'Solo lectura',
write_only: 'Solo escritura',
optional: 'Opcional',
min: 'Mín',
max: 'Máx',
nullable: 'Nulo',
body: 'Cuerpo',
payload: 'Caga útil',
headers: 'Encabezados',
authorizations: 'Autorizaciones',
responses: 'Respuestas',
path_parameters: 'Parámetros de ruta',
query_parameters: 'Parámetros de consulta',
header_parameters: 'Parámetros de encabezado',
attributes: 'Atributos',
test_it: 'Pruébalo',
information: 'Información',
success: 'Éxito',
redirect: 'Redirección',
error: 'Error',
show: 'Mostrar',
hide: 'Ocultar',
available_items: 'Elementos disponibles',
child_attributes: 'Atributos secundarios',
};
@@ -1,37 +0,0 @@
export const fr = {
required: 'Requis',
deprecated: 'Obsolète',
deprecated_and_sunset_on: 'Cette opération est obsolète et sera supprimée le ${1}.',
stability_experimental: 'Expérimental',
stability_alpha: 'Alpha',
stability_beta: 'Bêta',
copy_to_clipboard: 'Copier dans le presse-papiers',
copied: 'Copié',
no_content: 'Aucun contenu',
unresolved_reference: 'Référence non résolue',
circular_reference: 'Référence circulaire',
read_only: 'Lecture seule',
write_only: 'Écriture seule',
optional: 'Optionnel',
min: 'Min',
max: 'Max',
nullable: 'Nullable',
body: 'Corps',
payload: 'Charge utile',
headers: 'En-têtes',
authorizations: 'Autorisations',
responses: 'Réponses',
path_parameters: 'Paramètres de chemin',
query_parameters: 'Paramètres de requête',
header_parameters: 'Paramètres den-tête',
attributes: 'Attributs',
test_it: 'Tester',
information: 'Information',
success: 'Succès',
redirect: 'Redirection',
error: 'Erreur',
show: 'Afficher',
hide: 'Masquer',
available_items: 'Éléments disponibles',
child_attributes: 'Attributs enfants',
};
@@ -1,33 +0,0 @@
import { de } from './de';
import { en } from './en';
import { es } from './es';
import { fr } from './fr';
import { ja } from './ja';
import { nl } from './nl';
import { no } from './no';
import { pt_br } from './pt-br';
import type { Translation } from './types';
import { zh } from './zh';
export * from './types';
export const translations = {
en,
de,
es,
fr,
ja,
nl,
no,
'pt-br': pt_br,
zh,
} satisfies Record<string, Translation>;
export type TranslationLocale = keyof typeof translations;
/**
* Check if the locale is valid.
*/
export function checkIsValidLocale(locale: string): locale is TranslationLocale {
return Object.prototype.hasOwnProperty.call(translations, locale);
}
@@ -1,37 +0,0 @@
export const ja = {
required: '必須',
deprecated: '非推奨',
deprecated_and_sunset_on: 'この操作は非推奨であり、${1}に廃止されます。',
stability_experimental: '実験的',
stability_alpha: 'アルファ',
stability_beta: 'ベータ',
copy_to_clipboard: 'クリップボードにコピー',
copied: 'コピー済み',
no_content: 'コンテンツなし',
unresolved_reference: '未解決の参照',
circular_reference: '循環参照',
read_only: '読み取り専用',
write_only: '書き込み専用',
optional: 'オプション',
min: '最小',
max: '最大',
nullable: 'ヌル許容',
body: '本文',
payload: 'ペイロード',
headers: 'ヘッダー',
authorizations: '認可',
responses: 'レスポンス',
path_parameters: 'パスパラメータ',
query_parameters: 'クエリパラメータ',
header_parameters: 'ヘッダーパラメータ',
attributes: '属性',
test_it: 'テストする',
information: '情報',
success: '成功',
redirect: 'リダイレクト',
error: 'エラー',
show: '表示',
hide: '非表示',
available_items: '利用可能なアイテム',
child_attributes: '子属性',
};
@@ -1,37 +0,0 @@
export const nl = {
required: 'Vereist',
deprecated: 'Verouderd',
deprecated_and_sunset_on: 'Deze bewerking is verouderd en wordt op ${1} beëindigd.',
stability_experimental: 'Experimenteel',
stability_alpha: 'Alfa',
stability_beta: 'Bèta',
copy_to_clipboard: 'Kopiëren naar klembord',
copied: 'Gekopieerd',
no_content: 'Geen inhoud',
unresolved_reference: 'Onopgeloste verwijzing',
circular_reference: 'Circulaire verwijzing',
read_only: 'Alleen lezen',
write_only: 'Alleen schrijven',
optional: 'Optioneel',
min: 'Min',
max: 'Max',
nullable: 'Null toegestaan',
body: 'Body',
payload: 'Payload',
headers: 'Headers',
authorizations: 'Autorisaties',
responses: 'Reacties',
path_parameters: 'Padparameters',
query_parameters: 'Queryparameters',
header_parameters: 'Headerparameters',
attributes: 'Attributen',
test_it: 'Test het',
information: 'Informatie',
success: 'Succes',
redirect: 'Omleiding',
error: 'Fout',
show: 'Toon',
hide: 'Verbergen',
available_items: 'Beschikbare items',
child_attributes: 'Kindattributen',
};
@@ -1,37 +0,0 @@
export const no = {
required: 'Påkrevd',
deprecated: 'Foreldet',
deprecated_and_sunset_on: 'Denne operasjonen er foreldet og vil bli avviklet den ${1}.',
stability_experimental: 'Eksperimentell',
stability_alpha: 'Alfa',
stability_beta: 'Beta',
copy_to_clipboard: 'Kopier til utklippstavle',
copied: 'Kopiert',
no_content: 'Ingen innhold',
unresolved_reference: 'Uavklart referanse',
circular_reference: 'Sirkulær referanse',
read_only: 'Skrivebeskyttet',
write_only: 'Kun skriving',
optional: 'Valgfri',
min: 'Min',
max: 'Maks',
nullable: 'Kan være null',
body: 'Brødtekst',
payload: 'Nyttelast',
headers: 'Overskrifter',
authorizations: 'Autorisasjoner',
responses: 'Responser',
path_parameters: 'Sti-parametere',
query_parameters: 'Forespørselsparametere',
header_parameters: 'Header-parametere',
attributes: 'Attributter',
test_it: 'Test det',
information: 'Informasjon',
success: 'Suksess',
redirect: 'Viderekobling',
error: 'Feil',
show: 'Vis',
hide: 'Skjul',
available_items: 'Tilgjengelige elementer',
child_attributes: 'Barneattributter',
};
@@ -1,37 +0,0 @@
export const pt_br = {
required: 'Obrigatório',
deprecated: 'Obsoleto',
deprecated_and_sunset_on: 'Esta operação está obsoleta e será descontinuada em ${1}.',
stability_experimental: 'Experimental',
stability_alpha: 'Alfa',
stability_beta: 'Beta',
copy_to_clipboard: 'Copiar para a área de transferência',
copied: 'Copiado',
no_content: 'Sem conteúdo',
unresolved_reference: 'Referência não resolvida',
circular_reference: 'Referência circular',
read_only: 'Somente leitura',
write_only: 'Somente escrita',
optional: 'Opcional',
min: 'Mín',
max: 'Máx',
nullable: 'Nulo',
body: 'Corpo',
payload: 'Carga útil',
headers: 'Cabeçalhos',
authorizations: 'Autorizações',
responses: 'Respostas',
path_parameters: 'Parâmetros de rota',
query_parameters: 'Parâmetros de consulta',
header_parameters: 'Parâmetros de cabeçalho',
attributes: 'Atributos',
test_it: 'Testar',
information: 'Informação',
success: 'Sucesso',
redirect: 'Redirecionamento',
error: 'Erro',
show: 'Mostrar',
hide: 'Ocultar',
available_items: 'Itens disponíveis',
child_attributes: 'Atributos filhos',
};
@@ -1,7 +0,0 @@
import type { en } from './en';
export type TranslationKey = keyof typeof en;
export type Translation = {
[key in TranslationKey]: string;
};
@@ -1,37 +0,0 @@
export const zh = {
required: '必填',
deprecated: '已弃用',
deprecated_and_sunset_on: '此操作已弃用,将于 ${1} 停止使用。',
stability_experimental: '实验性',
stability_alpha: 'Alpha',
stability_beta: 'Beta',
copy_to_clipboard: '复制到剪贴板',
copied: '已复制',
no_content: '无内容',
unresolved_reference: '未解析的引用',
circular_reference: '循环引用',
read_only: '只读',
write_only: '只写',
optional: '可选',
min: '最小值',
max: '最大值',
nullable: '可为 null',
body: '请求体',
payload: '有效载荷',
headers: '头部信息',
authorizations: '授权',
responses: '响应',
path_parameters: '路径参数',
query_parameters: '查询参数',
header_parameters: '头参数',
attributes: '属性',
test_it: '测试一下',
information: '信息',
success: '成功',
redirect: '重定向',
error: '错误',
show: '显示',
hide: '隐藏',
available_items: '可用项',
child_attributes: '子属性',
};
+53 -11
View File
@@ -4,6 +4,59 @@ import type {
OpenAPIV3,
} from '@gitbook/openapi-parser';
export interface OpenAPIClientContext {
/**
* Icons used in the block.
*/
icons: {
chevronDown: React.ReactNode;
chevronRight: React.ReactNode;
plus: React.ReactNode;
};
/**
* Force all sections to be opened by default.
* @default false
*/
defaultInteractiveOpened?: boolean;
/**
* The key of the block
*/
blockKey?: string;
/**
* Optional id attached to the heading and used as an anchor.
*/
id?: string;
}
export interface OpenAPIContext extends OpenAPIClientContext {
/**
* Render a code block.
*/
renderCodeBlock: (props: { code: string; syntax: string }) => React.ReactNode;
/**
* Render the heading of the operation.
*/
renderHeading: (props: {
deprecated?: boolean;
title: string;
stability?: string;
}) => React.ReactNode;
/**
* Render the document of the operation.
*/
renderDocument: (props: { document: object }) => React.ReactNode;
/**
* Specification URL.
*/
specUrl: string;
}
export type OpenAPISecurityWithRequired = OpenAPIV3.SecuritySchemeObject & { required?: boolean };
export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
@@ -19,14 +72,3 @@ export interface OpenAPIOperationData extends OpenAPICustomSpecProperties {
/** Securities that should be used for this operation */
securities: [string, OpenAPISecurityWithRequired][];
}
export interface OpenAPIWebhookData extends OpenAPICustomSpecProperties {
name: string;
method: string;
/** Servers to be used for this operation */
servers: OpenAPIV3.ServerObject[];
/** Spec of the webhook */
operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
}
-129
View File
@@ -1,129 +0,0 @@
import type { OpenAPIV3 } from '@gitbook/openapi-parser';
import { OpenAPIExample } from '../OpenAPIExample';
import type { OpenAPIContext } from '../context';
import { generateSchemaExample } from '../generateSchemaExample';
import { tString } from '../translate';
import { checkIsReference } from '../utils';
/**
* Generate an example from a reference object.
*/
export function getExampleFromReference(
ref: OpenAPIV3.ReferenceObject,
context: OpenAPIContext
): OpenAPIV3.ExampleObject {
return {
summary: tString(context.translation, 'unresolved_reference'),
value: { $ref: ref.$ref },
};
}
/**
* Get examples from a media type object.
*/
export function getExamplesFromMediaTypeObject(args: {
mediaType: string;
mediaTypeObject: OpenAPIV3.MediaTypeObject;
context: OpenAPIContext;
}): { key: string; example: OpenAPIV3.ExampleObject }[] {
const { mediaTypeObject, mediaType, context } = args;
if (mediaTypeObject.examples) {
return Object.entries(mediaTypeObject.examples).map(([key, example]) => {
return {
key,
example: checkIsReference(example)
? getExampleFromReference(example, context)
: example,
};
});
}
if (mediaTypeObject.example) {
return [{ key: 'default', example: { value: mediaTypeObject.example } }];
}
if (mediaTypeObject.schema) {
if (mediaType === 'application/xml') {
// @TODO normally we should use the name of the schema but we don't have it
// fix it when we got the reference name
const root = mediaTypeObject.schema.xml?.name ?? 'object';
return [
{
key: 'default',
example: {
value: {
[root]: generateSchemaExample(mediaTypeObject.schema, {
xml: mediaType === 'application/xml',
mode: 'read',
}),
},
},
},
];
}
return [
{
key: 'default',
example: {
value: generateSchemaExample(mediaTypeObject.schema, {
mode: 'read',
}),
},
},
];
}
return [];
}
/**
* Get example from a schema object.
*/
export function getExampleFromSchema(args: {
schema: OpenAPIV3.SchemaObject;
}): OpenAPIV3.ExampleObject {
const { schema } = args;
if (schema.example) {
return { value: schema.example };
}
return { value: generateSchemaExample(schema, { mode: 'read' }) };
}
/**
* Get the examples from a media type object.
*/
export function getExamples(props: {
mediaTypeObject: OpenAPIV3.MediaTypeObject;
mediaType: string;
context: OpenAPIContext;
}) {
const { mediaTypeObject, mediaType, context } = props;
const examples = getExamplesFromMediaTypeObject({ mediaTypeObject, mediaType, context });
const syntax = getSyntaxFromMediaType(mediaType);
return examples.map((example) => {
return {
key: example.key,
label: example.example.summary || example.key,
body: (
<OpenAPIExample example={example.example} context={props.context} syntax={syntax} />
),
};
});
}
/**
* Get the syntax from a media type.
*/
function getSyntaxFromMediaType(mediaType: string): string {
if (mediaType.includes('json')) {
return 'json';
}
if (mediaType === 'application/xml') {
return 'xml';
}
return 'text';
}
-67
View File
@@ -1,7 +1,5 @@
import type { AnyObject, OpenAPIV3, OpenAPIV3_1 } from '@gitbook/openapi-parser';
import type { OpenAPIUniversalContext } from './context';
import { stringifyOpenAPI } from './stringifyOpenAPI';
import { tString } from './translate';
export function checkIsReference(input: unknown): input is OpenAPIV3.ReferenceObject {
return typeof input === 'object' && !!input && '$ref' in input;
@@ -151,68 +149,3 @@ function shouldDisplayExample(schema: OpenAPIV3.SchemaObject): boolean {
Object.keys(schema.example).length > 0)
);
}
/**
* Get the class name for a status code.
* 1xx: informational
* 2xx: success
* 3xx: redirect
* 4xx, 5xx: error
*/
export function getStatusCodeClassName(statusCode: number | string): string {
const category = getStatusCodeCategory(statusCode);
switch (category) {
case 1:
return 'informational';
case 2:
return 'success';
case 3:
return 'redirect';
case 4:
case 5:
return 'error';
default:
return 'unknown';
}
}
/**
* Get a default label for a status code.
* This is used when there is no label provided in the OpenAPI spec.
* 1xx: Information
* 2xx: Success
* 3xx: Redirect
* 4xx, 5xx: Error
*/
export function getStatusCodeDefaultLabel(
statusCode: number | string,
context: OpenAPIUniversalContext
): string {
const category = getStatusCodeCategory(statusCode);
switch (category) {
case 1:
return tString(context.translation, 'information');
case 2:
return tString(context.translation, 'success');
case 3:
return tString(context.translation, 'redirect');
case 4:
case 5:
return tString(context.translation, 'error');
default:
return '';
}
}
function getStatusCodeCategory(statusCode: number | string): number | string {
const code = typeof statusCode === 'string' ? Number.parseInt(statusCode, 10) : statusCode;
if (Number.isNaN(code) || code < 100 || code >= 600) {
return 'unknown';
}
// Determine the category of the status code based on the first digit
const category = Math.floor(code / 100);
return category;
}