From cf9b520015c4c8499b8051c660313394f7fe844e Mon Sep 17 00:00:00 2001 From: Vitor-Azion Date: Wed, 18 Jun 2025 18:05:21 -0300 Subject: [PATCH 1/3] chore: update package.json to specify pnpm as package manager and add Azion new provider --- package.json | 3 +- pages/_meta.json | 3 +- pages/azion/_meta.json | 12 + pages/azion/bindings.mdx | 68 ++ pages/azion/caching.mdx | 760 ++++++++++++++++++ pages/azion/examples.mdx | 23 + pages/azion/former-releases/0.2/_meta.json | 7 + pages/azion/former-releases/0.2/bindings.mdx | 63 ++ pages/azion/former-releases/0.2/caching.mdx | 36 + pages/azion/former-releases/0.2/examples.mdx | 22 + .../azion/former-releases/0.2/get-started.mdx | 140 ++++ pages/azion/former-releases/0.2/index.mdx | 65 ++ pages/azion/former-releases/0.3/_meta.json | 7 + pages/azion/former-releases/0.3/bindings.mdx | 63 ++ pages/azion/former-releases/0.3/caching.mdx | 47 ++ pages/azion/former-releases/0.3/examples.mdx | 24 + .../azion/former-releases/0.3/get-started.mdx | 194 +++++ pages/azion/former-releases/0.3/index.mdx | 71 ++ pages/azion/former-releases/0.5/_meta.json | 7 + pages/azion/former-releases/0.5/bindings.mdx | 74 ++ pages/azion/former-releases/0.5/caching.mdx | 156 ++++ pages/azion/former-releases/0.5/examples.mdx | 24 + .../azion/former-releases/0.5/get-started.mdx | 190 +++++ pages/azion/former-releases/0.5/index.mdx | 73 ++ pages/azion/former-releases/0.6/_meta.json | 7 + pages/azion/former-releases/0.6/bindings.mdx | 74 ++ pages/azion/former-releases/0.6/caching.mdx | 418 ++++++++++ pages/azion/former-releases/0.6/examples.mdx | 24 + .../azion/former-releases/0.6/get-started.mdx | 197 +++++ pages/azion/former-releases/_meta.json | 10 + .../migrate-from-0.2-to-0.3.mdx | 82 ++ .../migrate-from-0.3-to-0.4.mdx | 38 + .../migrate-from-0.4-to-0.5.mdx | 3 + .../migrate-from-0.5-to-0.6.mdx | 47 ++ pages/azion/get-started.mdx | 218 +++++ pages/azion/howtos/_meta.json | 10 + pages/azion/howtos/custom-worker.mdx | 43 + pages/azion/howtos/db.mdx | 229 ++++++ pages/azion/howtos/dev-deploy.mdx | 88 ++ pages/azion/howtos/env-vars.mdx | 44 + pages/azion/howtos/image.mdx | 71 ++ pages/azion/howtos/keep_names.mdx | 38 + pages/azion/howtos/stripeAPI.mdx | 14 + pages/azion/howtos/workerd.mdx | 49 ++ pages/azion/index.mdx | 72 ++ pages/azion/known-issues.mdx | 31 + pages/azion/troubleshooting.mdx | 127 +++ 47 files changed, 4064 insertions(+), 2 deletions(-) create mode 100644 pages/azion/_meta.json create mode 100644 pages/azion/bindings.mdx create mode 100644 pages/azion/caching.mdx create mode 100644 pages/azion/examples.mdx create mode 100644 pages/azion/former-releases/0.2/_meta.json create mode 100644 pages/azion/former-releases/0.2/bindings.mdx create mode 100644 pages/azion/former-releases/0.2/caching.mdx create mode 100644 pages/azion/former-releases/0.2/examples.mdx create mode 100644 pages/azion/former-releases/0.2/get-started.mdx create mode 100644 pages/azion/former-releases/0.2/index.mdx create mode 100644 pages/azion/former-releases/0.3/_meta.json create mode 100644 pages/azion/former-releases/0.3/bindings.mdx create mode 100644 pages/azion/former-releases/0.3/caching.mdx create mode 100644 pages/azion/former-releases/0.3/examples.mdx create mode 100644 pages/azion/former-releases/0.3/get-started.mdx create mode 100644 pages/azion/former-releases/0.3/index.mdx create mode 100644 pages/azion/former-releases/0.5/_meta.json create mode 100644 pages/azion/former-releases/0.5/bindings.mdx create mode 100644 pages/azion/former-releases/0.5/caching.mdx create mode 100644 pages/azion/former-releases/0.5/examples.mdx create mode 100644 pages/azion/former-releases/0.5/get-started.mdx create mode 100644 pages/azion/former-releases/0.5/index.mdx create mode 100644 pages/azion/former-releases/0.6/_meta.json create mode 100644 pages/azion/former-releases/0.6/bindings.mdx create mode 100644 pages/azion/former-releases/0.6/caching.mdx create mode 100644 pages/azion/former-releases/0.6/examples.mdx create mode 100644 pages/azion/former-releases/0.6/get-started.mdx create mode 100644 pages/azion/former-releases/_meta.json create mode 100644 pages/azion/former-releases/migrate-from-0.2-to-0.3.mdx create mode 100644 pages/azion/former-releases/migrate-from-0.3-to-0.4.mdx create mode 100644 pages/azion/former-releases/migrate-from-0.4-to-0.5.mdx create mode 100644 pages/azion/former-releases/migrate-from-0.5-to-0.6.mdx create mode 100644 pages/azion/get-started.mdx create mode 100644 pages/azion/howtos/_meta.json create mode 100644 pages/azion/howtos/custom-worker.mdx create mode 100644 pages/azion/howtos/db.mdx create mode 100644 pages/azion/howtos/dev-deploy.mdx create mode 100644 pages/azion/howtos/env-vars.mdx create mode 100644 pages/azion/howtos/image.mdx create mode 100644 pages/azion/howtos/keep_names.mdx create mode 100644 pages/azion/howtos/stripeAPI.mdx create mode 100644 pages/azion/howtos/workerd.mdx create mode 100644 pages/azion/index.mdx create mode 100644 pages/azion/known-issues.mdx create mode 100644 pages/azion/troubleshooting.mdx diff --git a/package.json b/package.json index eb81dbe..78e9004 100644 --- a/package.json +++ b/package.json @@ -24,5 +24,6 @@ }, "devDependencies": { "prettier": "^3.5.2" - } + }, + "packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee" } diff --git a/pages/_meta.json b/pages/_meta.json index 43f637a..2908f3b 100644 --- a/pages/_meta.json +++ b/pages/_meta.json @@ -2,5 +2,6 @@ "index": "Overview", "aws": "AWS", "cloudflare": "Cloudflare", - "netlify": "Netlify" + "netlify": "Netlify", + "azion": "Azion" } diff --git a/pages/azion/_meta.json b/pages/azion/_meta.json new file mode 100644 index 0000000..b3b3b10 --- /dev/null +++ b/pages/azion/_meta.json @@ -0,0 +1,12 @@ +{ + "index": "Overview", + "get-started": "", + "bindings": "", + "caching": "", + "howtos": "How-Tos", + "examples": "", + "known-issues": "Known issues", + "troubleshooting": "", + "migrate-from-0.6-to-1.0.0-beta": "Migrate from 0.6 to 1.0.0-beta", + "former-releases": "Former releases" +} diff --git a/pages/azion/bindings.mdx b/pages/azion/bindings.mdx new file mode 100644 index 0000000..51734ff --- /dev/null +++ b/pages/azion/bindings.mdx @@ -0,0 +1,68 @@ +import { Callout } from "nextra/components"; + +### Bindings + +[Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. When you declare a binding on your Worker, you grant it a specific capability, such as being able to read and write files to an [R2](https://developers.cloudflare.com/r2/) bucket. + +#### How to configure your Next.js app so it can access bindings + +Install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare), and then add a [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) in the root directory of your Next.js app, as described in [Get Started](/cloudflare/get-started#3-create-a-wranglerjson-file). + +#### How to access bindings in your Next.js app + +You can access [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) from any route of your Next.js app via `getCloudflareContext`: + +```js +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export async function GET(request) { + let responseText = "Hello World"; + + const myKv = getCloudflareContext().env.MY_KV_NAMESPACE; + await myKv.put("foo", "bar"); + const foo = await myKv.get("foo"); + + return new Response(foo); +} +``` + + + `getCloudflareContext` can only be used in SSG routes in "async mode" (making it return a promise), to run the function in such a way simply provide an options argument with `async` set to `true`: + ```js + const context = await getCloudflareContext({ async: true }); + ``` + + **WARNING**: During SSG caution is advised since secrets (stored in `.dev.vars` files) and local development + values from bindings (like values saved in a local KV) will be used for the pages static generation. + + + +#### How to add bindings to your Worker + +Add bindings to your Worker by adding them to your [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). + +## TypeScript type declarations for bindings + +To ensure that the `env` object from `getCloudflareContext().env` above has accurate TypeScript types, run the following Wrangler command to [generate types that match your Worker's configuration](https://developers.cloudflare.com/workers/languages/typescript/#generate-types-that-match-your-workers-configuration-experimental): + +``` +npx wrangler types --env-interface CloudflareEnv +``` + +This will generate a `d.ts` file and save it to `worker-configuration.d.ts`. + +To ensure that your types are always up-to-date, make sure to run `wrangler types --env-interface CloudflareEnv` after any changes to your config file. + +## Other Cloudflare APIs (`cf`, `ctx`) + +You can access context about the incoming request from the [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), as well as lifecycle methods from the [`ctx` object](https://developers.cloudflare.com/workers/runtime-apis/context) from the return value of [`getCloudflareContext()`](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src/api/get-cloudflare-context.ts): + +```js +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export async function GET(request) { + const { env, cf, ctx } = getCloudflareContext(); + + // ... +} +``` diff --git a/pages/azion/caching.mdx b/pages/azion/caching.mdx new file mode 100644 index 0000000..4b556c2 --- /dev/null +++ b/pages/azion/caching.mdx @@ -0,0 +1,760 @@ +import { Callout } from "nextra/components"; +import { Tabs } from "nextra/components"; + +## Caching + +Next.js offers multiple ways to improve an application's performance by [caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data) routes and network requests. An application will try to pre-render and cache as much data as possible during build-time to reduce the amount of work required when serving a response to a user. + +The cache data are updated using revalidation, either peridiocally or on-demand: + +- "[Time-based revalidation](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration#time-based-revalidation)" updates the cache data after the revalidation delay specified by the applications expires +- "[On-demand revalidation](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatetag)" allows to invalid cache entries with a specific tag (via `revalidateTag`) or at a given path (via `revalidatePath`). You can also use `res.revalidate` in Pages router API route. + +The `@opennextjs/cloudflare` caching supports rely on 3 components: + +- An **Incremental Cache** to store the cache data +- A **Queue** to synchronize and deduplicate time-based revalidations +- A **Tag Cache** for On-demand revalidations via [`revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag) and [`revalidatePath`](https://nextjs.org/docs/app/api-reference/functions/revalidatePath). + +You can also enable cache interception, to avoid calling the `NextServer` and thus loading the javascript associated with the page. It can slightly improve cold start performance for ISR/SSG route on cached routes. +As of now, cache interception does not work with PPR and is not enabled by default. + +Additionally some components uses the [Cache Api](https://developers.cloudflare.com/workers/runtime-apis/cache/) to improve the performance of these different components. +If you're planning on using On-Demand revalidation, you should also use the [Cache Purge component](#automatic-cache-purge) to automatically purge the cache when a page is revalidated. + +The adapter provides several implementations for each of those components configured in `open-next.config.ts`. + +This guide provides guidelines for common use cases before detailing all the configuration options. + + + Everything in this page only concerns SSG/ISR and the data cache, SSR route will work out of the box without + any caching config. + + +### Guidelines + +#### Small site using revalidation + +You should use the following implementation for a small site: + +- Incremental Cache: use R2 to store the data +- Queue: use a Queue backed by Durable Objects +- Tag Cache: `D1NextModeTagCache` + + + + +```jsonc +{ + "name": "", + // ... + + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], + + // R2 incremental cache + "r2_buckets": [ + { + "binding": "NEXT_INC_CACHE_R2_BUCKET", + "bucket_name": "", + }, + ], + + // DO Queue + "durable_objects": { + "bindings": [ + { + "name": "NEXT_CACHE_DO_QUEUE", + "class_name": "DOQueueHandler", + }, + ], + }, + "migrations": [ + { + "tag": "v1", + "new_sqlite_classes": ["DOQueueHandler"], + }, + ], + + // D1 Tag Cache (Next mode) + // This is only required if you use On-demand revalidation + "d1_databases": [ + { + "binding": "NEXT_TAG_CACHE_D1", + "database_id": "", + "database_name": "", + }, + ], +} +``` + + + + +```ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import d1NextTagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache"; +import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; +//import { withFilter, softTagFilter } from "@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter"; + +export default defineCloudflareConfig({ + incrementalCache: r2IncrementalCache, + queue: doQueue, + // This is only required if you use On-demand revalidation + tagCache: d1NextTagCache, + //If you don't use `revalidatePath`, you can also filter internal soft tags using the `softTagFilter` + // tagCache: withFilter({ + // tagCache: d1NextTagCache, + // filterFn: softTagFilter, + // }), + // Disable this if you want to use PPR + enableCacheInterception: true, +}); +``` + + + + +#### Large site using revalidation + +For a larger site, you should use the `ShardedDOTagCache` that can handle a higher load than the `D1NextModeTagCache`: + + + + +```jsonc +{ + "name": "", + // ... + + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], + + // R2 incremental cache + "r2_buckets": [ + { + "binding": "NEXT_INC_CACHE_R2_BUCKET", + "bucket_name": "", + }, + ], + + // DO Queue and DO Sharded Tag Cache + "durable_objects": { + "bindings": [ + { + "name": "NEXT_CACHE_DO_QUEUE", + "class_name": "DOQueueHandler", + }, + // This is only required if you use On-demand revalidation + { + "name": "NEXT_TAG_CACHE_DO_SHARDED", + "class_name": "DOShardedTagCache", + }, + { + "name": "NEXT_CACHE_DO_PURGE", + "class_name": "BucketCachePurge", + }, + ], + }, + "migrations": [ + { + "tag": "v1", + "new_sqlite_classes": [ + "DOQueueHandler", + // This is only required if you use On-demand revalidation + "DOShardedTagCache", + "BucketCachePurge", + ], + }, + ], +} +``` + + + + +```ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache"; +import doShardedTagCache from "@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache"; +import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; +import { purgeCache } from "@opennextjs/cloudflare/overrides/cache-purge/index"; + +export default defineCloudflareConfig({ + incrementalCache: withRegionalCache(r2IncrementalCache, { mode: "long-lived" }), + queue: doQueue, + // This is only required if you use On-demand revalidation + tagCache: doShardedTagCache({ + baseShardSize: 12, + regionalCache: true, // Enable regional cache to reduce the load on the DOs + regionalCacheTtlSec: 5, // The TTL for the regional cache + shardReplication: { + numberOfSoftReplicas: 4, + numberOfHardReplicas: 2, + regionalReplication: { + defaultRegion: "enam", + }, + }, + }), + // Disable this if you want to use PPR + enableCacheInterception: true, + // you can also use the `durableObject` option to use a durable object as a cache purge + cachePurge: purgeCache({ type: "direct" }), +}); +``` + + + + +#### SSG site + +If your site is static, you do not need a Queue nor a Tag Cache. You can use a read-only Workers Static Assets-based incremental cache for the prerendered routes. + + + + +```ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import staticAssetsIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/static-assets-incremental-cache"; + +export default defineCloudflareConfig({ + incrementalCache: staticAssetsIncrementalCache, + enableCacheInterception: true, +}); +``` + + + + +#### Staging + +For staging, when your site receives low traffic from a single IP, you can replace the DO queue with a memory queue. + +### References + +#### Static Assets Caching + + + The worker doesn't run **in front** of static assets, so the `headers` option of `next.config.ts` doesn't + apply to public files (`public`) and immutable build files (like `_next/static`). + + +By default, Cloudflare [Static Assets headers](https://developers.cloudflare.com/workers/static-assets/headers/#default-headers) use `max-age=0` with `must-revalidate`, allowing the browser to cache assets but with a revalidation request. This is the [same default behavior as the `public` folder on Next.js](https://nextjs.org/docs/app/api-reference/file-conventions/public-folder#caching). + +Next.js also generates _immutable_ files that don't change between builds. Those files will also be served from Static Assets. To match the [default cache behavior of immutable assets in Next.js](https://nextjs.org/docs/app/guides/self-hosting#automatic-caching), avoiding unnecessary revalidation requests, add the following header to the [`public/_headers`](https://developers.cloudflare.com/workers/static-assets/headers/#custom-headers) file: + +```txt +/_next/static/* + Cache-Control: public,max-age=31536000,immutable +``` + +#### Incremental Static Regeneration (ISR) + +There are 3 storage options for the incremental cache: + +- **R2 Object Storage:** A [cost-effective](https://developers.cloudflare.com/r2/pricing/) S3-compatible object storage option for large amounts of unstructured data. Data is stored in a single region, meaning cache interactions may be slower - this can be mitigated with a regional cache. +- **Workers KV:** A [fast](https://blog.cloudflare.com/faster-workers-kv) key value store, it uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. +- **Workers Static Assets:** A read-only store for the incremental cache, serving build-time values from [Workers Static Assets](https://developers.cloudflare.com/workers/static-assets/). Revalidation is not supported with this cache. + + + + +##### 1. Create an R2 Bucket + +```sh +npx wrangler@latest r2 bucket create +``` + +##### 2. Add the R2 Bucket and Service Binding to your Worker + +The binding name used in your app's worker is `NEXT_INC_CACHE_R2_BUCKET`. The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +The prefix used by the R2 bucket can be configured with the `NEXT_INC_CACHE_R2_PREFIX` environment variable, and defaults to `incremental-cache`. + +```jsonc +// wrangler.jsonc +{ + // ... + "name": "", + "r2_buckets": [ + { + "binding": "NEXT_INC_CACHE_R2_BUCKET", + "bucket_name": "", + }, + ], + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], +} +``` + +##### 3. Configure the cache + +In your project's OpenNext config, enable the R2 cache. + +You can optionally setup a regional cache to use with the R2 incremental cache. This will enable faster retrieval of cache entries and reduce the amount of requests being sent to object storage. + +The regional cache has two modes: + +- `short-lived`: Responses are re-used for up to a minute. +- `long-lived`: Fetch responses are re-used until revalidated, and ISR/SSG responses are re-used for up to 30 minutes. + +Additionally, lazy updating of the regional cache can be enabled with the `shouldLazilyUpdateOnCacheHit` option. When requesting data from the cache, it sends a background request to the R2 bucket to get the latest entry. This is enabled by default for the `long-lived` mode. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache"; +// ... + +// With regional cache enabled: +export default defineCloudflareConfig({ + incrementalCache: withRegionalCache(r2IncrementalCache, { + mode: "long-lived", + shouldLazilyUpdateOnCacheHit: true, + }), + // ... +}); + +// Without regional cache: +export default defineCloudflareConfig({ + incrementalCache: r2IncrementalCache, + // ... +}); +``` + + + + + + Workers KV is eventually consistent, which means that it can take up to 60 seconds for updates to be + reflected globally, when using the default TTL of 60 seconds. + + +**Create a KV namespace** + +```sh +npx wrangler@latest kv namespace create +``` + +**Add the KV namespace and Service Binding to your Worker** + +The binding name used in your app's worker is `NEXT_INC_CACHE_KV`. +The `WORKER_SELF_REFERENCE` service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +The prefix used by the cache keys can be configured with the `NEXT_INC_CACHE_KV_PREFIX` environment variable, and defaults to `incremental-cache`. + +```jsonc +// wrangler.jsonc +{ + // ... + "name": "", + "kv_namespaces": [ + { + "binding": "NEXT_INC_CACHE_KV", + "id": "", + }, + ], + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], +} +``` + +**Configure the cache** + +In your project's OpenNext config, enable the KV cache. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache"; +// ... + +export default defineCloudflareConfig({ + incrementalCache: kvIncrementalCache, + // ... +}); +``` + + + + + + + The Workers Static Assets cache is read-only. Requests that attempt to modify it will be ignored. + + +**Configure the cache** + +In your project's OpenNext config, enable the static assets cache. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import staticAssetsIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/static-assets-incremental-cache"; + +export default defineCloudflareConfig({ + incrementalCache: staticAssetsIncrementalCache, +}); +``` + + + + +#### Queue + +A queue must be setup for projects using revalidation (either Time based or On-demand). + +**Configure the queue** + +In your project's OpenNext config, enable the cache and set up a queue. + +The Durable Object Queue will send revalidation requests to a page when needed, and offers support for de-duplicating requests. +By default there will be a maximum of 10 instance of the Durables Object Queue and they can each process up to 5 requests in parallel, for up to 50 concurrent ISR revalidations. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +// ... +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; + +export default defineCloudflareConfig({ + // ... + incrementalCache: r2IncrementalCache, + queue: doQueue, +}); +``` + +You will also need to add some binding to your `wrangler.jsonc` file. + +```jsonc +"durable_objects": { + "bindings": [ + { + "name": "NEXT_CACHE_DO_QUEUE", + "class_name": "DOQueueHandler" + } + ] + }, + "migrations": [ + { + "tag": "v1", + "new_sqlite_classes": ["DOQueueHandler"] + } + ], +``` + +You can customize the behaviors of the queue with environment variables: + +- The max number of revalidations that can be processed by an instance of durable object at the same time (`NEXT_CACHE_DO_QUEUE_MAX_RETRIES`) +- The max time in milliseconds that a revalidation can take before being considered as failed (`NEXT_CACHE_DO_QUEUE_REVALIDATION_TIMEOUT_MS`) +- The amount of time after which a revalidation will be attempted again if it failed. If it fails again it will exponentially back off until it reaches the max retry interval (`NEXT_CACHE_DO_QUEUE_RETRY_INTERVAL_MS`) +- The maximum number of attempts that can be made to revalidate a path (`NEXT_CACHE_DO_QUEUE_MAX_RETRIES`) +- Disable SQLite for this durable object. It should only be used if your incremental cache is not eventually consistent (`NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE`) + + + There is 2 additional modes that you can use for the queue `direct` and the memory queue + +- The memory queue will dedupe request but only on a per isolate basis. It is not fully suitable for production deployments, you + can use it at your own risk! + +- The `direct` mode for the queue is intended for debugging purposes and is not recommended for use in + production. It only works in preview mode (i.e. `wrangler dev`) + + For apps using the Page Router, `res.revalidate` requires to provide a self reference service binding named `WORKER_SELF_REFERENCE`. + + + +In certain situations, you may encounter the limits of what the Durable Object queue can manage for a single page or route. In such cases, you can utilize the queueCache to minimize the number of stale requests sent to the queue. This is achieved by adding and verifying a cache entry via the Cache API before dispatching a request to the queue. If a cache entry already exists, the request will not be sent to the queue, as it will be considered already in process. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +// ... +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; +import queueCache from "@opennextjs/cloudflare/overrides/queue/queue-cache"; + +export default defineCloudflareConfig({ + // ... + incrementalCache: r2IncrementalCache, + queue: queueCache(doQueue, { + regionalCacheTtlSec: 5, // The TTL for the regional cache, defaults to 5 seconds + + // Whether to wait for the queue to acknowledge the request before returning + // When set to false, the cache will be populated asap and the queue will be called after. + // When set to true, the cache will be populated only after the queue ack is received. + waitForQueueAck: true, + }), +}); +``` + +#### Tag Cache for On-Demand Revalidation + +The tag revalidation mechanism can use either a [Cloudflare D1](https://developers.cloudflare.com/d1/) database or [Durable Objects](https://developers.cloudflare.com/durable-objects/) with `SqliteStorage` as its backing store for information about tags, paths, and revalidation times. + +To use on-demand revalidation, you should also follow the [ISR setup steps](#incremental-static-regeneration-isr). + + + If your app **only** uses the pages router, it does not need to have a tag cache and should skip this step. + You can also skip this step if your app doesn't use `revalidateTag` nor `revalidatePath`. + + +There are 2 different options to choose from for the tag cache: `d1NextTagCache`, `doShardedTagCache`. +Which one to choose should be based on two key factors: + +1. **Expected Load**: Consider the volume of traffic or data you anticipate. +2. **Usage of** `revalidateTag` / `revalidatePath`: Evaluate how frequently these features will be utilized. + +If either of these factors is significant, opting for a sharded database is recommended. Additionally, incorporating a regional cache can further enhance performance. + + + + +**Create a D1 database and Service Binding** + +The binding name used in your app's worker is `NEXT_TAG_CACHE_D1`. The `WORKER_SELF_REFERENCE` service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +```jsonc +// wrangler.jsonc +{ + // ... + "d1_databases": [ + { + "binding": "NEXT_TAG_CACHE_D1", + "database_id": "", + "database_name": "", + }, + ], + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], +} +``` + +**Create table for tag revalidations** + +The D1 tag cache requires a `revalidations` table that tracks On-Demand revalidation times. + +**Configure the cache** + +In your project's OpenNext config, enable the R2 cache and set up a queue (see above). The queue will send a revalidation request to a page when needed, but it will not dedupe requests. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; +import d1NextTagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache"; + +export default defineCloudflareConfig({ + incrementalCache: r2IncrementalCache, + queue: doQueue, + tagCache: d1NextTagCache, +}); +``` + +##### 4. Initialise the cache during deployments + +In order for the cache to be properly initialised with the build-time revalidation data, you need to run a command as part of your deploy step. This should be run as part of each deployment to ensure that the cache is being populated with each build's data. + +To populate remote bindings and create a new [version](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/#versions) of your application at the same time, you can use either the `deploy` command or the `upload` command. Similarly, the `preview` command will populate your local bindings and start a Wrangler dev server. + +```sh +# Populate remote and deploy the worker immediately. +opennextjs-cloudflare deploy + +# Populate remote and upload a new version of the worker. +opennextjs-cloudflare upload + +# Populate local and start dev server. +opennextjs-cloudflare preview +``` + +It is possible to only populate the cache without any other steps with the `populateCache` command. + +```sh +# The target is passed as an option, either `local` or `remote`. +opennextjs-cloudflare populateCache local +``` + + + + +**Create a Durable Object and Service Binding** + +The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +```jsonc +// wrangler.jsonc +{ + // ... + "durable_objects": { + "bindings": [ + { + "name": "NEXT_CACHE_DO_QUEUE", + "class_name": "DOQueueHandler", + }, + { + "name": "NEXT_TAG_CACHE_DO_SHARDED", + "class_name": "DOShardedTagCache", + }, + ], + }, + "migrations": [ + { + "tag": "v1", + "new_sqlite_classes": ["DOQueueHandler", "DOShardedTagCache"], + }, + ], + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], +} +``` + +**Configure the cache** + +In your project's OpenNext config, enable the R2 cache and set up a queue. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; +import doShardedTagCache from "@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache"; + +export default defineCloudflareConfig({ + incrementalCache: r2IncrementalCache, + tagCache: doShardedTagCache({ baseShardSize: 12, regionalCache: true }), + queue: doQueue, +}); +``` + +doShardedTagCache takes the following options: + +- `baseShardSize`: The number of shards to use for the cache. The more shards you have, the more evenly the cache will be distributed across the shards. The default is 4. Soft (internal next tags used for `revalidatePath`) and hard tags (the ones you define in your app) will be split in different shards +- `regionalCache`: Whether to use regional cache for the cache. The default is false. This option is useful when you want to reduce the stress on the durable object +- `regionalCacheTtlSec`: The TTL for the regional cache. The default is 5 seconds. Increasing this value will increase the time it takes for the cache to be invalidated across regions +- `shardReplication`: Enable replicating the Shard. Shard replication will duplicate each shards into replicas to spread the load even more + - `numberOfSoftReplicas`: Number of replicas for the soft tag shards + - `numberOfHardReplicas`: Number of replicas for the hard tag shards + - `regionalReplicationOptions` : This setting enables you to replicate shards across different regions, thereby reducing read latency and distributing the load more evenly. By enabling this option, each shard will be replicated in all available regions. Please note that this will increase the number of Durable Objects (DOs) created and the volume of write requests sent to the DOs. + - `defaultRegion`: The default region to use for the replication. +- `maxWriteRetries`: The number of retries to perform when writing tags + + + + + The `withFilter` option is a specialized configuration that enhances your `tagCache` by layering an additional filter. It requires another tag cache to be passed in as the tagCache (e.g., `d1NextTagCache`, `doShardedTagCache` or your own). + +This enhanced tag cache selectively filters which tags trigger revalidations, allowing you to focus on a specific subset and reduce unnecessary load on the underlying tag cache. For convenience, we provide the ready-to-use `softTagFilter` that automatically filters out tags used by the `revalidatePath` function making it a no-op. + +Its primary purpose is to filter out soft tags (i.e., those used by `revalidatePath`) from the tag cache, which are not relevant for your application. It could also be used to filter out hard tags (i.e., those you define in your app) if one of your dependencies uses them. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; +import d1NextTagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache"; +import { withFilter, softTagFilter } from "@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter"; + +export default defineCloudflareConfig({ + incrementalCache: r2IncrementalCache, + queue: doQueue, + tagCache: withFilter({ + tagCache: d1NextTagCache, + filterFn: softTagFilter, + }), +}); +``` + +You can also create your own custom filter function. This function must return a boolean value indicating whether a tag should be included. It will be invoked with a single tag (as a string) as its argument. Please note that "soft tags" (i.e., those used by revalidatePath) always start with the prefix `_N_T`. + + + + +#### Automatic Cache Purge + + + +You can only enable cache purge functionality on a zone (e.g., when using a custom domain). + + + +The cache purge component automatically clears the cache when a page is revalidated. It is only necessary if you use On-Demand revalidation along with one of the cache components that leverage the Cache API. + +This component can either call the Cache API's purge function directly or route the purge request through an intermediate durable object. Using a durable object helps buffer requests and avoid reaching [API rate limits] (https://developers.cloudflare.com/cache/how-to/purge-cache/#hostname-tag-prefix-url-and-purge-everything-limits). + +Cache purge are only called when you call `revalidateTag`, `revalidatePath` or `res.revalidate` in the pages router. It is not called for ISR revalidation. + +Below is an example configuration for integrating the cache purge component in your `open-next.config.ts`: + +```ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache"; +import doShardedTagCache from "@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache"; +import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; +import { purgeCache } from "@opennextjs/cloudflare/overrides/cache-purge/index"; + +export default defineCloudflareConfig({ + incrementalCache: withRegionalCache(r2IncrementalCache, { mode: "long-lived" }), + queue: doQueue, + // This is only required if you use On-demand revalidation + tagCache: doShardedTagCache({ baseShardSize: 12 }), + // Disable this if you want to use PPR + enableCacheInterception: true, + // you can also use the `durableObject` option to use a durable object as a cache purge + cachePurge: purgeCache({ type: "direct" }), +}); +``` + +If you want to use the durable object option, you will need to add the following binding to your `wrangler.jsonc` file: + +```jsonc +{ + "durable_objects": { + "bindings": [ + { + "name": "NEXT_CACHE_DO_PURGE", + "class_name": "BucketCachePurge", + }, + ], + }, + "migrations": [ + { + "tag": "v1", + "new_sqlite_classes": ["BucketCachePurge"], + }, + ], +} +``` + +You can customize the duration of the cache purge buffering with the `NEXT_CACHE_DO_PURGE_BUFFER_TIME_IN_SECONDS` environment variable. The default is 5 seconds. It works by buffering the purge requests for a given amount of time and then sending them all at once. This is useful to avoid hitting the API rate limits. diff --git a/pages/azion/examples.mdx b/pages/azion/examples.mdx new file mode 100644 index 0000000..8e491a0 --- /dev/null +++ b/pages/azion/examples.mdx @@ -0,0 +1,23 @@ +import { Callout } from "nextra/components"; + +## Examples + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +``` +npm create cloudflare@latest -- my-next-app --framework=next --platform=workers +``` + +### Basic starter projects + +Basic example apps are included in the repository for `@opennextjs/cloudflare` package: + +- [_`create-next-app`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/create-next-app) — a Next.js project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +- [_`middleware`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/middleware) — a minimal Next.js project using middleware +- [_`vercel-blog-starter`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/vercel-blog-starter) — a blog project using SSG + +You can use these to understand how to configure your Next.js app to use `@opennextjs/cloudflare`, or refer to [Get Started](/cloudflare/get-started). + +### Next.js Commerce Demo + +The [Next.js Commerce demo app](https://github.com/vercel/commerce/tree/v1) works with `@opennextjs/cloudflare`. You can view a deployed version of it [here](https://vercel-commerce-on-workers.web-experiments.workers.dev/). diff --git a/pages/azion/former-releases/0.2/_meta.json b/pages/azion/former-releases/0.2/_meta.json new file mode 100644 index 0000000..53a20a0 --- /dev/null +++ b/pages/azion/former-releases/0.2/_meta.json @@ -0,0 +1,7 @@ +{ + "index": "Overview", + "get-started": "", + "bindings": "", + "caching": "", + "examples": "" +} diff --git a/pages/azion/former-releases/0.2/bindings.mdx b/pages/azion/former-releases/0.2/bindings.mdx new file mode 100644 index 0000000..36d02b9 --- /dev/null +++ b/pages/azion/former-releases/0.2/bindings.mdx @@ -0,0 +1,63 @@ +import { Callout } from "nextra/components"; + +### Bindings + +[Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. When you declare a binding on your Worker, you grant it a specific capability, such as being able to read and write files to an [R2](https://developers.cloudflare.com/r2/) bucket. + +#### How to configure your Next.js app so it can access bindings + +Install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare), and then add a `wrangler.toml` file in the root directory of your Next.js app, as described in [Get Started](/cloudflare/get-started). + +#### How to access bindings in your Next.js app + +You can access [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) from any route of your Next.js app via `getCloudflareContext`: + +```js +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export async function GET(request) { + let responseText = "Hello World"; + + const myKv = (await getCloudflareContext()).env.MY_KV_NAMESPACE; + await myKv.put("foo", "bar"); + const foo = await myKv.get("foo"); + + return new Response(foo); +} +``` + +#### How to add bindings to your Worker + +Add bindings to your Worker by [adding them to your `wrangler.toml` configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). + +## TypeScript type declarations for bindings + +To ensure that the `env` object from `(await getCloudflareContext()).env` above has accurate TypeScript types, run the following Wrangler command to [generate types that match your Worker's configuration](https://developers.cloudflare.com/workers/languages/typescript/#generate-types-that-match-your-workers-configuration-experimental): + +``` +npx wrangler types --experimental-include-runtime +``` + +This will generate a `d.ts` file and (by default) save it to `.wrangler/types/runtime.d.ts`. You will be prompted in the command's output to add that file to your `tsconfig.json`'s `compilerOptions.types` array. + +If you would like to commit the file to git, you can provide a custom path. Here, for instance, the `runtime.d.ts` file will be saved to the root of your project: + +```bash +npx wrangler types --experimental-include-runtime="./runtime.d.ts" +``` + +To ensure that your types are always up-to-date, make sure to run `wrangler types --experimental-include-runtime` after any changes to your config file. + +## Other Cloudflare APIs (`cf`, `ctx`) + +You can access context about the incoming request from the [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), as well as lifecycle methods from the [`ctx` object](https://developers.cloudflare.com/workers/runtime-apis/context) from the return value of [`getCloudflareContext()`](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src/api/get-cloudflare-context.ts): + +```js +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export async function GET(request) { + const { env, cf, ctx } = await getCloudflareContext(); + + // ... +} +``` diff --git a/pages/azion/former-releases/0.2/caching.mdx b/pages/azion/former-releases/0.2/caching.mdx new file mode 100644 index 0000000..fab6edf --- /dev/null +++ b/pages/azion/former-releases/0.2/caching.mdx @@ -0,0 +1,36 @@ +import { Callout } from "nextra/components"; + +## Caching + +`@opennextjs/cloudflare` supports [caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data) and [revalidating](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data) data returned by subrequests you make in your app by calling [`fetch()`](https://developers.cloudflare.com/workers/runtime-apis/fetch/). + +By default, all `fetch()` subrequests made in your Next.js app are cached. Refer to the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/caching#opting-out-1) for information about how to disable caching for an individual subrequest, or for an entire route. + +[The cache persists across deployments](https://nextjs.org/docs/app/building-your-application/caching#data-cache). You are responsible for revalidating/purging this cache. + + + Workers KV is eventually consistent, which means that it can take up to 60 seconds for updates to be + reflected globally, when using the default TTL of 60 seconds. + + +### How to enable caching + +`@opennextjs/cloudflare` uses [Workers KV](https://developers.cloudflare.com/kv/) as the cache for your Next.js app. Workers KV is [fast](https://blog.cloudflare.com/faster-workers-kv) and uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. + +To enable caching, you must: + +#### 1. Create a KV namespace + +``` +npx wrangler@latest kv namespace create +``` + +#### 2. Add the KV namespace to your Worker + +The binding name used in your app's worker will be `NEXT_CACHE_WORKERS_KV` by default. This is configurable and can be changed by setting the `__OPENNEXT_KV_BINDING_NAME` build-time environment variable. + +``` +[[kv_namespaces]] +binding = "NEXT_CACHE_WORKERS_KV" +id = "" +``` diff --git a/pages/azion/former-releases/0.2/examples.mdx b/pages/azion/former-releases/0.2/examples.mdx new file mode 100644 index 0000000..6dd75b2 --- /dev/null +++ b/pages/azion/former-releases/0.2/examples.mdx @@ -0,0 +1,22 @@ +import { Callout } from "nextra/components"; + +## Examples + +To create a new Next.js app, pre-configured to run on Cloudflare using @opennextjs/cloudflare, run: + +``` +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +### Basic starter projects + +Two basic example apps are included in the repository for `@opennextjs/cloudflare` package: + +- [_`create-next-app`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/create-next-app) — a Next.js project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +- [_`api`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/api) — a minimal Next.js project with a single API route + +You can use these to understand how to configure your Next.js app to use `@opennextjs/cloudflare`, or refer to [Get Started](/cloudflare/get-started). + +### Next.js Commerce Demo + +The [Next.js Commerce demo app](https://github.com/vercel/commerce/tree/v1) works with `@opennextjs/cloudflare`. You can view a deployed version of it [here](https://vercel-commerce-on-workers.web-experiments.workers.dev/). diff --git a/pages/azion/former-releases/0.2/get-started.mdx b/pages/azion/former-releases/0.2/get-started.mdx new file mode 100644 index 0000000..2e16213 --- /dev/null +++ b/pages/azion/former-releases/0.2/get-started.mdx @@ -0,0 +1,140 @@ +import { Callout } from "nextra/components"; + +### Get Started + +#### New apps + +To create a new Next.js app, pre-configured to run on Cloudflare using @opennextjs/cloudflare, run: + +``` +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +#### Existing Next.js apps + +##### 1. Install @opennextjs/cloudflare + +First, install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare): + +```sh +npm install --save-dev @opennextjs/cloudflare +``` + +##### 2. Install Wrangler, and add a `wrangler.toml` file + +Install the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) as a devDependency: + +```npm +npm install -D wrangler@latest +``` + + + You must use Wrangler version `3.78.10` or later to deploy Next.js apps using `@opennextjs/cloudflare`. + + +Then, add a [`wrangler.toml`](https://developers.cloudflare.com/workers/wrangler/configuration/) file to the root directory of your Next.js app: + +```toml +main = ".worker-next/index.mjs" +name = "my-app" +compatibility_date = "2024-09-23" +compatibility_flags = ["nodejs_compat"] +assets = { directory = ".worker-next/assets", binding = "ASSETS" } +``` + + + As shown above, you must enable the [`nodejs_compat` compatibility + flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) *and* set your [compatibility + date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) to `2024-09-23` or + later, in order for your Next.js app to work with @opennextjs/cloudflare. + + +`wrangler.toml` is where you configure your Worker and define what resources it can access via [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings). + +##### 3. Update `package.json` + +Add the following to the scripts field of your `package.json` file: + +```json +"build:worker": "cloudflare", +"dev:worker": "wrangler dev --port 8771", +"preview:worker": "npm run build:worker && npm run dev:worker", +"deploy:worker": "npm run build:worker && wrangler deploy" +``` + +- `npm run build:worker`: Runs the [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) adapter. This first builds your app by running `next build` behind the scenes, and then transforms the build output to a format that you can run locally using [Wrangler](https://developers.cloudflare.com/workers/wrangler/), and deploy to Cloudflare. +- `npm run dev:worker`: Takes the output generated by `build:worker` and runs it locally in [workerd](https://github.com/cloudflare/workerd), the open-source Workers Runtime, allowing you to run the app locally in the same environment that it will run in production. If you instead run `next dev`, your app will run in Node.js, which is a different JavaScript runtime from the Workers runtime, with differences in behavior and APIs. +- `npm run preview:worker`: Runs `build:worker` and then `dev:worker`, allowing you to quickly preview your app running locally in the Workers runtime, via a single command. +- `npm run deploy`: Builds your app, and then deploys it to Cloudflare + +### 4. Add caching with Workers KV + +See the [Caching docs](/cloudflare/0.2/caching) for information on enabling Next.js caching in your OpenNext project. + +### 5. Remove `@cloudflare/next-on-pages` (if necessary) + +If your Next.js app currently uses `@cloudflare/next-on-pages`, you'll want to remove it, and make a few changes. + +#### Remove `export const runtime = "edge";` + +Before deploying your app, remove the `export const runtime = "edge";` line from your `next.config.js` file. This line is not needed when using `@opennextjs/cloudflare`. + +#### Add `.worker-next` to `.gitignore` + +You should add `.worker-next` to your `.gitignore` file to prevent the build output from being committed to your repository. + +#### Uninstall `@cloudflare/next-on-pages` + +You should uninstall `@cloudflare/next-on-pages` and remove any references to it. + +In `package.json`: + +```diff +"scripts": { +- "pages:build": "npx @cloudflare/next-on-pages", +- "preview": "npm run pages:build && wrangler pages dev", +- "deploy": "npm run pages:build && wrangler pages deploy" + +"devDependencies": { +- "@cloudflare/next-on-pages": "*", +``` + +(remember to also remove [eslint-plugin-next-on-pages](https://www.npmjs.com/package/eslint-plugin-next-on-pages) from your `.eslintrc.js` file) + +You no longer need to call `setupDevPlatform()` in your `next.config.mjs` file: + +```diff title="next.config.mjs" +- import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev'; + +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +- if (process.env.NODE_ENV === 'development') { +- await setupDevPlatform(); +- } +``` + +And you'll want to replace any uses of `getRequestContext` from `@cloudflare/next-on-pages` with `getCloudflareContext` from `@opennextjs/cloudflare`: + +```diff +- import { getRequestContext } from "@cloudflare/next-on-pages"; ++ import { getCloudflareContext } from "@opennextjs/cloudflare"; +``` + +##### 6. Develop locally + +You can continue to run `next dev` when developing locally. + +During local development, you can access local versions of Cloudflare bindings as indicated in the [bindings documentation](./bindings). + +In step 3, we also added the `npm run preview:worker`, which allows you to quickly preview your app running locally in the Workers runtime, rather than in Node.js. This allows you to test changes in the same runtime as your app will run in when deployed to Cloudflare. + +##### 7. Deploy to Cloudflare Workers + +Either deploy via the command line: + +```sh +npm run deploy:worker +``` + +Or [connect a Github or Gitlab repository](https://developers.cloudflare.com/workers/ci-cd/), and Cloudflare will automatically build and deploy each pull request you merge to your production branch. diff --git a/pages/azion/former-releases/0.2/index.mdx b/pages/azion/former-releases/0.2/index.mdx new file mode 100644 index 0000000..a3f53c2 --- /dev/null +++ b/pages/azion/former-releases/0.2/index.mdx @@ -0,0 +1,65 @@ +import { Callout } from "nextra/components"; + +## Cloudflare + +The [`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) adapter lets you deploy Next.js apps to [Cloudflare Workers](https://developers.cloudflare.com/workers) using the [Node.js "runtime" from Next.js](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). + + +[`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) is pre 1.0, and still in active development. You should try it, [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues), [share feedback](https://github.com/opennextjs/opennextjs-cloudflare/discussions), and contribute code to help make running Next.js apps on Cloudflare easier. We don't quite yet recommend using it for mission-critical production apps. + +You can also use [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) to deploy Next.js apps to Cloudflare Pages. You can review the differences in supported Next.js features below and by reviewing [the docs for `@cloudflare/next-on-pages`](https://developers.cloudflare.com/pages/framework-guides/nextjs/ssr/supported-features/), and understand the differences between Workers and Pages [here](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/). + + + +### Get Started + +##### New apps + +To create a new Next.js app, pre-configured to run on Cloudflare using @opennextjs/cloudflare, run: + +``` +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +##### Existing Next.js apps + +Follow the guide [here](/cloudflare/0.2/get-started) to use [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) with an existing Next.js app. + +### Supported Next.js runtimes + +Next.js has [two "runtimes"](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes) — "Edge" and "Node.js". When you use `@opennextjs/cloudflare`, your app can use the Node.js runtime, which is more fully featured, and allows you to use the [Node.js APIs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) that are provided by the Cloudflare Workers runtime. + +This is an important difference from `@cloudflare/next-on-pages`, which only supports the "Edge" runtime. The Edge Runtime code in Next.js [intentionally constrains which APIs from Node.js can be used](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/webpack/plugins/middleware-plugin.ts#L820), and the "Edge" runtime does not support all Next.js features. + +### Supported Next.js versions + +`@opennextjs/cloudflare` is pre 1.0, and still in active development. We intend to support all minor and patch version of Next.js 13 and 14, as well as Next.js 15 when it is released. (currently a release candidate) + +To help improve compatibility, we encourage you to [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues) and contribute code! + +### Supported Next.js features + +- [x] [App Router](https://nextjs.org/docs/app) +- [x] [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) +- [x] [Dynamic routes](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) +- [x] [Static Site Generation (SSG)](https://nextjs.org/docs/app/building-your-application/rendering/server-components#static-rendering-default) +- [x] [Server-Side Rendering (SSR)](https://nextjs.org/docs/app/building-your-application/rendering/server-components) + +### Not Yet Supported Next.js features + +The following Next.js features are not yet supported — but we welcome both contributions and feedback! Tell us what you'd like to see, or what you'd like to add support for: + +- [ ] [Pages Router](https://nextjs.org/docs/pages) (you should use the App Router instead, which was introduced in Next.js 13) +- [ ] [Incremental Static Regeneration (ISR)](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration) +- [ ] [Partial Prerendering (PPR)](https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering) +- [ ] [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) +- [ ] [Image optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) (you can integrate Cloudflare Images with Next.js by following [this guide](https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/)) +- [ ] [Experimental streaming support](https://nextjs.org/blog/next-15-rc#executing-code-after-a-response-with-nextafter-experimental) + +### How @opennextjs/cloudflare Works + +The OpenNext Cloudflare adapter works by taking the Next.js build output and transforming it, so that it can run in Cloudflare Workers. + +When you add [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) as a dependency to your Next.js app, and then run `npx cloudflare` the adapter first builds your app by running `next build`, and then transforms the build output to a format that you can run locally using [Wrangler](https://developers.cloudflare.com/workers/wrangler/), and deploy to Cloudflare. + +You can view the code for @opennextjs/cloudflare [here](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src) to understand what it does under the hood. diff --git a/pages/azion/former-releases/0.3/_meta.json b/pages/azion/former-releases/0.3/_meta.json new file mode 100644 index 0000000..53a20a0 --- /dev/null +++ b/pages/azion/former-releases/0.3/_meta.json @@ -0,0 +1,7 @@ +{ + "index": "Overview", + "get-started": "", + "bindings": "", + "caching": "", + "examples": "" +} diff --git a/pages/azion/former-releases/0.3/bindings.mdx b/pages/azion/former-releases/0.3/bindings.mdx new file mode 100644 index 0000000..1b3164d --- /dev/null +++ b/pages/azion/former-releases/0.3/bindings.mdx @@ -0,0 +1,63 @@ +import { Callout } from "nextra/components"; + +### Bindings + +[Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. When you declare a binding on your Worker, you grant it a specific capability, such as being able to read and write files to an [R2](https://developers.cloudflare.com/r2/) bucket. + +#### How to configure your Next.js app so it can access bindings + +Install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare), and then add a [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) in the root directory of your Next.js app, as described in [Get Started](/cloudflare/get-started#3-create-a-wranglerjson-file). + +#### How to access bindings in your Next.js app + +You can access [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) from any route of your Next.js app via `getCloudflareContext`: + +```js +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export async function GET(request) { + let responseText = "Hello World"; + + const myKv = (await getCloudflareContext()).env.MY_KV_NAMESPACE; + await myKv.put("foo", "bar"); + const foo = await myKv.get("foo"); + + return new Response(foo); +} +``` + +#### How to add bindings to your Worker + +Add bindings to your Worker by adding them to your [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). + +## TypeScript type declarations for bindings + +To ensure that the `env` object from `(await getCloudflareContext()).env` above has accurate TypeScript types, run the following Wrangler command to [generate types that match your Worker's configuration](https://developers.cloudflare.com/workers/languages/typescript/#generate-types-that-match-your-workers-configuration-experimental): + +``` +npx wrangler types --experimental-include-runtime +``` + +This will generate a `d.ts` file and (by default) save it to `.wrangler/types/runtime.d.ts`. You will be prompted in the command's output to add that file to your `tsconfig.json`'s `compilerOptions.types` array. + +If you would like to commit the file to git, you can provide a custom path. Here, for instance, the `runtime.d.ts` file will be saved to the root of your project: + +```bash +npx wrangler types --experimental-include-runtime="./runtime.d.ts" +``` + +To ensure that your types are always up-to-date, make sure to run `wrangler types --experimental-include-runtime` after any changes to your config file. + +## Other Cloudflare APIs (`cf`, `ctx`) + +You can access context about the incoming request from the [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), as well as lifecycle methods from the [`ctx` object](https://developers.cloudflare.com/workers/runtime-apis/context) from the return value of [`getCloudflareContext()`](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src/api/get-cloudflare-context.ts): + +```js +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export async function GET(request) { + const { env, cf, ctx } = await getCloudflareContext(); + + // ... +} +``` diff --git a/pages/azion/former-releases/0.3/caching.mdx b/pages/azion/former-releases/0.3/caching.mdx new file mode 100644 index 0000000..1314873 --- /dev/null +++ b/pages/azion/former-releases/0.3/caching.mdx @@ -0,0 +1,47 @@ +import { Callout } from "nextra/components"; + +## Caching + +`@opennextjs/cloudflare` supports [caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data). + +By default, all `fetch()` subrequests made in your Next.js app are cached. Refer to the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/caching#opting-out-1) for information about how to disable caching for an individual subrequest, or for an entire route. + +[The cache persists across deployments](https://nextjs.org/docs/app/building-your-application/caching#data-cache). You are responsible for revalidating/purging this cache. + +Note that [Revalidating](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data) is not yet supported. + +Next.js primes the cache at build time. The build time values are serverd by the [Workers Assets](https://developers.cloudflare.com/workers/static-assets/). + + + Workers KV is eventually consistent, which means that it can take up to 60 seconds for updates to be + reflected globally, when using the default TTL of 60 seconds. + + +### How to enable caching + +`@opennextjs/cloudflare` uses [Workers KV](https://developers.cloudflare.com/kv/) as the cache for your Next.js app. Workers KV is [fast](https://blog.cloudflare.com/faster-workers-kv) and uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. + +To enable caching, you must: + +#### 1. Create a KV namespace + +``` +npx wrangler@latest kv namespace create +``` + +#### 2. Add the KV namespace to your Worker + +The binding name used in your app's worker is `NEXT_CACHE_WORKERS_KV`. + +```jsonc +// wrangler.json +{ + // ... + "kv_namespaces": [ + { + "binding": "NEXT_CACHE_WORKERS_KV", + "id": "", + }, + ], +} +``` diff --git a/pages/azion/former-releases/0.3/examples.mdx b/pages/azion/former-releases/0.3/examples.mdx new file mode 100644 index 0000000..e44bee0 --- /dev/null +++ b/pages/azion/former-releases/0.3/examples.mdx @@ -0,0 +1,24 @@ +import { Callout } from "nextra/components"; + +## Examples + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +``` +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +### Basic starter projects + +Basic example apps are included in the repository for `@opennextjs/cloudflare` package: + +- [_`create-next-app`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/create-next-app) — a Next.js project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +- [_`api`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/api) — a minimal Next.js project with a single API route +- [_`middleware`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/middleware) — a minimal Next.js project using middleware +- [_`vercel-blog-starter`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/vercel-blog-starter) — a blog project using SSG + +You can use these to understand how to configure your Next.js app to use `@opennextjs/cloudflare`, or refer to [Get Started](/cloudflare/get-started). + +### Next.js Commerce Demo + +The [Next.js Commerce demo app](https://github.com/vercel/commerce/tree/v1) works with `@opennextjs/cloudflare`. You can view a deployed version of it [here](https://vercel-commerce-on-workers.web-experiments.workers.dev/). diff --git a/pages/azion/former-releases/0.3/get-started.mdx b/pages/azion/former-releases/0.3/get-started.mdx new file mode 100644 index 0000000..1345e34 --- /dev/null +++ b/pages/azion/former-releases/0.3/get-started.mdx @@ -0,0 +1,194 @@ +import { Callout } from "nextra/components"; + +### Get Started + +#### New apps + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +``` +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +#### Existing Next.js apps + +##### 1. Install @opennextjs/cloudflare + +First, install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare): + +```sh +npm install --save-dev @opennextjs/cloudflare@latest +``` + +##### 2. Install Wrangler + +Install the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) as a devDependency: + +```npm +npm install --save-dev wrangler@latest +``` + + + You must use Wrangler version `3.99.0` or later to deploy Next.js apps using `@opennextjs/cloudflare`. + + +##### 3. Create a wrangler configuration file + + + This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if + not already present). + + +A [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) is needed for your +application to be previewed and deployed, it is also where you configure your Worker and define what resources it can access via [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings). + +You can create one yourself in the root directory of your Next.js app with the name `wrangler.json` and the following content: + +```jsonc +{ + "$schema": "node_modules/wrangler/config-schema.json", + "main": ".open-next/worker.js", + "name": "my-app", + "compatibility_date": "2024-12-30", + "compatibility_flags": ["nodejs_compat"], + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS", + }, + "kv_namespaces": [ + // Create a KV binding with the binding name "NEXT_CACHE_WORKERS_KV" + // to enable the KV based caching: + // { + // "binding": "NEXT_CACHE_WORKERS_KV", + // "id": "" + // } + ], +} +``` + + + As shown above: - You must enable the [`nodejs_compat` compatibility + flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) *and* set your [compatibility + date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) to `2024-09-23` or + later, in order for your Next.js app to work with @opennextjs/cloudflare - The `main` and `assets` values + should also not be changed unless you modify the build output result in some way - You can add a binding + named `NEXT_CACHE_WORKERS_KV` to make use of Next.js' caching as described in the [Caching + docs](/cloudflare/caching) + + +##### 4. Add an `open-next.config.ts` file + + + This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if + not already present). + + +Add a [`open-next.config.ts`](https://opennext.js.org/aws/config) file to the root directory of your Next.js app: + +```ts +import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; +import cache from "@opennextjs/cloudflare/kvCache"; + +const config: OpenNextConfig = { + default: { + override: { + wrapper: "cloudflare-node", + converter: "edge", + // set `incrementalCache` to "dummy" to disable KV cache + incrementalCache: async () => cache, + tagCache: "dummy", + queue: "dummy", + }, + }, + + middleware: { + external: true, + override: { + wrapper: "cloudflare-edge", + converter: "edge", + proxyExternalRequest: "fetch", + }, + }, +}; + +export default config; +``` + + + To use the `OpenNextConfig` type as illustrated above (which is not necessary), you need to install the + `@opennextjs/aws` NPM package as a dev dependency. + + +##### 5. Add a `.dev.vars` file + +Then, add a [`.dev.vars`](https://developers.cloudflare.com/workers/testing/local-development/#local-only-environment-variables) file to the root directory of your Next.js app: + +```text +NEXTJS_ENV=development +``` + +The `NEXTJS_ENV` variable defines the environment to use when loading Next.js `.env` files. It defaults to "production" when not defined. + +##### 6. Update the `package.json` file + +Add the following to the scripts field of your `package.json` file: + +```json +"build:worker": "opennextjs-cloudflare", +"dev:worker": "wrangler dev --port 8771", +"preview": "npm run build:worker && npm run dev:worker", +"deploy": "npm run build:worker && wrangler deploy", +"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts", +``` + +- `npm run build:worker`: Runs the [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) adapter. This first builds your app by running the `build` script in your `package.json` (Next.js apps use `next build` by default), and then transforms the build output to a format that you can run locally using [Wrangler](https://developers.cloudflare.com/workers/wrangler/), and deploy to Cloudflare. The build command used by OpenNext can be overridden with the `buildCommand` option in your OpenNext config. +- `npm run dev:worker`: Takes the output generated by `build:worker` and runs it locally in [workerd](https://github.com/cloudflare/workerd), the open-source Workers Runtime, allowing you to run the app locally in the same environment that it will run in production. If you instead run `next dev`, your app will run in Node.js, which is a different JavaScript runtime from the Workers runtime, with differences in behavior and APIs. +- `npm run preview`: Runs `build:worker` and then `dev:worker`, allowing you to quickly preview your app running locally in the Workers runtime, via a single command. +- `npm run deploy`: Builds your app, and then deploys it to Cloudflare +- `cf-typegen`: Generates a `cloudflare-env.d.ts` file at the root of your project containing [the types for the `env`](https://developers.cloudflare.com/workers/wrangler/commands/#types). + +##### 7. Add caching with Workers KV + +See the [Caching docs](/cloudflare/caching) for information on enabling Next.js caching in your OpenNext project. + +##### 8. Remove any `export const runtime = "edge";` if present + +Before deploying your app, remove the `export const runtime = "edge";` line from any of your source files. + +The edge runtime is not supported yet with `@opennextjs/cloudflare`. + +##### 9. Add `.open-next` to `.gitignore` + +You should add `.open-next` to your `.gitignore` file to prevent the build output from being committed to your repository. + +##### 10. Remove `@cloudflare/next-on-pages` (if necessary) + +If your Next.js app currently uses `@cloudflare/next-on-pages`, you'll want to remove it, and make a few changes. + +Uninstalling the [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) package as well as the [`eslint-plugin-next-on-pages`](https://www.npmjs.com/package/eslint-plugin-next-on-pages) package if present. + +Remove any reference of these packages from your source and configuration files. +This includes: + +- `setupDevPlatform()` calls in your Next.js config file +- `getRequestContext` imports from `@cloudflare/next-on-pages` from your source files + (those can be replaced with `getCloudflareContext` calls from `@opennextjs/cloudflare`) +- next-on-pages eslint rules set in your Eslint config file + +##### 11. Develop locally + +You can continue to run `next dev` when developing locally. + +During local development, you can access local versions of Cloudflare bindings as indicated in the [bindings documentation](./bindings). + +In step 3, we also added the `npm run preview:worker`, which allows you to quickly preview your app running locally in the Workers runtime, rather than in Node.js. This allows you to test changes in the same runtime as your app will run in when deployed to Cloudflare. + +##### 12. Deploy to Cloudflare Workers + +Either deploy via the command line: + +```sh +npm run deploy:worker +``` + +Or [connect a Github or Gitlab repository](https://developers.cloudflare.com/workers/ci-cd/), and Cloudflare will automatically build and deploy each pull request you merge to your production branch. diff --git a/pages/azion/former-releases/0.3/index.mdx b/pages/azion/former-releases/0.3/index.mdx new file mode 100644 index 0000000..b44423e --- /dev/null +++ b/pages/azion/former-releases/0.3/index.mdx @@ -0,0 +1,71 @@ +import { Callout } from "nextra/components"; +import WindowsSupport from "../../../../shared/WindowsSupport.mdx"; + +## Cloudflare + +The [`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) adapter lets you deploy Next.js apps to [Cloudflare Workers](https://developers.cloudflare.com/workers) using the [Node.js "runtime" from Next.js](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). + + +[`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) is pre 1.0, and still in active development. You should try it, [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues), [share feedback](https://github.com/opennextjs/opennextjs-cloudflare/discussions), and contribute code to help make running Next.js apps on Cloudflare easier. We don't quite yet recommend using it for mission-critical production apps. + +You can also use [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) to deploy Next.js apps to Cloudflare Pages. You can review the differences in supported Next.js features below and by reviewing [the docs for `@cloudflare/next-on-pages`](https://developers.cloudflare.com/pages/framework-guides/nextjs/ssr/supported-features/), and understand the differences between Workers and Pages [here](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/). + + + +### Get Started + +##### New apps + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +``` +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +##### Existing Next.js apps + +Follow the guide [here](/cloudflare/get-started) to use [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) with an existing Next.js app. + +### Supported Next.js runtimes + +Next.js has [two "runtimes"](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes) — "Edge" and "Node.js". When you use `@opennextjs/cloudflare`, your app should use the Node.js runtime, which is more fully featured, and allows you to use the [Node.js APIs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) that are provided by the Cloudflare Workers runtime. + +This is an important difference from `@cloudflare/next-on-pages`, which only supports the "Edge" runtime. The Edge Runtime code in Next.js [intentionally constrains which APIs from Node.js can be used](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/webpack/plugins/middleware-plugin.ts#L820), and the "Edge" runtime does not support all Next.js features. + +### Supported Next.js versions + +`@opennextjs/cloudflare` is pre 1.0, and still in active development. We intend to support all minor and patch versions of Next.js 14 and 15. + +To help improve compatibility, we encourage you to [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues) and contribute code! + +### Supported Next.js features + +Some Next.js features are not yet supported are not fully tested. +We will update the list as we progress towards releasing 1.0. + +- [x] [App Router](https://nextjs.org/docs/app) +- [x] [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) +- [x] [Dynamic routes](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) +- [x] [Static Site Generation (SSG)](https://nextjs.org/docs/app/building-your-application/rendering/server-components#static-rendering-default) +- [x] [Server-Side Rendering (SSR)](https://nextjs.org/docs/app/building-your-application/rendering/server-components) +- [x] [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) +- [x] [Image optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) (you can integrate Cloudflare Images with Next.js by following [this guide](https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/)) +- [ ] [Pages Router](https://nextjs.org/docs/pages) +- [ ] [Incremental Static Regeneration (ISR)](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration) +- [ ] [Partial Prerendering (PPR)](https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering) +- [ ] [Support for after](https://nextjs.org/blog/next-15-rc#executing-code-after-a-response-with-nextafter-experimental) +- [ ] [Composable Caching](https://nextjs.org/blog/composable-caching) (`'use cache'`) is a Next.js 15 feature and not supported yet. + +We welcome both contributions and feedback! + +### Windows support + + + +### How `@opennextjs/cloudflare` Works + +The OpenNext Cloudflare adapter works by taking the Next.js build output and transforming it, so that it can run in Cloudflare Workers. + +When you add [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) as a dependency to your Next.js app, and then run `npx opennextjs-cloudflare` the adapter first builds your app by running the `build` script in your `package.json`, and then transforms the build output to a format that you can run locally using [Wrangler](https://developers.cloudflare.com/workers/wrangler/), and deploy to Cloudflare. + +You can view the code for `@opennextjs/cloudflare` [here](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src) to understand what it does under the hood. diff --git a/pages/azion/former-releases/0.5/_meta.json b/pages/azion/former-releases/0.5/_meta.json new file mode 100644 index 0000000..53a20a0 --- /dev/null +++ b/pages/azion/former-releases/0.5/_meta.json @@ -0,0 +1,7 @@ +{ + "index": "Overview", + "get-started": "", + "bindings": "", + "caching": "", + "examples": "" +} diff --git a/pages/azion/former-releases/0.5/bindings.mdx b/pages/azion/former-releases/0.5/bindings.mdx new file mode 100644 index 0000000..df0a480 --- /dev/null +++ b/pages/azion/former-releases/0.5/bindings.mdx @@ -0,0 +1,74 @@ +import { Callout } from "nextra/components"; + +### Bindings + +[Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. When you declare a binding on your Worker, you grant it a specific capability, such as being able to read and write files to an [R2](https://developers.cloudflare.com/r2/) bucket. + +#### How to configure your Next.js app so it can access bindings + +Install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare), and then add a [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) in the root directory of your Next.js app, as described in [Get Started](/cloudflare/get-started#3-create-a-wranglerjson-file). + +#### How to access bindings in your Next.js app + +You can access [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) from any route of your Next.js app via `getCloudflareContext`: + +```js +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export async function GET(request) { + let responseText = "Hello World"; + + const myKv = getCloudflareContext().env.MY_KV_NAMESPACE; + await myKv.put("foo", "bar"); + const foo = await myKv.get("foo"); + + return new Response(foo); +} +``` + + + `getCloudflareContext` can only be used in SSG routes in "async mode" (making it return a promise), to run the function in such a way simply provide an options argument with `async` set to `true`: + ```js + const context = await getCloudflareContext({ async: true }); + ``` + + **WARNING**: During SSG caution is advised since secrets (stored in `.dev.vars` files) and local development + values from bindings (like values saved in a local KV) will be used for the pages static generation. + + + +#### How to add bindings to your Worker + +Add bindings to your Worker by adding them to your [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). + +## TypeScript type declarations for bindings + +To ensure that the `env` object from `getCloudflareContext().env` above has accurate TypeScript types, run the following Wrangler command to [generate types that match your Worker's configuration](https://developers.cloudflare.com/workers/languages/typescript/#generate-types-that-match-your-workers-configuration-experimental): + +``` +npx wrangler types --experimental-include-runtime +``` + +This will generate a `d.ts` file and (by default) save it to `.wrangler/types/runtime.d.ts`. You will be prompted in the command's output to add that file to your `tsconfig.json`'s `compilerOptions.types` array. + +If you would like to commit the file to git, you can provide a custom path. Here, for instance, the `runtime.d.ts` file will be saved to the root of your project: + +```bash +npx wrangler types --experimental-include-runtime="./runtime.d.ts" +``` + +To ensure that your types are always up-to-date, make sure to run `wrangler types --experimental-include-runtime` after any changes to your config file. + +## Other Cloudflare APIs (`cf`, `ctx`) + +You can access context about the incoming request from the [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), as well as lifecycle methods from the [`ctx` object](https://developers.cloudflare.com/workers/runtime-apis/context) from the return value of [`getCloudflareContext()`](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src/api/get-cloudflare-context.ts): + +```js +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export async function GET(request) { + const { env, cf, ctx } = getCloudflareContext(); + + // ... +} +``` diff --git a/pages/azion/former-releases/0.5/caching.mdx b/pages/azion/former-releases/0.5/caching.mdx new file mode 100644 index 0000000..c5eed52 --- /dev/null +++ b/pages/azion/former-releases/0.5/caching.mdx @@ -0,0 +1,156 @@ +import { Callout } from "nextra/components"; + +## Caching + +`@opennextjs/cloudflare` supports [caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data). + +Next.js primes the cache at build time. The build time values are serverd by the [Workers Assets](https://developers.cloudflare.com/workers/static-assets/). + + + Workers KV is eventually consistent, which means that it can take up to 60 seconds for updates to be + reflected globally, when using the default TTL of 60 seconds. + + +### How to enable caching + +`@opennextjs/cloudflare` supports multiple caching mechanisms through a project's OpenNext configuration. + +#### Incremental Static Regeneration (ISR) + +The ISR adapter for Cloudflare uses [Workers KV](https://developers.cloudflare.com/kv/) as the cache for your Next.js app. Workers KV is [fast](https://blog.cloudflare.com/faster-workers-kv) and uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. Pricing information can be found in the Cloudflare [docs](https://developers.cloudflare.com/workers/platform/pricing/#workers-kv). + +##### 1. Create a KV namespace + +``` +npx wrangler@latest kv namespace create +``` + +##### 2. Add the KV namespace and Service Binding to your Worker + +The binding name used in your app's worker is `NEXT_CACHE_WORKERS_KV`. The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +```jsonc +// wrangler.jsonc +{ + // ... + "kv_namespaces": [ + { + "binding": "NEXT_CACHE_WORKERS_KV", + "id": "", + }, + ], + "services": [ + { + "binding": "NEXT_CACHE_REVALIDATION_WORKER", + "service": "", + }, + ], +} +``` + +#### 3. Configure the cache + +In your project's OpenNext config, enable the KV cache and set up a queue. + +The memory queue will send revalidation requests to a page when needed, and offers support for de-duplicating requests on a per-isolate basis. There might still be duplicate requests under high traffic or across regions. + + + The memory queue provided by `@opennextjs/cloudflare` is not fully suitable for production deployments, you + can use it at your own risk! + + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import kvIncrementalCache from "@opennextjs/cloudflare/kv-cache"; +import memoryQueue from "@opennextjs/cloudflare/memory-queue"; + +export default defineCloudflareConfig({ + incrementalCache: kvIncrementalCache, + queue: memoryQueue, +}); +``` + + + The `direct` mode for the queue is intended for debugging purposes and is not recommended for use in + production. We are actively working on a solution that will be suitable for production. + + +#### On-Demand Revalidation + +The tag revalidation mechanism uses a [Cloudflare D1](https://developers.cloudflare.com/d1/) database as its backing store for information about tags, paths, and revalidation times. + +To use on-demand revalidation, you should also follow the [ISR setup steps](#incremental-static-regeneration-isr). + + + If your app **only** uses the pages router, it does not need to have a tag cache and should skip this step. + + +##### 1. Create a D1 database and Service Binding + +The binding name used in your app's worker is `NEXT_CACHE_D1`. The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +```jsonc +// wrangler.jsonc +{ + // ... + "d1_databases": [ + { + "binding": "NEXT_CACHE_D1", + "database_id": "", + "database_name": "", + }, + ], + "services": [ + { + "binding": "NEXT_CACHE_REVALIDATION_WORKER", + "service": "", + }, + ], +} +``` + +##### 2. Create tables for tag revalidations + +The D1 tag cache requires two tables; one that keeps a record of the tag/path mappings, and another that tracks revalidation times. + +For the tag mappings, the default table name is `tags`, and can be configured by setting the `NEXT_CACHE_D1_TAGS_TABLE` environment variable to a string. + +For the revalidation times, the default table name is `revalidations` and can be configured by setting the `NEXT_CACHE_D1_REVALIDATIONS_TABLE` environment variable to a string. + +Wrangler can be used to create a table with it's [execute](https://developers.cloudflare.com/d1/wrangler-commands/#d1-execute) option. Ensure that you create a table for both your local dev database and your remote database. + +```sh +wrangler d1 execute NEXT_CACHE_D1 --command "CREATE TABLE IF NOT EXISTS tags (tag TEXT NOT NULL, path TEXT NOT NULL, UNIQUE(tag, path) ON CONFLICT REPLACE)" +wrangler d1 execute NEXT_CACHE_D1 --command "CREATE TABLE IF NOT EXISTS revalidations (tag TEXT NOT NULL, revalidatedAt INTEGER NOT NULL, UNIQUE(tag) ON CONFLICT REPLACE)" +``` + +##### 3. Configure the cache + +In your project's OpenNext config, enable the KV cache and set up a queue. The queue will send a revalidation request to a page when needed, but it will not dedupe requests. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import kvIncrementalCache from "@opennextjs/cloudflare/kv-cache"; +import d1TagCache from "@opennextjs/cloudflare/d1-tag-cache"; +import memoryQueue from "@opennextjs/cloudflare/memory-queue"; + +export default defineCloudflareConfig({ + incrementalCache: kvIncrementalCache, + tagCache: d1TagCache, + queue: memoryQueue, +}); +``` + +##### 4. Initialise the cache during deployments + +In order for the cache to be properly initialised with the build-time revalidation data, you need to setup a command that runs as part of your deploy step. + +OpenNext will generate an SQL file during the build that can be used to setup your D1 database. + +```sh +wrangler d1 execute NEXT_CACHE_D1 --file .open-next/cloudflare/cache-assets-manifest.sql +``` + +This should be run as part of each deployment to ensure that the cache is being populated with each build's initial revalidation data. diff --git a/pages/azion/former-releases/0.5/examples.mdx b/pages/azion/former-releases/0.5/examples.mdx new file mode 100644 index 0000000..e44bee0 --- /dev/null +++ b/pages/azion/former-releases/0.5/examples.mdx @@ -0,0 +1,24 @@ +import { Callout } from "nextra/components"; + +## Examples + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +``` +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +### Basic starter projects + +Basic example apps are included in the repository for `@opennextjs/cloudflare` package: + +- [_`create-next-app`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/create-next-app) — a Next.js project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +- [_`api`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/api) — a minimal Next.js project with a single API route +- [_`middleware`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/middleware) — a minimal Next.js project using middleware +- [_`vercel-blog-starter`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/vercel-blog-starter) — a blog project using SSG + +You can use these to understand how to configure your Next.js app to use `@opennextjs/cloudflare`, or refer to [Get Started](/cloudflare/get-started). + +### Next.js Commerce Demo + +The [Next.js Commerce demo app](https://github.com/vercel/commerce/tree/v1) works with `@opennextjs/cloudflare`. You can view a deployed version of it [here](https://vercel-commerce-on-workers.web-experiments.workers.dev/). diff --git a/pages/azion/former-releases/0.5/get-started.mdx b/pages/azion/former-releases/0.5/get-started.mdx new file mode 100644 index 0000000..f1d70dd --- /dev/null +++ b/pages/azion/former-releases/0.5/get-started.mdx @@ -0,0 +1,190 @@ +import { Callout } from "nextra/components"; + +### Get Started + +#### New apps + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +```sh +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +#### Existing Next.js apps + +##### 1. Install @opennextjs/cloudflare + +First, install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare): + +```sh +npm install --save-dev @opennextjs/cloudflare@latest +``` + +##### 2. Install Wrangler + +Install the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) as a devDependency: + +```sh +npm install --save-dev wrangler@latest +``` + + + You must use Wrangler version `3.99.0` or later to deploy Next.js apps using `@opennextjs/cloudflare`. + + +##### 3. Create a wrangler configuration file + + + This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if + not already present). + + +A [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) is needed for your +application to be previewed and deployed, it is also where you configure your Worker and define what resources it can access via [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings). + +You can create one yourself in the root directory of your Next.js app with the name `wrangler.jsonc` and the following content: + +```jsonc +{ + "$schema": "node_modules/wrangler/config-schema.json", + "main": ".open-next/worker.js", + "name": "my-app", + "compatibility_date": "2024-12-30", + "compatibility_flags": ["nodejs_compat"], + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS", + }, + "kv_namespaces": [ + // Create a KV binding with the binding name "NEXT_CACHE_WORKERS_KV" + // to enable the KV based caching: + // { + // "binding": "NEXT_CACHE_WORKERS_KV", + // "id": "" + // } + ], +} +``` + + + As shown above: - You must enable the [`nodejs_compat` compatibility + flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) *and* set your [compatibility + date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) to `2024-09-23` or + later, in order for your Next.js app to work with @opennextjs/cloudflare - The `main` and `assets` values + should also not be changed unless you modify the build output result in some way - You can add a binding + named `NEXT_CACHE_WORKERS_KV` to make use of Next.js' caching as described in the [Caching + docs](/cloudflare/caching) + + +##### 4. Add an `open-next.config.ts` file + + + This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if + not already present). + + +Add a [`open-next.config.ts`](https://opennext.js.org/aws/config) file to the root directory of your Next.js app: + +```ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import kvIncrementalCache from "@opennextjs/cloudflare/kv-cache"; + +export default defineCloudflareConfig({ + incrementalCache: kvIncrementalCache, +}); +``` + + + To use the `OpenNextConfig` type as illustrated above (which is not necessary), you need to install the + `@opennextjs/aws` NPM package as a dev dependency. + + +##### 5. Add a `.dev.vars` file + +Then, add a [`.dev.vars`](https://developers.cloudflare.com/workers/testing/local-development/#local-only-environment-variables) file to the root directory of your Next.js app: + +```text +NEXTJS_ENV=development +``` + +The `NEXTJS_ENV` variable defines the environment to use when loading Next.js `.env` files. It defaults to "production" when not defined. + +##### 6. Update the `package.json` file + +Add the following to the scripts field of your `package.json` file: + +```json +"preview": "opennextjs-cloudflare && wrangler dev", +"deploy": "opennextjs-cloudflare && wrangler deploy", +"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts", +``` + +- `npm run preview`: Builds your app and serves it locally, allowing you to quickly preview your app running locally in the Workers runtime, via a single command. +- `npm run deploy`: Builds your app, and then deploys it to Cloudflare +- `cf-typegen`: Generates a `cloudflare-env.d.ts` file at the root of your project containing [the types for the `env`](https://developers.cloudflare.com/workers/wrangler/commands/#types). + +##### 7. Add caching with Workers KV + +See the [Caching docs](/cloudflare/caching) for information on enabling Next.js caching in your OpenNext project. + +##### 8. Remove any `export const runtime = "edge";` if present + +Before deploying your app, remove the `export const runtime = "edge";` line from any of your source files. + +The edge runtime is not supported yet with `@opennextjs/cloudflare`. + +##### 9. Add `.open-next` to `.gitignore` + +You should add `.open-next` to your `.gitignore` file to prevent the build output from being committed to your repository. + +##### 10. Remove `@cloudflare/next-on-pages` (if necessary) + +If your Next.js app currently uses `@cloudflare/next-on-pages`, you'll want to remove it, and make a few changes. + +Uninstalling the [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) package as well as the [`eslint-plugin-next-on-pages`](https://www.npmjs.com/package/eslint-plugin-next-on-pages) package if present. + +Remove any reference of these packages from your source and configuration files. +This includes: + +- `setupDevPlatform()` calls in your Next.js config file +- `getRequestContext` imports from `@cloudflare/next-on-pages` from your source files + (those can be replaced with `getCloudflareContext` calls from `@opennextjs/cloudflare`) +- next-on-pages eslint rules set in your Eslint config file + +##### 11. Develop locally + +You can continue to run `next dev` when developing locally. + +Modify your Next.js configuration file to import and call the `initOpenNextCloudflareForDev` utility +from the `@opennextjs/cloudflare` package. This makes sure that the Next.js dev server can optimally integrate with the open-next cloudflare adapter and it is necessary for using bindings during local development. + +This is an example of a Next.js configuration file calling the utility: + +```ts +// next.config.ts +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; + +import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; +initOpenNextCloudflareForDev(); +``` + +After having added the `initOpenNextCloudflareForDev()` call in your Next.js configuration file, you will be able, during local development, to access in any of your server code, local versions of Cloudflare bindings as indicated in the [bindings documentation](./bindings). + +In step 3, we also added the `npm run preview`, which allows you to quickly preview your app running locally in the Workers runtime, +rather than in Node.js. This allows you to test changes in the same runtime as your app will run in when deployed to Cloudflare. + +##### 12. Deploy to Cloudflare Workers + +Either deploy via the command line: + +```sh +npm run deploy +``` + +Or [connect a Github or Gitlab repository](https://developers.cloudflare.com/workers/ci-cd/), and Cloudflare will automatically build and deploy each pull request you merge to your production branch. diff --git a/pages/azion/former-releases/0.5/index.mdx b/pages/azion/former-releases/0.5/index.mdx new file mode 100644 index 0000000..cdb70bb --- /dev/null +++ b/pages/azion/former-releases/0.5/index.mdx @@ -0,0 +1,73 @@ +import { Callout } from "nextra/components"; +import WindowsSupport from "../../../../shared/WindowsSupport.mdx"; + +## Cloudflare + +The [`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) adapter lets you deploy Next.js apps to [Cloudflare Workers](https://developers.cloudflare.com/workers) using the [Node.js "runtime" from Next.js](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). + + +[`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) is pre 1.0, and still in active development. You should try it, [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues), [share feedback](https://github.com/opennextjs/opennextjs-cloudflare/discussions), and contribute code to help make running Next.js apps on Cloudflare easier. We don't quite yet recommend using it for mission-critical production apps. + +You can also use [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) to deploy Next.js apps to Cloudflare Pages. You can review the differences in supported Next.js features below and by reviewing [the docs for `@cloudflare/next-on-pages`](https://developers.cloudflare.com/pages/framework-guides/nextjs/ssr/supported-features/), and understand the differences between Workers and Pages [here](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/). + + + +### Get Started + +##### New apps + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +``` +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +##### Existing Next.js apps + +Follow the guide [here](/cloudflare/get-started) to use [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) with an existing Next.js app. + +### Supported Next.js runtimes + +Next.js has [two "runtimes"](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes) — "Edge" and "Node.js". When you use `@opennextjs/cloudflare`, your app should use the Node.js runtime, which is more fully featured, and allows you to use the [Node.js APIs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) that are provided by the Cloudflare Workers runtime. + +This is an important difference from `@cloudflare/next-on-pages`, which only supports the "Edge" runtime. The Edge Runtime code in Next.js [intentionally constrains which APIs from Node.js can be used](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/webpack/plugins/middleware-plugin.ts#L820), and the "Edge" runtime does not support all Next.js features. + +### Supported Next.js versions + +`@opennextjs/cloudflare` is pre 1.0, and still in active development. We intend to support all minor and patch versions of Next.js 15 and the latest minor of Next.js 14. + +To help improve compatibility, we encourage you to [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues) and contribute code! + +### Supported Next.js features + +Some Next.js features are not yet supported are not fully tested. +We will update the list as we progress towards releasing 1.0. + +- [x] [App Router](https://nextjs.org/docs/app) +- [x] [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) +- [x] [Dynamic routes](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) +- [x] [Static Site Generation (SSG)](https://nextjs.org/docs/app/building-your-application/rendering/server-components#static-rendering-default) +- [x] [Server-Side Rendering (SSR)](https://nextjs.org/docs/app/building-your-application/rendering/server-components) +- [x] [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) +- [x] [Image optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) (you can integrate Cloudflare Images with Next.js by following [this guide](https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/)) +- [x] [Partial Prerendering (PPR)](https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering) +- [x] [Pages Router](https://nextjs.org/docs/pages) +- [x] [Incremental Static Regeneration (ISR)](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration) 1 +- [x] [Support for after](https://nextjs.org/blog/next-15-rc#executing-code-after-a-response-with-nextafter-experimental) +- [ ] [Composable Caching](https://nextjs.org/blog/composable-caching) (`'use cache'`) is a Next.js 15 feature and not supported yet. + +1 Only the `direct` mode is supported at the moment, and is not suitable for production. + +We welcome both contributions and feedback! + +### Windows support + + + +### How `@opennextjs/cloudflare` Works + +The OpenNext Cloudflare adapter works by taking the Next.js build output and transforming it, so that it can run in Cloudflare Workers. + +When you add [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) as a dependency to your Next.js app, and then run `npx opennextjs-cloudflare` the adapter first builds your app by running the `build` script in your `package.json`, and then transforms the build output to a format that you can run locally using [Wrangler](https://developers.cloudflare.com/workers/wrangler/), and deploy to Cloudflare. + +You can view the code for `@opennextjs/cloudflare` [here](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src) to understand what it does under the hood. diff --git a/pages/azion/former-releases/0.6/_meta.json b/pages/azion/former-releases/0.6/_meta.json new file mode 100644 index 0000000..53a20a0 --- /dev/null +++ b/pages/azion/former-releases/0.6/_meta.json @@ -0,0 +1,7 @@ +{ + "index": "Overview", + "get-started": "", + "bindings": "", + "caching": "", + "examples": "" +} diff --git a/pages/azion/former-releases/0.6/bindings.mdx b/pages/azion/former-releases/0.6/bindings.mdx new file mode 100644 index 0000000..df0a480 --- /dev/null +++ b/pages/azion/former-releases/0.6/bindings.mdx @@ -0,0 +1,74 @@ +import { Callout } from "nextra/components"; + +### Bindings + +[Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. When you declare a binding on your Worker, you grant it a specific capability, such as being able to read and write files to an [R2](https://developers.cloudflare.com/r2/) bucket. + +#### How to configure your Next.js app so it can access bindings + +Install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare), and then add a [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) in the root directory of your Next.js app, as described in [Get Started](/cloudflare/get-started#3-create-a-wranglerjson-file). + +#### How to access bindings in your Next.js app + +You can access [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) from any route of your Next.js app via `getCloudflareContext`: + +```js +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export async function GET(request) { + let responseText = "Hello World"; + + const myKv = getCloudflareContext().env.MY_KV_NAMESPACE; + await myKv.put("foo", "bar"); + const foo = await myKv.get("foo"); + + return new Response(foo); +} +``` + + + `getCloudflareContext` can only be used in SSG routes in "async mode" (making it return a promise), to run the function in such a way simply provide an options argument with `async` set to `true`: + ```js + const context = await getCloudflareContext({ async: true }); + ``` + + **WARNING**: During SSG caution is advised since secrets (stored in `.dev.vars` files) and local development + values from bindings (like values saved in a local KV) will be used for the pages static generation. + + + +#### How to add bindings to your Worker + +Add bindings to your Worker by adding them to your [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). + +## TypeScript type declarations for bindings + +To ensure that the `env` object from `getCloudflareContext().env` above has accurate TypeScript types, run the following Wrangler command to [generate types that match your Worker's configuration](https://developers.cloudflare.com/workers/languages/typescript/#generate-types-that-match-your-workers-configuration-experimental): + +``` +npx wrangler types --experimental-include-runtime +``` + +This will generate a `d.ts` file and (by default) save it to `.wrangler/types/runtime.d.ts`. You will be prompted in the command's output to add that file to your `tsconfig.json`'s `compilerOptions.types` array. + +If you would like to commit the file to git, you can provide a custom path. Here, for instance, the `runtime.d.ts` file will be saved to the root of your project: + +```bash +npx wrangler types --experimental-include-runtime="./runtime.d.ts" +``` + +To ensure that your types are always up-to-date, make sure to run `wrangler types --experimental-include-runtime` after any changes to your config file. + +## Other Cloudflare APIs (`cf`, `ctx`) + +You can access context about the incoming request from the [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), as well as lifecycle methods from the [`ctx` object](https://developers.cloudflare.com/workers/runtime-apis/context) from the return value of [`getCloudflareContext()`](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src/api/get-cloudflare-context.ts): + +```js +import { getCloudflareContext } from "@opennextjs/cloudflare"; + +export async function GET(request) { + const { env, cf, ctx } = getCloudflareContext(); + + // ... +} +``` diff --git a/pages/azion/former-releases/0.6/caching.mdx b/pages/azion/former-releases/0.6/caching.mdx new file mode 100644 index 0000000..0f642d3 --- /dev/null +++ b/pages/azion/former-releases/0.6/caching.mdx @@ -0,0 +1,418 @@ +import { Callout } from "nextra/components"; +import { Tabs } from "nextra/components"; + +## Caching + +`@opennextjs/cloudflare` supports [caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data). + +### How to enable caching + +`@opennextjs/cloudflare` supports multiple caching mechanisms through a project's OpenNext configuration. + +#### Incremental Static Regeneration (ISR) + +There are two storage options to use for the incremental cache. + +- **Workers KV:** A [fast](https://blog.cloudflare.com/faster-workers-kv) and uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. The build time values are serverd by the [Workers Assets](https://developers.cloudflare.com/workers/static-assets/). Pricing information can be found in the Cloudflare [docs](https://developers.cloudflare.com/workers/platform/pricing/#workers-kv). +- **R2 Object Storage:** A [cost-effective](https://developers.cloudflare.com/r2/pricing/) S3-compatible object storage option for large amounts of unstructured data. Data is stored in a single region, meaning cache interactions may be slower - this can be mitigated with a regional cache. + + + Workers KV is eventually consistent, which means that it can take up to 60 seconds for updates to be + reflected globally, when using the default TTL of 60 seconds. + + + + +##### 1. Create a KV namespace + +``` +npx wrangler@latest kv namespace create +``` + +##### 2. Add the KV namespace and Service Binding to your Worker + +The binding name used in your app's worker is `NEXT_INC_CACHE_KV`. +The `WORKER_SELF_REFERENCE` service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +```jsonc +// wrangler.jsonc +{ + // ... + "name": "", + "kv_namespaces": [ + { + "binding": "NEXT_INC_CACHE_KV", + "id": "", + }, + ], + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], +} +``` + +##### 3. Configure the cache + +In your project's OpenNext config, enable the KV cache. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache"; +// ... + +export default defineCloudflareConfig({ + incrementalCache: kvIncrementalCache, + // ... +}); +``` + + + + + +##### 1. Create an R2 Bucket + +``` +npx wrangler@latest r2 bucket create +``` + +##### 2. Add the R2 Bucket and Service Binding to your Worker + +The binding name used in your app's worker is `NEXT_INC_CACHE_R2_BUCKET`. The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +The prefix used by the R2 bucket can be configured with the `NEXT_INC_CACHE_R2_PREFIX` environment variable, and defaults to `incremental-cache`. + +```jsonc +// wrangler.jsonc +{ + // ... + "name": "", + "r2_buckets": [ + { + "binding": "NEXT_INC_CACHE_R2_BUCKET", + "bucket_name": "", + "preview_bucket_name": "", + }, + ], + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], +} +``` + +##### 3. Configure the cache + +In your project's OpenNext config, enable the R2 cache. + +You can optionally setup a regional cache to use with the R2 incremental cache. This will enable faster retrieval of cache entries and reduce the amount of requests being sent to object storage. + +The regional cache has two modes: + +- `short-lived`: Responses are re-used for up to a minute. +- `long-lived`: Fetch responses are re-used until revalidated, and ISR/SSG responses are re-used for up to 30 minutes. + +Additionally, lazy updating of the regional cache can be enabled with the `shouldLazilyUpdateOnCacheHit` option. When requesting data from the cache, it sends a background request to the R2 bucket to get the latest entry. This is enabled by default for the `long-lived` mode. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache"; +// ... + +// With regional cache enabled: +export default defineCloudflareConfig({ + incrementalCache: withRegionalCache(r2IncrementalCache, { + mode: "long-lived", + shouldLazilyUpdateOnCacheHit: true, + }), + // ... +}); + +// Without regional cache: +export default defineCloudflareConfig({ + incrementalCache: r2IncrementalCache, + // ... +}); +``` + + + + +##### 4. Configure the queue + +In your project's OpenNext config, enable the cache and set up a queue. + +The Durable Object Queue will send revalidation requests to a page when needed, and offers support for de-duplicating requests. +By default there will be a maximum of 10 instance of the Durables Object Queue and they can each process up to 5 requests in parallel, for up to 50 concurrent ISR revalidations. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +// ... +import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; + +export default defineCloudflareConfig({ + // ... + queue: doQueue, +}); +``` + +You will also need to add some binding to your `wrangler.jsonc` file. + +```jsonc +"durable_objects": { + "bindings": [ + { + "name": "NEXT_CACHE_DO_QUEUE", + "class_name": "DurableObjectQueueHandler" + } + ] + }, + "migrations": [ + { + "tag": "v1", + "new_sqlite_classes": ["DurableObjectQueueHandler"] + } + ], +``` + +You can customize the behaviors of the queue with environment variables: + +- The max number of revalidations that can be processed by an instance of durable object at the same time (`NEXT_CACHE_DO_QUEUE_MAX_REVALIDATION`) +- The max time in milliseconds that a revalidation can take before being considered as failed (`NEXT_CACHE_DO_QUEUE_REVALIDATION_TIMEOUT_MS`) +- The amount of time after which a revalidation will be attempted again if it failed. If it fails again it will exponentially back off until it reaches the max retry interval (`NEXT_CACHE_DO_QUEUE_RETRY_INTERVAL_MS`) +- The maximum number of attempts that can be made to revalidate a path (`NEXT_CACHE_DO_QUEUE_MAX_NUM_REVALIDATIONS`) +- Disable SQLite for this durable object. It should only be used if your incremental cache is not eventually consistent (`NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE`) + + + There is 2 additional modes that you can use for the queue `direct` and the memory queue + +- The memory queue will dedupe request but only on a per isolate basis. It is not fully suitable for production deployments, you + can use it at your own risk! + +- The `direct` mode for the queue is intended for debugging purposes and is not recommended for use in + production. It only works in preview mode (i.e. `wrangler dev`) + + For apps using the Page Router, `res.revalidate` requires to provide a self reference service binding named `WORKER_SELF_REFERENCE`. + + + +#### On-Demand Revalidation + +The tag revalidation mechanism can use either a [Cloudflare D1](https://developers.cloudflare.com/d1/) database or [Durable Objects](https://developers.cloudflare.com/durable-objects/) with `SqliteStorage` as its backing store for information about tags, paths, and revalidation times. + +To use on-demand revalidation, you should also follow the [ISR setup steps](#incremental-static-regeneration-isr). + + + If your app **only** uses the pages router, it does not need to have a tag cache and should skip this step. + You can also skip this step if your app doesn't to use `revalidateTag` nor `revalidatePath`. + + +There are 3 different options to choose from for the tag cache: `d1NextTagCache`, `doShardedTagCache` and `d1TagCache`. +Which one to choose should be based on two key factors: + +1. **Expected Load**: Consider the volume of traffic or data you anticipate. +2. **Usage of** `revalidateTag` / `revalidatePath`: Evaluate how frequently these features will be utilized. + +If either of these factors is significant, opting for a sharded database is recommended. Additionally, incorporating a regional cache can further enhance performance. + + + +##### 1. Create a D1 database and Service Binding + +The binding name used in your app's worker is `NEXT_TAG_CACHE_D1`. The `WORKER_SELF_REFERENCE` service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +```jsonc +// wrangler.jsonc +{ + // ... + "d1_databases": [ + { + "binding": "NEXT_TAG_CACHE_D1", + "database_id": "", + "database_name": "", + }, + ], + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], +} +``` + +##### 2. Create table for tag revalidations + +The D1 tag cache requires a `revalidations` table that tracks On-Demand revalidation times. + +##### 3. Configure the cache + +In your project's OpenNext config, enable the KV cache and set up a queue (see above). The queue will send a revalidation request to a page when needed, but it will not dedupe requests. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import kvIncrementalCache from "@opennextjs/cloudflare/kv-cache"; +import d1NextTagCache from "@opennextjs/cloudflare/d1-next-tag-cache"; +import memoryQueue from "@opennextjs/cloudflare/memory-queue"; + +export default defineCloudflareConfig({ + incrementalCache: kvIncrementalCache, + tagCache: d1NextTagCache, + queue: memoryQueue, +}); +``` + + + + +##### 1. Create a Durable Object and Service Binding +The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +```jsonc +// wrangler.jsonc +{ + // ... + "durable_objects": { + "bindings": [ + { + "name": "NEXT_CACHE_DO_QUEUE", + "class_name": "DurableObjectQueueHandler", + }, + { + "name": "NEXT_TAG_CACHE_DO_SHARDED", + "class_name": "DOShardedTagCache", + }, + ], + }, + "migrations": [ + { + "tag": "v1", + "new_sqlite_classes": ["DurableObjectQueueHandler", "DOShardedTagCache"], + }, + ], + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], +} +``` + +##### 2. Configure the cache + +In your project's OpenNext config, enable the KV cache and set up a queue. The queue will send a revalidation request to a page when needed, but it will not dedupe requests. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache"; +import memoryQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; +import doShardedTagCache from "@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache"; + +export default defineCloudflareConfig({ + incrementalCache: kvIncrementalCache, + tagCache: doShardedTagCache({ baseShardSize: 12, regionalCache: true }), + queue: memoryQueue, +}); +``` + +doShardedTagCache tahes the following options: + +- `baseShardSize` - The number of shards to use for the cache. The more shards you have, the more evenly the cache will be distributed across the shards. The default is 4. Soft (internal next tags used for `revalidatePath`) and hard tags (the one you define in your app) will be split in different shards +- `regionalCache` - Whether to use regional cache for the cache. The default is false. This option is useful when you want to reduce the stress on the durable object +- `regionalCacheTtlSec` - The TTL for the regional cache. The default is 5 seconds. Increasing this value will increase the time it takes for the cache to be invalidated across regions +- `enableShardReplication`: Enable replicating the Shard. Shard replication will duplicate each shards into replicas to spread the load even more +- `shardReplicationOptions.numberOfSoftReplicas`: Number of replicas for the soft tag shards +- `shardReplicationOptions.numberOfHardReplicas`: Number of replicas for the hard tag shards +- `maxWriteRetries`: The number of retries to perform when writing tags + + + + + + + The `d1TagCache` is not recommended for production use, as it does not scale well with the number of tags. + + +##### 1. Create a D1 database and Service Binding + +The binding name used in your app's worker is `NEXT_TAG_CACHE_D1`. The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. + +```jsonc +// wrangler.jsonc +{ + // ... + "name": "", + "d1_databases": [ + { + "binding": "NEXT_TAG_CACHE_D1", + "database_id": "", + "database_name": "", + }, + ], + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + "service": "", + }, + ], +} +``` + +The D1 database uses two tables, created when initialising the cache: + +- the "tags" table keeps a record of the tag/path mappings +- the "revalidations" table tracks revalidation times + +##### 2. Configure the cache + +In your project's OpenNext config, enable the KV cache and set up a queue. The queue will send a revalidation request to a page when needed, but it will not dedupe requests. + +```ts +// open-next.config.ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache"; +import d1TagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-tag-cache"; +import memoryQueue from "@opennextjs/cloudflare/overrides/queue/memory-queue"; + +export default defineCloudflareConfig({ + incrementalCache: kvIncrementalCache, + tagCache: d1TagCache, + queue: memoryQueue, +}); +``` + +##### 3. Initialise the cache during deployments + +In order for the cache to be properly initialised with the build-time revalidation data, you need to run a command as part of your deploy step. This should be run as part of each deployment to ensure that the cache is being populated with each build's data. + +To populate remote bindings and deploy your application at the same time, you can use the `deploy` command. Similarly, the `preview` command will populate your local bindings and start a Wrangler dev server. + +```sh +# Populate remote and deploy. +opennextjs-cloudflare deploy + +# Populate local and start dev server. +opennextjs-cloudflare preview +``` + +It is possible to only populate the cache without any other steps with the `populateCache` command. + +```sh +# The target is passed as an option, either `local` or `remote`. +opennextjs-cloudflare populateCache local +``` + + + diff --git a/pages/azion/former-releases/0.6/examples.mdx b/pages/azion/former-releases/0.6/examples.mdx new file mode 100644 index 0000000..e44bee0 --- /dev/null +++ b/pages/azion/former-releases/0.6/examples.mdx @@ -0,0 +1,24 @@ +import { Callout } from "nextra/components"; + +## Examples + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +``` +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +### Basic starter projects + +Basic example apps are included in the repository for `@opennextjs/cloudflare` package: + +- [_`create-next-app`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/create-next-app) — a Next.js project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +- [_`api`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/api) — a minimal Next.js project with a single API route +- [_`middleware`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/middleware) — a minimal Next.js project using middleware +- [_`vercel-blog-starter`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/vercel-blog-starter) — a blog project using SSG + +You can use these to understand how to configure your Next.js app to use `@opennextjs/cloudflare`, or refer to [Get Started](/cloudflare/get-started). + +### Next.js Commerce Demo + +The [Next.js Commerce demo app](https://github.com/vercel/commerce/tree/v1) works with `@opennextjs/cloudflare`. You can view a deployed version of it [here](https://vercel-commerce-on-workers.web-experiments.workers.dev/). diff --git a/pages/azion/former-releases/0.6/get-started.mdx b/pages/azion/former-releases/0.6/get-started.mdx new file mode 100644 index 0000000..5b1e9b0 --- /dev/null +++ b/pages/azion/former-releases/0.6/get-started.mdx @@ -0,0 +1,197 @@ +import { Callout } from "nextra/components"; + +### Get Started + +#### New apps + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +```sh +npm create cloudflare@latest -- my-next-app --framework=next --experimental +``` + +#### Existing Next.js apps + +##### 1. Install @opennextjs/cloudflare + +First, install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare): + +```sh +npm install --save-dev @opennextjs/cloudflare@latest +``` + +##### 2. Install Wrangler + +Install the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) as a devDependency: + +```sh +npm install --save-dev wrangler@latest +``` + + + You must use Wrangler version `3.99.0` or later to deploy Next.js apps using `@opennextjs/cloudflare`. + + +##### 3. Create a wrangler configuration file + + + This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if + not already present). + + +A [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) is needed for your +application to be previewed and deployed, it is also where you configure your Worker and define what resources it can access via [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings). + +You can create one yourself in the root directory of your Next.js app with the name `wrangler.jsonc` and the following content: + +```jsonc +{ + "$schema": "node_modules/wrangler/config-schema.json", + "main": ".open-next/worker.js", + "name": "my-app", + "compatibility_date": "2024-12-30", + "compatibility_flags": ["nodejs_compat"], + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS", + }, + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + // The service should match the "name" of your worker + "service": "my-app", + }, + ], + "kv_namespaces": [ + // Create a KV binding with the binding name "NEXT_INC_CACHE_KV" + // to enable the KV based caching: + // { + // "binding": "NEXT_INC_CACHE_KV", + // "id": "" + // } + ], +} +``` + + + As shown above: - You must enable the [`nodejs_compat` compatibility + flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) *and* set your [compatibility + date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) to `2024-09-23` or + later, in order for your Next.js app to work with @opennextjs/cloudflare - The `main` and `assets` values + should also not be changed unless you modify the build output result in some way - You can add a binding + named `NEXT_INC_CACHE_KV` to make use of Next.js' caching as described in the [Caching + docs](/cloudflare/caching) + + +##### 4. Add an `open-next.config.ts` file + + + This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if + not already present). + + +Add a [`open-next.config.ts`](https://opennext.js.org/aws/config) file to the root directory of your Next.js app: + +```ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache"; + +export default defineCloudflareConfig({ + incrementalCache: kvIncrementalCache, +}); +``` + + + To use the `OpenNextConfig` type as illustrated above (which is not necessary), you need to install the + `@opennextjs/aws` NPM package as a dev dependency. + + +##### 5. Add a `.dev.vars` file + +Then, add a [`.dev.vars`](https://developers.cloudflare.com/workers/testing/local-development/#local-only-environment-variables) file to the root directory of your Next.js app: + +```text +NEXTJS_ENV=development +``` + +The `NEXTJS_ENV` variable defines the environment to use when loading Next.js `.env` files. It defaults to "production" when not defined. + +##### 6. Update the `package.json` file + +Add the following to the scripts field of your `package.json` file: + +```json +"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", +"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy", +"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts", +``` + +- `npm run preview`: Builds your app and serves it locally, allowing you to quickly preview your app running locally in the Workers runtime, via a single command. +- `npm run deploy`: Builds your app, and then deploys it to Cloudflare +- `cf-typegen`: Generates a `cloudflare-env.d.ts` file at the root of your project containing [the types for the `env`](https://developers.cloudflare.com/workers/wrangler/commands/#types). + +##### 7. Add caching with Workers KV + +See the [Caching docs](/cloudflare/caching) for information on enabling Next.js caching in your OpenNext project. + +##### 8. Remove any `export const runtime = "edge";` if present + +Before deploying your app, remove the `export const runtime = "edge";` line from any of your source files. + +The edge runtime is not supported yet with `@opennextjs/cloudflare`. + +##### 9. Add `.open-next` to `.gitignore` + +You should add `.open-next` to your `.gitignore` file to prevent the build output from being committed to your repository. + +##### 10. Remove `@cloudflare/next-on-pages` (if necessary) + +If your Next.js app currently uses `@cloudflare/next-on-pages`, you'll want to remove it, and make a few changes. + +Uninstalling the [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) package as well as the [`eslint-plugin-next-on-pages`](https://www.npmjs.com/package/eslint-plugin-next-on-pages) package if present. + +Remove any reference of these packages from your source and configuration files. +This includes: + +- `setupDevPlatform()` calls in your Next.js config file +- `getRequestContext` imports from `@cloudflare/next-on-pages` from your source files + (those can be replaced with `getCloudflareContext` calls from `@opennextjs/cloudflare`) +- next-on-pages eslint rules set in your Eslint config file + +##### 11. Develop locally + +You can continue to run `next dev` when developing locally. + +Modify your Next.js configuration file to import and call the `initOpenNextCloudflareForDev` utility +from the `@opennextjs/cloudflare` package. This makes sure that the Next.js dev server can optimally integrate with the open-next cloudflare adapter and it is necessary for using bindings during local development. + +This is an example of a Next.js configuration file calling the utility: + +```ts +// next.config.ts +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; + +import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; +initOpenNextCloudflareForDev(); +``` + +After having added the `initOpenNextCloudflareForDev()` call in your Next.js configuration file, you will be able, during local development, to access in any of your server code, local versions of Cloudflare bindings as indicated in the [bindings documentation](./bindings). + +In step 3, we also added the `npm run preview`, which allows you to quickly preview your app running locally in the Workers runtime, +rather than in Node.js. This allows you to test changes in the same runtime as your app will run in when deployed to Cloudflare. + +##### 12. Deploy to Cloudflare Workers + +Either deploy via the command line: + +```sh +npm run deploy +``` + +Or [connect a Github or Gitlab repository](https://developers.cloudflare.com/workers/ci-cd/), and Cloudflare will automatically build and deploy each pull request you merge to your production branch. diff --git a/pages/azion/former-releases/_meta.json b/pages/azion/former-releases/_meta.json new file mode 100644 index 0000000..1186ae9 --- /dev/null +++ b/pages/azion/former-releases/_meta.json @@ -0,0 +1,10 @@ +{ + "0.6": "Release 0.6", + "migrate-from-0.5-to-0.6": "", + "0.5": "Release 0.5", + "migrate-from-0.4-to-0.5": "", + "migrate-from-0.3-to-0.4": "", + "0.3": "Release 0.3", + "migrate-from-0.2-to-0.3": "", + "0.2": "Release 0.2" +} diff --git a/pages/azion/former-releases/migrate-from-0.2-to-0.3.mdx b/pages/azion/former-releases/migrate-from-0.2-to-0.3.mdx new file mode 100644 index 0000000..e213c6f --- /dev/null +++ b/pages/azion/former-releases/migrate-from-0.2-to-0.3.mdx @@ -0,0 +1,82 @@ +import { Callout } from "nextra/components"; + +### Migrate from 0.2 to 0.3 + +The `@opennextjs/cloudflare` adapter is now more closely intgrated with `@opennextjs/aws`. + +You will need to update your projects based on the 0.2 release as described in the following sections. + +##### 1. Update the `wrangler.toml` file + +The entry point is now `.open-next/worker.js`, update `wrangler.toml` accordingly: + +```toml +# CHANGED: new entry point location +main = ".open-next/worker.js" +name = "my-app" + +compatibility_date = "2024-09-23" +compatibility_flags = ["nodejs_compat"] + +# The binding name must be "ASSETS" when the cache is enabled +# CHANGED: output folder location +assets = { directory = ".open-next/assets", binding = "ASSETS" } +``` + +##### 2. Add a `open-next.config.ts` file + +Add a [`open-next.config.ts`](https://opennext.js.org/aws/config) file to the root directory of your Next.js app: + +```ts +import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; +import cache from "@opennextjs/cloudflare/kvCache"; + +const config: OpenNextConfig = { + default: { + override: { + wrapper: "cloudflare-node", + converter: "edge", + // Set `incrementalCache` to "dummy" to disable KV cache + incrementalCache: async () => cache, + tagCache: "dummy", + queue: "dummy", + }, + }, + + middleware: { + external: true, + override: { + wrapper: "cloudflare-edge", + converter: "edge", + proxyExternalRequest: "fetch", + }, + }, +}; + +export default config; +``` + +You can either install the `@opennextjs/aws` NPM package to get the types or `open-next.config.ts` to the [`exclude`](https://www.typescriptlang.org/tsconfig/#exclude) configuration key of your `tsconfig.json`. + +##### 3. Add a `.dev.vars` file + +Add a [`.dev.vars`](https://developers.cloudflare.com/workers/testing/local-development/#local-only-environment-variables) file to the root directory of your Next.js app: + +```text +NEXTJS_ENV=development +``` + +The `NEXTJS_ENV` variable defines the environment to use when loading Next.js `.env` files. It defaults to "production" when not defined. + +##### 4. Update `package.json` + +The name of the CLI was changed to `opennextjs-cloudflare`: + +```json +"build:worker": "opennextjs-cloudflare", +``` + +##### 5 Add `.open-next` to `.gitignore` + +You should change `.worker-next` to `.open-next` in your `.gitignore` file to prevent the build output from being committed to your repository. +You can safely delete the content of the now unused `.worker-next`. diff --git a/pages/azion/former-releases/migrate-from-0.3-to-0.4.mdx b/pages/azion/former-releases/migrate-from-0.3-to-0.4.mdx new file mode 100644 index 0000000..70fb364 --- /dev/null +++ b/pages/azion/former-releases/migrate-from-0.3-to-0.4.mdx @@ -0,0 +1,38 @@ +import { Callout } from "nextra/components"; + +### Migrate from 0.3 to 0.4 + +`@opennextjs/cloudflare@0.4.0` introduced a new `initOpenNextCloudflareForDev` utility and made `getCloudflareContext` synchronous, +we'll explore those two differences below, and how they effect applications built using `0.3.x` versions of the adapter. + +##### `initOpenNextCloudflareForDev` + +`initOpenNextCloudflareForDev` is a new utility that needs to be added to the Next.js configuration file in order to integrate the adapter +with the Next.js dev server. If you don't plan on using the `next dev` command you can skip this section, otherwise update your Next.js +configuration file to import and call the utility. + +Example: + +```js +// next.config.mjs + +import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; + +initOpenNextCloudflareForDev(); + +/** @type {import('next').NextConfig} */ +const nextConfig = {}; + +export default nextConfig; +``` + +##### Synchronous `getCloudflareContext` synchronous + +`getCloudflareContext` is now synchronous. +a promise that resolves to it. + +This means that if you had code that `await`ed `getCloudflareContext()` calls, such `await`s are no longer necessary and +can be removed. + +If your application is instead combining the result of `getCloudflareContext()` with on Ecmascript Promises APIs such as +`then`, `catch` and `finally` those need to be removed since the function's result, as mentioned is no longer a promise. diff --git a/pages/azion/former-releases/migrate-from-0.4-to-0.5.mdx b/pages/azion/former-releases/migrate-from-0.4-to-0.5.mdx new file mode 100644 index 0000000..26d371d --- /dev/null +++ b/pages/azion/former-releases/migrate-from-0.4-to-0.5.mdx @@ -0,0 +1,3 @@ +### Migrate from 0.4 (to 0.5) + +`@opennextjs/cloudflare@0.5.0` did not introduce any breaking change, meaning that there are no migration steps from `0.4` to `0.5`. diff --git a/pages/azion/former-releases/migrate-from-0.5-to-0.6.mdx b/pages/azion/former-releases/migrate-from-0.5-to-0.6.mdx new file mode 100644 index 0000000..be0285f --- /dev/null +++ b/pages/azion/former-releases/migrate-from-0.5-to-0.6.mdx @@ -0,0 +1,47 @@ +import { Callout } from "nextra/components"; + +### Migrate from 0.5 to 0.6 + +`@opennextjs/cloudflare@0.6.0` introduces more options for caching. + +The codebase has been refactored with some breaking changes, read-on to update your apps: + +##### Overrides location + +The cloudflare overrides now live in `@opennextjs/cloudflare/overrides`: + +- Incremental cache overrides: `@opennextjs/cloudflare/overrides/incremental-cache/...` +- Tag cache overrides: `@opennextjs/cloudflare/overrides/tag-cache/...` +- Queue overrides: `@opennextjs/cloudflare/overrides/queue/...` + +For example the KV incremental cache override can be now imported as `@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache`. + +##### Environment variables and bindings + +We have updated the name for consistency: + +- `NEXT_CACHE_WORKERS_KV` -> `NEXT_INC_CACHE_KV` +- `NEXT_CACHE_R2_...` -> `NEXT_INC_CACHE_R2_...` +- `NEXT_CACHE_D1` -> `NEXT_TAG_CACHE_D1` +- `NEXT_CACHE_DO_...` -> `NEXT_TAG_CACHE_DO_...` +- `NEXT_CACHE_DO_REVALIDATION` -> `NEXT_CACHE_DO_QUEUE` +- `NEXT_CACHE_REVALIDATION_WORKER` -> `WORKER_SELF_REFERENCE` + +`NEXT_CACHE_D1_TAGS_TABLE` and `NEXT_CACHE_D1_REVALIDATIONS_TABLE` have been dropped. +The D1 tables have a fixed names `tags` and `revalidations`. + +##### CLI + +The CLI was previsouly invoked as `opennextjs-cloudflare` and now takes a mandatory command: + +- `opennextjs-cloudflare build` is the equivalent of executing `opennextjs-cloudflare` previously +- `opennextjs-cloudflare populateCache local` is used to populate the `local` or `remote` cache +- `opennextjs-cloudflare preview` is used to populate the local cache and start a dev server +- `opennextjs-cloudflare deploy` is used to populate the remote cache and deploy + +You will need to update the `scripts` in your `package.json`: + +```json +"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", +"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy", +``` diff --git a/pages/azion/get-started.mdx b/pages/azion/get-started.mdx new file mode 100644 index 0000000..050b8b3 --- /dev/null +++ b/pages/azion/get-started.mdx @@ -0,0 +1,218 @@ +import { Callout } from "nextra/components"; + +### Get Started + +#### New apps + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +```sh +npm create cloudflare@latest -- my-next-app --framework=next --platform=workers +``` + +#### Existing Next.js apps + +##### 1. Install @opennextjs/cloudflare + +First, install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare): + +```sh +npm install @opennextjs/cloudflare@latest +``` + +##### 2. Install Wrangler + +Install the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) as a devDependency: + +```sh +npm install --save-dev wrangler@latest +``` + + + You must use Wrangler version `3.99.0` or later to deploy Next.js apps using `@opennextjs/cloudflare`. + + +##### 3. Create a wrangler configuration file + + + This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if + not already present). + + +A [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) is needed for your +application to be previewed and deployed, it is also where you configure your Worker and define what resources it can access via [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings). + +You can create one yourself in the root directory of your Next.js app with the name `wrangler.jsonc` and the following content: + +```jsonc +{ + "$schema": "node_modules/wrangler/config-schema.json", + "main": ".open-next/worker.js", + "name": "my-app", + "compatibility_date": "2024-12-30", + "compatibility_flags": [ + // Enable Node.js API + // see https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag + "nodejs_compat", + // Allow to fetch URLs in your app + // see https://developers.cloudflare.com/workers/configuration/compatibility-flags/#global-fetch-strictly-public + "global_fetch_strictly_public", + ], + "assets": { + "directory": ".open-next/assets", + "binding": "ASSETS", + }, + "services": [ + { + "binding": "WORKER_SELF_REFERENCE", + // The service should match the "name" of your worker + "service": "my-app", + }, + ], + "r2_buckets": [ + // Create a R2 binding with the binding name "NEXT_INC_CACHE_R2_BUCKET" + // { + // "binding": "NEXT_INC_CACHE_R2_BUCKET", + // "bucket_name": "", + // }, + ], +} +``` + + + As shown above: - You must enable the [`nodejs_compat` compatibility + flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) *and* set your [compatibility + date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) to `2024-09-23` or + later, in order for your Next.js app to work with @opennextjs/cloudflare - The `main` and `assets` values + should also not be changed unless you modify the build output result in some way - You can add a binding + named `NEXT_INC_CACHE_R2_BUCKET` to make use of Next.js' caching as described in the [Caching + docs](/cloudflare/caching) + + +##### 4. Add an `open-next.config.ts` file + + + This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if + not already present). + + +Add a [`open-next.config.ts`](https://opennext.js.org/aws/config) file to the root directory of your Next.js app: + +```ts +import { defineCloudflareConfig } from "@opennextjs/cloudflare"; +import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; + +export default defineCloudflareConfig({ + incrementalCache: r2IncrementalCache, +}); +``` + + + To use the `OpenNextConfig` type as illustrated above (which is not necessary), you need to install the + `@opennextjs/aws` NPM package as a dev dependency. + + +##### 5. Add a `.dev.vars` file + +Then, add a [`.dev.vars`](https://developers.cloudflare.com/workers/testing/local-development/#local-only-environment-variables) file to the root directory of your Next.js app: + +```text +NEXTJS_ENV=development +``` + +The `NEXTJS_ENV` variable defines the environment to use when loading Next.js `.env` files. It defaults to "production" when not defined. + +##### 6. Update the `package.json` file + +Add the following to the scripts field of your `package.json` file: + +```json +"build": "next build", +"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", +"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy", +"upload": "opennextjs-cloudflare build && opennextjs-cloudflare upload", +"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts", +``` + +- The `build` script must invoke the Next.js build command, it will be invoke by `opennextjs-cloudflare build`. +- `npm run preview`: Builds your app and serves it locally, allowing you to quickly preview your app running locally in the Workers runtime, via a single command. +- `npm run deploy`: Builds your app, and then immediately deploys it to Cloudflare. +- `npm run upload`: Builds your app, and then uploads a new [version](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/#versions) of it to Cloudflare. +- `cf-typegen`: Generates a `cloudflare-env.d.ts` file at the root of your project containing [the types for the `env`](https://developers.cloudflare.com/workers/wrangler/commands/#types). + +##### 7. Add Static Asset Caching + +Add a `public/_headers` file, with at least the following headers: + +```txt +/_next/static/* + Cache-Control: public,max-age=31536000,immutable +``` + +See the [Static Assets Caching docs](/cloudflare/caching#static-assets-caching) for more information. + +##### 8. Add caching with Cloudflare R2 + +See the [Caching docs](/cloudflare/caching) for information on enabling Next.js caching in your OpenNext project. + +##### 9. Remove any `export const runtime = "edge";` if present + +Before deploying your app, remove the `export const runtime = "edge";` line from any of your source files. + +The edge runtime is not supported yet with `@opennextjs/cloudflare`. + +##### 10. Add `.open-next` to `.gitignore` + +You should add `.open-next` to your `.gitignore` file to prevent the build output from being committed to your repository. + +##### 11. Remove `@cloudflare/next-on-pages` (if necessary) + +If your Next.js app currently uses `@cloudflare/next-on-pages`, you'll want to remove it, and make a few changes. + +Uninstalling the [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) package as well as the [`eslint-plugin-next-on-pages`](https://www.npmjs.com/package/eslint-plugin-next-on-pages) package if present. + +Remove any reference of these packages from your source and configuration files. +This includes: + +- `setupDevPlatform()` calls in your Next.js config file +- `getRequestContext` imports from `@cloudflare/next-on-pages` from your source files + (those can be replaced with `getCloudflareContext` calls from `@opennextjs/cloudflare`) +- next-on-pages eslint rules set in your Eslint config file + +##### 12. Develop locally + +You can continue to run `next dev` when developing locally. + +Modify your Next.js configuration file to import and call the `initOpenNextCloudflareForDev` utility +from the `@opennextjs/cloudflare` package. This makes sure that the Next.js dev server can optimally integrate with the open-next cloudflare adapter and it is necessary for using bindings during local development. + +This is an example of a Next.js configuration file calling the utility: + +```ts +// next.config.ts +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; + +import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; +initOpenNextCloudflareForDev(); +``` + +After having added the `initOpenNextCloudflareForDev()` call in your Next.js configuration file, you will be able, during local development, to access in any of your server code, local versions of Cloudflare bindings as indicated in the [bindings documentation](./bindings). + +In step 3, we also added the `npm run preview`, which allows you to quickly preview your app running locally in the Workers runtime, +rather than in Node.js. This allows you to test changes in the same runtime as your app will run in when deployed to Cloudflare. + +##### 13. Deploy to Cloudflare Workers + +Either deploy via the command line: + +```sh +npm run deploy +``` + +Or [connect a Github or Gitlab repository](https://developers.cloudflare.com/workers/ci-cd/), and Cloudflare will automatically build and deploy each pull request you merge to your production branch. diff --git a/pages/azion/howtos/_meta.json b/pages/azion/howtos/_meta.json new file mode 100644 index 0000000..c8fe30e --- /dev/null +++ b/pages/azion/howtos/_meta.json @@ -0,0 +1,10 @@ +{ + "stripeAPI": "Stripe API", + "db": "Database & ORM", + "dev-deploy": "Develop and Deploy", + "env-vars": "Environment Variables", + "image": "Image Optimization", + "custom-worker": "Custom Worker", + "keep_names": "__name issues", + "workerd": "workerd specific packages" +} diff --git a/pages/azion/howtos/custom-worker.mdx b/pages/azion/howtos/custom-worker.mdx new file mode 100644 index 0000000..cab9ea9 --- /dev/null +++ b/pages/azion/howtos/custom-worker.mdx @@ -0,0 +1,43 @@ +## Custom Worker + +The worker generated by the Cloudflare adapter only exports [a fetch handler](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/). + +Sometimes your application needs to expose another type of handler (i.e. [a scheduled handler](https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/)) or export a [Durable Object](https://developers.cloudflare.com/durable-objects/api/base/). This can be achieved by creating a custom worker. + +The custom worker re-uses the generated fetch handler. + +### Create your custom worker Worker + +The following custom worker re-uses the generated fetch handler and adds a scheduled handler: + +```ts +// custom-worker.ts + +// @ts-ignore `.open-next/worker.ts` is generated at build time +import { default as handler } from "./.open-next/worker.js"; + +export default { + fetch: handler.fetch, + + async scheduled(event) { + // ... + }, +} satisfies ExportedHandler; + +// The re-export is only required if your app uses the DO Queue and DO Tag Cache +// See https://opennext.js.org/cloudflare/caching for details +// @ts-ignore `.open-next/worker.ts` is generated at build time +export { DOQueueHandler, DOShardedTagCache } from "./.open-next/worker.js"; +``` + +See [an example in the adapter repository](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/examples/playground14/worker.ts). + +### Update the entry point in your wrangler configuration + +```diff +// wrangler.jsonc +{ +- "main": "./.open-next/worker.js" ++ "main": "./path/to/custom-worker.ts", +} +``` diff --git a/pages/azion/howtos/db.mdx b/pages/azion/howtos/db.mdx new file mode 100644 index 0000000..2b9d0cd --- /dev/null +++ b/pages/azion/howtos/db.mdx @@ -0,0 +1,229 @@ +This page will show you how to setup some popular database ORM libraries to use in OpenNext. There are some subtleties to be aware of when using these libraries in Cloudflare Workers, so we will cover those here. + +If you encounter issue with a specific library, please open an issue on the [OpenNext GitHub repository](https://github.com/opennextjs/opennextjs-cloudflare/issues). + +## Drizzle ORM + +[Drizzle](https://developers.cloudflare.com/d1/reference/community-projects/#drizzle-orm) is a TypeScript ORM for SQL databases. It is designed to be lightweight and easy to use, making it a great choice for Cloudflare Workers. +There is not much specific to configure in Drizzle, but there is one important thing to note is that you don't want to have a global client. + +### `lib/db.ts` + +Instead of creating a global client, you should create a new client for each request. This is because some adapters (like Postgres) will use a connection pool, and reuse the same connection for multiple requests. This is not allowed in Cloudflare Workers, and will cause subsequent requests to fail. + +#### PostgreSQL + +Instead of that : + +```ts +//lib/db.ts +import { drizzle } from "drizzle-orm/node-postgres"; +import * as schema from "./schema/pg"; +import { Pool } from "pg"; + +const pool = new Pool({ + connectionString: process.env.PG_URL, +}); + +export const db = drizzle({ client: pool, schema }); +``` + +You should do this instead: + +```ts +//lib/db.ts +import { drizzle } from "drizzle-orm/node-postgres"; +// You can use cache from react to cache the client during the same request +// this is not mandatory and only has an effect for server components +import { cache } from "react"; +import * as schema from "./schema/pg"; +import { Pool } from "pg"; + +export const getDb = cache(() => { + const pool = new Pool({ + connectionString: process.env.PG_URL, + // You don't want to reuse the same connection for multiple requests + maxUses: 1, + }); + return drizzle({ client: pool, schema }); +}); +``` + +#### D1 example + +```ts +import { getCloudflareContext } from "@opennextjs/cloudflare"; +import { drizzle } from "drizzle-orm/d1"; +import { cache } from "react"; +import * as schema from "./schema/d1"; + +export const getDb = cache(() => { + const { env } = getCloudflareContext(); + return drizzle(env.MY_D1, { schema }); +}); + +// This is the one to use for static routes (i.e. ISR/SSG) +export const getDbAsync = cache(async () => { + const { env } = await getCloudflareContext({ async: true }); + return drizzle(env.MY_D1, { schema }); +}); +``` + +#### Hyperdrive example + +```ts +import { getCloudflareContext } from "@opennextjs/cloudflare"; +import { drizzle } from "drizzle-orm/node-postgres"; +import { cache } from "react"; +import * as schema from "./schema/pg"; +import { Pool } from "pg"; + +export const getDb = cache(() => { + const { env } = getCloudflareContext(); + const connectionString = env.HYPERDRIVE.connectionString; + const pool = new Pool({ + connectionString: process.env.PG_URL, + // You don't want to reuse the same connection for multiple requests + maxUses: 1, + }); + return drizzle({ client: pool, schema }); +}); + +// This is the one to use for static routes (i.e. ISR/SSG) +export const getDbAsync = cache(async () => { + const { env } = await getCloudflareContext({ async: true }); + const connectionString = env.HYPERDRIVE.connectionString; + const pool = new Pool({ + connectionString: process.env.PG_URL, + // You don't want to reuse the same connection for multiple requests + maxUses: 1, + }); + return drizzle({ client: pool, schema }); +}); +``` + +You can then use the `getDb` function to get a new client for each request. This will ensure that you don't run into any issues with connection pooling. + +## Prisma ORM + +[Prisma](https://developers.cloudflare.com/d1/reference/community-projects/#prisma-orm) is a popular ORM for Node.js and TypeScript. It is designed to be easy to use and provides a lot of features out of the box. However, there are some subtleties to be aware of when using Prisma in Cloudflare Workers. + +### `schema.prisma` + +When using prisma in OpenNext, you do not want to provide an output directory for the generated client. + +```prisma +generator client { + provider = "prisma-client-js" + previewFeatures = ["driverAdapters"] +} +``` + +This is because the generated client needs to be patched by OpenNext to work with Cloudflare Workers. If you provide an output directory, OpenNext will not be able to patch the client and it will not work. + +### `next.config.ts` + +Because prisma has some specific exports for cloudflare workers, you need to add the following to your `next.config.ts` file: + +```ts +const nextConfig: NextConfig = { + serverExternalPackages: ["@prisma/client", ".prisma/client"], +}; +``` + +By doing this, this will ensure that both the generated client and the prisma client are included in the build for the `workerd` runtime. + +### `lib/db.ts` + +Instead of creating a global client, you should create a new client for each request. This is because some adapters (like Postgres) will use a connection pool, and reuse the same connection for multiple requests. This is not allowed in Cloudflare Workers, and will cause subsequent requests to fail. + +#### D1 example + +Instead of that : + +```ts +//lib/db.ts +import { getCloudflareContext } from "@opennextjs/cloudflare"; +import { PrismaClient } from "@prisma/client"; +import { PrismaD1 } from "@prisma/adapter-d1"; + +const { env } = getCloudflareContext(); +const adapter = new PrismaD1(env.MY_D1); +export const db = new PrismaClient(); +``` + +You should do this instead: + +```ts +//lib/db.ts +import { getCloudflareContext } from "@opennextjs/cloudflare"; +// You can use cache from react to cache the client during the same request +// this is not mandatory and only has an effect for server components +import { cache } from "react"; +import { PrismaClient } from "@prisma/client"; +import { PrismaD1 } from "@prisma/adapter-d1"; + +export const getDb = cache(() => { + const { env } = getCloudflareContext(); + const adapter = new PrismaD1(env.MY_D1); + return new PrismaClient({ adapter }); +}); + +// If you need access to `getCloudflareContext` in a static route (i.e. ISR/SSG), you should use the async version of `getCloudflareContext` to get the context. +export const getDbAsync = async () => { + const { env } = await getCloudflareContext({ async: true }); + const adapter = new PrismaD1(env.MY_D1); + const prisma = new PrismaClient({ adapter }); + return prisma; +}; +``` + +You can then use the `getDb` function to get a new client for each request. This will ensure that you don't run into any issues with connection pooling. + +#### PostgreSQL + +You can also use Prisma with PostgreSQL. The setup is similar to the D1 setup above, but you need to use the `PrismaPostgres` adapter instead of the `PrismaD1` adapter. + +```ts +import { cache } from "react"; +import { PrismaClient } from "@prisma/client"; +import { PrismaPg } from "@prisma/adapter-pg"; + +export const getDb = cache(() => { + const connectionString = process.env.PG_URL ?? ""; + const adapter = new PrismaPg({ connectionString, maxUses: 1 }); + const prisma = new PrismaClient({ adapter }); + return prisma; +}); +``` + +You can then use the `getDb` function to get a new client for each request. This will ensure that you don't run into any issues with connection pooling. + +#### Hyperdrive + +You can also use Prisma with Hyperdrive. The setup is similar to the PostgreSQL setup above. + +```ts +//lib/db.ts +import { getCloudflareContext } from "@opennextjs/cloudflare"; +// You can use cache from react to cache the client during the same request +// this is not mandatory and only has an effect for server components +import { cache } from "react"; +import { PrismaClient } from "@prisma/client"; +import { PrismaPg } from "@prisma/adapter-pg"; + +export const getDb = cache(() => { + const { env } = getCloudflareContext(); + const connectionString = env.HYPERDRIVE.connectionString; + const adapter = new PrismaPg({ connectionString, maxUses: 1 }); + return new PrismaClient({ adapter }); +}); + +// This is the one to use for static routes (i.e. ISR/SSG) +export const getDbAsync = async () => { + const { env } = await getCloudflareContext({ async: true }); + const connectionString = env.HYPERDRIVE.connectionString; + const adapter = new PrismaPg({ connectionString, maxUses: 1 }); + return new PrismaClient({ adapter }); +}; +``` diff --git a/pages/azion/howtos/dev-deploy.mdx b/pages/azion/howtos/dev-deploy.mdx new file mode 100644 index 0000000..d7ea61d --- /dev/null +++ b/pages/azion/howtos/dev-deploy.mdx @@ -0,0 +1,88 @@ +import { Callout } from "nextra/components"; + +## Develop and deploy + +### Development workflow + +The primary purpose of `@opennextjs/cloudflare` is to take a Next.js application, built with standard Next.js tooling, and convert it into a format compatible with Cloudflare Workers. + +This code transformation process takes some time, making the adapter less than ideal for active application development, where a very fast feedback loop and other quality-of-life features, such as Hot Module Replacement (HMR), are crucial. Fortunately, Vercel already provides excellent tooling for this workflow, which Next.js developers are likely already familiar with. + +We recommend that developers continue using the tools they are already comfortable with for local development and then use `@opennextjs/cloudflare` when they are ready to deploy their applications to the Cloudflare platform. + +Let's explore, in more detail, the application development workflow we recommend for the best developer experience. + +#### Create a new application based on a template + +To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: + +```bash +npm create cloudflare@latest -- my-next-app --framework=next --platform=workers +``` + +#### Develop locally using `next dev` + +We believe that the best development workflow uses the `next dev` command provided by Next.js. + +To access Cloudflare resources using the `getCloudflareContext` API while running `next dev`, you will need to update the Next.js configuration to call `initOpenNextCloudflareForDev`, as shown in the following example: + +```ts +// next.config.ts +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; + +import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; +initOpenNextCloudflareForDev(); +``` + +#### Use `opennextjs-cloudflare` to build and test on the Workers runtime + +After you've finished iterating on your Next.js application with `next dev`, you can convert it to a Cloudflare Worker by running the `opennextjs-cloudflare build` command. This will generate the Worker code in the `.open-next` directory. + +You can then preview the app locally in the Cloudflare Workers runtime. + +To preview your worker locally, run the `opennextjs-cloudflare preview` command. This will populate the cache and create a local server that runs your worker in the Cloudflare Workers runtime. Testing your worker is important to ensure that it has been properly built and is working as expected. + +### Deploy your application to Cloudflare Workers + +Both the `deploy` and `upload` commands of `opennextjs-cloudflare` can be used to deploy your application to cloudflare Workers. Both commands will initialize the remote cache and upload your application to the Cloudflare infrastructure. + +While `deploy` will start serving your application as soon as it is uploaded, `upload` only creates a new version of the application so that you can use [gradual deployments](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/). + +#### Local build + +Use the `build` command followed by either `deploy` or `upload` to deploy your local build. + + + When running the `build` command locally, `.dev.vars` and [Next `.env` + files](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables) might + override your configuration. It is preferreable to use a CD system as [Workers + Builds](https://developers.cloudflare.com/workers/ci-cd/builds/) to deploy your application for reproducible + deployments. + + +#### Workers Builds + +When using Workers Builds, make sure to setup your environment variables as explained in [this guide](https://opennext.js.org/cloudflare/howtos/env-vars#production). + +You can then connect your GitHub repository by following [the documentation](https://developers.cloudflare.com/workers/ci-cd/builds/git-integration/). + +In the Build settings: + +- The "Build command" should be set to `npx opennextjs-cloudflare build`. +- The "Deploy command" should be set to `npx opennextjs-cloudflare deploy` (or `upload` to use gradual deployments). + + + The `deploy`, `upload`, and `populateCache` commands of `opennextjs-cloudflare` invoke `wrangler`. + You can pass arguments to `wrangler` by specifying them after `--`: + +```sh +opennextjs-cloudflare deploy -- --env=prod +``` + + diff --git a/pages/azion/howtos/env-vars.mdx b/pages/azion/howtos/env-vars.mdx new file mode 100644 index 0000000..9ec2b04 --- /dev/null +++ b/pages/azion/howtos/env-vars.mdx @@ -0,0 +1,44 @@ +import { Callout } from "nextra/components"; + +## Environment variables + +This entry describe the most sensible way to handle your environment variables which works well both during local development and once your application is deployed to Cloudflare Workers. + +On the Cloudflare platform, your environment variables can be stored in either ["Enviroment variables"](https://developers.cloudflare.com/workers/configuration/environment-variables/) or ["Secrets"](https://developers.cloudflare.com/workers/configuration/secrets/). The difference being that Secrets can not be read back from either the dashboard or the CLI after being created. + +### Local development + +While there are multiple ways to set environment variables for local development on the Cloudflare platform (adding them to to your [wrangler configuration](https://developers.cloudflare.com/workers/configuration/secrets/) or to a [.dev.vars](https://developers.cloudflare.com/workers/configuration/secrets/) file) that does not play well with the recommended development workflow as they would not be available while using `next dev`. + +What you should do instead is to use the Next.js [`.env` files](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables). By doing so the environment variables will be available on `process.env` both while running `next dev` and when running your app locally on a Worker with `wrangler dev`. + +Next.js `.env` files are environment specific. That is a `.env.development` will take precedence over a `.env` file when you use the "development" environment. See the Next.js site for a detailed explanation of the [loading order](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables). + +You should use the `NEXTJS_ENV` environment variable to select the environment to use when running your app locally on a worker, that's how you would select the "development" environment: + +```plain +# .dev.vars +NEXTJS_ENV=development +``` + +The "production" environment is used by default when `NEXTJS_ENV` is not explicitly set. + +### Production + +`.env` and `.dev.vars` are local files that should not be added to source control. You should instead use the Cloudflare dashboard to set your environment variables for production. + +#### Workers Builds + +When you use [Workers Builds](https://developers.cloudflare.com/workers/ci-cd/builds/) to deploy your application, the environment variables must be set in the ["Build variables and secrets"](https://developers.cloudflare.com/workers/ci-cd/builds/configuration/). + +By settings the "Build variables and secrets", the Next build executed by Workers Builds will have access to the environment variables. It needs that access to inline the [`NEXT_PUBLIC_...` variables](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) and access non-`NEXT_PUBLIC_...` variables needed for SSG pages. + +#### Runtime variables + +Your Next application needs to access environment variables at runtime. You should always set the runtime environment variables [in the Cloudflare dashboard](https://developers.cloudflare.com/workers/configuration/environment-variables/#add-environment-variables-via-the-dashboard) + + + If you set environment variables from the dashboard, you can use the + [`--keep-vars`](https://developers.cloudflare.com/workers/wrangler/commands/#deploy) option of wrangler to + prevent them from being deleted by deployments, i.e. `opennextjs-cloudflare deploy -- --keep-vars` + diff --git a/pages/azion/howtos/image.mdx b/pages/azion/howtos/image.mdx new file mode 100644 index 0000000..64307e1 --- /dev/null +++ b/pages/azion/howtos/image.mdx @@ -0,0 +1,71 @@ +import { Callout } from "nextra/components"; + +## Image Optimization + +Next.js has a builtin [`` component](https://nextjs.org/docs/pages/building-your-application/optimizing/images) to automatically optimize your images for faster page loads. + +In this post, we will look at how to integrate the Next.js image optimization with [Cloudflare Images](https://developers.cloudflare.com/images) + +### Enable Cloudflare Images + +You first need to [enable Cloudflare Images](https://developers.cloudflare.com/images/get-started/#enable-transformations-on-your-zone) for your zone. + +It is strongly advised to restrict the image origins that can be transformed to where your images are hosted, i.e. a [R2 bucket](https://developers.cloudflare.com/r2/buckets/). + +### Use a custom loader + +You then need to configure your Next application to use a custom loader for Cloudflare Images. + +Create an `image-loader.ts` at the root of your application: + +```ts +// image-loader.ts +import type { ImageLoaderProps } from "next/image"; + +const normalizeSrc = (src: string) => { + return src.startsWith("/") ? src.slice(1) : src; +}; + +export default function cloudflareLoader({ src, width, quality }: ImageLoaderProps) { + if (process.env.NODE_ENV === "development") { + // Serve the original image when using `next dev` + return src; + } + const params = [`width=${width}`]; + if (quality) { + params.push(`quality=${quality}`); + } + const paramsString = params.join(","); + return `/cdn-cgi/image/${paramsString}/${normalizeSrc(src)}`; +} +``` + + + This simple loader does not respect [Next.js + remotePatterns](https://nextjs.org/docs/pages/api-reference/components/image#remotepatterns). You should + configure [the allowed source origins](https://developers.cloudflare.com/images/transform-images/sources/) + in the dashboard. + + +You will then need to update your app configuration to use this loader: + +```ts +// next.config.ts +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + // ... + images: { + loader: "custom", + loaderFile: "./image-loader.ts", + }, +}; + +export default nextConfig; +``` + + + Images using the cloudflare loader are served directly without going through the middleware. + + +See more details in the [Cloudflare Images documentation](https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/). diff --git a/pages/azion/howtos/keep_names.mdx b/pages/azion/howtos/keep_names.mdx new file mode 100644 index 0000000..acd3014 --- /dev/null +++ b/pages/azion/howtos/keep_names.mdx @@ -0,0 +1,38 @@ +import { Callout } from "nextra/components"; + +## `__name` issues + +When using the OpenNext adapter, Wrangler processes the worker's code with [esbuild](https://esbuild.github.io/), and by default +enables the [`keep-names`](https://esbuild.github.io/api/#keep-names) option. While this is generally useful for debugging, it can +cause issues with certain Next.js libraries (e.g. [next-themes](https://www.npmjs.com/package/next-themes)) that convert scripts +to strings. This happens because `esbuild` introduces a `__name` function at the top of modules, which may inadvertently appear +in the generated script strings. When these strings are evaluated at runtime, the `__name` function might therefore not be defined, +leading to errors logged in the developper console: + +``` +Uncaught ReferenceError: __name is not defined +``` + + + Note that depending on your minification settings, the `__name` identifier might be minified, making the + error message less clear and potentially not explicitly mentioning `__name` in such cases. + + +### How to fix such issues + +To fix the issue you can simply set the `keep_names` option to `false` in your [`wrangler.jsonc` file](https://developers.cloudflare.com/workers/wrangler/configuration), like in the following example: + +```jsonc +{ + "$schema": "node_modules/wrangler/config-schema.json", + "main": ".open-next/worker.js", + "name": "my-app", + "keep_names": false, + /* ... */ +} +``` + +One potential drawback of this solution is that, depending on your minification settings, you may lose the ability to view the original +function names in debugging tools. + +You must use Wrangler version `4.13.0` or later to use this option. diff --git a/pages/azion/howtos/stripeAPI.mdx b/pages/azion/howtos/stripeAPI.mdx new file mode 100644 index 0000000..4c024b3 --- /dev/null +++ b/pages/azion/howtos/stripeAPI.mdx @@ -0,0 +1,14 @@ +## [Stripe API](https://www.npmjs.com/package/stripe) + +When [creating a Stripe object](https://docs.stripe.com/js/initializing), the default http client implementation is based on `node:https` which is not implemented on Workers. + +However you can use an http client based on fetch ([`FetchHttpClient`](https://github.com/stripe/stripe-node/blob/54d423e5d1118dc35c4b76260889826003e00e9f/src/net/FetchHttpClient.ts)) via the `httpClient` option: + +```ts +import Stripe from "stripe"; + +const stripe = Stripe(STRIPE_API_KEY, { + // Cloudflare Workers use the Fetch API for their API requests. + httpClient: Stripe.createFetchHttpClient(), +}); +``` diff --git a/pages/azion/howtos/workerd.mdx b/pages/azion/howtos/workerd.mdx new file mode 100644 index 0000000..2694c62 --- /dev/null +++ b/pages/azion/howtos/workerd.mdx @@ -0,0 +1,49 @@ +## `workerd` specific code + +### Configuration + +[`workerd`](https://github.com/cloudflare/workerd) is the runtime cloudflare uses to run Workers code. + +While the [`nodejs_compat`](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag) flag makes `workerd` mostly compatible with Node.js, +there are still minor differences. Some packages publish code for different runtimes to account for those differences. For example, `postgres` has [conditional exports](https://nodejs.org/api/packages.html#conditional-exports) in its [`package.json`](https://github.com/porsager/postgres/blob/ad0ed4476e09f41f147859cb5a42971d2b99e9c7/package.json#L8-L13): + +```json +"exports": { + "types": "./types/index.d.ts", + "bun": "./src/index.js", + "workerd": "./cf/src/index.js", + "import": "./src/index.js", + "default": "./cjs/src/index.js" +}, +``` + +With such exports, Node.js applications use either `src/index.js` or `cjs/src/index.js` depending if the app use ESM or CJS. + +However we want to use the `workerd` specific entrypoint when using the Cloudflare adapter. +For that, you need to instruct Next.js not to bundle packages as it would use the node conditions by default. + +To do that, add those packages in the `serverExternalPackages` key of your `next.config.ts`: + +```ts +// node.config.ts +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + serverExternalPackages: ["@prisma/client", ".prisma/client", "postgres"], +}; + +import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; +initOpenNextCloudflareForDev(); + +export default nextConfig; +``` + +### Packages known to have `workerd` specific code + +- `@libsql/isomorphic-ws` +- `@prisma/client` (and the generated `.prisma/client`) +- `jose` +- `postgres` +- `react-textarea-autosize` + +Please report an issue on [the adapter GH repository](https://github.com/opennextjs/opennextjs-cloudflare/issues) to have packages added to this list. diff --git a/pages/azion/index.mdx b/pages/azion/index.mdx new file mode 100644 index 0000000..5329edf --- /dev/null +++ b/pages/azion/index.mdx @@ -0,0 +1,72 @@ +import { Callout } from "nextra/components"; +import WindowsSupport from "../../shared/WindowsSupport.mdx"; + +## Azion + +The [`@opennextjs/azion`](https://www.npmjs.com/package/@opennextjs/azion) adapter lets you deploy Next.js apps to [Azion Edge Functions](https://www.azion.com/en/products/edge-functions/) using the [Node.js "runtime" from Next.js](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). + +### Get Started + +##### New apps + +To create a new Next.js app, pre-configured to run on Azion using `@opennextjs/azion`, run: + +``` +npm create azion@latest -- my-next-app --framework=next --platform=edge-functions +``` + +##### Existing Next.js apps + +Follow the guide [here](/azion/get-started) to use [@opennextjs/azion](https://www.npmjs.com/package/@opennextjs/azion) with an existing Next.js app. + +### Supported Next.js runtimes + +Next.js has [two "runtimes"](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes) — "Edge" and "Node.js". When you use `@opennextjs/azion`, your app should use the Node.js runtime, which is more fully featured, and allows you to use the [Node.js APIs](https://www.azion.com/en/documentation/products/edge-functions/runtime/) that are provided by the Azion Edge Functions runtime. + +This is an important difference from `@cloudflare/next-on-pages`, which only supports the "Edge" runtime. The Edge Runtime code in Next.js [intentionally constrains which APIs from Node.js can be used](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/webpack/plugins/middleware-plugin.ts#L820), and the "Edge" runtime does not support all Next.js features. + +### Supported Next.js versions + +All minor and patch versions of Next.js 15 and the latest minor of Next.js 14 are supported. + +To help improve compatibility, we encourage you to [report bugs](https://github.com/aziontech/opennextjs-azion/issues) and contribute code! + +### Supported Next.js features + +- [x] [App Router](https://nextjs.org/docs/app) +- [x] [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) +- [x] [Dynamic routes](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) +- [x] [Static Site Generation (SSG)](https://nextjs.org/docs/app/building-your-application/rendering/server-components#static-rendering-default) +- [x] [Server-Side Rendering (SSR)](https://nextjs.org/docs/app/building-your-application/rendering/server-components) +- [x] [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) +- [ ] [Node Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware#runtime) introduced in 15.2 are not yet supported +- [ ] [Image optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) (See [this guide](/azion/howtos/image) to configure [Azion Image Processor](https://www.azion.com/en/products/image-processor/)) +- [x] [Partial Prerendering (PPR)](https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering) +- [x] [Pages Router](https://nextjs.org/docs/pages) +- [x] [Incremental Static Regeneration (ISR)](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration) +- [x] [Support for after](https://nextjs.org/blog/next-15-rc#executing-code-after-a-response-with-nextafter-experimental) +- [ ] [Composable Caching](https://nextjs.org/blog/composable-caching) (`'use cache'`) + +We welcome both contributions and feedback! + +### Windows support + + + +### How `@opennextjs/azion` Works + +The OpenNext Azion adapter works by taking the Next.js build output and transforming it, so that it can run in Azion Edge Functions. + +When you add [@opennextjs/azion](https://www.npmjs.com/package/@opennextjs/azion) as a dependency to your Next.js app, and then run `npx opennextjs-azion` the adapter first builds your app by running the `build` script in your `package.json`, and then transforms the build output to a format that you can run locally using [Azion CLI](https://www.azion.com/en/documentation/products/cli/), and deploy to Azion. + +You can view the code for `@opennextjs/azion` [here](https://github.com/aziontech/opennextjs-azion/tree/main/packages/azion/src) to understand what it does under the hood. + +### Note on Edge Function Size Limits + +The size limit of an Azion Edge Function is 50 MiB. After building your function, the CLI will show both the original and compressed sizes: + +``` +Total Upload: 13833.20 KiB / gzip: 2295.89 KiB +``` + +Only the latter (compressed size) matters for [the Edge Function size limit](https://www.azion.com/en/documentation/products/edge-functions/limits/). diff --git a/pages/azion/known-issues.mdx b/pages/azion/known-issues.mdx new file mode 100644 index 0000000..ea8f040 --- /dev/null +++ b/pages/azion/known-issues.mdx @@ -0,0 +1,31 @@ +### Known issues + +#### Caching Durable Objects (`DOQueueHandler` and `DOShardedTagCache`) + +If your app uses [Durable Objects](https://developers.cloudflare.com/durable-objects/) for caching, you might see a warning while building your app: + +```text +┌─────────────────────────────────┐ +│ OpenNext — Building Next.js app │ +└─────────────────────────────────┘ + + +> next build + + ▲ Next.js 15.2.4 + +▲ [WARNING] You have defined bindings to the following internal Durable Objects: + + - {"name":"NEXT_CACHE_DO_QUEUE","class_name":"DOQueueHandler"} + These will not work in local development, but they should work in production. + + If you want to develop these locally, you can define your DO in a separate Worker, with a + separate configuration file. + For detailed instructions, refer to the Durable Objects section here: + https://developers.cloudflare.com/workers/wrangler/api#supported-bindings + + Creating an optimized production build ... +workerd/server/server.c++:1951: warning: A DurableObjectNamespace in the config referenced the class "DOQueueHandler", but no such Durable Object class is exported from the worker. Please make sure the class name matches, it is exported, and the class extends 'DurableObject'. Attempts to call to this Durable Object class will fail at runtime, but historically this was not a startup-time error. Future versions of workerd may make this a startup-time error. +``` + +The warning can be safely ignored as the caching Durable Objects are not used during the build. diff --git a/pages/azion/troubleshooting.mdx b/pages/azion/troubleshooting.mdx new file mode 100644 index 0000000..537e9be --- /dev/null +++ b/pages/azion/troubleshooting.mdx @@ -0,0 +1,127 @@ +import { Callout } from "nextra/components"; + +## Troubleshooting + +### Trying to deploy to Cloudflare Pages, instead of Cloudflare Workers? + +`@opennextjs/cloudflare` is specifically built for deploying Next.js apps to [Cloudflare Workers](https://developers.cloudflare.com/workers/) + +Cloudflare Workers now support the majority of functionality from Cloudflare Pages, and have features that are not yet supported by Cloudflare Pages. Refer to the [Compatibility Matrix](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/) in the Cloudflare Workers docs. + +If you need to deploy to Cloudflare Pages, you can use `@cloudflare/next-on-pages`, and follow the [Cloudflare Pages guides for deploying Next.js apps](https://developers.cloudflare.com/pages/framework-guides/nextjs/). + +### "Your Worker exceeded the size limit of 3 MiB" + +The Cloudflare Account you are deploying to is on the Workers Free plan, which [limits the size of each Worker to 3 MiB](https://developers.cloudflare.com/workers/platform/limits/#worker-size). When you subscribe to the Workers Paid plan, each Worker can be up to 10 MiB. + +When deploying your Worker, `wrangler` will show both the original and compressed sizes. Only the latter (gzipped size) matters for these limits. + +### "Your Worker exceeded the size limit of 10 MiB" + +If your Worker is larger than 10 MiB compressed — there might be unnecessary code ending up in your production bundle. You can visualize and understand this by running: + +1. `npx opennextjs-cloudflare build` within your project's root directory +2. `cd .open-next/server-functions/default` to open the directory that contains the bundled code +3. Take the file named `handler.mjs.meta.json` and use the [ESBuild Bundle Analyzer](https://esbuild.github.io/analyze/) to visualize your application's code, and understand the largest parts of your production bundle + +### My app fails to build when I import a specific NPM package + +First, make sure that the `nodejs_compat` compatibility flag is enabled, and your compatibility date is set to on or after "2024-09-23", in your [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). +Refer to the [Node.js Workers docs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) for more details on Node.js support in Cloudflare Workers. + +Some NPM packages define multiple exports. For example: + +``` +"exports": { + "other": "./src/other.js", + "node": "./src/node.js", + "browser": "./src/browser.js", + "default": "./src/default.js" +}, +``` + +When you use `@opennextjs/cloudflare`, [Wrangler](https://developers.cloudflare.com/workers/wrangler/) bundles your code before running it locally, or deploying it to Cloudflare. Wrangler has to choose which export to use, when you import a module. By default, Wrangler, which uses [esbuild](https://esbuild.github.io/), handles this in a way that is not compatible with some NPM packages. + +You may want to modify how Wrangler resolves multiple exports, such that when you import packages, the `node` export, if present, is used. You can do do by defining the following variables in a `.env` file within the root directory of your Next.js app: + +``` +WRANGLER_BUILD_CONDITIONS="" +WRANGLER_BUILD_PLATFORM="node" +``` + +### `Error: Cannot perform I/O on behalf of a different request.` + +Some DB clients (i.e. [`postgres`](https://www.npmjs.com/package/postgres)) create a connection to the DB server when they are first instantiated and re-use it for later requests. +This programming model is not compatible with the Workers runtime where a connection can not be re-used in a different request. + +The following error is generated in such a case: + +```text +⨯ Error: Cannot perform I/O on behalf of a different request. I/O objects (such as streams, request/response bodies, and others) created in the context of one request handler cannot be accessed from a different request's handler. This is a limitation of Cloudflare Workers which allows us to improve overall performance. (I/O type: Writable) +``` + +To solve this, you should create the DB client inside a request context and not keep a global DB client. + +A global client would not work: + +```ts +// src/lib/db.ts +import postgres from "postgres"; + +// `client` is global. +// As the connection would be shared across requests, it fails on worker +export const client = postgres(process.env.DATABASE_URL, { max: 5 }); + +// src/app/api/route.ts +import { client } from "@/db/db"; + +export const dynamic = "force-dynamic"; + +export async function GET() { + return new Response(JSON.stringify(await client`SELECT * FROM users;`)); +} +``` + +It can fixed by creating the client for each incoming request: + +```ts +// src/app/api/route.ts +export const dynamic = "force-dynamic"; + +export async function GET() { + // The client is created for each incoming request and no connection is shared across requests + const client = postgres(process.env.DATABASE_URL, { max: 5 }); + return new Response(JSON.stringify(await client`SELECT * FROM users;`)); +} +``` + +### `Error: Failed to load chunk server/chunks/ssr/.js` + +If you see an error similar to: + +```text +✘ [ERROR] ⨯ Error: Failed to load chunk server/chunks/ssr/.js + + at loadChunkPath + (...) + at Object.loadChunk + (...) + at .open-next/server-functions/default/.next/server/app/page.js +``` + +You are proably using a turbopack enabled build (`next build --turbo`) which is not currently supported by OpenNext. +Change your build command to `next build` to fix the issue. + +### `X [ERROR] Could not resolve ""` + +When you see the following error during the build: + +```text +⚙️ Bundling the OpenNext server... + +X [ERROR] Could not resolve "" +``` + +It might be because the package contains workerd specific code. + +Check this [howto](/cloudflare/howtos/workerd) for a solution. From 1308cde3e9f474f0e88c50612f5957c22992cc6a Mon Sep 17 00:00:00 2001 From: Vitor-Azion Date: Mon, 14 Jul 2025 10:57:55 -0300 Subject: [PATCH 2/3] chore: update Azion integration --- package-lock.json | 5106 ----------------- pages/azion/_meta.json | 13 +- pages/azion/bindings.mdx | 68 - pages/azion/examples.mdx | 19 +- pages/azion/former-releases/0.2/_meta.json | 7 - pages/azion/former-releases/0.2/bindings.mdx | 63 - pages/azion/former-releases/0.2/caching.mdx | 36 - pages/azion/former-releases/0.2/examples.mdx | 22 - .../azion/former-releases/0.2/get-started.mdx | 140 - pages/azion/former-releases/0.2/index.mdx | 65 - pages/azion/former-releases/0.3/_meta.json | 7 - pages/azion/former-releases/0.3/bindings.mdx | 63 - pages/azion/former-releases/0.3/caching.mdx | 47 - pages/azion/former-releases/0.3/examples.mdx | 24 - .../azion/former-releases/0.3/get-started.mdx | 194 - pages/azion/former-releases/0.3/index.mdx | 71 - pages/azion/former-releases/0.5/_meta.json | 7 - pages/azion/former-releases/0.5/bindings.mdx | 74 - pages/azion/former-releases/0.5/caching.mdx | 156 - pages/azion/former-releases/0.5/examples.mdx | 24 - .../azion/former-releases/0.5/get-started.mdx | 190 - pages/azion/former-releases/0.5/index.mdx | 73 - pages/azion/former-releases/0.6/_meta.json | 7 - pages/azion/former-releases/0.6/bindings.mdx | 74 - pages/azion/former-releases/0.6/caching.mdx | 418 -- pages/azion/former-releases/0.6/examples.mdx | 24 - .../azion/former-releases/0.6/get-started.mdx | 197 - pages/azion/former-releases/_meta.json | 10 - .../migrate-from-0.2-to-0.3.mdx | 82 - .../migrate-from-0.3-to-0.4.mdx | 38 - .../migrate-from-0.4-to-0.5.mdx | 3 - .../migrate-from-0.5-to-0.6.mdx | 47 - pages/azion/how-it-works.mdx | 9 + pages/azion/howtos/_meta.json | 10 - pages/azion/howtos/custom-worker.mdx | 43 - pages/azion/howtos/db.mdx | 229 - pages/azion/howtos/dev-deploy.mdx | 88 - pages/azion/howtos/env-vars.mdx | 44 - pages/azion/howtos/image.mdx | 71 - pages/azion/howtos/keep_names.mdx | 38 - pages/azion/howtos/stripeAPI.mdx | 14 - pages/azion/howtos/workerd.mdx | 49 - pages/azion/index.mdx | 35 +- 43 files changed, 26 insertions(+), 7973 deletions(-) delete mode 100644 package-lock.json delete mode 100644 pages/azion/bindings.mdx delete mode 100644 pages/azion/former-releases/0.2/_meta.json delete mode 100644 pages/azion/former-releases/0.2/bindings.mdx delete mode 100644 pages/azion/former-releases/0.2/caching.mdx delete mode 100644 pages/azion/former-releases/0.2/examples.mdx delete mode 100644 pages/azion/former-releases/0.2/get-started.mdx delete mode 100644 pages/azion/former-releases/0.2/index.mdx delete mode 100644 pages/azion/former-releases/0.3/_meta.json delete mode 100644 pages/azion/former-releases/0.3/bindings.mdx delete mode 100644 pages/azion/former-releases/0.3/caching.mdx delete mode 100644 pages/azion/former-releases/0.3/examples.mdx delete mode 100644 pages/azion/former-releases/0.3/get-started.mdx delete mode 100644 pages/azion/former-releases/0.3/index.mdx delete mode 100644 pages/azion/former-releases/0.5/_meta.json delete mode 100644 pages/azion/former-releases/0.5/bindings.mdx delete mode 100644 pages/azion/former-releases/0.5/caching.mdx delete mode 100644 pages/azion/former-releases/0.5/examples.mdx delete mode 100644 pages/azion/former-releases/0.5/get-started.mdx delete mode 100644 pages/azion/former-releases/0.5/index.mdx delete mode 100644 pages/azion/former-releases/0.6/_meta.json delete mode 100644 pages/azion/former-releases/0.6/bindings.mdx delete mode 100644 pages/azion/former-releases/0.6/caching.mdx delete mode 100644 pages/azion/former-releases/0.6/examples.mdx delete mode 100644 pages/azion/former-releases/0.6/get-started.mdx delete mode 100644 pages/azion/former-releases/_meta.json delete mode 100644 pages/azion/former-releases/migrate-from-0.2-to-0.3.mdx delete mode 100644 pages/azion/former-releases/migrate-from-0.3-to-0.4.mdx delete mode 100644 pages/azion/former-releases/migrate-from-0.4-to-0.5.mdx delete mode 100644 pages/azion/former-releases/migrate-from-0.5-to-0.6.mdx create mode 100644 pages/azion/how-it-works.mdx delete mode 100644 pages/azion/howtos/_meta.json delete mode 100644 pages/azion/howtos/custom-worker.mdx delete mode 100644 pages/azion/howtos/db.mdx delete mode 100644 pages/azion/howtos/dev-deploy.mdx delete mode 100644 pages/azion/howtos/env-vars.mdx delete mode 100644 pages/azion/howtos/image.mdx delete mode 100644 pages/azion/howtos/keep_names.mdx delete mode 100644 pages/azion/howtos/stripeAPI.mdx delete mode 100644 pages/azion/howtos/workerd.mdx diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 7a42dde..0000000 --- a/package-lock.json +++ /dev/null @@ -1,5106 +0,0 @@ -{ - "name": "@opennextjs/docs", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@opennextjs/docs", - "version": "0.0.0", - "dependencies": { - "@types/node": "18.11.9", - "@types/react": "18.0.25", - "@types/react-dom": "18.0.9", - "next": "13.4.12", - "nextra": "^2.13.1", - "nextra-theme-docs": "^2.13.1", - "react": "18.2.0", - "react-dom": "18.2.0", - "typescript": "4.9.3" - }, - "devDependencies": { - "prettier": "^3.5.2" - } - }, - "node_modules/@babel/runtime": { - "version": "7.25.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", - "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@braintree/sanitize-url": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz", - "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" - }, - "node_modules/@headlessui/react": { - "version": "1.7.19", - "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.19.tgz", - "integrity": "sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==", - "dependencies": { - "@tanstack/react-virtual": "^3.0.0-beta.60", - "client-only": "^0.0.1" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": "^16 || ^17 || ^18", - "react-dom": "^16 || ^17 || ^18" - } - }, - "node_modules/@mdx-js/mdx": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-2.3.0.tgz", - "integrity": "sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/mdx": "^2.0.0", - "estree-util-build-jsx": "^2.0.0", - "estree-util-is-identifier-name": "^2.0.0", - "estree-util-to-js": "^1.1.0", - "estree-walker": "^3.0.0", - "hast-util-to-estree": "^2.0.0", - "markdown-extensions": "^1.0.0", - "periscopic": "^3.0.0", - "remark-mdx": "^2.0.0", - "remark-parse": "^10.0.0", - "remark-rehype": "^10.0.0", - "unified": "^10.0.0", - "unist-util-position-from-estree": "^1.0.0", - "unist-util-stringify-position": "^3.0.0", - "unist-util-visit": "^4.0.0", - "vfile": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/mdx/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/mdx/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/react": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", - "integrity": "sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==", - "dependencies": { - "@types/mdx": "^2.0.0", - "@types/react": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "react": ">=16" - } - }, - "node_modules/@napi-rs/simple-git": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git/-/simple-git-0.1.19.tgz", - "integrity": "sha512-jMxvwzkKzd3cXo2EB9GM2ic0eYo2rP/BS6gJt6HnWbsDO1O8GSD4k7o2Cpr2YERtMpGF/MGcDfsfj2EbQPtrXw==", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@napi-rs/simple-git-android-arm-eabi": "0.1.19", - "@napi-rs/simple-git-android-arm64": "0.1.19", - "@napi-rs/simple-git-darwin-arm64": "0.1.19", - "@napi-rs/simple-git-darwin-x64": "0.1.19", - "@napi-rs/simple-git-freebsd-x64": "0.1.19", - "@napi-rs/simple-git-linux-arm-gnueabihf": "0.1.19", - "@napi-rs/simple-git-linux-arm64-gnu": "0.1.19", - "@napi-rs/simple-git-linux-arm64-musl": "0.1.19", - "@napi-rs/simple-git-linux-powerpc64le-gnu": "0.1.19", - "@napi-rs/simple-git-linux-s390x-gnu": "0.1.19", - "@napi-rs/simple-git-linux-x64-gnu": "0.1.19", - "@napi-rs/simple-git-linux-x64-musl": "0.1.19", - "@napi-rs/simple-git-win32-arm64-msvc": "0.1.19", - "@napi-rs/simple-git-win32-x64-msvc": "0.1.19" - } - }, - "node_modules/@napi-rs/simple-git-android-arm-eabi": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-android-arm-eabi/-/simple-git-android-arm-eabi-0.1.19.tgz", - "integrity": "sha512-XryEH/hadZ4Duk/HS/HC/cA1j0RHmqUGey3MsCf65ZS0VrWMqChXM/xlTPWuY5jfCc/rPubHaqI7DZlbexnX/g==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-android-arm64": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-android-arm64/-/simple-git-android-arm64-0.1.19.tgz", - "integrity": "sha512-ZQ0cPvY6nV9p7zrR9ZPo7hQBkDAcY/CHj3BjYNhykeUCiSNCrhvwX+WEeg5on8M1j4d5jcI/cwVG2FslfiByUg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-darwin-arm64": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-darwin-arm64/-/simple-git-darwin-arm64-0.1.19.tgz", - "integrity": "sha512-viZB5TYgjA1vH+QluhxZo0WKro3xBA+1xSzYx8mcxUMO5gnAoUMwXn0ZO/6Zy6pai+aGae+cj6XihGnrBRu3Pg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-darwin-x64": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-darwin-x64/-/simple-git-darwin-x64-0.1.19.tgz", - "integrity": "sha512-6dNkzSNUV5X9rsVYQbpZLyJu4Gtkl2vNJ3abBXHX/Etk0ILG5ZasO3ncznIANZQpqcbn/QPHr49J2QYAXGoKJA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-freebsd-x64": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-freebsd-x64/-/simple-git-freebsd-x64-0.1.19.tgz", - "integrity": "sha512-sB9krVIchzd20FjI2ZZ8FDsTSsXLBdnwJ6CpeVyrhXHnoszfcqxt49ocZHujAS9lMpXq7i2Nv1EXJmCy4KdhwA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-linux-arm-gnueabihf": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm-gnueabihf/-/simple-git-linux-arm-gnueabihf-0.1.19.tgz", - "integrity": "sha512-6HPn09lr9N1n5/XKfP8Np53g4fEXVxOFqNkS6rTH3Rm1lZHdazTRH62RggXLTguZwjcE+MvOLvoTIoR5kAS8+g==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-linux-arm64-gnu": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm64-gnu/-/simple-git-linux-arm64-gnu-0.1.19.tgz", - "integrity": "sha512-G0gISckt4cVDp3oh5Z6PV3GHJrJO6Z8bIS+9xA7vTtKdqB1i5y0n3cSFLlzQciLzhr+CajFD27doW4lEyErQ/Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-linux-arm64-musl": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-arm64-musl/-/simple-git-linux-arm64-musl-0.1.19.tgz", - "integrity": "sha512-OwTRF+H4IZYxmDFRi1IrLMfqbdIpvHeYbJl2X94NVsLVOY+3NUHvEzL3fYaVx5urBaMnIK0DD3wZLbcueWvxbA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-linux-powerpc64le-gnu": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-powerpc64le-gnu/-/simple-git-linux-powerpc64le-gnu-0.1.19.tgz", - "integrity": "sha512-p7zuNNVyzpRvkCt2RIGv9FX/WPcPbZ6/FRUgUTZkA2WU33mrbvNqSi4AOqCCl6mBvEd+EOw5NU4lS9ORRJvAEg==", - "cpu": [ - "powerpc64le" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-linux-s390x-gnu": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-s390x-gnu/-/simple-git-linux-s390x-gnu-0.1.19.tgz", - "integrity": "sha512-6N2vwJUPLiak8GLrS0a3is0gSb0UwI2CHOOqtvQxPmv+JVI8kn3vKiUscsktdDb0wGEPeZ8PvZs0y8UWix7K4g==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-linux-x64-gnu": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-x64-gnu/-/simple-git-linux-x64-gnu-0.1.19.tgz", - "integrity": "sha512-61YfeO1J13WK7MalLgP3QlV6of2rWnVw1aqxWkAgy/lGxoOFSJ4Wid6ANVCEZk4tJpPX/XNeneqkUz5xpeb2Cw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-linux-x64-musl": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-linux-x64-musl/-/simple-git-linux-x64-musl-0.1.19.tgz", - "integrity": "sha512-cCTWNpMJnN3PrUBItWcs3dQKCydsIasbrS3laMzq8k7OzF93Zrp2LWDTPlLCO9brbBVpBzy2Qk5Xg9uAfe/Ukw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-win32-arm64-msvc": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-win32-arm64-msvc/-/simple-git-win32-arm64-msvc-0.1.19.tgz", - "integrity": "sha512-sWavb1BjeLKKBA+PbTsRSSzVNfb7V/dOpaJvkgR5d2kWFn/AHmCZHSSj/3nyZdYf0BdDC+DIvqk3daAEZ6QMVw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@napi-rs/simple-git-win32-x64-msvc": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@napi-rs/simple-git-win32-x64-msvc/-/simple-git-win32-x64-msvc-0.1.19.tgz", - "integrity": "sha512-FmNuPoK4+qwaSCkp8lm3sJlrxk374enW+zCE5ZksXlZzj/9BDJAULJb5QUJ7o9Y8A/G+d8LkdQLPBE2Jaxe5XA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/env": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.12.tgz", - "integrity": "sha512-RmHanbV21saP/6OEPBJ7yJMuys68cIf8OBBWd7+uj40LdpmswVAwe1uzeuFyUsd6SfeITWT3XnQfn6wULeKwDQ==" - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.12.tgz", - "integrity": "sha512-deUrbCXTMZ6ZhbOoloqecnUeNpUOupi8SE2tx4jPfNS9uyUR9zK4iXBvH65opVcA/9F5I/p8vDXSYbUlbmBjZg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.12.tgz", - "integrity": "sha512-WRvH7RxgRHlC1yb5oG0ZLx8F7uci9AivM5/HGGv9ZyG2Als8Ij64GC3d+mQ5sJhWjusyU6T6V1WKTUoTmOB0zQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.12.tgz", - "integrity": "sha512-YEKracAWuxp54tKiAvvq73PUs9lok57cc8meYRibTWe/VdPB2vLgkTVWFcw31YDuRXdEhdX0fWS6Q+ESBhnEig==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.12.tgz", - "integrity": "sha512-LhJR7/RAjdHJ2Isl2pgc/JaoxNk0KtBgkVpiDJPVExVWA1c6gzY57+3zWuxuyWzTG+fhLZo2Y80pLXgIJv7g3g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.12.tgz", - "integrity": "sha512-1DWLL/B9nBNiQRng+1aqs3OaZcxC16Nf+mOnpcrZZSdyKHek3WQh6j/fkbukObgNGwmCoVevLUa/p3UFTTqgqg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.12.tgz", - "integrity": "sha512-kEAJmgYFhp0VL+eRWmUkVxLVunn7oL9Mdue/FS8yzRBVj7Z0AnIrHpTIeIUl1bbdQq1VaoOztnKicAjfkLTRCQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.12.tgz", - "integrity": "sha512-GMLuL/loR6yIIRTnPRY6UGbLL9MBdw2anxkOnANxvLvsml4F0HNIgvnU3Ej4BjbqMTNjD4hcPFdlEow4XHPdZA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.12.tgz", - "integrity": "sha512-PhgNqN2Vnkm7XaMdRmmX0ZSwZXQAtamBVSa9A/V1dfKQCV1rjIZeiy/dbBnVYGdj63ANfsOR/30XpxP71W0eww==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.12.tgz", - "integrity": "sha512-Z+56e/Ljt0bUs+T+jPjhFyxYBcdY2RIq9ELFU+qAMQMteHo7ymbV7CKmlcX59RI9C4YzN8PgMgLyAoi916b5HA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@swc/helpers": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz", - "integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tanstack/react-virtual": { - "version": "3.10.8", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.8.tgz", - "integrity": "sha512-VbzbVGSsZlQktyLrP5nxE+vE1ZR+U0NFAWPbJLoG2+DKPwd2D7dVICTVIIaYlJqX1ZCEnYDbaOpmMwbsyhBoIA==", - "dependencies": { - "@tanstack/virtual-core": "3.10.8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@tanstack/virtual-core": { - "version": "3.10.8", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.8.tgz", - "integrity": "sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@theguild/remark-mermaid": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/@theguild/remark-mermaid/-/remark-mermaid-0.0.5.tgz", - "integrity": "sha512-e+ZIyJkEv9jabI4m7q29wZtZv+2iwPGsXJ2d46Zi7e+QcFudiyuqhLhHG/3gX3ZEB+hxTch+fpItyMS8jwbIcw==", - "dependencies": { - "mermaid": "^10.2.2", - "unist-util-visit": "^5.0.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/@theguild/remark-npm2yarn": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@theguild/remark-npm2yarn/-/remark-npm2yarn-0.2.1.tgz", - "integrity": "sha512-jUTFWwDxtLEFtGZh/TW/w30ySaDJ8atKWH8dq2/IiQF61dPrGfETpl0WxD0VdBfuLOeU14/kop466oBSRO/5CA==", - "dependencies": { - "npm-to-yarn": "^2.1.0", - "unist-util-visit": "^5.0.0" - } - }, - "node_modules/@types/acorn": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", - "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/d3-scale": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", - "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-scale-chromatic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.3.tgz", - "integrity": "sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==" - }, - "node_modules/@types/d3-time": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", - "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/hast": { - "version": "2.3.10", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", - "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==" - }, - "node_modules/@types/katex": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", - "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==" - }, - "node_modules/@types/mdast": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", - "integrity": "sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==" - }, - "node_modules/@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" - }, - "node_modules/@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" - }, - "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" - }, - "node_modules/@types/react": { - "version": "18.0.25", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.25.tgz", - "integrity": "sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g==", - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.0.9", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.9.tgz", - "integrity": "sha512-qnVvHxASt/H7i+XG1U1xMiY5t+IHcPGUK7TDMDzom08xa7e86eCeKOiLZezwCKVxJn6NEiiy2ekgX8aQssjIKg==", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==" - }, - "node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" - }, - "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ansi-sequence-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", - "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==" - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/arg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/arg/-/arg-1.0.0.tgz", - "integrity": "sha512-Wk7TEzl1KqvTGs/uyhmHO/3XLd3t1UeU4IstvPXVzGPM522cTjqjNZ99esCkcL52sjqjo8e8CTBcWhkxvGzoAw==" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "bin": { - "astring": "bin/astring" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001660", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001660.tgz", - "integrity": "sha512-GacvNTTuATm26qC74pt+ad1fW15mlQ/zuTzzY1ZoIzECTP8HURDfF43kNxPgf7H1jmelCBQTTbBNxdSXOA7Bqg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz", - "integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==", - "dependencies": { - "ansi-styles": "^3.1.0", - "escape-string-regexp": "^1.0.5", - "supports-color": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" - }, - "node_modules/clipboardy": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-1.2.2.tgz", - "integrity": "sha512-16KrBOV7bHmHdxcQiCvfUFYVFyEah4FI8vYT1Fr7CGSA4G+xBWMEfUEQJS1hxeHGtI9ju1Bzs9uXSbj5HZKArw==", - "dependencies": { - "arch": "^2.1.0", - "execa": "^0.8.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/compute-scroll-into-view": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.0.tgz", - "integrity": "sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==" - }, - "node_modules/cose-base": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", - "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", - "dependencies": { - "layout-base": "^1.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", - "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" - }, - "node_modules/cytoscape": { - "version": "3.30.2", - "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.2.tgz", - "integrity": "sha512-oICxQsjW8uSaRmn4UK/jkczKOqTrVqt5/1WL0POiJUT2EKNc9STM4hYFHv917yu55aTBMFNRzymlJhVAiWPCxw==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/cytoscape-cose-bilkent": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", - "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", - "dependencies": { - "cose-base": "^1.0.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/d3": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", - "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", - "dependencies": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-contour": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", - "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", - "dependencies": { - "d3-array": "^3.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", - "dependencies": { - "delaunator": "5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv/node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "dependencies": { - "d3-dsv": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", - "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-sankey": { - "version": "0.12.3", - "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", - "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", - "dependencies": { - "d3-array": "1 - 2", - "d3-shape": "^1.2.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-array": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", - "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", - "dependencies": { - "internmap": "^1.0.0" - } - }, - "node_modules/d3-sankey/node_modules/d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" - }, - "node_modules/d3-sankey/node_modules/d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", - "dependencies": { - "d3-path": "1" - } - }, - "node_modules/d3-sankey/node_modules/internmap": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", - "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", - "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", - "dependencies": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" - } - }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dagre-d3-es": { - "version": "7.0.10", - "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.10.tgz", - "integrity": "sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==", - "dependencies": { - "d3": "^7.8.2", - "lodash-es": "^4.17.21" - } - }, - "node_modules/dayjs": { - "version": "1.11.13", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" - }, - "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/delaunator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", - "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", - "dependencies": { - "robust-predicates": "^3.0.2" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dompurify": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.6.tgz", - "integrity": "sha512-cTOAhc36AalkjtBpfG6O8JimdTMWNXjiePT2xQH/ppBGi/4uIpmj8eKyIkMJErXWARyINV/sB38yf8JCLF5pbQ==" - }, - "node_modules/elkjs": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", - "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estree-util-attach-comments": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-2.1.1.tgz", - "integrity": "sha512-+5Ba/xGGS6mnwFbXIuQiDPTbuTxuMCooq3arVv7gPZtYpjp+VXH/NkHAP35OOefPhNG/UGqU3vt/LTABwcHX0w==", - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-2.2.2.tgz", - "integrity": "sha512-m56vOXcOBuaF+Igpb9OPAy7f9w9OIkb5yhjsZuaPm7HoGi4oTOQi0h2+yZ+AtKklYFZ+rPC4n0wYCJCEU1ONqg==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "estree-util-is-identifier-name": "^2.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.1.0.tgz", - "integrity": "sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-1.2.0.tgz", - "integrity": "sha512-IzU74r1PK5IMMGZXUVZbmiu4A1uhiPgW5hm1GjcOfr4ZzHaMPpLNJjR7HjXiIOzi25nZDrgFTobHTkV5Q6ITjA==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-value-to-estree": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/estree-util-value-to-estree/-/estree-util-value-to-estree-1.3.0.tgz", - "integrity": "sha512-Y+ughcF9jSUJvncXwqRageavjrNPAI+1M/L3BI3PyLp1nmgYTGUXU6t5z1Y7OWuThoDdhPME07bQU+d5LxdJqw==", - "dependencies": { - "is-plain-obj": "^3.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/estree-util-visit": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-1.2.1.tgz", - "integrity": "sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/execa": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.8.0.tgz", - "integrity": "sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==", - "dependencies": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/flexsearch": { - "version": "0.7.43", - "resolved": "https://registry.npmjs.org/flexsearch/-/flexsearch-0.7.43.tgz", - "integrity": "sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg==" - }, - "node_modules/focus-visible": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.2.1.tgz", - "integrity": "sha512-8Bx950VD1bWTQJEH/AM6SpEk+SU55aVnp4Ujhuuxy3eMEBCRwBnTBnVXr9YAPvZL3/CNjCa8u4IWfNmEO53whA==" - }, - "node_modules/get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/git-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/git-up/-/git-up-7.0.0.tgz", - "integrity": "sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==", - "dependencies": { - "is-ssh": "^1.4.0", - "parse-url": "^8.1.0" - } - }, - "node_modules/git-url-parse": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-13.1.1.tgz", - "integrity": "sha512-PCFJyeSSdtnbfhSNRw9Wk96dDCNx+sogTe4YNXeXSJxt7xz5hvXekuRn9JX7m+Mf4OscCu8h+mtAl3+h5Fo8lQ==", - "dependencies": { - "git-up": "^7.0.0" - } - }, - "node_modules/github-slugger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", - "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/hash-obj": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hash-obj/-/hash-obj-4.0.0.tgz", - "integrity": "sha512-FwO1BUVWkyHasWDW4S8o0ssQXjvyghLV2rfVhnN36b2bbcj45eGiuzdn9XOvOpjV3TKQD7Gm2BWNXdE9V4KKYg==", - "dependencies": { - "is-obj": "^3.0.0", - "sort-keys": "^5.0.0", - "type-fest": "^1.0.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/hast-util-from-dom": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.0.tgz", - "integrity": "sha512-d6235voAp/XR3Hh5uy7aGLbM3S4KamdW0WEgOaU1YoewnuYw4HXb5eRtv9g65m/RFGEfUY1Mw4UqCc5Y8L4Stg==", - "dependencies": { - "@types/hast": "^3.0.0", - "hastscript": "^8.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-dom/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-from-html": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", - "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", - "dependencies": { - "@types/hast": "^3.0.0", - "devlop": "^1.1.0", - "hast-util-from-parse5": "^8.0.0", - "parse5": "^7.0.0", - "vfile": "^6.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html-isomorphic": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", - "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-dom": "^5.0.0", - "hast-util-from-html": "^2.0.0", - "unist-util-remove-position": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html-isomorphic/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-from-html/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-from-html/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/hast-util-from-html/node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", - "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^8.0.0", - "property-information": "^6.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-from-parse5/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/hast-util-from-parse5/node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", - "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-element/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-raw": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.4.tgz", - "integrity": "sha512-LHE65TD2YiNsHD3YuXcKPHXPLuYh/gjp12mOfU8jxSrm1f/yJpsb0F/KKljS6U9LJoP0Ux+tCe8iJ2AsPzTdgA==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-raw/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/hast-util-raw/node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw/node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-2.3.3.tgz", - "integrity": "sha512-ihhPIUPxN0v0w6M5+IiAZZrn0LH2uZomeWwhn7uP7avZC6TE7lIiEh2yBMPr5+zi1aUCXq6VoYRgs2Bw9xmycQ==", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "estree-util-attach-comments": "^2.0.0", - "estree-util-is-identifier-name": "^2.0.0", - "hast-util-whitespace": "^2.0.0", - "mdast-util-mdx-expression": "^1.0.0", - "mdast-util-mdxjs-esm": "^1.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^0.4.1", - "unist-util-position": "^4.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", - "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", - "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-to-text/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/hast-util-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", - "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", - "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inline-style-parser": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", - "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/intersection-observer": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz", - "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==" - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-3.0.0.tgz", - "integrity": "sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/is-ssh": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz", - "integrity": "sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==", - "dependencies": { - "protocols": "^2.0.1" - } - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==" - }, - "node_modules/katex": { - "version": "0.16.11", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.11.tgz", - "integrity": "sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==", - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" - ], - "dependencies": { - "commander": "^8.3.0" - }, - "bin": { - "katex": "cli.js" - } - }, - "node_modules/khroma": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", - "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/layout-base": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", - "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/markdown-extensions": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz", - "integrity": "sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/markdown-table": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", - "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/match-sorter": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", - "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", - "dependencies": { - "@babel/runtime": "^7.23.8", - "remove-accents": "0.5.0" - } - }, - "node_modules/mdast-util-definitions": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", - "integrity": "sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "unist-util-visit": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-definitions/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-definitions/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.2.tgz", - "integrity": "sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==", - "dependencies": { - "@types/mdast": "^3.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", - "integrity": "sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "mdast-util-to-string": "^3.1.0", - "micromark": "^3.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-decode-string": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "unist-util-stringify-position": "^3.0.0", - "uvu": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz", - "integrity": "sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==", - "dependencies": { - "mdast-util-from-markdown": "^1.0.0", - "mdast-util-gfm-autolink-literal": "^1.0.0", - "mdast-util-gfm-footnote": "^1.0.0", - "mdast-util-gfm-strikethrough": "^1.0.0", - "mdast-util-gfm-table": "^1.0.0", - "mdast-util-gfm-task-list-item": "^1.0.0", - "mdast-util-to-markdown": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.3.tgz", - "integrity": "sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==", - "dependencies": { - "@types/mdast": "^3.0.0", - "ccount": "^2.0.0", - "mdast-util-find-and-replace": "^2.0.0", - "micromark-util-character": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz", - "integrity": "sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-markdown": "^1.3.0", - "micromark-util-normalize-identifier": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz", - "integrity": "sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-markdown": "^1.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz", - "integrity": "sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==", - "dependencies": { - "@types/mdast": "^3.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^1.0.0", - "mdast-util-to-markdown": "^1.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz", - "integrity": "sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-markdown": "^1.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-math": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-2.0.2.tgz", - "integrity": "sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==", - "dependencies": { - "@types/mdast": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-to-markdown": "^1.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-2.0.1.tgz", - "integrity": "sha512-38w5y+r8nyKlGvNjSEqWrhG0w5PmnRA+wnBvm+ulYCct7nsGYhFVb0lljS9bQav4psDAS1eGkP2LMVcZBi/aqw==", - "dependencies": { - "mdast-util-from-markdown": "^1.0.0", - "mdast-util-mdx-expression": "^1.0.0", - "mdast-util-mdx-jsx": "^2.0.0", - "mdast-util-mdxjs-esm": "^1.0.0", - "mdast-util-to-markdown": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-1.3.2.tgz", - "integrity": "sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "mdast-util-from-markdown": "^1.0.0", - "mdast-util-to-markdown": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-2.1.4.tgz", - "integrity": "sha512-DtMn9CmVhVzZx3f+optVDF8yFgQVt7FghCRNdlIaS3X5Bnym3hZwPbg/XW86vdpKjlc1PVj26SpnLGeJBXD3JA==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "ccount": "^2.0.0", - "mdast-util-from-markdown": "^1.1.0", - "mdast-util-to-markdown": "^1.3.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-remove-position": "^4.0.0", - "unist-util-stringify-position": "^3.0.0", - "vfile-message": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-remove-position": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-4.0.2.tgz", - "integrity": "sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-visit": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-1.3.1.tgz", - "integrity": "sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "mdast-util-from-markdown": "^1.0.0", - "mdast-util-to-markdown": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", - "integrity": "sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==", - "dependencies": { - "@types/mdast": "^3.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/mdast-util-to-hast/node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/mdast-util-to-hast/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/mdast-util-to-hast/node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/mdast-util-to-hast/node_modules/micromark-util-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/mdast-util-to-hast/node_modules/micromark-util-sanitize-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", - "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/mdast-util-to-hast/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/mdast-util-to-hast/node_modules/micromark-util-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", - "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/mdast-util-to-hast/node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast/node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-1.5.0.tgz", - "integrity": "sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==", - "dependencies": { - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^3.0.0", - "mdast-util-to-string": "^3.0.0", - "micromark-util-decode-string": "^1.0.0", - "unist-util-visit": "^4.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-3.2.0.tgz", - "integrity": "sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==", - "dependencies": { - "@types/mdast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mermaid": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.9.1.tgz", - "integrity": "sha512-Mx45Obds5W1UkW1nv/7dHRsbfMM1aOKA2+Pxs/IGHNonygDHwmng8xTHyS9z4KWVi0rbko8gjiBmuwwXQ7tiNA==", - "dependencies": { - "@braintree/sanitize-url": "^6.0.1", - "@types/d3-scale": "^4.0.3", - "@types/d3-scale-chromatic": "^3.0.0", - "cytoscape": "^3.28.1", - "cytoscape-cose-bilkent": "^4.1.0", - "d3": "^7.4.0", - "d3-sankey": "^0.12.3", - "dagre-d3-es": "7.0.10", - "dayjs": "^1.11.7", - "dompurify": "^3.0.5", - "elkjs": "^0.9.0", - "katex": "^0.16.9", - "khroma": "^2.0.0", - "lodash-es": "^4.17.21", - "mdast-util-from-markdown": "^1.3.0", - "non-layered-tidy-tree-layout": "^2.0.2", - "stylis": "^4.1.3", - "ts-dedent": "^2.2.0", - "uuid": "^9.0.0", - "web-worker": "^1.2.0" - } - }, - "node_modules/micromark": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-3.2.0.tgz", - "integrity": "sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "micromark-core-commonmark": "^1.0.1", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-combine-extensions": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz", - "integrity": "sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-factory-destination": "^1.0.0", - "micromark-factory-label": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-factory-title": "^1.0.0", - "micromark-factory-whitespace": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-chunked": "^1.0.0", - "micromark-util-classify-character": "^1.0.0", - "micromark-util-html-tag-name": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-subtokenize": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.1", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz", - "integrity": "sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^1.0.0", - "micromark-extension-gfm-footnote": "^1.0.0", - "micromark-extension-gfm-strikethrough": "^1.0.0", - "micromark-extension-gfm-table": "^1.0.0", - "micromark-extension-gfm-tagfilter": "^1.0.0", - "micromark-extension-gfm-task-list-item": "^1.0.0", - "micromark-util-combine-extensions": "^1.0.0", - "micromark-util-types": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.5.tgz", - "integrity": "sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==", - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.2.tgz", - "integrity": "sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==", - "dependencies": { - "micromark-core-commonmark": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-normalize-identifier": "^1.0.0", - "micromark-util-sanitize-uri": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.7.tgz", - "integrity": "sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==", - "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-classify-character": "^1.0.0", - "micromark-util-resolve-all": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.7.tgz", - "integrity": "sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==", - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.2.tgz", - "integrity": "sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==", - "dependencies": { - "micromark-util-types": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.5.tgz", - "integrity": "sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==", - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-math": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-2.1.2.tgz", - "integrity": "sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==", - "dependencies": { - "@types/katex": "^0.16.0", - "katex": "^0.16.0", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-expression": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-1.0.8.tgz", - "integrity": "sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/estree": "^1.0.0", - "micromark-factory-mdx-expression": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-events-to-acorn": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-1.0.5.tgz", - "integrity": "sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==", - "dependencies": { - "@types/acorn": "^4.0.0", - "@types/estree": "^1.0.0", - "estree-util-is-identifier-name": "^2.0.0", - "micromark-factory-mdx-expression": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0", - "vfile-message": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-md": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-1.0.1.tgz", - "integrity": "sha512-7MSuj2S7xjOQXAjjkbjBsHkMtb+mDGVW6uI2dBL9snOBCbZmoNgDAeZ0nSn9j3T42UE/g2xVNMn18PJxZvkBEA==", - "dependencies": { - "micromark-util-types": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-1.0.1.tgz", - "integrity": "sha512-7YA7hF6i5eKOfFUzZ+0z6avRG52GpWR8DL+kN47y3f2KhxbBZMhmxe7auOeaTBrW2DenbbZTf1ea9tA2hDpC2Q==", - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^1.0.0", - "micromark-extension-mdx-jsx": "^1.0.0", - "micromark-extension-mdx-md": "^1.0.0", - "micromark-extension-mdxjs-esm": "^1.0.0", - "micromark-util-combine-extensions": "^1.0.0", - "micromark-util-types": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-1.0.5.tgz", - "integrity": "sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==", - "dependencies": { - "@types/estree": "^1.0.0", - "micromark-core-commonmark": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-events-to-acorn": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "unist-util-position-from-estree": "^1.1.0", - "uvu": "^0.5.0", - "vfile-message": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", - "integrity": "sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-1.1.0.tgz", - "integrity": "sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-factory-mdx-expression": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-1.0.9.tgz", - "integrity": "sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/estree": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-events-to-acorn": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "unist-util-position-from-estree": "^1.0.0", - "uvu": "^0.5.0", - "vfile-message": "^3.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-space": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", - "integrity": "sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-1.1.0.tgz", - "integrity": "sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-1.1.0.tgz", - "integrity": "sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-1.2.0.tgz", - "integrity": "sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-1.1.0.tgz", - "integrity": "sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-1.1.0.tgz", - "integrity": "sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.1.0.tgz", - "integrity": "sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.1.0.tgz", - "integrity": "sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-1.1.0.tgz", - "integrity": "sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-decode-numeric-character-reference": "^1.0.0", - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-1.1.0.tgz", - "integrity": "sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-events-to-acorn": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.2.3.tgz", - "integrity": "sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/acorn": "^4.0.0", - "@types/estree": "^1.0.0", - "@types/unist": "^2.0.0", - "estree-util-visit": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0", - "vfile-message": "^3.0.0" - } - }, - "node_modules/micromark-util-events-to-acorn/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-util-html-tag-name": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", - "integrity": "sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.1.0.tgz", - "integrity": "sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-1.1.0.tgz", - "integrity": "sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-types": "^1.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.2.0.tgz", - "integrity": "sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^1.0.0", - "micromark-util-encode": "^1.0.0", - "micromark-util-symbol": "^1.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-1.1.0.tgz", - "integrity": "sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-chunked": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-1.1.0.tgz", - "integrity": "sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-1.1.0.tgz", - "integrity": "sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/next": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/next/-/next-13.4.12.tgz", - "integrity": "sha512-eHfnru9x6NRmTMcjQp6Nz0J4XH9OubmzOa7CkWL+AUrUxpibub3vWwttjduu9No16dug1kq04hiUUpo7J3m3Xw==", - "dependencies": { - "@next/env": "13.4.12", - "@swc/helpers": "0.5.1", - "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", - "styled-jsx": "5.1.1", - "watchpack": "2.4.0", - "zod": "3.21.4" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=16.8.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "13.4.12", - "@next/swc-darwin-x64": "13.4.12", - "@next/swc-linux-arm64-gnu": "13.4.12", - "@next/swc-linux-arm64-musl": "13.4.12", - "@next/swc-linux-x64-gnu": "13.4.12", - "@next/swc-linux-x64-musl": "13.4.12", - "@next/swc-win32-arm64-msvc": "13.4.12", - "@next/swc-win32-ia32-msvc": "13.4.12", - "@next/swc-win32-x64-msvc": "13.4.12" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "fibers": ">= 3.1.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "fibers": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/next-mdx-remote": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-4.4.1.tgz", - "integrity": "sha512-1BvyXaIou6xy3XoNF4yaMZUCb6vD2GTAa5ciOa6WoO+gAUTYsb1K4rI/HSC2ogAWLrb/7VSV52skz07vOzmqIQ==", - "dependencies": { - "@mdx-js/mdx": "^2.2.1", - "@mdx-js/react": "^2.2.1", - "vfile": "^5.3.0", - "vfile-matter": "^3.0.1" - }, - "engines": { - "node": ">=14", - "npm": ">=7" - }, - "peerDependencies": { - "react": ">=16.x <=18.x", - "react-dom": ">=16.x <=18.x" - } - }, - "node_modules/next-seo": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/next-seo/-/next-seo-6.6.0.tgz", - "integrity": "sha512-0VSted/W6XNtgAtH3D+BZrMLLudqfm0D5DYNJRXHcDgan/1ZF1tDFIsWrmvQlYngALyphPfZ3ZdOqlKpKdvG6w==", - "peerDependencies": { - "next": "^8.1.1-canary.54 || >=9.0.0", - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, - "node_modules/next-themes": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.2.1.tgz", - "integrity": "sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==", - "peerDependencies": { - "next": "*", - "react": "*", - "react-dom": "*" - } - }, - "node_modules/nextra": { - "version": "2.13.4", - "resolved": "https://registry.npmjs.org/nextra/-/nextra-2.13.4.tgz", - "integrity": "sha512-7of2rSBxuUa3+lbMmZwG9cqgftcoNOVQLTT6Rxf3EhBR9t1EI7b43dted8YoqSNaigdE3j1CoyNkX8N/ZzlEpw==", - "dependencies": { - "@headlessui/react": "^1.7.17", - "@mdx-js/mdx": "^2.3.0", - "@mdx-js/react": "^2.3.0", - "@napi-rs/simple-git": "^0.1.9", - "@theguild/remark-mermaid": "^0.0.5", - "@theguild/remark-npm2yarn": "^0.2.0", - "clsx": "^2.0.0", - "github-slugger": "^2.0.0", - "graceful-fs": "^4.2.11", - "gray-matter": "^4.0.3", - "katex": "^0.16.9", - "lodash.get": "^4.4.2", - "next-mdx-remote": "^4.2.1", - "p-limit": "^3.1.0", - "rehype-katex": "^7.0.0", - "rehype-pretty-code": "0.9.11", - "rehype-raw": "^7.0.0", - "remark-gfm": "^3.0.1", - "remark-math": "^5.1.1", - "remark-reading-time": "^2.0.1", - "shiki": "^0.14.3", - "slash": "^3.0.0", - "title": "^3.5.3", - "unist-util-remove": "^4.0.0", - "unist-util-visit": "^5.0.0", - "zod": "^3.22.3" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "next": ">=9.5.3", - "react": ">=16.13.1", - "react-dom": ">=16.13.1" - } - }, - "node_modules/nextra-theme-docs": { - "version": "2.13.4", - "resolved": "https://registry.npmjs.org/nextra-theme-docs/-/nextra-theme-docs-2.13.4.tgz", - "integrity": "sha512-2XOoMfwBCTYBt8ds4ZHftt9Wyf2XsykiNo02eir/XEYB+sGeUoE77kzqfidjEOKCSzOHYbK9BDMcg2+B/2vYRw==", - "dependencies": { - "@headlessui/react": "^1.7.17", - "@popperjs/core": "^2.11.8", - "clsx": "^2.0.0", - "escape-string-regexp": "^5.0.0", - "flexsearch": "^0.7.31", - "focus-visible": "^5.2.0", - "git-url-parse": "^13.1.0", - "intersection-observer": "^0.12.2", - "match-sorter": "^6.3.1", - "next-seo": "^6.0.0", - "next-themes": "^0.2.1", - "scroll-into-view-if-needed": "^3.1.0", - "zod": "^3.22.3" - }, - "peerDependencies": { - "next": ">=9.5.3", - "nextra": "2.13.4", - "react": ">=16.13.1", - "react-dom": ">=16.13.1" - } - }, - "node_modules/nextra-theme-docs/node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/nextra/node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/non-layered-tidy-tree-layout": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz", - "integrity": "sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==" - }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/npm-to-yarn": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/npm-to-yarn/-/npm-to-yarn-2.2.1.tgz", - "integrity": "sha512-O/j/ROyX0KGLG7O6Ieut/seQ0oiTpHF2tXAcFbpdTLQFiaNtkyTXXocM1fwpaa60dg1qpWj0nHlbNhx6qwuENQ==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/nebrelbug/npm-to-yarn?sponsor=1" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-entities": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", - "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-numeric-range": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", - "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" - }, - "node_modules/parse-path": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.0.0.tgz", - "integrity": "sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==", - "dependencies": { - "protocols": "^2.0.0" - } - }, - "node_modules/parse-url": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-8.1.0.tgz", - "integrity": "sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==", - "dependencies": { - "parse-path": "^7.0.0" - } - }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" - }, - "node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/protocols": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz", - "integrity": "sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==" - }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==" - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/reading-time": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/reading-time/-/reading-time-1.5.0.tgz", - "integrity": "sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==" - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/rehype-katex": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", - "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/katex": "^0.16.0", - "hast-util-from-html-isomorphic": "^2.0.0", - "hast-util-to-text": "^4.0.0", - "katex": "^0.16.0", - "unist-util-visit-parents": "^6.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-katex/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/rehype-katex/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/rehype-katex/node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-pretty-code": { - "version": "0.9.11", - "resolved": "https://registry.npmjs.org/rehype-pretty-code/-/rehype-pretty-code-0.9.11.tgz", - "integrity": "sha512-Eq90eCYXQJISktfRZ8PPtwc5SUyH6fJcxS8XOMnHPUQZBtC6RYo67gGlley9X2nR8vlniPj0/7oCDEYHKQa/oA==", - "dependencies": { - "@types/hast": "^2.0.0", - "hash-obj": "^4.0.0", - "parse-numeric-range": "^1.3.0" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "shiki": "*" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-raw/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/rehype-raw/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/rehype-raw/node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", - "integrity": "sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-gfm": "^2.0.0", - "micromark-extension-gfm": "^2.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-math": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-5.1.1.tgz", - "integrity": "sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-math": "^2.0.0", - "micromark-extension-math": "^2.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-mdx": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-2.3.0.tgz", - "integrity": "sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g==", - "dependencies": { - "mdast-util-mdx": "^2.0.0", - "micromark-extension-mdxjs": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", - "integrity": "sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==", - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-from-markdown": "^1.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-reading-time": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/remark-reading-time/-/remark-reading-time-2.0.1.tgz", - "integrity": "sha512-fy4BKy9SRhtYbEHvp6AItbRTnrhiDGbqLQTSYVbQPGuRCncU1ubSsh9p/W5QZSxtYcUXv8KGL0xBgPLyNJA1xw==", - "dependencies": { - "estree-util-is-identifier-name": "^2.0.0", - "estree-util-value-to-estree": "^1.3.0", - "reading-time": "^1.3.0", - "unist-util-visit": "^3.1.0" - } - }, - "node_modules/remark-reading-time/node_modules/unist-util-visit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-3.1.0.tgz", - "integrity": "sha512-Szoh+R/Ll68QWAyQyZZpQzZQm2UPbxibDvaY8Xc9SUtYgPsDzx5AWSk++UUt2hJuow8mvwR+rG+LQLw+KsuAKA==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-reading-time/node_modules/unist-util-visit-parents": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-4.1.1.tgz", - "integrity": "sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-10.1.0.tgz", - "integrity": "sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "mdast-util-to-hast": "^12.1.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype/node_modules/mdast-util-to-hast": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-12.3.0.tgz", - "integrity": "sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==", - "dependencies": { - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "mdast-util-definitions": "^5.0.0", - "micromark-util-sanitize-uri": "^1.1.0", - "trim-lines": "^3.0.0", - "unist-util-generated": "^2.0.0", - "unist-util-position": "^4.0.0", - "unist-util-visit": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype/node_modules/unist-util-visit": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-4.1.2.tgz", - "integrity": "sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype/node_modules/unist-util-visit-parents": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", - "integrity": "sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remove-accents": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", - "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" - }, - "node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" - }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" - }, - "node_modules/sade": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", - "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", - "dependencies": { - "mri": "^1.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/scroll-into-view-if-needed": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", - "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", - "dependencies": { - "compute-scroll-into-view": "^3.0.2" - } - }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shiki": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", - "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", - "dependencies": { - "ansi-sequence-parser": "^1.1.0", - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/sort-keys": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-5.1.0.tgz", - "integrity": "sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==", - "dependencies": { - "is-plain-obj": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sort-keys/node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/style-to-object": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.4.4.tgz", - "integrity": "sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==", - "dependencies": { - "inline-style-parser": "0.1.1" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/stylis": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.4.tgz", - "integrity": "sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==" - }, - "node_modules/supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha512-ycQR/UbvI9xIlEdQT1TQqwoXtEldExbCEAJgRo5YXlmSKjv6ThHnP9/vwGa1gr19Gfw+LkFd7KqYMhzrRC5JYw==", - "dependencies": { - "has-flag": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/title": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/title/-/title-3.5.3.tgz", - "integrity": "sha512-20JyowYglSEeCvZv3EZ0nZ046vLarO37prvV0mbtQV7C8DJPGgN967r8SJkqd3XK3K3lD3/Iyfp3avjfil8Q2Q==", - "dependencies": { - "arg": "1.0.0", - "chalk": "2.3.0", - "clipboardy": "1.2.2", - "titleize": "1.0.0" - }, - "bin": { - "title": "bin/title.js" - } - }, - "node_modules/titleize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-1.0.0.tgz", - "integrity": "sha512-TARUb7z1pGvlLxgPk++7wJ6aycXF3GJ0sNSBTAsTuJrQG5QuZlkUQP+zl+nbjAh4gMX9yDw9ZYklMd7vAfJKEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", - "engines": { - "node": ">=6.10" - } - }, - "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" - }, - "node_modules/type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.3.tgz", - "integrity": "sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/unified": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", - "integrity": "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==", - "dependencies": { - "@types/unist": "^2.0.0", - "bail": "^2.0.0", - "extend": "^3.0.0", - "is-buffer": "^2.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unified/node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", - "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-find-after/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/unist-util-find-after/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-generated": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", - "integrity": "sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-5.2.1.tgz", - "integrity": "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", - "integrity": "sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position-from-estree": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.2.tgz", - "integrity": "sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-4.0.0.tgz", - "integrity": "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/unist-util-remove/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/unist-util-remove/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", - "integrity": "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==", - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/unist-util-visit-parents/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/unist-util-visit/node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/uvu": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/uvu/-/uvu-0.5.6.tgz", - "integrity": "sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==", - "dependencies": { - "dequal": "^2.0.0", - "diff": "^5.0.0", - "kleur": "^4.0.3", - "sade": "^1.7.3" - }, - "bin": { - "uvu": "bin.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/vfile": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz", - "integrity": "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==", - "dependencies": { - "@types/unist": "^2.0.0", - "is-buffer": "^2.0.0", - "unist-util-stringify-position": "^3.0.0", - "vfile-message": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/vfile-location/node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-matter": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/vfile-matter/-/vfile-matter-3.0.1.tgz", - "integrity": "sha512-CAAIDwnh6ZdtrqAuxdElUqQRQDQgbbIrYtDYI8gCjXS1qQ+1XdLoK8FIZWxJwn0/I+BkSSZpar3SOgjemQz4fg==", - "dependencies": { - "@types/js-yaml": "^4.0.0", - "is-buffer": "^2.0.0", - "js-yaml": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-matter/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/vfile-matter/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message/node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" - }, - "node_modules/vfile-message/node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==" - }, - "node_modules/vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==" - }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/web-worker": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.3.0.tgz", - "integrity": "sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==" - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.21.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz", - "integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/pages/azion/_meta.json b/pages/azion/_meta.json index b3b3b10..953da8c 100644 --- a/pages/azion/_meta.json +++ b/pages/azion/_meta.json @@ -1,12 +1,9 @@ { "index": "Overview", - "get-started": "", - "bindings": "", - "caching": "", - "howtos": "How-Tos", - "examples": "", + "how-it-works": "How it Works", + "get-started": "Get Started", + "caching": "Caching", + "examples": "Examples", "known-issues": "Known issues", - "troubleshooting": "", - "migrate-from-0.6-to-1.0.0-beta": "Migrate from 0.6 to 1.0.0-beta", - "former-releases": "Former releases" + "troubleshooting": "Troubleshooting" } diff --git a/pages/azion/bindings.mdx b/pages/azion/bindings.mdx deleted file mode 100644 index 51734ff..0000000 --- a/pages/azion/bindings.mdx +++ /dev/null @@ -1,68 +0,0 @@ -import { Callout } from "nextra/components"; - -### Bindings - -[Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. When you declare a binding on your Worker, you grant it a specific capability, such as being able to read and write files to an [R2](https://developers.cloudflare.com/r2/) bucket. - -#### How to configure your Next.js app so it can access bindings - -Install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare), and then add a [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) in the root directory of your Next.js app, as described in [Get Started](/cloudflare/get-started#3-create-a-wranglerjson-file). - -#### How to access bindings in your Next.js app - -You can access [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) from any route of your Next.js app via `getCloudflareContext`: - -```js -import { getCloudflareContext } from "@opennextjs/cloudflare"; - -export async function GET(request) { - let responseText = "Hello World"; - - const myKv = getCloudflareContext().env.MY_KV_NAMESPACE; - await myKv.put("foo", "bar"); - const foo = await myKv.get("foo"); - - return new Response(foo); -} -``` - - - `getCloudflareContext` can only be used in SSG routes in "async mode" (making it return a promise), to run the function in such a way simply provide an options argument with `async` set to `true`: - ```js - const context = await getCloudflareContext({ async: true }); - ``` - - **WARNING**: During SSG caution is advised since secrets (stored in `.dev.vars` files) and local development - values from bindings (like values saved in a local KV) will be used for the pages static generation. - - - -#### How to add bindings to your Worker - -Add bindings to your Worker by adding them to your [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). - -## TypeScript type declarations for bindings - -To ensure that the `env` object from `getCloudflareContext().env` above has accurate TypeScript types, run the following Wrangler command to [generate types that match your Worker's configuration](https://developers.cloudflare.com/workers/languages/typescript/#generate-types-that-match-your-workers-configuration-experimental): - -``` -npx wrangler types --env-interface CloudflareEnv -``` - -This will generate a `d.ts` file and save it to `worker-configuration.d.ts`. - -To ensure that your types are always up-to-date, make sure to run `wrangler types --env-interface CloudflareEnv` after any changes to your config file. - -## Other Cloudflare APIs (`cf`, `ctx`) - -You can access context about the incoming request from the [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), as well as lifecycle methods from the [`ctx` object](https://developers.cloudflare.com/workers/runtime-apis/context) from the return value of [`getCloudflareContext()`](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src/api/get-cloudflare-context.ts): - -```js -import { getCloudflareContext } from "@opennextjs/cloudflare"; - -export async function GET(request) { - const { env, cf, ctx } = getCloudflareContext(); - - // ... -} -``` diff --git a/pages/azion/examples.mdx b/pages/azion/examples.mdx index 8e491a0..5bb9ae5 100644 --- a/pages/azion/examples.mdx +++ b/pages/azion/examples.mdx @@ -2,22 +2,11 @@ import { Callout } from "nextra/components"; ## Examples -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: - -``` -npm create cloudflare@latest -- my-next-app --framework=next --platform=workers -``` +To create a new Next.js app for Azion, you can use the following starter projects and templates: ### Basic starter projects -Basic example apps are included in the repository for `@opennextjs/cloudflare` package: - -- [_`create-next-app`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/create-next-app) — a Next.js project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). -- [_`middleware`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/middleware) — a minimal Next.js project using middleware -- [_`vercel-blog-starter`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/vercel-blog-starter) — a blog project using SSG - -You can use these to understand how to configure your Next.js app to use `@opennextjs/cloudflare`, or refer to [Get Started](/cloudflare/get-started). - -### Next.js Commerce Demo +- [Next.js + TypeScript + Tailwind Template](https://github.com/aziontech/azion-samples/tree/dev/templates/opennextjs/nextal-next-typescript-tailwind) — a ready-to-use template for building modern Next.js apps on Azion Edge Functions. +- [Node Playground (Next.js 13)](https://github.com/aziontech/bundler-examples/tree/main/examples/nextjs/node-playground-13) — an example showing how to run Next.js 13 in a Node.js environment on Azion. -The [Next.js Commerce demo app](https://github.com/vercel/commerce/tree/v1) works with `@opennextjs/cloudflare`. You can view a deployed version of it [here](https://vercel-commerce-on-workers.web-experiments.workers.dev/). +You can use these repositories to understand how to configure your Next.js app to run on Azion, or as a starting point for your own projects. \ No newline at end of file diff --git a/pages/azion/former-releases/0.2/_meta.json b/pages/azion/former-releases/0.2/_meta.json deleted file mode 100644 index 53a20a0..0000000 --- a/pages/azion/former-releases/0.2/_meta.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "index": "Overview", - "get-started": "", - "bindings": "", - "caching": "", - "examples": "" -} diff --git a/pages/azion/former-releases/0.2/bindings.mdx b/pages/azion/former-releases/0.2/bindings.mdx deleted file mode 100644 index 36d02b9..0000000 --- a/pages/azion/former-releases/0.2/bindings.mdx +++ /dev/null @@ -1,63 +0,0 @@ -import { Callout } from "nextra/components"; - -### Bindings - -[Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. When you declare a binding on your Worker, you grant it a specific capability, such as being able to read and write files to an [R2](https://developers.cloudflare.com/r2/) bucket. - -#### How to configure your Next.js app so it can access bindings - -Install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare), and then add a `wrangler.toml` file in the root directory of your Next.js app, as described in [Get Started](/cloudflare/get-started). - -#### How to access bindings in your Next.js app - -You can access [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) from any route of your Next.js app via `getCloudflareContext`: - -```js -import { getCloudflareContext } from "@opennextjs/cloudflare"; - -export async function GET(request) { - let responseText = "Hello World"; - - const myKv = (await getCloudflareContext()).env.MY_KV_NAMESPACE; - await myKv.put("foo", "bar"); - const foo = await myKv.get("foo"); - - return new Response(foo); -} -``` - -#### How to add bindings to your Worker - -Add bindings to your Worker by [adding them to your `wrangler.toml` configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). - -## TypeScript type declarations for bindings - -To ensure that the `env` object from `(await getCloudflareContext()).env` above has accurate TypeScript types, run the following Wrangler command to [generate types that match your Worker's configuration](https://developers.cloudflare.com/workers/languages/typescript/#generate-types-that-match-your-workers-configuration-experimental): - -``` -npx wrangler types --experimental-include-runtime -``` - -This will generate a `d.ts` file and (by default) save it to `.wrangler/types/runtime.d.ts`. You will be prompted in the command's output to add that file to your `tsconfig.json`'s `compilerOptions.types` array. - -If you would like to commit the file to git, you can provide a custom path. Here, for instance, the `runtime.d.ts` file will be saved to the root of your project: - -```bash -npx wrangler types --experimental-include-runtime="./runtime.d.ts" -``` - -To ensure that your types are always up-to-date, make sure to run `wrangler types --experimental-include-runtime` after any changes to your config file. - -## Other Cloudflare APIs (`cf`, `ctx`) - -You can access context about the incoming request from the [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), as well as lifecycle methods from the [`ctx` object](https://developers.cloudflare.com/workers/runtime-apis/context) from the return value of [`getCloudflareContext()`](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src/api/get-cloudflare-context.ts): - -```js -import { getCloudflareContext } from "@opennextjs/cloudflare"; - -export async function GET(request) { - const { env, cf, ctx } = await getCloudflareContext(); - - // ... -} -``` diff --git a/pages/azion/former-releases/0.2/caching.mdx b/pages/azion/former-releases/0.2/caching.mdx deleted file mode 100644 index fab6edf..0000000 --- a/pages/azion/former-releases/0.2/caching.mdx +++ /dev/null @@ -1,36 +0,0 @@ -import { Callout } from "nextra/components"; - -## Caching - -`@opennextjs/cloudflare` supports [caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data) and [revalidating](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data) data returned by subrequests you make in your app by calling [`fetch()`](https://developers.cloudflare.com/workers/runtime-apis/fetch/). - -By default, all `fetch()` subrequests made in your Next.js app are cached. Refer to the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/caching#opting-out-1) for information about how to disable caching for an individual subrequest, or for an entire route. - -[The cache persists across deployments](https://nextjs.org/docs/app/building-your-application/caching#data-cache). You are responsible for revalidating/purging this cache. - - - Workers KV is eventually consistent, which means that it can take up to 60 seconds for updates to be - reflected globally, when using the default TTL of 60 seconds. - - -### How to enable caching - -`@opennextjs/cloudflare` uses [Workers KV](https://developers.cloudflare.com/kv/) as the cache for your Next.js app. Workers KV is [fast](https://blog.cloudflare.com/faster-workers-kv) and uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. - -To enable caching, you must: - -#### 1. Create a KV namespace - -``` -npx wrangler@latest kv namespace create -``` - -#### 2. Add the KV namespace to your Worker - -The binding name used in your app's worker will be `NEXT_CACHE_WORKERS_KV` by default. This is configurable and can be changed by setting the `__OPENNEXT_KV_BINDING_NAME` build-time environment variable. - -``` -[[kv_namespaces]] -binding = "NEXT_CACHE_WORKERS_KV" -id = "" -``` diff --git a/pages/azion/former-releases/0.2/examples.mdx b/pages/azion/former-releases/0.2/examples.mdx deleted file mode 100644 index 6dd75b2..0000000 --- a/pages/azion/former-releases/0.2/examples.mdx +++ /dev/null @@ -1,22 +0,0 @@ -import { Callout } from "nextra/components"; - -## Examples - -To create a new Next.js app, pre-configured to run on Cloudflare using @opennextjs/cloudflare, run: - -``` -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -### Basic starter projects - -Two basic example apps are included in the repository for `@opennextjs/cloudflare` package: - -- [_`create-next-app`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/create-next-app) — a Next.js project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). -- [_`api`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/api) — a minimal Next.js project with a single API route - -You can use these to understand how to configure your Next.js app to use `@opennextjs/cloudflare`, or refer to [Get Started](/cloudflare/get-started). - -### Next.js Commerce Demo - -The [Next.js Commerce demo app](https://github.com/vercel/commerce/tree/v1) works with `@opennextjs/cloudflare`. You can view a deployed version of it [here](https://vercel-commerce-on-workers.web-experiments.workers.dev/). diff --git a/pages/azion/former-releases/0.2/get-started.mdx b/pages/azion/former-releases/0.2/get-started.mdx deleted file mode 100644 index 2e16213..0000000 --- a/pages/azion/former-releases/0.2/get-started.mdx +++ /dev/null @@ -1,140 +0,0 @@ -import { Callout } from "nextra/components"; - -### Get Started - -#### New apps - -To create a new Next.js app, pre-configured to run on Cloudflare using @opennextjs/cloudflare, run: - -``` -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -#### Existing Next.js apps - -##### 1. Install @opennextjs/cloudflare - -First, install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare): - -```sh -npm install --save-dev @opennextjs/cloudflare -``` - -##### 2. Install Wrangler, and add a `wrangler.toml` file - -Install the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) as a devDependency: - -```npm -npm install -D wrangler@latest -``` - - - You must use Wrangler version `3.78.10` or later to deploy Next.js apps using `@opennextjs/cloudflare`. - - -Then, add a [`wrangler.toml`](https://developers.cloudflare.com/workers/wrangler/configuration/) file to the root directory of your Next.js app: - -```toml -main = ".worker-next/index.mjs" -name = "my-app" -compatibility_date = "2024-09-23" -compatibility_flags = ["nodejs_compat"] -assets = { directory = ".worker-next/assets", binding = "ASSETS" } -``` - - - As shown above, you must enable the [`nodejs_compat` compatibility - flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) *and* set your [compatibility - date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) to `2024-09-23` or - later, in order for your Next.js app to work with @opennextjs/cloudflare. - - -`wrangler.toml` is where you configure your Worker and define what resources it can access via [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings). - -##### 3. Update `package.json` - -Add the following to the scripts field of your `package.json` file: - -```json -"build:worker": "cloudflare", -"dev:worker": "wrangler dev --port 8771", -"preview:worker": "npm run build:worker && npm run dev:worker", -"deploy:worker": "npm run build:worker && wrangler deploy" -``` - -- `npm run build:worker`: Runs the [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) adapter. This first builds your app by running `next build` behind the scenes, and then transforms the build output to a format that you can run locally using [Wrangler](https://developers.cloudflare.com/workers/wrangler/), and deploy to Cloudflare. -- `npm run dev:worker`: Takes the output generated by `build:worker` and runs it locally in [workerd](https://github.com/cloudflare/workerd), the open-source Workers Runtime, allowing you to run the app locally in the same environment that it will run in production. If you instead run `next dev`, your app will run in Node.js, which is a different JavaScript runtime from the Workers runtime, with differences in behavior and APIs. -- `npm run preview:worker`: Runs `build:worker` and then `dev:worker`, allowing you to quickly preview your app running locally in the Workers runtime, via a single command. -- `npm run deploy`: Builds your app, and then deploys it to Cloudflare - -### 4. Add caching with Workers KV - -See the [Caching docs](/cloudflare/0.2/caching) for information on enabling Next.js caching in your OpenNext project. - -### 5. Remove `@cloudflare/next-on-pages` (if necessary) - -If your Next.js app currently uses `@cloudflare/next-on-pages`, you'll want to remove it, and make a few changes. - -#### Remove `export const runtime = "edge";` - -Before deploying your app, remove the `export const runtime = "edge";` line from your `next.config.js` file. This line is not needed when using `@opennextjs/cloudflare`. - -#### Add `.worker-next` to `.gitignore` - -You should add `.worker-next` to your `.gitignore` file to prevent the build output from being committed to your repository. - -#### Uninstall `@cloudflare/next-on-pages` - -You should uninstall `@cloudflare/next-on-pages` and remove any references to it. - -In `package.json`: - -```diff -"scripts": { -- "pages:build": "npx @cloudflare/next-on-pages", -- "preview": "npm run pages:build && wrangler pages dev", -- "deploy": "npm run pages:build && wrangler pages deploy" - -"devDependencies": { -- "@cloudflare/next-on-pages": "*", -``` - -(remember to also remove [eslint-plugin-next-on-pages](https://www.npmjs.com/package/eslint-plugin-next-on-pages) from your `.eslintrc.js` file) - -You no longer need to call `setupDevPlatform()` in your `next.config.mjs` file: - -```diff title="next.config.mjs" -- import { setupDevPlatform } from '@cloudflare/next-on-pages/next-dev'; - -/** @type {import('next').NextConfig} */ -const nextConfig = {}; - -- if (process.env.NODE_ENV === 'development') { -- await setupDevPlatform(); -- } -``` - -And you'll want to replace any uses of `getRequestContext` from `@cloudflare/next-on-pages` with `getCloudflareContext` from `@opennextjs/cloudflare`: - -```diff -- import { getRequestContext } from "@cloudflare/next-on-pages"; -+ import { getCloudflareContext } from "@opennextjs/cloudflare"; -``` - -##### 6. Develop locally - -You can continue to run `next dev` when developing locally. - -During local development, you can access local versions of Cloudflare bindings as indicated in the [bindings documentation](./bindings). - -In step 3, we also added the `npm run preview:worker`, which allows you to quickly preview your app running locally in the Workers runtime, rather than in Node.js. This allows you to test changes in the same runtime as your app will run in when deployed to Cloudflare. - -##### 7. Deploy to Cloudflare Workers - -Either deploy via the command line: - -```sh -npm run deploy:worker -``` - -Or [connect a Github or Gitlab repository](https://developers.cloudflare.com/workers/ci-cd/), and Cloudflare will automatically build and deploy each pull request you merge to your production branch. diff --git a/pages/azion/former-releases/0.2/index.mdx b/pages/azion/former-releases/0.2/index.mdx deleted file mode 100644 index a3f53c2..0000000 --- a/pages/azion/former-releases/0.2/index.mdx +++ /dev/null @@ -1,65 +0,0 @@ -import { Callout } from "nextra/components"; - -## Cloudflare - -The [`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) adapter lets you deploy Next.js apps to [Cloudflare Workers](https://developers.cloudflare.com/workers) using the [Node.js "runtime" from Next.js](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). - - -[`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) is pre 1.0, and still in active development. You should try it, [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues), [share feedback](https://github.com/opennextjs/opennextjs-cloudflare/discussions), and contribute code to help make running Next.js apps on Cloudflare easier. We don't quite yet recommend using it for mission-critical production apps. - -You can also use [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) to deploy Next.js apps to Cloudflare Pages. You can review the differences in supported Next.js features below and by reviewing [the docs for `@cloudflare/next-on-pages`](https://developers.cloudflare.com/pages/framework-guides/nextjs/ssr/supported-features/), and understand the differences between Workers and Pages [here](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/). - - - -### Get Started - -##### New apps - -To create a new Next.js app, pre-configured to run on Cloudflare using @opennextjs/cloudflare, run: - -``` -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -##### Existing Next.js apps - -Follow the guide [here](/cloudflare/0.2/get-started) to use [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) with an existing Next.js app. - -### Supported Next.js runtimes - -Next.js has [two "runtimes"](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes) — "Edge" and "Node.js". When you use `@opennextjs/cloudflare`, your app can use the Node.js runtime, which is more fully featured, and allows you to use the [Node.js APIs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) that are provided by the Cloudflare Workers runtime. - -This is an important difference from `@cloudflare/next-on-pages`, which only supports the "Edge" runtime. The Edge Runtime code in Next.js [intentionally constrains which APIs from Node.js can be used](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/webpack/plugins/middleware-plugin.ts#L820), and the "Edge" runtime does not support all Next.js features. - -### Supported Next.js versions - -`@opennextjs/cloudflare` is pre 1.0, and still in active development. We intend to support all minor and patch version of Next.js 13 and 14, as well as Next.js 15 when it is released. (currently a release candidate) - -To help improve compatibility, we encourage you to [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues) and contribute code! - -### Supported Next.js features - -- [x] [App Router](https://nextjs.org/docs/app) -- [x] [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) -- [x] [Dynamic routes](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) -- [x] [Static Site Generation (SSG)](https://nextjs.org/docs/app/building-your-application/rendering/server-components#static-rendering-default) -- [x] [Server-Side Rendering (SSR)](https://nextjs.org/docs/app/building-your-application/rendering/server-components) - -### Not Yet Supported Next.js features - -The following Next.js features are not yet supported — but we welcome both contributions and feedback! Tell us what you'd like to see, or what you'd like to add support for: - -- [ ] [Pages Router](https://nextjs.org/docs/pages) (you should use the App Router instead, which was introduced in Next.js 13) -- [ ] [Incremental Static Regeneration (ISR)](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration) -- [ ] [Partial Prerendering (PPR)](https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering) -- [ ] [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) -- [ ] [Image optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) (you can integrate Cloudflare Images with Next.js by following [this guide](https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/)) -- [ ] [Experimental streaming support](https://nextjs.org/blog/next-15-rc#executing-code-after-a-response-with-nextafter-experimental) - -### How @opennextjs/cloudflare Works - -The OpenNext Cloudflare adapter works by taking the Next.js build output and transforming it, so that it can run in Cloudflare Workers. - -When you add [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) as a dependency to your Next.js app, and then run `npx cloudflare` the adapter first builds your app by running `next build`, and then transforms the build output to a format that you can run locally using [Wrangler](https://developers.cloudflare.com/workers/wrangler/), and deploy to Cloudflare. - -You can view the code for @opennextjs/cloudflare [here](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src) to understand what it does under the hood. diff --git a/pages/azion/former-releases/0.3/_meta.json b/pages/azion/former-releases/0.3/_meta.json deleted file mode 100644 index 53a20a0..0000000 --- a/pages/azion/former-releases/0.3/_meta.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "index": "Overview", - "get-started": "", - "bindings": "", - "caching": "", - "examples": "" -} diff --git a/pages/azion/former-releases/0.3/bindings.mdx b/pages/azion/former-releases/0.3/bindings.mdx deleted file mode 100644 index 1b3164d..0000000 --- a/pages/azion/former-releases/0.3/bindings.mdx +++ /dev/null @@ -1,63 +0,0 @@ -import { Callout } from "nextra/components"; - -### Bindings - -[Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. When you declare a binding on your Worker, you grant it a specific capability, such as being able to read and write files to an [R2](https://developers.cloudflare.com/r2/) bucket. - -#### How to configure your Next.js app so it can access bindings - -Install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare), and then add a [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) in the root directory of your Next.js app, as described in [Get Started](/cloudflare/get-started#3-create-a-wranglerjson-file). - -#### How to access bindings in your Next.js app - -You can access [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) from any route of your Next.js app via `getCloudflareContext`: - -```js -import { getCloudflareContext } from "@opennextjs/cloudflare"; - -export async function GET(request) { - let responseText = "Hello World"; - - const myKv = (await getCloudflareContext()).env.MY_KV_NAMESPACE; - await myKv.put("foo", "bar"); - const foo = await myKv.get("foo"); - - return new Response(foo); -} -``` - -#### How to add bindings to your Worker - -Add bindings to your Worker by adding them to your [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). - -## TypeScript type declarations for bindings - -To ensure that the `env` object from `(await getCloudflareContext()).env` above has accurate TypeScript types, run the following Wrangler command to [generate types that match your Worker's configuration](https://developers.cloudflare.com/workers/languages/typescript/#generate-types-that-match-your-workers-configuration-experimental): - -``` -npx wrangler types --experimental-include-runtime -``` - -This will generate a `d.ts` file and (by default) save it to `.wrangler/types/runtime.d.ts`. You will be prompted in the command's output to add that file to your `tsconfig.json`'s `compilerOptions.types` array. - -If you would like to commit the file to git, you can provide a custom path. Here, for instance, the `runtime.d.ts` file will be saved to the root of your project: - -```bash -npx wrangler types --experimental-include-runtime="./runtime.d.ts" -``` - -To ensure that your types are always up-to-date, make sure to run `wrangler types --experimental-include-runtime` after any changes to your config file. - -## Other Cloudflare APIs (`cf`, `ctx`) - -You can access context about the incoming request from the [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), as well as lifecycle methods from the [`ctx` object](https://developers.cloudflare.com/workers/runtime-apis/context) from the return value of [`getCloudflareContext()`](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src/api/get-cloudflare-context.ts): - -```js -import { getCloudflareContext } from "@opennextjs/cloudflare"; - -export async function GET(request) { - const { env, cf, ctx } = await getCloudflareContext(); - - // ... -} -``` diff --git a/pages/azion/former-releases/0.3/caching.mdx b/pages/azion/former-releases/0.3/caching.mdx deleted file mode 100644 index 1314873..0000000 --- a/pages/azion/former-releases/0.3/caching.mdx +++ /dev/null @@ -1,47 +0,0 @@ -import { Callout } from "nextra/components"; - -## Caching - -`@opennextjs/cloudflare` supports [caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data). - -By default, all `fetch()` subrequests made in your Next.js app are cached. Refer to the [Next.js documentation](https://nextjs.org/docs/app/building-your-application/caching#opting-out-1) for information about how to disable caching for an individual subrequest, or for an entire route. - -[The cache persists across deployments](https://nextjs.org/docs/app/building-your-application/caching#data-cache). You are responsible for revalidating/purging this cache. - -Note that [Revalidating](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data) is not yet supported. - -Next.js primes the cache at build time. The build time values are serverd by the [Workers Assets](https://developers.cloudflare.com/workers/static-assets/). - - - Workers KV is eventually consistent, which means that it can take up to 60 seconds for updates to be - reflected globally, when using the default TTL of 60 seconds. - - -### How to enable caching - -`@opennextjs/cloudflare` uses [Workers KV](https://developers.cloudflare.com/kv/) as the cache for your Next.js app. Workers KV is [fast](https://blog.cloudflare.com/faster-workers-kv) and uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. - -To enable caching, you must: - -#### 1. Create a KV namespace - -``` -npx wrangler@latest kv namespace create -``` - -#### 2. Add the KV namespace to your Worker - -The binding name used in your app's worker is `NEXT_CACHE_WORKERS_KV`. - -```jsonc -// wrangler.json -{ - // ... - "kv_namespaces": [ - { - "binding": "NEXT_CACHE_WORKERS_KV", - "id": "", - }, - ], -} -``` diff --git a/pages/azion/former-releases/0.3/examples.mdx b/pages/azion/former-releases/0.3/examples.mdx deleted file mode 100644 index e44bee0..0000000 --- a/pages/azion/former-releases/0.3/examples.mdx +++ /dev/null @@ -1,24 +0,0 @@ -import { Callout } from "nextra/components"; - -## Examples - -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: - -``` -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -### Basic starter projects - -Basic example apps are included in the repository for `@opennextjs/cloudflare` package: - -- [_`create-next-app`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/create-next-app) — a Next.js project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). -- [_`api`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/api) — a minimal Next.js project with a single API route -- [_`middleware`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/middleware) — a minimal Next.js project using middleware -- [_`vercel-blog-starter`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/vercel-blog-starter) — a blog project using SSG - -You can use these to understand how to configure your Next.js app to use `@opennextjs/cloudflare`, or refer to [Get Started](/cloudflare/get-started). - -### Next.js Commerce Demo - -The [Next.js Commerce demo app](https://github.com/vercel/commerce/tree/v1) works with `@opennextjs/cloudflare`. You can view a deployed version of it [here](https://vercel-commerce-on-workers.web-experiments.workers.dev/). diff --git a/pages/azion/former-releases/0.3/get-started.mdx b/pages/azion/former-releases/0.3/get-started.mdx deleted file mode 100644 index 1345e34..0000000 --- a/pages/azion/former-releases/0.3/get-started.mdx +++ /dev/null @@ -1,194 +0,0 @@ -import { Callout } from "nextra/components"; - -### Get Started - -#### New apps - -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: - -``` -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -#### Existing Next.js apps - -##### 1. Install @opennextjs/cloudflare - -First, install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare): - -```sh -npm install --save-dev @opennextjs/cloudflare@latest -``` - -##### 2. Install Wrangler - -Install the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) as a devDependency: - -```npm -npm install --save-dev wrangler@latest -``` - - - You must use Wrangler version `3.99.0` or later to deploy Next.js apps using `@opennextjs/cloudflare`. - - -##### 3. Create a wrangler configuration file - - - This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if - not already present). - - -A [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) is needed for your -application to be previewed and deployed, it is also where you configure your Worker and define what resources it can access via [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings). - -You can create one yourself in the root directory of your Next.js app with the name `wrangler.json` and the following content: - -```jsonc -{ - "$schema": "node_modules/wrangler/config-schema.json", - "main": ".open-next/worker.js", - "name": "my-app", - "compatibility_date": "2024-12-30", - "compatibility_flags": ["nodejs_compat"], - "assets": { - "directory": ".open-next/assets", - "binding": "ASSETS", - }, - "kv_namespaces": [ - // Create a KV binding with the binding name "NEXT_CACHE_WORKERS_KV" - // to enable the KV based caching: - // { - // "binding": "NEXT_CACHE_WORKERS_KV", - // "id": "" - // } - ], -} -``` - - - As shown above: - You must enable the [`nodejs_compat` compatibility - flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) *and* set your [compatibility - date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) to `2024-09-23` or - later, in order for your Next.js app to work with @opennextjs/cloudflare - The `main` and `assets` values - should also not be changed unless you modify the build output result in some way - You can add a binding - named `NEXT_CACHE_WORKERS_KV` to make use of Next.js' caching as described in the [Caching - docs](/cloudflare/caching) - - -##### 4. Add an `open-next.config.ts` file - - - This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if - not already present). - - -Add a [`open-next.config.ts`](https://opennext.js.org/aws/config) file to the root directory of your Next.js app: - -```ts -import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; -import cache from "@opennextjs/cloudflare/kvCache"; - -const config: OpenNextConfig = { - default: { - override: { - wrapper: "cloudflare-node", - converter: "edge", - // set `incrementalCache` to "dummy" to disable KV cache - incrementalCache: async () => cache, - tagCache: "dummy", - queue: "dummy", - }, - }, - - middleware: { - external: true, - override: { - wrapper: "cloudflare-edge", - converter: "edge", - proxyExternalRequest: "fetch", - }, - }, -}; - -export default config; -``` - - - To use the `OpenNextConfig` type as illustrated above (which is not necessary), you need to install the - `@opennextjs/aws` NPM package as a dev dependency. - - -##### 5. Add a `.dev.vars` file - -Then, add a [`.dev.vars`](https://developers.cloudflare.com/workers/testing/local-development/#local-only-environment-variables) file to the root directory of your Next.js app: - -```text -NEXTJS_ENV=development -``` - -The `NEXTJS_ENV` variable defines the environment to use when loading Next.js `.env` files. It defaults to "production" when not defined. - -##### 6. Update the `package.json` file - -Add the following to the scripts field of your `package.json` file: - -```json -"build:worker": "opennextjs-cloudflare", -"dev:worker": "wrangler dev --port 8771", -"preview": "npm run build:worker && npm run dev:worker", -"deploy": "npm run build:worker && wrangler deploy", -"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts", -``` - -- `npm run build:worker`: Runs the [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) adapter. This first builds your app by running the `build` script in your `package.json` (Next.js apps use `next build` by default), and then transforms the build output to a format that you can run locally using [Wrangler](https://developers.cloudflare.com/workers/wrangler/), and deploy to Cloudflare. The build command used by OpenNext can be overridden with the `buildCommand` option in your OpenNext config. -- `npm run dev:worker`: Takes the output generated by `build:worker` and runs it locally in [workerd](https://github.com/cloudflare/workerd), the open-source Workers Runtime, allowing you to run the app locally in the same environment that it will run in production. If you instead run `next dev`, your app will run in Node.js, which is a different JavaScript runtime from the Workers runtime, with differences in behavior and APIs. -- `npm run preview`: Runs `build:worker` and then `dev:worker`, allowing you to quickly preview your app running locally in the Workers runtime, via a single command. -- `npm run deploy`: Builds your app, and then deploys it to Cloudflare -- `cf-typegen`: Generates a `cloudflare-env.d.ts` file at the root of your project containing [the types for the `env`](https://developers.cloudflare.com/workers/wrangler/commands/#types). - -##### 7. Add caching with Workers KV - -See the [Caching docs](/cloudflare/caching) for information on enabling Next.js caching in your OpenNext project. - -##### 8. Remove any `export const runtime = "edge";` if present - -Before deploying your app, remove the `export const runtime = "edge";` line from any of your source files. - -The edge runtime is not supported yet with `@opennextjs/cloudflare`. - -##### 9. Add `.open-next` to `.gitignore` - -You should add `.open-next` to your `.gitignore` file to prevent the build output from being committed to your repository. - -##### 10. Remove `@cloudflare/next-on-pages` (if necessary) - -If your Next.js app currently uses `@cloudflare/next-on-pages`, you'll want to remove it, and make a few changes. - -Uninstalling the [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) package as well as the [`eslint-plugin-next-on-pages`](https://www.npmjs.com/package/eslint-plugin-next-on-pages) package if present. - -Remove any reference of these packages from your source and configuration files. -This includes: - -- `setupDevPlatform()` calls in your Next.js config file -- `getRequestContext` imports from `@cloudflare/next-on-pages` from your source files - (those can be replaced with `getCloudflareContext` calls from `@opennextjs/cloudflare`) -- next-on-pages eslint rules set in your Eslint config file - -##### 11. Develop locally - -You can continue to run `next dev` when developing locally. - -During local development, you can access local versions of Cloudflare bindings as indicated in the [bindings documentation](./bindings). - -In step 3, we also added the `npm run preview:worker`, which allows you to quickly preview your app running locally in the Workers runtime, rather than in Node.js. This allows you to test changes in the same runtime as your app will run in when deployed to Cloudflare. - -##### 12. Deploy to Cloudflare Workers - -Either deploy via the command line: - -```sh -npm run deploy:worker -``` - -Or [connect a Github or Gitlab repository](https://developers.cloudflare.com/workers/ci-cd/), and Cloudflare will automatically build and deploy each pull request you merge to your production branch. diff --git a/pages/azion/former-releases/0.3/index.mdx b/pages/azion/former-releases/0.3/index.mdx deleted file mode 100644 index b44423e..0000000 --- a/pages/azion/former-releases/0.3/index.mdx +++ /dev/null @@ -1,71 +0,0 @@ -import { Callout } from "nextra/components"; -import WindowsSupport from "../../../../shared/WindowsSupport.mdx"; - -## Cloudflare - -The [`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) adapter lets you deploy Next.js apps to [Cloudflare Workers](https://developers.cloudflare.com/workers) using the [Node.js "runtime" from Next.js](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). - - -[`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) is pre 1.0, and still in active development. You should try it, [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues), [share feedback](https://github.com/opennextjs/opennextjs-cloudflare/discussions), and contribute code to help make running Next.js apps on Cloudflare easier. We don't quite yet recommend using it for mission-critical production apps. - -You can also use [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) to deploy Next.js apps to Cloudflare Pages. You can review the differences in supported Next.js features below and by reviewing [the docs for `@cloudflare/next-on-pages`](https://developers.cloudflare.com/pages/framework-guides/nextjs/ssr/supported-features/), and understand the differences between Workers and Pages [here](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/). - - - -### Get Started - -##### New apps - -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: - -``` -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -##### Existing Next.js apps - -Follow the guide [here](/cloudflare/get-started) to use [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) with an existing Next.js app. - -### Supported Next.js runtimes - -Next.js has [two "runtimes"](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes) — "Edge" and "Node.js". When you use `@opennextjs/cloudflare`, your app should use the Node.js runtime, which is more fully featured, and allows you to use the [Node.js APIs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) that are provided by the Cloudflare Workers runtime. - -This is an important difference from `@cloudflare/next-on-pages`, which only supports the "Edge" runtime. The Edge Runtime code in Next.js [intentionally constrains which APIs from Node.js can be used](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/webpack/plugins/middleware-plugin.ts#L820), and the "Edge" runtime does not support all Next.js features. - -### Supported Next.js versions - -`@opennextjs/cloudflare` is pre 1.0, and still in active development. We intend to support all minor and patch versions of Next.js 14 and 15. - -To help improve compatibility, we encourage you to [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues) and contribute code! - -### Supported Next.js features - -Some Next.js features are not yet supported are not fully tested. -We will update the list as we progress towards releasing 1.0. - -- [x] [App Router](https://nextjs.org/docs/app) -- [x] [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) -- [x] [Dynamic routes](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) -- [x] [Static Site Generation (SSG)](https://nextjs.org/docs/app/building-your-application/rendering/server-components#static-rendering-default) -- [x] [Server-Side Rendering (SSR)](https://nextjs.org/docs/app/building-your-application/rendering/server-components) -- [x] [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) -- [x] [Image optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) (you can integrate Cloudflare Images with Next.js by following [this guide](https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/)) -- [ ] [Pages Router](https://nextjs.org/docs/pages) -- [ ] [Incremental Static Regeneration (ISR)](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration) -- [ ] [Partial Prerendering (PPR)](https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering) -- [ ] [Support for after](https://nextjs.org/blog/next-15-rc#executing-code-after-a-response-with-nextafter-experimental) -- [ ] [Composable Caching](https://nextjs.org/blog/composable-caching) (`'use cache'`) is a Next.js 15 feature and not supported yet. - -We welcome both contributions and feedback! - -### Windows support - - - -### How `@opennextjs/cloudflare` Works - -The OpenNext Cloudflare adapter works by taking the Next.js build output and transforming it, so that it can run in Cloudflare Workers. - -When you add [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) as a dependency to your Next.js app, and then run `npx opennextjs-cloudflare` the adapter first builds your app by running the `build` script in your `package.json`, and then transforms the build output to a format that you can run locally using [Wrangler](https://developers.cloudflare.com/workers/wrangler/), and deploy to Cloudflare. - -You can view the code for `@opennextjs/cloudflare` [here](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src) to understand what it does under the hood. diff --git a/pages/azion/former-releases/0.5/_meta.json b/pages/azion/former-releases/0.5/_meta.json deleted file mode 100644 index 53a20a0..0000000 --- a/pages/azion/former-releases/0.5/_meta.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "index": "Overview", - "get-started": "", - "bindings": "", - "caching": "", - "examples": "" -} diff --git a/pages/azion/former-releases/0.5/bindings.mdx b/pages/azion/former-releases/0.5/bindings.mdx deleted file mode 100644 index df0a480..0000000 --- a/pages/azion/former-releases/0.5/bindings.mdx +++ /dev/null @@ -1,74 +0,0 @@ -import { Callout } from "nextra/components"; - -### Bindings - -[Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. When you declare a binding on your Worker, you grant it a specific capability, such as being able to read and write files to an [R2](https://developers.cloudflare.com/r2/) bucket. - -#### How to configure your Next.js app so it can access bindings - -Install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare), and then add a [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) in the root directory of your Next.js app, as described in [Get Started](/cloudflare/get-started#3-create-a-wranglerjson-file). - -#### How to access bindings in your Next.js app - -You can access [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) from any route of your Next.js app via `getCloudflareContext`: - -```js -import { getCloudflareContext } from "@opennextjs/cloudflare"; - -export async function GET(request) { - let responseText = "Hello World"; - - const myKv = getCloudflareContext().env.MY_KV_NAMESPACE; - await myKv.put("foo", "bar"); - const foo = await myKv.get("foo"); - - return new Response(foo); -} -``` - - - `getCloudflareContext` can only be used in SSG routes in "async mode" (making it return a promise), to run the function in such a way simply provide an options argument with `async` set to `true`: - ```js - const context = await getCloudflareContext({ async: true }); - ``` - - **WARNING**: During SSG caution is advised since secrets (stored in `.dev.vars` files) and local development - values from bindings (like values saved in a local KV) will be used for the pages static generation. - - - -#### How to add bindings to your Worker - -Add bindings to your Worker by adding them to your [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). - -## TypeScript type declarations for bindings - -To ensure that the `env` object from `getCloudflareContext().env` above has accurate TypeScript types, run the following Wrangler command to [generate types that match your Worker's configuration](https://developers.cloudflare.com/workers/languages/typescript/#generate-types-that-match-your-workers-configuration-experimental): - -``` -npx wrangler types --experimental-include-runtime -``` - -This will generate a `d.ts` file and (by default) save it to `.wrangler/types/runtime.d.ts`. You will be prompted in the command's output to add that file to your `tsconfig.json`'s `compilerOptions.types` array. - -If you would like to commit the file to git, you can provide a custom path. Here, for instance, the `runtime.d.ts` file will be saved to the root of your project: - -```bash -npx wrangler types --experimental-include-runtime="./runtime.d.ts" -``` - -To ensure that your types are always up-to-date, make sure to run `wrangler types --experimental-include-runtime` after any changes to your config file. - -## Other Cloudflare APIs (`cf`, `ctx`) - -You can access context about the incoming request from the [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), as well as lifecycle methods from the [`ctx` object](https://developers.cloudflare.com/workers/runtime-apis/context) from the return value of [`getCloudflareContext()`](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src/api/get-cloudflare-context.ts): - -```js -import { getCloudflareContext } from "@opennextjs/cloudflare"; - -export async function GET(request) { - const { env, cf, ctx } = getCloudflareContext(); - - // ... -} -``` diff --git a/pages/azion/former-releases/0.5/caching.mdx b/pages/azion/former-releases/0.5/caching.mdx deleted file mode 100644 index c5eed52..0000000 --- a/pages/azion/former-releases/0.5/caching.mdx +++ /dev/null @@ -1,156 +0,0 @@ -import { Callout } from "nextra/components"; - -## Caching - -`@opennextjs/cloudflare` supports [caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data). - -Next.js primes the cache at build time. The build time values are serverd by the [Workers Assets](https://developers.cloudflare.com/workers/static-assets/). - - - Workers KV is eventually consistent, which means that it can take up to 60 seconds for updates to be - reflected globally, when using the default TTL of 60 seconds. - - -### How to enable caching - -`@opennextjs/cloudflare` supports multiple caching mechanisms through a project's OpenNext configuration. - -#### Incremental Static Regeneration (ISR) - -The ISR adapter for Cloudflare uses [Workers KV](https://developers.cloudflare.com/kv/) as the cache for your Next.js app. Workers KV is [fast](https://blog.cloudflare.com/faster-workers-kv) and uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. Pricing information can be found in the Cloudflare [docs](https://developers.cloudflare.com/workers/platform/pricing/#workers-kv). - -##### 1. Create a KV namespace - -``` -npx wrangler@latest kv namespace create -``` - -##### 2. Add the KV namespace and Service Binding to your Worker - -The binding name used in your app's worker is `NEXT_CACHE_WORKERS_KV`. The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. - -```jsonc -// wrangler.jsonc -{ - // ... - "kv_namespaces": [ - { - "binding": "NEXT_CACHE_WORKERS_KV", - "id": "", - }, - ], - "services": [ - { - "binding": "NEXT_CACHE_REVALIDATION_WORKER", - "service": "", - }, - ], -} -``` - -#### 3. Configure the cache - -In your project's OpenNext config, enable the KV cache and set up a queue. - -The memory queue will send revalidation requests to a page when needed, and offers support for de-duplicating requests on a per-isolate basis. There might still be duplicate requests under high traffic or across regions. - - - The memory queue provided by `@opennextjs/cloudflare` is not fully suitable for production deployments, you - can use it at your own risk! - - -```ts -// open-next.config.ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import kvIncrementalCache from "@opennextjs/cloudflare/kv-cache"; -import memoryQueue from "@opennextjs/cloudflare/memory-queue"; - -export default defineCloudflareConfig({ - incrementalCache: kvIncrementalCache, - queue: memoryQueue, -}); -``` - - - The `direct` mode for the queue is intended for debugging purposes and is not recommended for use in - production. We are actively working on a solution that will be suitable for production. - - -#### On-Demand Revalidation - -The tag revalidation mechanism uses a [Cloudflare D1](https://developers.cloudflare.com/d1/) database as its backing store for information about tags, paths, and revalidation times. - -To use on-demand revalidation, you should also follow the [ISR setup steps](#incremental-static-regeneration-isr). - - - If your app **only** uses the pages router, it does not need to have a tag cache and should skip this step. - - -##### 1. Create a D1 database and Service Binding - -The binding name used in your app's worker is `NEXT_CACHE_D1`. The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. - -```jsonc -// wrangler.jsonc -{ - // ... - "d1_databases": [ - { - "binding": "NEXT_CACHE_D1", - "database_id": "", - "database_name": "", - }, - ], - "services": [ - { - "binding": "NEXT_CACHE_REVALIDATION_WORKER", - "service": "", - }, - ], -} -``` - -##### 2. Create tables for tag revalidations - -The D1 tag cache requires two tables; one that keeps a record of the tag/path mappings, and another that tracks revalidation times. - -For the tag mappings, the default table name is `tags`, and can be configured by setting the `NEXT_CACHE_D1_TAGS_TABLE` environment variable to a string. - -For the revalidation times, the default table name is `revalidations` and can be configured by setting the `NEXT_CACHE_D1_REVALIDATIONS_TABLE` environment variable to a string. - -Wrangler can be used to create a table with it's [execute](https://developers.cloudflare.com/d1/wrangler-commands/#d1-execute) option. Ensure that you create a table for both your local dev database and your remote database. - -```sh -wrangler d1 execute NEXT_CACHE_D1 --command "CREATE TABLE IF NOT EXISTS tags (tag TEXT NOT NULL, path TEXT NOT NULL, UNIQUE(tag, path) ON CONFLICT REPLACE)" -wrangler d1 execute NEXT_CACHE_D1 --command "CREATE TABLE IF NOT EXISTS revalidations (tag TEXT NOT NULL, revalidatedAt INTEGER NOT NULL, UNIQUE(tag) ON CONFLICT REPLACE)" -``` - -##### 3. Configure the cache - -In your project's OpenNext config, enable the KV cache and set up a queue. The queue will send a revalidation request to a page when needed, but it will not dedupe requests. - -```ts -// open-next.config.ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import kvIncrementalCache from "@opennextjs/cloudflare/kv-cache"; -import d1TagCache from "@opennextjs/cloudflare/d1-tag-cache"; -import memoryQueue from "@opennextjs/cloudflare/memory-queue"; - -export default defineCloudflareConfig({ - incrementalCache: kvIncrementalCache, - tagCache: d1TagCache, - queue: memoryQueue, -}); -``` - -##### 4. Initialise the cache during deployments - -In order for the cache to be properly initialised with the build-time revalidation data, you need to setup a command that runs as part of your deploy step. - -OpenNext will generate an SQL file during the build that can be used to setup your D1 database. - -```sh -wrangler d1 execute NEXT_CACHE_D1 --file .open-next/cloudflare/cache-assets-manifest.sql -``` - -This should be run as part of each deployment to ensure that the cache is being populated with each build's initial revalidation data. diff --git a/pages/azion/former-releases/0.5/examples.mdx b/pages/azion/former-releases/0.5/examples.mdx deleted file mode 100644 index e44bee0..0000000 --- a/pages/azion/former-releases/0.5/examples.mdx +++ /dev/null @@ -1,24 +0,0 @@ -import { Callout } from "nextra/components"; - -## Examples - -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: - -``` -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -### Basic starter projects - -Basic example apps are included in the repository for `@opennextjs/cloudflare` package: - -- [_`create-next-app`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/create-next-app) — a Next.js project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). -- [_`api`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/api) — a minimal Next.js project with a single API route -- [_`middleware`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/middleware) — a minimal Next.js project using middleware -- [_`vercel-blog-starter`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/vercel-blog-starter) — a blog project using SSG - -You can use these to understand how to configure your Next.js app to use `@opennextjs/cloudflare`, or refer to [Get Started](/cloudflare/get-started). - -### Next.js Commerce Demo - -The [Next.js Commerce demo app](https://github.com/vercel/commerce/tree/v1) works with `@opennextjs/cloudflare`. You can view a deployed version of it [here](https://vercel-commerce-on-workers.web-experiments.workers.dev/). diff --git a/pages/azion/former-releases/0.5/get-started.mdx b/pages/azion/former-releases/0.5/get-started.mdx deleted file mode 100644 index f1d70dd..0000000 --- a/pages/azion/former-releases/0.5/get-started.mdx +++ /dev/null @@ -1,190 +0,0 @@ -import { Callout } from "nextra/components"; - -### Get Started - -#### New apps - -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: - -```sh -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -#### Existing Next.js apps - -##### 1. Install @opennextjs/cloudflare - -First, install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare): - -```sh -npm install --save-dev @opennextjs/cloudflare@latest -``` - -##### 2. Install Wrangler - -Install the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) as a devDependency: - -```sh -npm install --save-dev wrangler@latest -``` - - - You must use Wrangler version `3.99.0` or later to deploy Next.js apps using `@opennextjs/cloudflare`. - - -##### 3. Create a wrangler configuration file - - - This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if - not already present). - - -A [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) is needed for your -application to be previewed and deployed, it is also where you configure your Worker and define what resources it can access via [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings). - -You can create one yourself in the root directory of your Next.js app with the name `wrangler.jsonc` and the following content: - -```jsonc -{ - "$schema": "node_modules/wrangler/config-schema.json", - "main": ".open-next/worker.js", - "name": "my-app", - "compatibility_date": "2024-12-30", - "compatibility_flags": ["nodejs_compat"], - "assets": { - "directory": ".open-next/assets", - "binding": "ASSETS", - }, - "kv_namespaces": [ - // Create a KV binding with the binding name "NEXT_CACHE_WORKERS_KV" - // to enable the KV based caching: - // { - // "binding": "NEXT_CACHE_WORKERS_KV", - // "id": "" - // } - ], -} -``` - - - As shown above: - You must enable the [`nodejs_compat` compatibility - flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) *and* set your [compatibility - date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) to `2024-09-23` or - later, in order for your Next.js app to work with @opennextjs/cloudflare - The `main` and `assets` values - should also not be changed unless you modify the build output result in some way - You can add a binding - named `NEXT_CACHE_WORKERS_KV` to make use of Next.js' caching as described in the [Caching - docs](/cloudflare/caching) - - -##### 4. Add an `open-next.config.ts` file - - - This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if - not already present). - - -Add a [`open-next.config.ts`](https://opennext.js.org/aws/config) file to the root directory of your Next.js app: - -```ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import kvIncrementalCache from "@opennextjs/cloudflare/kv-cache"; - -export default defineCloudflareConfig({ - incrementalCache: kvIncrementalCache, -}); -``` - - - To use the `OpenNextConfig` type as illustrated above (which is not necessary), you need to install the - `@opennextjs/aws` NPM package as a dev dependency. - - -##### 5. Add a `.dev.vars` file - -Then, add a [`.dev.vars`](https://developers.cloudflare.com/workers/testing/local-development/#local-only-environment-variables) file to the root directory of your Next.js app: - -```text -NEXTJS_ENV=development -``` - -The `NEXTJS_ENV` variable defines the environment to use when loading Next.js `.env` files. It defaults to "production" when not defined. - -##### 6. Update the `package.json` file - -Add the following to the scripts field of your `package.json` file: - -```json -"preview": "opennextjs-cloudflare && wrangler dev", -"deploy": "opennextjs-cloudflare && wrangler deploy", -"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts", -``` - -- `npm run preview`: Builds your app and serves it locally, allowing you to quickly preview your app running locally in the Workers runtime, via a single command. -- `npm run deploy`: Builds your app, and then deploys it to Cloudflare -- `cf-typegen`: Generates a `cloudflare-env.d.ts` file at the root of your project containing [the types for the `env`](https://developers.cloudflare.com/workers/wrangler/commands/#types). - -##### 7. Add caching with Workers KV - -See the [Caching docs](/cloudflare/caching) for information on enabling Next.js caching in your OpenNext project. - -##### 8. Remove any `export const runtime = "edge";` if present - -Before deploying your app, remove the `export const runtime = "edge";` line from any of your source files. - -The edge runtime is not supported yet with `@opennextjs/cloudflare`. - -##### 9. Add `.open-next` to `.gitignore` - -You should add `.open-next` to your `.gitignore` file to prevent the build output from being committed to your repository. - -##### 10. Remove `@cloudflare/next-on-pages` (if necessary) - -If your Next.js app currently uses `@cloudflare/next-on-pages`, you'll want to remove it, and make a few changes. - -Uninstalling the [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) package as well as the [`eslint-plugin-next-on-pages`](https://www.npmjs.com/package/eslint-plugin-next-on-pages) package if present. - -Remove any reference of these packages from your source and configuration files. -This includes: - -- `setupDevPlatform()` calls in your Next.js config file -- `getRequestContext` imports from `@cloudflare/next-on-pages` from your source files - (those can be replaced with `getCloudflareContext` calls from `@opennextjs/cloudflare`) -- next-on-pages eslint rules set in your Eslint config file - -##### 11. Develop locally - -You can continue to run `next dev` when developing locally. - -Modify your Next.js configuration file to import and call the `initOpenNextCloudflareForDev` utility -from the `@opennextjs/cloudflare` package. This makes sure that the Next.js dev server can optimally integrate with the open-next cloudflare adapter and it is necessary for using bindings during local development. - -This is an example of a Next.js configuration file calling the utility: - -```ts -// next.config.ts -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ -}; - -export default nextConfig; - -import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; -initOpenNextCloudflareForDev(); -``` - -After having added the `initOpenNextCloudflareForDev()` call in your Next.js configuration file, you will be able, during local development, to access in any of your server code, local versions of Cloudflare bindings as indicated in the [bindings documentation](./bindings). - -In step 3, we also added the `npm run preview`, which allows you to quickly preview your app running locally in the Workers runtime, -rather than in Node.js. This allows you to test changes in the same runtime as your app will run in when deployed to Cloudflare. - -##### 12. Deploy to Cloudflare Workers - -Either deploy via the command line: - -```sh -npm run deploy -``` - -Or [connect a Github or Gitlab repository](https://developers.cloudflare.com/workers/ci-cd/), and Cloudflare will automatically build and deploy each pull request you merge to your production branch. diff --git a/pages/azion/former-releases/0.5/index.mdx b/pages/azion/former-releases/0.5/index.mdx deleted file mode 100644 index cdb70bb..0000000 --- a/pages/azion/former-releases/0.5/index.mdx +++ /dev/null @@ -1,73 +0,0 @@ -import { Callout } from "nextra/components"; -import WindowsSupport from "../../../../shared/WindowsSupport.mdx"; - -## Cloudflare - -The [`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) adapter lets you deploy Next.js apps to [Cloudflare Workers](https://developers.cloudflare.com/workers) using the [Node.js "runtime" from Next.js](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). - - -[`@opennextjs/cloudflare`](https://www.npmjs.com/package/@opennextjs/cloudflare) is pre 1.0, and still in active development. You should try it, [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues), [share feedback](https://github.com/opennextjs/opennextjs-cloudflare/discussions), and contribute code to help make running Next.js apps on Cloudflare easier. We don't quite yet recommend using it for mission-critical production apps. - -You can also use [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) to deploy Next.js apps to Cloudflare Pages. You can review the differences in supported Next.js features below and by reviewing [the docs for `@cloudflare/next-on-pages`](https://developers.cloudflare.com/pages/framework-guides/nextjs/ssr/supported-features/), and understand the differences between Workers and Pages [here](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/). - - - -### Get Started - -##### New apps - -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: - -``` -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -##### Existing Next.js apps - -Follow the guide [here](/cloudflare/get-started) to use [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) with an existing Next.js app. - -### Supported Next.js runtimes - -Next.js has [two "runtimes"](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes) — "Edge" and "Node.js". When you use `@opennextjs/cloudflare`, your app should use the Node.js runtime, which is more fully featured, and allows you to use the [Node.js APIs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) that are provided by the Cloudflare Workers runtime. - -This is an important difference from `@cloudflare/next-on-pages`, which only supports the "Edge" runtime. The Edge Runtime code in Next.js [intentionally constrains which APIs from Node.js can be used](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/webpack/plugins/middleware-plugin.ts#L820), and the "Edge" runtime does not support all Next.js features. - -### Supported Next.js versions - -`@opennextjs/cloudflare` is pre 1.0, and still in active development. We intend to support all minor and patch versions of Next.js 15 and the latest minor of Next.js 14. - -To help improve compatibility, we encourage you to [report bugs](https://github.com/opennextjs/opennextjs-cloudflare/issues) and contribute code! - -### Supported Next.js features - -Some Next.js features are not yet supported are not fully tested. -We will update the list as we progress towards releasing 1.0. - -- [x] [App Router](https://nextjs.org/docs/app) -- [x] [Route Handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) -- [x] [Dynamic routes](https://nextjs.org/docs/app/building-your-application/routing/dynamic-routes) -- [x] [Static Site Generation (SSG)](https://nextjs.org/docs/app/building-your-application/rendering/server-components#static-rendering-default) -- [x] [Server-Side Rendering (SSR)](https://nextjs.org/docs/app/building-your-application/rendering/server-components) -- [x] [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) -- [x] [Image optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) (you can integrate Cloudflare Images with Next.js by following [this guide](https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/)) -- [x] [Partial Prerendering (PPR)](https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering) -- [x] [Pages Router](https://nextjs.org/docs/pages) -- [x] [Incremental Static Regeneration (ISR)](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration) 1 -- [x] [Support for after](https://nextjs.org/blog/next-15-rc#executing-code-after-a-response-with-nextafter-experimental) -- [ ] [Composable Caching](https://nextjs.org/blog/composable-caching) (`'use cache'`) is a Next.js 15 feature and not supported yet. - -1 Only the `direct` mode is supported at the moment, and is not suitable for production. - -We welcome both contributions and feedback! - -### Windows support - - - -### How `@opennextjs/cloudflare` Works - -The OpenNext Cloudflare adapter works by taking the Next.js build output and transforming it, so that it can run in Cloudflare Workers. - -When you add [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare) as a dependency to your Next.js app, and then run `npx opennextjs-cloudflare` the adapter first builds your app by running the `build` script in your `package.json`, and then transforms the build output to a format that you can run locally using [Wrangler](https://developers.cloudflare.com/workers/wrangler/), and deploy to Cloudflare. - -You can view the code for `@opennextjs/cloudflare` [here](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src) to understand what it does under the hood. diff --git a/pages/azion/former-releases/0.6/_meta.json b/pages/azion/former-releases/0.6/_meta.json deleted file mode 100644 index 53a20a0..0000000 --- a/pages/azion/former-releases/0.6/_meta.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "index": "Overview", - "get-started": "", - "bindings": "", - "caching": "", - "examples": "" -} diff --git a/pages/azion/former-releases/0.6/bindings.mdx b/pages/azion/former-releases/0.6/bindings.mdx deleted file mode 100644 index df0a480..0000000 --- a/pages/azion/former-releases/0.6/bindings.mdx +++ /dev/null @@ -1,74 +0,0 @@ -import { Callout } from "nextra/components"; - -### Bindings - -[Bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) allow your Worker to interact with resources on the Cloudflare Developer Platform. When you declare a binding on your Worker, you grant it a specific capability, such as being able to read and write files to an [R2](https://developers.cloudflare.com/r2/) bucket. - -#### How to configure your Next.js app so it can access bindings - -Install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare), and then add a [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) in the root directory of your Next.js app, as described in [Get Started](/cloudflare/get-started#3-create-a-wranglerjson-file). - -#### How to access bindings in your Next.js app - -You can access [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/) from any route of your Next.js app via `getCloudflareContext`: - -```js -import { getCloudflareContext } from "@opennextjs/cloudflare"; - -export async function GET(request) { - let responseText = "Hello World"; - - const myKv = getCloudflareContext().env.MY_KV_NAMESPACE; - await myKv.put("foo", "bar"); - const foo = await myKv.get("foo"); - - return new Response(foo); -} -``` - - - `getCloudflareContext` can only be used in SSG routes in "async mode" (making it return a promise), to run the function in such a way simply provide an options argument with `async` set to `true`: - ```js - const context = await getCloudflareContext({ async: true }); - ``` - - **WARNING**: During SSG caution is advised since secrets (stored in `.dev.vars` files) and local development - values from bindings (like values saved in a local KV) will be used for the pages static generation. - - - -#### How to add bindings to your Worker - -Add bindings to your Worker by adding them to your [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). - -## TypeScript type declarations for bindings - -To ensure that the `env` object from `getCloudflareContext().env` above has accurate TypeScript types, run the following Wrangler command to [generate types that match your Worker's configuration](https://developers.cloudflare.com/workers/languages/typescript/#generate-types-that-match-your-workers-configuration-experimental): - -``` -npx wrangler types --experimental-include-runtime -``` - -This will generate a `d.ts` file and (by default) save it to `.wrangler/types/runtime.d.ts`. You will be prompted in the command's output to add that file to your `tsconfig.json`'s `compilerOptions.types` array. - -If you would like to commit the file to git, you can provide a custom path. Here, for instance, the `runtime.d.ts` file will be saved to the root of your project: - -```bash -npx wrangler types --experimental-include-runtime="./runtime.d.ts" -``` - -To ensure that your types are always up-to-date, make sure to run `wrangler types --experimental-include-runtime` after any changes to your config file. - -## Other Cloudflare APIs (`cf`, `ctx`) - -You can access context about the incoming request from the [`cf` object](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties), as well as lifecycle methods from the [`ctx` object](https://developers.cloudflare.com/workers/runtime-apis/context) from the return value of [`getCloudflareContext()`](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/packages/cloudflare/src/api/get-cloudflare-context.ts): - -```js -import { getCloudflareContext } from "@opennextjs/cloudflare"; - -export async function GET(request) { - const { env, cf, ctx } = getCloudflareContext(); - - // ... -} -``` diff --git a/pages/azion/former-releases/0.6/caching.mdx b/pages/azion/former-releases/0.6/caching.mdx deleted file mode 100644 index 0f642d3..0000000 --- a/pages/azion/former-releases/0.6/caching.mdx +++ /dev/null @@ -1,418 +0,0 @@ -import { Callout } from "nextra/components"; -import { Tabs } from "nextra/components"; - -## Caching - -`@opennextjs/cloudflare` supports [caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data). - -### How to enable caching - -`@opennextjs/cloudflare` supports multiple caching mechanisms through a project's OpenNext configuration. - -#### Incremental Static Regeneration (ISR) - -There are two storage options to use for the incremental cache. - -- **Workers KV:** A [fast](https://blog.cloudflare.com/faster-workers-kv) and uses Cloudflare's [Tiered Cache](https://developers.cloudflare.com/cache/how-to/tiered-cache/) to increase cache hit rates. When you write cached data to Workers KV, you write to storage that can be read by any Cloudflare location. This means your app can fetch data, cache it in KV, and then subsequent requests anywhere around the world can read from this cache. The build time values are serverd by the [Workers Assets](https://developers.cloudflare.com/workers/static-assets/). Pricing information can be found in the Cloudflare [docs](https://developers.cloudflare.com/workers/platform/pricing/#workers-kv). -- **R2 Object Storage:** A [cost-effective](https://developers.cloudflare.com/r2/pricing/) S3-compatible object storage option for large amounts of unstructured data. Data is stored in a single region, meaning cache interactions may be slower - this can be mitigated with a regional cache. - - - Workers KV is eventually consistent, which means that it can take up to 60 seconds for updates to be - reflected globally, when using the default TTL of 60 seconds. - - - - -##### 1. Create a KV namespace - -``` -npx wrangler@latest kv namespace create -``` - -##### 2. Add the KV namespace and Service Binding to your Worker - -The binding name used in your app's worker is `NEXT_INC_CACHE_KV`. -The `WORKER_SELF_REFERENCE` service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. - -```jsonc -// wrangler.jsonc -{ - // ... - "name": "", - "kv_namespaces": [ - { - "binding": "NEXT_INC_CACHE_KV", - "id": "", - }, - ], - "services": [ - { - "binding": "WORKER_SELF_REFERENCE", - "service": "", - }, - ], -} -``` - -##### 3. Configure the cache - -In your project's OpenNext config, enable the KV cache. - -```ts -// open-next.config.ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache"; -// ... - -export default defineCloudflareConfig({ - incrementalCache: kvIncrementalCache, - // ... -}); -``` - - - - - -##### 1. Create an R2 Bucket - -``` -npx wrangler@latest r2 bucket create -``` - -##### 2. Add the R2 Bucket and Service Binding to your Worker - -The binding name used in your app's worker is `NEXT_INC_CACHE_R2_BUCKET`. The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. - -The prefix used by the R2 bucket can be configured with the `NEXT_INC_CACHE_R2_PREFIX` environment variable, and defaults to `incremental-cache`. - -```jsonc -// wrangler.jsonc -{ - // ... - "name": "", - "r2_buckets": [ - { - "binding": "NEXT_INC_CACHE_R2_BUCKET", - "bucket_name": "", - "preview_bucket_name": "", - }, - ], - "services": [ - { - "binding": "WORKER_SELF_REFERENCE", - "service": "", - }, - ], -} -``` - -##### 3. Configure the cache - -In your project's OpenNext config, enable the R2 cache. - -You can optionally setup a regional cache to use with the R2 incremental cache. This will enable faster retrieval of cache entries and reduce the amount of requests being sent to object storage. - -The regional cache has two modes: - -- `short-lived`: Responses are re-used for up to a minute. -- `long-lived`: Fetch responses are re-used until revalidated, and ISR/SSG responses are re-used for up to 30 minutes. - -Additionally, lazy updating of the regional cache can be enabled with the `shouldLazilyUpdateOnCacheHit` option. When requesting data from the cache, it sends a background request to the R2 bucket to get the latest entry. This is enabled by default for the `long-lived` mode. - -```ts -// open-next.config.ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; -import { withRegionalCache } from "@opennextjs/cloudflare/overrides/incremental-cache/regional-cache"; -// ... - -// With regional cache enabled: -export default defineCloudflareConfig({ - incrementalCache: withRegionalCache(r2IncrementalCache, { - mode: "long-lived", - shouldLazilyUpdateOnCacheHit: true, - }), - // ... -}); - -// Without regional cache: -export default defineCloudflareConfig({ - incrementalCache: r2IncrementalCache, - // ... -}); -``` - - - - -##### 4. Configure the queue - -In your project's OpenNext config, enable the cache and set up a queue. - -The Durable Object Queue will send revalidation requests to a page when needed, and offers support for de-duplicating requests. -By default there will be a maximum of 10 instance of the Durables Object Queue and they can each process up to 5 requests in parallel, for up to 50 concurrent ISR revalidations. - -```ts -// open-next.config.ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -// ... -import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; - -export default defineCloudflareConfig({ - // ... - queue: doQueue, -}); -``` - -You will also need to add some binding to your `wrangler.jsonc` file. - -```jsonc -"durable_objects": { - "bindings": [ - { - "name": "NEXT_CACHE_DO_QUEUE", - "class_name": "DurableObjectQueueHandler" - } - ] - }, - "migrations": [ - { - "tag": "v1", - "new_sqlite_classes": ["DurableObjectQueueHandler"] - } - ], -``` - -You can customize the behaviors of the queue with environment variables: - -- The max number of revalidations that can be processed by an instance of durable object at the same time (`NEXT_CACHE_DO_QUEUE_MAX_REVALIDATION`) -- The max time in milliseconds that a revalidation can take before being considered as failed (`NEXT_CACHE_DO_QUEUE_REVALIDATION_TIMEOUT_MS`) -- The amount of time after which a revalidation will be attempted again if it failed. If it fails again it will exponentially back off until it reaches the max retry interval (`NEXT_CACHE_DO_QUEUE_RETRY_INTERVAL_MS`) -- The maximum number of attempts that can be made to revalidate a path (`NEXT_CACHE_DO_QUEUE_MAX_NUM_REVALIDATIONS`) -- Disable SQLite for this durable object. It should only be used if your incremental cache is not eventually consistent (`NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE`) - - - There is 2 additional modes that you can use for the queue `direct` and the memory queue - -- The memory queue will dedupe request but only on a per isolate basis. It is not fully suitable for production deployments, you - can use it at your own risk! - -- The `direct` mode for the queue is intended for debugging purposes and is not recommended for use in - production. It only works in preview mode (i.e. `wrangler dev`) - - For apps using the Page Router, `res.revalidate` requires to provide a self reference service binding named `WORKER_SELF_REFERENCE`. - - - -#### On-Demand Revalidation - -The tag revalidation mechanism can use either a [Cloudflare D1](https://developers.cloudflare.com/d1/) database or [Durable Objects](https://developers.cloudflare.com/durable-objects/) with `SqliteStorage` as its backing store for information about tags, paths, and revalidation times. - -To use on-demand revalidation, you should also follow the [ISR setup steps](#incremental-static-regeneration-isr). - - - If your app **only** uses the pages router, it does not need to have a tag cache and should skip this step. - You can also skip this step if your app doesn't to use `revalidateTag` nor `revalidatePath`. - - -There are 3 different options to choose from for the tag cache: `d1NextTagCache`, `doShardedTagCache` and `d1TagCache`. -Which one to choose should be based on two key factors: - -1. **Expected Load**: Consider the volume of traffic or data you anticipate. -2. **Usage of** `revalidateTag` / `revalidatePath`: Evaluate how frequently these features will be utilized. - -If either of these factors is significant, opting for a sharded database is recommended. Additionally, incorporating a regional cache can further enhance performance. - - - -##### 1. Create a D1 database and Service Binding - -The binding name used in your app's worker is `NEXT_TAG_CACHE_D1`. The `WORKER_SELF_REFERENCE` service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. - -```jsonc -// wrangler.jsonc -{ - // ... - "d1_databases": [ - { - "binding": "NEXT_TAG_CACHE_D1", - "database_id": "", - "database_name": "", - }, - ], - "services": [ - { - "binding": "WORKER_SELF_REFERENCE", - "service": "", - }, - ], -} -``` - -##### 2. Create table for tag revalidations - -The D1 tag cache requires a `revalidations` table that tracks On-Demand revalidation times. - -##### 3. Configure the cache - -In your project's OpenNext config, enable the KV cache and set up a queue (see above). The queue will send a revalidation request to a page when needed, but it will not dedupe requests. - -```ts -// open-next.config.ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import kvIncrementalCache from "@opennextjs/cloudflare/kv-cache"; -import d1NextTagCache from "@opennextjs/cloudflare/d1-next-tag-cache"; -import memoryQueue from "@opennextjs/cloudflare/memory-queue"; - -export default defineCloudflareConfig({ - incrementalCache: kvIncrementalCache, - tagCache: d1NextTagCache, - queue: memoryQueue, -}); -``` - - - - -##### 1. Create a Durable Object and Service Binding -The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. - -```jsonc -// wrangler.jsonc -{ - // ... - "durable_objects": { - "bindings": [ - { - "name": "NEXT_CACHE_DO_QUEUE", - "class_name": "DurableObjectQueueHandler", - }, - { - "name": "NEXT_TAG_CACHE_DO_SHARDED", - "class_name": "DOShardedTagCache", - }, - ], - }, - "migrations": [ - { - "tag": "v1", - "new_sqlite_classes": ["DurableObjectQueueHandler", "DOShardedTagCache"], - }, - ], - "services": [ - { - "binding": "WORKER_SELF_REFERENCE", - "service": "", - }, - ], -} -``` - -##### 2. Configure the cache - -In your project's OpenNext config, enable the KV cache and set up a queue. The queue will send a revalidation request to a page when needed, but it will not dedupe requests. - -```ts -// open-next.config.ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache"; -import memoryQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; -import doShardedTagCache from "@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache"; - -export default defineCloudflareConfig({ - incrementalCache: kvIncrementalCache, - tagCache: doShardedTagCache({ baseShardSize: 12, regionalCache: true }), - queue: memoryQueue, -}); -``` - -doShardedTagCache tahes the following options: - -- `baseShardSize` - The number of shards to use for the cache. The more shards you have, the more evenly the cache will be distributed across the shards. The default is 4. Soft (internal next tags used for `revalidatePath`) and hard tags (the one you define in your app) will be split in different shards -- `regionalCache` - Whether to use regional cache for the cache. The default is false. This option is useful when you want to reduce the stress on the durable object -- `regionalCacheTtlSec` - The TTL for the regional cache. The default is 5 seconds. Increasing this value will increase the time it takes for the cache to be invalidated across regions -- `enableShardReplication`: Enable replicating the Shard. Shard replication will duplicate each shards into replicas to spread the load even more -- `shardReplicationOptions.numberOfSoftReplicas`: Number of replicas for the soft tag shards -- `shardReplicationOptions.numberOfHardReplicas`: Number of replicas for the hard tag shards -- `maxWriteRetries`: The number of retries to perform when writing tags - - - - - - - The `d1TagCache` is not recommended for production use, as it does not scale well with the number of tags. - - -##### 1. Create a D1 database and Service Binding - -The binding name used in your app's worker is `NEXT_TAG_CACHE_D1`. The service binding should be a self reference to your worker where `` is the name in your wrangler configuration file. - -```jsonc -// wrangler.jsonc -{ - // ... - "name": "", - "d1_databases": [ - { - "binding": "NEXT_TAG_CACHE_D1", - "database_id": "", - "database_name": "", - }, - ], - "services": [ - { - "binding": "WORKER_SELF_REFERENCE", - "service": "", - }, - ], -} -``` - -The D1 database uses two tables, created when initialising the cache: - -- the "tags" table keeps a record of the tag/path mappings -- the "revalidations" table tracks revalidation times - -##### 2. Configure the cache - -In your project's OpenNext config, enable the KV cache and set up a queue. The queue will send a revalidation request to a page when needed, but it will not dedupe requests. - -```ts -// open-next.config.ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache"; -import d1TagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-tag-cache"; -import memoryQueue from "@opennextjs/cloudflare/overrides/queue/memory-queue"; - -export default defineCloudflareConfig({ - incrementalCache: kvIncrementalCache, - tagCache: d1TagCache, - queue: memoryQueue, -}); -``` - -##### 3. Initialise the cache during deployments - -In order for the cache to be properly initialised with the build-time revalidation data, you need to run a command as part of your deploy step. This should be run as part of each deployment to ensure that the cache is being populated with each build's data. - -To populate remote bindings and deploy your application at the same time, you can use the `deploy` command. Similarly, the `preview` command will populate your local bindings and start a Wrangler dev server. - -```sh -# Populate remote and deploy. -opennextjs-cloudflare deploy - -# Populate local and start dev server. -opennextjs-cloudflare preview -``` - -It is possible to only populate the cache without any other steps with the `populateCache` command. - -```sh -# The target is passed as an option, either `local` or `remote`. -opennextjs-cloudflare populateCache local -``` - - - diff --git a/pages/azion/former-releases/0.6/examples.mdx b/pages/azion/former-releases/0.6/examples.mdx deleted file mode 100644 index e44bee0..0000000 --- a/pages/azion/former-releases/0.6/examples.mdx +++ /dev/null @@ -1,24 +0,0 @@ -import { Callout } from "nextra/components"; - -## Examples - -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: - -``` -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -### Basic starter projects - -Basic example apps are included in the repository for `@opennextjs/cloudflare` package: - -- [_`create-next-app`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/create-next-app) — a Next.js project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). -- [_`api`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/api) — a minimal Next.js project with a single API route -- [_`middleware`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/middleware) — a minimal Next.js project using middleware -- [_`vercel-blog-starter`_](https://github.com/opennextjs/opennextjs-cloudflare/tree/main/examples/vercel-blog-starter) — a blog project using SSG - -You can use these to understand how to configure your Next.js app to use `@opennextjs/cloudflare`, or refer to [Get Started](/cloudflare/get-started). - -### Next.js Commerce Demo - -The [Next.js Commerce demo app](https://github.com/vercel/commerce/tree/v1) works with `@opennextjs/cloudflare`. You can view a deployed version of it [here](https://vercel-commerce-on-workers.web-experiments.workers.dev/). diff --git a/pages/azion/former-releases/0.6/get-started.mdx b/pages/azion/former-releases/0.6/get-started.mdx deleted file mode 100644 index 5b1e9b0..0000000 --- a/pages/azion/former-releases/0.6/get-started.mdx +++ /dev/null @@ -1,197 +0,0 @@ -import { Callout } from "nextra/components"; - -### Get Started - -#### New apps - -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: - -```sh -npm create cloudflare@latest -- my-next-app --framework=next --experimental -``` - -#### Existing Next.js apps - -##### 1. Install @opennextjs/cloudflare - -First, install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare): - -```sh -npm install --save-dev @opennextjs/cloudflare@latest -``` - -##### 2. Install Wrangler - -Install the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) as a devDependency: - -```sh -npm install --save-dev wrangler@latest -``` - - - You must use Wrangler version `3.99.0` or later to deploy Next.js apps using `@opennextjs/cloudflare`. - - -##### 3. Create a wrangler configuration file - - - This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if - not already present). - - -A [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) is needed for your -application to be previewed and deployed, it is also where you configure your Worker and define what resources it can access via [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings). - -You can create one yourself in the root directory of your Next.js app with the name `wrangler.jsonc` and the following content: - -```jsonc -{ - "$schema": "node_modules/wrangler/config-schema.json", - "main": ".open-next/worker.js", - "name": "my-app", - "compatibility_date": "2024-12-30", - "compatibility_flags": ["nodejs_compat"], - "assets": { - "directory": ".open-next/assets", - "binding": "ASSETS", - }, - "services": [ - { - "binding": "WORKER_SELF_REFERENCE", - // The service should match the "name" of your worker - "service": "my-app", - }, - ], - "kv_namespaces": [ - // Create a KV binding with the binding name "NEXT_INC_CACHE_KV" - // to enable the KV based caching: - // { - // "binding": "NEXT_INC_CACHE_KV", - // "id": "" - // } - ], -} -``` - - - As shown above: - You must enable the [`nodejs_compat` compatibility - flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) *and* set your [compatibility - date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) to `2024-09-23` or - later, in order for your Next.js app to work with @opennextjs/cloudflare - The `main` and `assets` values - should also not be changed unless you modify the build output result in some way - You can add a binding - named `NEXT_INC_CACHE_KV` to make use of Next.js' caching as described in the [Caching - docs](/cloudflare/caching) - - -##### 4. Add an `open-next.config.ts` file - - - This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if - not already present). - - -Add a [`open-next.config.ts`](https://opennext.js.org/aws/config) file to the root directory of your Next.js app: - -```ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import kvIncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache"; - -export default defineCloudflareConfig({ - incrementalCache: kvIncrementalCache, -}); -``` - - - To use the `OpenNextConfig` type as illustrated above (which is not necessary), you need to install the - `@opennextjs/aws` NPM package as a dev dependency. - - -##### 5. Add a `.dev.vars` file - -Then, add a [`.dev.vars`](https://developers.cloudflare.com/workers/testing/local-development/#local-only-environment-variables) file to the root directory of your Next.js app: - -```text -NEXTJS_ENV=development -``` - -The `NEXTJS_ENV` variable defines the environment to use when loading Next.js `.env` files. It defaults to "production" when not defined. - -##### 6. Update the `package.json` file - -Add the following to the scripts field of your `package.json` file: - -```json -"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", -"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy", -"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts", -``` - -- `npm run preview`: Builds your app and serves it locally, allowing you to quickly preview your app running locally in the Workers runtime, via a single command. -- `npm run deploy`: Builds your app, and then deploys it to Cloudflare -- `cf-typegen`: Generates a `cloudflare-env.d.ts` file at the root of your project containing [the types for the `env`](https://developers.cloudflare.com/workers/wrangler/commands/#types). - -##### 7. Add caching with Workers KV - -See the [Caching docs](/cloudflare/caching) for information on enabling Next.js caching in your OpenNext project. - -##### 8. Remove any `export const runtime = "edge";` if present - -Before deploying your app, remove the `export const runtime = "edge";` line from any of your source files. - -The edge runtime is not supported yet with `@opennextjs/cloudflare`. - -##### 9. Add `.open-next` to `.gitignore` - -You should add `.open-next` to your `.gitignore` file to prevent the build output from being committed to your repository. - -##### 10. Remove `@cloudflare/next-on-pages` (if necessary) - -If your Next.js app currently uses `@cloudflare/next-on-pages`, you'll want to remove it, and make a few changes. - -Uninstalling the [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) package as well as the [`eslint-plugin-next-on-pages`](https://www.npmjs.com/package/eslint-plugin-next-on-pages) package if present. - -Remove any reference of these packages from your source and configuration files. -This includes: - -- `setupDevPlatform()` calls in your Next.js config file -- `getRequestContext` imports from `@cloudflare/next-on-pages` from your source files - (those can be replaced with `getCloudflareContext` calls from `@opennextjs/cloudflare`) -- next-on-pages eslint rules set in your Eslint config file - -##### 11. Develop locally - -You can continue to run `next dev` when developing locally. - -Modify your Next.js configuration file to import and call the `initOpenNextCloudflareForDev` utility -from the `@opennextjs/cloudflare` package. This makes sure that the Next.js dev server can optimally integrate with the open-next cloudflare adapter and it is necessary for using bindings during local development. - -This is an example of a Next.js configuration file calling the utility: - -```ts -// next.config.ts -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ -}; - -export default nextConfig; - -import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; -initOpenNextCloudflareForDev(); -``` - -After having added the `initOpenNextCloudflareForDev()` call in your Next.js configuration file, you will be able, during local development, to access in any of your server code, local versions of Cloudflare bindings as indicated in the [bindings documentation](./bindings). - -In step 3, we also added the `npm run preview`, which allows you to quickly preview your app running locally in the Workers runtime, -rather than in Node.js. This allows you to test changes in the same runtime as your app will run in when deployed to Cloudflare. - -##### 12. Deploy to Cloudflare Workers - -Either deploy via the command line: - -```sh -npm run deploy -``` - -Or [connect a Github or Gitlab repository](https://developers.cloudflare.com/workers/ci-cd/), and Cloudflare will automatically build and deploy each pull request you merge to your production branch. diff --git a/pages/azion/former-releases/_meta.json b/pages/azion/former-releases/_meta.json deleted file mode 100644 index 1186ae9..0000000 --- a/pages/azion/former-releases/_meta.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "0.6": "Release 0.6", - "migrate-from-0.5-to-0.6": "", - "0.5": "Release 0.5", - "migrate-from-0.4-to-0.5": "", - "migrate-from-0.3-to-0.4": "", - "0.3": "Release 0.3", - "migrate-from-0.2-to-0.3": "", - "0.2": "Release 0.2" -} diff --git a/pages/azion/former-releases/migrate-from-0.2-to-0.3.mdx b/pages/azion/former-releases/migrate-from-0.2-to-0.3.mdx deleted file mode 100644 index e213c6f..0000000 --- a/pages/azion/former-releases/migrate-from-0.2-to-0.3.mdx +++ /dev/null @@ -1,82 +0,0 @@ -import { Callout } from "nextra/components"; - -### Migrate from 0.2 to 0.3 - -The `@opennextjs/cloudflare` adapter is now more closely intgrated with `@opennextjs/aws`. - -You will need to update your projects based on the 0.2 release as described in the following sections. - -##### 1. Update the `wrangler.toml` file - -The entry point is now `.open-next/worker.js`, update `wrangler.toml` accordingly: - -```toml -# CHANGED: new entry point location -main = ".open-next/worker.js" -name = "my-app" - -compatibility_date = "2024-09-23" -compatibility_flags = ["nodejs_compat"] - -# The binding name must be "ASSETS" when the cache is enabled -# CHANGED: output folder location -assets = { directory = ".open-next/assets", binding = "ASSETS" } -``` - -##### 2. Add a `open-next.config.ts` file - -Add a [`open-next.config.ts`](https://opennext.js.org/aws/config) file to the root directory of your Next.js app: - -```ts -import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js"; -import cache from "@opennextjs/cloudflare/kvCache"; - -const config: OpenNextConfig = { - default: { - override: { - wrapper: "cloudflare-node", - converter: "edge", - // Set `incrementalCache` to "dummy" to disable KV cache - incrementalCache: async () => cache, - tagCache: "dummy", - queue: "dummy", - }, - }, - - middleware: { - external: true, - override: { - wrapper: "cloudflare-edge", - converter: "edge", - proxyExternalRequest: "fetch", - }, - }, -}; - -export default config; -``` - -You can either install the `@opennextjs/aws` NPM package to get the types or `open-next.config.ts` to the [`exclude`](https://www.typescriptlang.org/tsconfig/#exclude) configuration key of your `tsconfig.json`. - -##### 3. Add a `.dev.vars` file - -Add a [`.dev.vars`](https://developers.cloudflare.com/workers/testing/local-development/#local-only-environment-variables) file to the root directory of your Next.js app: - -```text -NEXTJS_ENV=development -``` - -The `NEXTJS_ENV` variable defines the environment to use when loading Next.js `.env` files. It defaults to "production" when not defined. - -##### 4. Update `package.json` - -The name of the CLI was changed to `opennextjs-cloudflare`: - -```json -"build:worker": "opennextjs-cloudflare", -``` - -##### 5 Add `.open-next` to `.gitignore` - -You should change `.worker-next` to `.open-next` in your `.gitignore` file to prevent the build output from being committed to your repository. -You can safely delete the content of the now unused `.worker-next`. diff --git a/pages/azion/former-releases/migrate-from-0.3-to-0.4.mdx b/pages/azion/former-releases/migrate-from-0.3-to-0.4.mdx deleted file mode 100644 index 70fb364..0000000 --- a/pages/azion/former-releases/migrate-from-0.3-to-0.4.mdx +++ /dev/null @@ -1,38 +0,0 @@ -import { Callout } from "nextra/components"; - -### Migrate from 0.3 to 0.4 - -`@opennextjs/cloudflare@0.4.0` introduced a new `initOpenNextCloudflareForDev` utility and made `getCloudflareContext` synchronous, -we'll explore those two differences below, and how they effect applications built using `0.3.x` versions of the adapter. - -##### `initOpenNextCloudflareForDev` - -`initOpenNextCloudflareForDev` is a new utility that needs to be added to the Next.js configuration file in order to integrate the adapter -with the Next.js dev server. If you don't plan on using the `next dev` command you can skip this section, otherwise update your Next.js -configuration file to import and call the utility. - -Example: - -```js -// next.config.mjs - -import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; - -initOpenNextCloudflareForDev(); - -/** @type {import('next').NextConfig} */ -const nextConfig = {}; - -export default nextConfig; -``` - -##### Synchronous `getCloudflareContext` synchronous - -`getCloudflareContext` is now synchronous. -a promise that resolves to it. - -This means that if you had code that `await`ed `getCloudflareContext()` calls, such `await`s are no longer necessary and -can be removed. - -If your application is instead combining the result of `getCloudflareContext()` with on Ecmascript Promises APIs such as -`then`, `catch` and `finally` those need to be removed since the function's result, as mentioned is no longer a promise. diff --git a/pages/azion/former-releases/migrate-from-0.4-to-0.5.mdx b/pages/azion/former-releases/migrate-from-0.4-to-0.5.mdx deleted file mode 100644 index 26d371d..0000000 --- a/pages/azion/former-releases/migrate-from-0.4-to-0.5.mdx +++ /dev/null @@ -1,3 +0,0 @@ -### Migrate from 0.4 (to 0.5) - -`@opennextjs/cloudflare@0.5.0` did not introduce any breaking change, meaning that there are no migration steps from `0.4` to `0.5`. diff --git a/pages/azion/former-releases/migrate-from-0.5-to-0.6.mdx b/pages/azion/former-releases/migrate-from-0.5-to-0.6.mdx deleted file mode 100644 index be0285f..0000000 --- a/pages/azion/former-releases/migrate-from-0.5-to-0.6.mdx +++ /dev/null @@ -1,47 +0,0 @@ -import { Callout } from "nextra/components"; - -### Migrate from 0.5 to 0.6 - -`@opennextjs/cloudflare@0.6.0` introduces more options for caching. - -The codebase has been refactored with some breaking changes, read-on to update your apps: - -##### Overrides location - -The cloudflare overrides now live in `@opennextjs/cloudflare/overrides`: - -- Incremental cache overrides: `@opennextjs/cloudflare/overrides/incremental-cache/...` -- Tag cache overrides: `@opennextjs/cloudflare/overrides/tag-cache/...` -- Queue overrides: `@opennextjs/cloudflare/overrides/queue/...` - -For example the KV incremental cache override can be now imported as `@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache`. - -##### Environment variables and bindings - -We have updated the name for consistency: - -- `NEXT_CACHE_WORKERS_KV` -> `NEXT_INC_CACHE_KV` -- `NEXT_CACHE_R2_...` -> `NEXT_INC_CACHE_R2_...` -- `NEXT_CACHE_D1` -> `NEXT_TAG_CACHE_D1` -- `NEXT_CACHE_DO_...` -> `NEXT_TAG_CACHE_DO_...` -- `NEXT_CACHE_DO_REVALIDATION` -> `NEXT_CACHE_DO_QUEUE` -- `NEXT_CACHE_REVALIDATION_WORKER` -> `WORKER_SELF_REFERENCE` - -`NEXT_CACHE_D1_TAGS_TABLE` and `NEXT_CACHE_D1_REVALIDATIONS_TABLE` have been dropped. -The D1 tables have a fixed names `tags` and `revalidations`. - -##### CLI - -The CLI was previsouly invoked as `opennextjs-cloudflare` and now takes a mandatory command: - -- `opennextjs-cloudflare build` is the equivalent of executing `opennextjs-cloudflare` previously -- `opennextjs-cloudflare populateCache local` is used to populate the `local` or `remote` cache -- `opennextjs-cloudflare preview` is used to populate the local cache and start a dev server -- `opennextjs-cloudflare deploy` is used to populate the remote cache and deploy - -You will need to update the `scripts` in your `package.json`: - -```json -"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", -"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy", -``` diff --git a/pages/azion/how-it-works.mdx b/pages/azion/how-it-works.mdx new file mode 100644 index 0000000..a601b03 --- /dev/null +++ b/pages/azion/how-it-works.mdx @@ -0,0 +1,9 @@ +import { Callout } from "nextra/components"; + +# How it Works + +The OpenNext Azion adapter works by taking the Next.js build output and transforming it, so that it can run in Azion Edge Functions. + +When you add [@aziontech/opennextjs-azion](https://www.npmjs.com/package/@aziontech/opennextjs-azion) as a dependency to your Next.js app, and then run `npx opennextjs-azion` the adapter first builds your app by running the `build` script in your `package.json`, and then transforms the build output to a format that you can run locally using [Azion CLI](https://www.azion.com/en/documentation/products/cli/), and deploy to Azion. + +You can view the code for `@aziontech/opennextjs-azion` [here](https://github.com/aziontech/opennextjs-azion/tree/main/packages/azion/src) to understand what it does under the hood. \ No newline at end of file diff --git a/pages/azion/howtos/_meta.json b/pages/azion/howtos/_meta.json deleted file mode 100644 index c8fe30e..0000000 --- a/pages/azion/howtos/_meta.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "stripeAPI": "Stripe API", - "db": "Database & ORM", - "dev-deploy": "Develop and Deploy", - "env-vars": "Environment Variables", - "image": "Image Optimization", - "custom-worker": "Custom Worker", - "keep_names": "__name issues", - "workerd": "workerd specific packages" -} diff --git a/pages/azion/howtos/custom-worker.mdx b/pages/azion/howtos/custom-worker.mdx deleted file mode 100644 index cab9ea9..0000000 --- a/pages/azion/howtos/custom-worker.mdx +++ /dev/null @@ -1,43 +0,0 @@ -## Custom Worker - -The worker generated by the Cloudflare adapter only exports [a fetch handler](https://developers.cloudflare.com/workers/runtime-apis/handlers/fetch/). - -Sometimes your application needs to expose another type of handler (i.e. [a scheduled handler](https://developers.cloudflare.com/workers/runtime-apis/handlers/scheduled/)) or export a [Durable Object](https://developers.cloudflare.com/durable-objects/api/base/). This can be achieved by creating a custom worker. - -The custom worker re-uses the generated fetch handler. - -### Create your custom worker Worker - -The following custom worker re-uses the generated fetch handler and adds a scheduled handler: - -```ts -// custom-worker.ts - -// @ts-ignore `.open-next/worker.ts` is generated at build time -import { default as handler } from "./.open-next/worker.js"; - -export default { - fetch: handler.fetch, - - async scheduled(event) { - // ... - }, -} satisfies ExportedHandler; - -// The re-export is only required if your app uses the DO Queue and DO Tag Cache -// See https://opennext.js.org/cloudflare/caching for details -// @ts-ignore `.open-next/worker.ts` is generated at build time -export { DOQueueHandler, DOShardedTagCache } from "./.open-next/worker.js"; -``` - -See [an example in the adapter repository](https://github.com/opennextjs/opennextjs-cloudflare/blob/main/examples/playground14/worker.ts). - -### Update the entry point in your wrangler configuration - -```diff -// wrangler.jsonc -{ -- "main": "./.open-next/worker.js" -+ "main": "./path/to/custom-worker.ts", -} -``` diff --git a/pages/azion/howtos/db.mdx b/pages/azion/howtos/db.mdx deleted file mode 100644 index 2b9d0cd..0000000 --- a/pages/azion/howtos/db.mdx +++ /dev/null @@ -1,229 +0,0 @@ -This page will show you how to setup some popular database ORM libraries to use in OpenNext. There are some subtleties to be aware of when using these libraries in Cloudflare Workers, so we will cover those here. - -If you encounter issue with a specific library, please open an issue on the [OpenNext GitHub repository](https://github.com/opennextjs/opennextjs-cloudflare/issues). - -## Drizzle ORM - -[Drizzle](https://developers.cloudflare.com/d1/reference/community-projects/#drizzle-orm) is a TypeScript ORM for SQL databases. It is designed to be lightweight and easy to use, making it a great choice for Cloudflare Workers. -There is not much specific to configure in Drizzle, but there is one important thing to note is that you don't want to have a global client. - -### `lib/db.ts` - -Instead of creating a global client, you should create a new client for each request. This is because some adapters (like Postgres) will use a connection pool, and reuse the same connection for multiple requests. This is not allowed in Cloudflare Workers, and will cause subsequent requests to fail. - -#### PostgreSQL - -Instead of that : - -```ts -//lib/db.ts -import { drizzle } from "drizzle-orm/node-postgres"; -import * as schema from "./schema/pg"; -import { Pool } from "pg"; - -const pool = new Pool({ - connectionString: process.env.PG_URL, -}); - -export const db = drizzle({ client: pool, schema }); -``` - -You should do this instead: - -```ts -//lib/db.ts -import { drizzle } from "drizzle-orm/node-postgres"; -// You can use cache from react to cache the client during the same request -// this is not mandatory and only has an effect for server components -import { cache } from "react"; -import * as schema from "./schema/pg"; -import { Pool } from "pg"; - -export const getDb = cache(() => { - const pool = new Pool({ - connectionString: process.env.PG_URL, - // You don't want to reuse the same connection for multiple requests - maxUses: 1, - }); - return drizzle({ client: pool, schema }); -}); -``` - -#### D1 example - -```ts -import { getCloudflareContext } from "@opennextjs/cloudflare"; -import { drizzle } from "drizzle-orm/d1"; -import { cache } from "react"; -import * as schema from "./schema/d1"; - -export const getDb = cache(() => { - const { env } = getCloudflareContext(); - return drizzle(env.MY_D1, { schema }); -}); - -// This is the one to use for static routes (i.e. ISR/SSG) -export const getDbAsync = cache(async () => { - const { env } = await getCloudflareContext({ async: true }); - return drizzle(env.MY_D1, { schema }); -}); -``` - -#### Hyperdrive example - -```ts -import { getCloudflareContext } from "@opennextjs/cloudflare"; -import { drizzle } from "drizzle-orm/node-postgres"; -import { cache } from "react"; -import * as schema from "./schema/pg"; -import { Pool } from "pg"; - -export const getDb = cache(() => { - const { env } = getCloudflareContext(); - const connectionString = env.HYPERDRIVE.connectionString; - const pool = new Pool({ - connectionString: process.env.PG_URL, - // You don't want to reuse the same connection for multiple requests - maxUses: 1, - }); - return drizzle({ client: pool, schema }); -}); - -// This is the one to use for static routes (i.e. ISR/SSG) -export const getDbAsync = cache(async () => { - const { env } = await getCloudflareContext({ async: true }); - const connectionString = env.HYPERDRIVE.connectionString; - const pool = new Pool({ - connectionString: process.env.PG_URL, - // You don't want to reuse the same connection for multiple requests - maxUses: 1, - }); - return drizzle({ client: pool, schema }); -}); -``` - -You can then use the `getDb` function to get a new client for each request. This will ensure that you don't run into any issues with connection pooling. - -## Prisma ORM - -[Prisma](https://developers.cloudflare.com/d1/reference/community-projects/#prisma-orm) is a popular ORM for Node.js and TypeScript. It is designed to be easy to use and provides a lot of features out of the box. However, there are some subtleties to be aware of when using Prisma in Cloudflare Workers. - -### `schema.prisma` - -When using prisma in OpenNext, you do not want to provide an output directory for the generated client. - -```prisma -generator client { - provider = "prisma-client-js" - previewFeatures = ["driverAdapters"] -} -``` - -This is because the generated client needs to be patched by OpenNext to work with Cloudflare Workers. If you provide an output directory, OpenNext will not be able to patch the client and it will not work. - -### `next.config.ts` - -Because prisma has some specific exports for cloudflare workers, you need to add the following to your `next.config.ts` file: - -```ts -const nextConfig: NextConfig = { - serverExternalPackages: ["@prisma/client", ".prisma/client"], -}; -``` - -By doing this, this will ensure that both the generated client and the prisma client are included in the build for the `workerd` runtime. - -### `lib/db.ts` - -Instead of creating a global client, you should create a new client for each request. This is because some adapters (like Postgres) will use a connection pool, and reuse the same connection for multiple requests. This is not allowed in Cloudflare Workers, and will cause subsequent requests to fail. - -#### D1 example - -Instead of that : - -```ts -//lib/db.ts -import { getCloudflareContext } from "@opennextjs/cloudflare"; -import { PrismaClient } from "@prisma/client"; -import { PrismaD1 } from "@prisma/adapter-d1"; - -const { env } = getCloudflareContext(); -const adapter = new PrismaD1(env.MY_D1); -export const db = new PrismaClient(); -``` - -You should do this instead: - -```ts -//lib/db.ts -import { getCloudflareContext } from "@opennextjs/cloudflare"; -// You can use cache from react to cache the client during the same request -// this is not mandatory and only has an effect for server components -import { cache } from "react"; -import { PrismaClient } from "@prisma/client"; -import { PrismaD1 } from "@prisma/adapter-d1"; - -export const getDb = cache(() => { - const { env } = getCloudflareContext(); - const adapter = new PrismaD1(env.MY_D1); - return new PrismaClient({ adapter }); -}); - -// If you need access to `getCloudflareContext` in a static route (i.e. ISR/SSG), you should use the async version of `getCloudflareContext` to get the context. -export const getDbAsync = async () => { - const { env } = await getCloudflareContext({ async: true }); - const adapter = new PrismaD1(env.MY_D1); - const prisma = new PrismaClient({ adapter }); - return prisma; -}; -``` - -You can then use the `getDb` function to get a new client for each request. This will ensure that you don't run into any issues with connection pooling. - -#### PostgreSQL - -You can also use Prisma with PostgreSQL. The setup is similar to the D1 setup above, but you need to use the `PrismaPostgres` adapter instead of the `PrismaD1` adapter. - -```ts -import { cache } from "react"; -import { PrismaClient } from "@prisma/client"; -import { PrismaPg } from "@prisma/adapter-pg"; - -export const getDb = cache(() => { - const connectionString = process.env.PG_URL ?? ""; - const adapter = new PrismaPg({ connectionString, maxUses: 1 }); - const prisma = new PrismaClient({ adapter }); - return prisma; -}); -``` - -You can then use the `getDb` function to get a new client for each request. This will ensure that you don't run into any issues with connection pooling. - -#### Hyperdrive - -You can also use Prisma with Hyperdrive. The setup is similar to the PostgreSQL setup above. - -```ts -//lib/db.ts -import { getCloudflareContext } from "@opennextjs/cloudflare"; -// You can use cache from react to cache the client during the same request -// this is not mandatory and only has an effect for server components -import { cache } from "react"; -import { PrismaClient } from "@prisma/client"; -import { PrismaPg } from "@prisma/adapter-pg"; - -export const getDb = cache(() => { - const { env } = getCloudflareContext(); - const connectionString = env.HYPERDRIVE.connectionString; - const adapter = new PrismaPg({ connectionString, maxUses: 1 }); - return new PrismaClient({ adapter }); -}); - -// This is the one to use for static routes (i.e. ISR/SSG) -export const getDbAsync = async () => { - const { env } = await getCloudflareContext({ async: true }); - const connectionString = env.HYPERDRIVE.connectionString; - const adapter = new PrismaPg({ connectionString, maxUses: 1 }); - return new PrismaClient({ adapter }); -}; -``` diff --git a/pages/azion/howtos/dev-deploy.mdx b/pages/azion/howtos/dev-deploy.mdx deleted file mode 100644 index d7ea61d..0000000 --- a/pages/azion/howtos/dev-deploy.mdx +++ /dev/null @@ -1,88 +0,0 @@ -import { Callout } from "nextra/components"; - -## Develop and deploy - -### Development workflow - -The primary purpose of `@opennextjs/cloudflare` is to take a Next.js application, built with standard Next.js tooling, and convert it into a format compatible with Cloudflare Workers. - -This code transformation process takes some time, making the adapter less than ideal for active application development, where a very fast feedback loop and other quality-of-life features, such as Hot Module Replacement (HMR), are crucial. Fortunately, Vercel already provides excellent tooling for this workflow, which Next.js developers are likely already familiar with. - -We recommend that developers continue using the tools they are already comfortable with for local development and then use `@opennextjs/cloudflare` when they are ready to deploy their applications to the Cloudflare platform. - -Let's explore, in more detail, the application development workflow we recommend for the best developer experience. - -#### Create a new application based on a template - -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: - -```bash -npm create cloudflare@latest -- my-next-app --framework=next --platform=workers -``` - -#### Develop locally using `next dev` - -We believe that the best development workflow uses the `next dev` command provided by Next.js. - -To access Cloudflare resources using the `getCloudflareContext` API while running `next dev`, you will need to update the Next.js configuration to call `initOpenNextCloudflareForDev`, as shown in the following example: - -```ts -// next.config.ts -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - /* config options here */ -}; - -export default nextConfig; - -import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; -initOpenNextCloudflareForDev(); -``` - -#### Use `opennextjs-cloudflare` to build and test on the Workers runtime - -After you've finished iterating on your Next.js application with `next dev`, you can convert it to a Cloudflare Worker by running the `opennextjs-cloudflare build` command. This will generate the Worker code in the `.open-next` directory. - -You can then preview the app locally in the Cloudflare Workers runtime. - -To preview your worker locally, run the `opennextjs-cloudflare preview` command. This will populate the cache and create a local server that runs your worker in the Cloudflare Workers runtime. Testing your worker is important to ensure that it has been properly built and is working as expected. - -### Deploy your application to Cloudflare Workers - -Both the `deploy` and `upload` commands of `opennextjs-cloudflare` can be used to deploy your application to cloudflare Workers. Both commands will initialize the remote cache and upload your application to the Cloudflare infrastructure. - -While `deploy` will start serving your application as soon as it is uploaded, `upload` only creates a new version of the application so that you can use [gradual deployments](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/gradual-deployments/). - -#### Local build - -Use the `build` command followed by either `deploy` or `upload` to deploy your local build. - - - When running the `build` command locally, `.dev.vars` and [Next `.env` - files](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables) might - override your configuration. It is preferreable to use a CD system as [Workers - Builds](https://developers.cloudflare.com/workers/ci-cd/builds/) to deploy your application for reproducible - deployments. - - -#### Workers Builds - -When using Workers Builds, make sure to setup your environment variables as explained in [this guide](https://opennext.js.org/cloudflare/howtos/env-vars#production). - -You can then connect your GitHub repository by following [the documentation](https://developers.cloudflare.com/workers/ci-cd/builds/git-integration/). - -In the Build settings: - -- The "Build command" should be set to `npx opennextjs-cloudflare build`. -- The "Deploy command" should be set to `npx opennextjs-cloudflare deploy` (or `upload` to use gradual deployments). - - - The `deploy`, `upload`, and `populateCache` commands of `opennextjs-cloudflare` invoke `wrangler`. - You can pass arguments to `wrangler` by specifying them after `--`: - -```sh -opennextjs-cloudflare deploy -- --env=prod -``` - - diff --git a/pages/azion/howtos/env-vars.mdx b/pages/azion/howtos/env-vars.mdx deleted file mode 100644 index 9ec2b04..0000000 --- a/pages/azion/howtos/env-vars.mdx +++ /dev/null @@ -1,44 +0,0 @@ -import { Callout } from "nextra/components"; - -## Environment variables - -This entry describe the most sensible way to handle your environment variables which works well both during local development and once your application is deployed to Cloudflare Workers. - -On the Cloudflare platform, your environment variables can be stored in either ["Enviroment variables"](https://developers.cloudflare.com/workers/configuration/environment-variables/) or ["Secrets"](https://developers.cloudflare.com/workers/configuration/secrets/). The difference being that Secrets can not be read back from either the dashboard or the CLI after being created. - -### Local development - -While there are multiple ways to set environment variables for local development on the Cloudflare platform (adding them to to your [wrangler configuration](https://developers.cloudflare.com/workers/configuration/secrets/) or to a [.dev.vars](https://developers.cloudflare.com/workers/configuration/secrets/) file) that does not play well with the recommended development workflow as they would not be available while using `next dev`. - -What you should do instead is to use the Next.js [`.env` files](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables). By doing so the environment variables will be available on `process.env` both while running `next dev` and when running your app locally on a Worker with `wrangler dev`. - -Next.js `.env` files are environment specific. That is a `.env.development` will take precedence over a `.env` file when you use the "development" environment. See the Next.js site for a detailed explanation of the [loading order](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables). - -You should use the `NEXTJS_ENV` environment variable to select the environment to use when running your app locally on a worker, that's how you would select the "development" environment: - -```plain -# .dev.vars -NEXTJS_ENV=development -``` - -The "production" environment is used by default when `NEXTJS_ENV` is not explicitly set. - -### Production - -`.env` and `.dev.vars` are local files that should not be added to source control. You should instead use the Cloudflare dashboard to set your environment variables for production. - -#### Workers Builds - -When you use [Workers Builds](https://developers.cloudflare.com/workers/ci-cd/builds/) to deploy your application, the environment variables must be set in the ["Build variables and secrets"](https://developers.cloudflare.com/workers/ci-cd/builds/configuration/). - -By settings the "Build variables and secrets", the Next build executed by Workers Builds will have access to the environment variables. It needs that access to inline the [`NEXT_PUBLIC_...` variables](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser) and access non-`NEXT_PUBLIC_...` variables needed for SSG pages. - -#### Runtime variables - -Your Next application needs to access environment variables at runtime. You should always set the runtime environment variables [in the Cloudflare dashboard](https://developers.cloudflare.com/workers/configuration/environment-variables/#add-environment-variables-via-the-dashboard) - - - If you set environment variables from the dashboard, you can use the - [`--keep-vars`](https://developers.cloudflare.com/workers/wrangler/commands/#deploy) option of wrangler to - prevent them from being deleted by deployments, i.e. `opennextjs-cloudflare deploy -- --keep-vars` - diff --git a/pages/azion/howtos/image.mdx b/pages/azion/howtos/image.mdx deleted file mode 100644 index 64307e1..0000000 --- a/pages/azion/howtos/image.mdx +++ /dev/null @@ -1,71 +0,0 @@ -import { Callout } from "nextra/components"; - -## Image Optimization - -Next.js has a builtin [`` component](https://nextjs.org/docs/pages/building-your-application/optimizing/images) to automatically optimize your images for faster page loads. - -In this post, we will look at how to integrate the Next.js image optimization with [Cloudflare Images](https://developers.cloudflare.com/images) - -### Enable Cloudflare Images - -You first need to [enable Cloudflare Images](https://developers.cloudflare.com/images/get-started/#enable-transformations-on-your-zone) for your zone. - -It is strongly advised to restrict the image origins that can be transformed to where your images are hosted, i.e. a [R2 bucket](https://developers.cloudflare.com/r2/buckets/). - -### Use a custom loader - -You then need to configure your Next application to use a custom loader for Cloudflare Images. - -Create an `image-loader.ts` at the root of your application: - -```ts -// image-loader.ts -import type { ImageLoaderProps } from "next/image"; - -const normalizeSrc = (src: string) => { - return src.startsWith("/") ? src.slice(1) : src; -}; - -export default function cloudflareLoader({ src, width, quality }: ImageLoaderProps) { - if (process.env.NODE_ENV === "development") { - // Serve the original image when using `next dev` - return src; - } - const params = [`width=${width}`]; - if (quality) { - params.push(`quality=${quality}`); - } - const paramsString = params.join(","); - return `/cdn-cgi/image/${paramsString}/${normalizeSrc(src)}`; -} -``` - - - This simple loader does not respect [Next.js - remotePatterns](https://nextjs.org/docs/pages/api-reference/components/image#remotepatterns). You should - configure [the allowed source origins](https://developers.cloudflare.com/images/transform-images/sources/) - in the dashboard. - - -You will then need to update your app configuration to use this loader: - -```ts -// next.config.ts -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - // ... - images: { - loader: "custom", - loaderFile: "./image-loader.ts", - }, -}; - -export default nextConfig; -``` - - - Images using the cloudflare loader are served directly without going through the middleware. - - -See more details in the [Cloudflare Images documentation](https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/). diff --git a/pages/azion/howtos/keep_names.mdx b/pages/azion/howtos/keep_names.mdx deleted file mode 100644 index acd3014..0000000 --- a/pages/azion/howtos/keep_names.mdx +++ /dev/null @@ -1,38 +0,0 @@ -import { Callout } from "nextra/components"; - -## `__name` issues - -When using the OpenNext adapter, Wrangler processes the worker's code with [esbuild](https://esbuild.github.io/), and by default -enables the [`keep-names`](https://esbuild.github.io/api/#keep-names) option. While this is generally useful for debugging, it can -cause issues with certain Next.js libraries (e.g. [next-themes](https://www.npmjs.com/package/next-themes)) that convert scripts -to strings. This happens because `esbuild` introduces a `__name` function at the top of modules, which may inadvertently appear -in the generated script strings. When these strings are evaluated at runtime, the `__name` function might therefore not be defined, -leading to errors logged in the developper console: - -``` -Uncaught ReferenceError: __name is not defined -``` - - - Note that depending on your minification settings, the `__name` identifier might be minified, making the - error message less clear and potentially not explicitly mentioning `__name` in such cases. - - -### How to fix such issues - -To fix the issue you can simply set the `keep_names` option to `false` in your [`wrangler.jsonc` file](https://developers.cloudflare.com/workers/wrangler/configuration), like in the following example: - -```jsonc -{ - "$schema": "node_modules/wrangler/config-schema.json", - "main": ".open-next/worker.js", - "name": "my-app", - "keep_names": false, - /* ... */ -} -``` - -One potential drawback of this solution is that, depending on your minification settings, you may lose the ability to view the original -function names in debugging tools. - -You must use Wrangler version `4.13.0` or later to use this option. diff --git a/pages/azion/howtos/stripeAPI.mdx b/pages/azion/howtos/stripeAPI.mdx deleted file mode 100644 index 4c024b3..0000000 --- a/pages/azion/howtos/stripeAPI.mdx +++ /dev/null @@ -1,14 +0,0 @@ -## [Stripe API](https://www.npmjs.com/package/stripe) - -When [creating a Stripe object](https://docs.stripe.com/js/initializing), the default http client implementation is based on `node:https` which is not implemented on Workers. - -However you can use an http client based on fetch ([`FetchHttpClient`](https://github.com/stripe/stripe-node/blob/54d423e5d1118dc35c4b76260889826003e00e9f/src/net/FetchHttpClient.ts)) via the `httpClient` option: - -```ts -import Stripe from "stripe"; - -const stripe = Stripe(STRIPE_API_KEY, { - // Cloudflare Workers use the Fetch API for their API requests. - httpClient: Stripe.createFetchHttpClient(), -}); -``` diff --git a/pages/azion/howtos/workerd.mdx b/pages/azion/howtos/workerd.mdx deleted file mode 100644 index 2694c62..0000000 --- a/pages/azion/howtos/workerd.mdx +++ /dev/null @@ -1,49 +0,0 @@ -## `workerd` specific code - -### Configuration - -[`workerd`](https://github.com/cloudflare/workerd) is the runtime cloudflare uses to run Workers code. - -While the [`nodejs_compat`](https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag) flag makes `workerd` mostly compatible with Node.js, -there are still minor differences. Some packages publish code for different runtimes to account for those differences. For example, `postgres` has [conditional exports](https://nodejs.org/api/packages.html#conditional-exports) in its [`package.json`](https://github.com/porsager/postgres/blob/ad0ed4476e09f41f147859cb5a42971d2b99e9c7/package.json#L8-L13): - -```json -"exports": { - "types": "./types/index.d.ts", - "bun": "./src/index.js", - "workerd": "./cf/src/index.js", - "import": "./src/index.js", - "default": "./cjs/src/index.js" -}, -``` - -With such exports, Node.js applications use either `src/index.js` or `cjs/src/index.js` depending if the app use ESM or CJS. - -However we want to use the `workerd` specific entrypoint when using the Cloudflare adapter. -For that, you need to instruct Next.js not to bundle packages as it would use the node conditions by default. - -To do that, add those packages in the `serverExternalPackages` key of your `next.config.ts`: - -```ts -// node.config.ts -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - serverExternalPackages: ["@prisma/client", ".prisma/client", "postgres"], -}; - -import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; -initOpenNextCloudflareForDev(); - -export default nextConfig; -``` - -### Packages known to have `workerd` specific code - -- `@libsql/isomorphic-ws` -- `@prisma/client` (and the generated `.prisma/client`) -- `jose` -- `postgres` -- `react-textarea-autosize` - -Please report an issue on [the adapter GH repository](https://github.com/opennextjs/opennextjs-cloudflare/issues) to have packages added to this list. diff --git a/pages/azion/index.mdx b/pages/azion/index.mdx index 5329edf..9e55e38 100644 --- a/pages/azion/index.mdx +++ b/pages/azion/index.mdx @@ -3,27 +3,21 @@ import WindowsSupport from "../../shared/WindowsSupport.mdx"; ## Azion -The [`@opennextjs/azion`](https://www.npmjs.com/package/@opennextjs/azion) adapter lets you deploy Next.js apps to [Azion Edge Functions](https://www.azion.com/en/products/edge-functions/) using the [Node.js "runtime" from Next.js](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). +The [`@aziontech/opennextjs-azion`](https://www.npmjs.com/package/@aziontech/opennextjs-azion) adapter lets you deploy Next.js apps to [Azion Edge Functions](https://www.azion.com/en/products/edge-functions/) using the [Node.js "runtime" from Next.js](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). ### Get Started ##### New apps -To create a new Next.js app, pre-configured to run on Azion using `@opennextjs/azion`, run: +To create a new Next.js app, pre-configured to run on Azion using `@aziontech/opennextjs-azion`, run: ``` -npm create azion@latest -- my-next-app --framework=next --platform=edge-functions +npx create-next-app@14.2.4 my-next-app --use-npm && cd my-next-app && npm install @aziontech/opennextjs-azion@latest ``` ##### Existing Next.js apps -Follow the guide [here](/azion/get-started) to use [@opennextjs/azion](https://www.npmjs.com/package/@opennextjs/azion) with an existing Next.js app. - -### Supported Next.js runtimes - -Next.js has [two "runtimes"](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes) — "Edge" and "Node.js". When you use `@opennextjs/azion`, your app should use the Node.js runtime, which is more fully featured, and allows you to use the [Node.js APIs](https://www.azion.com/en/documentation/products/edge-functions/runtime/) that are provided by the Azion Edge Functions runtime. - -This is an important difference from `@cloudflare/next-on-pages`, which only supports the "Edge" runtime. The Edge Runtime code in Next.js [intentionally constrains which APIs from Node.js can be used](https://github.com/vercel/next.js/blob/canary/packages/next/src/build/webpack/plugins/middleware-plugin.ts#L820), and the "Edge" runtime does not support all Next.js features. +Follow the guide [here](/azion/get-started) to use [@aziontech/opennextjs-azion](https://www.npmjs.com/package/@aziontech/opennextjs-azion) with an existing Next.js app. ### Supported Next.js versions @@ -39,8 +33,8 @@ To help improve compatibility, we encourage you to [report bugs](https://github. - [x] [Static Site Generation (SSG)](https://nextjs.org/docs/app/building-your-application/rendering/server-components#static-rendering-default) - [x] [Server-Side Rendering (SSR)](https://nextjs.org/docs/app/building-your-application/rendering/server-components) - [x] [Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware) -- [ ] [Node Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware#runtime) introduced in 15.2 are not yet supported -- [ ] [Image optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) (See [this guide](/azion/howtos/image) to configure [Azion Image Processor](https://www.azion.com/en/products/image-processor/)) +- [x] [Node Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware#runtime) introduced in 15.2 are not yet supported +- [ ] [Image optimization](https://nextjs.org/docs/app/building-your-application/optimizing/images) - [x] [Partial Prerendering (PPR)](https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering) - [x] [Pages Router](https://nextjs.org/docs/pages) - [x] [Incremental Static Regeneration (ISR)](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration) @@ -53,20 +47,7 @@ We welcome both contributions and feedback! -### How `@opennextjs/azion` Works - -The OpenNext Azion adapter works by taking the Next.js build output and transforming it, so that it can run in Azion Edge Functions. - -When you add [@opennextjs/azion](https://www.npmjs.com/package/@opennextjs/azion) as a dependency to your Next.js app, and then run `npx opennextjs-azion` the adapter first builds your app by running the `build` script in your `package.json`, and then transforms the build output to a format that you can run locally using [Azion CLI](https://www.azion.com/en/documentation/products/cli/), and deploy to Azion. - -You can view the code for `@opennextjs/azion` [here](https://github.com/aziontech/opennextjs-azion/tree/main/packages/azion/src) to understand what it does under the hood. - ### Note on Edge Function Size Limits -The size limit of an Azion Edge Function is 50 MiB. After building your function, the CLI will show both the original and compressed sizes: - -``` -Total Upload: 13833.20 KiB / gzip: 2295.89 KiB -``` - -Only the latter (compressed size) matters for [the Edge Function size limit](https://www.azion.com/en/documentation/products/edge-functions/limits/). +Azion Edge Functions have a maximum size limit of 20 MiB per function. After building your project, pay attention to the compressed (gzipped) size of your deployment package—only the compressed size counts toward this limit. +For more details, see the [official Azion documentation on Edge Function size limits](https://www.azion.com/en/documentation/products/build/edge-application/edge-functions/#limits). \ No newline at end of file From e3bb782aaebcc48796d9cf5ce4cc226697706d2d Mon Sep 17 00:00:00 2001 From: Vitor-Azion Date: Thu, 7 Aug 2025 09:10:52 -0300 Subject: [PATCH 3/3] feat: Updated Azion integration documentation, including get-started, caching mechanisms and local development guidelines. --- pages/azion/_meta.json | 1 - pages/azion/caching.mdx | 166 ++++++++------- pages/azion/get-started.mdx | 349 +++++++++++++++++--------------- pages/azion/how-it-works.mdx | 9 - pages/azion/index.mdx | 2 +- pages/azion/known-issues.mdx | 52 +++-- pages/azion/troubleshooting.mdx | 195 ++++++++++-------- pages/index.mdx | 1 + 8 files changed, 408 insertions(+), 367 deletions(-) delete mode 100644 pages/azion/how-it-works.mdx diff --git a/pages/azion/_meta.json b/pages/azion/_meta.json index 953da8c..8ef1ae4 100644 --- a/pages/azion/_meta.json +++ b/pages/azion/_meta.json @@ -1,6 +1,5 @@ { "index": "Overview", - "how-it-works": "How it Works", "get-started": "Get Started", "caching": "Caching", "examples": "Examples", diff --git a/pages/azion/caching.mdx b/pages/azion/caching.mdx index 4b556c2..1252d9e 100644 --- a/pages/azion/caching.mdx +++ b/pages/azion/caching.mdx @@ -1,27 +1,27 @@ import { Callout } from "nextra/components"; import { Tabs } from "nextra/components"; -## Caching +## **Caching** -Next.js offers multiple ways to improve an application's performance by [caching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#caching-data) routes and network requests. An application will try to pre-render and cache as much data as possible during build-time to reduce the amount of work required when serving a response to a user. +Next.js offers multiple ways to improve an application's performance by caching routes and network requests. An Azion-deployed Next.js app will pre-render and cache as much data as possible at build time to minimize work when serving user requests. -The cache data are updated using revalidation, either peridiocally or on-demand: +Cache data is updated via **revalidation**, either periodically or on-demand: -- "[Time-based revalidation](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration#time-based-revalidation)" updates the cache data after the revalidation delay specified by the applications expires -- "[On-demand revalidation](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration#on-demand-revalidation-with-revalidatetag)" allows to invalid cache entries with a specific tag (via `revalidateTag`) or at a given path (via `revalidatePath`). You can also use `res.revalidate` in Pages router API route. +* **Time-based revalidation:** After a specified interval (set via `revalidate`), stale data is served once and then updated in the background. This behavior is similar to *stale-while-revalidate*, providing fresh content on subsequent requests. -The `@opennextjs/cloudflare` caching supports rely on 3 components: +* **On-demand revalidation:** Specific cached entries are invalidated immediately via functions like [`revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag) or [`revalidatePath`](https://nextjs.org/docs/app/api-reference/functions/revalidatePath). This ensures the latest data is shown as soon as possible by purging the stale cache entry so that the next request fetches fresh data. In the Pages Router, you can also trigger revalidation with `res.revalidate()`. -- An **Incremental Cache** to store the cache data -- A **Queue** to synchronize and deduplicate time-based revalidations -- A **Tag Cache** for On-demand revalidations via [`revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag) and [`revalidatePath`](https://nextjs.org/docs/app/api-reference/functions/revalidatePath). +OpenNext’s Azion adapter supports caching through three key components: -You can also enable cache interception, to avoid calling the `NextServer` and thus loading the javascript associated with the page. It can slightly improve cold start performance for ISR/SSG route on cached routes. -As of now, cache interception does not work with PPR and is not enabled by default. +* An **Incremental Cache** to persist generated pages and data. -Additionally some components uses the [Cache Api](https://developers.cloudflare.com/workers/runtime-apis/cache/) to improve the performance of these different components. -If you're planning on using On-Demand revalidation, you should also use the [Cache Purge component](#automatic-cache-purge) to automatically purge the cache when a page is revalidated. +* A **Revalidation mechanism** (analogous to a queue) to coordinate and deduplicate background revalidations for time-based ISR. +* A **Tag Cache** to track tags/paths for on-demand revalidation (used by `revalidateTag`/`revalidatePath`). + +It is also possible to enable *cache interception*, which allows the Edge Function to serve cached responses directly from Azion’s Edge Cache without invoking the full Next.js server runtime for ISR/SSG pages. This can slightly improve cold start performance on cached routes by bypassing Next.js rendering when a cached HTML is available. (Note: Cache interception may not be compatible with certain dynamic SSR features and is typically disabled by default.) + +Internally, these components leverage Azion’s platform features like [Edge Cache](https://www.azion.com/en/documentation/products/build/edge-application/edge-cache/) and [Edge Storage](https://www.azion.com/en/documentation/products/store/edge-storage/) to maximize performance. For example, Azion’s Edge Cache can serve cached content from edge nodes near users, and its Edge Functions Runtime provides a [Cache API for programmatic caching](https://www.azion.com/en/documentation/runtime/api-reference/cache/) and a [Storage API for persistent data](https://www.azion.com/en/documentation/runtime/api-reference/storage/). If you plan to use on-demand revalidation, you should also integrate Azion’s [Real-Time Purge](https://www.azion.com/en/documentation/products/azion-lib/purge/) to invalidate content from the edge cache when a page is updated. The adapter provides several implementations for each of those components configured in `open-next.config.ts`. This guide provides guidelines for common use cases before detailing all the configuration options. @@ -31,64 +31,72 @@ This guide provides guidelines for common use cases before detailing all the con any caching config. -### Guidelines +### **Guidelines** -#### Small site using revalidation +#### **Small site using revalidation** -You should use the following implementation for a small site: +For a small Next.js site with Incremental Static Regeneration enabled, we recommend the following configuration on Azion: -- Incremental Cache: use R2 to store the data -- Queue: use a Queue backed by Durable Objects -- Tag Cache: `D1NextModeTagCache` +* **Incremental Cache:** Use **Azion Edge Storage** (object storage) to store generated pages and data. Edge Storage is an S3-compatible, cost-effective storage service integrated with Azion’s platform. - - +* **Revalidation Coordination:** Rely on Azion’s built-in request coordination. Azion Edge Cache implements thundering herd protection at each edge node (only one request per node triggers an origin fetch for a stale resource). This reduces the need for a custom queue. The Next.js adapter will use background rendering (via `event.waitUntil`) to update content asynchronously when possible. -```jsonc -{ - "name": "", - // ... + + - "services": [ +```js +module.exports = { + build: { + preset: 'opennextjs', + polyfills: true + }, + origin: [ { - "binding": "WORKER_SELF_REFERENCE", - "service": "", - }, + name: 'origin-storage-default', + type: 'object_storage' + } ], - - // R2 incremental cache - "r2_buckets": [ + functions: [ { - "binding": "NEXT_INC_CACHE_R2_BUCKET", - "bucket_name": "", - }, + name: 'handler', + path: '.edge/worker.js' + } ], - - // DO Queue - "durable_objects": { - "bindings": [ + rules: { + request: [ { - "name": "NEXT_CACHE_DO_QUEUE", - "class_name": "DOQueueHandler", + name: 'Set storage origin for all requests _next_static', + match: '^\\/_next\\/static\\/', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage' + }, + deliver: true + } }, - ], - }, - "migrations": [ - { - "tag": "v1", - "new_sqlite_classes": ["DOQueueHandler"], - }, - ], - - // D1 Tag Cache (Next mode) - // This is only required if you use On-demand revalidation - "d1_databases": [ - { - "binding": "NEXT_TAG_CACHE_D1", - "database_id": "", - "database_name": "", - }, - ], + { + name: 'Deliver Static Assets', + match: + '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage' + }, + deliver: true + } + }, + { + name: 'Execute Edge Function', + match: '^/', + behavior: { + runFunction: 'handler', + forwardCookies: true + } + } + ] + } } ``` @@ -96,31 +104,33 @@ You should use the following implementation for a small site: ```ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; -import d1NextTagCache from "@opennextjs/cloudflare/overrides/tag-cache/d1-next-tag-cache"; -import doQueue from "@opennextjs/cloudflare/overrides/queue/do-queue"; -//import { withFilter, softTagFilter } from "@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter"; - -export default defineCloudflareConfig({ - incrementalCache: r2IncrementalCache, - queue: doQueue, - // This is only required if you use On-demand revalidation - tagCache: d1NextTagCache, - //If you don't use `revalidatePath`, you can also filter internal soft tags using the `softTagFilter` - // tagCache: withFilter({ - // tagCache: d1NextTagCache, - // filterFn: softTagFilter, - // }), - // Disable this if you want to use PPR - enableCacheInterception: true, +/** + * This code was originally copied and modified from the @opennextjs/cloudflare repository. + * Significant changes have been made to adapt it for use with Azion. + */ +// default open-next.config.ts file created by @aziontech/opennextjs-azion +import { defineAzionConfig } from "@aziontech/opennextjs-azion/config"; +import StorageIncrementalCache from "@aziontech/opennextjs-azion/overrides/incremental-cache/storage-incremental-cache"; +import MemoryCacheQueue from "@aziontech/opennextjs-azion/overrides/queue/memory-queue"; +import StorageTagCache from "@aziontech/opennextjs-azion/overrides/tag-cache/storage-tag-cache"; +import AzionWrapperNode from "@aziontech/opennextjs-azion/overrides/wrapper/azion-wrapper-node"; + +export default defineAzionConfig({ + incrementalCache: StorageIncrementalCache, + queue: MemoryCacheQueue, + wrapper: AzionWrapperNode, + tagCache: StorageTagCache, }); ``` -#### Large site using revalidation + + If your application does not use on-demand revalidation (no revalidateTag/revalidatePath), you can omit the tag cache configuration. Also, Azion’s adapter uses an in-memory queue for deduplicating revalidation requests within a single edge function instance (to avoid redundant renders). This is sufficient for low traffic scenarios. + + +#### **Large site using revalidation** For a larger site, you should use the `ShardedDOTagCache` that can handle a higher load than the `D1NextModeTagCache`: diff --git a/pages/azion/get-started.mdx b/pages/azion/get-started.mdx index 050b8b3..481beb3 100644 --- a/pages/azion/get-started.mdx +++ b/pages/azion/get-started.mdx @@ -1,218 +1,239 @@ import { Callout } from "nextra/components"; - -### Get Started - -#### New apps - -To create a new Next.js app, pre-configured to run on Cloudflare using `@opennextjs/cloudflare`, run: +import { Tabs } from "nextra/components"; + +--- + +### Get Started with Azion + +The `@aziontech/opennextjs-azion` adapter lets you deploy Next.js apps to [Azion Web Platform](https://www.azion.com/en/). This guide will help you set up a new or existing Next.js project for Azion, configure caching, develop locally, and deploy to production. + +--- +### Install Azion CLI + +You can install the Azion CLI using your preferred package manager. Select your operating system or package manager below: + + + + **RPM**: For Red Hat-based distributions (Fedora, CentOS, RHEL, etc.) + ```sh + sudo rpm -i + ``` + + + **dpkg**: For Debian-based distributions (Debian, Ubuntu, etc.) + ```sh + sudo dpkg -i + ``` + + + **APK**: For Alpine Linux + ```sh + apk add + ``` + + + **Brew**: For macOS and Linux with Homebrew + ```sh + brew install azion + ``` + + + **APT**: For Debian/Ubuntu using APT + ```sh + sudo apt install ./ + ``` + + + **Windows**: + + Using **Chocolatey**: + ```sh + choco install azion + ``` + + Using **Windows Package Manager**: + ```sh + winget install aziontech.azion + ``` + + + +For more details and advanced usage, see the [Azion CLI documentation](https://www.azion.com/en/documentation/products/cli/). +--- + +#### New Next.js Apps + +To create a new Next.js app pre-configured for Azion: ```sh -npm create cloudflare@latest -- my-next-app --framework=next --platform=workers +npx create-next-app@14.2.4 my-next-app --use-npm +cd my-next-app +npm install @aziontech/opennextjs-azion@latest ``` -#### Existing Next.js apps - -##### 1. Install @opennextjs/cloudflare +Or use a starter template: -First, install [@opennextjs/cloudflare](https://www.npmjs.com/package/@opennextjs/cloudflare): +- [Next.js + TypeScript + Tailwind Template](https://github.com/aziontech/azion-samples/tree/dev/templates/opennextjs/nextal-next-typescript-tailwind) +- [Node Playground (Next.js 13)](https://github.com/aziontech/bundler-examples/tree/main/examples/nextjs/node-playground-13) -```sh -npm install @opennextjs/cloudflare@latest -``` +--- -##### 2. Install Wrangler +#### Existing Next.js Apps -Install the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) as a devDependency: +1. **Install the Azion Adapter** ```sh -npm install --save-dev wrangler@latest +npm install @aziontech/opennextjs-azion@latest ``` - - You must use Wrangler version `3.99.0` or later to deploy Next.js apps using `@opennextjs/cloudflare`. - +2. **Configure `open-next.config.ts`** -##### 3. Create a wrangler configuration file - - - This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if - not already present). - - -A [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/) is needed for your -application to be previewed and deployed, it is also where you configure your Worker and define what resources it can access via [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/service-bindings). - -You can create one yourself in the root directory of your Next.js app with the name `wrangler.jsonc` and the following content: - -```jsonc -{ - "$schema": "node_modules/wrangler/config-schema.json", - "main": ".open-next/worker.js", - "name": "my-app", - "compatibility_date": "2024-12-30", - "compatibility_flags": [ - // Enable Node.js API - // see https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag - "nodejs_compat", - // Allow to fetch URLs in your app - // see https://developers.cloudflare.com/workers/configuration/compatibility-flags/#global-fetch-strictly-public - "global_fetch_strictly_public", - ], - "assets": { - "directory": ".open-next/assets", - "binding": "ASSETS", - }, - "services": [ - { - "binding": "WORKER_SELF_REFERENCE", - // The service should match the "name" of your worker - "service": "my-app", - }, - ], - "r2_buckets": [ - // Create a R2 binding with the binding name "NEXT_INC_CACHE_R2_BUCKET" - // { - // "binding": "NEXT_INC_CACHE_R2_BUCKET", - // "bucket_name": "", - // }, - ], -} -``` - - - As shown above: - You must enable the [`nodejs_compat` compatibility - flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) *and* set your [compatibility - date](https://developers.cloudflare.com/workers/configuration/compatibility-dates/) to `2024-09-23` or - later, in order for your Next.js app to work with @opennextjs/cloudflare - The `main` and `assets` values - should also not be changed unless you modify the build output result in some way - You can add a binding - named `NEXT_INC_CACHE_R2_BUCKET` to make use of Next.js' caching as described in the [Caching - docs](/cloudflare/caching) - - -##### 4. Add an `open-next.config.ts` file - - - This step is optional since `@opennextjs/cloudflare` creates this file for you during the build process (if - not already present). - - -Add a [`open-next.config.ts`](https://opennext.js.org/aws/config) file to the root directory of your Next.js app: +Create or update `open-next.config.ts` in your project root. Example: ```ts -import { defineCloudflareConfig } from "@opennextjs/cloudflare"; -import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"; +import { defineAzionConfig } from "@aziontech/opennextjs-azion"; -export default defineCloudflareConfig({ - incrementalCache: r2IncrementalCache, +export default defineAzionConfig({ + // See /azion/caching for advanced options }); ``` - - To use the `OpenNextConfig` type as illustrated above (which is not necessary), you need to install the - `@opennextjs/aws` NPM package as a dev dependency. - - -##### 5. Add a `.dev.vars` file - -Then, add a [`.dev.vars`](https://developers.cloudflare.com/workers/testing/local-development/#local-only-environment-variables) file to the root directory of your Next.js app: - -```text -NEXTJS_ENV=development -``` - -The `NEXTJS_ENV` variable defines the environment to use when loading Next.js `.env` files. It defaults to "production" when not defined. +3. **Update `tsconfig.json`** -##### 6. Update the `package.json` file + + Your `tsconfig.json` must include: ```json "moduleResolution": "bundler" ``` This is required for correct + build and runtime behavior. + -Add the following to the scripts field of your `package.json` file: +If you encounter issues with ESM or `open-next.config.ts`, you may need to add: ```json -"build": "next build", -"preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview", -"deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy", -"upload": "opennextjs-cloudflare build && opennextjs-cloudflare upload", -"cf-typegen": "wrangler types --env-interface CloudflareEnv cloudflare-env.d.ts", +"exclude": ["node_modules", "open-next.config.ts"] ``` -- The `build` script must invoke the Next.js build command, it will be invoke by `opennextjs-cloudflare build`. -- `npm run preview`: Builds your app and serves it locally, allowing you to quickly preview your app running locally in the Workers runtime, via a single command. -- `npm run deploy`: Builds your app, and then immediately deploys it to Cloudflare. -- `npm run upload`: Builds your app, and then uploads a new [version](https://developers.cloudflare.com/workers/configuration/versions-and-deployments/#versions) of it to Cloudflare. -- `cf-typegen`: Generates a `cloudflare-env.d.ts` file at the root of your project containing [the types for the `env`](https://developers.cloudflare.com/workers/wrangler/commands/#types). - -##### 7. Add Static Asset Caching +See [Known Issues](/azion/known-issues) for more details. -Add a `public/_headers` file, with at least the following headers: +4. **Azion CLI Build and Deploy** -```txt -/_next/static/* - Cache-Control: public,max-age=31536000,immutable -``` - -See the [Static Assets Caching docs](/cloudflare/caching#static-assets-caching) for more information. -##### 8. Add caching with Cloudflare R2 +- `azion build`: Builds your app for Azion. +- `azion preview`: Runs a local preview using Azion CLI. +- `azion deploy`: Deploys to Azion Web Platform using Remote Deployment. + - or `azion deploy --local` to deploy to Azion Web Platform using Local Deployment. -See the [Caching docs](/cloudflare/caching) for information on enabling Next.js caching in your OpenNext project. -##### 9. Remove any `export const runtime = "edge";` if present +5. **Set Up Caching and Storage** -Before deploying your app, remove the `export const runtime = "edge";` line from any of your source files. +See the [Caching Guide](/azion/caching) for how to configure Azion Edge Storage and Edge Cache for ISR/SSG. Example Azion config: -The edge runtime is not supported yet with `@opennextjs/cloudflare`. - -##### 10. Add `.open-next` to `.gitignore` +```ts +AzionCache +Type definition for the cache configuration. + +Properties: + +name: string - Name of the cache configuration. +stale?: boolean - Whether to allow stale content. +queryStringSort?: boolean - Whether to sort query string parameters. +methods?: CacheMethods - HTTP methods to cache. +post?: boolean - Whether to cache POST requests. +options?: boolean - Whether to cache OPTIONS requests. +browser?: BrowserCacheConfig - Browser cache settings. +maxAgeSeconds: number | string - Maximum age for browser cache in seconds. +edge?: EdgeCacheConfig - Edge cache settings. +maxAgeSeconds: number | string - Maximum age for edge cache in seconds. +cacheByCookie?: CacheByCookieConfig - Cache by cookie settings. +option: 'ignore' | 'varies' | 'whitelist' | 'blacklist' - Cache by cookie option. +list?: string[] - List of cookies to use for caching. +cacheByQueryString?: CacheByQueryStringConfig - Cache by query string settings. +option: 'ignore' | 'varies' | 'whitelist' | 'blacklist' - Cache by query string option. +list?: string[] - List of query string parameters to use for caching. -You should add `.open-next` to your `.gitignore` file to prevent the build output from being committed to your repository. +``` -##### 11. Remove `@cloudflare/next-on-pages` (if necessary) -If your Next.js app currently uses `@cloudflare/next-on-pages`, you'll want to remove it, and make a few changes. +```js +// azion.config.cjs +module.exports = { + build: { preset: "opennextjs", polyfills: true }, + origin: [{ name: "origin-storage-default", type: "object_storage" }], + functions: [{ name: "handler", path: ".edge/worker.js" }], + cache: [ + { + name: 'Default Cache', + browser: { maxAgeSeconds: 3600 }, + edge: { maxAgeSeconds: 7200 }, + }, + ], + rules: { + request: [ + { + name: "Set storage origin for _next/static", + match: "^/_next/static/", + behavior: { setOrigin: { name: "origin-storage-default", type: "object_storage" }, deliver: true }, + }, + { + name: "Deliver Static Assets", + match: ".(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json)$", + behavior: { setOrigin: { name: "origin-storage-default", type: "object_storage" }, deliver: true }, + }, + { + name: "Execute Edge Function", + match: "^/", + behavior: { runFunction: "handler", forwardCookies: true }, + }, + ], + }, +}; +``` -Uninstalling the [`@cloudflare/next-on-pages`](https://www.npmjs.com/package/@cloudflare/next-on-pages) package as well as the [`eslint-plugin-next-on-pages`](https://www.npmjs.com/package/eslint-plugin-next-on-pages) package if present. +--- -Remove any reference of these packages from your source and configuration files. -This includes: +### Local Development -- `setupDevPlatform()` calls in your Next.js config file -- `getRequestContext` imports from `@cloudflare/next-on-pages` from your source files - (those can be replaced with `getCloudflareContext` calls from `@opennextjs/cloudflare`) -- next-on-pages eslint rules set in your Eslint config file +Use the [Azion CLI](https://www.azion.com/en/documentation/products/cli/) for local development: -##### 12. Develop locally +```sh +azion dev +``` -You can continue to run `next dev` when developing locally. +This runs your Application locally, simulating the Azion platform. See [Troubleshooting](/azion/troubleshooting) for tips on debugging and log monitoring. -Modify your Next.js configuration file to import and call the `initOpenNextCloudflareForDev` utility -from the `@opennextjs/cloudflare` package. This makes sure that the Next.js dev server can optimally integrate with the open-next cloudflare adapter and it is necessary for using bindings during local development. +--- -This is an example of a Next.js configuration file calling the utility: +### Deployment -```ts -// next.config.ts -import type { NextConfig } from "next"; +Deploy your app to Azion Web Platform: -const nextConfig: NextConfig = { - /* config options here */ -}; +```sh +azion deploy +``` -export default nextConfig; +Or use the Azion CLI to deploy to Azion Web Platform using Local Deployment: -import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare"; -initOpenNextCloudflareForDev(); +```sh +azion deploy --local ``` -After having added the `initOpenNextCloudflareForDev()` call in your Next.js configuration file, you will be able, during local development, to access in any of your server code, local versions of Cloudflare bindings as indicated in the [bindings documentation](./bindings). + + Azion Edge Functions have a 50 MiB compressed size limit per function. Monitor your build output size. + [Learn more](https://www.azion.com/en/documentation/products/build/edge-application/edge-functions/#limits). + -In step 3, we also added the `npm run preview`, which allows you to quickly preview your app running locally in the Workers runtime, -rather than in Node.js. This allows you to test changes in the same runtime as your app will run in when deployed to Cloudflare. +--- -##### 13. Deploy to Cloudflare Workers +### Best Practices & Troubleshooting -Either deploy via the command line: +- See [Known Issues](/azion/known-issues) for important config notes. +- See [Troubleshooting](/azion/troubleshooting) for local dev, logging, and debugging tips. +- Explore [Examples](/azion/examples) for starter projects and templates. +- For advanced caching, see [Caching](/azion/caching). -```sh -npm run deploy -``` + + Some Node.js or Web APIs may not be fully supported in Azion's local dev environment but will work in + production. If you hit issues locally, try deploying before deep troubleshooting. + + +--- -Or [connect a Github or Gitlab repository](https://developers.cloudflare.com/workers/ci-cd/), and Cloudflare will automatically build and deploy each pull request you merge to your production branch. +You are now ready to build, test, and deploy Next.js apps on Azion Edge Functions! diff --git a/pages/azion/how-it-works.mdx b/pages/azion/how-it-works.mdx deleted file mode 100644 index a601b03..0000000 --- a/pages/azion/how-it-works.mdx +++ /dev/null @@ -1,9 +0,0 @@ -import { Callout } from "nextra/components"; - -# How it Works - -The OpenNext Azion adapter works by taking the Next.js build output and transforming it, so that it can run in Azion Edge Functions. - -When you add [@aziontech/opennextjs-azion](https://www.npmjs.com/package/@aziontech/opennextjs-azion) as a dependency to your Next.js app, and then run `npx opennextjs-azion` the adapter first builds your app by running the `build` script in your `package.json`, and then transforms the build output to a format that you can run locally using [Azion CLI](https://www.azion.com/en/documentation/products/cli/), and deploy to Azion. - -You can view the code for `@aziontech/opennextjs-azion` [here](https://github.com/aziontech/opennextjs-azion/tree/main/packages/azion/src) to understand what it does under the hood. \ No newline at end of file diff --git a/pages/azion/index.mdx b/pages/azion/index.mdx index 9e55e38..593f903 100644 --- a/pages/azion/index.mdx +++ b/pages/azion/index.mdx @@ -49,5 +49,5 @@ We welcome both contributions and feedback! ### Note on Edge Function Size Limits -Azion Edge Functions have a maximum size limit of 20 MiB per function. After building your project, pay attention to the compressed (gzipped) size of your deployment package—only the compressed size counts toward this limit. +Azion Edge Functions have a maximum size limit of 50 MiB per function. After building your project, pay attention to the compressed (gzipped) size of your deployment package—only the compressed size counts toward this limit. For more details, see the [official Azion documentation on Edge Function size limits](https://www.azion.com/en/documentation/products/build/edge-application/edge-functions/#limits). \ No newline at end of file diff --git a/pages/azion/known-issues.mdx b/pages/azion/known-issues.mdx index ea8f040..5fa7e03 100644 --- a/pages/azion/known-issues.mdx +++ b/pages/azion/known-issues.mdx @@ -1,31 +1,27 @@ ### Known issues -#### Caching Durable Objects (`DOQueueHandler` and `DOShardedTagCache`) +#### TS Config +- Your `tsconfig.json` must include the following setting: + + ```json + "moduleResolution": "bundler" + ``` + + If this is not set, you may encounter issues during the build or runtime process. + +- There is a known issue with ESM and the `open-next.config.ts` file. If you have an older project or one that depends on a specific TypeScript version or custom configuration, you may need to: + - Reconfigure your project (for example, by setting `moduleResolution: bundler` as above), **or** + - Add the following to your `tsconfig.json`: + + ```json + "exclude": ["node_modules", "open-next.config.ts"] + ``` + + Whether this works depends on your project setup. In some cases, excluding `open-next.config.ts` allows the build to proceed, but you may still encounter errors, especially if your project uses features like `_app.mdx` or other advanced configurations. This is particularly relevant for projects that haven't been tested with these setups, and issues may also occur in other environments (such as the Cloudflare package). + +#### Local Development + +- Some applications may use Node.js or Web Standard APIs that are not fully supported by Azion's local development environment. However, these features might be available in the actual Azion runtime. + + If your application does not work as expected during local development, try deploying it to Azion before troubleshooting further, as it may work correctly in the deployed environment. -If your app uses [Durable Objects](https://developers.cloudflare.com/durable-objects/) for caching, you might see a warning while building your app: - -```text -┌─────────────────────────────────┐ -│ OpenNext — Building Next.js app │ -└─────────────────────────────────┘ - - -> next build - - ▲ Next.js 15.2.4 - -▲ [WARNING] You have defined bindings to the following internal Durable Objects: - - - {"name":"NEXT_CACHE_DO_QUEUE","class_name":"DOQueueHandler"} - These will not work in local development, but they should work in production. - - If you want to develop these locally, you can define your DO in a separate Worker, with a - separate configuration file. - For detailed instructions, refer to the Durable Objects section here: - https://developers.cloudflare.com/workers/wrangler/api#supported-bindings - - Creating an optimized production build ... -workerd/server/server.c++:1951: warning: A DurableObjectNamespace in the config referenced the class "DOQueueHandler", but no such Durable Object class is exported from the worker. Please make sure the class name matches, it is exported, and the class extends 'DurableObject'. Attempts to call to this Durable Object class will fail at runtime, but historically this was not a startup-time error. Future versions of workerd may make this a startup-time error. -``` - -The warning can be safely ignored as the caching Durable Objects are not used during the build. diff --git a/pages/azion/troubleshooting.mdx b/pages/azion/troubleshooting.mdx index 537e9be..b64e211 100644 --- a/pages/azion/troubleshooting.mdx +++ b/pages/azion/troubleshooting.mdx @@ -1,127 +1,150 @@ import { Callout } from "nextra/components"; -## Troubleshooting +# Troubleshooting -### Trying to deploy to Cloudflare Pages, instead of Cloudflare Workers? +## 1. Local Development and Debugging with the Azion CLI -`@opennextjs/cloudflare` is specifically built for deploying Next.js apps to [Cloudflare Workers](https://developers.cloudflare.com/workers/) +The first and most crucial troubleshooting step is performed even before deployment. The Azion CLI allows you to run your edge function in a local environment that simulates the Azion platform. -Cloudflare Workers now support the majority of functionality from Cloudflare Pages, and have features that are not yet supported by Cloudflare Pages. Refer to the [Compatibility Matrix](https://developers.cloudflare.com/workers/static-assets/compatibility-matrix/) in the Cloudflare Workers docs. - -If you need to deploy to Cloudflare Pages, you can use `@cloudflare/next-on-pages`, and follow the [Cloudflare Pages guides for deploying Next.js apps](https://developers.cloudflare.com/pages/framework-guides/nextjs/). - -### "Your Worker exceeded the size limit of 3 MiB" - -The Cloudflare Account you are deploying to is on the Workers Free plan, which [limits the size of each Worker to 3 MiB](https://developers.cloudflare.com/workers/platform/limits/#worker-size). When you subscribe to the Workers Paid plan, each Worker can be up to 10 MiB. - -When deploying your Worker, `wrangler` will show both the original and compressed sizes. Only the latter (gzipped size) matters for these limits. - -### "Your Worker exceeded the size limit of 10 MiB" - -If your Worker is larger than 10 MiB compressed — there might be unnecessary code ending up in your production bundle. You can visualize and understand this by running: +**Key Command:** +```bash +azion dev +``` +When you run this command in the root of your project, the CLI starts a local server. Any request made to this server will execute your function, and outputs from `console.log`, as well as any errors, will be displayed directly in your terminal. + +**Benefits:** +* **Immediate Feedback:** See the results of your code changes instantly. +* **Time Savings:** Avoids the need to deploy for every minor change. +* **Controlled Environment:** Debug your application's logic without affecting the production environment. + +**Example:** +```javascript +// main.js +export default function myWorker(event) { + console.log("Function executed successfully!"); + return new Response('Hello World'); +} +``` +After running `azion dev` and accessing the local server, your terminal will display: +``` +Function executed successfully! +``` -1. `npx opennextjs-cloudflare build` within your project's root directory -2. `cd .open-next/server-functions/default` to open the directory that contains the bundled code -3. Take the file named `handler.mjs.meta.json` and use the [ESBuild Bundle Analyzer](https://esbuild.github.io/analyze/) to visualize your application's code, and understand the largest parts of your production bundle +## 2. Monitoring Logs in Production -### My app fails to build when I import a specific NPM package +Once your application is in production, you need tools to observe its behavior in real time and analyze past events. -First, make sure that the `nodejs_compat` compatibility flag is enabled, and your compatibility date is set to on or after "2024-09-23", in your [wrangler configuration file](https://developers.cloudflare.com/workers/wrangler/configuration/). -Refer to the [Node.js Workers docs](https://developers.cloudflare.com/workers/runtime-apis/nodejs/) for more details on Node.js support in Cloudflare Workers. +### Real-Time Access (Tailing) -Some NPM packages define multiple exports. For example: +To monitor logs as they happen, use the `--tail` argument with the Azion CLI. This creates a continuous session that displays new logs as they are generated. +**Key Command:** +```bash +azion logs cells --tail ``` -"exports": { - "other": "./src/other.js", - "node": "./src/node.js", - "browser": "./src/browser.js", - "default": "./src/default.js" -}, -``` +This command is ideal for debugging issues that are currently happening or for monitoring the application's behavior during a new deployment. -When you use `@opennextjs/cloudflare`, [Wrangler](https://developers.cloudflare.com/workers/wrangler/) bundles your code before running it locally, or deploying it to Cloudflare. Wrangler has to choose which export to use, when you import a module. By default, Wrangler, which uses [esbuild](https://esbuild.github.io/), handles this in a way that is not compatible with some NPM packages. +### Viewing via the Azion Console (Real-Time Events) -You may want to modify how Wrangler resolves multiple exports, such that when you import packages, the `node` export, if present, is used. You can do do by defining the following variables in a `.env` file within the root directory of your Next.js app: +For a more visual analysis with more filtering options, the Azion Console is the ideal tool. -``` -WRANGLER_BUILD_CONDITIONS="" -WRANGLER_BUILD_PLATFORM="node" -``` +1. Access the **Real-Time Manager**. +2. In the menu, go to **Observe > Real-Time Events**. +3. Select the **Edge Functions Console** tab. -### `Error: Cannot perform I/O on behalf of a different request.` +On this screen, you can: +* View all logs generated by `console.log` in your functions. +* Filter events by time periods (last 15 minutes, last hour, custom range). +* Search for specific log messages. +* Analyze detailed information for each event, such as `Function ID` and `Timestamp`. -Some DB clients (i.e. [`postgres`](https://www.npmjs.com/package/postgres)) create a connection to the DB server when they are first instantiated and re-use it for later requests. -This programming model is not compatible with the Workers runtime where a connection can not be re-used in a different request. +## 3. Advanced Analysis with the Metadata API -The following error is generated in such a case: +Often, the problem lies not just in the code, but in the context of the request (origin, headers, etc.). The Azion **Metadata API** allows your functions to access this data. You can log this metadata to better understand your application's behavior. -```text -⨯ Error: Cannot perform I/O on behalf of a different request. I/O objects (such as streams, request/response bodies, and others) created in the context of one request handler cannot be accessed from a different request's handler. This is a limitation of Cloudflare Workers which allows us to improve overall performance. (I/O type: Writable) -``` +**Practical Example:** +Imagine you want to check which continent a request is coming from. -To solve this, you should create the DB client inside a request context and not keep a global DB client. +```javascript +// main.js +export default function myWorker(event) { + const request = event.request; + const continentCode = request.metadata['geoip_continent_code']; -A global client would not work: + console.log(`Request received from continent: ${continentCode}`); -```ts -// src/lib/db.ts -import postgres from "postgres"; + // Additional logic based on the continent... -// `client` is global. -// As the connection would be shared across requests, it fails on worker -export const client = postgres(process.env.DATABASE_URL, { max: 5 }); + return new Response(`Hello, ${continentCode}!`); +} +``` -// src/app/api/route.ts -import { client } from "@/db/db"; +By deploying this code and monitoring the logs with `azion logs cells --tail`, you will see messages like: -export const dynamic = "force-dynamic"; +``` +Request received from continent: SA -export async function GET() { - return new Response(JSON.stringify(await client`SELECT * FROM users;`)); +``` +This allows you to debug routing logic, security (e.g., blocking by region), and content personalization. + +## 4. Structured Log Collection with the GraphQL API + +For automation, integration with external monitoring systems (like Datadog or New Relic), or for performing complex analyses, Azion provides a **GraphQL API**. + +With it, you can execute queries to fetch logs in a structured format. + +**Key Tool:** The **GraphQL Playground**, available in the Azion Console, allows you to build and test your queries before integrating them into your tools. + +**Example Query:** +This query fetches the 10 most recent console events containing a specific log line. + +```graphql +query getConsoleEvents { + cellsConsoleEvents( + limit: 10, + filter: { + tsRange: {begin: "2024-09-28T12:00:00", end: "2024-09-28T13:00:00"}, + line: {contains: "Edge Function failed"} + }, + orderBy: [ts_ASC] + ) { + ts + configurationId + functionId + line + } } ``` -It can fixed by creating the client for each incoming request: +## 5. Function size exceeds limit -```ts -// src/app/api/route.ts -export const dynamic = "force-dynamic"; +Azion imposes limits on the size of edge functions, the default limit is 20MB. If your deployment fails with an error about function size, it usually means your bundled code is too large for the platform. -export async function GET() { - // The client is created for each incoming request and no connection is shared across requests - const client = postgres(process.env.DATABASE_URL, { max: 5 }); - return new Response(JSON.stringify(await client`SELECT * FROM users;`)); -} -``` +To resolve this: -### `Error: Failed to load chunk server/chunks/ssr/.js` +1. **Analyze your bundle:** Use tools like [Webpack Bundle Analyzer](https://www.npmjs.com/package/webpack-bundle-analyzer) or [ESBuild Analyzer](https://esbuild.github.io/analyze/) to identify large dependencies. +2. **Remove unused dependencies:** Audit your `package.json` and codebase for unnecessary packages. +3. **Optimize imports:** Prefer importing specific functions instead of entire libraries (e.g., `import { parse } from "date-fns"` instead of `import * as dateFns from "date-fns"`). +4. **Leverage Azion’s edge caching:** Move static assets and large files to Azion’s Edge Storage or CDN, and reference them via URLs instead of bundling them. -If you see an error similar to: +Refer to [Azion’s documentation on function limits](https://www.azion.com/en/documentation/products/build/edge-application/edge-functions/#limits) for up-to-date size restrictions. -```text -✘ [ERROR] ⨯ Error: Failed to load chunk server/chunks/ssr/.js +--- - at loadChunkPath - (...) - at Object.loadChunk - (...) - at .open-next/server-functions/default/.next/server/app/page.js -``` +## 6. My environment variables are not available in the Edge Function -You are proably using a turbopack enabled build (`next build --turbo`) which is not currently supported by OpenNext. -Change your build command to `next build` to fix the issue. +Azion allows you to define environment variables for your edge functions, but they must be configured explicitly in the Azion console or via the API. -### `X [ERROR] Could not resolve ""` +If your code cannot access an expected environment variable: -When you see the following error during the build: +1. **Check the Azion console:** Go to your Edge Application > Edge Functions > Environment Variables and ensure the variable is defined. +2. **Redeploy after changes:** Any change to environment variables requires a redeploy of your function to take effect. +3. **Access variables correctly:** In your code, use `process.env.MY_VARIABLE` (for Node.js compatibility) or the platform-specific API if using another runtime. -```text -⚙️ Bundling the OpenNext server... +If you’re using a framework like Next.js, ensure your build process does not overwrite or ignore these variables. For more, see [Azion’s guide to environment variables](https://www.azion.com/en/documentation/products/edge-functions/environment-variables/). -X [ERROR] Could not resolve "" -``` +--- -It might be because the package contains workerd specific code. +If you encounter other issues, check the [Azion documentation](https://www.azion.com/en/documentation/) or reach out to their support for platform-specific guidance. -Check this [howto](/cloudflare/howtos/workerd) for a solution. +--- diff --git a/pages/index.mdx b/pages/index.mdx index 3dfb914..31ff6cf 100644 --- a/pages/index.mdx +++ b/pages/index.mdx @@ -19,6 +19,7 @@ OpenNext is currently backed by: 1. [SST](https://sst.dev) community, maintains the [AWS](aws) adapter 2. [Cloudflare](https://developers.cloudflare.com/) team, maintains the [Cloudflare](cloudflare) adapter 3. [Netlify](https://www.netlify.com/) team, maintains the [Netlify](netlify) adapter +4. [Azion](https://www.azion.com/) team, maintains the [Azion](azion) adapter If you'd like to join the effort, connect with us on [Discord](https://discord.gg/opennextjs).