diff --git a/README.md b/README.md index 47f6f73..b0d094d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,17 @@ # ⛨ CrossWS -[![npm version][npm-version-src]][npm-version-href] -[![npm downloads][npm-downloads-src]][npm-downloads-href] + -👉 Elegant, typed, and simple interface to implement platform-agnostic WebSocket servers +[![npm version](https://flat.badgen.net/npm/v/crossws)](https://npmjs.com/package/crossws) +[![npm downloads](https://flat.badgen.net/npm/dm/crossws)](https://npmjs.com/package/crossws) + + + +Elegant, typed, and simple interface to implement platform-agnostic WebSocket servers. + +👉 [📖 documentation](https://crossws.unjs.io) + +## Features 🧩 Seamlessly integrates with, [Bun](https://bun.sh/), [Deno](https://deno.com/), [Cloudflare Workers](https://workers.cloudflare.com/) and [Node.js](https://nodejs.org/en) ([ws](https://github.com/websockets/ws) || [uWebSockets](https://github.com/uNetworking/uWebSockets.js)). @@ -15,298 +23,41 @@ 🔍 Developer-friendly object logging -> [!WARNING] +> [!IMPORTANT] > This project and API are under development. -## Install - -```sh -# npm -npm install crossws - -# yarn -yarn add crossws - -# pnpm -pnpm install crossws - -# bun -bun install crossws -``` - -## Unified Hooks - -CrossWS provides a cross-platform API to define WebSocket servers. An implementation with these hooks works across runtimes without needing you to go into details of any of them (while you always have the power to control low-level hooks). You can only define the life-cycle hooks that you only need and only those will be called run runtime. - -> [!NOTE] -> For type support and IDE auto-completion, you can use `defineWebSocketHooks` utility or `WebSocketHooks` type export from the main. - -```ts -import { defineWebSocketHooks } from "crossws"; - -const websocketHooks = defineWebSocketHooks({ - open(peer) { - console.log("[ws] open", peer); - }, - - message(peer, message) { - console.log("[ws] message", peer, message); - if (message.text().includes("ping")) { - peer.send("pong"); - } - }, - - close(peer, event) { - console.log("[ws] close", peer, event); - }, - - error(peer, error) { - console.log("[ws] error", peer, error); - }, - - // ... platform hooks such as bun:drain ... -}); -``` - -### Peer Object - -Websocket hooks always accept a peer instance as the first argument. `peer`, keeps the state of the connected client. - -**Properties:** - -- `peer.id?`: The peer address or unique id (might be `undefined`) -- `peer.readyState`: The connection status ([MDN](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/readyState)) -- `peer.ctx[name]`: Keeps the state of native client connection - -**Methods:** - -- `send(message, compress)`: Send a message to the connected client - -> [!TIP] -> You can safely log a peer instance to the console using `console.log` it will be automatically stringified with useful information including the remote address and connection status! - -### Message Object - -on `message` hook, you receive a message object containing an incoming message from the client. - -**Properties:** - -- `message.rawData`: Raw message data -- `message.isBinary`: Indicates if the message is binary (might be `undefined`) - -**Methods:** - -- `message.text()`: Get stringified version of the message - -> [!TIP] -> You can safely log `message` object to the console using `console.log` it will be automatically stringified! - -## Error handling - -You can catch errors using `error` hook. The second argument is error wrapped into a `WebSocketError` class. - -## Universal WebSocket client - -CrossWS also exposes a universal way to use [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) API in order to connect to the server. For all runtimes, except Node.js, native implementation is used, and for Node.js, a bundled version of [`ws.WebSocket`](https://www.npmjs.com/package/ws) is bundled. - -```js -import WebSocket from "crossws/websocket"; -``` - -> [!NOTE] -> Using export conditions, the correct version will be always used so you don't have to worry about picking the right build! - -## Integrations - -CrossWS allows integrating your WebSocket hooks with different runtimes and platforms using built-in adapters. Each runtime has a specific method of integrating WebSocket. Once integrated, your custom hooks (such as `message` and `close`) will work consistently even if you change the runtime! - -### Integration with **Node.js** (ws) - -To integrate CrossWS with your Node.js HTTP server, you need to connect the `upgrade` event to the `handleUpgrade` method returned from the adapter. Behind the scenes, CrossWS uses an embedded version of [ws](https://github.com/websockets/ws). - -```ts -import { createServer } from "node:http"; -import wsAdapter from "crossws/adapters/node"; - -const server = createServer((req, res) => { - res.end( - ``, - ); -}).listen(3000); - -const { handleUpgrade } = wsAdapter({ message: console.log }); -server.on("upgrade", handleUpgrade); -``` - -**Adapter hooks:** +## Contribution -- `node:open (peer)` -- `node:message (peer, data, isBinary)` -- `node:close (peer, code, reason)` -- `node:error (peer, error)` -- `node:ping (peer)` -- `node:pong (peer)` -- `node:unexpected-response (peer, req, res)` -- `node:upgrade (peer, req)` - -See [`playground/node.ts`](./playground/node.ts) for demo and [`src/adapters/node.ts`](./src/adapters/node.ts) for implementation. - -### Integration with **Node.js** (uWebSockets) - -You can alternatively use [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js) server for Node.js WebSockets. - -```ts -import { App } from "uWebSockets.js"; -import wsAdapter from "crossws/adapters/node-uws"; - -const { websocket } = wsAdapter({ message: console.log }); - -const server = App().ws("/*", websocket); - -server.get("/*", (res, req) => { - res.writeStatus("200 OK").writeHeader("Content-Type", "text/html"); - res.end( - ``, - ); -}); - -server.listen(3001, () => { - console.log("Listening to port 3001"); -}); -``` - -**Adapter hooks:** - -- `uws:open(ws)` -- `uws:message(ws, message, isBinary)` -- `uws:close(ws, code, message)` -- `uws:ping(ws, message)` -- `uws:pong(ws, message)` -- `uws:drain(ws)` -- `uws:upgrade (res, req, context)` -- `subscription(ws, topic, newCount, oldCount)` - -See [`playground/node-uws.ts`](./playground/node-uws.ts) for demo and [`src/adapters/node-uws.ts`](./src/adapters/node-uws.ts) for implementation. - -### Integration with **Bun** - -To integrate CrossWS with your Bun server, you need to check for `server.upgrade` and also pass the `websocket` object returned from the adapter to server options. CrossWS leverages native Bun WebSocket API. - -```ts -import wsAdapter from "./dist/adapters/bun"; - -const { websocket } = wsAdapter({ message: console.log }); - -Bun.serve({ - port: 3000, - websocket, - fetch(req, server) { - if (server.upgrade(req)) { - return; - } - return new Response( - ``, - { headers: { "content-type": "text/html" } }, - ); - }, -}); -``` - -**Adapter hooks:** - -- `bun:message (peer, ws,message)` -- `bun:open (peer, ws)` -- `bun:close (peer, ws)` -- `bun:drain (peer)` -- `bun:error (peer, ws, error)` -- `bun:ping (peer, ws, data)` -- `bun:pong (peer, ws, data)` - -See [`playground/bun.ts`](./playground/bun.ts) for demo and [`src/adapters/bun.ts]`(./src/adapters/bun.ts) for implementation. - -### Integration with **Deno** - -To integrate CrossWS with your Deno server, you need to check for the `upgrade` header and then call `handleUpgrade` method from the adapter passing the incoming request object. The returned value is the server upgrade response. - -```ts -import wsAdapter from "crossws/adapters/deno"; - -const { handleUpgrade } = wsAdapter({ message: console.log }); - -Deno.serve({ port: 3000 }, (request) => { - if (request.headers.get("upgrade") === "websocket") { - return handleUpgrade(request); - } - return new Response( - ``, - { headers: { "content-type": "text/html" } }, - ); -}); -``` - -**Adapter hooks:** - -- `deno:open (peer)` -- `deno:message (peer, event)` -- `deno:close (peer)` -- `deno:error (peer, error)` - -See [`playground/deno.ts`](./playground/deno.ts) for demo and [`src/adapters/deno.ts`](./src/adapters/deno.ts) for implementation. - -### Integration with **Cloudflare Workers** - -To integrate CrossWS with your Cloudflare Workers, you need to check for the `upgrade` header - -```ts -import wsAdapter from "crossws/adapters/cloudflare"; - -const { handleUpgrade } = wsAdapter({ message: console.log }); - -export default { - async fetch(request, env, context) { - if (request.headers.get("upgrade") === "websocket") { - return handleUpgrade(request, env, context); - } - return new Response( - ``, - { headers: { "content-type": "text/html" } }, - ); - }, -}; -``` - -**Adapter hooks:** - -- `cloudflare:accept(peer)` -- `cloudflare:message(peer, event)` -- `cloudflare:error(peer, event)` -- `cloudflare:close(peer, event)` - -See [`playground/cloudflare.ts`](./playground/cloudflare.ts) for demo and [`src/adapters/cloudflare.ts`](./src/adapters/cloudflare.ts) for implementation. - -### Integration with other runtimes - -You can define your custom adapters using `defineWebSocketAdapter` wrapper. - -See other adapter implementations in [./src/adapters](./src/adapters/) to get an idea of how adapters can be implemented and feel free to directly make a Pull Request to support your environment in CrossWS! - -## Development +
+ Local development - Clone this repository - Install the latest LTS version of [Node.js](https://nodejs.org/en/) - Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable` - Install dependencies using `pnpm install` -- Run interactive tests using `pnpm dev` +- Run examples using `pnpm play:` scripts + +
+ + ## License -Made with 💛 + + +Published under the [MIT](https://github.com/unjs/crossws/blob/main/LICENSE) license. +Made by [@pi0](https://github.com/pi0) and [community](https://github.com/unjs/crossws/graphs/contributors) 💛 +

+ + + + + + + -Published under [MIT License](./LICENSE). +--- - +_🤖 auto updated with [automd](https://automd.unjs.io)_ -[npm-version-src]: https://img.shields.io/npm/v/crossws?style=flat&colorA=18181B&colorB=F0DB4F -[npm-version-href]: https://npmjs.com/package/crossws -[npm-downloads-src]: https://img.shields.io/npm/dm/crossws?style=flat&colorA=18181B&colorB=F0DB4F -[npm-downloads-href]: https://npmjs.com/package/crossws + diff --git a/docs/2.adapters/bun.md b/docs/2.adapters/bun.md index 2cff310..26e88aa 100644 --- a/docs/2.adapters/bun.md +++ b/docs/2.adapters/bun.md @@ -9,7 +9,7 @@ icon: simple-icons:bun To integrate CrossWS with your Bun server, you need to handle upgrade with `handleUpgrade` util and also pass the `websocket` object returned from the adapter to server options. CrossWS leverages native Bun WebSocket API. ```ts -import wsAdapter from "./dist/adapters/bun"; +import wsAdapter from "crossws/adapters/bun"; const { websocket, handleUpgrade } = wsAdapter({ message: console.log }); diff --git a/docs/2.adapters/node.md b/docs/2.adapters/node.md index 8d11f7f..f6d18fe 100644 --- a/docs/2.adapters/node.md +++ b/docs/2.adapters/node.md @@ -45,7 +45,7 @@ Instead of [using `ws`](/adapters/node-ws) you can use [uWebSockets.js](https:// ```ts import { App } from "uWebSockets.js"; -import wsAdapter from "crossws/adapters/node-uws"; +import wsAdapter from "crossws/adapters/uws"; const { websocket } = wsAdapter({ message: console.log }); diff --git a/package.json b/package.json index ba15c30..ab27458 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "@types/node": "^20.11.10", "@types/web": "^0.0.135", "@types/ws": "^8.5.10", + "automd": "^0.3.6", "changelogen": "^0.5.5", "consola": "^3.2.3", "eslint": "^8.56.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c01dd3c..14b0776 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: '@types/ws': specifier: ^8.5.10 version: 8.5.10 + automd: + specifier: ^0.3.6 + version: 0.3.6 changelogen: specifier: ^0.5.5 version: 0.5.5 @@ -252,6 +255,13 @@ packages: '@babel/types': 7.23.9 dev: true + /@babel/runtime@7.23.9: + resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.1 + dev: true + /@babel/standalone@7.23.9: resolution: {integrity: sha512-89NGhVfgKDqDQrtNPxqfnhIReKvp2CR80ofPNEAUpbtnouFelq33hQFURLralD9I+eFS7s5zVK61JRg/D1nLWg==} engines: {node: '>=6.9.0'} @@ -910,6 +920,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-android-arm64@2.4.1: + resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-darwin-arm64@2.4.0: resolution: {integrity: sha512-T/At5pansFuQ8VJLRx0C6C87cgfqIYhW2N/kBfLCUvDhCah0EnLLwaD/6MW3ux+rpgkpQAnMELOCTKlbwncwiA==} engines: {node: '>= 10.0.0'} @@ -918,6 +937,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-darwin-arm64@2.4.1: + resolution: {integrity: sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-darwin-x64@2.4.0: resolution: {integrity: sha512-vZMv9jl+szz5YLsSqEGCMSllBl1gU1snfbRL5ysJU03MEa6gkVy9OMcvXV1j4g0++jHEcvzhs3Z3LpeEbVmY6Q==} engines: {node: '>= 10.0.0'} @@ -926,6 +954,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-darwin-x64@2.4.1: + resolution: {integrity: sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-freebsd-x64@2.4.0: resolution: {integrity: sha512-dHTRMIplPDT1M0+BkXjtMN+qLtqq24sLDUhmU+UxxLP2TEY2k8GIoqIJiVrGWGomdWsy5IO27aDV1vWyQ6gfHA==} engines: {node: '>= 10.0.0'} @@ -934,6 +971,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-freebsd-x64@2.4.1: + resolution: {integrity: sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-linux-arm-glibc@2.4.0: resolution: {integrity: sha512-9NQXD+qk46RwATNC3/UB7HWurscY18CnAPMTFcI9Y8CTbtm63/eex1SNt+BHFinEQuLBjaZwR2Lp+n7pmEJPpQ==} engines: {node: '>= 10.0.0'} @@ -942,6 +988,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-linux-arm-glibc@2.4.1: + resolution: {integrity: sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-linux-arm64-glibc@2.4.0: resolution: {integrity: sha512-QuJTAQdsd7PFW9jNGaV9Pw+ZMWV9wKThEzzlY3Lhnnwy7iW23qtQFPql8iEaSFMCVI5StNNmONUopk+MFKpiKg==} engines: {node: '>= 10.0.0'} @@ -950,6 +1005,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-linux-arm64-glibc@2.4.1: + resolution: {integrity: sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-linux-arm64-musl@2.4.0: resolution: {integrity: sha512-oyN+uA9xcTDo/45bwsd6TFHa7Lc7hKujyMlvwrCLvSckvWogndCEoVYFNfZ6JJ2KNL/6fFiGPcbjp8jJmEh5Ng==} engines: {node: '>= 10.0.0'} @@ -958,6 +1022,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-linux-arm64-musl@2.4.1: + resolution: {integrity: sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-linux-x64-glibc@2.4.0: resolution: {integrity: sha512-KphV8awJmxU3q52JQvJot0QMu07CIyEjV+2Tb2ZtbucEgqyRcxOBDMsqp1JNq5nuDXtcCC0uHQICeiEz38dPBQ==} engines: {node: '>= 10.0.0'} @@ -966,6 +1039,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-linux-x64-glibc@2.4.1: + resolution: {integrity: sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-linux-x64-musl@2.4.0: resolution: {integrity: sha512-7jzcOonpXNWcSijPpKD5IbC6xC7yTibjJw9jviVzZostYLGxbz8LDJLUnLzLzhASPlPGgpeKLtFUMjAAzM+gSA==} engines: {node: '>= 10.0.0'} @@ -974,6 +1056,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-linux-x64-musl@2.4.1: + resolution: {integrity: sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-wasm@2.4.0: resolution: {integrity: sha512-MNgQ4WCbBybqQ97KwR/hqJGYTg3+s8qHpgIyFWB2qJOBvoJWbXuJGmm4ZkPLq2bMaANqCZqrXwmKYagZTkMKZA==} engines: {node: '>= 10.0.0'} @@ -991,6 +1082,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-win32-arm64@2.4.1: + resolution: {integrity: sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-win32-ia32@2.4.0: resolution: {integrity: sha512-IO/nM+K2YD/iwjWAfHFMBPz4Zqn6qBDqZxY4j2n9s+4+OuTSRM/y/irksnuqcspom5DjkSeF9d0YbO+qpys+JA==} engines: {node: '>= 10.0.0'} @@ -999,6 +1099,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-win32-ia32@2.4.1: + resolution: {integrity: sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher-win32-x64@2.4.0: resolution: {integrity: sha512-pAUyUVjfFjWaf/pShmJpJmNxZhbMvJASUpdes9jL6bTEJ+gDxPRSpXTIemNyNsb9AtbiGXs9XduP1reThmd+dA==} engines: {node: '>= 10.0.0'} @@ -1007,6 +1116,15 @@ packages: requiresBuild: true optional: true + /@parcel/watcher-win32-x64@2.4.1: + resolution: {integrity: sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@parcel/watcher@2.4.0: resolution: {integrity: sha512-XJLGVL0DEclX5pcWa2N9SX1jCGTDd8l972biNooLFtjneuGqodupPQh6XseXIBBeVIMaaJ7bTcs3qGvXwsp4vg==} engines: {node: '>= 10.0.0'} @@ -1029,6 +1147,29 @@ packages: '@parcel/watcher-win32-ia32': 2.4.0 '@parcel/watcher-win32-x64': 2.4.0 + /@parcel/watcher@2.4.1: + resolution: {integrity: sha512-HNjmfLQEVRZmHRET336f20H/8kOozUGwk7yajvsonjNxbj2wBTK1WsQuHkD5yYh9RxFGL2EyDHryOihOwUoKDA==} + engines: {node: '>= 10.0.0'} + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.5 + node-addon-api: 7.1.0 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.4.1 + '@parcel/watcher-darwin-arm64': 2.4.1 + '@parcel/watcher-darwin-x64': 2.4.1 + '@parcel/watcher-freebsd-x64': 2.4.1 + '@parcel/watcher-linux-arm-glibc': 2.4.1 + '@parcel/watcher-linux-arm64-glibc': 2.4.1 + '@parcel/watcher-linux-arm64-musl': 2.4.1 + '@parcel/watcher-linux-x64-glibc': 2.4.1 + '@parcel/watcher-linux-x64-musl': 2.4.1 + '@parcel/watcher-win32-arm64': 2.4.1 + '@parcel/watcher-win32-ia32': 2.4.1 + '@parcel/watcher-win32-x64': 2.4.1 + dev: true + /@rollup/plugin-alias@5.1.0(rollup@3.29.4): resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==} engines: {node: '>=14.0.0'} @@ -1120,6 +1261,11 @@ packages: rollup: 3.29.4 dev: true + /@sindresorhus/merge-streams@2.3.0: + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + dev: true + /@trysound/sax@0.2.0: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -1443,6 +1589,31 @@ packages: printable-characters: 1.0.42 dev: true + /automd@0.3.6: + resolution: {integrity: sha512-6FmbZ6vTxFNohdU8ELwUUAee9u+ev8UNFk22A265P7g/IDviZthlWc4PTRgk1IlDJTXS1KHeMpl/PmuBgBOa+w==} + hasBin: true + dependencies: + '@parcel/watcher': 2.4.1 + c12: 1.9.0 + citty: 0.1.6 + consola: 3.2.3 + defu: 6.1.4 + destr: 2.0.3 + didyoumean2: 6.0.1 + globby: 14.0.1 + magic-string: 0.30.7 + mdbox: 0.1.0 + mlly: 1.6.1 + ofetch: 1.3.3 + pathe: 1.1.2 + perfect-debounce: 1.0.0 + pkg-types: 1.0.3 + scule: 1.3.0 + untyped: 1.4.2 + transitivePeerDependencies: + - supports-color + dev: true + /autoprefixer@10.4.17(postcss@8.4.33): resolution: {integrity: sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==} engines: {node: ^10 || ^12 || >=14} @@ -1565,6 +1736,23 @@ packages: rc9: 2.1.1 dev: true + /c12@1.9.0: + resolution: {integrity: sha512-7KTCZXdIbOA2hLRQ+1KzJ15Qp9Wn58one74dkihMVp2H6EzKTa3OYBy0BSfS1CCcmxYyqeX8L02m40zjQ+dstg==} + dependencies: + chokidar: 3.5.3 + confbox: 0.1.3 + defu: 6.1.4 + dotenv: 16.4.1 + giget: 1.2.1 + jiti: 1.21.0 + mlly: 1.6.1 + ohash: 1.1.3 + pathe: 1.1.2 + perfect-debounce: 1.0.0 + pkg-types: 1.0.3 + rc9: 2.1.1 + dev: true + /call-bind@1.0.5: resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} dependencies: @@ -1674,6 +1862,12 @@ packages: dependencies: consola: 3.2.3 + /citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + dependencies: + consola: 3.2.3 + dev: true + /clean-regexp@1.0.0: resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} engines: {node: '>=4'} @@ -1733,6 +1927,10 @@ packages: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} dev: true + /confbox@0.1.3: + resolution: {integrity: sha512-eH3ZxAihl1PhKfpr4VfEN6/vUd87fmgb6JkldHgg/YR6aEBhW63qUDgzP2Y6WM0UumdsYp5H3kibalXAdHfbgg==} + dev: true + /consola@3.2.3: resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} engines: {node: ^14.18.0 || >=16.10.0} @@ -1955,11 +2153,24 @@ packages: /destr@2.0.2: resolution: {integrity: sha512-65AlobnZMiCET00KaFFjUefxDX0khFA/E4myqZ7a6Sq1yZtR8+FVIvilVX66vF2uobSumxooYZChiRPCKNqhmg==} + /destr@2.0.3: + resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} + dev: true + /detect-libc@1.0.3: resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} engines: {node: '>=0.10'} hasBin: true + /didyoumean2@6.0.1: + resolution: {integrity: sha512-PSy0zQwMg5O+LjT5Mz7vnKC8I7DfWLPF6M7oepqW7WP5mn2CY3hz46xZOa1GJY+KVfyXhdmz6+tdgXwrHlZc5g==} + engines: {node: ^16.14.0 || >=18.12.0} + dependencies: + '@babel/runtime': 7.23.9 + fastest-levenshtein: 1.0.16 + lodash.deburr: 4.1.0 + dev: true + /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -2626,6 +2837,11 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true + /fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + dev: true + /fastq@1.17.0: resolution: {integrity: sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w==} dependencies: @@ -2878,6 +3094,18 @@ packages: slash: 4.0.0 dev: true + /globby@14.0.1: + resolution: {integrity: sha512-jOMLD2Z7MAhyG8aJpNOpmziMOP4rPLcc95oQPKXBazW82z+CEgPFBQvEpRUa1KeIMUJo4Wsm+q6uzO/Q/4BksQ==} + engines: {node: '>=18'} + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.2 + ignore: 5.3.0 + path-type: 5.0.0 + slash: 5.1.0 + unicorn-magic: 0.1.0 + dev: true + /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: @@ -3348,6 +3576,10 @@ packages: p-locate: 5.0.0 dev: true + /lodash.deburr@4.1.0: + resolution: {integrity: sha512-m/M1U1f3ddMCs6Hq2tAsYThTBDaAKFDX3dwDo97GEYzamXi9SqUpjWi/Rrj/gf3X2n8ktwgZrlP1z6E3v/IExQ==} + dev: true + /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true @@ -3390,6 +3622,23 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /magic-string@0.30.7: + resolution: {integrity: sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /md4w@0.2.2: + resolution: {integrity: sha512-dJwbVIueCp4HgI9Jg4I9pDVLiig1lAVXYqVmTrIVsobh3b50EtgQrXmFEzzRRR6HaK6xVqB/bd3Ac/Nt2kdwPA==} + dev: true + + /mdbox@0.1.0: + resolution: {integrity: sha512-eQA+6vf5XM4LqdfLsfPMxqUBSU8AMzSCSFbojWLXSDL2jZeO+xgHhxTggrG2jfGPAyyIWIukj6SuoFBd9a7XZw==} + dependencies: + md4w: 0.2.2 + dev: true + /mdn-data@2.0.28: resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} dev: true @@ -3534,6 +3783,15 @@ packages: pkg-types: 1.0.3 ufo: 1.3.2 + /mlly@1.6.1: + resolution: {integrity: sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==} + dependencies: + acorn: 8.11.3 + pathe: 1.1.2 + pkg-types: 1.0.3 + ufo: 1.3.2 + dev: true + /mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -3809,6 +4067,11 @@ packages: engines: {node: '>=8'} dev: true + /path-type@5.0.0: + resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} + engines: {node: '>=12'} + dev: true + /pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -4215,6 +4478,10 @@ packages: picomatch: 2.3.1 dev: true + /regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + dev: true + /regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true @@ -4361,6 +4628,10 @@ packages: resolution: {integrity: sha512-CRCmi5zHQnSoeCik9565PONMg0kfkvYmcSqrbOJY4txFfy1wvVULV4FDaiXhUblUgahdqz3F2NwHZ8i4eBTwUw==} dev: true + /scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + dev: true + /selfsigned@2.4.1: resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} engines: {node: '>=10'} @@ -4443,6 +4714,11 @@ packages: engines: {node: '>=12'} dev: true + /slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + dev: true + /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -4806,6 +5082,11 @@ packages: node-fetch-native: 1.6.1 pathe: 1.1.2 + /unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + dev: true + /universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'}