From dc2425cadc14ee40791b49de14ea0e433998aef0 Mon Sep 17 00:00:00 2001 From: Phil Miller Date: Tue, 17 Dec 2024 04:12:42 -0500 Subject: [PATCH] First version of @dmno/cloudflare-platform (#161) * move signup api to cloudflare pages functions and refactor associated code * force a preview deploy * testing functions * unclean * cloudflare platform and dwrangler * final cleanup --------- Co-authored-by: Theo Ephraim --- .changeset/soft-terms-try.md | 9 + .dmno/config.mts | 2 +- packages/core/package.json | 5 + .../core/src/config-engine/authoring-utils.ts | 49 +- .../core/src/config-loader/vite-server.ts | 2 + .../core/src/globals-injector/auto-inject.ts | 13 +- .../core/tsup.inject-standalone.config.ts | 1 + packages/docs-site/astro.config.ts | 4 + packages/docs-site/public/_redirects | 0 .../docs-site/src/components/EmailSignup.vue | 2 +- .../content/docs/docs/guides/typescript.mdx | 7 +- .../docs/docs/platforms/cloudflare.mdx | 269 ++++ .../content/docs/docs/platforms/overview.mdx | 12 +- packages/integrations/astro/src/index.ts | 1 + packages/integrations/nextjs/src/index.ts | 2 + packages/integrations/remix/src/index.ts | 2 + packages/platforms/cloudflare/.eslintrc.cjs | 6 + packages/platforms/cloudflare/README.md | 9 + packages/platforms/cloudflare/package.json | 70 ++ .../platforms/cloudflare/src/data-types.ts | 203 +++ .../platforms/cloudflare/src/dwrangler.ts | 340 ++++++ packages/platforms/cloudflare/src/index.ts | 1 + packages/platforms/cloudflare/tsconfig.json | 15 + packages/platforms/cloudflare/tsup.config.ts | 21 + packages/signup-api/.dmno/config.mts | 18 +- packages/signup-api/.gitignore | 3 + .../{src => functions-src}/dmno-env.d.ts | 0 .../functions-src/lib/mailerlite.ts | 42 + packages/signup-api/functions-src/signup.ts | 56 + .../functions-src/types/emailvalid.d.ts | 24 + packages/signup-api/functions/README.md | 1 + packages/signup-api/netlify.toml | 24 - packages/signup-api/package.json | 7 +- packages/signup-api/public/_routes.json | 5 + packages/signup-api/src/endpoints/signup.ts | 86 -- packages/signup-api/src/lib/mailerlite.ts | 45 - packages/signup-api/tsconfig.json | 9 +- packages/signup-api/tsup.config.ts | 6 +- packages/signup-api/wrangler.toml | 2 + pnpm-lock.yaml | 1087 ++++++++++++++--- 40 files changed, 2135 insertions(+), 325 deletions(-) create mode 100644 .changeset/soft-terms-try.md create mode 100644 packages/docs-site/public/_redirects create mode 100644 packages/docs-site/src/content/docs/docs/platforms/cloudflare.mdx create mode 100644 packages/platforms/cloudflare/.eslintrc.cjs create mode 100644 packages/platforms/cloudflare/README.md create mode 100644 packages/platforms/cloudflare/package.json create mode 100644 packages/platforms/cloudflare/src/data-types.ts create mode 100644 packages/platforms/cloudflare/src/dwrangler.ts create mode 100644 packages/platforms/cloudflare/src/index.ts create mode 100644 packages/platforms/cloudflare/tsconfig.json create mode 100644 packages/platforms/cloudflare/tsup.config.ts rename packages/signup-api/{src => functions-src}/dmno-env.d.ts (100%) create mode 100644 packages/signup-api/functions-src/lib/mailerlite.ts create mode 100644 packages/signup-api/functions-src/signup.ts create mode 100644 packages/signup-api/functions-src/types/emailvalid.d.ts create mode 100644 packages/signup-api/functions/README.md delete mode 100644 packages/signup-api/netlify.toml create mode 100644 packages/signup-api/public/_routes.json delete mode 100644 packages/signup-api/src/endpoints/signup.ts delete mode 100644 packages/signup-api/src/lib/mailerlite.ts create mode 100644 packages/signup-api/wrangler.toml diff --git a/.changeset/soft-terms-try.md b/.changeset/soft-terms-try.md new file mode 100644 index 00000000..5866537f --- /dev/null +++ b/.changeset/soft-terms-try.md @@ -0,0 +1,9 @@ +--- +"@dmno/cloudflare-platform": patch +"@dmno/nextjs-integration": patch +"@dmno/astro-integration": patch +"@dmno/remix-integration": patch +"dmno": patch +--- + +cloudflare platform support diff --git a/.dmno/config.mts b/.dmno/config.mts index fbb28742..567a7c3c 100644 --- a/.dmno/config.mts +++ b/.dmno/config.mts @@ -1,4 +1,4 @@ -import { DmnoBaseTypes, defineDmnoService } from 'dmno'; +import { DmnoBaseTypes, defineDmnoService, pickFromSchemaObject } from 'dmno'; import { GitDataTypes, GithubDataTypes } from 'dmno/vendor-types'; export default defineDmnoService({ diff --git a/packages/core/package.json b/packages/core/package.json index c8e53b73..f9b59c3c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -79,6 +79,11 @@ "import": "./dist/globals-injector-standalone/edge/injector.js", "default": "./dist/globals-injector-standalone/edge/injector.cjs" }, + "./injector-standalone/edge-auto": { + "ts-src": "./src/globals-injector/auto-inject.ts", + "import": "./dist/globals-injector-standalone/edge/auto-inject.js", + "default": "./dist/globals-injector-standalone/edge/auto-inject.cjs" + }, "./tsconfigs/*.json": "./tsconfigs/*", "./tsconfigs/*": "./tsconfigs/*.json" }, diff --git a/packages/core/src/config-engine/authoring-utils.ts b/packages/core/src/config-engine/authoring-utils.ts index 2775342b..9e9f0f28 100644 --- a/packages/core/src/config-engine/authoring-utils.ts +++ b/packages/core/src/config-engine/authoring-utils.ts @@ -7,31 +7,54 @@ import { DmnoDataTypeMetadata } from './configraph-adapter'; // export function pickFromSchemaObject>(schemaObj: S, keys: Array); // export function pickFromSchemaObject>(schemaObj: S, ...keys: Array); -export function pickFromSchemaObject>( +export function pickFromSchemaObject< + S extends Record>, +>( schemaObj: S, - firstKeyOrKeyArray: keyof S | Array, + pickSettings: ( + keyof S | + Array | + Partial>> + ), ...keysRest: Array -) { - const keyArray = _.isArray(firstKeyOrKeyArray) ? firstKeyOrKeyArray : [firstKeyOrKeyArray, ...keysRest]; - return Object.fromEntries(keyArray.map((k) => [k, schemaObj[k]])); +): Partial>> { + if (_.isArray(pickSettings) || _.isString(pickSettings)) { + const keyArray = _.isArray(pickSettings) + ? pickSettings + : [pickSettings, ...keysRest]; + return _.pick(schemaObj, keyArray); + } else if (_.isObject(pickSettings)) { + return _.mapValues(pickSettings, (itemOverrides, key) => { + return { + ...schemaObj[key], + ...itemOverrides, + }; + }); + } else { + throw new Error('Invalid pick from schema'); + } } -export function createVendorSchema( - schema: Record>, +export function createVendorSchema< + S extends Record>, +>( + schema: S, commonTraits?: Partial>, -) { - if (!commonTraits) return schema; +): Record< keyof S, ConfigraphDataTypeDefinition> { return _.mapValues(schema, (item, _itemKey) => { if (item instanceof ConfigraphDataType || _.isString(item) || _.isFunction(item)) { return { extends: item, ...commonTraits, }; + } else { + return { + // TODO: not sure why I need this any :( + ...item as any, + ...commonTraits, + }; } - return { - ...item, - ...commonTraits, - }; + // throw Error('invalid vendor schema'); }); } diff --git a/packages/core/src/config-loader/vite-server.ts b/packages/core/src/config-loader/vite-server.ts index 7c4dd9bd..3e53d7cd 100644 --- a/packages/core/src/config-loader/vite-server.ts +++ b/packages/core/src/config-loader/vite-server.ts @@ -34,6 +34,8 @@ export async function setupViteServer(opts: { }, transform(code, id, options) { + // TODO: need to detect file types more broadly and can do better detection of if we need to inject at all + if (id.endsWith('.json')) return; // fairly naive way of doing this... but for now we are replacing `DMNO_CONFIG.SOME_KEY` with `getResolverCtx().get('SOME_KEY')` // TODO: we probably should limit which files this applies in const fixedCode = new MagicString(code); diff --git a/packages/core/src/globals-injector/auto-inject.ts b/packages/core/src/globals-injector/auto-inject.ts index 90f48114..5272ad1a 100644 --- a/packages/core/src/globals-injector/auto-inject.ts +++ b/packages/core/src/globals-injector/auto-inject.ts @@ -3,7 +3,18 @@ import { injectDmnoGlobals as _injectDmnoGlobals } from './injector'; -_injectDmnoGlobals(); +let defineInjectedConfig; +try { + // we'll attempt to inject data from a global/replaced var of __DMNO_INJECTED_CONFIG__ + // this is used in something like the cloudflare dwrangler integration, where we use an esbuild replacement + // otherwise we call injectDmnoGlobals() with nothing, and it will look for an env var `DMNO_INJECTED_ENV` + // which would come come something like `dmno run` + + // @ts-ignore + defineInjectedConfig = __DMNO_INJECTED_CONFIG__; +} catch (err) {} +if (defineInjectedConfig) _injectDmnoGlobals({ injectedConfig: defineInjectedConfig }); +else _injectDmnoGlobals(); export const injectDmnoGlobals = _injectDmnoGlobals; diff --git a/packages/core/tsup.inject-standalone.config.ts b/packages/core/tsup.inject-standalone.config.ts index 92011ca2..c7307348 100644 --- a/packages/core/tsup.inject-standalone.config.ts +++ b/packages/core/tsup.inject-standalone.config.ts @@ -28,6 +28,7 @@ const outDir = `dist/globals-injector-standalone${process.env.DMNO_EDGE_COMPAT ? export default defineConfig({ entry: [ // Entry point(s) 'src/globals-injector/injector.ts', // function used to inject dmno globals + 'src/globals-injector/auto-inject.ts', // exports inject function and automatically calls it once ], esbuildPlugins: [ diff --git a/packages/docs-site/astro.config.ts b/packages/docs-site/astro.config.ts index 87e9a88e..cea40c88 100644 --- a/packages/docs-site/astro.config.ts +++ b/packages/docs-site/astro.config.ts @@ -206,6 +206,10 @@ export default defineConfig({ label: 'Overview', link: '/docs/platforms/overview/', }, + { + label: 'Cloudflare', + link: '/docs/platforms/cloudflare/', + }, { label: 'Netlify', link: '/docs/platforms/netlify/', diff --git a/packages/docs-site/public/_redirects b/packages/docs-site/public/_redirects new file mode 100644 index 00000000..e69de29b diff --git a/packages/docs-site/src/components/EmailSignup.vue b/packages/docs-site/src/components/EmailSignup.vue index 5cd04834..3b320d82 100644 --- a/packages/docs-site/src/components/EmailSignup.vue +++ b/packages/docs-site/src/components/EmailSignup.vue @@ -9,7 +9,7 @@ diff --git a/packages/docs-site/src/content/docs/docs/guides/typescript.mdx b/packages/docs-site/src/content/docs/docs/guides/typescript.mdx index 0a57c9de..2bb228a6 100644 --- a/packages/docs-site/src/content/docs/docs/guides/typescript.mdx +++ b/packages/docs-site/src/content/docs/docs/guides/typescript.mdx @@ -82,12 +82,17 @@ Even if you are not writing TypeScript, these days you are still likely relying // ... "include": [ "**/*.js", -+ ".dmno/.typegen/global.d.ts" ++ ".dmno/.typegen/global.d.ts", + ".dmno/.typegen/global-public.d.ts" ] } ``` +:::note[Invalid keys are not strict] +JavaScript is by default not strict - so while you will get nice autocompletion and IntelliSense on your config, your IDE will not give you the handy red squiggle if you use an _invalid_ key. Depending on your setup you may still get a build/runtime error. +::: + + #### Injecting `DMNO_CONFIG` vs `DMNO_PUBLIC_CONFIG` diff --git a/packages/docs-site/src/content/docs/docs/platforms/cloudflare.mdx b/packages/docs-site/src/content/docs/docs/platforms/cloudflare.mdx new file mode 100644 index 00000000..391bba6d --- /dev/null +++ b/packages/docs-site/src/content/docs/docs/platforms/cloudflare.mdx @@ -0,0 +1,269 @@ +--- +title: Using DMNO with Cloudflare +description: Use DMNO while deploying on Cloudflare Workers and Pages +npmPackage: "@dmno/cloudflare-platform" +--- + +import CloudflareLogo from "~icons/logos/cloudflare"; + +import TabbedCode from '@/components/TabbedCode.astro'; +import BugReportLink from '@/components/BugReportLink.astro'; + +At DMNO we _love_ [Cloudflare](https://cloudflare.com/). This very site is hosted on it! That's why we're very excited to make it easier and safer to manage config and secrets in all of your Cloudflare projects. + +This platform integration exposes pre-made schemas and underlying types to interact with env vars related to Cloudflare, as well as a special cli wrapper around `wrangler` that helps deal with config. Aside from all the usual benefits of DMNO - validation, type-safety, sync with backends like 1Password, share config across a monorepo - our Cloudflare integration has a few extra tricks up it's sleeve: +- configure `wrangler` using DMNO, injecting built-in env vars, and special new ones that are passed in via flags +- inject config into Cloudflare Workers - both during local dev and deployment +- enable DMNO security features in workers - leak prevention, log redaction +- unified system to handle both static and dynamic config +- add full type-safety to your config, without needing to run `wrangler types` +- access your config anywhere, not just within route handlers + + +## Setup + + + +:::note +If you run into any issues, feel free to report them to us on GitHub or hop in our Discord. +::: + + +## Configuring `wrangler` using DMNO +Many facets of the `wrangler` cli, including authentication, can be set using [system environment variables](https://developers.cloudflare.com/workers/wrangler/system-environment-variables/). By default, if you have a `.env` file in your repo, `wrangler` will automatically use it, but of course this suffers from all the same usual headaches of using a `.env` file. Instead, DMNO provides a pre-made vendor schema and associated types so you can inject validated settings into `wrangler` without resorting to a gitignored `.env` file. Instead, we could could pull our sensitive Cloudflare API keys from any DMNO plugin, like [1Password](/docs/plugins/1password/) or an [encrypted vault](/docs/plugins/encrypted-vault/), share values across a monorepo, or compose our config however we see fit. + +Because `wrangler` is configured using a mix of `wrangler.toml`, env vars, CLI flags, and has no concept of plugins, we also provide a small wrapper CLI, called `dwrangler`, that handles everything automatically. + +For example, if we wanted to pull our Cloudflare credentials from 1Password, configure dev options, and inject our config, our `.dmno/config.mts` might look like this: + +```ts title=".dmno/config.mts" +import { CloudflareWranglerEnvSchema, DmnoWranglerEnvSchema } from '@dmno/cloudflare-platform'; +import { OnePasswordDmnoPlugin } from '@dmno/1password-plugin'; +import { + DmnoBaseTypes, defineDmnoService, pickFromSchemaObject, switchBy, +} from 'dmno'; + +// initialize our 1Password plugin +const opSecrets = new OnePasswordDmnoPlugin('1pass', { + fallbackToCliBasedAuth: true, +}); + +export default defineDmnoService({ + schema: { + // config that affects wrangler directly + ...pickFromSchemaObject(CloudflareWranglerEnvSchema, { + CLOUDFLARE_ACCOUNT_ID: { + value: opSecrets.itemByReference('op://Shared/Cloudflare/account id'), + }, + CLOUDFLARE_API_TOKEN: { + value: opSecrets.itemByReference('op://Shared/Cloudflare/workers api token'), + }, + }), + + // special config that controls wrangler via `dwrangler` cli wrapper (all optional) + ...pickFromSchemaObject(DmnoWranglerEnvSchema, { + WRANGLER_ENV: {}, // passed as --env + WRANGLER_DEV_IP: { value: 'custom.host.local' }, // passed as --ip + WRANGLER_DEV_PORT: { value: 8881 }, // passed as --port + WRANGLER_DEV_URL: {}, // will be populated with full dev URL + WRANGLER_LIVE_RELOAD: { value: true }, // passed as `--live-reload` + WRANGLER_DEV_ACTIVE: {}, // true when running `dwrangler dev` or `dwrangler pages dev` + WRANGLER_BUILD_ACTIVE: {}, // true when dwrangler is performing a build for deployment + }), + + // ... rest of your app config + SOME_VAR: { + value: switchBy('WRANGLER_DEV_ACTIVE', { // use info from wrangler to affect other config + _default: 'dev value', + false: 'prod value', + }), + }, + }, +}); +``` + +To take advantage of this new config, we swap usage of `wrangler` to `dwrangler`, whether calling it directly or in our `package.json` scripts. + +```diff lang="json" title="package.json" ins=/(d)wrangler/ +{ + "scripts": { + "dev": "dwrangler dev", + "deploy": "dwrangler deploy" + } +} +``` + +:::note[Don't worry, `dwrangler` is a simple wrapper] +There's not too much magic going on. It's a simple wrapper script that puts config into env vars, flags, and handles smart reloading in dev mode. +::: + + +## Cloudflare Workers + +Dealing with [config in Cloudflare Workers](https://developers.cloudflare.com/workers/configuration/environment-variables/) is a bit different than other JS/TS environments. Instead of relying on a global `process.env`, config is passed in as "bindings" to route handlers. These values can be either "vars" (not-sensitive, plaintext) or "secrets" (sensitive, encrypted), and can be set in a variety of ways for local development and for deployments: + +- `wrangler --var` cli option - sets vars during local dev, also sets non-sensitive vars during `wrangler deploy` +- `wrangler.toml` in a `[vars]` section - sets vars, can also be varied per environment (e.g., `[staging.vars]`) +- `.dev.vars` file - set sensitive secrets during local development only +- `wrangler secret` - command to set secrets remotely, also see `wrangler versions secret` to handle versioned deploys with secrets + +Additionally, if not relying on a custom build, `wrangler` internally uses [esbuild](https://esbuild.github.io), and you can do build-time replacements, which can also be used for configuration purposes: + +- `wrangler.toml` in a `[define]` section - static vars, can also be varied per environment (e.g., `[staging.define]`) +- `wrangler --define` cli option - does static replacements during the build, both locally and during deploys + +Navigating all of this can be tricky, so we built this integration to make it as easy as possible. Define your DMNO config, and we take care of the rest, giving you a unified way to access your configuration, regardless of if it is [static or dynamic](/docs/guides/dynamic-config/), and sensitive or not. + +### Injection via "inline" mode + +While it is not very _Cloudflare-y_, our preferred way to inject DMNO config is by inlining the entire resolved config during the build/deploy process. While it may feel a little odd, config changes always trigger a new deployment anyway. + +Doing it this way has a few important benefits: +- we now have the full validated and coerced config, with additional metadata, and it is directly a part of a specific version of your worker +- all config can now be accessed _everywhere_, just like `process.env`, not just within request handlers +- full type-safety and intellisense on your config, without re-running `wrangler types` +- we can enable DMNO's [security features](/docs/get-started/security/) to prevent secrets from leaking over http responses, requests to other servers, and redact secrets from logs + +To set this up, we must import the DMNO globals injector at the top of your main worker entrypoint. This version is specifically made to be compatible with Cloudflare's edge runtime, and will look for data injected by `dwrangler` at build time. + +```diff lang="js" title="your-worker.js" ++import 'dmno/injector-standalone/edge-auto'; + +console.log(DMNO_CONFIG.SOME_ITEM); // 🎉 config is now available everywhere! + +export default { + async fetch(request, env, ctx) { + return new Response(`API host: ${DMNO_CONFIG.API_HOST}`); + }, +}; +``` + +There are 2 downsides of this approach to consider: +- existing uses of `env.SOME_SECRET` will no longer work - **you must use `DMNO_CONFIG.SOME_SECRET` instead**. +- the current version of bundled worker code is accessible within the cloudflare UI, so your devs with access to your account can access this code and see these secrets + + +### Injection via "secrets" mode + +If you want to continue to use Cloudflare's built-in secrets functionality, you can instead use the "secrets" injection mode. In this mode, while static config items will still be replaced at build time, dynamic config items will be set as [Cloudflare secrets](https://developers.cloudflare.com/workers/configuration/secrets/#secrets-on-deployed-workers) and you will continue to read them from the `env` binding injected into your route handlers. + +To enable this mode, you must set the `WRANGLER_INJECT_MODE` in your config to `secrets`: +```diff lang="ts" title=".dmno/config.mts" +export default defineDmnoService({ + schema: { + ...pickFromSchemaObject(DmnoWranglerEnvSchema, { ++ WRANGLER_INJECT_MODE: { value: 'secrets' }, + }), + }, +}); +``` + +**Pros:** +- secrets are no longer bundled into your code +- existing calls to `env.SOME_KEY` will continue to work + +**Cons:** +- we cannot activate DMNO's security features - sensitive secrets can appear in logs, and easily be leaked +- you (still) cannot access config outside of route handlers +- secrets will always be injected as strings +- less reliable types + +To make things a bit more convenient, we do replace references to `DMNO_CONFIG.SOME_VAR` to `env.SOME_VAR`, which means you get automatic type-safety and intellisense, and you never have to think about whether a config item is static or dynamic. + +:::caution[You must use the `env` convention] +This replacement means that to use `DMNO_CONFIG` and take advantage of the type safety it provides, you must stick with the convention of naming the bindings argument of your route handlers `env`, and if you pass it around to other functions, you should also stick with the name. + +```js title="my-worker.js" /env/ +export default { + async fetch(request, env, ctx) { + someHelper(env); + return new Response(`API host: ${DMNO_CONFIG.API_HOST}`); + }, +}; + +function someHelper(env) { + return DMNO_CONFIG.ANOTHER_ITEM; +} +``` +::: + + + + +While it should all feel pretty seamless, running `dwrangler deploy` in this mode calls multiple commands to do a 3-part deployment: +- build and upload a new versioned deploy, without activating it +- upload resolved config as encrypted secrets, creating another version with secrets attached +- activate the new version with correct secrets attached + +### Which mode should you use? + +We think for most users the "inline" mode is the better choice, but both options are totally sensible. Here is a comparison table to help: + +| Inline mode | Secrets mode | +|---|---| +| 👍 Static config injected at build time, accessible anywhere | < same | +| ✨ Dynamic config acessible outside route handlers! | 😪 Dynamic config only accessible within route handlers
must use `env` naming convention | +| 🔐 Log redaction, leak detection! | 😢 No DMNO security features | +| 🙊 Bundled worker code is accessible within the Cloudflare UI | 🙈 Cloudflare secrets are never visible within the Cloudflare UI | +| 🛠️ Additional installation code | 🛠️ More complex deployment | + + +### Using `dwrangler` with the Workers Builds git integration +Cloudflare's new [Workers Builds (beta)](https://developers.cloudflare.com/workers/ci-cd/builds/) allows you to connect your worker to a git repo, and it will run the CI process within Cloudflare, similar to how it works for Pages. + +While this is great, it's not much different than running your CI anywhere else, and the same issues of injecting config into your running worker are present. + +To use `dmno` within Workers Builds, just swap your deploy command from `wrangler` to `dwrangler`, just like if you were running it anywhere else. There is also a section for setting "Build variables and secrets" where you can populate any additional config needed during the build. If you are using DMNO to fetch sensitive config from somehwere else, this is how you would pass in that _secret-zero_, for example, a service account token for 1password. + +:::caution[No preview deployments] +At this time, Workers Builds has no notion of deploy previews for PRs or branches. Your only option is to create another worker and point the build settings at a specific branch. +::: + + +## Cloudflare Pages + +Cloudflare pages allows you to host static sites and provides a little sugar on top of Cloudflare Workers for attached functions. Unfortunately, Wrangler's Pages functionality does not allow the same level of configuration of the underlying ESBuild process. However it may not matter, because in practice, most users are likely using an existing framework and have their own build process already. In this case, you can rely on our [drop-in integrations](/docs/integrations/overview/) to inject your DMNO config. + +:::tip[Just use workers!] +Workers support hosting [static assets](https://developers.cloudflare.com/workers/static-assets/) too and it seems that the Cloudflare team is working to make workers do everything that Pages can. Our hunch is that Pages will eventually be deprecated, and moving over to Workers will be a smart bet. +::: + +Regardless, you could still use DMNO to resolve your config and upload secrets to Cloudflare, but without a custom build, you would have to rely on `env` rather than `DMNO_CONFIG`, and you would not get all the benefits of DMNO. + +Please reach out if you need help setting things up, or have a use case that is not supported. + + +### Cloudflare Pages env vars + +The Cloudflare Pages environment injects a few [environment variables](https://docs.netlify.com/configure-builds/environment-variables/#read-only-variables) into its _build environment_ that provide information about the current build. This module exposes a pre-made config schema object which you can use in your own schema. You can use the `pickFromSchemaObject` utility to pick only the env var keys that you need from the full list. For example: + +```ts title='.dmno/config.mts' +import { defineDmnoService, switchBy, pickFromSchemaObject } from 'dmno'; +import { CloudflarePagesEnvSchema } from '@dmno/cloudflare-platform'; + +export default defineDmnoService({ + schema: { + ...pickFromSchemaObject(CloudflarePagesEnvSchema, 'CONTEXT', 'BUILD_ID'), + APP_ENV: { + value: switchBy('CONTEXT', { + _default: 'local', + 'deploy-preview': 'staging', + 'branch-deploy': 'staging', + production: 'production', + }), + }, + }, +}); +``` + + +## Other workflows +If you have a totally custom setup, that does not fit with the above workflows, you can still use `dwrangler` to manage Cloudflare auth, and push resolved config to Cloudflare however you want. + +Note that `wrangler` has several bulk secret related methods, and they all take JSON from stdin. + +- [`wrangler secret:bulk`](https://developers.cloudflare.com/workers/wrangler/commands/#secretbulk) +- [`wrangler versions secret bulk`](https://developers.cloudflare.com/workers/wrangler/commands/#secret-bulk-1) +- [`wrangler pages secret bulk`](https://developers.cloudflare.com/workers/wrangler/commands/#secret-bulk) + +For example: `dwrangler secrets:bulk < dmno resolve --format json` diff --git a/packages/docs-site/src/content/docs/docs/platforms/overview.mdx b/packages/docs-site/src/content/docs/docs/platforms/overview.mdx index acffa5a0..00a7c5df 100644 --- a/packages/docs-site/src/content/docs/docs/platforms/overview.mdx +++ b/packages/docs-site/src/content/docs/docs/platforms/overview.mdx @@ -10,6 +10,16 @@ In many cases, you may not need to do anything special, but our platform-specifi ![Platform config schema example](../../../../assets/docs-images/platform-intellisense-example.png) +### [Cloudflare](/docs/platforms/cloudflare/) + +Package: `@dmno/cloudflare-platform` + +Includes: +- Cloudflare-specific data types +- Config schema of env vars injected by the Cloudflare platform +- `dwrangler` cli - wrapper around `wrangler` to inject DMNO config + + ### [Netlify](/docs/platforms/netlify/) Package: `@dmno/netlify-platform` @@ -27,13 +37,11 @@ Includes: - Config schema of env vars injected by the Vercel platform - Vercel-specific data types - ### Next up We're already working on more platform integrations, but we'd love to hear from you which platforms to tackle next! On the roadmap: -- Cloudflare - Fly.io - Heroku - Render.com diff --git a/packages/integrations/astro/src/index.ts b/packages/integrations/astro/src/index.ts index 4cf82e6b..f6bc36c2 100644 --- a/packages/integrations/astro/src/index.ts +++ b/packages/integrations/astro/src/index.ts @@ -270,6 +270,7 @@ function dmnoAstroIntegration(dmnoIntegrationOpts?: DmnoAstroIntegrationOptions) ssrInjectConfigAtBuildTime = [ '@astrojs/netlify', '@astrojs/vercel/serverless', + '@astrojs/cloudflare', ].includes(opts.config.adapter?.name || ''); }, diff --git a/packages/integrations/nextjs/src/index.ts b/packages/integrations/nextjs/src/index.ts index 54530d8f..1516d299 100644 --- a/packages/integrations/nextjs/src/index.ts +++ b/packages/integrations/nextjs/src/index.ts @@ -63,7 +63,9 @@ export function dmnoNextConfigPlugin(dmnoOptions?: DmnoPluginOptions) { (process.env.__VERCEL_BUILD_RUNNING || process.env.VERCEL) // build running via `vercel` cli or on vercel || process.env.NETLIFY // build running remotely on netlify || (process.env.NETLIFY_LOCAL && !process.env.NETLIFY_DEV) // build running locally via `netlify` cli + || process.env.CF_PAGES // maybe add additional check for /functions folder? || dmnoOptions?.injectResolvedConfigAtBuildTime // explicit opt-in + ); // nextjs doesnt have a proper plugin system, so we write a function which takes in a config object and returns an augmented one diff --git a/packages/integrations/remix/src/index.ts b/packages/integrations/remix/src/index.ts index bbb0d234..064a5bf1 100644 --- a/packages/integrations/remix/src/index.ts +++ b/packages/integrations/remix/src/index.ts @@ -63,7 +63,9 @@ export function dmnoRemixVitePlugin(dmnoOptions?: DmnoPluginOptions) { process.env.__VERCEL_BUILD_RUNNING // build running via `vercel` cli || process.env.NETLIFY // build running remotely on netlify || (process.env.NETLIFY_LOCAL && !process.env.NETLIFY_DEV) // build running locally via `netlify` cli + || process.env.CF_PAGES // maybe add additional check for /functions folder? || dmnoOptions?.injectResolvedConfigAtBuildTime // explicit opt-in + ); return { diff --git a/packages/platforms/cloudflare/.eslintrc.cjs b/packages/platforms/cloudflare/.eslintrc.cjs new file mode 100644 index 00000000..8f2a36a2 --- /dev/null +++ b/packages/platforms/cloudflare/.eslintrc.cjs @@ -0,0 +1,6 @@ +module.exports = { + extends: ["@dmno/eslint-config/base"], + ignorePatterns: ["tsup.config.ts"], + rules: { + }, +}; diff --git a/packages/platforms/cloudflare/README.md b/packages/platforms/cloudflare/README.md new file mode 100644 index 00000000..6a12fd59 --- /dev/null +++ b/packages/platforms/cloudflare/README.md @@ -0,0 +1,9 @@ +Check out the [docs](https://dmno.dev/docs/platforms/cloudflare/) for more information on how to use [DMNO](https://dmno.dev) with [Cloudflare](https://cloudflare.com/). + +*** THIS IS PREVIEW SOFTWARE AND SUBJECT TO RAPID CHANGE *** + +If you have any questions, please reach out to us on [Discord](https://chat.dmno.dev). + +---- + +# @dmno/cloudflare-platform [![npm](https://img.shields.io/npm/v/@dmno/cloudflare-platform)](https://www.npmjs.com/package/@dmno/cloudflare-platform) diff --git a/packages/platforms/cloudflare/package.json b/packages/platforms/cloudflare/package.json new file mode 100644 index 00000000..9c2ee282 --- /dev/null +++ b/packages/platforms/cloudflare/package.json @@ -0,0 +1,70 @@ +{ + "name": "@dmno/cloudflare-platform", + "version": "0.0.0", + "description": "Tools and data-types to use DMNO with Cloudflare", + "author": "dmno-dev", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/dmno-dev/dmno.git", + "directory": "packages/platforms/cloudflare" + }, + "bugs": "https://github.com/dmno-dev/dmno/issues", + "homepage": "https://dmno.dev/docs/platforms/cloudflare", + "keywords": [ + "dmno", + "cloudflare", + "config", + "env vars", + "environment variables", + "secrets", + "dmno-plugin" + ], + "type": "module", + "exports": { + ".": { + "ts-src": "./src/index.js", + "default": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "bin": { + "dwrangler": "./dist/dwrangler.js" + }, + "files": [ + "/dist" + ], + "scripts": { + "build": "tsup", + "build:ifnodist": "[ -d \"./dist\" ] && echo 'dist exists' || pnpm build", + "build:tarball": "turbo build && pnpm pack --pack-destination \"../../../../tmp-package-registry\"", + "dev": "pnpm run build --watch", + "lint": "eslint src --ext .ts,.cjs,.mjs", + "lint:fix": "pnpm run lint --fix" + }, + "devDependencies": { + "@dmno/eslint-config": "workspace:*", + "@dmno/tsconfig": "workspace:*", + "@types/debug": "catalog:", + "@types/lodash-es": "catalog:", + "@types/node": "catalog:", + "dmno": "workspace:*", + "tsup": "catalog:", + "typescript": "catalog:" + }, + "dependencies": { + "debug": "catalog:", + "execa": "^9.5.1", + "lodash-es": "catalog:", + "nano-spawn": "^0.2.0" + }, + "peerDependencies": { + "dmno": "^0", + "wrangler": "^3" + }, + "peerDependenciesMeta": { + "wrangler": { + "optional": true + } + } +} diff --git a/packages/platforms/cloudflare/src/data-types.ts b/packages/platforms/cloudflare/src/data-types.ts new file mode 100644 index 00000000..19e33441 --- /dev/null +++ b/packages/platforms/cloudflare/src/data-types.ts @@ -0,0 +1,203 @@ +import { DmnoBaseTypes, createDmnoDataType, createVendorSchema } from 'dmno'; +import { GitDataTypes } from 'dmno/vendor-types'; + +function getCommonPagesTypeInfo(skipIcon = false) { + return { + externalDocs: { + url: 'https://developers.cloudflare.com/pages/configuration/build-configuration/#environment-variables', + description: 'Cloudflare Pages docs - env vars', + }, + ...!skipIcon && { + ui: { + icon: 'simple-icons:cloudflarepages', + color: 'F38020', // cloudflare brand + }, + }, + }; +} + +export const CloudflarePagesEnvSchema = createVendorSchema({ + CF_PAGES: { + extends: DmnoBaseTypes.boolean(), + typeDescription: 'Flag to detect the build is running on Cloudflare Pages rather than locally', + ...getCommonPagesTypeInfo(), + }, + CF_PAGES_BRANCH: { + extends: GitDataTypes.BranchName, + description: 'unique ID for the Cloudflare Pages commit', + ...getCommonPagesTypeInfo(), + }, + CF_PAGES_COMMIT_SHA: { + extends: GitDataTypes.CommitSha, + description: 'sha hash of the current Cloudflare Pages commit', + ...getCommonPagesTypeInfo(), + }, + CF_PAGES_URL: { + extends: 'url', + description: 'URL of the current Cloudflare Pages site', + ...getCommonPagesTypeInfo(), + }, +}, { fromVendor: 'cloudflare-pages' }); + +function getCommonWranglerTypeInfo(skipIcon = false) { + return { + externalDocs: { + url: 'https://developers.cloudflare.com/workers/wrangler/system-environment-variables/', + description: 'Cloudflare Wrangler env var docs', + }, + ...!skipIcon && { + ui: { + icon: 'simple-icons:cloudflare', + color: 'F38020', // cloudflare brand + }, + }, + }; +} + +export const CloudflareWranglerDataTypes = { + AccountId: createDmnoDataType({ + extends: DmnoBaseTypes.string(), + typeLabel: 'cloudflare-wrangler/account-id', + typeDescription: 'The account ID for the Workers related account.', + includeInDmnoConfig: false, + ...getCommonWranglerTypeInfo(), + }), + ApiToken: createDmnoDataType({ + extends: DmnoBaseTypes.string(), + typeLabel: 'cloudflare-wrangler/api-token', + typeDescription: 'The API token for your Cloudflare account, can be used for authentication for situations like CI/CD, and other automation.', + sensitive: true, + includeInDmnoConfig: false, + ...getCommonWranglerTypeInfo(), + }), + ApiKey: createDmnoDataType({ + extends: DmnoBaseTypes.string(), + typeLabel: 'cloudflare-wrangler/api-key', + typeDescription: 'The API key for your Cloudflare account, usually used for older authentication method with `CLOUDFLARE_EMAIL`.', + sensitive: true, + includeInDmnoConfig: false, + ...getCommonWranglerTypeInfo(), + }), + HyperDriveLocalConnectionString: createDmnoDataType({ + extends: DmnoBaseTypes.string(), + typeLabel: 'cloudflare-wrangler/hyperdrive-local-connection-string', + typeDescription: 'The local connection string for your database to use in local development with Hyperdrive. For example, if the binding for your Hyperdrive is named PROD_DB, this would be WRANGLER_HYPERDRIVE_LOCAL_CONNECTION_STRING_PROD_DB="postgres://user:password@127.0.0.1:5432/testdb". Each Hyperdrive is uniquely distinguished by the binding name.', + exampleValue: 'postgres://user:password@127.0.0.1:5432/testdb', + sensitive: true, + includeInDmnoConfig: false, + ...getCommonWranglerTypeInfo(), + }), +}; + +export const CloudflareWranglerEnvSchema = createVendorSchema({ + CLOUDFLARE_ACCOUNT_ID: CloudflareWranglerDataTypes.AccountId, + CLOUDFLARE_API_TOKEN: CloudflareWranglerDataTypes.ApiToken, + CLOUDFLARE_API_KEY: CloudflareWranglerDataTypes.ApiKey, + CLOUDFLARE_EMAIL: { + extends: DmnoBaseTypes.email(), + description: 'The email address associated with your Cloudflare account, usually used for older authentication method with `CLOUDFLARE_API_KEY`.', + ...getCommonWranglerTypeInfo(), + }, + CLOUDFLARE_SEND_METRICS: { + // CF says this is a string, but it's actually a boolean + extends: DmnoBaseTypes.boolean(), + description: 'Whether to send anonymous usage metrics to Cloudflare.', + ...getCommonWranglerTypeInfo(), + }, + CLOUDFLARE_API_BASE_URL: { + extends: DmnoBaseTypes.url(), + description: 'The base URL for the Cloudflare API.', + exampleValue: 'https://api.cloudflare.com/client/v4', + ...getCommonWranglerTypeInfo(), + }, + WRANGLER_LOG: { + // TODO: this could be a non-cloudflare specific reusable type? + extends: DmnoBaseTypes.enum(['none', 'error', 'warn', 'info', 'log', 'debug']), + coerce: (val) => val.toString().toLowerCase(), + typeDescription: 'controls what level of logs should be shown', + description: 'If an invalid level is specified, Wrangler will fallback to the default (log)', + ...getCommonWranglerTypeInfo(), + }, + FORCE_COLOR: { + // TODO: need boolean number type + extends: DmnoBaseTypes.enum([0, 1]), + description: 'By setting this to 0, you can disable Wrangler\'s colorised output, which makes it easier to read with some terminal setups', + ...getCommonWranglerTypeInfo(), + }, +}, { fromVendor: 'cloudflare-wrangler' }); + + +/** + * These settings are related to `dwrangler`, the DMNO wrangler wrapper + */ +export const DmnoWranglerEnvSchema = createVendorSchema({ + WRANGLER_ENV: { + description: 'sets value of wrangler --env flag', + // can do something special here, where dwrangler will populate it for us + }, + WRANGLER_DEV_IP: { + description: 'IP/host for wrangler dev to listen on (defaults to localhost)', + extends: 'string', // TODO: add a proper host type + value: 'localhost', + }, + WRANGLER_DEV_PORT: { + description: 'Port for wrangler dev to listen on (defaults to 8787)', + extends: DmnoBaseTypes.port(), + value: 8787, + }, + WRANGLER_DEV_PROTOCOL: { + description: 'Protocol to listen to requests on (defaults to http)', + extends: DmnoBaseTypes.enum(['http', 'https']), + value: 'http', + }, + WRANGLER_DEV_URL: { + description: 'Full url of local wrangler dev server', + extends: 'url', + // TODO: this would be a good one to mark as NOT overrideable? (flag does not exist yet) + // override: false, + value: (ctx) => { + return [ + ctx.get('WRANGLER_DEV_PROTOCOL', { allowMissing: true }) || 'http', + '://', + ctx.get('WRANGLER_DEV_IP', { allowMissing: true }) || 'localhost', + ':', + ctx.get('WRANGLER_DEV_PORT', { allowMissing: true }) || 8787, + ].join(''); + }, + }, + WRANGLER_LIVE_RELOAD: { + extends: DmnoBaseTypes.boolean, + description: 'enable wrangler live-reload without needing to pass in a --live-reload flag', + }, + + WRANGLER_INJECT_MODE: { + extends: DmnoBaseTypes.enum({ + inline: { description: 'inlines entire resolved config into built files' }, + secrets: { description: 'sets config as secrets on Cloudflare' }, + }), + value: 'inline', + description: 'Controls how _dynamic_ config values are injected into your cloudflare workers', + externalDocs: { + description: 'DMNO docs - Wrangler platform plugin', + url: 'https://dmno.dev/docs/platforms/cloudflare/', // TODO: add link to specific section? + }, + includeInDmnoConfig: false, + }, + WRANGLER_DEV_ACTIVE: { + extends: DmnoBaseTypes.boolean, + description: 'Flag to detect if wrangler is running in dev mode or not', + externalDocs: { + description: 'DMNO docs - Wrangler platform plugin', + url: 'https://dmno.dev/docs/platforms/cloudflare/', // TODO: add link to specific section? + }, + }, + WRANGLER_BUILD_ACTIVE: { + extends: DmnoBaseTypes.boolean, + description: 'Flag to detect if wrangler is performing a build', + externalDocs: { + description: 'DMNO docs - Wrangler platform plugin', + url: 'https://dmno.dev/docs/platforms/cloudflare/', // TODO: add link to specific section? + }, + }, + +}); diff --git a/packages/platforms/cloudflare/src/dwrangler.ts b/packages/platforms/cloudflare/src/dwrangler.ts new file mode 100644 index 00000000..5dd925e7 --- /dev/null +++ b/packages/platforms/cloudflare/src/dwrangler.ts @@ -0,0 +1,340 @@ +/** + * small wrapper around the wrangler cli to help inject DMNO config + * ideally we could inject using other methods, + * - we dont want to write resolved config to local temp files + * - using xargs in complex scenarios proved very awkward + * - making users change their scripts to long complicated things isn't great + */ + +import { + ChildProcess, spawn, +} from 'node:child_process'; +import { execa } from 'execa'; +import { checkServiceIsValid, DmnoServer, InjectedDmnoEnv } from 'dmno'; +import Debug from 'debug'; +import { CloudflareWranglerEnvSchema, DmnoWranglerEnvSchema } from './data-types'; + +const debug = Debug('dmno:dwrangler'); + +const args = process.argv.slice(2); +let wranglerCommand = args[0]; +if (['versions', 'pages'].includes(wranglerCommand)) wranglerCommand += ` ${args[1]}`; + +// TODO: more bulletproof way of finding this +const wranglerBinPath = './node_modules/.bin/wrangler'; + +const isDevMode = ['dev', 'pages dev'].includes(wranglerCommand); +const isBuildMode = ['deploy', 'versions upload'].includes(wranglerCommand); + +let injectedDmnoEnv: InjectedDmnoEnv; +let injectedProcessEnv = {} as Record; +let injectedValues = {} as Record; +let injectedDynamicValues = {} as Record; +let dmnoHasTriggeredReload = false; +let dmnoConfigValid = true; +let wranglerSystemEnv: Record = {}; +let dmnoWranglerSettings: Record = {}; +const staticReplacements = { + dmnoConfig: {} as Record, + dmnoPublicConfig: {} as Record, +}; + +const wranglerSystemEnvKeys = Object.keys(CloudflareWranglerEnvSchema); +const dmnoWranglerEnvKeys = Object.keys(DmnoWranglerEnvSchema); + +// pass back some info about the current run into the dmno config +// TODO: ideally we'd pass this in as overrides explcitly with a different source rather than using process.env +process.env.WRANGLER_DEV_ACTIVE = isDevMode ? 'true' : 'false'; +process.env.WRANGLER_BUILD_ACTIVE = isBuildMode ? 'true' : 'false'; + +const dmnoServer = new DmnoServer({ watch: isDevMode }); + +async function reloadDmnoConfig() { + const resolvedService = await dmnoServer.getCurrentPackageConfig(); + injectedProcessEnv = resolvedService.injectedProcessEnv; + injectedDmnoEnv = resolvedService.injectedDmnoEnv; + dmnoConfigValid = resolvedService.serviceDetails.isValid; + + // shows nicely formatted errors in the terminal + checkServiceIsValid(resolvedService.serviceDetails); + + staticReplacements.dmnoConfig = {}; + staticReplacements.dmnoPublicConfig = {}; + + wranglerSystemEnv = {}; + dmnoWranglerSettings = {}; + injectedValues = {}; + injectedDynamicValues = {}; + + for (const itemKey in injectedDmnoEnv) { + if (itemKey.startsWith('$')) continue; + + const injectedItem = injectedDmnoEnv[itemKey]; + const val = injectedItem.value; + + // some config is to affect wrangler itself + if ( + wranglerSystemEnvKeys.includes(itemKey) + // users can pass in multiple bindings that will have different keys + || itemKey.startsWith('WRANGLER_HYPERDRIVE_LOCAL_CONNECTION_STRING_') + ) { + wranglerSystemEnv[itemKey] = val; + } else if (dmnoWranglerEnvKeys.includes(itemKey)) { + dmnoWranglerSettings[itemKey] = val; + } + + // set up static build-time replacements + if (!injectedItem.dynamic) { + if (!injectedItem.sensitive) { + staticReplacements.dmnoPublicConfig[`DMNO_PUBLIC_CONFIG.${itemKey}`] = JSON.stringify(injectedItem.value); + } + staticReplacements.dmnoConfig[`DMNO_CONFIG.${itemKey}`] = JSON.stringify(injectedItem.value); + } + + injectedValues[itemKey] = val; + } +} + +let wranglerProcess: ChildProcess | undefined; + +if (isDevMode) { + dmnoServer.enableWatchMode(async () => { + console.log('reload!!!'); + dmnoHasTriggeredReload = true; + if (wranglerProcess) { + // wranglerProcess.kill(15); + wranglerProcess.kill(2); + } + await restartWrangler(); + }); +} + + +let exitCode; +async function restartWrangler() { + await reloadDmnoConfig(); + if (!dmnoConfigValid) { + if (isDevMode) { + console.log('watching for config changes...'); + return; + } else { + process.exit(1); + } + } + + + const injectMode: 'inline' | 'secrets' = dmnoWranglerSettings.DWRANGLER_INJECT_MODE as any || 'inline'; + + const wranglerBuildArgs = []; + const wranglerDevArgs = []; + + // some commands trigger a build, and we need to inject config + // this would be easier if wrangler had a .js config option and allowed plugins... + if (['dev', 'deploy', 'versions upload'].includes(wranglerCommand)) { + // all "static" config will be replaced at build time (using --define) + + // for "dynamic" config, we have 2 methods of injecting - vars or define + + // users can toggle between these modes by setting DWRANGLER_INJECT_MODE in their config.mts + + // in "define" mode, we inline the entire resolved config with additional metadata into the build code + // this is necessary for our global patching stuff to work, and it also enables users to use config + // outside of request handlers. Generally it's a lot simpler, but the idea of injecting at build time + // and using globals may be offputting to some - although it is safe! + if (injectMode === 'inline') { + debug('injecting dynamic config using define'); + wranglerBuildArgs.push('--define', `__DMNO_INJECTED_CONFIG__:${JSON.stringify(injectedDmnoEnv)}`); + + + // in "vars" mode, we do things the cloudflare way, and inject config as cloudflare "secrets" + // during local dev, we use --var to inject, and to deploy we must do a multi-step versioned deploy + // we also replace `DMNO_CONFIG.X` with `env.X` for convenience and type safety + // this does require that users use the conventional `env` name for their handler + // TODO: they will actually all be strings, so we need to figure out what to do about that + // the upside is we are doing things the cloudflare way, the downsides are + // - no security features (log redaction, leak detection) + // - everythign is a string + // - only able to use config within request handlers + // - more complicated deployment workflow + } else if (injectMode === 'secrets') { + debug('injecting dynamic config as secrets'); + for (const key in injectedValues) { + // we don't need to inject any static items, as they will be replaced + if (injectedDmnoEnv[key].dynamic) { + injectedDynamicValues[key] = injectedValues[key]; + // during local dev, we use --var to inject the dynamic env vars + // but if we do that during deploy, it will treat them as "text" instead of "secret" + if (wranglerCommand === 'dev') { + wranglerBuildArgs.push('--var', `${key}:${injectedValues[key]}`); + } + wranglerBuildArgs.push('--define', `DMNO_CONFIG.${key}:env.${key}`); + } + } + } + // always inject static items using --define + const allReplacements = { ...staticReplacements.dmnoConfig, ...staticReplacements.dmnoPublicConfig }; + console.log(allReplacements); + for (const k in allReplacements) { + wranglerBuildArgs.push('--define', `${k}:${allReplacements[k]}`); + } + } + + // apply some config items as args for wrangler dev + // this is useful because we can manage it in our config, and we can reuse that data in our config + if (isDevMode) { + if (dmnoWranglerSettings.WRANGLER_DEV_PORT) { + wranglerDevArgs.push('--port', dmnoWranglerSettings.WRANGLER_DEV_PORT); + } + if (dmnoWranglerSettings.WRANGLER_DEV_IP) { + wranglerDevArgs.push('--ip', dmnoWranglerSettings.WRANGLER_DEV_IP); + } + if (dmnoWranglerSettings.WRANGLER_LIVE_RELOAD) { + wranglerDevArgs.push('--live-reload'); + } + if (dmnoWranglerSettings.WRANGLER_ENV) { + wranglerDevArgs.push('--env', dmnoWranglerSettings.WRANGLER_ENV); + } + } + + // if we are deploying and using vars, we need to actually do a multi-step deployment: + // - create new version + // - set bulk secrets for that new version + // - deploy the version + if (wranglerCommand === 'deploy' && injectMode === 'secrets' && !args.includes('--dry-run')) { + console.log('dwrangler will run a multi-step deployment for you'); + + console.log('1 - CREATING NEW VERSION'); + + + const multiStepDeployEnv = { + FORCE_COLOR: 'true', + ...process.env, + ...wranglerSystemEnv, + // ...injectedProcessEnv // might want to inject all of injectedProcessEnv instead? + }; + + try { + console.log('> STEP 1: BUILD AND CREATE NEW PENDING VERSION'); + const { stdout: versionsUploadOutput } = await execa( + 'wrangler', + [ + 'versions', 'upload', + '--message', 'dmno dwrangler create deployment (secrets not yet set)', + ...wranglerBuildArgs, + ], + { + stdout: ['pipe', 'inherit'], + env: multiStepDeployEnv, + }, + ); + + const versionId = versionsUploadOutput.match(/Worker Version ID: ([^\s]+)\n/)?.[1]; + // const previewUrl = versionsUploadOutput.match(/Version Preview URL: ([^\s]+)\n/)?.[1]; + if (!versionId) throw new Error('Did not create new version successfully'); + + console.log('> STEP 2: ATTACH SECRETS TO PENDING VERSION'); + const { stdout: secretsOutput } = await execa( + 'wrangler', + [ + 'versions', 'secret', 'bulk', + '--message', 'dmno dwrangler set bulk secrets', + ], + { + stdout: ['pipe', 'inherit'], + env: multiStepDeployEnv, + input: JSON.stringify(injectedDynamicValues), + }, + ); + + const versionIdWithSecrets = secretsOutput.match(/Created version ([^\s]+)\s/)?.[1]; + if (!versionId) throw new Error('Did not deploy version with secrets successfully'); + + console.log('> STEP 3: ACTIVATE NEW VERSION'); + const { stdout: deployOutput } = await execa( + 'wrangler', + [ + 'versions', 'deploy', + `${versionIdWithSecrets}@100%`, + '--yes', + ], + { + stdout: ['pipe', 'inherit'], + env: multiStepDeployEnv, + }, + ); + } catch (err) { + console.log(err); + } + } else { + wranglerProcess = spawn( + // TODO: how do we want to find the path to wrangler? + wranglerBinPath, + [...args, ...wranglerBuildArgs, ...wranglerDevArgs], + { + stdio: 'inherit', + env: { + ...process.env, + ...wranglerSystemEnv, + }, + }, + ); + + + // wranglerProcess?.stdout?.on('data', (data) => { + // console.log('stdoutt!', data); + // }); + // wranglerProcess?.stderr?.on('data', (data) => { + // console.log('stderr!', data); + // }); + // wranglerProcess?.stderr?.on('error', (data) => { + // console.log('error!', data); + // }); + wranglerProcess.on('exit', (code, signal) => { + console.log('wranglerProcess exit', { code, signal }); + // we are seeing wrangler exit code 0 and no signal both when + // the user hits CTRL+C and when we restart the process + // so we have to track whether we restarted it and ignore the close signal we get right after + exitCode = code ?? 0; + if (exitCode === 0) { + if (dmnoHasTriggeredReload) { + dmnoHasTriggeredReload = false; + } else { + process.exit(0); + } + } else { + process.exit(exitCode); + } + }); + // wranglerProcess.on('close', () => { + // console.log('wrangler process - close'); + // }); + // wranglerProcess.on('disconnect', () => { + // console.log('wrangler process - disconnect'); + // }); + // wranglerProcess.on('error', () => { + // console.log('wrangler process - error'); + // }); + // wranglerProcess.on('message', () => { + // console.log('wrangler process - message'); + // }); + } +} + +// if first run, we need to attach some extra exit handling + +// try to make sure we shut down cleanly and kill the child process +process.on('exit', (code: any, signal: any) => { + debug('dwrangler exit!'); + wranglerProcess?.kill(9); +}); + +['SIGTERM', 'SIGINT'].forEach((signal) => { + process.on(signal, () => { + debug('dwrangler received signal', signal); + wranglerProcess?.kill(9); + process.exit(1); + }); +}); +// TODO: handle other signals? + +await restartWrangler(); diff --git a/packages/platforms/cloudflare/src/index.ts b/packages/platforms/cloudflare/src/index.ts new file mode 100644 index 00000000..ea98209e --- /dev/null +++ b/packages/platforms/cloudflare/src/index.ts @@ -0,0 +1 @@ +export * from './data-types'; diff --git a/packages/platforms/cloudflare/tsconfig.json b/packages/platforms/cloudflare/tsconfig.json new file mode 100644 index 00000000..8572ecc4 --- /dev/null +++ b/packages/platforms/cloudflare/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "@dmno/tsconfig/tsconfig.node.json", + "compilerOptions": { + "rootDir": ".", + "module": "ESNext", + "outDir": "dist", + "lib": [ + "ESNext" + ], + }, + "include": [ + "src/**/*.ts", + "src/**/*.d.ts", + ] +} diff --git a/packages/platforms/cloudflare/tsup.config.ts b/packages/platforms/cloudflare/tsup.config.ts new file mode 100644 index 00000000..3b17a6a8 --- /dev/null +++ b/packages/platforms/cloudflare/tsup.config.ts @@ -0,0 +1,21 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: [ // Entry point(s) + 'src/index.ts', // preset + data types + 'src/dwrangler.ts', + ], + + dts: true, // Generate .d.ts files + // minify: true, // Minify output + sourcemap: true, // Generate sourcemaps + treeshake: true, // Remove unused code + + clean: true, // Clean output directory before building + outDir: "dist", // Output directory + + format: ['esm'], // Output format(s) + + splitting: true, // split output into chunks - MUST BE ON! or we get issues with multiple copies of classes and instanceof + keepNames: true, // stops build from prefixing our class names with `_` in some cases +}); diff --git a/packages/signup-api/.dmno/config.mts b/packages/signup-api/.dmno/config.mts index ef26287c..32c56634 100644 --- a/packages/signup-api/.dmno/config.mts +++ b/packages/signup-api/.dmno/config.mts @@ -1,15 +1,31 @@ -import { DmnoBaseTypes, defineDmnoService, configPath, switchBy } from 'dmno'; +import { DmnoBaseTypes, defineDmnoService, configPath, switchBy, pickFromSchemaObject } from 'dmno'; +import { CloudflareWranglerEnvSchema, DmnoWranglerEnvSchema } from '@dmno/cloudflare-platform'; import { EncryptedVaultDmnoPlugin, EncryptedVaultTypes } from '@dmno/encrypted-vault-plugin'; +import { OnePasswordDmnoPlugin } from '@dmno/1password-plugin'; const EncryptedVault = new EncryptedVaultDmnoPlugin('vault', { key: configPath('..', 'DMNO_VAULT_KEY'), name: 'prod', }); +const onepass = new OnePasswordDmnoPlugin('1pass', { + fallbackToCliBasedAuth: true +}); + + export default defineDmnoService({ name: 'signup-api', pick: [], schema: { + ...pickFromSchemaObject(CloudflareWranglerEnvSchema, { + CLOUDFLARE_ACCOUNT_ID: { + value: onepass.itemByReference("op://Shared/Cloudflare/account id"), + }, + CLOUDFLARE_API_TOKEN: { + value: onepass.itemByReference("op://Shared/Cloudflare/workers api token"), + }, + }), + DMNO_ENV: { // TODO: formalize this // will be overridden by netlify during deploys - see netlify.toml value: 'development', diff --git a/packages/signup-api/.gitignore b/packages/signup-api/.gitignore index 6e93a6ac..d3b5916f 100644 --- a/packages/signup-api/.gitignore +++ b/packages/signup-api/.gitignore @@ -1,4 +1,7 @@ functions-dist +functions/*.js +functions/*.map +.wrangler # netlify deploy fails if it doesnt have a non-functions folder to deploy # so we put a folder with a single dummy txt file in it... !dist diff --git a/packages/signup-api/src/dmno-env.d.ts b/packages/signup-api/functions-src/dmno-env.d.ts similarity index 100% rename from packages/signup-api/src/dmno-env.d.ts rename to packages/signup-api/functions-src/dmno-env.d.ts diff --git a/packages/signup-api/functions-src/lib/mailerlite.ts b/packages/signup-api/functions-src/lib/mailerlite.ts new file mode 100644 index 00000000..594e0649 --- /dev/null +++ b/packages/signup-api/functions-src/lib/mailerlite.ts @@ -0,0 +1,42 @@ +const MAILERLITE_API_URL = 'https://connect.mailerlite.com/api'; +const MAILERLITE_TOKEN = DMNO_CONFIG.MAILERLITE_TOKEN; + +async function createSubscriber(signup: { + email: string, + email_opt_in?: boolean, + signup_date: string, // iso date string + user_study_opt_in?: boolean, + ip?: string, + source?: string, +}) { + try { + const response = await fetch(`${MAILERLITE_API_URL}/subscribers`, { + method: 'POST', + headers: { + Authorization: `Bearer ${MAILERLITE_TOKEN}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + body: JSON.stringify({ + email: signup.email, + fields: { + ...signup.user_study_opt_in && { user_study_opt_in: 1 }, + dmno_source: signup.source, + }, + ip_address: signup.ip, + ...signup.email_opt_in && { + opted_in_at: signup.signup_date.replace('T', ' ').replace(/\..*Z/, ''), + optin_ip: signup.ip, + }, + groups: [DMNO_CONFIG.MAILERLITE_GROUP_ID], + }), + }); + return response.json(); + } catch (err) { + throw new Error(`MailerLite API error: ${err}`); + } +} + +export default { + createSubscriber, +}; diff --git a/packages/signup-api/functions-src/signup.ts b/packages/signup-api/functions-src/signup.ts new file mode 100644 index 00000000..fcbcdcba --- /dev/null +++ b/packages/signup-api/functions-src/signup.ts @@ -0,0 +1,56 @@ +import EmailValidation from 'emailvalid'; +import { PagesFunction, type Response as CFResponse } from '@cloudflare/workers-types'; +import mailerlite from './lib/mailerlite'; + +const ev = new EmailValidation({ allowFreemail: true }); + +export const onRequestOptions: PagesFunction = async () => { + return new Response(null, { + status: 204, + }) as unknown as CFResponse; +}; + +// Set CORS to all responses +export const onRequest: PagesFunction = async (context) => { + const response = await context.next(); + response.headers.set('Access-Control-Allow-Origin', '*'); + response.headers.set('Access-Control-Allow-Headers', '*'); + response.headers.set('Access-Control-Allow-Methods', '*'); + response.headers.set('Access-Control-Max-Age', '86400'); + return response; +}; + + + +export const onRequestPost: PagesFunction = async (context) => { + // TODO: type reqBody + let reqBody: any; + try { + reqBody = await context.request.json(); + } catch (err: any) { + return new Response(`Error parsing JSON body - ${err.message}`, { status: 400 }) as unknown as CFResponse; + } + + if (!reqBody.email) return new Response('email is missing', { status: 400 }) as unknown as CFResponse; + const checkEmail = ev.check(reqBody.email); + if (!checkEmail.valid) return new Response('email is invalid', { status: 400 }) as unknown as CFResponse; + + const clientIp = context.request.headers.get('cf-connecting-ip'); + + const signupObj = { + email: reqBody.email, + signup_date: new Date().toISOString(), + email_opt_in: !!reqBody.emailOptIn, + user_study_opt_in: !!reqBody.userStudyOptIn, + ip: clientIp ?? undefined, + source: reqBody.source, + }; + + try { + await mailerlite.createSubscriber(signupObj); + } catch (err: any) { + return new Response(`Error creating subscriber - ${err.message}`, { status: 500 }) as unknown as CFResponse; + } + + return new Response('ok', { status: 200 }) as unknown as CFResponse; +}; diff --git a/packages/signup-api/functions-src/types/emailvalid.d.ts b/packages/signup-api/functions-src/types/emailvalid.d.ts new file mode 100644 index 00000000..aca54c7b --- /dev/null +++ b/packages/signup-api/functions-src/types/emailvalid.d.ts @@ -0,0 +1,24 @@ +declare module 'emailvalid' { + interface EmailValidationOptions { + allowFreemail?: boolean; + whitelist?: Array; + blacklist?: Array; + allowDisposable?: boolean; + } + + interface ValidationResult { + email: string; + domain: string; + valid: boolean; + errors: Array; + typo: string; + } + + export default class EmailValidation { + constructor(options?: EmailValidationOptions); + check(email: string): ValidationResult; + blacklist(email: string): void; + whitelist(email: string): void; + setOptions(options: EmailValidationOptions): void; + } +} diff --git a/packages/signup-api/functions/README.md b/packages/signup-api/functions/README.md new file mode 100644 index 00000000..73148318 --- /dev/null +++ b/packages/signup-api/functions/README.md @@ -0,0 +1 @@ +We need this folder to exist. diff --git a/packages/signup-api/netlify.toml b/packages/signup-api/netlify.toml deleted file mode 100644 index 55a7ab39..00000000 --- a/packages/signup-api/netlify.toml +++ /dev/null @@ -1,24 +0,0 @@ -[build] -ignore = "git diff --quiet $COMMIT_REF $CACHED_COMMIT_REF -- packages/signup-api" -command = "pnpm --filter \"@dmno/signup-api\" run build" -publish = "packages/signup-api/dist" - -[functions] -directory = "packages/signup-api/functions-dist" - -[dev] -autoLaunch = false - -[[redirects]] -from = "/*" -to = "/.netlify/functions/:splat" -status = 200 - - -# setting NODE_ENV to production will make netlify not install dev dependencies -# which we need for the build... -[context.production.environment] -DMNO_ENV = "production" - -[context.deploy-preview.environment] -DMNO_ENV = "staging" diff --git a/packages/signup-api/package.json b/packages/signup-api/package.json index 76bfe9d7..ed080184 100644 --- a/packages/signup-api/package.json +++ b/packages/signup-api/package.json @@ -7,6 +7,7 @@ "dev": "pnpm --filter=\"@dmno/signup-api\" run --parallel \"/dev:*/\" ", "dev:ts": "dmno run -w -- pnpm run build --watch", "dev:netlify": "netlify dev", + "deploy": "dwrangler pages deploy", "lint": "eslint src --ext .ts,.cjs,.astro,.md,.mdx", "lint:fix": "pnpm run lint --fix" }, @@ -19,7 +20,10 @@ "google-spreadsheet": "^4.1.1" }, "devDependencies": { + "@cloudflare/workers-types": "^4.20241112.0", + "@dmno/cloudflare-platform": "workspace:*", "@dmno/encrypted-vault-plugin": "workspace:*", + "@dmno/1password-plugin": "workspace:*", "@dmno/eslint-config": "workspace:*", "@dmno/tsconfig": "workspace:*", "@netlify/functions": "^2.6.3", @@ -27,6 +31,7 @@ "dmno": "workspace:*", "netlify-cli": "^17.1.0", "tsup": "catalog:", - "typescript": "catalog:" + "typescript": "catalog:", + "wrangler": "^3.96.0" } } diff --git a/packages/signup-api/public/_routes.json b/packages/signup-api/public/_routes.json new file mode 100644 index 00000000..271c75c4 --- /dev/null +++ b/packages/signup-api/public/_routes.json @@ -0,0 +1,5 @@ +{ + "version": 1, + "include": [ "/signup" ], + "exclude": [ ] +} diff --git a/packages/signup-api/src/endpoints/signup.ts b/packages/signup-api/src/endpoints/signup.ts deleted file mode 100644 index 797beb97..00000000 --- a/packages/signup-api/src/endpoints/signup.ts +++ /dev/null @@ -1,86 +0,0 @@ -import EmailValidation from 'emailvalid'; -import * as async from 'async'; -import { GoogleSpreadsheet } from 'google-spreadsheet'; -import { JWT } from 'google-auth-library'; -import mailerlite from 'src/lib/mailerlite'; -import type { Context } from '@netlify/functions'; - -const ev = new EmailValidation({ allowFreemail: true }); - - -const CORS_HEADERS = { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': '*', - 'Access-Control-Allow-Methods': '*', -}; - -function errorResponse(message: string, statusCode = 400) { - return Response.json({ - message, - }, { status: statusCode, headers: CORS_HEADERS }); -} -function validResponse(obj: any) { - return Response.json(obj, { - headers: CORS_HEADERS, - }); -} - - -const serviceAccountAuth = new JWT({ - email: DMNO_CONFIG.GOOGLE_SHEETS_ACCOUNT_EMAIL, - key: DMNO_CONFIG.GOOGLE_SHEETS_ACCOUNT_KEY.replaceAll('\\n', '\n'), - scopes: [ - 'https://www.googleapis.com/auth/spreadsheets', - ], -}); -const signupsDoc = new GoogleSpreadsheet(DMNO_CONFIG.SIGNUPS_GOOGLE_SHEET_ID, serviceAccountAuth); - -export default async (req: Request, context: Context) => { - // TODO: extract this to helper... set more strict in prod - if (req.method === 'OPTIONS') { - return new Response(undefined, { headers: CORS_HEADERS }); - } - - if (req.method !== 'POST') { - return errorResponse('only POST supported'); - } - - let reqBody; - try { - reqBody = await req.json(); - } catch (err) { - return errorResponse(`Error parsing JSON body - ${err.message}`); - } - - if (!reqBody.email) return errorResponse('email is missing'); - const checkEmail = ev.check(reqBody.email); - if (!checkEmail.valid) return errorResponse('email is invalid'); - - // set by netlify in deployed envs - const clientIp = req.headers.get('x-nf-client-connection-ip'); - - const signupObj = { - email: reqBody.email, - signup_date: new Date().toISOString(), - email_opt_in: !!reqBody.emailOptIn, - user_study_opt_in: !!reqBody.userStudyOptIn, - ip: clientIp, - source: reqBody.source, - }; - - await async.parallel([ - async () => await mailerlite.createSubscriber(signupObj), - async () => { - try { - // push signup to google sheets - await signupsDoc.loadInfo(); - await signupsDoc.sheetsByIndex[0].addRow(signupObj); - } catch (err) { - console.log('Failed writing data to google sheet', signupObj); - console.log(err); - } - }, - ]); - - return validResponse({ success: true }); -}; diff --git a/packages/signup-api/src/lib/mailerlite.ts b/packages/signup-api/src/lib/mailerlite.ts deleted file mode 100644 index e5aab0c3..00000000 --- a/packages/signup-api/src/lib/mailerlite.ts +++ /dev/null @@ -1,45 +0,0 @@ -import Axios from 'axios'; - -const mailerLiteApi = Axios.create({ - baseURL: 'https://connect.mailerlite.com/api', - headers: { - Authorization: `Bearer ${DMNO_CONFIG.MAILERLITE_TOKEN}`, - 'Content-Type': 'application/json', - Accept: 'application/json', - }, -}); - - - -async function createSubscriber(signup: { - email: string, - email_opt_in?: boolean, - signup_date: string, // iso date string - user_study_opt_in?: boolean, - ip?: string, - source?: string, -}) { - try { - const result = await mailerLiteApi.post('/subscribers', { - email: signup.email, - fields: { - ...signup.user_study_opt_in && { user_study_opt_in: 1 }, - dmno_source: signup.source, - }, - ip_address: signup.ip, - ...signup.email_opt_in && { - opted_in_at: signup.signup_date.replace('T', ' ').replace(/\..*Z/, ''), - optin_ip: signup.ip, - }, - groups: [DMNO_CONFIG.MAILERLITE_GROUP_ID], - }); - - console.log('ML result', result.data); - } catch (err) { - console.log(err); - } -} - -export default { - createSubscriber, -}; diff --git a/packages/signup-api/tsconfig.json b/packages/signup-api/tsconfig.json index 82fa49f1..54d955e5 100644 --- a/packages/signup-api/tsconfig.json +++ b/packages/signup-api/tsconfig.json @@ -2,9 +2,12 @@ "extends": "dmno/tsconfigs/dmno-folder", "include": [ "src/**/*.ts", + "functions-src/**/*.ts" ], "compilerOptions": { - "lib": [ "ES2023" ], + "target": "esnext", + "module": "esnext", + "lib": [ "esnext" ], "baseUrl": ".", "paths": { "@/components/*": [ @@ -12,6 +15,10 @@ ], "@/utils/*": [ "src/utils/*" + ], + "@/types/*": [ + "src/types/*", + "@cloudflare/workers-types" ] } } diff --git a/packages/signup-api/tsup.config.ts b/packages/signup-api/tsup.config.ts index 4061ee2b..5a1f8cac 100644 --- a/packages/signup-api/tsup.config.ts +++ b/packages/signup-api/tsup.config.ts @@ -21,13 +21,13 @@ for (const itemKey in parsedLoadedEnv) { export default defineConfig({ // Entry points entry: [ - 'src/endpoints/signup.ts', + 'functions-src/signup.ts', ], sourcemap: true, // Generate sourcemaps treeshake: true, // Remove unused code - clean: true, // Clean output directory before building - outDir: "functions-dist", // Output directory + // clean: true, // Clean output directory before building + outDir: "functions", // Output directory format: ['esm'], // Output format(s) // add replacements diff --git a/packages/signup-api/wrangler.toml b/packages/signup-api/wrangler.toml new file mode 100644 index 00000000..80620af5 --- /dev/null +++ b/packages/signup-api/wrangler.toml @@ -0,0 +1,2 @@ +name = "signup-api" +pages_build_output_dir = "./dist" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd29bcca..4cefe7ed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,7 +44,7 @@ catalogs: version: 5.5.4 vite: specifier: ^5.4.6 - version: 5.4.6 + version: 5.4.11 vite-node: specifier: ^2.1.2 version: 2.1.2 @@ -170,7 +170,7 @@ importers: version: 12.0.0 debug: specifier: 'catalog:' - version: 4.3.7 + version: 4.3.7(supports-color@9.4.0) diff: specifier: ^5.2.0 version: 5.2.0 @@ -236,7 +236,7 @@ importers: version: 5.0.0 vite: specifier: 'catalog:' - version: 5.4.6(@types/node@20.14.12)(less@4.2.0) + version: 5.4.11(@types/node@20.14.12)(less@4.2.0) vite-node: specifier: 'catalog:' version: 2.1.2(@types/node@20.14.12)(less@4.2.0) @@ -358,10 +358,10 @@ importers: version: 20.14.12 '@vitejs/plugin-basic-ssl': specifier: ^1.1.0 - version: 1.1.0(vite@5.4.6(@types/node@20.14.12)(less@4.2.0)) + version: 1.1.0(vite@5.4.11(@types/node@20.14.12)(less@4.2.0)) '@vitejs/plugin-vue': specifier: ^5.1.4 - version: 5.1.4(vite@5.4.6(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4)) + version: 5.1.4(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4)) '@vue/tsconfig': specifier: ^0.5.1 version: 0.5.1 @@ -379,10 +379,10 @@ importers: version: 5.5.4 vite: specifier: 'catalog:' - version: 5.4.6(@types/node@20.14.12)(less@4.2.0) + version: 5.4.11(@types/node@20.14.12)(less@4.2.0) vite-plugin-vue-devtools: specifier: ^7.5.2 - version: 7.5.2(rollup@4.21.3)(vite@5.4.6(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4)) + version: 7.5.2(rollup@4.21.3)(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4)) vite-svg-loader: specifier: ^5.1.0 version: 5.1.0(vue@3.5.12(typescript@5.5.4)) @@ -673,7 +673,7 @@ importers: version: link:../../tsconfig '@remix-run/dev': specifier: ^2.10.3 - version: 2.10.3(@remix-run/react@2.10.3(react-dom@18.3.0(react@18.3.0))(react@18.3.0)(typescript@5.5.4))(@types/node@20.14.12)(less@4.2.0)(ts-node@10.9.2(@types/node@20.14.12)(typescript@5.5.4))(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(less@4.2.0)) + version: 2.10.3(@remix-run/react@2.10.3(react-dom@18.3.0(react@18.3.0))(react@18.3.0)(typescript@5.5.4))(@types/node@20.14.12)(less@4.2.0)(ts-node@10.9.2(@types/node@20.14.12)(typescript@5.5.4))(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(less@4.2.0))(wrangler@3.96.0) '@remix-run/react': specifier: ^2.10.3 version: 2.10.3(react-dom@18.3.0(react@18.3.0))(react@18.3.0)(typescript@5.5.4) @@ -742,6 +742,49 @@ importers: specifier: ^5.4.10 version: 5.4.11(@types/node@20.14.12)(less@4.2.0) + packages/platforms/cloudflare: + dependencies: + debug: + specifier: 'catalog:' + version: 4.3.7(supports-color@9.4.0) + execa: + specifier: ^9.5.1 + version: 9.5.2 + lodash-es: + specifier: 'catalog:' + version: 4.17.21 + nano-spawn: + specifier: ^0.2.0 + version: 0.2.0 + wrangler: + specifier: ^3 + version: 3.95.0 + devDependencies: + '@dmno/eslint-config': + specifier: workspace:* + version: link:../../eslint-config + '@dmno/tsconfig': + specifier: workspace:* + version: link:../../tsconfig + '@types/debug': + specifier: 'catalog:' + version: 4.1.12 + '@types/lodash-es': + specifier: 'catalog:' + version: 4.17.12 + '@types/node': + specifier: 'catalog:' + version: 20.14.12 + dmno: + specifier: workspace:* + version: link:../../core + tsup: + specifier: 'catalog:' + version: 8.2.4(jiti@1.21.0)(postcss@8.4.47)(tsx@4.19.2)(typescript@5.5.4)(yaml@2.5.1) + typescript: + specifier: 'catalog:' + version: 5.5.4 + packages/platforms/netlify: dependencies: '@netlify/blobs': @@ -973,6 +1016,15 @@ importers: specifier: ^4.1.1 version: 4.1.1(google-auth-library@9.9.0) devDependencies: + '@cloudflare/workers-types': + specifier: ^4.20241112.0 + version: 4.20241112.0 + '@dmno/1password-plugin': + specifier: workspace:* + version: link:../plugins/1password + '@dmno/cloudflare-platform': + specifier: workspace:* + version: link:../platforms/cloudflare '@dmno/encrypted-vault-plugin': specifier: workspace:* version: link:../plugins/encrypted-vault @@ -1000,6 +1052,9 @@ importers: typescript: specifier: 'catalog:' version: 5.5.4 + wrangler: + specifier: ^3.96.0 + version: 3.96.0(@cloudflare/workers-types@4.20241112.0) packages/ts-lib: dependencies: @@ -1529,6 +1584,47 @@ packages: '@changesets/write@0.3.0': resolution: {integrity: sha512-slGLb21fxZVUYbyea+94uFiD6ntQW0M2hIKNznFizDhZPDgn2c/fv1UzzlW43RVzh1BEDuIqW6hzlJ1OflNmcw==} + '@cloudflare/kv-asset-handler@0.3.4': + resolution: {integrity: sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==} + engines: {node: '>=16.13'} + + '@cloudflare/workerd-darwin-64@1.20241205.0': + resolution: {integrity: sha512-TArEZkSZkHJyEwnlWWkSpCI99cF6lJ14OVeEoI9Um/+cD9CKZLM9vCmsLeKglKheJ0KcdCnkA+DbeD15t3VaWg==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + + '@cloudflare/workerd-darwin-arm64@1.20241205.0': + resolution: {integrity: sha512-u5eqKa9QRdA8MugfgCoD+ADDjY6EpKbv3hSYJETmmUh17l7WXjWBzv4pUvOKIX67C0UzMUy4jZYwC53MymhX3w==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + + '@cloudflare/workerd-linux-64@1.20241205.0': + resolution: {integrity: sha512-OYA7S5zpumMamWEW+IhhBU6YojIEocyE5X/YFPiTOCrDE3dsfr9t6oqNE7hxGm1VAAu+Irtl+a/5LwmBOU681w==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + + '@cloudflare/workerd-linux-arm64@1.20241205.0': + resolution: {integrity: sha512-qAzecONjFJGIAVJZKExQ5dlbic0f3d4A+GdKa+H6SoUJtPaWiE3K6WuePo4JOT7W3/Zfh25McmX+MmpMUUcM5Q==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + + '@cloudflare/workerd-windows-64@1.20241205.0': + resolution: {integrity: sha512-BEab+HiUgCdl6GXAT7EI2yaRtDPiRJlB94XLvRvXi1ZcmQqsrq6awGo6apctFo4WUL29V7c09LxmN4HQ3X2Tvg==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + + '@cloudflare/workers-shared@0.11.0': + resolution: {integrity: sha512-A+lQ8xp7992qSeMmuQ0ssL6CPmm+ZmAv6Ddikan0n1jjpMAic+97l7xtVIsswSn9iLMFPYQ9uNN/8Fl0AgARIQ==} + engines: {node: '>=16.7.0'} + + '@cloudflare/workers-types@4.20241112.0': + resolution: {integrity: sha512-Q4p9bAWZrX14bSCKY9to19xl0KMU7nsO5sJ2cTVspHoypsjPUMeQCsjHjmsO2C4Myo8/LPeDvmqFmkyNAPPYZw==} + '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} @@ -1579,6 +1675,16 @@ packages: '@emotion/hash@0.9.1': resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==} + '@esbuild-plugins/node-globals-polyfill@0.2.3': + resolution: {integrity: sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==} + peerDependencies: + esbuild: '*' + + '@esbuild-plugins/node-modules-polyfill@0.2.2': + resolution: {integrity: sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA==} + peerDependencies: + esbuild: '*' + '@esbuild/aix-ppc64@0.19.11': resolution: {integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==} engines: {node: '>=12'} @@ -1615,6 +1721,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.17.19': + resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.17.6': resolution: {integrity: sha512-YnYSCceN/dUzUr5kdtUzB+wZprCafuD89Hs0Aqv9QSdwhYQybhXTaSTcrl6X/aWThn1a/j0eEpUBGOE7269REg==} engines: {node: '>=12'} @@ -1657,6 +1769,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm@0.17.19': + resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.17.6': resolution: {integrity: sha512-bSC9YVUjADDy1gae8RrioINU6e1lCkg3VGVwm0QQ2E1CWcC4gnMce9+B6RpxuSsrsXsk1yojn7sp1fnG8erE2g==} engines: {node: '>=12'} @@ -1699,6 +1817,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-x64@0.17.19': + resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.17.6': resolution: {integrity: sha512-MVcYcgSO7pfu/x34uX9u2QIZHmXAB7dEiLQC5bBl5Ryqtpj9lT2sg3gNDEsrPEmimSJW2FXIaxqSQ501YLDsZQ==} engines: {node: '>=12'} @@ -1741,6 +1865,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.17.19': + resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.17.6': resolution: {integrity: sha512-bsDRvlbKMQMt6Wl08nHtFz++yoZHsyTOxnjfB2Q95gato+Yi4WnRl13oC2/PJJA9yLCoRv9gqT/EYX0/zDsyMA==} engines: {node: '>=12'} @@ -1783,6 +1913,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.17.19': + resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.17.6': resolution: {integrity: sha512-xh2A5oPrYRfMFz74QXIQTQo8uA+hYzGWJFoeTE8EvoZGHb+idyV4ATaukaUvnnxJiauhs/fPx3vYhU4wiGfosg==} engines: {node: '>=12'} @@ -1825,6 +1961,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.17.19': + resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.17.6': resolution: {integrity: sha512-EnUwjRc1inT4ccZh4pB3v1cIhohE2S4YXlt1OvI7sw/+pD+dIE4smwekZlEPIwY6PhU6oDWwITrQQm5S2/iZgg==} engines: {node: '>=12'} @@ -1867,6 +2009,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.17.19': + resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.17.6': resolution: {integrity: sha512-Uh3HLWGzH6FwpviUcLMKPCbZUAFzv67Wj5MTwK6jn89b576SR2IbEp+tqUHTr8DIl0iDmBAf51MVaP7pw6PY5Q==} engines: {node: '>=12'} @@ -1909,6 +2057,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.17.19': + resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.17.6': resolution: {integrity: sha512-bUR58IFOMJX523aDVozswnlp5yry7+0cRLCXDsxnUeQYJik1DukMY+apBsLOZJblpH+K7ox7YrKrHmJoWqVR9w==} engines: {node: '>=12'} @@ -1951,6 +2105,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.17.19': + resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.17.6': resolution: {integrity: sha512-7YdGiurNt7lqO0Bf/U9/arrPWPqdPqcV6JCZda4LZgEn+PTQ5SMEI4MGR52Bfn3+d6bNEGcWFzlIxiQdS48YUw==} engines: {node: '>=12'} @@ -1993,6 +2153,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.17.19': + resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.17.6': resolution: {integrity: sha512-ujp8uoQCM9FRcbDfkqECoARsLnLfCUhKARTP56TFPog8ie9JG83D5GVKjQ6yVrEVdMie1djH86fm98eY3quQkQ==} engines: {node: '>=12'} @@ -2035,6 +2201,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.17.19': + resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.17.6': resolution: {integrity: sha512-y2NX1+X/Nt+izj9bLoiaYB9YXT/LoaQFYvCkVD77G/4F+/yuVXYCWz4SE9yr5CBMbOxOfBcy/xFL4LlOeNlzYQ==} engines: {node: '>=12'} @@ -2077,6 +2249,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.17.19': + resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.17.6': resolution: {integrity: sha512-09AXKB1HDOzXD+j3FdXCiL/MWmZP0Ex9eR8DLMBVcHorrWJxWmY8Nms2Nm41iRM64WVx7bA/JVHMv081iP2kUA==} engines: {node: '>=12'} @@ -2119,6 +2297,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.17.19': + resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.17.6': resolution: {integrity: sha512-AmLhMzkM8JuqTIOhxnX4ubh0XWJIznEynRnZAVdA2mMKE6FAfwT2TWKTwdqMG+qEaeyDPtfNoZRpJbD4ZBv0Tg==} engines: {node: '>=12'} @@ -2161,6 +2345,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.17.19': + resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.17.6': resolution: {integrity: sha512-Y4Ri62PfavhLQhFbqucysHOmRamlTVK10zPWlqjNbj2XMea+BOs4w6ASKwQwAiqf9ZqcY9Ab7NOU4wIgpxwoSQ==} engines: {node: '>=12'} @@ -2203,6 +2393,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.17.19': + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.17.6': resolution: {integrity: sha512-SPUiz4fDbnNEm3JSdUW8pBJ/vkop3M1YwZAVwvdwlFLoJwKEZ9L98l3tzeyMzq27CyepDQ3Qgoba44StgbiN5Q==} engines: {node: '>=12'} @@ -2245,6 +2441,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.17.19': + resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.17.6': resolution: {integrity: sha512-a3yHLmOodHrzuNgdpB7peFGPx1iJ2x6m+uDvhP2CKdr2CwOaqEFMeSqYAHU7hG+RjCq8r2NFujcd/YsEsFgTGw==} engines: {node: '>=12'} @@ -2287,6 +2489,12 @@ packages: cpu: [x64] os: [linux] + '@esbuild/netbsd-x64@0.17.19': + resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.17.6': resolution: {integrity: sha512-EanJqcU/4uZIBreTrnbnre2DXgXSa+Gjap7ifRfllpmyAU7YMvaXmljdArptTHmjrkkKm9BK6GH5D5Yo+p6y5A==} engines: {node: '>=12'} @@ -2335,6 +2543,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.17.19': + resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.17.6': resolution: {integrity: sha512-xaxeSunhQRsTNGFanoOkkLtnmMn5QbA0qBhNet/XLVsc+OVkpIWPHcr3zTW2gxVU5YOHFbIHR9ODuaUdNza2Vw==} engines: {node: '>=12'} @@ -2377,6 +2591,12 @@ packages: cpu: [x64] os: [openbsd] + '@esbuild/sunos-x64@0.17.19': + resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.17.6': resolution: {integrity: sha512-gnMnMPg5pfMkZvhHee21KbKdc6W3GR8/JuE0Da1kjwpK6oiFU3nqfHuVPgUX2rsOx9N2SadSQTIYV1CIjYG+xw==} engines: {node: '>=12'} @@ -2419,6 +2639,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.17.19': + resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.17.6': resolution: {integrity: sha512-G95n7vP1UnGJPsVdKXllAJPtqjMvFYbN20e8RK8LVLhlTiSOH1sd7+Gt7rm70xiG+I5tM58nYgwWrLs6I1jHqg==} engines: {node: '>=12'} @@ -2461,6 +2687,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.17.19': + resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.17.6': resolution: {integrity: sha512-96yEFzLhq5bv9jJo5JhTs1gI+1cKQ83cUpyxHuGqXVwQtY5Eq54ZEsKs8veKtiKwlrNimtckHEkj4mRh4pPjsg==} engines: {node: '>=12'} @@ -2503,6 +2735,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.17.19': + resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.17.6': resolution: {integrity: sha512-n6d8MOyUrNp6G4VSpRcgjs5xj4A91svJSaiwLIDWVWEsZtpN5FA9NlBbZHDmAJc2e8e6SF4tkBD3HAvPF+7igA==} engines: {node: '>=12'} @@ -2585,6 +2823,10 @@ packages: '@fastify/ajv-compiler@4.0.1': resolution: {integrity: sha512-DxrBdgsjNLP0YM6W5Hd6/Fmj43S8zMKiFJYgi+Ri3htTGAowPVG/tG1wpnWLMjufEnehRivUCKZ1pLDIoZdTuw==} + '@fastify/busboy@2.1.1': + resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} + engines: {node: '>=14'} + '@fastify/error@3.4.1': resolution: {integrity: sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==} @@ -2638,8 +2880,8 @@ packages: '@iconify/json@2.2.212': resolution: {integrity: sha512-d1IXjpbSwk8V3C9D+mruRTJRPqZZpCnOWh9zyG/Zc5zJmPDLBEHNTKz+ZmeiJQ6LdzgjwLJai1WgFvt1HWVIPw==} - '@iconify/tools@4.0.7': - resolution: {integrity: sha512-zOJxKIfZn96ZRGGvIWzDRLD9vb2CsxjcLuM+QIdvwWbv6SWhm49gECzUnd4d2P0sq9sfodT7yCNobWK8nvavxQ==} + '@iconify/tools@4.1.1': + resolution: {integrity: sha512-Hybu/HGhv6T8nLQhiG9rKx+ekF7NNpPOEQAy7JRSKht3s3dcFSsPccYzk24Znc9MTxrR6Gak3cg6CPP5dyvS2Q==} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -2647,6 +2889,9 @@ packages: '@iconify/utils@2.1.32': resolution: {integrity: sha512-LeifFZPPKu28O3AEDpYJNdEbvS4/ojAPyIW+pF/vUpJTYnbTiXUHkCh0bwgFRzKvdpb8H4Fbfd/742++MF4fPQ==} + '@iconify/utils@2.2.1': + resolution: {integrity: sha512-0/7J7hk4PqXmxo5PDBDxmnecw5PxklZJfNjIVG9FM0mEfVrvfudS22rYWsqVk6gR3UJ/mSYS90X4R3znXnqfNA==} + '@iconify/vue@4.1.2': resolution: {integrity: sha512-CQnYqLiQD5LOAaXhBrmj1mdL2/NCJvwcC4jtW2Z8ukhThiFkLDkutarTOV2trfc9EXqUqRs0KqXOL9pZ/IyysA==} peerDependencies: @@ -3696,6 +3941,9 @@ packages: cpu: [x64] os: [win32] + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@shikijs/core@1.18.0': resolution: {integrity: sha512-VK4BNVCd2leY62Nm2JjyxtRLkyrZT/tv104O81eyaCjHq4Adceq2uJVFJJAIof6lT1mBwZrEo2qT/T+grv3MQQ==} @@ -3722,6 +3970,10 @@ packages: resolution: {integrity: sha512-QWLl2P+rsCJeofkDNIT3WFmb6NrRud1SUYW8dIhXK/46XFV8Q/g7Bsvib0Askb0reRLe+WYPeeE+l5cH7SlkuQ==} engines: {node: '>=18'} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@sindresorhus/slugify@2.2.1': resolution: {integrity: sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==} engines: {node: '>=12'} @@ -4442,6 +4694,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.14.0: + resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + engines: {node: '>=0.4.0'} + hasBin: true + agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -4662,6 +4919,9 @@ packages: resolution: {integrity: sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==} engines: {node: '>=12'} + as-table@1.0.55: + resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==} + asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} @@ -4764,6 +5024,9 @@ packages: axios@1.7.7: resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} + axios@1.7.9: + resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==} + axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -4860,6 +5123,9 @@ packages: bl@5.1.0: resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} + blake3-wasm@2.1.5: + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + blueimp-md5@2.19.0: resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} @@ -5016,6 +5282,9 @@ packages: caniuse-lite@1.0.30001663: resolution: {integrity: sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==} + capnp-ts@0.7.0: + resolution: {integrity: sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==} + ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -5085,6 +5354,10 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + chokidar@4.0.1: + resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==} + engines: {node: '>= 14.16.0'} + chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -5304,6 +5577,9 @@ packages: confbox@0.1.7: resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -5357,6 +5633,10 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cookie@1.0.2: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} @@ -5489,6 +5769,9 @@ packages: cyclist@1.0.2: resolution: {integrity: sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA==} + data-uri-to-buffer@2.0.2: + resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==} + data-uri-to-buffer@3.0.1: resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==} engines: {node: '>= 6'} @@ -5512,6 +5795,9 @@ packages: dataloader@1.4.0: resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + date-time@3.1.0: resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==} engines: {node: '>=6'} @@ -5553,6 +5839,15 @@ packages: supports-color: optional: true + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decache@4.6.2: resolution: {integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==} @@ -5950,6 +6245,11 @@ packages: peerDependencies: esbuild: ^0.14.0 || ^0.15.0 || ^0.16.0 || ^0.17.0 || ^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 + esbuild@0.17.19: + resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.17.6: resolution: {integrity: sha512-TKFRp9TxrJDdRWfSsSERKEovm6v30iHnrjlcGhLBOtReE28Yp1VSBRfO3GTaOFMoxsNerx4TjrhzSuma9ha83Q==} engines: {node: '>=12'} @@ -6218,6 +6518,9 @@ packages: estree-util-visit@2.0.0: resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} + estree-walker@0.6.1: + resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==} + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -6266,6 +6569,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + execa@9.5.2: + resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} + engines: {node: ^18.19.0 || >=20.5.0} + exit-hook@2.2.1: resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} engines: {node: '>=6'} @@ -6441,6 +6748,10 @@ packages: resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} engines: {node: '>=14'} + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} @@ -6705,6 +7016,9 @@ packages: resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} engines: {node: '>=16'} + get-source@2.0.12: + resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} + get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} @@ -6717,6 +7031,10 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + get-symbol-description@1.0.2: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} @@ -6756,6 +7074,9 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + glob@10.3.12: resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} engines: {node: '>=16 || 14 >=14.17'} @@ -6786,6 +7107,10 @@ packages: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} engines: {node: '>=8'} + globals@15.13.0: + resolution: {integrity: sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==} + engines: {node: '>=18'} + globals@15.3.0: resolution: {integrity: sha512-cCdyVjIUVTtX8ZsPkq1oCsOsLmGIswqnjZYMJJTGaNApj1yHtLSymKhwH51ttirREn75z3p4k051clwg7rvNKA==} engines: {node: '>=18'} @@ -7092,6 +7417,10 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + human-signals@8.0.0: + resolution: {integrity: sha512-/1/GPCpDUCCYwlERiYjxoczfP0zfvZMU/OWgQPMya9AbAE24vseigFdhAMObpc8Q4lc/kjutPfUddDYyAmejnA==} + engines: {node: '>=18.18.0'} + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -7462,6 +7791,10 @@ packages: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + is-string@1.0.7: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} @@ -7554,6 +7887,9 @@ packages: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} + itty-time@1.0.6: + resolution: {integrity: sha512-+P8IZaLLBtFv8hCkIjcymZOp4UJ+xW6bSlQsXGqrkmJh7vSiMFSlNne0mCYagEE0N7HDNR5jJBRxwN0oYv61Rw==} + jackspeak@2.3.6: resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} engines: {node: '>=14'} @@ -7796,6 +8132,10 @@ packages: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} + local-pkg@0.5.1: + resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==} + engines: {node: '>=14'} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -7927,6 +8267,9 @@ packages: resolution: {integrity: sha512-fSErXALFNsnowREYZ49XCdOHF8wOPWuFOGQrAhP7x5J/BqQv+B02cNsTykGpDgRVx43EKg++6ANmTaGTtW+hUA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + magic-string@0.30.11: resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} @@ -8414,6 +8757,11 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} + miniflare@3.20241205.0: + resolution: {integrity: sha512-Z0cTtIf6ZrcAJ3SrOI9EUM3s4dkGhNeU6Ubl8sroYhsPVD+rtz3m5+p6McHFWCkcMff1o60X5XEKVTmkz0gbpA==} + engines: {node: '>=16.13'} + hasBin: true + minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -8497,6 +8845,9 @@ packages: mlly@1.7.1: resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + mlly@1.7.3: + resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==} + modern-ahocorasick@1.0.1: resolution: {integrity: sha512-yoe+JbhTClckZ67b2itRtistFKf8yPYelHLc7e5xAwtNAXxM6wJTUx2C7QeVSJFDzKT7bCIFyBVybPMKvmB9AA==} @@ -8543,6 +8894,10 @@ packages: resolution: {integrity: sha512-Ak6EUJZuhGS8hJ3c2fY6UW5MbkGUPMBEGd13djUzoY/BHqV/gTuFWtC6IuVA7A2+v3yjBS6c4or50xhzTQZImQ==} engines: {node: '>= 0.10'} + mustache@4.2.0: + resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} + hasBin: true + mute-stream@0.0.7: resolution: {integrity: sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==} @@ -8556,6 +8911,10 @@ packages: nan@2.19.0: resolution: {integrity: sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==} + nano-spawn@0.2.0: + resolution: {integrity: sha512-IjZBIOLxSlxu+m/kacg9JuP93oUpRemeV0mEuCy64nzBKKIL9m0aLJHtVPcVuzJDHFhElzjpwbW4a3tMzgKoZQ==} + engines: {node: '>=18.19'} + nanoassert@2.0.0: resolution: {integrity: sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==} @@ -8790,6 +9149,10 @@ packages: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + npmlog@5.0.1: resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==} deprecated: This package is no longer supported. @@ -8843,8 +9206,8 @@ packages: ofetch@1.3.4: resolution: {integrity: sha512-KLIET85ik3vhEfS+3fDlc/BAZiAp+43QEC/yCo5zkNoY2YaKvNkOaFr/6wCFgFH1kuYQM5pMNi0Tg8koiIemtw==} - ohash@1.1.3: - resolution: {integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==} + ohash@1.1.4: + resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} omit.js@2.0.2: resolution: {integrity: sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==} @@ -9098,6 +9461,10 @@ packages: resolution: {integrity: sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==} engines: {node: '>=12'} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + parse-node-version@1.0.1: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} @@ -9152,6 +9519,9 @@ packages: path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -9257,6 +9627,9 @@ packages: pkg-types@1.1.3: resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} + pkg-types@1.2.1: + resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==} + posix-character-classes@0.1.1: resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} engines: {node: '>=0.10.0'} @@ -9413,10 +9786,17 @@ packages: resolution: {integrity: sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==} engines: {node: '>=14.16'} + pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + engines: {node: '>=18'} + prettyjson@1.2.5: resolution: {integrity: sha512-rksPWtoZb2ZpT5OVgtmy0KHVM+Dca3iVwWY9ifwhcexfjebtgjg3wmrUt9PvJ59XIYBcknQeYHD8IAnVlh9lAw==} hasBin: true + printable-characters@1.0.42: + resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} + prismjs@1.29.0: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} engines: {node: '>=6'} @@ -9698,6 +10078,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + readdirp@4.0.2: + resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} + engines: {node: '>= 14.16.0'} + real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -9929,6 +10313,16 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + rollup-plugin-inject@3.0.2: + resolution: {integrity: sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==} + deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject. + + rollup-plugin-node-polyfills@0.2.1: + resolution: {integrity: sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==} + + rollup-pluginutils@2.8.2: + resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} + rollup@4.19.0: resolution: {integrity: sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -10014,6 +10408,10 @@ packages: resolution: {integrity: sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==} hasBin: true + selfsigned@2.4.1: + resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} + engines: {node: '>=10'} + semver-diff@4.0.0: resolution: {integrity: sha512-0Ju4+6A8iOnpL/Thra7dZsSlOHYAHIeMxfhWQRI1/VLcT3WDBZKKtQt/QkBOsiIN9ZpuvHE6cGZ0x4glCMmfiA==} engines: {node: '>=12'} @@ -10247,6 +10645,10 @@ packages: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} @@ -10299,6 +10701,9 @@ packages: stackframe@1.3.4: resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + stacktracey@2.1.8: + resolution: {integrity: sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==} + starlight-blog@0.11.1: resolution: {integrity: sha512-2nMcYzCOrWZ8/34G7SouSX1jVzwKPARFwyszTF2497FN6ZUdrTsnhLB6Q7/l6fEKdcQkh9UCyMI7coeR670guA==} engines: {node: '>=18.14.1'} @@ -10336,6 +10741,10 @@ packages: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} + stoppable@1.1.0: + resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==} + engines: {node: '>=4', npm: '>=6'} + stream-replace-string@2.0.0: resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} @@ -10447,6 +10856,10 @@ packages: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -10961,6 +11374,9 @@ packages: ufo@1.5.3: resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==} + ufo@1.5.4: + resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} + uid-safe@2.1.5: resolution: {integrity: sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==} engines: {node: '>= 0.8'} @@ -10984,10 +11400,17 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + undici@5.28.4: + resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} + engines: {node: '>=14.0'} + undici@6.19.7: resolution: {integrity: sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A==} engines: {node: '>=18.17'} + unenv-nightly@2.0.0-20241204-140205-a5d5190: + resolution: {integrity: sha512-jpmAytLeiiW01pl5bhVn9wYJ4vtiLdhGe10oXlJBuQEX8mxjxO8BlEXGHU4vr4yEikjFP1wsomTHt/CLU8kUwg==} + unenv@1.9.0: resolution: {integrity: sha512-QKnFNznRxmbOF1hDgzpqrlIf6NC5sbZ2OJ+5Wl3OX8uM+LUJXbj4TXvLJCtwbPTmbMHCLIz6JLKNinNsMShK9g==} @@ -10998,6 +11421,10 @@ packages: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + unified-engine@11.2.1: resolution: {integrity: sha512-xBAdZ8UY2X4R9Hm6X6kMne4Nz0PlpOc1oE6DPeqJnewr5Imkb8uT5Eyvy1h7xNekPL3PSWh3ZJyNrMW6jnNQBg==} @@ -11414,37 +11841,6 @@ packages: terser: optional: true - vite@5.4.6: - resolution: {integrity: sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - vitefu@1.0.2: resolution: {integrity: sha512-0/iAvbXyM3RiPPJ4lyD4w6Mjgtf4ejTK6TPvTNG3H32PLwuT0N/ZjJLiXug7ETE/LWtTeHw9WRv7uX/tIKYyKg==} peerDependencies: @@ -11805,6 +12201,31 @@ packages: resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} engines: {node: '>= 10.0.0'} + workerd@1.20241205.0: + resolution: {integrity: sha512-vso/2n0c5SdBDWiD+Sx5gM7unA6SiZXRVUHDqH1euoP/9mFVHZF8icoYsNLB87b/TX8zNgpae+I5N/xFpd9v0g==} + engines: {node: '>=16'} + hasBin: true + + wrangler@3.95.0: + resolution: {integrity: sha512-3w5852i3FNyDz421K2Qk4v5L8jjwegO5O8E1+VAQmjnm82HFNxpIRUBq0bmM7CTLvOPI/Jjcmj/eAWjQBL7QYg==} + engines: {node: '>=16.17.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^4.20241205.0 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + + wrangler@3.96.0: + resolution: {integrity: sha512-KjbHTUnwTa5eKl3hzv2h6nHBfAsbUkdurL7f6Y288/Bdn6tcEis13jLVR/nw/eWa3tNCBG1xOMZJboUyzWcC1g==} + engines: {node: '>=16.17.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^4.20241205.0 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -11883,6 +12304,18 @@ packages: utf-8-validate: optional: true + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xdg-basedir@5.1.0: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} @@ -11983,6 +12416,13 @@ packages: resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} engines: {node: '>=12.20'} + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + + youch@3.3.4: + resolution: {integrity: sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==} + zhead@2.2.4: resolution: {integrity: sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==} @@ -12874,6 +13314,32 @@ snapshots: human-id: 1.0.2 prettier: 2.8.8 + '@cloudflare/kv-asset-handler@0.3.4': + dependencies: + mime: 3.0.0 + + '@cloudflare/workerd-darwin-64@1.20241205.0': + optional: true + + '@cloudflare/workerd-darwin-arm64@1.20241205.0': + optional: true + + '@cloudflare/workerd-linux-64@1.20241205.0': + optional: true + + '@cloudflare/workerd-linux-arm64@1.20241205.0': + optional: true + + '@cloudflare/workerd-windows-64@1.20241205.0': + optional: true + + '@cloudflare/workers-shared@0.11.0': + dependencies: + mime: 3.0.0 + zod: 3.23.8 + + '@cloudflare/workers-types@4.20241112.0': {} + '@colors/colors@1.6.0': {} '@cspotcode/source-map-support@0.8.1': @@ -12925,6 +13391,16 @@ snapshots: '@emotion/hash@0.9.1': {} + '@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.17.19)': + dependencies: + esbuild: 0.17.19 + + '@esbuild-plugins/node-modules-polyfill@0.2.2(esbuild@0.17.19)': + dependencies: + esbuild: 0.17.19 + escape-string-regexp: 4.0.0 + rollup-plugin-node-polyfills: 0.2.1 + '@esbuild/aix-ppc64@0.19.11': optional: true @@ -12943,6 +13419,9 @@ snapshots: '@esbuild/aix-ppc64@0.23.0': optional: true + '@esbuild/android-arm64@0.17.19': + optional: true + '@esbuild/android-arm64@0.17.6': optional: true @@ -12964,6 +13443,9 @@ snapshots: '@esbuild/android-arm64@0.23.0': optional: true + '@esbuild/android-arm@0.17.19': + optional: true + '@esbuild/android-arm@0.17.6': optional: true @@ -12985,6 +13467,9 @@ snapshots: '@esbuild/android-arm@0.23.0': optional: true + '@esbuild/android-x64@0.17.19': + optional: true + '@esbuild/android-x64@0.17.6': optional: true @@ -13006,6 +13491,9 @@ snapshots: '@esbuild/android-x64@0.23.0': optional: true + '@esbuild/darwin-arm64@0.17.19': + optional: true + '@esbuild/darwin-arm64@0.17.6': optional: true @@ -13027,6 +13515,9 @@ snapshots: '@esbuild/darwin-arm64@0.23.0': optional: true + '@esbuild/darwin-x64@0.17.19': + optional: true + '@esbuild/darwin-x64@0.17.6': optional: true @@ -13048,6 +13539,9 @@ snapshots: '@esbuild/darwin-x64@0.23.0': optional: true + '@esbuild/freebsd-arm64@0.17.19': + optional: true + '@esbuild/freebsd-arm64@0.17.6': optional: true @@ -13069,6 +13563,9 @@ snapshots: '@esbuild/freebsd-arm64@0.23.0': optional: true + '@esbuild/freebsd-x64@0.17.19': + optional: true + '@esbuild/freebsd-x64@0.17.6': optional: true @@ -13087,7 +13584,10 @@ snapshots: '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.23.0': + '@esbuild/freebsd-x64@0.23.0': + optional: true + + '@esbuild/linux-arm64@0.17.19': optional: true '@esbuild/linux-arm64@0.17.6': @@ -13111,6 +13611,9 @@ snapshots: '@esbuild/linux-arm64@0.23.0': optional: true + '@esbuild/linux-arm@0.17.19': + optional: true + '@esbuild/linux-arm@0.17.6': optional: true @@ -13132,6 +13635,9 @@ snapshots: '@esbuild/linux-arm@0.23.0': optional: true + '@esbuild/linux-ia32@0.17.19': + optional: true + '@esbuild/linux-ia32@0.17.6': optional: true @@ -13153,6 +13659,9 @@ snapshots: '@esbuild/linux-ia32@0.23.0': optional: true + '@esbuild/linux-loong64@0.17.19': + optional: true + '@esbuild/linux-loong64@0.17.6': optional: true @@ -13174,6 +13683,9 @@ snapshots: '@esbuild/linux-loong64@0.23.0': optional: true + '@esbuild/linux-mips64el@0.17.19': + optional: true + '@esbuild/linux-mips64el@0.17.6': optional: true @@ -13195,6 +13707,9 @@ snapshots: '@esbuild/linux-mips64el@0.23.0': optional: true + '@esbuild/linux-ppc64@0.17.19': + optional: true + '@esbuild/linux-ppc64@0.17.6': optional: true @@ -13216,6 +13731,9 @@ snapshots: '@esbuild/linux-ppc64@0.23.0': optional: true + '@esbuild/linux-riscv64@0.17.19': + optional: true + '@esbuild/linux-riscv64@0.17.6': optional: true @@ -13237,6 +13755,9 @@ snapshots: '@esbuild/linux-riscv64@0.23.0': optional: true + '@esbuild/linux-s390x@0.17.19': + optional: true + '@esbuild/linux-s390x@0.17.6': optional: true @@ -13258,6 +13779,9 @@ snapshots: '@esbuild/linux-s390x@0.23.0': optional: true + '@esbuild/linux-x64@0.17.19': + optional: true + '@esbuild/linux-x64@0.17.6': optional: true @@ -13279,6 +13803,9 @@ snapshots: '@esbuild/linux-x64@0.23.0': optional: true + '@esbuild/netbsd-x64@0.17.19': + optional: true + '@esbuild/netbsd-x64@0.17.6': optional: true @@ -13303,6 +13830,9 @@ snapshots: '@esbuild/openbsd-arm64@0.23.0': optional: true + '@esbuild/openbsd-x64@0.17.19': + optional: true + '@esbuild/openbsd-x64@0.17.6': optional: true @@ -13324,6 +13854,9 @@ snapshots: '@esbuild/openbsd-x64@0.23.0': optional: true + '@esbuild/sunos-x64@0.17.19': + optional: true + '@esbuild/sunos-x64@0.17.6': optional: true @@ -13345,6 +13878,9 @@ snapshots: '@esbuild/sunos-x64@0.23.0': optional: true + '@esbuild/win32-arm64@0.17.19': + optional: true + '@esbuild/win32-arm64@0.17.6': optional: true @@ -13366,6 +13902,9 @@ snapshots: '@esbuild/win32-arm64@0.23.0': optional: true + '@esbuild/win32-ia32@0.17.19': + optional: true + '@esbuild/win32-ia32@0.17.6': optional: true @@ -13387,6 +13926,9 @@ snapshots: '@esbuild/win32-ia32@0.23.0': optional: true + '@esbuild/win32-x64@0.17.19': + optional: true + '@esbuild/win32-x64@0.17.6': optional: true @@ -13470,6 +14012,8 @@ snapshots: ajv-formats: 3.0.1(ajv@8.12.0) fast-uri: 3.0.3 + '@fastify/busboy@2.1.1': {} + '@fastify/error@3.4.1': {} '@fastify/error@4.0.0': {} @@ -13534,16 +14078,16 @@ snapshots: '@iconify/types': 2.0.0 pathe: 1.1.2 - '@iconify/tools@4.0.7': + '@iconify/tools@4.1.1': dependencies: '@iconify/types': 2.0.0 - '@iconify/utils': 2.1.32 + '@iconify/utils': 2.2.1 '@types/tar': 6.1.13 - axios: 1.7.7 + axios: 1.7.9 cheerio: 1.0.0 domhandler: 5.0.3 extract-zip: 2.0.1 - local-pkg: 0.5.0 + local-pkg: 0.5.1 pathe: 1.1.2 svgo: 3.3.2 tar: 6.2.1 @@ -13565,6 +14109,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@iconify/utils@2.2.1': + dependencies: + '@antfu/install-pkg': 0.4.1 + '@antfu/utils': 0.7.10 + '@iconify/types': 2.0.0 + debug: 4.4.0 + globals: 15.13.0 + kolorist: 1.8.0 + local-pkg: 0.5.1 + mlly: 1.7.3 + transitivePeerDependencies: + - supports-color + '@iconify/vue@4.1.2(vue@3.5.12(typescript@5.5.4))': dependencies: '@iconify/types': 2.0.0 @@ -14784,7 +15341,7 @@ snapshots: '@polka/url@1.0.0-next.25': {} - '@remix-run/dev@2.10.3(@remix-run/react@2.10.3(react-dom@18.3.0(react@18.3.0))(react@18.3.0)(typescript@5.5.4))(@types/node@20.14.12)(less@4.2.0)(ts-node@10.9.2(@types/node@20.14.12)(typescript@5.5.4))(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(less@4.2.0))': + '@remix-run/dev@2.10.3(@remix-run/react@2.10.3(react-dom@18.3.0(react@18.3.0))(react@18.3.0)(typescript@5.5.4))(@types/node@20.14.12)(less@4.2.0)(ts-node@10.9.2(@types/node@20.14.12)(typescript@5.5.4))(typescript@5.5.4)(vite@5.3.5(@types/node@20.14.12)(less@4.2.0))(wrangler@3.96.0)': dependencies: '@babel/core': 7.24.7 '@babel/generator': 7.24.7 @@ -14843,6 +15400,7 @@ snapshots: optionalDependencies: typescript: 5.5.4 vite: 5.3.5(@types/node@20.14.12)(less@4.2.0) + wrangler: 3.96.0(@cloudflare/workers-types@4.20241112.0) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -15034,6 +15592,8 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.21.3': optional: true + '@sec-ant/readable-stream@0.4.1': {} + '@shikijs/core@1.18.0': dependencies: '@shikijs/engine-javascript': 1.18.0 @@ -15067,6 +15627,8 @@ snapshots: '@sindresorhus/is@7.0.1': {} + '@sindresorhus/merge-streams@4.0.0': {} + '@sindresorhus/slugify@2.2.1': dependencies: '@sindresorhus/transliterate': 1.6.0 @@ -15598,9 +16160,9 @@ snapshots: - encoding - supports-color - '@vitejs/plugin-basic-ssl@1.1.0(vite@5.4.6(@types/node@20.14.12)(less@4.2.0))': + '@vitejs/plugin-basic-ssl@1.1.0(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))': dependencies: - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) + vite: 5.4.11(@types/node@20.14.12)(less@4.2.0) '@vitejs/plugin-vue-jsx@4.0.0(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.8(typescript@5.5.4))': dependencies: @@ -15612,15 +16174,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.1.4(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.8(typescript@5.5.4))': + '@vitejs/plugin-vue@5.1.4(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4))': dependencies: vite: 5.4.11(@types/node@20.14.12)(less@4.2.0) - vue: 3.5.8(typescript@5.5.4) + vue: 3.5.12(typescript@5.5.4) - '@vitejs/plugin-vue@5.1.4(vite@5.4.6(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4))': + '@vitejs/plugin-vue@5.1.4(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.8(typescript@5.5.4))': dependencies: - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) - vue: 3.5.12(typescript@5.5.4) + vite: 5.4.11(@types/node@20.14.12)(less@4.2.0) + vue: 3.5.8(typescript@5.5.4) '@vitest/expect@2.1.2': dependencies: @@ -15629,13 +16191,13 @@ snapshots: chai: 5.1.1 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.4.6(@types/node@20.14.12)(less@4.2.0))': + '@vitest/mocker@2.1.2(@vitest/spy@2.1.2)(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))': dependencies: '@vitest/spy': 2.1.2 estree-walker: 3.0.3 magic-string: 0.30.12 optionalDependencies: - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) + vite: 5.4.11(@types/node@20.14.12)(less@4.2.0) '@vitest/pretty-format@2.1.2': dependencies: @@ -15841,7 +16403,7 @@ snapshots: '@vue/compiler-ssr': 3.4.25 '@vue/shared': 3.4.25 estree-walker: 2.0.2 - magic-string: 0.30.11 + magic-string: 0.30.12 postcss: 8.4.47 source-map-js: 1.2.1 @@ -15889,7 +16451,7 @@ snapshots: '@vue/compiler-ssr': 3.5.9 '@vue/shared': 3.5.9 estree-walker: 2.0.2 - magic-string: 0.30.11 + magic-string: 0.30.12 postcss: 8.4.47 source-map-js: 1.2.1 @@ -15925,7 +16487,7 @@ snapshots: '@vue/devtools-api@6.6.4': {} - '@vue/devtools-core@7.5.2(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.8(typescript@5.5.4))': + '@vue/devtools-core@7.5.2(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4))': dependencies: '@vue/devtools-kit': 7.5.2 '@vue/devtools-shared': 7.5.2 @@ -15933,19 +16495,19 @@ snapshots: nanoid: 3.3.7 pathe: 1.1.2 vite-hot-client: 0.2.3(vite@5.4.11(@types/node@20.14.12)(less@4.2.0)) - vue: 3.5.8(typescript@5.5.4) + vue: 3.5.12(typescript@5.5.4) transitivePeerDependencies: - vite - '@vue/devtools-core@7.5.2(vite@5.4.6(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4))': + '@vue/devtools-core@7.5.2(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.8(typescript@5.5.4))': dependencies: '@vue/devtools-kit': 7.5.2 '@vue/devtools-shared': 7.5.2 mitt: 3.0.1 nanoid: 3.3.7 pathe: 1.1.2 - vite-hot-client: 0.2.3(vite@5.4.6(@types/node@20.14.12)(less@4.2.0)) - vue: 3.5.12(typescript@5.5.4) + vite-hot-client: 0.2.3(vite@5.4.11(@types/node@20.14.12)(less@4.2.0)) + vue: 3.5.8(typescript@5.5.4) transitivePeerDependencies: - vite @@ -16163,6 +16725,8 @@ snapshots: acorn@8.12.1: {} + acorn@8.14.0: {} + agent-base@6.0.2(supports-color@9.4.0): dependencies: debug: 4.3.7(supports-color@9.4.0) @@ -16403,6 +16967,10 @@ snapshots: arrify@3.0.0: {} + as-table@1.0.55: + dependencies: + printable-characters: 1.0.42 + asap@2.0.6: {} ascii-table@0.0.9: {} @@ -16445,7 +17013,7 @@ snapshots: astro-iconify@1.2.0: dependencies: - '@iconify/tools': 4.0.7 + '@iconify/tools': 4.1.1 node-fetch: 3.3.2 resolve-pkg: 2.0.0 svgo: 3.3.2 @@ -16531,8 +17099,8 @@ snapshots: tsconfck: 3.1.3(typescript@5.5.4) unist-util-visit: 5.0.0 vfile: 6.0.3 - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) - vitefu: 1.0.2(vite@5.4.6(@types/node@20.14.12)(less@4.2.0)) + vite: 5.4.11(@types/node@20.14.12)(less@4.2.0) + vitefu: 1.0.2(vite@5.4.11(@types/node@20.14.12)(less@4.2.0)) which-pm: 3.0.0 xxhash-wasm: 1.0.2 yargs-parser: 21.1.1 @@ -16618,6 +17186,14 @@ snapshots: transitivePeerDependencies: - debug + axios@1.7.9: + dependencies: + follow-redirects: 1.15.6(debug@4.3.4) + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + axobject-query@4.1.0: {} b4a@1.6.6: {} @@ -16724,6 +17300,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + blake3-wasm@2.1.5: {} + blueimp-md5@2.19.0: {} body-parser@1.20.2: @@ -16928,6 +17506,13 @@ snapshots: caniuse-lite@1.0.30001663: {} + capnp-ts@0.7.0: + dependencies: + debug: 4.4.0 + tslib: 2.6.2 + transitivePeerDependencies: + - supports-color + ccount@2.0.1: {} chai@5.1.1: @@ -17022,6 +17607,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chokidar@4.0.1: + dependencies: + readdirp: 4.0.2 + chownr@1.1.4: {} chownr@2.0.0: {} @@ -17240,6 +17829,8 @@ snapshots: confbox@0.1.7: {} + confbox@0.1.8: {} + config-chain@1.1.13: dependencies: ini: 1.3.8 @@ -17284,6 +17875,8 @@ snapshots: cookie@0.6.0: {} + cookie@0.7.2: {} + cookie@1.0.2: {} copy-anything@2.0.6: @@ -17430,6 +18023,8 @@ snapshots: cyclist@1.0.2: {} + data-uri-to-buffer@2.0.2: {} + data-uri-to-buffer@3.0.1: {} data-uri-to-buffer@4.0.1: {} @@ -17454,6 +18049,8 @@ snapshots: dataloader@1.4.0: {} + date-fns@4.1.0: {} + date-time@3.1.0: dependencies: time-zone: 1.0.0 @@ -17472,16 +18069,16 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.3.7: - dependencies: - ms: 2.1.3 - debug@4.3.7(supports-color@9.4.0): dependencies: ms: 2.1.3 optionalDependencies: supports-color: 9.4.0 + debug@4.4.0: + dependencies: + ms: 2.1.3 + decache@4.6.2: dependencies: callsite: 1.0.0 @@ -17758,7 +18355,7 @@ snapshots: base64id: 2.0.0 cookie: 0.4.2 cors: 2.8.5 - debug: 4.3.7 + debug: 4.3.7(supports-color@9.4.0) engine.io-parser: 5.2.3 ws: 8.17.1 transitivePeerDependencies: @@ -17895,6 +18492,31 @@ snapshots: local-pkg: 0.5.0 resolve.exports: 2.0.2 + esbuild@0.17.19: + optionalDependencies: + '@esbuild/android-arm': 0.17.19 + '@esbuild/android-arm64': 0.17.19 + '@esbuild/android-x64': 0.17.19 + '@esbuild/darwin-arm64': 0.17.19 + '@esbuild/darwin-x64': 0.17.19 + '@esbuild/freebsd-arm64': 0.17.19 + '@esbuild/freebsd-x64': 0.17.19 + '@esbuild/linux-arm': 0.17.19 + '@esbuild/linux-arm64': 0.17.19 + '@esbuild/linux-ia32': 0.17.19 + '@esbuild/linux-loong64': 0.17.19 + '@esbuild/linux-mips64el': 0.17.19 + '@esbuild/linux-ppc64': 0.17.19 + '@esbuild/linux-riscv64': 0.17.19 + '@esbuild/linux-s390x': 0.17.19 + '@esbuild/linux-x64': 0.17.19 + '@esbuild/netbsd-x64': 0.17.19 + '@esbuild/openbsd-x64': 0.17.19 + '@esbuild/sunos-x64': 0.17.19 + '@esbuild/win32-arm64': 0.17.19 + '@esbuild/win32-ia32': 0.17.19 + '@esbuild/win32-x64': 0.17.19 + esbuild@0.17.6: optionalDependencies: '@esbuild/android-arm': 0.17.6 @@ -18415,6 +19037,8 @@ snapshots: '@types/estree-jsx': 1.0.5 '@types/unist': 3.0.2 + estree-walker@0.6.1: {} + estree-walker@2.0.2: {} estree-walker@3.0.3: @@ -18476,6 +19100,21 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + execa@9.5.2: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.3 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.0 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.2.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.1 + exit-hook@2.2.1: {} expand-brackets@2.1.4: @@ -18765,6 +19404,10 @@ snapshots: escape-string-regexp: 5.0.0 is-unicode-supported: 1.3.0 + figures@6.1.0: + dependencies: + is-unicode-supported: 2.0.0 + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 @@ -19047,6 +19690,11 @@ snapshots: get-port@7.1.0: {} + get-source@2.0.12: + dependencies: + data-uri-to-buffer: 2.0.2 + source-map: 0.6.1 + get-stream@5.2.0: dependencies: pump: 3.0.0 @@ -19055,6 +19703,11 @@ snapshots: get-stream@8.0.1: {} + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 @@ -19096,6 +19749,8 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regexp@0.4.1: {} + glob@10.3.12: dependencies: foreground-child: 3.1.1 @@ -19136,6 +19791,8 @@ snapshots: dependencies: type-fest: 0.20.2 + globals@15.13.0: {} + globals@15.3.0: {} globalthis@1.0.3: @@ -19248,9 +19905,9 @@ snapshots: defu: 6.1.4 destr: 2.0.3 iron-webcrypto: 1.1.1 - ohash: 1.1.3 + ohash: 1.1.4 radix3: 1.1.2 - ufo: 1.5.3 + ufo: 1.5.4 uncrypto: 0.1.3 unenv: 1.9.0 transitivePeerDependencies: @@ -19633,6 +20290,8 @@ snapshots: human-signals@5.0.0: {} + human-signals@8.0.0: {} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -19969,6 +20628,8 @@ snapshots: is-stream@3.0.0: {} + is-stream@4.0.1: {} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -20037,6 +20698,8 @@ snapshots: isobject@3.0.1: {} + itty-time@1.0.6: {} + jackspeak@2.3.6: dependencies: '@isaacs/cliui': 8.0.2 @@ -20274,7 +20937,7 @@ snapshots: node-forge: 1.3.1 pathe: 1.1.2 std-env: 3.7.0 - ufo: 1.5.3 + ufo: 1.5.4 untun: 0.1.3 uqr: 0.1.2 transitivePeerDependencies: @@ -20310,6 +20973,11 @@ snapshots: mlly: 1.7.1 pkg-types: 1.1.3 + local-pkg@0.5.1: + dependencies: + mlly: 1.7.3 + pkg-types: 1.2.1 + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -20439,6 +21107,10 @@ snapshots: macos-release@3.2.0: {} + magic-string@0.25.9: + dependencies: + sourcemap-codec: 1.4.8 + magic-string@0.30.11: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -21408,6 +22080,25 @@ snapshots: min-indent@1.0.1: {} + miniflare@3.20241205.0: + dependencies: + '@cspotcode/source-map-support': 0.8.1 + acorn: 8.14.0 + acorn-walk: 8.3.3 + capnp-ts: 0.7.0 + exit-hook: 2.2.1 + glob-to-regexp: 0.4.1 + stoppable: 1.1.0 + undici: 5.28.4 + workerd: 1.20241205.0 + ws: 8.18.0 + youch: 3.3.4 + zod: 3.23.8 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 @@ -21481,14 +22172,21 @@ snapshots: acorn: 8.12.1 pathe: 1.1.2 pkg-types: 1.1.0 - ufo: 1.5.3 + ufo: 1.5.4 mlly@1.7.1: dependencies: acorn: 8.12.1 pathe: 1.1.2 pkg-types: 1.1.3 - ufo: 1.5.3 + ufo: 1.5.4 + + mlly@1.7.3: + dependencies: + acorn: 8.14.0 + pathe: 1.1.2 + pkg-types: 1.2.1 + ufo: 1.5.4 modern-ahocorasick@1.0.1: {} @@ -21531,6 +22229,8 @@ snapshots: safe-buffer: 5.2.1 uid-safe: 2.1.5 + mustache@4.2.0: {} + mute-stream@0.0.7: {} mute-stream@1.0.0: {} @@ -21544,6 +22244,8 @@ snapshots: nan@2.19.0: optional: true + nano-spawn@0.2.0: {} + nanoassert@2.0.0: {} nanoid@3.3.7: {} @@ -22078,6 +22780,11 @@ snapshots: dependencies: path-key: 4.0.0 + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + npmlog@5.0.1: dependencies: are-we-there-yet: 2.0.0 @@ -22145,9 +22852,9 @@ snapshots: dependencies: destr: 2.0.3 node-fetch-native: 1.6.4 - ufo: 1.5.3 + ufo: 1.5.4 - ohash@1.1.3: {} + ohash@1.1.4: {} omit.js@2.0.2: {} @@ -22443,6 +23150,8 @@ snapshots: parse-ms@3.0.0: {} + parse-ms@4.0.0: {} + parse-node-version@1.0.1: {} parse5-htmlparser2-tree-adapter@7.0.0: @@ -22483,6 +23192,8 @@ snapshots: path-to-regexp@0.1.7: {} + path-to-regexp@6.3.0: {} + path-type@4.0.0: {} path-type@5.0.0: {} @@ -22581,7 +23292,7 @@ snapshots: pkg-types@1.1.0: dependencies: confbox: 0.1.7 - mlly: 1.7.1 + mlly: 1.7.3 pathe: 1.1.2 pkg-types@1.1.3: @@ -22590,6 +23301,12 @@ snapshots: mlly: 1.7.1 pathe: 1.1.2 + pkg-types@1.2.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.3 + pathe: 1.1.2 + posix-character-classes@0.1.1: {} possible-typed-array-names@1.0.0: {} @@ -22768,11 +23485,17 @@ snapshots: dependencies: parse-ms: 3.0.0 + pretty-ms@9.2.0: + dependencies: + parse-ms: 4.0.0 + prettyjson@1.2.5: dependencies: colors: 1.4.0 minimist: 1.2.8 + printable-characters@1.0.42: {} + prismjs@1.29.0: {} proc-log@3.0.0: {} @@ -23112,6 +23835,8 @@ snapshots: dependencies: picomatch: 2.3.1 + readdirp@4.0.2: {} + real-require@0.2.0: {} redent@3.0.0: @@ -23405,6 +24130,20 @@ snapshots: dependencies: glob: 7.2.3 + rollup-plugin-inject@3.0.2: + dependencies: + estree-walker: 0.6.1 + magic-string: 0.25.9 + rollup-pluginutils: 2.8.2 + + rollup-plugin-node-polyfills@0.2.1: + dependencies: + rollup-plugin-inject: 3.0.2 + + rollup-pluginutils@2.8.2: + dependencies: + estree-walker: 0.6.1 + rollup@4.19.0: dependencies: '@types/estree': 1.0.5 @@ -23523,6 +24262,11 @@ snapshots: dependencies: commander: 2.20.3 + selfsigned@2.4.1: + dependencies: + '@types/node-forge': 1.3.11 + node-forge: 1.3.1 + semver-diff@4.0.0: dependencies: semver: 7.6.3 @@ -23763,7 +24507,7 @@ snapshots: socket.io-adapter@2.5.5: dependencies: - debug: 4.3.7 + debug: 4.3.7(supports-color@9.4.0) ws: 8.17.1 transitivePeerDependencies: - bufferutil @@ -23784,7 +24528,7 @@ snapshots: socket.io-parser@4.2.4: dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 + debug: 4.3.7(supports-color@9.4.0) transitivePeerDependencies: - supports-color @@ -23793,7 +24537,7 @@ snapshots: accepts: 1.3.8 base64id: 2.0.0 cors: 2.8.5 - debug: 4.3.7 + debug: 4.3.7(supports-color@9.4.0) engine.io: 6.6.1 socket.io-adapter: 2.5.5 socket.io-parser: 4.2.4 @@ -23847,6 +24591,8 @@ snapshots: dependencies: whatwg-url: 7.1.0 + sourcemap-codec@1.4.8: {} + space-separated-tokens@2.0.2: {} spawndamnit@2.0.0: @@ -23896,6 +24642,11 @@ snapshots: stackframe@1.3.4: {} + stacktracey@2.1.8: + dependencies: + as-table: 1.0.55 + get-source: 2.0.12 + starlight-blog@0.11.1(@astrojs/starlight@0.25.5(astro@4.15.9(@types/node@20.14.12)(less@4.2.0)(rollup@4.21.3)(typescript@5.5.4)))(astro@4.15.9(@types/node@20.14.12)(less@4.2.0)(rollup@4.21.3)(typescript@5.5.4)): dependencies: '@astrojs/rss': 4.0.5 @@ -23937,6 +24688,8 @@ snapshots: stdin-discarder@0.2.2: {} + stoppable@1.1.0: {} + stream-replace-string@2.0.0: {} stream-shift@1.0.3: {} @@ -24060,6 +24813,8 @@ snapshots: strip-final-newline@3.0.0: {} + strip-final-newline@4.0.0: {} + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 @@ -24427,7 +25182,7 @@ snapshots: cac: 6.7.14 chokidar: 3.6.0 consola: 3.2.3 - debug: 4.3.7 + debug: 4.3.7(supports-color@9.4.0) esbuild: 0.23.0 execa: 5.1.1 globby: 11.1.0 @@ -24598,6 +25353,8 @@ snapshots: ufo@1.5.3: {} + ufo@1.5.4: {} + uid-safe@2.1.5: dependencies: random-bytes: 1.0.0 @@ -24622,8 +25379,19 @@ snapshots: undici-types@5.26.5: {} + undici@5.28.4: + dependencies: + '@fastify/busboy': 2.1.1 + undici@6.19.7: {} + unenv-nightly@2.0.0-20241204-140205-a5d5190: + dependencies: + defu: 6.1.4 + ohash: 1.1.4 + pathe: 1.1.2 + ufo: 1.5.4 + unenv@1.9.0: dependencies: consola: 3.2.3 @@ -24641,6 +25409,8 @@ snapshots: unicorn-magic@0.1.0: {} + unicorn-magic@0.3.0: {} + unified-engine@11.2.1: dependencies: '@types/concat-stream': 2.0.3 @@ -24860,7 +25630,7 @@ snapshots: mri: 1.2.0 node-fetch-native: 1.6.4 ofetch: 1.3.4 - ufo: 1.5.3 + ufo: 1.5.4 optionalDependencies: '@netlify/blobs': 7.3.0 transitivePeerDependencies: @@ -25024,10 +25794,6 @@ snapshots: dependencies: vite: 5.4.11(@types/node@20.14.12)(less@4.2.0) - vite-hot-client@0.2.3(vite@5.4.6(@types/node@20.14.12)(less@4.2.0)): - dependencies: - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) - vite-node@1.6.0(@types/node@20.14.12)(less@4.2.0): dependencies: cac: 6.7.14 @@ -25049,9 +25815,9 @@ snapshots: vite-node@2.1.2(@types/node@20.14.12)(less@4.2.0): dependencies: cac: 6.7.14 - debug: 4.3.7 + debug: 4.3.7(supports-color@9.4.0) pathe: 1.1.2 - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) + vite: 5.4.11(@types/node@20.14.12)(less@4.2.0) transitivePeerDependencies: - '@types/node' - less @@ -25079,25 +25845,9 @@ snapshots: - rollup - supports-color - vite-plugin-inspect@0.8.7(rollup@4.21.3)(vite@5.4.6(@types/node@20.14.12)(less@4.2.0)): - dependencies: - '@antfu/utils': 0.7.10 - '@rollup/pluginutils': 5.1.0(rollup@4.21.3) - debug: 4.3.7(supports-color@9.4.0) - error-stack-parser-es: 0.1.5 - fs-extra: 11.2.0 - open: 10.1.0 - perfect-debounce: 1.0.0 - picocolors: 1.1.0 - sirv: 2.0.4 - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) - transitivePeerDependencies: - - rollup - - supports-color - - vite-plugin-vue-devtools@7.5.2(rollup@4.21.3)(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.8(typescript@5.5.4)): + vite-plugin-vue-devtools@7.5.2(rollup@4.21.3)(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4)): dependencies: - '@vue/devtools-core': 7.5.2(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.8(typescript@5.5.4)) + '@vue/devtools-core': 7.5.2(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4)) '@vue/devtools-kit': 7.5.2 '@vue/devtools-shared': 7.5.2 execa: 8.0.1 @@ -25111,16 +25861,16 @@ snapshots: - supports-color - vue - vite-plugin-vue-devtools@7.5.2(rollup@4.21.3)(vite@5.4.6(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4)): + vite-plugin-vue-devtools@7.5.2(rollup@4.21.3)(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.8(typescript@5.5.4)): dependencies: - '@vue/devtools-core': 7.5.2(vite@5.4.6(@types/node@20.14.12)(less@4.2.0))(vue@3.5.12(typescript@5.5.4)) + '@vue/devtools-core': 7.5.2(vite@5.4.11(@types/node@20.14.12)(less@4.2.0))(vue@3.5.8(typescript@5.5.4)) '@vue/devtools-kit': 7.5.2 '@vue/devtools-shared': 7.5.2 execa: 8.0.1 sirv: 2.0.4 - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) - vite-plugin-inspect: 0.8.7(rollup@4.21.3)(vite@5.4.6(@types/node@20.14.12)(less@4.2.0)) - vite-plugin-vue-inspector: 5.2.0(vite@5.4.6(@types/node@20.14.12)(less@4.2.0)) + vite: 5.4.11(@types/node@20.14.12)(less@4.2.0) + vite-plugin-inspect: 0.8.7(rollup@4.21.3)(vite@5.4.11(@types/node@20.14.12)(less@4.2.0)) + vite-plugin-vue-inspector: 5.2.0(vite@5.4.11(@types/node@20.14.12)(less@4.2.0)) transitivePeerDependencies: - '@nuxt/kit' - rollup @@ -25142,21 +25892,6 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-vue-inspector@5.2.0(vite@5.4.6(@types/node@20.14.12)(less@4.2.0)): - dependencies: - '@babel/core': 7.25.2 - '@babel/plugin-proposal-decorators': 7.24.1(@babel/core@7.25.2) - '@babel/plugin-syntax-import-attributes': 7.24.1(@babel/core@7.25.2) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.25.2) - '@babel/plugin-transform-typescript': 7.24.7(@babel/core@7.25.2) - '@vue/babel-plugin-jsx': 1.2.2(@babel/core@7.25.2) - '@vue/compiler-dom': 3.5.12 - kolorist: 1.8.0 - magic-string: 0.30.11 - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) - transitivePeerDependencies: - - supports-color - vite-svg-loader@5.1.0(vue@3.5.12(typescript@5.5.4)): dependencies: svgo: 3.3.2 @@ -25193,31 +25928,21 @@ snapshots: fsevents: 2.3.3 less: 4.2.0 - vite@5.4.6(@types/node@20.14.12)(less@4.2.0): - dependencies: - esbuild: 0.21.5 - postcss: 8.4.47 - rollup: 4.21.3 - optionalDependencies: - '@types/node': 20.14.12 - fsevents: 2.3.3 - less: 4.2.0 - - vitefu@1.0.2(vite@5.4.6(@types/node@20.14.12)(less@4.2.0)): + vitefu@1.0.2(vite@5.4.11(@types/node@20.14.12)(less@4.2.0)): optionalDependencies: - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) + vite: 5.4.11(@types/node@20.14.12)(less@4.2.0) vitest@2.1.2(@types/node@20.14.12)(less@4.2.0): dependencies: '@vitest/expect': 2.1.2 - '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.4.6(@types/node@20.14.12)(less@4.2.0)) + '@vitest/mocker': 2.1.2(@vitest/spy@2.1.2)(vite@5.4.11(@types/node@20.14.12)(less@4.2.0)) '@vitest/pretty-format': 2.1.2 '@vitest/runner': 2.1.2 '@vitest/snapshot': 2.1.2 '@vitest/spy': 2.1.2 '@vitest/utils': 2.1.2 chai: 5.1.1 - debug: 4.3.7 + debug: 4.3.7(supports-color@9.4.0) magic-string: 0.30.12 pathe: 1.1.2 std-env: 3.7.0 @@ -25225,7 +25950,7 @@ snapshots: tinyexec: 0.3.0 tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.4.6(@types/node@20.14.12)(less@4.2.0) + vite: 5.4.11(@types/node@20.14.12)(less@4.2.0) vite-node: 2.1.2(@types/node@20.14.12)(less@4.2.0) why-is-node-running: 2.3.0 optionalDependencies: @@ -25613,6 +26338,68 @@ snapshots: assert-never: 1.2.1 babel-walk: 3.0.0-canary-5 + workerd@1.20241205.0: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20241205.0 + '@cloudflare/workerd-darwin-arm64': 1.20241205.0 + '@cloudflare/workerd-linux-64': 1.20241205.0 + '@cloudflare/workerd-linux-arm64': 1.20241205.0 + '@cloudflare/workerd-windows-64': 1.20241205.0 + + wrangler@3.95.0: + dependencies: + '@cloudflare/kv-asset-handler': 0.3.4 + '@cloudflare/workers-shared': 0.11.0 + '@esbuild-plugins/node-globals-polyfill': 0.2.3(esbuild@0.17.19) + '@esbuild-plugins/node-modules-polyfill': 0.2.2(esbuild@0.17.19) + blake3-wasm: 2.1.5 + chokidar: 4.0.1 + date-fns: 4.1.0 + esbuild: 0.17.19 + itty-time: 1.0.6 + miniflare: 3.20241205.0 + nanoid: 3.3.7 + path-to-regexp: 6.3.0 + resolve: 1.22.8 + selfsigned: 2.4.1 + source-map: 0.6.1 + unenv: unenv-nightly@2.0.0-20241204-140205-a5d5190 + workerd: 1.20241205.0 + xxhash-wasm: 1.0.2 + optionalDependencies: + fsevents: 2.3.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + wrangler@3.96.0(@cloudflare/workers-types@4.20241112.0): + dependencies: + '@cloudflare/kv-asset-handler': 0.3.4 + '@esbuild-plugins/node-globals-polyfill': 0.2.3(esbuild@0.17.19) + '@esbuild-plugins/node-modules-polyfill': 0.2.2(esbuild@0.17.19) + blake3-wasm: 2.1.5 + chokidar: 4.0.1 + date-fns: 4.1.0 + esbuild: 0.17.19 + itty-time: 1.0.6 + miniflare: 3.20241205.0 + nanoid: 3.3.7 + path-to-regexp: 6.3.0 + resolve: 1.22.8 + selfsigned: 2.4.1 + source-map: 0.6.1 + unenv: unenv-nightly@2.0.0-20241204-140205-a5d5190 + workerd: 1.20241205.0 + xxhash-wasm: 1.0.2 + optionalDependencies: + '@cloudflare/workers-types': 4.20241112.0 + fsevents: 2.3.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -25664,6 +26451,8 @@ snapshots: ws@8.17.1: {} + ws@8.18.0: {} + xdg-basedir@5.1.0: {} xml-name-validator@4.0.0: {} @@ -25761,6 +26550,14 @@ snapshots: yocto-queue@1.1.1: {} + yoctocolors@2.1.1: {} + + youch@3.3.4: + dependencies: + cookie: 0.7.2 + mustache: 4.2.0 + stacktracey: 2.1.8 + zhead@2.2.4: {} zip-stream@5.0.2: