diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c0e966..ac74e58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,9 @@ jobs: - name: Run frontend checks run: pnpm check + - name: Validate OpenAPI contract + run: pnpm api:lint + test: name: PHP ${{ matrix.php }} runs-on: ubuntu-latest diff --git a/.spectral.yaml b/.spectral.yaml new file mode 100644 index 0000000..47000fd --- /dev/null +++ b/.spectral.yaml @@ -0,0 +1,9 @@ +extends: + - spectral:oas + +rules: + operation-description: off + operation-tags: error + operation-operationId: error + operation-operationId-unique: error + path-params: error diff --git a/README.md b/README.md index a31744e..14f9490 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ Start your web server and navigate to: See: - `docs/public-api.md` (HTTP + WebSocket + frontend entrypoints + reusable PHP APIs) +- `docs/openapi.yaml` (machine-readable OpenAPI 3.1 contract, rendered at `/api/docs`) - `docs/api.md` (message reaction HTTP and WebSocket contracts) - `docs/user-profiles.md` (profile settings, avatars, themes, and WebSocket presence) - `docs/channels.md` (public channels, private DMs, APIs, WebSocket routing, and migration operations) diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 785f5da..57d5334 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -31,6 +31,8 @@ $routes->get('chat/svelte', 'Chat::svelte'); // Stable, machine-consumed API routes. +$routes->get('api/docs', 'ApiDocs::index'); + $routes->group('api/v1', ['namespace' => 'App\Controllers\Api\V1'], static function (RouteCollection $routes): void { $routes->get('messages', 'MessagesController::list', ['filter' => ['apiFormat', 'auth']]); $routes->get('messages/search', 'MessagesController::search', ['filter' => ['apiFormat', 'auth']]); diff --git a/app/Config/Services.php b/app/Config/Services.php index 5ec37ba..b699084 100644 --- a/app/Config/Services.php +++ b/app/Config/Services.php @@ -15,6 +15,7 @@ use App\Controllers\Api\V1\MessagesController; use App\Controllers\Api\V1\PushSubscriptionsController; use App\Controllers\Api\V1\ReactionsController; +use App\Controllers\ApiDocs; use App\Controllers\AuditLog; use App\Controllers\Auth; use App\Controllers\Chat; @@ -72,6 +73,7 @@ public static function controller(string $controllerClass): Controller return match ($controllerClass) { Home::class => new Home(), + ApiDocs::class => new ApiDocs(), Chat::class => new Chat(static::chatRepository(), static::chatFormatter()), MessagesController::class => new MessagesController(static::chatRepository(), static::chatFormatter()), ChannelsController::class => new ChannelsController(static::channelRepository(), static::chatRepository(), static::userRepository()), diff --git a/app/Controllers/ApiDocs.php b/app/Controllers/ApiDocs.php new file mode 100644 index 0000000..e1390d3 --- /dev/null +++ b/app/Controllers/ApiDocs.php @@ -0,0 +1,23 @@ +response + ->setStatusCode(500) + ->setHeader('Content-Type', 'text/plain; charset=UTF-8') + ->setBody('The OpenAPI specification is unavailable.'); + } + + return $this->respondWithView('api/docs', [ + 'openapiBase64' => base64_encode($spec), + ], cacheable: true, cacheTime: 300); + } +} diff --git a/app/Views/api/docs.php b/app/Views/api/docs.php new file mode 100644 index 0000000..cd32a21 --- /dev/null +++ b/app/Views/api/docs.php @@ -0,0 +1,21 @@ + + + + + + + CodeIgniter Chat API documentation + + + + +
+
+
+ + + diff --git a/docs/contributing.md b/docs/contributing.md index 8e66564..d076d38 100755 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -64,6 +64,23 @@ pnpm format Please ensure the PHP and frontend checks pass before submitting a pull request. Biome covers repository JavaScript and TypeScript; framework-aware diagnostics are handled separately by `svelte-check` and `vue-tsc`. +### API contract + +`docs/openapi.yaml` is the reviewed source of truth for every `/api/v1/*` +operation. When a pull request adds, removes, or changes a versioned endpoint, +update the OpenAPI operation, schemas, status codes, and concrete examples in +the same pull request. + +Validate the contract locally with: + +```bash +pnpm api:lint +``` + +This runs Spectral's OpenAPI 3.1 rules and compares the documented operations +with the CodeIgniter route group. CI rejects invalid specifications and route +drift. The rendered contract is available at `/api/docs` in a running app. + ## Code Style Guidelines - Follow PSR-12 coding standards (enforced by PHP-CS-Fixer) diff --git a/docs/openapi.yaml b/docs/openapi.yaml new file mode 100644 index 0000000..81864f5 --- /dev/null +++ b/docs/openapi.yaml @@ -0,0 +1,1458 @@ +openapi: 3.1.0 +info: + title: CodeIgniter Chat API + version: 1.0.0 + description: | + Contract for the versioned HTTP API. Browser clients authenticate with the + CodeIgniter session cookie. Mutating requests also send the current CSRF + token in the `X-CSRF-TOKEN` header or form body. + + The YAML file is the source of truth. Generated client SDKs are a possible + follow-up and are intentionally not shipped by this project. + contact: + name: CodeIgniter Chat maintainers + url: https://github.com/llbbl/codeigniter-chat +servers: + - url: / + description: Current application origin + - url: http://localhost:8080 + description: Local development + - url: https://chat.example.com + description: Production placeholder +tags: + - name: Messages + - name: Channels + - name: Reactions + - name: Push subscriptions + - name: Profiles +security: + - sessionAuth: [] +paths: + /api/v1/messages: + get: + operationId: listMessages + tags: [Messages] + summary: List messages in the general channel + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PerPage' + responses: + '200': + $ref: '#/components/responses/GeneralMessagePage' + '401': + $ref: '#/components/responses/Unauthorized' + post: + operationId: createGeneralMessage + tags: [Messages] + summary: Create a message in the general channel + parameters: + - $ref: '#/components/parameters/CsrfToken' + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + type: object + required: [message] + properties: + message: + type: string + minLength: 1 + maxLength: 500 + example: + message: Hello from the API + responses: + '200': + description: Message created. + content: + application/json: + schema: + $ref: '#/components/schemas/Success' + example: + success: true + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/messages/search: + get: + operationId: searchMessages + tags: [Messages] + summary: Search live messages in the general channel + description: At least one of `text`, `user`, `from`, or `to` is required. + parameters: + - $ref: '#/components/parameters/SearchText' + - $ref: '#/components/parameters/SearchUser' + - $ref: '#/components/parameters/FromTimestamp' + - $ref: '#/components/parameters/ToTimestamp' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PerPage' + responses: + '200': + description: Matching messages, pagination, and normalized filters. + content: + application/json: + schema: + $ref: '#/components/schemas/MessageSearchResult' + example: + messages: + - id: 42 + channel_id: 1 + user: alice + msg: Hello from class + time: 1700000000 + pagination: + page: 1 + perPage: 10 + totalItems: 1 + totalPages: 1 + hasNext: false + hasPrev: false + filters: + text: Hello + user: alice + from: 1699990000 + to: 1700010000 + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + /api/v1/messages/xml: + get: + operationId: listMessagesXml + tags: [Messages] + summary: List general-channel messages as XML + parameters: + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PerPage' + responses: + '200': + description: XML message page. + content: + text/xml: + schema: + type: string + example: '111011falsefalse42aliceHello' + '401': + $ref: '#/components/responses/Unauthorized' + /api/v1/channels: + get: + operationId: listChannels + tags: [Channels] + summary: List channels visible to the current user + responses: + '200': + description: Visible public channels and joined direct messages. + content: + application/json: + schema: + type: object + required: [channels] + properties: + channels: + type: array + items: + $ref: '#/components/schemas/Channel' + example: + channels: + - id: 1 + name: General + slug: general + channel_type: public + topic: General conversation + created_by: null + archived_at: null + created_at: '2026-09-18 10:00:00' + updated_at: '2026-09-18 10:00:00' + last_read_message_id: 41 + unread_count: 1 + is_member: true + members: [alice, bob] + '401': + $ref: '#/components/responses/Unauthorized' + post: + operationId: createChannel + tags: [Channels] + summary: Create a public channel + parameters: + - $ref: '#/components/parameters/CsrfToken' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ChannelCreateInput' + example: + name: Project Room + slug: project-room + topic: Planning the next release + responses: + '201': + $ref: '#/components/responses/ChannelResult' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '409': + $ref: '#/components/responses/Conflict' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/channels/{channelId}: + patch: + operationId: updateChannel + tags: [Channels] + summary: Update a public channel as its creator + parameters: + - $ref: '#/components/parameters/ChannelId' + - $ref: '#/components/parameters/CsrfToken' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ChannelUpdateInput' + example: + name: Release Room + topic: Release coordination + archived: false + responses: + '200': + $ref: '#/components/responses/ChannelResult' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '409': + $ref: '#/components/responses/Conflict' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/channels/{channelId}/join: + post: + operationId: joinChannel + tags: [Channels] + summary: Join an active public channel + parameters: + - $ref: '#/components/parameters/ChannelId' + - $ref: '#/components/parameters/CsrfToken' + responses: + '200': + $ref: '#/components/responses/ChannelResult' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/channels/{channelId}/leave: + post: + operationId: leaveChannel + tags: [Channels] + summary: Leave a public channel + parameters: + - $ref: '#/components/parameters/ChannelId' + - $ref: '#/components/parameters/CsrfToken' + responses: + '200': + description: Membership removed. + content: + application/json: + schema: + $ref: '#/components/schemas/Success' + example: + success: true + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + /api/v1/channels/{channelId}/messages: + get: + operationId: listChannelMessages + tags: [Messages] + summary: List live or cursor-based channel history + description: Use `before`/`limit` for live plus archived cursor history, or `page`/`per_page` for live-message pagination. + parameters: + - $ref: '#/components/parameters/ChannelId' + - $ref: '#/components/parameters/BeforeMessageId' + - $ref: '#/components/parameters/Limit' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PerPage' + responses: + '200': + $ref: '#/components/responses/ChannelMessagePage' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + post: + operationId: createChannelMessage + tags: [Messages] + summary: Create a message in a joined channel + parameters: + - $ref: '#/components/parameters/ChannelId' + - $ref: '#/components/parameters/CsrfToken' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [message] + properties: + message: + type: string + minLength: 1 + maxLength: 500 + example: + message: Channel-only update + responses: + '201': + description: Created message. + content: + application/json: + schema: + type: object + required: [message] + properties: + message: + $ref: '#/components/schemas/CreatedMessage' + example: + message: + id: 43 + channel_id: 7 + user: alice + msg: Channel-only update + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/channels/{channelId}/messages/search: + get: + operationId: searchChannelMessages + tags: [Messages] + summary: Search live messages in a joined channel + parameters: + - $ref: '#/components/parameters/ChannelId' + - $ref: '#/components/parameters/SearchText' + - $ref: '#/components/parameters/SearchUser' + - $ref: '#/components/parameters/FromTimestamp' + - $ref: '#/components/parameters/ToTimestamp' + - $ref: '#/components/parameters/Page' + - $ref: '#/components/parameters/PerPage' + responses: + '200': + description: Channel-scoped search result. + content: + application/json: + schema: + $ref: '#/components/schemas/MessageSearchResult' + example: + messages: + - id: 43 + channel_id: 7 + user: alice + msg: Channel-only update + time: 1700000001 + pagination: + page: 1 + perPage: 10 + totalItems: 1 + totalPages: 1 + hasNext: false + hasPrev: false + filters: + text: update + user: alice + from: null + to: null + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + /api/v1/channels/{channelId}/export: + get: + operationId: exportChannelMessages + tags: [Messages] + summary: Download all live and archived messages in a joined channel + parameters: + - $ref: '#/components/parameters/ChannelId' + - $ref: '#/components/parameters/ExportFormat' + responses: + '200': + $ref: '#/components/responses/MessageExport' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/users/me/export: + get: + operationId: exportMyMessages + tags: [Messages] + summary: Download all live and archived messages authored by the current user + parameters: + - $ref: '#/components/parameters/ExportFormat' + responses: + '200': + $ref: '#/components/responses/MessageExport' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/dms: + post: + operationId: createDirectMessage + tags: [Channels] + summary: Find or create a direct-message channel + parameters: + - $ref: '#/components/parameters/CsrfToken' + requestBody: + required: true + content: + application/json: + schema: + oneOf: + - type: object + required: [user_id] + properties: + user_id: + type: integer + minimum: 1 + - type: object + required: [username] + properties: + username: + type: string + minLength: 3 + maxLength: 30 + example: + username: bob + responses: + '201': + $ref: '#/components/responses/ChannelResult' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/messages/{messageId}/reactions: + get: + operationId: listMessageReactions + tags: [Reactions] + summary: List reactions for an accessible live message + parameters: + - $ref: '#/components/parameters/MessageId' + responses: + '200': + description: Aggregated reaction groups. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Reaction' + example: + - emoji: 👍 + count: 2 + users: [alice, bob] + reacted_by_current_user: true + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + post: + operationId: createMessageReaction + tags: [Reactions] + summary: Add the current user's reaction + parameters: + - $ref: '#/components/parameters/MessageId' + - $ref: '#/components/parameters/CsrfToken' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [emoji] + properties: + emoji: + type: string + minLength: 1 + maxLength: 10 + example: + emoji: 👍 + responses: + '201': + description: Current aggregate for the saved emoji. + content: + application/json: + schema: + type: object + required: [message_id, reaction] + properties: + message_id: + type: integer + reaction: + $ref: '#/components/schemas/Reaction' + example: + message_id: 42 + reaction: + emoji: 👍 + count: 2 + users: [alice, bob] + reacted_by_current_user: true + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/messages/{messageId}/reactions/{emoji}: + delete: + operationId: deleteMessageReaction + tags: [Reactions] + summary: Remove the current user's reaction + parameters: + - $ref: '#/components/parameters/MessageId' + - name: emoji + in: path + required: true + description: URL-encoded emoji. + schema: + type: string + minLength: 1 + maxLength: 10 + example: 👍 + - $ref: '#/components/parameters/CsrfToken' + responses: + '204': + description: Reaction removed; no response body. + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/RateLimited' + /api/v1/push-subscriptions: + post: + operationId: savePushSubscription + tags: [Push subscriptions] + summary: Save or replace a browser push subscription + parameters: + - $ref: '#/components/parameters/CsrfToken' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PushSubscriptionInput' + example: + endpoint: https://push.example.test/subscriptions/abc123 + keys: + p256dh: BPV1_public_key_material + auth: auth_secret + responses: + '201': + description: Subscription saved. + content: + application/json: + schema: + $ref: '#/components/schemas/Success' + example: + success: true + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + delete: + operationId: deletePushSubscription + tags: [Push subscriptions] + summary: Delete a browser push subscription + parameters: + - $ref: '#/components/parameters/CsrfToken' + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [endpoint] + properties: + endpoint: + type: string + format: uri + maxLength: 2048 + example: + endpoint: https://push.example.test/subscriptions/abc123 + responses: + '204': + description: Subscription removed; no response body. + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/RateLimited' + /api/v1/profile: + get: + operationId: getCurrentProfile + tags: [Profiles] + summary: Get the current user's private profile + responses: + '200': + description: Current profile and a fresh CSRF token. + content: + application/json: + schema: + type: object + required: [profile, csrf_token] + properties: + profile: + $ref: '#/components/schemas/PrivateProfile' + csrf_token: + type: string + example: + profile: + id: 7 + username: alice + display_name: Alice Example + avatar_url: https://chat.example.com/profile/avatar/7 + presence: online + last_seen_at: '2026-09-18 10:00:00' + email: alice@example.com + theme: dark + notification_prefs: + desktop: true + sound: false + csrf_token: 8e90d72f31e54e9aa26760cf6f26a06f + '401': + $ref: '#/components/responses/Unauthorized' + '404': + $ref: '#/components/responses/NotFound' + post: + operationId: updateCurrentProfile + tags: [Profiles] + summary: Update the current user's profile preferences + parameters: + - $ref: '#/components/parameters/CsrfToken' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ProfileUpdateInput' + example: + display_name: Alice Example + theme: dark + presence: online + notification_prefs: + desktop: true + sound: false + responses: + '200': + description: Updated profile and fresh CSRF token. + content: + application/json: + schema: + type: object + required: [profile, csrf_token] + properties: + profile: + $ref: '#/components/schemas/PrivateProfile' + csrf_token: + type: string + example: + profile: + id: 7 + username: alice + display_name: Alice Example + avatar_url: null + presence: online + last_seen_at: '2026-09-18 10:00:00' + email: alice@example.com + theme: dark + notification_prefs: + desktop: true + sound: false + csrf_token: 8e90d72f31e54e9aa26760cf6f26a06f + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/profile/avatar: + post: + operationId: uploadProfileAvatar + tags: [Profiles] + summary: Upload and normalize the current user's avatar + parameters: + - $ref: '#/components/parameters/CsrfToken' + requestBody: + required: true + content: + multipart/form-data: + schema: + type: object + required: [avatar] + properties: + avatar: + type: string + format: binary + encoding: + avatar: + contentType: image/jpeg, image/png, image/webp + responses: + '201': + description: Avatar saved and resized to WebP. + content: + application/json: + schema: + type: object + required: [avatar_url, csrf_token] + properties: + avatar_url: + type: string + format: uri + csrf_token: + type: string + example: + avatar_url: https://chat.example.com/profile/avatar/7 + csrf_token: 8e90d72f31e54e9aa26760cf6f26a06f + '401': + $ref: '#/components/responses/Unauthorized' + '422': + $ref: '#/components/responses/UnprocessableEntity' + '429': + $ref: '#/components/responses/RateLimited' + '500': + $ref: '#/components/responses/ServerError' + /api/v1/profiles: + get: + operationId: listPublicProfiles + tags: [Profiles] + summary: Resolve up to 50 public profiles by username + parameters: + - name: usernames + in: query + required: true + description: Comma-separated usernames; at most the first 50 are used. + schema: + type: string + example: alice,bob + responses: + '200': + description: Matching public profiles and a fresh CSRF token. + content: + application/json: + schema: + type: object + required: [profiles, csrf_token] + properties: + profiles: + type: array + items: + $ref: '#/components/schemas/PublicProfile' + csrf_token: + type: string + example: + profiles: + - id: 7 + username: alice + display_name: Alice Example + avatar_url: https://chat.example.com/profile/avatar/7 + presence: online + last_seen_at: '2026-09-18 10:00:00' + - id: 8 + username: bob + display_name: bob + avatar_url: null + presence: away + last_seen_at: '2026-09-18 09:55:00' + csrf_token: 8e90d72f31e54e9aa26760cf6f26a06f + '401': + $ref: '#/components/responses/Unauthorized' +components: + securitySchemes: + sessionAuth: + type: apiKey + in: cookie + name: ci_session + description: CodeIgniter browser session cookie. + apiKeyAuth: + type: apiKey + in: header + name: X-API-Key + description: Reserved for a future non-browser authentication flow; not currently accepted. + parameters: + ChannelId: + name: channelId + in: path + required: true + schema: + type: integer + minimum: 1 + example: 7 + MessageId: + name: messageId + in: path + required: true + schema: + type: integer + minimum: 1 + example: 42 + CsrfToken: + name: X-CSRF-TOKEN + in: header + required: true + schema: + type: string + example: 8e90d72f31e54e9aa26760cf6f26a06f + Page: + name: page + in: query + schema: + type: integer + minimum: 1 + default: 1 + example: 1 + PerPage: + name: per_page + in: query + schema: + type: integer + minimum: 1 + maximum: 100 + default: 10 + example: 10 + BeforeMessageId: + name: before + in: query + description: Return messages with IDs lower than this cursor. + schema: + type: integer + minimum: 1 + example: 100 + Limit: + name: limit + in: query + schema: + type: integer + minimum: 1 + maximum: 100 + default: 25 + example: 25 + SearchText: + name: text + in: query + schema: + type: string + minLength: 1 + maxLength: 500 + example: release notes + SearchUser: + name: user + in: query + schema: + type: string + minLength: 1 + maxLength: 30 + example: alice + FromTimestamp: + name: from + in: query + schema: + type: integer + minimum: 0 + example: 1699990000 + ToTimestamp: + name: to + in: query + schema: + type: integer + minimum: 0 + example: 1700010000 + ExportFormat: + name: format + in: query + schema: + type: string + enum: [json, csv] + default: json + example: json + schemas: + Success: + type: object + required: [success] + properties: + success: + type: boolean + const: true + Message: + type: object + required: [id, channel_id, user, msg, time] + properties: + id: + type: integer + channel_id: + type: integer + user: + type: string + description: Current username, or `[deleted]` after account deletion. + msg: + type: string + minLength: 1 + maxLength: 500 + time: + type: integer + minimum: 1 + archived: + type: integer + enum: [0, 1] + description: Present on cursor history and exports. + CreatedMessage: + type: object + required: [id, channel_id, user, msg] + properties: + id: + type: integer + channel_id: + type: integer + user: + type: string + msg: + type: string + minLength: 1 + maxLength: 500 + Pagination: + type: object + required: [page, perPage, totalItems, totalPages, hasNext, hasPrev] + properties: + page: + type: integer + minimum: 1 + perPage: + type: integer + minimum: 1 + totalItems: + type: integer + minimum: 0 + totalPages: + type: integer + minimum: 0 + hasNext: + type: boolean + hasPrev: + type: boolean + MessagePage: + type: object + required: [messages, pagination] + properties: + messages: + type: array + items: + $ref: '#/components/schemas/Message' + pagination: + $ref: '#/components/schemas/Pagination' + GeneralMessagePage: + allOf: + - $ref: '#/components/schemas/MessagePage' + - type: object + required: [status, time] + properties: + status: + type: integer + enum: [1, 2] + description: "1 when messages are present and 2 when the page is empty." + time: + type: integer + minimum: 1 + CursorPagination: + type: object + required: [limit, nextBefore, hasMore] + properties: + limit: + type: integer + minimum: 1 + maximum: 100 + nextBefore: + type: [integer, 'null'] + minimum: 1 + hasMore: + type: boolean + ChannelMessagePage: + oneOf: + - $ref: '#/components/schemas/MessagePage' + - type: object + required: [messages, pagination] + properties: + messages: + type: array + items: + $ref: '#/components/schemas/Message' + pagination: + $ref: '#/components/schemas/CursorPagination' + MessageSearchResult: + allOf: + - $ref: '#/components/schemas/MessagePage' + - type: object + required: [filters] + properties: + filters: + type: object + required: [text, user, from, to] + properties: + text: + type: [string, 'null'] + user: + type: [string, 'null'] + from: + type: [integer, 'null'] + minimum: 0 + to: + type: [integer, 'null'] + minimum: 0 + Channel: + type: object + required: [id, name, slug, channel_type] + properties: + id: + type: integer + name: + type: string + maxLength: 80 + slug: + type: string + pattern: '^[a-z0-9-]{3,50}$' + channel_type: + type: string + enum: [public, dm] + topic: + type: [string, 'null'] + maxLength: 200 + created_by: + type: [integer, 'null'] + archived_at: + type: [string, 'null'] + created_at: + type: [string, 'null'] + updated_at: + type: [string, 'null'] + last_read_message_id: + type: [integer, 'null'] + unread_count: + type: integer + minimum: 0 + is_member: + type: boolean + members: + type: array + items: + type: string + ChannelCreateInput: + type: object + required: [name, slug] + properties: + name: + type: string + minLength: 1 + maxLength: 80 + slug: + type: string + pattern: '^[a-z0-9-]{3,50}$' + topic: + type: [string, 'null'] + maxLength: 200 + ChannelUpdateInput: + type: object + minProperties: 1 + properties: + name: + type: string + minLength: 1 + maxLength: 80 + slug: + type: string + pattern: '^[a-z0-9-]{3,50}$' + topic: + type: [string, 'null'] + maxLength: 200 + archived: + type: boolean + Reaction: + type: object + required: [emoji, count, users, reacted_by_current_user] + properties: + emoji: + type: string + minLength: 1 + maxLength: 10 + count: + type: integer + minimum: 0 + users: + type: array + items: + type: string + reacted_by_current_user: + type: boolean + PushSubscriptionInput: + type: object + required: [endpoint, keys] + properties: + endpoint: + type: string + format: uri + maxLength: 2048 + keys: + type: object + required: [p256dh, auth] + properties: + p256dh: + type: string + minLength: 1 + maxLength: 255 + auth: + type: string + minLength: 1 + maxLength: 255 + PublicProfile: + type: object + required: [id, username, display_name, avatar_url, presence, last_seen_at] + properties: + id: + type: integer + username: + type: string + pattern: '^[A-Za-z0-9_]{3,30}$' + display_name: + type: string + maxLength: 100 + avatar_url: + type: [string, 'null'] + format: uri + presence: + type: string + enum: [online, away, busy, offline] + last_seen_at: + type: [string, 'null'] + PrivateProfile: + allOf: + - $ref: '#/components/schemas/PublicProfile' + - type: object + required: [email, theme, notification_prefs] + properties: + email: + type: string + format: email + theme: + type: string + enum: [system, light, dark] + notification_prefs: + $ref: '#/components/schemas/NotificationPreferences' + NotificationPreferences: + type: object + required: [desktop, sound] + properties: + desktop: + type: boolean + sound: + type: boolean + ProfileUpdateInput: + type: object + properties: + display_name: + type: string + maxLength: 100 + theme: + type: string + enum: [system, light, dark] + presence: + type: string + enum: [online, away, busy] + notification_prefs: + $ref: '#/components/schemas/NotificationPreferences' + ErrorEnvelope: + type: object + required: [error] + properties: + error: + type: object + required: [type, message] + properties: + type: + type: string + enum: [validation, database, storage, export, conflict, authentication, authorization, not_found, rate_limit, server] + message: + type: string + details: + type: object + additionalProperties: true + correlation_id: + type: string + description: Present on errors emitted by shared filters and the central error handler. + responses: + GeneralMessagePage: + description: Paginated general-channel messages with legacy status metadata. + content: + application/json: + schema: + $ref: '#/components/schemas/GeneralMessagePage' + example: + messages: + - id: 42 + channel_id: 1 + user: alice + msg: Hello from class + time: 1700000000 + status: 1 + time: 1700000001 + pagination: + page: 1 + perPage: 10 + totalItems: 1 + totalPages: 1 + hasNext: false + hasPrev: false + ChannelMessagePage: + description: Live-message page or live-plus-archive cursor history. + content: + application/json: + schema: + $ref: '#/components/schemas/ChannelMessagePage' + examples: + page: + summary: Page-based live messages + value: + messages: + - id: 42 + channel_id: 7 + user: alice + msg: Hello from class + time: 1700000000 + pagination: + page: 1 + perPage: 10 + totalItems: 1 + totalPages: 1 + hasNext: false + hasPrev: false + cursor: + summary: Cursor history including archived messages + value: + messages: + - id: 42 + channel_id: 7 + user: alice + msg: Hello from class + time: 1700000000 + archived: 0 + pagination: + limit: 25 + nextBefore: null + hasMore: false + ChannelResult: + description: Channel state. + content: + application/json: + schema: + type: object + required: [channel] + properties: + channel: + $ref: '#/components/schemas/Channel' + example: + channel: + id: 7 + name: Project Room + slug: project-room + channel_type: public + topic: Planning the next release + created_by: 7 + archived_at: null + created_at: '2026-09-18 10:00:00' + updated_at: '2026-09-18 10:00:00' + MessageExport: + description: Downloaded message history in the selected format. + headers: + Content-Disposition: + schema: + type: string + description: Attachment filename. + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Message' + example: + - id: 42 + channel_id: 7 + user: alice + msg: Exported message + time: 1700000000 + archived: 0 + text/csv: + schema: + type: string + example: |- + id,channel_id,user,msg,time,archived + 42,7,alice,"Exported message",1700000000,0 + BadRequest: + description: Invalid query, form, or JSON input. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + example: + error: + type: validation + message: Provide at least one search filter. + Unauthorized: + description: A valid logged-in session is required. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + example: + error: + type: authentication + message: Authentication required + details: {} + correlation_id: 8fbc0e2d79d745569abeb2f5335c1712 + Forbidden: + description: The current user cannot perform this action. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + example: + error: + type: authorization + message: You are not a channel member. + NotFound: + description: The requested resource is missing or inaccessible. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + example: + error: + type: not_found + message: Channel not found. + Conflict: + description: A unique resource value is already in use. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + example: + error: + type: conflict + message: The channel slug is already in use. + UnprocessableEntity: + description: The submitted entity has invalid fields. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + example: + error: + type: validation + message: Choose a valid theme. + RateLimited: + description: The endpoint's rate limit was exceeded. + headers: + Retry-After: + schema: + type: integer + description: Seconds until another request is allowed. + X-Correlation-ID: + schema: + type: string + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + example: + error: + type: rate_limit + message: Too many requests. Please try again later. + details: + retry_after: 60 + correlation_id: 8fbc0e2d79d745569abeb2f5335c1712 + ServerError: + description: Storage or server failure. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorEnvelope' + example: + error: + type: server + message: An unexpected error occurred. diff --git a/docs/public-api.md b/docs/public-api.md index f631e32..3426865 100644 --- a/docs/public-api.md +++ b/docs/public-api.md @@ -1,5 +1,9 @@ # Public API Reference (Backend + WebSocket + Frontend) +The machine-readable OpenAPI 3.1 contract for every `/api/v1/*` route is in +[`openapi.yaml`](openapi.yaml) and is rendered as interactive Swagger UI at +`/api/docs`. + This document describes the **public surface area** of this repository: - **HTTP endpoints** (CodeIgniter routes/controllers) diff --git a/e2e/api-docs.spec.ts b/e2e/api-docs.spec.ts new file mode 100644 index 0000000..b206dc7 --- /dev/null +++ b/e2e/api-docs.spec.ts @@ -0,0 +1,17 @@ +import { expect, test } from '@playwright/test'; + +test('renders the embedded OpenAPI contract with Swagger UI', async ({ page }) => { + const consoleErrors: string[] = []; + page.on('console', (message) => { + if (message.type() === 'error') { + consoleErrors.push(message.text()); + } + }); + + await page.goto('/api/docs'); + + await expect(page).toHaveTitle('CodeIgniter Chat API documentation'); + await expect(page.locator('.swagger-ui .info .title')).toContainText('CodeIgniter Chat API'); + await expect(page.locator('.swagger-ui .opblock')).toHaveCount(24); + expect(consoleErrors).toEqual([]); +}); diff --git a/package.json b/package.json index 333b566..931ff3b 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "scripts": { "dev": "vite", "build": "vite build", + "api:lint": "spectral lint docs/openapi.yaml --fail-severity=warn && node scripts/check-openapi-routes.mjs", "pwa:assets": "pwa-assets-generator --preset minimal-2023 public/pwa-icon.svg", "preview": "vite preview", "bench:post": "k6 run bench/post-messages.js", @@ -29,12 +30,14 @@ "@axe-core/playwright": "^4.13.0", "@biomejs/biome": "^2.5.13", "@playwright/test": "^1.63.0", + "@stoplight/spectral-cli": "^6.16.3", "@sveltejs/vite-plugin-svelte": "^7.3.0", "@vite-pwa/assets-generator": "^1.0.4", "@vitejs/plugin-vue": "^6.0.9", "sass": "^1.104.1", "svelte": "^5.57.0", "svelte-check": "^4.7.6", + "swagger-ui-dist": "^5.33.0", "typescript": "^6.0.3", "vite": "^8.3.0", "vite-plugin-pwa": "^1.3.0", @@ -48,6 +51,7 @@ "workbox-precaching": "^7.4.1", "workbox-routing": "^7.4.1", "workbox-strategies": "^7.4.1", - "workbox-window": "^7.4.1" + "workbox-window": "^7.4.1", + "yaml": "^2.9.1" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e43513..b6b35da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,15 +20,18 @@ importers: '@playwright/test': specifier: ^1.63.0 version: 1.63.0 + '@stoplight/spectral-cli': + specifier: ^6.16.3 + version: 6.16.3 '@sveltejs/vite-plugin-svelte': specifier: ^7.3.0 - version: 7.3.0(svelte@5.57.0)(vite@8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)) + version: 7.3.0(svelte@5.57.0)(vite@8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1)) '@vite-pwa/assets-generator': specifier: ^1.0.4 - version: 1.0.4 + version: 1.0.4(@types/node@26.6.1) '@vitejs/plugin-vue': specifier: ^6.0.9 - version: 6.0.9(vite@8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2))(vue@3.5.42(typescript@6.0.3)) + version: 6.0.9(vite@8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1))(vue@3.5.42(typescript@6.0.3)) sass: specifier: ^1.104.1 version: 1.104.1 @@ -38,15 +41,18 @@ importers: svelte-check: specifier: ^4.7.6 version: 4.7.6(picomatch@4.0.7)(svelte@5.57.0)(typescript@6.0.3) + swagger-ui-dist: + specifier: ^5.33.0 + version: 5.33.0 typescript: specifier: ^6.0.3 version: 6.0.3 vite: specifier: ^8.3.0 - version: 8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2) + version: 8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1) vite-plugin-pwa: specifier: ^1.3.0 - version: 1.3.0(@vite-pwa/assets-generator@1.0.4)(vite@8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2))(workbox-build@7.4.1)(workbox-window@7.4.1) + version: 1.3.0(@vite-pwa/assets-generator@1.0.4(@types/node@26.6.1))(vite@8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1))(workbox-build@7.4.1)(workbox-window@7.4.1) vue: specifier: ^3.5.42 version: 3.5.42(typescript@6.0.3) @@ -80,6 +86,9 @@ importers: workbox-window: specifier: ^7.4.1 version: 7.4.1 + yaml: + specifier: ^2.9.1 + version: 2.9.1 packages: @@ -89,6 +98,9 @@ packages: peerDependencies: ajv: '>=8' + '@asyncapi/specs@6.11.1': + resolution: {integrity: sha512-A3WBLqAKGoJ2+6FWFtpjBlCQ1oFCcs4GxF7zsIGvNqp/klGUHjlA3aAcZ9XMMpLGE8zPeYDz2x9FmO6DSuKraQ==} + '@axe-core/playwright@4.13.0': resolution: {integrity: sha512-6YLx+kxXu5GJceG4ozFg+33a2EMTdjYwWGloJ3sb9Kta5pp+ZNS53uxGVog5JetIY8s++P5UrtX+cri+u0VAVg==} peerDependencies: @@ -846,6 +858,24 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jsep-plugin/assignment@1.3.0': + resolution: {integrity: sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + + '@jsep-plugin/regex@1.0.4': + resolution: {integrity: sha512-q7qL4Mgjs1vByCaTnDFcBnV9HS7GVPJX5vyVoCgZHNSC9rjwIlmbXG5sUuorR5ndfHAIlJ8pVStxvjXHbNvtUg==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + + '@jsep-plugin/ternary@1.1.4': + resolution: {integrity: sha512-ck5wiqIbqdMX6WRQztBL7ASDty9YLgJ3sSAK5ZpBzXeySvFGCzIvM6UiAI4hTZ22fEcYQVV/zhUbNscggW+Ukg==} + engines: {node: '>= 10.16.0'} + peerDependencies: + jsep: ^0.4.0||^1.0.0 + '@napi-rs/lzma-linux-x64-gnu@1.5.1': resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} engines: {node: ^22.20 || ^24.12 || >=25} @@ -853,6 +883,18 @@ packages: os: [linux] libc: [glibc] + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + '@oxc-project/types@0.149.0': resolution: {integrity: sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==} @@ -1058,6 +1100,12 @@ packages: rollup: optional: true + '@rollup/plugin-commonjs@22.0.2': + resolution: {integrity: sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg==} + engines: {node: '>= 12.0.0'} + peerDependencies: + rollup: ^2.68.0 + '@rollup/plugin-node-resolve@16.0.3': resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} engines: {node: '>=14.0.0'} @@ -1085,6 +1133,12 @@ packages: rollup: optional: true + '@rollup/pluginutils@3.1.0': + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + '@rollup/pluginutils@5.4.0': resolution: {integrity: sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==} engines: {node: '>=14.0.0'} @@ -1232,6 +1286,106 @@ packages: cpu: [x64] os: [win32] + '@scarf/scarf@1.4.0': + resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + + '@stoplight/better-ajv-errors@1.0.3': + resolution: {integrity: sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==} + engines: {node: ^12.20 || >= 14.13} + peerDependencies: + ajv: '>=8' + + '@stoplight/json-ref-readers@1.2.2': + resolution: {integrity: sha512-nty0tHUq2f1IKuFYsLM4CXLZGHdMn+X/IwEUIpeSOXt0QjMUbL0Em57iJUDzz+2MkWG83smIigNZ3fauGjqgdQ==} + engines: {node: '>=8.3.0'} + + '@stoplight/json-ref-resolver@3.1.6': + resolution: {integrity: sha512-YNcWv3R3n3U6iQYBsFOiWSuRGE5su1tJSiX6pAPRVk7dP0L7lqCteXGzuVRQ0gMZqUl8v1P0+fAKxF6PLo9B5A==} + engines: {node: '>=8.3.0'} + + '@stoplight/json@3.21.7': + resolution: {integrity: sha512-xcJXgKFqv/uCEgtGlPxy3tPA+4I+ZI4vAuMJ885+ThkTHFVkC+0Fm58lA9NlsyjnkpxFh4YiQWpH+KefHdbA0A==} + engines: {node: '>=8.3.0'} + + '@stoplight/ordered-object-literal@1.0.5': + resolution: {integrity: sha512-COTiuCU5bgMUtbIFBuyyh2/yVVzlr5Om0v5utQDgBCuQUOPgU1DwoffkTfg4UBQOvByi5foF4w4T+H9CoRe5wg==} + engines: {node: '>=8'} + + '@stoplight/path@1.3.2': + resolution: {integrity: sha512-lyIc6JUlUA8Ve5ELywPC8I2Sdnh1zc1zmbYgVarhXIp9YeAB0ReeqmGEOWNtlHkbP2DAA1AL65Wfn2ncjK/jtQ==} + engines: {node: '>=8'} + + '@stoplight/spectral-cli@6.16.3': + resolution: {integrity: sha512-corAOQ/WhGoPJOQ3Tcipyn64TgKYDkEhsDplS6vNSGys3kzi9+zzxiVOlbzk9mWuafovzoW+TpGCoNwIZvFf5w==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + hasBin: true + + '@stoplight/spectral-core@1.23.1': + resolution: {integrity: sha512-VLC8OhpO/pMJKb6IHhurxJjXO1qB56Ng1unIb8b+hNxdw0+SEcASvmR+RpjfHYX/jv/DfSaA1x8QhFBJBmqBOQ==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-formats@1.8.5': + resolution: {integrity: sha512-xaC0rCH0p7/bzNJsz+JgLSj+Cp6uwYGWpePQxdLkF2G6a8Zyp3OyS7umkGYNiimEwKrOjvCNNTFJpeuiENZSBA==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-formatters@1.5.1': + resolution: {integrity: sha512-mGXaiIrPglPokSnbFqbkWN3DoozIbwrZAA6OgqSIl+djeD5+e6PMELg0g6r3ot3ZzntO+6/GXaDnxEQ/p9M/EQ==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-functions@1.10.5': + resolution: {integrity: sha512-vDCd0NJ93715bcUpZZ5vNHiyxd4cgHF6tuXsDiXOXKAByg+I1fR5/dMijEo6Ce1Lz95a+RZ22JKYhF1YuzVvuA==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-parsers@1.0.5': + resolution: {integrity: sha512-ANDTp2IHWGvsQDAY85/jQi9ZrF4mRrA5bciNHX+PUxPr4DwS6iv4h+FVWJMVwcEYdpyoIdyL+SRmHdJfQEPmwQ==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-ref-resolver@1.0.5': + resolution: {integrity: sha512-gj3TieX5a9zMW29z3mBlAtDOCgN3GEc1VgZnCVlr5irmR4Qi5LuECuFItAq4pTn5Zu+sW5bqutsCH7D4PkpyAA==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-ruleset-bundler@1.7.0': + resolution: {integrity: sha512-PpIdj5Wje0T7ktxY8EUzBWLU0+mGGQHznT8nlQxTMnRhWLNYsm6HvSZDXLtMi+86yqvTuf7loJy6JvLBDzHGAA==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-ruleset-migrator@1.12.2': + resolution: {integrity: sha512-9hTcyXnBGppM1kMA8vVvY6aWzF3vXbU7+mZvvd9wdPE8QdHj9NO//1s+A/TfiWOKdH9GOERC5NITCnVvbEYEWg==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-rulesets@1.22.7': + resolution: {integrity: sha512-cT1B6Ly21923lvr235lu7iIgmcnoCTJaN3ANOyTqr4D5GA/4p2k0+yhjhdfj/1ks/Os3kUzSFnFkzpWH7WszFA==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/spectral-runtime@1.1.6': + resolution: {integrity: sha512-Y8rEDyMN4bSMJCrDs2shdcVHYyCnH3FvXRP4dBhha4Z8iJv+JPp7KqOV/hwVB/hWFC209upiwj2oDmLfR0qCDg==} + engines: {node: ^16.20 || ^18.18 || >= 20.17} + + '@stoplight/types@13.20.0': + resolution: {integrity: sha512-2FNTv05If7ib79VPDA/r9eUet76jewXFH2y2K5vuge6SXbRHtWBhcaRmu+6QpF4/WRNoJj5XYRSwLGXDxysBGA==} + engines: {node: ^12.20 || >=14.13} + + '@stoplight/types@13.6.0': + resolution: {integrity: sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ==} + engines: {node: ^12.20 || >=14.13} + + '@stoplight/types@14.1.1': + resolution: {integrity: sha512-/kjtr+0t0tjKr+heVfviO9FrU/uGLc+QNX3fHJc19xsCNYqU7lVhaXxDmEID9BZTjG+/r9pK9xP/xU02XGg65g==} + engines: {node: ^12.20 || >=14.13} + + '@stoplight/yaml-ast-parser@0.0.48': + resolution: {integrity: sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==} + + '@stoplight/yaml-ast-parser@0.0.50': + resolution: {integrity: sha512-Pb6M8TDO9DtSVla9yXSTAxmo9GVEouq5P40DWXdOie69bXogZTkgvopCq+yEvTMA0F6PEvdJmbtTV3ccIp11VQ==} + + '@stoplight/yaml@4.2.3': + resolution: {integrity: sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==} + engines: {node: '>=10.8'} + + '@stoplight/yaml@4.3.0': + resolution: {integrity: sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==} + engines: {node: '>=10.8'} + '@sveltejs/acorn-typescript@1.0.13': resolution: {integrity: sha512-wgKggnhZVL9Bfx1OaKKTrYY9BFRk6C8UAkQNUcIv1+llzYrIqy+RZm5HPKzn0NpEBvTVhTqB4kQyllZywsRBRQ==} peerDependencies: @@ -1252,15 +1406,36 @@ packages: resolution: {integrity: sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==} engines: {node: '>=12'} + '@types/es-aggregate-error@1.0.6': + resolution: {integrity: sha512-qJ7LIFp06h1QE1aVxbVd+zJP2wdaugYXYfd6JxsyRMrYHaxb6itXPogW2tz+ylUJ1n1b+JF1PHyYCfYHm0dvUg==} + + '@types/estree@0.0.39': + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/markdown-escape@1.1.3': + resolution: {integrity: sha512-JIc1+s3y5ujKnt/+N+wq6s/QdL2qZ11fP79MijrVXsAAnzSxCbT2j/3prHRouJdZ2yFLN3vkP0HytfnoCczjOw==} + + '@types/node@26.6.1': + resolution: {integrity: sha512-VqGJBMCtdhqkBUCcBLvywI0NJ+KLuVzgNnlBUNFOQjqVxzo2lxLUNg1DSey8+u2u6ktswSAxg+s68QLzWHNOuA==} + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + '@types/sarif@2.1.7': + resolution: {integrity: sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/urijs@1.19.26': + resolution: {integrity: sha512-wkXrVzX5yoqLnndOwFsieJA7oKM8cNkOKJtf/3vVGSUFkWDKZvFHpIl9Pvqb/T9UsawBBFMTTD8xu7sK5MWuvg==} + '@vite-pwa/assets-generator@1.0.4': resolution: {integrity: sha512-LlYM5CEmFmdl7le4B0Pr9+NPipQgStFOslvfxEQ/1LW3wUquZ3s3ec2z132lzfVEYAiJAmfFhQ0EVs6ntO7RKw==} engines: {node: '>=16.14.0'} @@ -1317,12 +1492,41 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-errors@3.0.0: + resolution: {integrity: sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==} + peerDependencies: + ajv: ^8.0.1 + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} alien-signals@3.2.1: resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + aria-query@5.3.1: resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} engines: {node: '>= 0.4'} @@ -1335,6 +1539,17 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + as-table@1.0.55: + resolution: {integrity: sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==} + + ast-types@0.14.2: + resolution: {integrity: sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==} + engines: {node: '>=4'} + + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} @@ -1385,6 +1600,9 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + brace-expansion@1.1.21: + resolution: {integrity: sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==} + brace-expansion@2.1.4: resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} @@ -1392,6 +1610,10 @@ packages: resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} engines: {node: 20 || >=22} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + browserslist@4.28.9: resolution: {integrity: sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1400,6 +1622,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + builtins@1.0.3: + resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1419,6 +1644,10 @@ packages: caniuse-lite@1.0.30001810: resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} @@ -1427,10 +1656,24 @@ packages: resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} engines: {node: '>= 20.19.0'} + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + clsx@2.1.1: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -1441,6 +1684,12 @@ packages: resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} engines: {node: '>=4.0.0'} + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -1463,6 +1712,9 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-uri-to-buffer@2.0.2: + resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -1507,6 +1759,10 @@ packages: defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + dependency-graph@0.11.0: + resolution: {integrity: sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==} + engines: {node: '>= 0.6.0'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1526,6 +1782,9 @@ packages: electron-to-chromium@1.5.427: resolution: {integrity: sha512-n14zb3FdsChZ2BNobqNHAJMcP3ifFv4paox2LvCrfVAQcqGiSURgbJl+PfMpHVCNFkStnNc+RRVtPBTVW5PDgw==} + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + entities@7.0.1: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} @@ -1538,6 +1797,10 @@ packages: resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} engines: {node: '>= 0.4'} + es-aggregate-error@1.0.14: + resolution: {integrity: sha512-3YxX6rVb07B5TV11AV5wsL7nQCHXNwoHPsQC8S4AmBiqYhyNCJ5BRKXkXyDJvs8QzXN20NgRtxe3dEEQD9NLHA==} + engines: {node: '>= 0.4'} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1573,6 +1836,9 @@ packages: '@typescript-eslint/types': optional: true + estree-walker@1.0.1: + resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} @@ -1584,15 +1850,29 @@ packages: resolution: {integrity: sha512-lW6is4T1NFOYnmqGZIfvixqj7A7sSvScF+DN8EK6K58xI5MZ5UvYe0GjopxOXQtZvUn4eDdVuZ8XSoYWTMEKwA==} engines: {node: '>=20'} + expr-eval-fork@3.0.3: + resolution: {integrity: sha512-BhC+hbc5lIVjygr840n5DEkW3MQq7H9o+mc1/N7Z5uIiCFVyESLL5DIE7LNq4CYUNxy+XjA+3jRrL/h0Kt2xcg==} + engines: {node: '>=16.9.0'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-glob@3.2.12: + resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} + engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-memoize@2.5.2: + resolution: {integrity: sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==} + fast-uri@3.1.7: resolution: {integrity: sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==} + fastq@1.20.3: + resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -1605,6 +1885,10 @@ packages: filelist@1.0.6: resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -1613,10 +1897,17 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1640,6 +1931,10 @@ packages: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -1651,16 +1946,27 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + get-source@2.0.12: + resolution: {integrity: sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==} + get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + glob@11.1.0: resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} engines: {node: 20 || >=22} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} @@ -1676,6 +1982,10 @@ packages: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} @@ -1695,15 +2005,29 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} + hpagent@1.2.0: + resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==} + engines: {node: '>=14'} + ico-endec@0.1.6: resolution: {integrity: sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ==} idb@7.1.1: resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + immutable@5.1.9: resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -1752,6 +2076,10 @@ packages: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -1775,10 +2103,17 @@ packages: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + is-obj@1.0.1: resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} engines: {node: '>=0.10.0'} + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} @@ -1848,6 +2183,10 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + jsep@1.4.0: + resolution: {integrity: sha512-B7qPcEVE3NVkmSJbaYxvv4cHkVW7DQsZz13pUMrfS8z8Q/BuShN+gcTXrUlPiGqM2/t/EEaI030bpxMqY8gMlw==} + engines: {node: '>= 10.16.0'} + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -1861,9 +2200,17 @@ packages: engines: {node: '>=6'} hasBin: true + jsonc-parser@2.2.1: + resolution: {integrity: sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==} + jsonfile@6.2.1: resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} + jsonpath-plus@10.4.0: + resolution: {integrity: sha512-T92WWatJXmhBbKsgH/0hl+jxjdXrifi5IKeMY02DWggRxX0UElcbVzPlmgLTbvsPeW1PasQ6xE2Q75stkhGbsA==} + engines: {node: '>=18.0.0'} + hasBin: true + jsonpointer@5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} @@ -1952,6 +2299,12 @@ packages: lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + lodash.topath@4.5.2: + resolution: {integrity: sha512-1/W4dM+35DwvE/iEd1M9ekewOSTlpFekhw9mhAtrwjVqUr83/ilQiyAvmg4tVX7Unkcfl1KC+i9WdaT4B6aQcg==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + lru-cache@11.5.2: resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} @@ -1959,20 +2312,37 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} magic-string@1.2.0: resolution: {integrity: sha512-ptco+HFxTLgjafSLim2LojBSwfg5feBjd+SqyiwdGkzC38UPdZy3zgrHMI2CoTf5fJL38tbHMYWVzIH8BxGqJw==} + markdown-escape@2.0.0: + resolution: {integrity: sha512-Trz4v0+XWlwy68LJIyw3bLbsJiC8XAbRCKF9DbEtZjyndKOGVx6n+wNB0VfoRmY2LKboQLeniap3xrb6LGSJ8A==} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + minimatch@10.2.6: resolution: {integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==} engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@5.1.9: resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} engines: {node: '>=10'} @@ -1996,13 +2366,30 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nimma@0.2.3: + resolution: {integrity: sha512-1ZOI8J+1PKKGceo/5CT5GfQOG6H8I2BencSK06YarZ2wXwH37BSSUWldqJmMJYA5JfqDqffxDXynt6f11AyKcA==} + engines: {node: ^12.20 || >=14.13} + node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-releases@2.0.55: resolution: {integrity: sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==} engines: {node: '>=18'} + node-sarif-builder@2.0.3: + resolution: {integrity: sha512-Pzr3rol8fvhG/oJjIq2NTVB0vmdNNlz22FENhhPojYRZ4/ee08CfK4YuKmuL54V9MLhI1kpzxfOJ/63LzmZzDg==} + engines: {node: '>=14'} + object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -2019,6 +2406,9 @@ packages: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + own-keys@1.0.2: resolution: {integrity: sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==} engines: {node: '>= 0.4'} @@ -2029,6 +2419,10 @@ packages: path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -2057,6 +2451,10 @@ packages: engines: {node: '>=20'} hasBin: true + pony-cause@1.1.1: + resolution: {integrity: sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==} + engines: {node: '>=12.0.0'} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -2073,9 +2471,15 @@ packages: resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} engines: {node: ^14.13.1 || >=16.0.0} + printable-characters@1.0.42: + resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==} + quansync@1.0.0: resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} @@ -2110,25 +2514,45 @@ packages: resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==} hasBin: true + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + reserved@0.1.2: + resolution: {integrity: sha512-/qO54MWj5L8WCBP9/UNe2iefJc+L9yETbH32xO/ft/EYPOTCR5k+azvDUgdCOKwZH8hXwPd0b8XBL78Nn2U69g==} + engines: {node: '>=0.8'} + resolve@1.22.12: resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rolldown@1.2.8: resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rollup@2.80.0: + resolution: {integrity: sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==} + engines: {node: '>=10.0.0'} + hasBin: true + rollup@4.63.2: resolution: {integrity: sha512-l5eyksV4tPBj6lJyEa37YzIOCSOV7lkZzEHUdpjWZbtD7wTcFYmEYXSgm5bT4vV+dZLb9rBG1W9GROOG4NS4Ew==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + sade@1.8.1: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} @@ -2145,6 +2569,9 @@ packages: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} + safe-stable-stringify@1.1.1: + resolution: {integrity: sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw==} + sass@1.104.1: resolution: {integrity: sha512-yDA+1aIG3EHgN4V/BvuhCvu61FF6hEd4e+9DxikUm9U0CAGuvdIZ/UYy7qbOxjhbbWQraoyLlMuzdRGOSV5Bmw==} engines: {node: '>=20.19.0'} @@ -2234,10 +2661,21 @@ packages: resolution: {integrity: sha512-d8EqvL+k/SOXCreS/SUzg2ciyHqBBLcN/yuRjFsbvVhHTE2pgei7oAhmPM7kWFbkX6OSMQfUq4KbkF3au9lhYQ==} engines: {node: '>= 12'} + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + + stacktracey@2.2.0: + resolution: {integrity: sha512-ETyQEz+CzXiLjEbyJqpbp+/T79RQD/6wqFucRBIlVNZfYq2Ay7wbretD4cxpbymZlaPWx58aIhPEY1Cr8DlVvg==} + stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + string.prototype.matchall@4.1.0: resolution: {integrity: sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==} engines: {node: '>= 0.4'} @@ -2258,10 +2696,18 @@ packages: resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} engines: {node: '>=4'} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + strip-comments@2.0.1: resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} engines: {node: '>=10'} + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -2278,6 +2724,9 @@ packages: resolution: {integrity: sha512-NdbDn7fl4be1ViUG0oq/lvG6OZy3oENolV2ONjiqqsfVoeAfzaQAKUcEX3MrQod/Bebv1PgwET9rfXhgn9s4Kg==} engines: {node: '>=18'} + swagger-ui-dist@5.33.0: + resolution: {integrity: sha512-wpdK+m6BU5yj6pmUdMskZVTSWYG4DLglAx3sIhylloY37i8O37IrH+YEpqdXNfpaTGxILRBFzUqLF2jKqbfI7A==} + temp-dir@2.0.0: resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} engines: {node: '>=8'} @@ -2291,6 +2740,9 @@ packages: engines: {node: '>=10'} hasBin: true + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -2298,6 +2750,16 @@ packages: to-data-view@1.1.0: resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==} + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -2336,6 +2798,9 @@ packages: unconfig@7.5.0: resolution: {integrity: sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==} + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} + unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -2370,6 +2835,16 @@ packages: peerDependencies: browserslist: '>= 4.21.0' + urijs@1.19.11: + resolution: {integrity: sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==} + + utility-types@3.11.0: + resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} + engines: {node: '>= 4'} + + validate-npm-package-name@3.0.0: + resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==} + vite-plugin-pwa@1.3.0: resolution: {integrity: sha512-c5kMgN+ITrOtHXp8PAtk2uOIEea6XjP/unCGxOWWBzQ6qa65qj/awHg0wf+QF9E/2u9vh86LqxPwzEPNbM2r5A==} engines: {node: '>=16.0.0'} @@ -2450,6 +2925,12 @@ packages: typescript: optional: true + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -2520,9 +3001,33 @@ packages: workbox-window@7.4.1: resolution: {integrity: sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==} + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.3: + resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} + engines: {node: '>=12'} + zimmerframe@1.1.4: resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} @@ -2534,6 +3039,10 @@ snapshots: jsonpointer: 5.0.1 leven: 3.1.0 + '@asyncapi/specs@6.11.1': + dependencies: + '@types/json-schema': 7.0.15 + '@axe-core/playwright@4.13.0(playwright-core@1.63.0)': dependencies: axe-core: 4.13.0 @@ -3378,9 +3887,33 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.6.0 + '@jsep-plugin/assignment@1.3.0(jsep@1.4.0)': + dependencies: + jsep: 1.4.0 + + '@jsep-plugin/regex@1.0.4(jsep@1.4.0)': + dependencies: + jsep: 1.4.0 + + '@jsep-plugin/ternary@1.1.4(jsep@1.4.0)': + dependencies: + jsep: 1.4.0 + '@napi-rs/lzma-linux-x64-gnu@1.5.1': optional: true + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.3 + '@oxc-project/types@0.149.0': {} '@parcel/watcher-android-arm64@2.6.0': @@ -3505,6 +4038,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@rollup/plugin-commonjs@22.0.2(rollup@2.80.0)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.80.0) + commondir: 1.0.1 + estree-walker: 2.0.2 + glob: 7.2.3 + is-reference: 1.2.1 + magic-string: 0.25.9 + resolve: 1.22.12 + rollup: 2.80.0 + '@rollup/plugin-node-resolve@16.0.3(rollup@4.63.2)': dependencies: '@rollup/pluginutils': 5.4.0(rollup@4.63.2) @@ -3530,6 +4074,13 @@ snapshots: optionalDependencies: rollup: 4.63.2 + '@rollup/pluginutils@3.1.0(rollup@2.80.0)': + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 4.0.7 + rollup: 2.80.0 + '@rollup/pluginutils@5.4.0(rollup@4.63.2)': dependencies: '@types/estree': 1.0.9 @@ -3613,20 +4164,280 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.63.2': optional: true + '@scarf/scarf@1.4.0': {} + + '@stoplight/better-ajv-errors@1.0.3(ajv@8.20.0)': + dependencies: + ajv: 8.20.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + + '@stoplight/json-ref-readers@1.2.2': + dependencies: + node-fetch: 2.7.0 + tslib: 1.14.1 + transitivePeerDependencies: + - encoding + + '@stoplight/json-ref-resolver@3.1.6': + dependencies: + '@stoplight/json': 3.21.7 + '@stoplight/path': 1.3.2 + '@stoplight/types': 13.20.0 + '@types/urijs': 1.19.26 + dependency-graph: 0.11.0 + fast-memoize: 2.5.2 + immer: 9.0.21 + lodash: 4.18.1 + tslib: 2.8.1 + urijs: 1.19.11 + + '@stoplight/json@3.21.7': + dependencies: + '@stoplight/ordered-object-literal': 1.0.5 + '@stoplight/path': 1.3.2 + '@stoplight/types': 13.20.0 + jsonc-parser: 2.2.1 + lodash: 4.18.1 + safe-stable-stringify: 1.1.1 + + '@stoplight/ordered-object-literal@1.0.5': {} + + '@stoplight/path@1.3.2': {} + + '@stoplight/spectral-cli@6.16.3': + dependencies: + '@scarf/scarf': 1.4.0 + '@stoplight/json': 3.21.7 + '@stoplight/path': 1.3.2 + '@stoplight/spectral-core': 1.23.1 + '@stoplight/spectral-formatters': 1.5.1 + '@stoplight/spectral-parsers': 1.0.5 + '@stoplight/spectral-ref-resolver': 1.0.5 + '@stoplight/spectral-ruleset-bundler': 1.7.0 + '@stoplight/spectral-ruleset-migrator': 1.12.2 + '@stoplight/spectral-rulesets': 1.22.7 + '@stoplight/spectral-runtime': 1.1.6 + '@stoplight/types': 13.20.0 + chalk: 4.1.2 + fast-glob: 3.2.12 + hpagent: 1.2.0 + lodash: 4.18.1 + pony-cause: 1.1.1 + stacktracey: 2.2.0 + tslib: 2.8.1 + yargs: 17.7.3 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-core@1.23.1': + dependencies: + '@scarf/scarf': 1.4.0 + '@stoplight/better-ajv-errors': 1.0.3(ajv@8.20.0) + '@stoplight/json': 3.21.7 + '@stoplight/path': 1.3.2 + '@stoplight/spectral-parsers': 1.0.5 + '@stoplight/spectral-ref-resolver': 1.0.5 + '@stoplight/spectral-runtime': 1.1.6 + '@stoplight/types': 13.6.0 + '@types/es-aggregate-error': 1.0.6 + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-errors: 3.0.0(ajv@8.20.0) + ajv-formats: 2.1.1(ajv@8.20.0) + es-aggregate-error: 1.0.14 + expr-eval-fork: 3.0.3 + jsonpath-plus: 10.4.0 + lodash: 4.18.1 + lodash.topath: 4.5.2 + minimatch: 3.1.5 + nimma: 0.2.3 + pony-cause: 1.1.1 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-formats@1.8.5': + dependencies: + '@scarf/scarf': 1.4.0 + '@stoplight/json': 3.21.7 + '@stoplight/spectral-core': 1.23.1 + '@types/json-schema': 7.0.15 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-formatters@1.5.1': + dependencies: + '@stoplight/path': 1.3.2 + '@stoplight/spectral-core': 1.23.1 + '@stoplight/spectral-runtime': 1.1.6 + '@stoplight/types': 13.20.0 + '@types/markdown-escape': 1.1.3 + chalk: 4.1.2 + cliui: 7.0.4 + lodash: 4.18.1 + markdown-escape: 2.0.0 + node-sarif-builder: 2.0.3 + strip-ansi: 6.0.1 + text-table: 0.2.0 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-functions@1.10.5': + dependencies: + '@scarf/scarf': 1.4.0 + '@stoplight/better-ajv-errors': 1.0.3(ajv@8.20.0) + '@stoplight/json': 3.21.7 + '@stoplight/spectral-core': 1.23.1 + '@stoplight/spectral-formats': 1.8.5 + '@stoplight/spectral-runtime': 1.1.6 + ajv: 8.20.0 + ajv-draft-04: 1.0.0(ajv@8.20.0) + ajv-errors: 3.0.0(ajv@8.20.0) + ajv-formats: 2.1.1(ajv@8.20.0) + lodash: 4.18.1 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-parsers@1.0.5': + dependencies: + '@stoplight/json': 3.21.7 + '@stoplight/types': 14.1.1 + '@stoplight/yaml': 4.3.0 + tslib: 2.8.1 + + '@stoplight/spectral-ref-resolver@1.0.5': + dependencies: + '@stoplight/json-ref-readers': 1.2.2 + '@stoplight/json-ref-resolver': 3.1.6 + '@stoplight/spectral-runtime': 1.1.6 + dependency-graph: 0.11.0 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-ruleset-bundler@1.7.0': + dependencies: + '@rollup/plugin-commonjs': 22.0.2(rollup@2.80.0) + '@stoplight/path': 1.3.2 + '@stoplight/spectral-core': 1.23.1 + '@stoplight/spectral-formats': 1.8.5 + '@stoplight/spectral-functions': 1.10.5 + '@stoplight/spectral-parsers': 1.0.5 + '@stoplight/spectral-ref-resolver': 1.0.5 + '@stoplight/spectral-ruleset-migrator': 1.12.2 + '@stoplight/spectral-rulesets': 1.22.7 + '@stoplight/spectral-runtime': 1.1.6 + '@stoplight/types': 13.20.0 + '@types/node': 26.6.1 + pony-cause: 1.1.1 + rollup: 2.80.0 + tslib: 2.8.1 + validate-npm-package-name: 3.0.0 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-ruleset-migrator@1.12.2': + dependencies: + '@stoplight/json': 3.21.7 + '@stoplight/ordered-object-literal': 1.0.5 + '@stoplight/path': 1.3.2 + '@stoplight/spectral-functions': 1.10.5 + '@stoplight/spectral-runtime': 1.1.6 + '@stoplight/types': 13.20.0 + '@stoplight/yaml': 4.2.3 + '@types/node': 26.6.1 + ajv: 8.20.0 + ast-types: 0.14.2 + astring: 1.9.0 + reserved: 0.1.2 + tslib: 2.8.1 + validate-npm-package-name: 3.0.0 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-rulesets@1.22.7': + dependencies: + '@asyncapi/specs': 6.11.1 + '@scarf/scarf': 1.4.0 + '@stoplight/better-ajv-errors': 1.0.3(ajv@8.20.0) + '@stoplight/json': 3.21.7 + '@stoplight/spectral-core': 1.23.1 + '@stoplight/spectral-formats': 1.8.5 + '@stoplight/spectral-functions': 1.10.5 + '@stoplight/spectral-runtime': 1.1.6 + '@stoplight/types': 13.20.0 + '@types/json-schema': 7.0.15 + ajv: 8.20.0 + ajv-formats: 2.1.1(ajv@8.20.0) + json-schema-traverse: 1.0.0 + leven: 3.1.0 + lodash: 4.18.1 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/spectral-runtime@1.1.6': + dependencies: + '@stoplight/json': 3.21.7 + '@stoplight/path': 1.3.2 + '@stoplight/types': 13.20.0 + lodash: 4.18.1 + node-fetch: 2.7.0 + tslib: 2.8.1 + transitivePeerDependencies: + - encoding + + '@stoplight/types@13.20.0': + dependencies: + '@types/json-schema': 7.0.15 + utility-types: 3.11.0 + + '@stoplight/types@13.6.0': + dependencies: + '@types/json-schema': 7.0.15 + utility-types: 3.11.0 + + '@stoplight/types@14.1.1': + dependencies: + '@types/json-schema': 7.0.15 + utility-types: 3.11.0 + + '@stoplight/yaml-ast-parser@0.0.48': {} + + '@stoplight/yaml-ast-parser@0.0.50': {} + + '@stoplight/yaml@4.2.3': + dependencies: + '@stoplight/ordered-object-literal': 1.0.5 + '@stoplight/types': 13.20.0 + '@stoplight/yaml-ast-parser': 0.0.48 + tslib: 2.8.1 + + '@stoplight/yaml@4.3.0': + dependencies: + '@stoplight/ordered-object-literal': 1.0.5 + '@stoplight/types': 14.1.1 + '@stoplight/yaml-ast-parser': 0.0.50 + tslib: 2.8.1 + '@sveltejs/acorn-typescript@1.0.13(acorn@8.18.0)': dependencies: acorn: 8.18.0 '@sveltejs/load-config@0.2.3': {} - '@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.57.0)(vite@8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2))': + '@sveltejs/vite-plugin-svelte@7.3.0(svelte@5.57.0)(vite@8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1))': dependencies: deepmerge: 4.3.1 magic-string: 1.2.0 obug: 2.1.4 svelte: 5.57.0 - vite: 8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2) - vitefu: 1.1.3(vite@8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)) + vite: 8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1) + vitefu: 1.1.3(vite@8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1)) '@trickfilm400/rollup-plugin-off-main-thread@3.0.0-pre1': dependencies: @@ -3635,27 +4446,45 @@ snapshots: magic-string: 0.30.21 string.prototype.matchall: 4.1.0 + '@types/es-aggregate-error@1.0.6': + dependencies: + '@types/node': 26.6.1 + + '@types/estree@0.0.39': {} + '@types/estree@1.0.9': {} + '@types/json-schema@7.0.15': {} + + '@types/markdown-escape@1.1.3': {} + + '@types/node@26.6.1': + dependencies: + undici-types: 8.9.0 + '@types/resolve@1.20.2': {} + '@types/sarif@2.1.7': {} + '@types/trusted-types@2.0.7': {} - '@vite-pwa/assets-generator@1.0.4': + '@types/urijs@1.19.26': {} + + '@vite-pwa/assets-generator@1.0.4(@types/node@26.6.1)': dependencies: cac: 6.7.14 colorette: 2.0.20 consola: 3.4.2 - sharp: 0.35.4 - sharp-ico: 0.1.5 + sharp: 0.35.4(@types/node@26.6.1) + sharp-ico: 0.1.5(@types/node@26.6.1) unconfig: 7.5.0 transitivePeerDependencies: - '@types/node' - '@vitejs/plugin-vue@6.0.9(vite@8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2))(vue@3.5.42(typescript@6.0.3))': + '@vitejs/plugin-vue@6.0.9(vite@8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1))(vue@3.5.42(typescript@6.0.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2) + vite: 8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1) vue: 3.5.42(typescript@6.0.3) '@volar/language-core@2.4.28': @@ -3736,6 +4565,18 @@ snapshots: acorn@8.18.0: {} + ajv-draft-04@1.0.0(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv-errors@3.0.0(ajv@8.20.0): + dependencies: + ajv: 8.20.0 + + ajv-formats@2.1.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -3745,6 +4586,12 @@ snapshots: alien-signals@3.2.1: {} + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + aria-query@5.3.1: {} array-buffer-byte-length@1.0.2: @@ -3762,6 +4609,16 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + as-table@1.0.55: + dependencies: + printable-characters: 1.0.42 + + ast-types@0.14.2: + dependencies: + tslib: 2.8.1 + + astring@1.9.0: {} + async-function@1.0.0: {} async@3.2.6: {} @@ -3806,6 +4663,11 @@ snapshots: baseline-browser-mapping@2.11.23: {} + brace-expansion@1.1.21: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.1.4: dependencies: balanced-match: 1.0.2 @@ -3814,6 +4676,10 @@ snapshots: dependencies: balanced-match: 4.0.4 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + browserslist@4.28.9: dependencies: baseline-browser-mapping: 2.11.23 @@ -3824,6 +4690,8 @@ snapshots: buffer-from@1.1.2: {} + builtins@1.0.3: {} + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -3845,6 +4713,11 @@ snapshots: caniuse-lite@1.0.30001810: {} + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + chokidar@4.0.3: dependencies: readdirp: 4.1.2 @@ -3853,14 +4726,36 @@ snapshots: dependencies: readdirp: 5.1.1 + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + clsx@2.1.1: {} + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + colorette@2.0.20: {} commander@2.20.3: {} common-tags@1.8.2: {} + commondir@1.0.1: {} + + concat-map@0.0.1: {} + consola@3.4.2: {} convert-source-map@2.0.0: {} @@ -3879,6 +4774,8 @@ snapshots: csstype@3.2.3: {} + data-uri-to-buffer@2.0.2: {} + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -3928,6 +4825,8 @@ snapshots: defu@6.1.7: {} + dependency-graph@0.11.0: {} + detect-libc@2.1.2: {} devalue@5.9.2: {} @@ -3944,6 +4843,8 @@ snapshots: electron-to-chromium@1.5.427: {} + emoji-regex@8.0.0: {} + entities@7.0.1: {} es-abstract-get@1.0.0: @@ -4010,6 +4911,17 @@ snapshots: unbox-primitive: 1.1.0 which-typed-array: 1.1.22 + es-aggregate-error@1.0.14: + dependencies: + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + function-bind: 1.1.2 + globalthis: 1.0.4 + has-property-descriptors: 1.0.2 + set-function-name: 2.0.2 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -4042,18 +4954,36 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 + estree-walker@1.0.1: {} + estree-walker@2.0.2: {} esutils@2.0.3: {} eta@4.6.0: {} + expr-eval-fork@3.0.3: {} + fast-deep-equal@3.1.3: {} + fast-glob@3.2.12: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} + fast-memoize@2.5.2: {} + fast-uri@3.1.7: {} + fastq@1.20.3: + dependencies: + reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 @@ -4062,6 +4992,10 @@ snapshots: dependencies: minimatch: 5.1.9 + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -4071,6 +5005,12 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + fs-extra@9.1.0: dependencies: at-least-node: 1.0.0 @@ -4078,6 +5018,8 @@ snapshots: jsonfile: 6.2.1 universalify: 2.0.1 + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true @@ -4101,6 +5043,8 @@ snapshots: gensync@1.0.0-beta.2: {} + get-caller-file@2.0.5: {} + get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4121,12 +5065,21 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.2 + get-source@2.0.12: + dependencies: + data-uri-to-buffer: 2.0.2 + source-map: 0.6.1 + get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + glob@11.1.0: dependencies: foreground-child: 3.3.1 @@ -4136,6 +5089,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 2.0.2 + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.5 + once: 1.4.0 + path-is-absolute: 1.0.1 + globalthis@1.0.4: dependencies: define-properties: 1.2.1 @@ -4147,6 +5109,8 @@ snapshots: has-bigints@1.1.0: {} + has-flag@4.0.0: {} + has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 @@ -4165,12 +5129,23 @@ snapshots: dependencies: function-bind: 1.1.2 + hpagent@1.2.0: {} + ico-endec@0.1.6: {} idb@7.1.1: {} + immer@9.0.21: {} + immutable@5.1.9: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -4221,13 +5196,14 @@ snapshots: dependencies: call-bound: 1.0.4 - is-extglob@2.1.1: - optional: true + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: dependencies: call-bound: 1.0.4 + is-fullwidth-code-point@3.0.0: {} + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -4239,7 +5215,6 @@ snapshots: is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - optional: true is-map@2.0.3: {} @@ -4252,8 +5227,14 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 + is-number@7.0.0: {} + is-obj@1.0.1: {} + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.9 + is-reference@3.0.3: dependencies: '@types/estree': 1.0.9 @@ -4319,18 +5300,28 @@ snapshots: js-tokens@4.0.0: {} + jsep@1.4.0: {} + jsesc@3.1.0: {} json-schema-traverse@1.0.0: {} json5@2.2.3: {} + jsonc-parser@2.2.1: {} + jsonfile@6.2.1: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 + jsonpath-plus@10.4.0: + dependencies: + '@jsep-plugin/assignment': 1.3.0(jsep@1.4.0) + '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) + jsep: 1.4.0 + jsonpointer@5.0.1: {} leven@3.1.0: {} @@ -4388,12 +5379,20 @@ snapshots: lodash.debounce@4.0.8: {} + lodash.topath@4.5.2: {} + + lodash@4.18.1: {} + lru-cache@11.5.2: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 + magic-string@0.25.9: + dependencies: + sourcemap-codec: 1.4.8 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -4402,12 +5401,25 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + markdown-escape@2.0.0: {} + math-intrinsics@1.1.0: {} + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 4.0.7 + minimatch@10.2.6: dependencies: brace-expansion: 5.0.9 + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.21 + minimatch@5.1.9: dependencies: brace-expansion: 2.1.4 @@ -4422,11 +5434,30 @@ snapshots: nanoid@3.3.19: {} + nimma@0.2.3: + dependencies: + '@jsep-plugin/regex': 1.0.4(jsep@1.4.0) + '@jsep-plugin/ternary': 1.1.4(jsep@1.4.0) + astring: 1.9.0 + jsep: 1.4.0 + optionalDependencies: + jsonpath-plus: 10.4.0 + lodash.topath: 4.5.2 + node-addon-api@7.1.1: optional: true + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + node-releases@2.0.55: {} + node-sarif-builder@2.0.3: + dependencies: + '@types/sarif': 2.1.7 + fs-extra: 10.1.0 + object-inspect@1.13.4: {} object-keys@1.1.1: {} @@ -4442,6 +5473,10 @@ snapshots: obug@2.1.4: {} + once@1.4.0: + dependencies: + wrappy: 1.0.2 + own-keys@1.0.2: dependencies: call-bound: 1.0.4 @@ -4453,6 +5488,8 @@ snapshots: path-browserify@1.0.1: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -4472,6 +5509,8 @@ snapshots: dependencies: playwright-core: 1.63.0 + pony-cause@1.1.1: {} + possible-typed-array-names@1.1.0: {} postcss@8.5.28: @@ -4484,8 +5523,12 @@ snapshots: pretty-bytes@6.1.1: {} + printable-characters@1.0.42: {} + quansync@1.0.0: {} + queue-microtask@1.2.3: {} + readdirp@4.1.2: {} readdirp@5.1.1: {} @@ -4531,8 +5574,12 @@ snapshots: dependencies: jsesc: 3.1.0 + require-directory@2.1.1: {} + require-from-string@2.0.2: {} + reserved@0.1.2: {} + resolve@1.22.12: dependencies: es-errors: 1.3.0 @@ -4540,6 +5587,8 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + reusify@1.1.0: {} + rolldown@1.2.8: dependencies: '@oxc-project/types': 0.149.0 @@ -4561,6 +5610,10 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.8 '@rolldown/binding-win32-x64-msvc': 1.2.8 + rollup@2.80.0: + optionalDependencies: + fsevents: 2.3.3 + rollup@4.63.2: dependencies: '@types/estree': 1.0.9 @@ -4593,6 +5646,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.63.2 fsevents: 2.3.3 + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + sade@1.8.1: dependencies: mri: 1.2.0 @@ -4616,6 +5673,8 @@ snapshots: es-errors: 1.3.0 is-regex: 1.2.1 + safe-stable-stringify@1.1.1: {} + sass@1.104.1: dependencies: chokidar: 5.0.0 @@ -4652,15 +5711,15 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.2 - sharp-ico@0.1.5: + sharp-ico@0.1.5(@types/node@26.6.1): dependencies: decode-ico: 0.4.1 ico-endec: 0.1.6 - sharp: 0.35.4 + sharp: 0.35.4(@types/node@26.6.1) transitivePeerDependencies: - '@types/node' - sharp@0.35.4: + sharp@0.35.4(@types/node@26.6.1): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 @@ -4691,6 +5750,7 @@ snapshots: '@img/sharp-win32-arm64': 0.35.4 '@img/sharp-win32-ia32': 0.35.4 '@img/sharp-win32-x64': 0.35.4 + '@types/node': 26.6.1 shebang-command@2.0.0: dependencies: @@ -4741,11 +5801,24 @@ snapshots: source-map@0.8.0: {} + sourcemap-codec@1.4.8: {} + + stacktracey@2.2.0: + dependencies: + as-table: 1.0.55 + get-source: 2.0.12 + stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 internal-slot: 1.1.0 + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + string.prototype.matchall@4.1.0: dependencies: call-bind: 1.0.9 @@ -4792,8 +5865,16 @@ snapshots: is-obj: 1.0.1 is-regexp: 1.0.0 + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + strip-comments@2.0.1: {} + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + supports-preserve-symlinks-flag@1.0.0: {} svelte-check@4.7.6(picomatch@4.0.7)(svelte@5.57.0)(typescript@6.0.3): @@ -4829,6 +5910,10 @@ snapshots: transitivePeerDependencies: - '@typescript-eslint/types' + swagger-ui-dist@5.33.0: + dependencies: + '@scarf/scarf': 1.4.0 + temp-dir@2.0.0: {} tempy@0.6.0: @@ -4845,6 +5930,8 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + text-table@0.2.0: {} + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.7) @@ -4852,8 +5939,15 @@ snapshots: to-data-view@1.1.0: {} - tslib@2.8.1: - optional: true + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tr46@0.0.3: {} + + tslib@1.14.1: {} + + tslib@2.8.1: {} type-fest@0.16.0: {} @@ -4912,6 +6006,8 @@ snapshots: quansync: 1.0.0 unconfig-core: 7.5.0 + undici-types@8.9.0: {} + unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -4937,20 +6033,28 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - vite-plugin-pwa@1.3.0(@vite-pwa/assets-generator@1.0.4)(vite@8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2))(workbox-build@7.4.1)(workbox-window@7.4.1): + urijs@1.19.11: {} + + utility-types@3.11.0: {} + + validate-npm-package-name@3.0.0: + dependencies: + builtins: 1.0.3 + + vite-plugin-pwa@1.3.0(@vite-pwa/assets-generator@1.0.4(@types/node@26.6.1))(vite@8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1))(workbox-build@7.4.1)(workbox-window@7.4.1): dependencies: debug: 4.4.3 pretty-bytes: 6.1.1 tinyglobby: 0.2.17 - vite: 8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2) + vite: 8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1) workbox-build: 7.4.1 workbox-window: 7.4.1 optionalDependencies: - '@vite-pwa/assets-generator': 1.0.4 + '@vite-pwa/assets-generator': 1.0.4(@types/node@26.6.1) transitivePeerDependencies: - supports-color - vite@8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2): + vite@8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -4958,14 +6062,16 @@ snapshots: rolldown: 1.2.8 tinyglobby: 0.2.17 optionalDependencies: + '@types/node': 26.6.1 fsevents: 2.3.3 jiti: 2.7.0 sass: 1.104.1 terser: 5.51.2 + yaml: 2.9.1 - vitefu@1.1.3(vite@8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)): + vitefu@1.1.3(vite@8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1)): optionalDependencies: - vite: 8.3.0(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2) + vite: 8.3.0(@types/node@26.6.1)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1) vscode-uri@3.2.0: {} @@ -4985,6 +6091,13 @@ snapshots: optionalDependencies: typescript: 6.0.3 + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -5143,6 +6256,30 @@ snapshots: '@types/trusted-types': 2.0.7 workbox-core: 7.4.1 + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + y18n@5.0.8: {} + yallist@3.1.1: {} + yaml@2.9.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + zimmerframe@1.1.4: {} diff --git a/scripts/check-openapi-routes.mjs b/scripts/check-openapi-routes.mjs new file mode 100644 index 0000000..ea0d785 --- /dev/null +++ b/scripts/check-openapi-routes.mjs @@ -0,0 +1,58 @@ +import { readFileSync } from 'node:fs'; +import { parse } from 'yaml'; + +const routesSource = readFileSync(new URL('../app/Config/Routes.php', import.meta.url), 'utf8'); +const spec = parse(readFileSync(new URL('../docs/openapi.yaml', import.meta.url), 'utf8')); +const groupStart = routesSource.indexOf("$routes->group('api/v1'"); +const groupEnd = routesSource.indexOf('// Compatibility shims', groupStart); + +if (groupStart === -1 || groupEnd === -1) { + throw new Error('Unable to locate the api/v1 route group.'); +} + +const routeOperations = new Set(); +const routePattern = /\$routes->(get|post|patch|delete)\('([^']+)'/g; + +for (const match of routesSource.slice(groupStart, groupEnd).matchAll(routePattern)) { + const [, method, route] = match; + const parameters = route.startsWith('channels/') + ? ['channelId'] + : route.startsWith('messages/') + ? ['messageId', 'emoji'] + : []; + let parameterIndex = 0; + const normalizedRoute = route.replace(/\(:(?:num|segment)\)/g, () => { + const parameter = parameters[parameterIndex]; + parameterIndex += 1; + if (!parameter) { + throw new Error(`No OpenAPI parameter mapping exists for ${route}.`); + } + return `{${parameter}}`; + }); + + routeOperations.add(`${method.toUpperCase()} /api/v1/${normalizedRoute}`); +} + +const specOperations = new Set(); +const operationMethods = new Set(['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'trace']); + +for (const [path, pathItem] of Object.entries(spec.paths ?? {})) { + for (const method of Object.keys(pathItem ?? {})) { + if (operationMethods.has(method)) { + specOperations.add(`${method.toUpperCase()} ${path}`); + } + } +} + +const missing = [...routeOperations].filter((operation) => !specOperations.has(operation)).sort(); +const extra = [...specOperations].filter((operation) => !routeOperations.has(operation)).sort(); + +if (missing.length > 0 || extra.length > 0) { + const details = [ + missing.length > 0 ? `Missing from docs/openapi.yaml:\n- ${missing.join('\n- ')}` : '', + extra.length > 0 ? `Not present in the api/v1 route group:\n- ${extra.join('\n- ')}` : '', + ].filter(Boolean); + throw new Error(details.join('\n')); +} + +console.log(`OpenAPI route coverage is complete (${routeOperations.size} operations).`); diff --git a/src/js/api-docs.js b/src/js/api-docs.js new file mode 100644 index 0000000..87365b3 --- /dev/null +++ b/src/js/api-docs.js @@ -0,0 +1,31 @@ +import SwaggerUIBundle from 'swagger-ui-dist/swagger-ui-bundle.js'; +import 'swagger-ui-dist/swagger-ui.css'; +import { parse } from 'yaml'; + +const root = document.querySelector('#swagger-ui'); + +if (!(root instanceof HTMLElement)) { + throw new Error('Swagger UI mount point is missing.'); +} + +const encodedSpec = root.dataset.openapiBase64; + +if (!encodedSpec) { + throw new Error('The embedded OpenAPI specification is missing.'); +} + +const binarySpec = atob(encodedSpec); +const specBytes = Uint8Array.from(binarySpec, (character) => character.charCodeAt(0)); +const spec = parse(new TextDecoder().decode(specBytes)); + +SwaggerUIBundle({ + deepLinking: true, + displayRequestDuration: true, + dom_id: '#swagger-ui', + filter: true, + requestInterceptor(request) { + request.credentials = 'same-origin'; + return request; + }, + spec, +}); diff --git a/tests/Tests/Feature/ApiDocsTest.php b/tests/Tests/Feature/ApiDocsTest.php new file mode 100644 index 0000000..a183ea9 --- /dev/null +++ b/tests/Tests/Feature/ApiDocsTest.php @@ -0,0 +1,35 @@ +call('get', '/api/docs'); + + $result->assertOK(); + $this->assertStringContainsString('public, max-age=300', $result->response()->getHeaderLine('Cache-Control')); + $body = $result->getBody(); + $this->assertStringContainsString('id="swagger-ui"', $body); + $this->assertMatchesRegularExpression('#/dist/(?:(?:js/)?api-docs(?:-[A-Za-z0-9_-]+)?|src/js/api-docs)\\.js#', $body); + $this->assertStringNotContainsString('cdn.jsdelivr.net', $body); + + preg_match('/data-openapi-base64="([^"]+)"/', $body, $matches); + $this->assertArrayHasKey(1, $matches); + $spec = base64_decode(html_entity_decode($matches[1], ENT_QUOTES), true); + $this->assertIsString($spec); + $this->assertStringContainsString('openapi: 3.1.0', $spec); + $this->assertStringContainsString('title: CodeIgniter Chat API', $spec); + } +} diff --git a/vite.config.js b/vite.config.js index e747490..ec984b1 100644 --- a/vite.config.js +++ b/vite.config.js @@ -38,6 +38,9 @@ export default defineConfig({ 'maskable-icon-512x512.png', 'apple-touch-icon-180x180.png', ], + // Swagger UI is only needed on /api/docs. Keep its large, docs-only + // bundles out of the install-time cache used by regular chat clients. + globIgnores: ['dist/js/api-docs-*.js', 'dist/css/api-docs-*.css'], }, }), ], @@ -70,6 +73,7 @@ export default defineConfig({ 'chat-html': resolve(import.meta.dirname, 'src/js/chat-html.js'), 'chat-vue': resolve(import.meta.dirname, 'src/vue/main.js'), // Vue.js entry point 'chat-svelte': resolve(import.meta.dirname, 'src/svelte/main.js'), // Svelte entry point + 'api-docs': resolve(import.meta.dirname, 'src/js/api-docs.js'), styles: resolve(import.meta.dirname, 'src/css/chat.scss'), zipcodes: resolve(import.meta.dirname, 'src/css/zipcodes.scss'), },