diff --git a/docs/docs.go b/docs/docs.go index 25973eb..a66077f 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -1522,6 +1522,58 @@ const docTemplate = `{ } } }, + "/pages/bulk": { + "put": { + "description": "Update multiple pages in a single request. Each object must include \"id\". Use /pages/bulk [put].", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Pages" + ], + "summary": "Bulk update pages", + "parameters": [ + { + "description": "Array of rol_pages update requests", + "name": "request", + "in": "body", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.PagesUpdateRequest" + } + } + } + ], + "responses": { + "200": { + "description": "Updated pages", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.PagesUpdateResponse" + } + } + }, + "400": { + "description": "Bad request or validation error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponse" + } + } + } + } + }, "/pages/{id}": { "put": { "description": "Update an existing page", @@ -1631,7 +1683,7 @@ const docTemplate = `{ }, "/patient": { "get": { - "description": "Returns a paginated list of patients with optional summary statistics", + "description": "Get list of patients with pagination and filters, including their attachments and payment types.", "consumes": [ "application/json" ], @@ -1641,7 +1693,7 @@ const docTemplate = `{ "tags": [ "Patient" ], - "summary": "Get patient with pagination and optional aggregation", + "summary": "Get Patients List", "parameters": [ { "type": "integer", @@ -1657,24 +1709,24 @@ const docTemplate = `{ "name": "offset", "in": "query" }, + { + "type": "string", + "description": "Filter by active status (true/false)", + "name": "active", + "in": "query" + }, + { + "type": "string", + "description": "Search in medical_record_number, name, or phone_number", + "name": "search", + "in": "query" + }, { "type": "boolean", "default": false, "description": "Include aggregation summary", "name": "include_summary", "in": "query" - }, - { - "type": "string", - "description": "Filter by status", - "name": "status", - "in": "query" - }, - { - "type": "string", - "description": "Search in multiple fields", - "name": "search", - "in": "query" } ], "responses": { @@ -1697,9 +1749,11 @@ const docTemplate = `{ } } } - }, + } + }, + "/patient/detail": { "post": { - "description": "Creates a new patient record", + "description": "Retrieves a single patient record by its ID, including attachments and payment types.", "consumes": [ "application/json" ], @@ -1709,21 +1763,21 @@ const docTemplate = `{ "tags": [ "Patient" ], - "summary": "Create patient", + "summary": "Get Patient by ID", "parameters": [ { - "description": "Patient creation request", + "description": "Patient post request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api-service_internal_models_patient.PatientCreateRequest" + "$ref": "#/definitions/api-service_internal_models_patient.PatientPostRequest" } } ], "responses": { - "201": { - "description": "Patient created successfully", + "200": { + "description": "Patient details retrieved successfully", "schema": { "$ref": "#/definitions/api-service_internal_models_patient.PatientCreateResponse" } @@ -1734,107 +1788,6 @@ const docTemplate = `{ "$ref": "#/definitions/api-service_internal_models.ErrorResponse" } }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/api-service_internal_models.ErrorResponse" - } - } - } - } - }, - "/patient/{id}": { - "put": { - "description": "Updates an existing patient record", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Patient" - ], - "summary": "Update patient", - "parameters": [ - { - "type": "string", - "description": "Medical Record Number", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Patient update request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/api-service_internal_models_patient.PatientUpdateRequest" - } - } - ], - "responses": { - "200": { - "description": "Patient updated successfully", - "schema": { - "$ref": "#/definitions/api-service_internal_models_patient.PatientUpdateResponse" - } - }, - "400": { - "description": "Bad request or validation error", - "schema": { - "$ref": "#/definitions/api-service_internal_models.ErrorResponse" - } - }, - "404": { - "description": "Patient not found", - "schema": { - "$ref": "#/definitions/api-service_internal_models.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/api-service_internal_models.ErrorResponse" - } - } - } - }, - "delete": { - "description": "Soft deletes a patient by setting status to 'deleted'", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Patient" - ], - "summary": "Delete patient", - "parameters": [ - { - "type": "string", - "description": "Medical Record Number", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Patient deleted successfully", - "schema": { - "$ref": "#/definitions/api-service_internal_models_patient.PatientDeleteResponse" - } - }, - "400": { - "description": "Invalid ID format", - "schema": { - "$ref": "#/definitions/api-service_internal_models.ErrorResponse" - } - }, "404": { "description": "Patient not found", "schema": { @@ -2292,11 +2245,6 @@ const docTemplate = `{ }, "api-service_internal_models_component.ComponentUpdateRequest": { "type": "object", - "required": [ - "directory", - "fk_rol_pages_id", - "name" - ], "properties": { "active": { "type": "boolean" @@ -2320,7 +2268,8 @@ const docTemplate = `{ "minLength": 1 }, "sort": { - "type": "integer" + "type": "integer", + "minimum": 1 } } }, @@ -2381,6 +2330,71 @@ const docTemplate = `{ } } }, + "api-service_internal_models_pages.ComponentCreateRequest": { + "type": "object", + "required": [ + "directory", + "name" + ], + "properties": { + "active": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 255 + }, + "directory": { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + "name": { + "type": "string", + "maxLength": 100, + "minLength": 1 + }, + "sort": { + "type": "integer" + } + } + }, + "api-service_internal_models_pages.ComponentUpdateRequest": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "active": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 255 + }, + "directory": { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + "fk_rol_pages_id": { + "type": "integer", + "minimum": 1 + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "maxLength": 100, + "minLength": 1 + }, + "sort": { + "type": "integer", + "minimum": 1 + } + } + }, "api-service_internal_models_pages.PagesCreateRequest": { "type": "object", "required": [ @@ -2390,9 +2404,15 @@ const docTemplate = `{ "active": { "type": "boolean" }, + "components": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.ComponentCreateRequest" + } + }, "icon": { "type": "string", - "maxLength": 20, + "maxLength": 100, "minLength": 1 }, "id": { @@ -2411,6 +2431,12 @@ const docTemplate = `{ "type": "integer", "minimum": 1 }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.PermissionCreateRequest" + } + }, "sort": { "type": "integer" }, @@ -2468,21 +2494,29 @@ const docTemplate = `{ }, "api-service_internal_models_pages.PagesUpdateRequest": { "type": "object", - "required": [ - "name" - ], "properties": { "active": { "type": "boolean" }, + "components": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.ComponentUpdateRequest" + } + }, "icon": { "type": "string", - "maxLength": 20, + "maxLength": 100, "minLength": 1 }, - "level": { + "id": { + "description": "used when updating via JSON array (bulk)", "type": "integer" }, + "level": { + "type": "integer", + "minimum": 1 + }, "name": { "description": "Status string ` + "`" + `json:\"status\" validate:\"required,oneof=draft active inactive\"` + "`" + `", "type": "string", @@ -2493,8 +2527,15 @@ const docTemplate = `{ "type": "integer", "minimum": 1 }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.PermissionUpdateRequest" + } + }, "sort": { - "type": "integer" + "type": "integer", + "minimum": 1 }, "url": { "type": "string", @@ -2514,6 +2555,86 @@ const docTemplate = `{ } } }, + "api-service_internal_models_pages.PermissionCreateRequest": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "create": { + "type": "boolean" + }, + "delete": { + "type": "boolean" + }, + "disable": { + "type": "boolean" + }, + "group_keycloak": { + "type": "array", + "items": { + "type": "string" + } + }, + "read": { + "type": "boolean" + }, + "role_keycloak": { + "description": "FkRolPagesID *int64 ` + "`" + `json:\"fk_rol_pages_id\" validate:\"omitempty,\"` + "`" + `", + "type": "array", + "items": { + "type": "string" + } + }, + "update": { + "type": "boolean" + } + } + }, + "api-service_internal_models_pages.PermissionUpdateRequest": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "active": { + "type": "boolean" + }, + "create": { + "type": "boolean" + }, + "delete": { + "type": "boolean" + }, + "disable": { + "type": "boolean" + }, + "fk_rol_pages_id": { + "type": "integer" + }, + "group_keycloak": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "integer" + }, + "read": { + "type": "boolean" + }, + "role_keycloak": { + "type": "array", + "items": { + "type": "string" + } + }, + "update": { + "type": "boolean" + } + } + }, "api-service_internal_models_pages.Rol_component": { "type": "object", "properties": { @@ -2954,6 +3075,12 @@ const docTemplate = `{ "address": { "$ref": "#/definitions/sql.NullString" }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_patient.PatientAttachment" + } + }, "birth_date": { "$ref": "#/definitions/sql.NullTime" }, @@ -2993,78 +3120,37 @@ const docTemplate = `{ "name": { "$ref": "#/definitions/sql.NullString" }, + "payment_types": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_patient.PatientPaymentType" + } + }, "phone_number": { "$ref": "#/definitions/sql.NullString" } } }, - "api-service_internal_models_patient.PatientCreateRequest": { + "api-service_internal_models_patient.PatientAttachment": { "type": "object", "properties": { "active": { - "type": "boolean" + "$ref": "#/definitions/sql.NullBool" }, - "address": { - "type": "string", - "maxLength": 255, - "minLength": 1 + "directory": { + "$ref": "#/definitions/sql.NullString" }, - "birth_date": { - "type": "string" + "file_name": { + "$ref": "#/definitions/sql.NullString" }, - "ds_sd_kabupaten_kota": { - "type": "string", - "maxLength": 255, - "minLength": 1 - }, - "ds_sd_kecamatan": { - "type": "string", - "maxLength": 255, - "minLength": 1 - }, - "ds_sd_kelurahan": { - "type": "string", - "maxLength": 255, - "minLength": 1 - }, - "ds_sd_provinsi": { - "type": "string", - "maxLength": 255, - "minLength": 1 - }, - "fk_sd_kabupaten_kota_id": { - "type": "integer" - }, - "fk_sd_kecamatan_id": { - "type": "integer" - }, - "fk_sd_kelurahan_id": { - "type": "integer" - }, - "fk_sd_provinsi_id": { - "type": "integer" - }, - "gender": { - "type": "string", - "maxLength": 1 + "fk_ms_patient_id": { + "$ref": "#/definitions/api-service_internal_models.NullableInt32" }, "id": { "type": "integer" }, - "medical_record_number": { - "type": "string", - "maxLength": 20, - "minLength": 1 - }, "name": { - "type": "string", - "maxLength": 100, - "minLength": 1 - }, - "phone_number": { - "type": "string", - "maxLength": 20, - "minLength": 1 + "$ref": "#/definitions/sql.NullString" } } }, @@ -3079,20 +3165,6 @@ const docTemplate = `{ } } }, - "api-service_internal_models_patient.PatientDeleteResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "medical_record_number": { - "type": "string" - }, - "message": { - "type": "string" - } - } - }, "api-service_internal_models_patient.PatientGetResponse": { "type": "object", "properties": { @@ -3113,84 +3185,39 @@ const docTemplate = `{ } } }, - "api-service_internal_models_patient.PatientUpdateRequest": { + "api-service_internal_models_patient.PatientPaymentType": { "type": "object", - "required": [ - "id" - ], "properties": { "active": { - "type": "boolean" + "$ref": "#/definitions/sql.NullBool" }, - "address": { - "type": "string", - "maxLength": 255, - "minLength": 1 + "fk_ms_patient_id": { + "$ref": "#/definitions/api-service_internal_models.NullableInt32" }, - "birth_date": { - "type": "string" - }, - "ds_sd_kabupaten_kota": { - "type": "string", - "maxLength": 255 - }, - "ds_sd_kecamatan": { - "type": "string", - "maxLength": 255 - }, - "ds_sd_kelurahan": { - "type": "string", - "maxLength": 255 - }, - "ds_sd_provinsi": { - "type": "string", - "maxLength": 255 - }, - "fk_sd_kabupaten_kota_id": { - "type": "integer" - }, - "fk_sd_kecamatan_id": { - "type": "integer" - }, - "fk_sd_kelurahan_id": { - "type": "integer" - }, - "fk_sd_provinsi_id": { - "type": "integer" - }, - "gender": { - "type": "string", - "maxLength": 1 + "fk_ref_payment_type": { + "$ref": "#/definitions/api-service_internal_models.NullableInt32" }, "id": { - "type": "integer", - "minimum": 1 - }, - "medical_record_number": { - "type": "string", - "maxLength": 20, - "minLength": 1 + "type": "integer" }, "name": { - "type": "string", - "maxLength": 100, - "minLength": 1 + "$ref": "#/definitions/sql.NullString" }, - "phone_number": { - "type": "string", - "maxLength": 20, - "minLength": 1 + "number": { + "$ref": "#/definitions/sql.NullString" } } }, - "api-service_internal_models_patient.PatientUpdateResponse": { + "api-service_internal_models_patient.PatientPostRequest": { "type": "object", + "required": [ + "medical_record_number" + ], "properties": { - "data": { - "$ref": "#/definitions/api-service_internal_models_patient.Patient" - }, - "message": { - "type": "string" + "medical_record_number": { + "description": "ID int64 ` + "`" + `json:\"id\" validate:\"required,min=1\"` + "`" + `", + "type": "string", + "minLength": 1 } } }, diff --git a/docs/swagger.json b/docs/swagger.json index c62739c..002aa82 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1520,6 +1520,58 @@ } } }, + "/pages/bulk": { + "put": { + "description": "Update multiple pages in a single request. Each object must include \"id\". Use /pages/bulk [put].", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Pages" + ], + "summary": "Bulk update pages", + "parameters": [ + { + "description": "Array of rol_pages update requests", + "name": "request", + "in": "body", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.PagesUpdateRequest" + } + } + } + ], + "responses": { + "200": { + "description": "Updated pages", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.PagesUpdateResponse" + } + } + }, + "400": { + "description": "Bad request or validation error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponse" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "#/definitions/api-service_internal_models.ErrorResponse" + } + } + } + } + }, "/pages/{id}": { "put": { "description": "Update an existing page", @@ -1629,7 +1681,7 @@ }, "/patient": { "get": { - "description": "Returns a paginated list of patients with optional summary statistics", + "description": "Get list of patients with pagination and filters, including their attachments and payment types.", "consumes": [ "application/json" ], @@ -1639,7 +1691,7 @@ "tags": [ "Patient" ], - "summary": "Get patient with pagination and optional aggregation", + "summary": "Get Patients List", "parameters": [ { "type": "integer", @@ -1655,24 +1707,24 @@ "name": "offset", "in": "query" }, + { + "type": "string", + "description": "Filter by active status (true/false)", + "name": "active", + "in": "query" + }, + { + "type": "string", + "description": "Search in medical_record_number, name, or phone_number", + "name": "search", + "in": "query" + }, { "type": "boolean", "default": false, "description": "Include aggregation summary", "name": "include_summary", "in": "query" - }, - { - "type": "string", - "description": "Filter by status", - "name": "status", - "in": "query" - }, - { - "type": "string", - "description": "Search in multiple fields", - "name": "search", - "in": "query" } ], "responses": { @@ -1695,9 +1747,11 @@ } } } - }, + } + }, + "/patient/detail": { "post": { - "description": "Creates a new patient record", + "description": "Retrieves a single patient record by its ID, including attachments and payment types.", "consumes": [ "application/json" ], @@ -1707,21 +1761,21 @@ "tags": [ "Patient" ], - "summary": "Create patient", + "summary": "Get Patient by ID", "parameters": [ { - "description": "Patient creation request", + "description": "Patient post request", "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/api-service_internal_models_patient.PatientCreateRequest" + "$ref": "#/definitions/api-service_internal_models_patient.PatientPostRequest" } } ], "responses": { - "201": { - "description": "Patient created successfully", + "200": { + "description": "Patient details retrieved successfully", "schema": { "$ref": "#/definitions/api-service_internal_models_patient.PatientCreateResponse" } @@ -1732,107 +1786,6 @@ "$ref": "#/definitions/api-service_internal_models.ErrorResponse" } }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/api-service_internal_models.ErrorResponse" - } - } - } - } - }, - "/patient/{id}": { - "put": { - "description": "Updates an existing patient record", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Patient" - ], - "summary": "Update patient", - "parameters": [ - { - "type": "string", - "description": "Medical Record Number", - "name": "id", - "in": "path", - "required": true - }, - { - "description": "Patient update request", - "name": "request", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/api-service_internal_models_patient.PatientUpdateRequest" - } - } - ], - "responses": { - "200": { - "description": "Patient updated successfully", - "schema": { - "$ref": "#/definitions/api-service_internal_models_patient.PatientUpdateResponse" - } - }, - "400": { - "description": "Bad request or validation error", - "schema": { - "$ref": "#/definitions/api-service_internal_models.ErrorResponse" - } - }, - "404": { - "description": "Patient not found", - "schema": { - "$ref": "#/definitions/api-service_internal_models.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/api-service_internal_models.ErrorResponse" - } - } - } - }, - "delete": { - "description": "Soft deletes a patient by setting status to 'deleted'", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Patient" - ], - "summary": "Delete patient", - "parameters": [ - { - "type": "string", - "description": "Medical Record Number", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "Patient deleted successfully", - "schema": { - "$ref": "#/definitions/api-service_internal_models_patient.PatientDeleteResponse" - } - }, - "400": { - "description": "Invalid ID format", - "schema": { - "$ref": "#/definitions/api-service_internal_models.ErrorResponse" - } - }, "404": { "description": "Patient not found", "schema": { @@ -2290,11 +2243,6 @@ }, "api-service_internal_models_component.ComponentUpdateRequest": { "type": "object", - "required": [ - "directory", - "fk_rol_pages_id", - "name" - ], "properties": { "active": { "type": "boolean" @@ -2318,7 +2266,8 @@ "minLength": 1 }, "sort": { - "type": "integer" + "type": "integer", + "minimum": 1 } } }, @@ -2379,6 +2328,71 @@ } } }, + "api-service_internal_models_pages.ComponentCreateRequest": { + "type": "object", + "required": [ + "directory", + "name" + ], + "properties": { + "active": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 255 + }, + "directory": { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + "name": { + "type": "string", + "maxLength": 100, + "minLength": 1 + }, + "sort": { + "type": "integer" + } + } + }, + "api-service_internal_models_pages.ComponentUpdateRequest": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "active": { + "type": "boolean" + }, + "description": { + "type": "string", + "maxLength": 255 + }, + "directory": { + "type": "string", + "maxLength": 255, + "minLength": 1 + }, + "fk_rol_pages_id": { + "type": "integer", + "minimum": 1 + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string", + "maxLength": 100, + "minLength": 1 + }, + "sort": { + "type": "integer", + "minimum": 1 + } + } + }, "api-service_internal_models_pages.PagesCreateRequest": { "type": "object", "required": [ @@ -2388,9 +2402,15 @@ "active": { "type": "boolean" }, + "components": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.ComponentCreateRequest" + } + }, "icon": { "type": "string", - "maxLength": 20, + "maxLength": 100, "minLength": 1 }, "id": { @@ -2409,6 +2429,12 @@ "type": "integer", "minimum": 1 }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.PermissionCreateRequest" + } + }, "sort": { "type": "integer" }, @@ -2466,21 +2492,29 @@ }, "api-service_internal_models_pages.PagesUpdateRequest": { "type": "object", - "required": [ - "name" - ], "properties": { "active": { "type": "boolean" }, + "components": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.ComponentUpdateRequest" + } + }, "icon": { "type": "string", - "maxLength": 20, + "maxLength": 100, "minLength": 1 }, - "level": { + "id": { + "description": "used when updating via JSON array (bulk)", "type": "integer" }, + "level": { + "type": "integer", + "minimum": 1 + }, "name": { "description": "Status string `json:\"status\" validate:\"required,oneof=draft active inactive\"`", "type": "string", @@ -2491,8 +2525,15 @@ "type": "integer", "minimum": 1 }, + "permissions": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_pages.PermissionUpdateRequest" + } + }, "sort": { - "type": "integer" + "type": "integer", + "minimum": 1 }, "url": { "type": "string", @@ -2512,6 +2553,86 @@ } } }, + "api-service_internal_models_pages.PermissionCreateRequest": { + "type": "object", + "properties": { + "active": { + "type": "boolean" + }, + "create": { + "type": "boolean" + }, + "delete": { + "type": "boolean" + }, + "disable": { + "type": "boolean" + }, + "group_keycloak": { + "type": "array", + "items": { + "type": "string" + } + }, + "read": { + "type": "boolean" + }, + "role_keycloak": { + "description": "FkRolPagesID *int64 `json:\"fk_rol_pages_id\" validate:\"omitempty,\"`", + "type": "array", + "items": { + "type": "string" + } + }, + "update": { + "type": "boolean" + } + } + }, + "api-service_internal_models_pages.PermissionUpdateRequest": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "active": { + "type": "boolean" + }, + "create": { + "type": "boolean" + }, + "delete": { + "type": "boolean" + }, + "disable": { + "type": "boolean" + }, + "fk_rol_pages_id": { + "type": "integer" + }, + "group_keycloak": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "integer" + }, + "read": { + "type": "boolean" + }, + "role_keycloak": { + "type": "array", + "items": { + "type": "string" + } + }, + "update": { + "type": "boolean" + } + } + }, "api-service_internal_models_pages.Rol_component": { "type": "object", "properties": { @@ -2952,6 +3073,12 @@ "address": { "$ref": "#/definitions/sql.NullString" }, + "attachments": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_patient.PatientAttachment" + } + }, "birth_date": { "$ref": "#/definitions/sql.NullTime" }, @@ -2991,78 +3118,37 @@ "name": { "$ref": "#/definitions/sql.NullString" }, + "payment_types": { + "type": "array", + "items": { + "$ref": "#/definitions/api-service_internal_models_patient.PatientPaymentType" + } + }, "phone_number": { "$ref": "#/definitions/sql.NullString" } } }, - "api-service_internal_models_patient.PatientCreateRequest": { + "api-service_internal_models_patient.PatientAttachment": { "type": "object", "properties": { "active": { - "type": "boolean" + "$ref": "#/definitions/sql.NullBool" }, - "address": { - "type": "string", - "maxLength": 255, - "minLength": 1 + "directory": { + "$ref": "#/definitions/sql.NullString" }, - "birth_date": { - "type": "string" + "file_name": { + "$ref": "#/definitions/sql.NullString" }, - "ds_sd_kabupaten_kota": { - "type": "string", - "maxLength": 255, - "minLength": 1 - }, - "ds_sd_kecamatan": { - "type": "string", - "maxLength": 255, - "minLength": 1 - }, - "ds_sd_kelurahan": { - "type": "string", - "maxLength": 255, - "minLength": 1 - }, - "ds_sd_provinsi": { - "type": "string", - "maxLength": 255, - "minLength": 1 - }, - "fk_sd_kabupaten_kota_id": { - "type": "integer" - }, - "fk_sd_kecamatan_id": { - "type": "integer" - }, - "fk_sd_kelurahan_id": { - "type": "integer" - }, - "fk_sd_provinsi_id": { - "type": "integer" - }, - "gender": { - "type": "string", - "maxLength": 1 + "fk_ms_patient_id": { + "$ref": "#/definitions/api-service_internal_models.NullableInt32" }, "id": { "type": "integer" }, - "medical_record_number": { - "type": "string", - "maxLength": 20, - "minLength": 1 - }, "name": { - "type": "string", - "maxLength": 100, - "minLength": 1 - }, - "phone_number": { - "type": "string", - "maxLength": 20, - "minLength": 1 + "$ref": "#/definitions/sql.NullString" } } }, @@ -3077,20 +3163,6 @@ } } }, - "api-service_internal_models_patient.PatientDeleteResponse": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "medical_record_number": { - "type": "string" - }, - "message": { - "type": "string" - } - } - }, "api-service_internal_models_patient.PatientGetResponse": { "type": "object", "properties": { @@ -3111,84 +3183,39 @@ } } }, - "api-service_internal_models_patient.PatientUpdateRequest": { + "api-service_internal_models_patient.PatientPaymentType": { "type": "object", - "required": [ - "id" - ], "properties": { "active": { - "type": "boolean" + "$ref": "#/definitions/sql.NullBool" }, - "address": { - "type": "string", - "maxLength": 255, - "minLength": 1 + "fk_ms_patient_id": { + "$ref": "#/definitions/api-service_internal_models.NullableInt32" }, - "birth_date": { - "type": "string" - }, - "ds_sd_kabupaten_kota": { - "type": "string", - "maxLength": 255 - }, - "ds_sd_kecamatan": { - "type": "string", - "maxLength": 255 - }, - "ds_sd_kelurahan": { - "type": "string", - "maxLength": 255 - }, - "ds_sd_provinsi": { - "type": "string", - "maxLength": 255 - }, - "fk_sd_kabupaten_kota_id": { - "type": "integer" - }, - "fk_sd_kecamatan_id": { - "type": "integer" - }, - "fk_sd_kelurahan_id": { - "type": "integer" - }, - "fk_sd_provinsi_id": { - "type": "integer" - }, - "gender": { - "type": "string", - "maxLength": 1 + "fk_ref_payment_type": { + "$ref": "#/definitions/api-service_internal_models.NullableInt32" }, "id": { - "type": "integer", - "minimum": 1 - }, - "medical_record_number": { - "type": "string", - "maxLength": 20, - "minLength": 1 + "type": "integer" }, "name": { - "type": "string", - "maxLength": 100, - "minLength": 1 + "$ref": "#/definitions/sql.NullString" }, - "phone_number": { - "type": "string", - "maxLength": 20, - "minLength": 1 + "number": { + "$ref": "#/definitions/sql.NullString" } } }, - "api-service_internal_models_patient.PatientUpdateResponse": { + "api-service_internal_models_patient.PatientPostRequest": { "type": "object", + "required": [ + "medical_record_number" + ], "properties": { - "data": { - "$ref": "#/definitions/api-service_internal_models_patient.Patient" - }, - "message": { - "type": "string" + "medical_record_number": { + "description": "ID int64 `json:\"id\" validate:\"required,min=1\"`", + "type": "string", + "minLength": 1 } } }, diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 58b8dba..0ef4691 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -168,11 +168,8 @@ definitions: minLength: 1 type: string sort: + minimum: 1 type: integer - required: - - directory - - fk_rol_pages_id - - name type: object api-service_internal_models_component.ComponentUpdateResponse: properties: @@ -211,12 +208,63 @@ definitions: sort: type: integer type: object + api-service_internal_models_pages.ComponentCreateRequest: + properties: + active: + type: boolean + description: + maxLength: 255 + type: string + directory: + maxLength: 255 + minLength: 1 + type: string + name: + maxLength: 100 + minLength: 1 + type: string + sort: + type: integer + required: + - directory + - name + type: object + api-service_internal_models_pages.ComponentUpdateRequest: + properties: + active: + type: boolean + description: + maxLength: 255 + type: string + directory: + maxLength: 255 + minLength: 1 + type: string + fk_rol_pages_id: + minimum: 1 + type: integer + id: + type: integer + name: + maxLength: 100 + minLength: 1 + type: string + sort: + minimum: 1 + type: integer + required: + - id + type: object api-service_internal_models_pages.PagesCreateRequest: properties: active: type: boolean + components: + items: + $ref: '#/definitions/api-service_internal_models_pages.ComponentCreateRequest' + type: array icon: - maxLength: 20 + maxLength: 100 minLength: 1 type: string id: @@ -232,6 +280,10 @@ definitions: parent: minimum: 1 type: integer + permissions: + items: + $ref: '#/definitions/api-service_internal_models_pages.PermissionCreateRequest' + type: array sort: type: integer url: @@ -274,11 +326,19 @@ definitions: properties: active: type: boolean + components: + items: + $ref: '#/definitions/api-service_internal_models_pages.ComponentUpdateRequest' + type: array icon: - maxLength: 20 + maxLength: 100 minLength: 1 type: string + id: + description: used when updating via JSON array (bulk) + type: integer level: + minimum: 1 type: integer name: description: Status string `json:"status" validate:"required,oneof=draft @@ -289,14 +349,17 @@ definitions: parent: minimum: 1 type: integer + permissions: + items: + $ref: '#/definitions/api-service_internal_models_pages.PermissionUpdateRequest' + type: array sort: + minimum: 1 type: integer url: maxLength: 100 minLength: 1 type: string - required: - - name type: object api-service_internal_models_pages.PagesUpdateResponse: properties: @@ -305,6 +368,59 @@ definitions: message: type: string type: object + api-service_internal_models_pages.PermissionCreateRequest: + properties: + active: + type: boolean + create: + type: boolean + delete: + type: boolean + disable: + type: boolean + group_keycloak: + items: + type: string + type: array + read: + type: boolean + role_keycloak: + description: FkRolPagesID *int64 `json:"fk_rol_pages_id" validate:"omitempty,"` + items: + type: string + type: array + update: + type: boolean + type: object + api-service_internal_models_pages.PermissionUpdateRequest: + properties: + active: + type: boolean + create: + type: boolean + delete: + type: boolean + disable: + type: boolean + fk_rol_pages_id: + type: integer + group_keycloak: + items: + type: string + type: array + id: + type: integer + read: + type: boolean + role_keycloak: + items: + type: string + type: array + update: + type: boolean + required: + - id + type: object api-service_internal_models_pages.Rol_component: properties: active: @@ -600,6 +716,10 @@ definitions: $ref: '#/definitions/sql.NullBool' address: $ref: '#/definitions/sql.NullString' + attachments: + items: + $ref: '#/definitions/api-service_internal_models_patient.PatientAttachment' + type: array birth_date: $ref: '#/definitions/sql.NullTime' ds_sd_kabupaten_kota: @@ -626,60 +746,27 @@ definitions: $ref: '#/definitions/sql.NullString' name: $ref: '#/definitions/sql.NullString' + payment_types: + items: + $ref: '#/definitions/api-service_internal_models_patient.PatientPaymentType' + type: array phone_number: $ref: '#/definitions/sql.NullString' type: object - api-service_internal_models_patient.PatientCreateRequest: + api-service_internal_models_patient.PatientAttachment: properties: active: - type: boolean - address: - maxLength: 255 - minLength: 1 - type: string - birth_date: - type: string - ds_sd_kabupaten_kota: - maxLength: 255 - minLength: 1 - type: string - ds_sd_kecamatan: - maxLength: 255 - minLength: 1 - type: string - ds_sd_kelurahan: - maxLength: 255 - minLength: 1 - type: string - ds_sd_provinsi: - maxLength: 255 - minLength: 1 - type: string - fk_sd_kabupaten_kota_id: - type: integer - fk_sd_kecamatan_id: - type: integer - fk_sd_kelurahan_id: - type: integer - fk_sd_provinsi_id: - type: integer - gender: - maxLength: 1 - type: string + $ref: '#/definitions/sql.NullBool' + directory: + $ref: '#/definitions/sql.NullString' + file_name: + $ref: '#/definitions/sql.NullString' + fk_ms_patient_id: + $ref: '#/definitions/api-service_internal_models.NullableInt32' id: type: integer - medical_record_number: - maxLength: 20 - minLength: 1 - type: string name: - maxLength: 100 - minLength: 1 - type: string - phone_number: - maxLength: 20 - minLength: 1 - type: string + $ref: '#/definitions/sql.NullString' type: object api-service_internal_models_patient.PatientCreateResponse: properties: @@ -688,15 +775,6 @@ definitions: message: type: string type: object - api-service_internal_models_patient.PatientDeleteResponse: - properties: - id: - type: string - medical_record_number: - type: string - message: - type: string - type: object api-service_internal_models_patient.PatientGetResponse: properties: data: @@ -710,63 +788,29 @@ definitions: summary: $ref: '#/definitions/api-service_internal_models.AggregateData' type: object - api-service_internal_models_patient.PatientUpdateRequest: + api-service_internal_models_patient.PatientPaymentType: properties: active: - type: boolean - address: - maxLength: 255 - minLength: 1 - type: string - birth_date: - type: string - ds_sd_kabupaten_kota: - maxLength: 255 - type: string - ds_sd_kecamatan: - maxLength: 255 - type: string - ds_sd_kelurahan: - maxLength: 255 - type: string - ds_sd_provinsi: - maxLength: 255 - type: string - fk_sd_kabupaten_kota_id: - type: integer - fk_sd_kecamatan_id: - type: integer - fk_sd_kelurahan_id: - type: integer - fk_sd_provinsi_id: - type: integer - gender: - maxLength: 1 - type: string + $ref: '#/definitions/sql.NullBool' + fk_ms_patient_id: + $ref: '#/definitions/api-service_internal_models.NullableInt32' + fk_ref_payment_type: + $ref: '#/definitions/api-service_internal_models.NullableInt32' id: - minimum: 1 type: integer - medical_record_number: - maxLength: 20 - minLength: 1 - type: string name: - maxLength: 100 - minLength: 1 - type: string - phone_number: - maxLength: 20 + $ref: '#/definitions/sql.NullString' + number: + $ref: '#/definitions/sql.NullString' + type: object + api-service_internal_models_patient.PatientPostRequest: + properties: + medical_record_number: + description: ID int64 `json:"id" validate:"required,min=1"` minLength: 1 type: string required: - - id - type: object - api-service_internal_models_patient.PatientUpdateResponse: - properties: - data: - $ref: '#/definitions/api-service_internal_models_patient.Patient' - message: - type: string + - medical_record_number type: object api-service_internal_models_permission.PermissionCreateRequest: properties: @@ -2200,11 +2244,47 @@ paths: summary: Update Page tags: - Pages + /pages/bulk: + put: + consumes: + - application/json + description: Update multiple pages in a single request. Each object must include + "id". Use /pages/bulk [put]. + parameters: + - description: Array of rol_pages update requests + in: body + name: request + required: true + schema: + items: + $ref: '#/definitions/api-service_internal_models_pages.PagesUpdateRequest' + type: array + produces: + - application/json + responses: + "200": + description: Updated pages + schema: + items: + $ref: '#/definitions/api-service_internal_models_pages.PagesUpdateResponse' + type: array + "400": + description: Bad request or validation error + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponse' + "500": + description: Internal server error + schema: + $ref: '#/definitions/api-service_internal_models.ErrorResponse' + summary: Bulk update pages + tags: + - Pages /patient: get: consumes: - application/json - description: Returns a paginated list of patients with optional summary statistics + description: Get list of patients with pagination and filters, including their + attachments and payment types. parameters: - default: 10 description: Limit (max 100) @@ -2216,19 +2296,19 @@ paths: in: query name: offset type: integer + - description: Filter by active status (true/false) + in: query + name: active + type: string + - description: Search in medical_record_number, name, or phone_number + in: query + name: search + type: string - default: false description: Include aggregation summary in: query name: include_summary type: boolean - - description: Filter by status - in: query - name: status - type: string - - description: Search in multiple fields - in: query - name: search - type: string produces: - application/json responses: @@ -2244,60 +2324,33 @@ paths: description: Internal server error schema: $ref: '#/definitions/api-service_internal_models.ErrorResponse' - summary: Get patient with pagination and optional aggregation + summary: Get Patients List tags: - Patient + /patient/detail: post: consumes: - application/json - description: Creates a new patient record + description: Retrieves a single patient record by its ID, including attachments + and payment types. parameters: - - description: Patient creation request + - description: Patient post request in: body name: request required: true schema: - $ref: '#/definitions/api-service_internal_models_patient.PatientCreateRequest' + $ref: '#/definitions/api-service_internal_models_patient.PatientPostRequest' produces: - application/json responses: - "201": - description: Patient created successfully + "200": + description: Patient details retrieved successfully schema: $ref: '#/definitions/api-service_internal_models_patient.PatientCreateResponse' "400": description: Bad request or validation error schema: $ref: '#/definitions/api-service_internal_models.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/api-service_internal_models.ErrorResponse' - summary: Create patient - tags: - - Patient - /patient/{id}: - delete: - consumes: - - application/json - description: Soft deletes a patient by setting status to 'deleted' - parameters: - - description: Medical Record Number - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Patient deleted successfully - schema: - $ref: '#/definitions/api-service_internal_models_patient.PatientDeleteResponse' - "400": - description: Invalid ID format - schema: - $ref: '#/definitions/api-service_internal_models.ErrorResponse' "404": description: Patient not found schema: @@ -2306,45 +2359,7 @@ paths: description: Internal server error schema: $ref: '#/definitions/api-service_internal_models.ErrorResponse' - summary: Delete patient - tags: - - Patient - put: - consumes: - - application/json - description: Updates an existing patient record - parameters: - - description: Medical Record Number - in: path - name: id - required: true - type: string - - description: Patient update request - in: body - name: request - required: true - schema: - $ref: '#/definitions/api-service_internal_models_patient.PatientUpdateRequest' - produces: - - application/json - responses: - "200": - description: Patient updated successfully - schema: - $ref: '#/definitions/api-service_internal_models_patient.PatientUpdateResponse' - "400": - description: Bad request or validation error - schema: - $ref: '#/definitions/api-service_internal_models.ErrorResponse' - "404": - description: Patient not found - schema: - $ref: '#/definitions/api-service_internal_models.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/api-service_internal_models.ErrorResponse' - summary: Update patient + summary: Get Patient by ID tags: - Patient /permission: diff --git a/examples/clientsocket/.gitignore b/examples/clientsocket/.gitignore new file mode 100644 index 0000000..4a7f73a --- /dev/null +++ b/examples/clientsocket/.gitignore @@ -0,0 +1,24 @@ +# Nuxt dev/build outputs +.output +.data +.nuxt +.nitro +.cache +dist + +# Node dependencies +node_modules + +# Logs +logs +*.log + +# Misc +.DS_Store +.fleet +.idea + +# Local env files +.env +.env.* +!.env.example diff --git a/examples/clientsocket/README.md b/examples/clientsocket/README.md new file mode 100644 index 0000000..25b5821 --- /dev/null +++ b/examples/clientsocket/README.md @@ -0,0 +1,75 @@ +# Nuxt Minimal Starter + +Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. + +## Setup + +Make sure to install dependencies: + +```bash +# npm +npm install + +# pnpm +pnpm install + +# yarn +yarn install + +# bun +bun install +``` + +## Development Server + +Start the development server on `http://localhost:3000`: + +```bash +# npm +npm run dev + +# pnpm +pnpm dev + +# yarn +yarn dev + +# bun +bun run dev +``` + +## Production + +Build the application for production: + +```bash +# npm +npm run build + +# pnpm +pnpm build + +# yarn +yarn build + +# bun +bun run build +``` + +Locally preview production build: + +```bash +# npm +npm run preview + +# pnpm +pnpm preview + +# yarn +yarn preview + +# bun +bun run preview +``` + +Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. diff --git a/examples/clientsocket/app/app.vue b/examples/clientsocket/app/app.vue new file mode 100644 index 0000000..09f935b --- /dev/null +++ b/examples/clientsocket/app/app.vue @@ -0,0 +1,6 @@ + diff --git a/examples/clientsocket/assets/css/main.css b/examples/clientsocket/assets/css/main.css new file mode 100644 index 0000000..e3c5807 --- /dev/null +++ b/examples/clientsocket/assets/css/main.css @@ -0,0 +1,672 @@ +/* Enhanced WebSocket Client Styles */ +* { + box-sizing: border-box; +} + +body { + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + margin: 0; + padding: 20px; + background-color: #f5f5f5; + color: #333; +} + +.container { + max-width: 1400px; + margin: 0 auto; + background: white; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); + padding: 24px; + overflow: hidden; +} + +h1 { + color: #333; + text-align: center; + margin-bottom: 32px; + font-weight: 600; +} + +.status-bar { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 16px; + padding: 20px; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + border-radius: 12px; + margin-bottom: 24px; +} + +.status-item { + display: flex; + align-items: center; + gap: 8px; + font-weight: 500; +} + +.status-indicator { + width: 12px; + height: 12px; + border-radius: 50%; + display: inline-block; +} + +.status-connected { + background: #4CAF50; + box-shadow: 0 0 8px rgba(76, 175, 80, 0.6); +} + +.status-connecting { + background: #FFC107; + animation: pulse 1.5s infinite; +} + +.status-disconnected { + background: #F44336; +} + +@keyframes pulse { + 0% { opacity: 1; } + 50% { opacity: 0.5; } + 100% { opacity: 1; } +} + +.health-indicator { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-radius: 6px; + font-size: 12px; + font-weight: bold; +} + +.health-excellent { + background: #d4edda; + color: #155724; +} + +.health-good { + background: #d1ecf1; + color: #0c5460; +} + +.health-warning { + background: #fff3cd; + color: #856404; +} + +.health-poor { + background: #f8d7da; + color: #721c24; +} + +.tabs { + display: flex; + border-bottom: 2px solid #e9ecef; + margin-bottom: 24px; + overflow-x: auto; + background: white; + border-radius: 8px 8px 0 0; +} + +.tab { + padding: 16px 24px; + background: transparent; + border: none; + border-bottom: 3px solid transparent; + cursor: pointer; + white-space: nowrap; + transition: all 0.3s ease; + font-weight: 500; + color: #666; +} + +.tab:hover { + background: #f8f9fa; + color: #333; +} + +.tab.active { + border-bottom-color: #1976d2; + color: #1976d2; + font-weight: 600; + background: #f8f9fa; +} + +.tab-content { + display: none; + animation: fadeIn 0.3s ease; +} + +.tab-content.active { + display: block; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} + +.controls { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; + margin-bottom: 24px; + padding: 24px; + background: #f8f9fa; + border-radius: 8px; +} + +.input-group { + display: flex; + flex-direction: column; + gap: 12px; +} + +.input-group label { + font-weight: 600; + color: #333; + margin-bottom: 8px; +} + +.input-group input, +.input-group select, +.input-group textarea { + padding: 12px 16px; + border: 2px solid #e9ecef; + border-radius: 6px; + font-size: 14px; + transition: border-color 0.3s ease; +} + +.input-group input:focus, +.input-group select:focus, +.input-group textarea:focus { + outline: none; + border-color: #1976d2; + box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.1); +} + +.checkbox-group { + display: flex; + align-items: center; + gap: 8px; + margin: 8px 0; +} + +.checkbox-group input[type="checkbox"] { + margin: 0; + width: 18px; + height: 18px; +} + +.btn { + padding: 12px 24px; + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 14px; + font-weight: 600; + transition: all 0.3s ease; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.btn-primary { + background: #1976d2; + color: white; +} + +.btn-primary:hover { + background: #1565c0; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(25, 118, 210, 0.3); +} + +.btn-secondary { + background: #6c757d; + color: white; +} + +.btn-secondary:hover { + background: #5a6268; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(108, 117, 125, 0.3); +} + +.btn-warning { + background: #FFC107; + color: #212529; +} + +.btn-warning:hover { + background: #e0a800; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(255, 193, 7, 0.3); +} + +.btn-info { + background: #17a2b8; + color: white; +} + +.btn-info:hover { + background: #138496; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(23, 162, 184, 0.3); +} + +.btn-danger { + background: #dc3545; + color: white; +} + +.btn-danger:hover { + background: #c82333; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(220, 53, 69, 0.3); +} + +.btn-success { + background: #28a745; + color: white; +} + +.btn-success:hover { + background: #218838; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3); +} + +.messages-container { + height: 500px; + overflow-y: auto; + border: 2px solid #e9ecef; + border-radius: 8px; + padding: 20px; + background: #fafafa; + font-family: "SF Mono", "Monaco", "Inconsolata", "Roboto Mono", monospace; + font-size: 13px; + line-height: 1.5; +} + +.messages-container::-webkit-scrollbar { + width: 8px; +} + +.messages-container::-webkit-scrollbar-track { + background: #f1f1f1; + border-radius: 4px; +} + +.messages-container::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 4px; +} + +.messages-container::-webkit-scrollbar-thumb:hover { + background: #a8a8a8; +} + +.message { + margin-bottom: 16px; + padding: 16px; + border-radius: 8px; + border-left: 4px solid #1976d2; + background: white; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + animation: slideIn 0.3s ease; +} + +@keyframes slideIn { + from { opacity: 0; transform: translateX(-20px); } + to { opacity: 1; transform: translateX(0); } +} + +.message.error { + border-left-color: #dc3545; + background: #fff5f5; +} + +.message.warning { + border-left-color: #FFC107; + background: #fffbf0; +} + +.message.info { + border-left-color: #17a2b8; + background: #f0f9ff; +} + +.message.success { + border-left-color: #28a745; + background: #f0fff4; +} + +.message-time { + font-size: 11px; + color: #666; + margin-bottom: 8px; + font-weight: 500; +} + +.message-type { + font-weight: 600; + color: #333; + margin-bottom: 8px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + margin: 24px 0; +} + +.stat-card { + background: white; + padding: 24px; + border-radius: 12px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + text-align: center; + border: 1px solid #e9ecef; +} + +.stat-number { + font-size: 2.5em; + font-weight: 700; + color: #1976d2; + margin-bottom: 8px; +} + +.stat-label { + color: #666; + font-size: 14px; + font-weight: 500; +} + +.online-users { + max-height: 400px; + overflow-y: auto; + background: #f8f9fa; + border-radius: 8px; + padding: 20px; + border: 1px solid #e9ecef; +} + +.online-users::-webkit-scrollbar { + width: 6px; +} + +.online-users::-webkit-scrollbar-track { + background: #f1f1f1; +} + +.online-users::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 3px; +} + +.user-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 16px; + margin: 8px 0; + background: white; + border-radius: 6px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + border: 1px solid #e9ecef; +} + +.user-info { + display: flex; + flex-direction: column; + gap: 4px; +} + +.user-id { + font-weight: 600; + color: #333; +} + +.user-details { + font-size: 12px; + color: #666; +} + +.loading-indicator { + display: inline-block; + width: 16px; + height: 16px; + border: 2px solid #f3f3f3; + border-top: 2px solid #1976d2; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-left: 8px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.message-limit-warning { + background: #fff3cd; + border: 1px solid #ffeaa7; + padding: 16px; + margin: 16px 0; + border-radius: 6px; + font-size: 14px; + color: #856404; + display: flex; + align-items: center; + gap: 8px; +} + +.admin-controls { + background: #fff3cd; + padding: 20px; + border-radius: 8px; + margin-bottom: 24px; + border: 1px solid #ffeaa7; +} + +.admin-warning { + color: #856404; + font-weight: 600; + margin-bottom: 16px; + display: flex; + align-items: center; + gap: 8px; +} + +pre { + background: #f8f9fa; + padding: 12px; + border-radius: 6px; + overflow-x: auto; + font-size: 12px; + border: 1px solid #e9ecef; + margin: 8px 0; +} + +code { + background: #f8f9fa; + padding: 2px 6px; + border-radius: 3px; + font-family: "SF Mono", "Monaco", "Inconsolata", "Roboto Mono", monospace; + font-size: 13px; +} + +.message pre { + white-space: pre-wrap; + word-break: break-word; +} + +.message code { + display: block; + margin: 8px 0; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .container { + margin: 10px; + padding: 16px; + border-radius: 8px; + } + + .controls { + grid-template-columns: 1fr; + padding: 16px; + } + + .status-bar { + grid-template-columns: 1fr; + padding: 16px; + } + + .stats-grid { + grid-template-columns: 1fr; + } + + .tabs { + flex-wrap: wrap; + } + + .tab { + padding: 12px 16px; + font-size: 14px; + } + + .messages-container { + height: 300px; + font-size: 12px; + } + + .message { + padding: 12px; + font-size: 13px; + } + + .btn { + padding: 10px 16px; + font-size: 13px; + } +} + +@media (max-width: 480px) { + .container { + padding: 12px; + } + + .controls { + padding: 12px; + } + + .status-bar { + padding: 12px; + } + + .messages-container { + height: 250px; + padding: 12px; + } + + .tab { + padding: 8px 12px; + font-size: 12px; + } +} + +/* Dark mode support */ +@media (prefers-color-scheme: dark) { + body { + background-color: #121212; + color: #e0e0e0; + } + + .container { + background: #1e1e1e; + border: 1px solid #333; + } + + .controls { + background: #2a2a2a; + border: 1px solid #404040; + } + + .message { + background: #2a2a2a; + border: 1px solid #404040; + } + + .stat-card { + background: #2a2a2a; + border: 1px solid #404040; + } + + .online-users { + background: #2a2a2a; + border: 1px solid #404040; + } + + .user-item { + background: #333; + border: 1px solid #404040; + } + + .messages-container { + background: #1a1a1a; + border-color: #404040; + } + + pre { + background: #2a2a2a; + border-color: #404040; + } + + code { + background: #333; + } +} + +/* Accessibility improvements */ +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} + +/* Focus styles for keyboard navigation */ +.btn:focus, +.tab:focus, +.input-group input:focus, +.input-group select:focus, +.input-group textarea:focus { + outline: 2px solid #1976d2; + outline-offset: 2px; +} + +/* High contrast mode support */ +@media (prefers-contrast: high) { + .btn { + border: 2px solid currentColor; + } + + .message { + border: 2px solid currentColor; + } + + .stat-card { + border: 2px solid currentColor; + } +} diff --git a/examples/clientsocket/client.html b/examples/clientsocket/client.html new file mode 100644 index 0000000..98e2b40 --- /dev/null +++ b/examples/clientsocket/client.html @@ -0,0 +1,2251 @@ + + + + WebSocket Test Client + + + +
+

🌐 WebSocket Test Client - Enhanced & Optimized

+ + +
+
+ + Status: + Disconnected + +
+
+ Health: +
+ 🔴 Poor +
+
+
+ Uptime: 0s +
+
+ Client ID: + Not connected +
+
+ Reconnect: Not needed +
+
+ + + + + +
+ + + + + +
+ + +
+
+
+ + + + +
+ + +
+
+ + +
+
+ + +
+ + +
+ +
+ + + + + +
+
+
+ + +
+
+
+ + + +
+ +
+ + + + +
+ +
+ + + + + +
+
+
+ + +
+
+
+ + + + + +
+ +
+ + + + +
+
+
+ + +
+
+
+
0
+
Messages Received
+
+
+
0
+
Messages Sent
+
+
+
0ms
+
Connection Latency
+
+
+
0
+
Reconnection Attempts
+
+
+ +
+
+ + +
+
+ No users data available. Click "Refresh Users" to load. +
+
+
+
+
+ + +
+
+
+ ⚠️ Admin functions - Use with caution! +
+
+
+ + + + +
+ +
+ + + + +
+
+
+
+ + +
+

+ 📨 Messages + 0 messages + + +

+
+
+
+ + + + diff --git a/examples/clientsocket/components/WebSocketClient.vue b/examples/clientsocket/components/WebSocketClient.vue new file mode 100644 index 0000000..9e35f9d --- /dev/null +++ b/examples/clientsocket/components/WebSocketClient.vue @@ -0,0 +1,367 @@ + + + + + diff --git a/examples/clientsocket/components/tabs/AdminTab.vue b/examples/clientsocket/components/tabs/AdminTab.vue new file mode 100644 index 0000000..eacad47 --- /dev/null +++ b/examples/clientsocket/components/tabs/AdminTab.vue @@ -0,0 +1,665 @@ + + + + + diff --git a/examples/clientsocket/components/tabs/ConnectionTab.vue b/examples/clientsocket/components/tabs/ConnectionTab.vue new file mode 100644 index 0000000..988e02f --- /dev/null +++ b/examples/clientsocket/components/tabs/ConnectionTab.vue @@ -0,0 +1,397 @@ + + + + + diff --git a/examples/clientsocket/components/tabs/DatabaseTab.vue b/examples/clientsocket/components/tabs/DatabaseTab.vue new file mode 100644 index 0000000..6879f18 --- /dev/null +++ b/examples/clientsocket/components/tabs/DatabaseTab.vue @@ -0,0 +1,536 @@ + + + + + diff --git a/examples/clientsocket/components/tabs/MessagingTab.vue b/examples/clientsocket/components/tabs/MessagingTab.vue new file mode 100644 index 0000000..5d4dd99 --- /dev/null +++ b/examples/clientsocket/components/tabs/MessagingTab.vue @@ -0,0 +1,602 @@ + + + + + diff --git a/examples/clientsocket/components/tabs/MonitoringTab.vue b/examples/clientsocket/components/tabs/MonitoringTab.vue new file mode 100644 index 0000000..ad12093 --- /dev/null +++ b/examples/clientsocket/components/tabs/MonitoringTab.vue @@ -0,0 +1,637 @@ + + + + + diff --git a/examples/clientsocket/composables/note b/examples/clientsocket/composables/note new file mode 100644 index 0000000..7478c16 --- /dev/null +++ b/examples/clientsocket/composables/note @@ -0,0 +1,1073 @@ +import { ref, computed, reactive, nextTick } from "vue"; +import type { + WebSocketMessage, + ConnectionState, + WebSocketConfig, + MessageHistory, + ConnectionStats, + MonitoringData, + ClientInfo, + OnlineUser, + ActivityLog, +} from "../types/websocket"; + +export const useWebSocket = () => { + // Check if we're in browser environment + const isBrowser = process.client; + + const ws = ref(null); + const isConnected = ref(false); + const isConnecting = ref(false); + const connectionStatus = ref< + "disconnected" | "connecting" | "connected" | "error" + >("disconnected"); + + const connectionState = reactive({ + isConnected: false, + isConnecting: false, + connectionStatus: "disconnected", + clientId: null, + staticId: null, + currentRoom: null, + userId: "anonymous", + ipAddress: null, + connectionStartTime: null, + lastPingTime: null, + connectionLatency: 0, + connectionHealth: "poor", + reconnectAttempts: 0, + messagesReceived: 0, + messagesSent: 0, + uptime: "00:00:00", + }); + + const config = reactive({ + wsUrl: "ws://localhost:8080/api/v1/ws", + userId: "anonymous", + room: "default", + staticId: "", + useIPBasedId: false, + autoReconnect: true, + heartbeatEnabled: true, + maxReconnectAttempts: 10, + reconnectDelay: 1000, + maxReconnectDelay: 30000, + heartbeatInterval: 30000, + heartbeatTimeout: 5000, + maxMissedHeartbeats: 3, + maxMessages: 1000, + messageWarningThreshold: 800, + actionThrottle: 100, + }); + + const messages = ref([]); + const stats = ref(null); + const monitoringData = ref(null); + const onlineUsers = ref([]); + const activityLog = ref([]); + + let reconnectTimeout: number | null = null; + let heartbeatInterval: number | null = null; + let heartbeatTimeout: number | null = null; + let missedHeartbeats = 0; + let lastHeartbeatTime = 0; + let messageCount = 0; + let connectionStartTime: number | null = null; + let uptimeInterval: number | null = null; + let isManualDisconnect = false; + let reconnectAttempts = 0; + let connectionHealth: "excellent" | "good" | "warning" | "poor" = "poor"; + + const updateConnectionStatus = ( + status: "disconnected" | "connecting" | "connected" | "error" + ) => { + connectionStatus.value = status; + connectionState.connectionStatus = status; + + switch (status) { + case "connected": + isConnected.value = true; + isConnecting.value = false; + connectionState.isConnected = true; + connectionState.isConnecting = false; + connectionState.connectionHealth = "good"; + break; + case "connecting": + isConnected.value = false; + isConnecting.value = true; + connectionState.isConnected = false; + connectionState.isConnecting = true; + connectionState.connectionHealth = "warning"; + break; + case "disconnected": + isConnected.value = false; + isConnecting.value = false; + connectionState.isConnected = false; + connectionState.isConnecting = false; + connectionState.connectionHealth = "poor"; + break; + case "error": + isConnected.value = false; + isConnecting.value = false; + connectionState.isConnected = false; + connectionState.isConnecting = false; + connectionState.connectionHealth = "poor"; + break; + } + }; + + const updateUptime = () => { + if (connectionStartTime && isConnected.value) { + const now = Date.now(); + const uptime = now - connectionStartTime; + const seconds = Math.floor(uptime / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + + connectionState.uptime = `${hours.toString().padStart(2, "0")}:${( + minutes % 60 + ) + .toString() + .padStart(2, "0")}:${(seconds % 60).toString().padStart(2, "0")}`; + } else { + connectionState.uptime = "00:00:00"; + } + }; + + const startUptimeTimer = () => { + if (uptimeInterval) { + clearInterval(uptimeInterval); + } + uptimeInterval = window.setInterval(updateUptime, 1000); + }; + + const stopUptimeTimer = () => { + if (uptimeInterval) { + clearInterval(uptimeInterval); + uptimeInterval = null; + } + }; + + // Only run WebSocket logic in browser + if (isBrowser) { + // Initialize connection state + updateConnectionStatus("disconnected"); + } + + const addMessage = ( + type: string, + data: any, + messageId?: string, + icon?: string + ) => { + if (!isBrowser) return; + + const now = new Date(); + const timeString = now.toLocaleTimeString("en-US", { + hour12: true, + hour: "numeric", + minute: "2-digit", + second: "2-digit", + }); + + // Enhanced message formatting based on type + let formattedData = data; + let displayType = type; + + switch (type) { + case "connection_info": + displayType = "info"; + formattedData = { + client_id: data.clientId, + connected_at: data.connectedAt, + ip_address: data.ipAddress, + last_ping: data.lastPingTime || "N/A", + room: data.room || "default", + static_id: data.staticId || "none", + user_id: data.userId || "anonymous", + }; + break; + + case "heartbeat": + displayType = "info"; + formattedData = `❤️ Heartbeat started\nInterval: ${ + config.heartbeatInterval / 1000 + }s`; + break; + + case "pong": + displayType = "success"; + formattedData = `🏓 Pong received\nLatency: ${connectionState.connectionLatency}ms`; + break; + + case "message": + displayType = "info"; + formattedData = `📨 Message received\n${JSON.stringify(data, null, 2)}`; + break; + + case "broadcast": + displayType = "info"; + formattedData = `📡 Broadcast received\n${data}`; + break; + + case "direct_message": + displayType = "info"; + formattedData = `📨 Direct message from ${data.from}\n${data.message}`; + break; + + case "room_message": + displayType = "info"; + formattedData = `📢 Room message from ${data.room}\n${data.message}`; + break; + + case "stats": + displayType = "info"; + formattedData = `📊 Stats updated\n${JSON.stringify(data, null, 2)}`; + break; + + case "monitoring": + displayType = "info"; + formattedData = `📈 Monitoring data\n${JSON.stringify(data, null, 2)}`; + break; + + case "online_users": + displayType = "info"; + formattedData = `👥 Online users updated\nCount: ${ + data.users?.length || 0 + }`; + break; + + case "server_info": + displayType = "info"; + formattedData = `🔧 Server info\n${JSON.stringify(data, null, 2)}`; + break; + + case "system_health": + displayType = "info"; + formattedData = `💚 System health\n${JSON.stringify(data, null, 2)}`; + break; + + default: + if (typeof data === "string") { + formattedData = data; + } else { + formattedData = JSON.stringify(data, null, 2); + } + } + + const message: MessageHistory = { + timestamp: now, + type: displayType, + data: formattedData, + messageId, + size: JSON.stringify(data).length, + icon: icon || getIconForMessageType(displayType), + timeString: timeString, + }; + + messages.value.unshift(message); + messageCount++; + + // Keep only the last maxMessages + if (messages.value.length > config.maxMessages) { + messages.value = messages.value.slice(0, config.maxMessages); + } + + // Update connection state + connectionState.messagesReceived++; + }; + + const getIconForMessageType = (type: string): string => { + switch (type) { + case "success": + return "🟢"; + case "error": + return "❌"; + case "warning": + return "⚠️"; + case "info": + return "ℹ️"; + default: + return "📝"; + } + }; + + const connectionHealthColor = computed(() => { + switch (connectionState.connectionHealth) { + case "excellent": + return "#4CAF50"; + case "good": + return "#2196F3"; + case "warning": + return "#FFC107"; + case "poor": + return "#F44336"; + default: + return "#9E9E9E"; + } + }); + + const connectionHealthText = computed(() => { + switch (connectionState.connectionHealth) { + case "excellent": + return "Excellent"; + case "good": + return "Good"; + case "warning": + return "Warning"; + case "poor": + return "Poor"; + default: + return "Unknown"; + } + }); + + // Admin functionality + const serverInfo = ref(null); + const systemHealth = ref(null); + + const executeAdminCommand = async (command: string, params: any) => { + if (!isBrowser || !ws.value) throw new Error("Not connected"); + + const message = { + type: "admin_command", + command, + params, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + return { success: true, message: "Command sent successfully" }; + }; + + const getServerInfo = async () => { + if (!isBrowser || !ws.value) throw new Error("Not connected"); + + const message = { + type: "get_server_info", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + }; + + const getSystemHealth = async () => { + if (!isBrowser || !ws.value) throw new Error("Not connected"); + + const message = { + type: "get_system_health", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + }; + + // Cleanup on unmount + const cleanup = () => { + if (!isBrowser) return; + + disconnect(); + if (reconnectTimeout) { + clearTimeout(reconnectTimeout); + } + stopHeartbeat(); + }; + + // WebSocket connection methods (only available in browser) + const connect = () => { + if (!isBrowser) return; + + // Prevent multiple connection attempts + if (isConnecting.value || isConnected.value) { + return; + } + + // Clean up existing connection + if (ws.value) { + cleanupConnection(); + } + + updateConnectionStatus("connecting"); + isManualDisconnect = false; + connectionStartTime = Date.now(); + startUptimeTimer(); + + try { + // Build WebSocket URL with parameters + let url = config.wsUrl; + const params = new URLSearchParams(); + + if (config.userId) { + params.append("user_id", config.userId); + } + if (config.room) { + params.append("room", config.room); + } + if (config.useIPBasedId) { + params.append("ip_based", "true"); + } else if (config.staticId) { + params.append("static_id", config.staticId); + } + + if (params.toString()) { + url += "?" + params.toString(); + } + + ws.value = new WebSocket(url); + + // Connection timeout + const connectionTimeout = setTimeout(() => { + if (ws.value && ws.value.readyState === WebSocket.CONNECTING) { + ws.value.close(); + updateConnectionStatus("error"); + addMessage("❌ Connection timeout after 15 seconds", "error"); + scheduleReconnect(); + } + }, 15000); + + ws.value.onopen = function (event) { + clearTimeout(connectionTimeout); + updateConnectionStatus("connected"); + reconnectAttempts = 0; + connectionHealth = "good"; + connectionState.connectionStartTime = connectionStartTime; + + addMessage( + "🟢 Connected to WebSocket server", + "success", + "Connection established successfully" + ); + + // Start heartbeat if enabled + if (config.heartbeatEnabled) { + startHeartbeat(); + } + }; + + ws.value.onmessage = function (event) { + handleIncomingMessage(event); + }; + + ws.value.onclose = function (event) { + clearTimeout(connectionTimeout); + cleanupTimers(); + updateConnectionStatus("disconnected"); + + const reason = getCloseReason(event.code); + addMessage( + `🔴 Disconnected: ${reason}`, + "error", + `Code: ${event.code}, Reason: ${event.reason || "Unknown"}` + ); + + // Auto-reconnect logic + if (!isManualDisconnect && config.autoReconnect) { + scheduleReconnect(); + } + }; + + ws.value.onerror = function (error) { + updateConnectionStatus("error"); + connectionHealth = "poor"; + addMessage("❌ WebSocket Error occurred", "error", error.toString()); + }; + } catch (error) { + updateConnectionStatus("error"); + addMessage( + "❌ Failed to create WebSocket connection", + "error", + error instanceof Error ? error.message : String(error) + ); + scheduleReconnect(); + } + }; + + const disconnect = () => { + if (!isBrowser) return; + + isManualDisconnect = true; + cleanupConnection(); + updateConnectionStatus("disconnected"); + addMessage("🔴 Manually disconnected from WebSocket", "info"); + }; + + const sendMessage = (message: any) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send message: not connected", "error"); + return; + } + + try { + const messageData = { + type: "message", + data: message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage("📤 Message sent", "info", message); + } catch (error) { + addMessage( + "❌ Failed to send message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const broadcastMessage = (message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot broadcast message: not connected", "error"); + return; + } + + try { + const messageData = { + type: "broadcast", + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage("📡 Message broadcasted", "info", message); + } catch (error) { + addMessage( + "❌ Failed to broadcast message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const sendDirectMessage = (clientId: string, message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send direct message: not connected", "error"); + return; + } + + try { + const messageData = { + type: "direct_message", + clientId, + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage(`📨 Direct message sent to ${clientId}`, "info", message); + } catch (error) { + addMessage( + "❌ Failed to send direct message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const sendRoomMessage = (room: string, message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send room message: not connected", "error"); + return; + } + + try { + const messageData = { + type: "room_message", + room, + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage(`📢 Room message sent to ${room}`, "info", message); + } catch (error) { + addMessage( + "❌ Failed to send room message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const getOnlineUsers = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot get online users: not connected", "error"); + return; + } + + try { + const message = { + type: "get_online_users", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + } catch (error) { + addMessage( + "❌ Failed to request online users", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const testConnection = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot test connection: not connected", "error"); + return; + } + + try { + const message = { + type: "ping", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + addMessage("🏓 Connection test sent", "info"); + } catch (error) { + addMessage( + "❌ Failed to test connection", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const sendHeartbeat = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send heartbeat: not connected", "error"); + return; + } + + try { + const message = { + type: "ping", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + lastHeartbeatTime = Date.now(); + } catch (error) { + addMessage( + "❌ Failed to send heartbeat", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const executeDatabaseQuery = async (query: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + throw new Error("Not connected"); + } + + try { + const message = { + type: "database_query", + query, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + return { success: true, message: "Database query sent successfully" }; + } catch (error) { + throw new Error( + `Failed to execute database query: ${ + error instanceof Error ? error.message : String(error) + }` + ); + } + }; + + const triggerNotification = async (message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + throw new Error("Not connected"); + } + + try { + const notificationData = { + type: "notification", + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(notificationData)); + return { success: true, message: "Notification sent successfully" }; + } catch (error) { + throw new Error( + `Failed to trigger notification: ${ + error instanceof Error ? error.message : String(error) + }` + ); + } + }; + + const getStats = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot get stats: not connected", "error"); + return; + } + + try { + const message = { + type: "get_stats", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + } catch (error) { + addMessage( + "❌ Failed to request stats", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const getMonitoringData = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot get monitoring data: not connected", "error"); + return; + } + + try { + const message = { + type: "get_monitoring", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + } catch (error) { + addMessage( + "❌ Failed to request monitoring data", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const clearMessages = () => { + if (!isBrowser) return; + messages.value = []; + messageCount = 0; + }; + + const clearActivityLog = () => { + if (!isBrowser) return; + activityLog.value = []; + }; + + const getMessagesByType = (type: string) => { + return messages.value.filter((msg) => msg.type === type); + }; + + const getRecentMessages = (count: number = 10) => { + return messages.value.slice(0, count); + }; + + const stopHeartbeat = () => { + if (!isBrowser) return; + + if (heartbeatInterval) { + clearInterval(heartbeatInterval); + heartbeatInterval = null; + } + if (heartbeatTimeout) { + clearTimeout(heartbeatTimeout); + heartbeatTimeout = null; + } + missedHeartbeats = 0; + }; + + const cleanupConnection = () => { + if (!isBrowser) return; + + if (ws.value) { + ws.value.close(); + ws.value = null; + } + cleanupTimers(); + }; + + const cleanupTimers = () => { + if (!isBrowser) return; + + if (reconnectTimeout) { + clearTimeout(reconnectTimeout); + reconnectTimeout = null; + } + stopHeartbeat(); + stopUptimeTimer(); + }; + + const scheduleReconnect = () => { + if (!isBrowser || !config.autoReconnect) return; + + if (reconnectAttempts >= config.maxReconnectAttempts) { + addMessage("❌ Max reconnection attempts reached", "error"); + return; + } + + reconnectAttempts++; + const delay = Math.min( + config.reconnectDelay * Math.pow(2, reconnectAttempts - 1), + config.maxReconnectDelay + ); + + addMessage( + `🔄 Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${config.maxReconnectAttempts})`, + "info" + ); + + reconnectTimeout = window.setTimeout(() => { + connect(); + }, delay); + }; + + const startHeartbeat = () => { + if (!isBrowser || !config.heartbeatEnabled) return; + + stopHeartbeat(); + + heartbeatInterval = window.setInterval(() => { + if (ws.value && ws.value.readyState === WebSocket.OPEN) { + const heartbeatMessage = { + type: "ping", + timestamp: Date.now(), + }; + ws.value.send(JSON.stringify(heartbeatMessage)); + lastHeartbeatTime = Date.now(); + + // Set timeout for heartbeat response + heartbeatTimeout = window.setTimeout(() => { + missedHeartbeats++; + if (missedHeartbeats >= config.maxMissedHeartbeats) { + addMessage( + "❌ Heartbeat timeout - connection unhealthy", + "warning" + ); + connectionHealth = "warning"; + } + }, config.heartbeatTimeout); + } + }, config.heartbeatInterval); + }; + + const handleIncomingMessage = (event: MessageEvent) => { + if (!isBrowser) return; + + try { + const data = JSON.parse(event.data); + + // Handle heartbeat response + if (data.type === "pong") { + missedHeartbeats = 0; + connectionHealth = "excellent"; + const latency = Date.now() - lastHeartbeatTime; + connectionState.connectionLatency = latency; + return; + } + + // Handle connection info + if (data.type === "connection_info") { + connectionState.clientId = data.clientId; + connectionState.staticId = data.staticId; + connectionState.currentRoom = data.room; + connectionState.userId = data.userId; + connectionState.ipAddress = data.ipAddress; + return; + } + + // Handle welcome message (server sends connection info in welcome message) + if (data.type === "welcome") { + console.log("Received welcome message:", data); + + // Map server snake_case fields to camelCase for Vue components + if (data.client_id) { + connectionState.clientId = data.client_id; + console.log("Set clientId:", data.client_id); + } + if (data.static_id) { + connectionState.staticId = data.static_id; + console.log("Set staticId:", data.static_id); + } + if (data.room) { + connectionState.currentRoom = data.room; + console.log("Set currentRoom:", data.room); + } + if (data.user_id) { + connectionState.userId = data.user_id; + console.log("Set userId:", data.user_id); + } + if (data.ip_address) { + connectionState.ipAddress = data.ip_address; + console.log("Set ipAddress:", data.ip_address); + } + if (data.connected_at) { + connectionState.connectionStartTime = data.connected_at * 1000; + console.log("Set connectionStartTime:", data.connected_at * 1000); + } + + // Force reactive update by triggering a change + connectionState.connectionHealth = + connectionState.connectionHealth === "good" ? "excellent" : "good"; + + console.log("Updated connectionState:", connectionState); + addMessage( + "🟢 Connection established successfully", + "success", + "Welcome message processed" + ); + return; + } + + // Handle stats + if (data.type === "stats") { + stats.value = data; + return; + } + + // Handle monitoring data + if (data.type === "monitoring") { + monitoringData.value = data; + return; + } + + // Handle online users + if (data.type === "online_users") { + onlineUsers.value = data.users; + return; + } + + // Handle activity log + if (data.type === "activity") { + activityLog.value.unshift(data); + if (activityLog.value.length > 100) { + activityLog.value = activityLog.value.slice(0, 100); + } + return; + } + + // Handle server info + if (data.type === "server_info") { + serverInfo.value = data; + return; + } + + // Handle system health + if (data.type === "system_health") { + systemHealth.value = data; + return; + } + + // Handle regular messages + addMessage(data.type || "message", data, data.messageId); + } catch (error) { + addMessage( + "❌ Failed to parse message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const getCloseReason = (code: number): string => { + switch (code) { + case 1000: + return "Normal closure"; + case 1001: + return "Going away"; + case 1002: + return "Protocol error"; + case 1003: + return "Unsupported data"; + case 1004: + return "Reserved"; + case 1005: + return "No status received"; + case 1006: + return "Abnormal closure"; + case 1007: + return "Invalid frame payload data"; + case 1008: + return "Policy violation"; + case 1009: + return "Message too big"; + case 1010: + return "Mandatory extension"; + case 1011: + return "Internal server error"; + case 1012: + return "Service restart"; + case 1013: + return "Try again later"; + case 1014: + return "Bad gateway"; + case 1015: + return "TLS handshake"; + default: + return "Unknown reason"; + } + }; + + const isMessageLimitReached = computed(() => { + return messages.value.length >= config.maxMessages; + }); + + const shouldShowMessageWarning = computed(() => { + return messages.value.length >= config.messageWarningThreshold; + }); + + return { + // State + ws, + isConnected, + isConnecting, + connectionStatus, + connectionState, + config, + messages, + stats, + monitoringData, + onlineUsers, + activityLog, + + // Admin state + serverInfo, + systemHealth, + + // Methods + connect, + disconnect, + sendMessage, + broadcastMessage, + sendDirectMessage, + sendRoomMessage, + getServerInfo, + getOnlineUsers, + testConnection, + sendHeartbeat, + executeDatabaseQuery, + triggerNotification, + getStats, + getMonitoringData, + executeAdminCommand, + getSystemHealth, + clearMessages, + clearActivityLog, + getMessagesByType, + getRecentMessages, + cleanup, + + // Computed + isMessageLimitReached, + shouldShowMessageWarning, + connectionHealthColor, + connectionHealthText, + }; +}; diff --git a/examples/clientsocket/composables/useWebSocket.fixed b/examples/clientsocket/composables/useWebSocket.fixed new file mode 100644 index 0000000..972b9d5 --- /dev/null +++ b/examples/clientsocket/composables/useWebSocket.fixed @@ -0,0 +1,1073 @@ +import { ref, computed, reactive, type Ref } from "vue"; +import type { + WebSocketMessage, + ConnectionState, + WebSocketConfig, + MessageHistory, + ConnectionStats, + MonitoringData, + ClientInfo, + OnlineUser, + ActivityLog, +} from "../types/websocket"; + +export const useWebSocket = () => { + // Check if we're in browser environment + const isBrowser = process.client; + + // WebSocket connection + const ws: Ref = ref(null); + + // Connection state - consolidated into single reactive object + const connectionState = reactive({ + isConnected: false, + isConnecting: false, + connectionStatus: "disconnected", + clientId: null, + staticId: null, + currentRoom: null, + userId: "anonymous", + ipAddress: null, + connectionStartTime: null, + lastPingTime: null, + connectionLatency: 0, + connectionHealth: "poor", + reconnectAttempts: 0, + messagesReceived: 0, + messagesSent: 0, + uptime: "00:00:00", + }); + + // Configuration + const config = reactive({ + wsUrl: "ws://localhost:8080/api/v1/ws", + userId: "anonymous", + room: "default", + staticId: "", + useIPBasedId: false, + autoReconnect: true, + heartbeatEnabled: true, + maxReconnectAttempts: 10, + reconnectDelay: 1000, + maxReconnectDelay: 30000, + heartbeatInterval: 30000, + heartbeatTimeout: 5000, + maxMissedHeartbeats: 3, + maxMessages: 1000, + messageWarningThreshold: 800, + actionThrottle: 100, + }); + + // Message history and data + const messages = ref([]); + const stats = ref(null); + const monitoringData = ref(null); + const onlineUsers = ref([]); + const activityLog = ref([]); + + // Admin functionality + const serverInfo = ref(null); + const systemHealth = ref(null); + + // Timer references for proper cleanup + let reconnectTimeout: number | null = null; + let heartbeatInterval: number | null = null; + let heartbeatTimeout: number | null = null; + let uptimeInterval: number | null = null; + let connectionStartTime: number | null = null; + + // Heartbeat tracking + let missedHeartbeats = 0; + let lastHeartbeatTime = 0; + let isManualDisconnect = false; + + // Helper function to update connection status + const updateConnectionStatus = ( + status: "disconnected" | "connecting" | "connected" | "error" + ) => { + connectionState.connectionStatus = status; + + switch (status) { + case "connected": + connectionState.isConnected = true; + connectionState.isConnecting = false; + connectionState.connectionHealth = "good"; + break; + case "connecting": + connectionState.isConnected = false; + connectionState.isConnecting = true; + connectionState.connectionHealth = "warning"; + break; + case "disconnected": + case "error": + connectionState.isConnected = false; + connectionState.isConnecting = false; + connectionState.connectionHealth = "poor"; + break; + } + }; + + // Helper function to update uptime + const updateUptime = () => { + if (connectionStartTime && connectionState.isConnected) { + const now = Date.now(); + const uptime = now - connectionStartTime; + const seconds = Math.floor(uptime / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + + connectionState.uptime = `${hours.toString().padStart(2, "0")}:${( + minutes % 60 + ) + .toString() + .padStart(2, "0")}:${(seconds % 60).toString().padStart(2, "0")}`; + } else { + connectionState.uptime = "00:00:00"; + } + }; + + // Timer management + const startUptimeTimer = () => { + if (uptimeInterval) { + clearInterval(uptimeInterval); + } + uptimeInterval = window.setInterval(updateUptime, 1000); + }; + + const stopUptimeTimer = () => { + if (uptimeInterval) { + clearInterval(uptimeInterval); + uptimeInterval = null; + } + }; + + // Message handling + const getIconForMessageType = (type: string): string => { + switch (type) { + case "success": + return "🟢"; + case "error": + return "❌"; + case "warning": + return "⚠️"; + case "info": + return "ℹ️"; + default: + return "📝"; + } + }; + + const addMessage = ( + type: string, + data: any, + messageId?: string, + icon?: string + ) => { + if (!isBrowser) return; + + const now = new Date(); + const timeString = now.toLocaleTimeString("en-US", { + hour12: true, + hour: "numeric", + minute: "2-digit", + second: "2-digit", + }); + + // Enhanced message formatting based on type + let formattedData = data; + let displayType = type; + + switch (type) { + case "connection_info": + displayType = "info"; + formattedData = { + client_id: data.clientId, + connected_at: data.connectedAt, + ip_address: data.ipAddress, + last_ping: data.lastPingTime || "N/A", + room: data.room || "default", + static_id: data.staticId || "none", + user_id: data.userId || "anonymous", + }; + break; + + case "heartbeat": + displayType = "info"; + formattedData = `❤️ Heartbeat started\nInterval: ${ + config.heartbeatInterval / 1000 + }s`; + break; + + case "pong": + displayType = "success"; + formattedData = `🏓 Pong received\nLatency: ${connectionState.connectionLatency}ms`; + break; + + case "message": + displayType = "info"; + formattedData = `📨 Message received\n${JSON.stringify(data, null, 2)}`; + break; + + case "broadcast": + displayType = "info"; + formattedData = `📡 Broadcast received\n${data}`; + break; + + case "direct_message": + displayType = "info"; + formattedData = `📨 Direct message from ${data.from}\n${data.message}`; + break; + + case "room_message": + displayType = "info"; + formattedData = `📢 Room message from ${data.room}\n${data.message}`; + break; + + case "stats": + displayType = "info"; + formattedData = `📊 Stats updated\n${JSON.stringify(data, null, 2)}`; + break; + + case "monitoring": + displayType = "info"; + formattedData = `📈 Monitoring data\n${JSON.stringify(data, null, 2)}`; + break; + + case "online_users": + displayType = "info"; + formattedData = `👥 Online users updated\nCount: ${ + data.users?.length || 0 + }`; + break; + + case "server_info": + displayType = "info"; + formattedData = `🔧 Server info\n${JSON.stringify(data, null, 2)}`; + break; + + case "system_health": + displayType = "info"; + formattedData = `💚 System health\n${JSON.stringify(data, null, 2)}`; + break; + + default: + if (typeof data === "string") { + formattedData = data; + } else { + formattedData = JSON.stringify(data, null, 2); + } + } + + const message: MessageHistory = { + timestamp: now, + type: displayType, + data: formattedData, + messageId, + size: JSON.stringify(data).length, + icon: icon || getIconForMessageType(displayType), + timeString: timeString, + }; + + messages.value.unshift(message); + + // Keep only the last maxMessages + if (messages.value.length > config.maxMessages) { + messages.value = messages.value.slice(0, config.maxMessages); + } + + // Update connection state + connectionState.messagesReceived++; + }; + + // Computed properties + const connectionHealthColor = computed(() => { + switch (connectionState.connectionHealth) { + case "excellent": + return "#4CAF50"; + case "good": + return "#2196F3"; + case "warning": + return "#FFC107"; + case "poor": + return "#F44336"; + default: + return "#9E9E9E"; + } + }); + + const connectionHealthText = computed(() => { + switch (connectionState.connectionHealth) { + case "excellent": + return "Excellent"; + case "good": + return "Good"; + case "warning": + return "Warning"; + case "poor": + return "Poor"; + default: + return "Unknown"; + } + }); + + const isMessageLimitReached = computed(() => { + return messages.value.length >= config.maxMessages; + }); + + const shouldShowMessageWarning = computed(() => { + return messages.value.length >= config.messageWarningThreshold; + }); + + // Timer cleanup + const cleanupTimers = () => { + if (!isBrowser) return; + + if (reconnectTimeout) { + clearTimeout(reconnectTimeout); + reconnectTimeout = null; + } + if (heartbeatInterval) { + clearInterval(heartbeatInterval); + heartbeatInterval = null; + } + if (heartbeatTimeout) { + clearTimeout(heartbeatTimeout); + heartbeatTimeout = null; + } + stopUptimeTimer(); + }; + + // Connection cleanup + const cleanupConnection = () => { + if (!isBrowser) return; + + if (ws.value) { + ws.value.close(); + ws.value = null; + } + cleanupTimers(); + }; + + // Get close reason description + const getCloseReason = (code: number): string => { + switch (code) { + case 1000: + return "Normal closure"; + case 1001: + return "Going away"; + case 1002: + return "Protocol error"; + case 1003: + return "Unsupported data"; + case 1004: + return "Reserved"; + case 1005: + return "No status received"; + case 1006: + return "Abnormal closure"; + case 1007: + return "Invalid frame payload data"; + case 1008: + return "Policy violation"; + case 1009: + return "Message too big"; + case 1010: + return "Mandatory extension"; + case 1011: + return "Internal server error"; + case 1012: + return "Service restart"; + case 1013: + return "Try again later"; + case 1014: + return "Bad gateway"; + case 1015: + return "TLS handshake"; + default: + return "Unknown reason"; + } + }; + + // Heartbeat management + const stopHeartbeat = () => { + if (!isBrowser) return; + + if (heartbeatInterval) { + clearInterval(heartbeatInterval); + heartbeatInterval = null; + } + if (heartbeatTimeout) { + clearTimeout(heartbeatTimeout); + heartbeatTimeout = null; + } + missedHeartbeats = 0; + }; + + const startHeartbeat = () => { + if (!isBrowser || !config.heartbeatEnabled) return; + + stopHeartbeat(); + + heartbeatInterval = window.setInterval(() => { + if (ws.value && ws.value.readyState === WebSocket.OPEN) { + const heartbeatMessage = { + type: "ping", + timestamp: Date.now(), + }; + ws.value.send(JSON.stringify(heartbeatMessage)); + lastHeartbeatTime = Date.now(); + + // Set timeout for heartbeat response + heartbeatTimeout = window.setTimeout(() => { + missedHeartbeats++; + if (missedHeartbeats >= config.maxMissedHeartbeats) { + addMessage( + "❌ Heartbeat timeout - connection unhealthy", + "warning" + ); + connectionState.connectionHealth = "warning"; + } + }, config.heartbeatTimeout); + } + }, config.heartbeatInterval); + }; + + // Reconnection logic + const scheduleReconnect = () => { + if (!isBrowser || !config.autoReconnect) return; + + if (connectionState.reconnectAttempts >= config.maxReconnectAttempts) { + addMessage("❌ Max reconnection attempts reached", "error"); + return; + } + + connectionState.reconnectAttempts++; + const delay = Math.min( + config.reconnectDelay * + Math.pow(2, connectionState.reconnectAttempts - 1), + config.maxReconnectDelay + ); + + addMessage( + `🔄 Reconnecting in ${delay}ms (attempt ${connectionState.reconnectAttempts}/${config.maxReconnectAttempts})`, + "info" + ); + + reconnectTimeout = window.setTimeout(() => { + connect(); + }, delay); + }; + + // Message handling + const handleIncomingMessage = (event: MessageEvent) => { + if (!isBrowser) return; + + try { + const data = JSON.parse(event.data); + + // Handle heartbeat response + if (data.type === "pong") { + missedHeartbeats = 0; + connectionState.connectionHealth = "excellent"; + const latency = Date.now() - lastHeartbeatTime; + connectionState.connectionLatency = latency; + return; + } + + // Handle connection info + if (data.type === "connection_info") { + connectionState.clientId = data.clientId; + connectionState.staticId = data.staticId; + connectionState.currentRoom = data.room; + connectionState.userId = data.userId; + connectionState.ipAddress = data.ipAddress; + return; + } + + // Handle welcome message (server sends connection info in welcome message) + if (data.type === "welcome") { + console.log("Received welcome message:", data); + + // Map server snake_case fields to camelCase for Vue components + if (data.client_id) { + connectionState.clientId = data.client_id; + console.log("Set clientId:", data.client_id); + } + if (data.static_id) { + connectionState.staticId = data.static_id; + console.log("Set staticId:", data.static_id); + } + if (data.room) { + connectionState.currentRoom = data.room; + console.log("Set currentRoom:", data.room); + } + if (data.user_id) { + connectionState.userId = data.user_id; + console.log("Set userId:", data.user_id); + } + if (data.ip_address) { + connectionState.ipAddress = data.ip_address; + console.log("Set ipAddress:", data.ip_address); + } + if (data.connected_at) { + connectionState.connectionStartTime = data.connected_at * 1000; + console.log("Set connectionStartTime:", data.connected_at * 1000); + } + + // Force reactive update by triggering a change + connectionState.connectionHealth = + connectionState.connectionHealth === "good" ? "excellent" : "good"; + + console.log("Updated connectionState:", connectionState); + addMessage( + "🟢 Connection established successfully", + "success", + "Welcome message processed" + ); + return; + } + + // Handle stats + if (data.type === "stats") { + stats.value = data; + return; + } + + // Handle monitoring data + if (data.type === "monitoring") { + monitoringData.value = data; + return; + } + + // Handle online users + if (data.type === "online_users") { + onlineUsers.value = data.users; + return; + } + + // Handle activity log + if (data.type === "activity") { + activityLog.value.unshift(data); + if (activityLog.value.length > 100) { + activityLog.value = activityLog.value.slice(0, 100); + } + return; + } + + // Handle server info + if (data.type === "server_info") { + serverInfo.value = data; + return; + } + + // Handle system health + if (data.type === "system_health") { + systemHealth.value = data; + return; + } + + // Handle regular messages + addMessage(data.type || "message", data, data.messageId); + } catch (error) { + addMessage( + "❌ Failed to parse message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + // WebSocket connection methods + const connect = () => { + if (!isBrowser) return; + + // Prevent multiple connection attempts + if (connectionState.isConnecting || connectionState.isConnected) { + return; + } + + // Clean up existing connection + if (ws.value) { + cleanupConnection(); + } + + updateConnectionStatus("connecting"); + isManualDisconnect = false; + connectionStartTime = Date.now(); + startUptimeTimer(); + + try { + // Build WebSocket URL with parameters + let url = config.wsUrl; + const params = new URLSearchParams(); + + if (config.userId) { + params.append("user_id", config.userId); + } + if (config.room) { + params.append("room", config.room); + } + if (config.useIPBasedId) { + params.append("ip_based", "true"); + } else if (config.staticId) { + params.append("static_id", config.staticId); + } + + if (params.toString()) { + url += "?" + params.toString(); + } + + ws.value = new WebSocket(url); + + // Connection timeout + const connectionTimeout = setTimeout(() => { + if (ws.value && ws.value.readyState === WebSocket.CONNECTING) { + ws.value.close(); + updateConnectionStatus("error"); + addMessage("❌ Connection timeout after 15 seconds", "error"); + scheduleReconnect(); + } + }, 15000); + + ws.value.onopen = function (event) { + clearTimeout(connectionTimeout); + updateConnectionStatus("connected"); + connectionState.reconnectAttempts = 0; + connectionState.connectionStartTime = connectionStartTime; + + addMessage( + "🟢 Connected to WebSocket server", + "success", + "Connection established successfully" + ); + + // Start heartbeat if enabled + if (config.heartbeatEnabled) { + startHeartbeat(); + } + }; + + ws.value.onmessage = function (event) { + handleIncomingMessage(event); + }; + + ws.value.onclose = function (event) { + clearTimeout(connectionTimeout); + cleanupTimers(); + updateConnectionStatus("disconnected"); + + const reason = getCloseReason(event.code); + addMessage( + `🔴 Disconnected: ${reason}`, + "error", + `Code: ${event.code}, Reason: ${event.reason || "Unknown"}` + ); + + // Auto-reconnect logic + if (!isManualDisconnect && config.autoReconnect) { + scheduleReconnect(); + } + }; + + ws.value.onerror = function (error) { + updateConnectionStatus("error"); + addMessage("❌ WebSocket Error occurred", "error", error.toString()); + }; + } catch (error) { + updateConnectionStatus("error"); + addMessage( + "❌ Failed to create WebSocket connection", + "error", + error instanceof Error ? error.message : String(error) + ); + scheduleReconnect(); + } + }; + + const disconnect = () => { + if (!isBrowser) return; + + isManualDisconnect = true; + cleanupConnection(); + updateConnectionStatus("disconnected"); + addMessage("🔴 Manually disconnected from WebSocket", "info"); + }; + + // Message sending methods + const sendMessage = (message: any) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send message: not connected", "error"); + return false; + } + + try { + const messageData = { + type: "message", + data: message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage("📤 Message sent", "info", message); + return true; + } catch (error) { + addMessage( + "❌ Failed to send message", + "error", + error instanceof Error ? error.message : String(error) + ); + return false; + } + }; + + const broadcastMessage = (message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot broadcast message: not connected", "error"); + return false; + } + + try { + const messageData = { + type: "broadcast", + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage("📡 Message broadcasted", "info", message); + return true; + } catch (error) { + addMessage( + "❌ Failed to broadcast message", + "error", + error instanceof Error ? error.message : String(error) + ); + return false; + } + }; + + const sendDirectMessage = (clientId: string, message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send direct message: not connected", "error"); + return false; + } + + try { + const messageData = { + type: "direct_message", + clientId, + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage(`📨 Direct message sent to ${clientId}`, "info", message); + return true; + } catch (error) { + addMessage( + "❌ Failed to send direct message", + "error", + error instanceof Error ? error.message : String(error) + ); + return false; + } + }; + + const sendRoomMessage = (room: string, message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send room message: not connected", "error"); + return false; + } + + try { + const messageData = { + type: "room_message", + room, + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage(`📢 Room message sent to ${room}`, "info", message); + return true; + } catch (error) { + addMessage( + "❌ Failed to send room message", + "error", + error instanceof Error ? error.message : String(error) + ); + return false; + } + }; + + // Admin and utility methods + const executeAdminCommand = async (command: string, params: any) => { + if (!isBrowser || !ws.value) throw new Error("Not connected"); + + const message = { + type: "admin_command", + command, + params, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + return { success: true, message: "Command sent successfully" }; + }; + + const getServerInfo = async () => { + if (!isBrowser || !ws.value) throw new Error("Not connected"); + + const message = { + type: "get_server_info", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + }; + + const getSystemHealth = async () => { + if (!isBrowser || !ws.value) throw new Error("Not connected"); + + const message = { + type: "get_system_health", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + }; + + const getOnlineUsers = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot get online users: not connected", "error"); + return; + } + + try { + const message = { + type: "get_online_users", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + } catch (error) { + addMessage( + "❌ Failed to request online users", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const testConnection = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot test connection: not connected", "error"); + return; + } + + try { + const message = { + type: "ping", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + addMessage("🏓 Connection test sent", "info"); + } catch (error) { + addMessage( + "❌ Failed to test connection", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const sendHeartbeat = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send heartbeat: not connected", "error"); + return; + } + + try { + const message = { + type: "ping", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + lastHeartbeatTime = Date.now(); + } catch (error) { + addMessage( + "❌ Failed to send heartbeat", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const executeDatabaseQuery = async (query: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + throw new Error("Not connected"); + } + + try { + const message = { + type: "database_query", + query, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + return { success: true, message: "Database query sent successfully" }; + } catch (error) { + throw new Error( + `Failed to execute database query: ${ + error instanceof Error ? error.message : String(error) + }` + ); + } + }; + + const triggerNotification = async (message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + throw new Error("Not connected"); + } + + try { + const notificationData = { + type: "notification", + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(notificationData)); + return { success: true, message: "Notification sent successfully" }; + } catch (error) { + throw new Error( + `Failed to trigger notification: ${ + error instanceof Error ? error.message : String(error) + }` + ); + } + }; + + const getStats = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot get stats: not connected", "error"); + return; + } + + try { + const message = { + type: "get_stats", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + } catch (error) { + addMessage( + "❌ Failed to request stats", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const getMonitoringData = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot get monitoring data: not connected", "error"); + return; + } + + try { + const message = { + type: "get_monitoring", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + } catch (error) { + addMessage( + "❌ Failed to request monitoring data", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + // Utility methods + const clearMessages = () => { + if (!isBrowser) return; + messages.value = []; + }; + + const clearActivityLog = () => { + if (!isBrowser) return; + activityLog.value = []; + }; + + const getMessagesByType = (type: string) => { + return messages.value.filter((msg) => msg.type === type); + }; + + const getRecentMessages = (count: number = 10) => { + return messages.value.slice(0, count); + }; + + // Cleanup on unmount + const cleanup = () => { + if (!isBrowser) return; + + disconnect(); + cleanupTimers(); + }; + + return { + // State + ws, + connectionState, + config, + messages, + stats, + monitoringData, + onlineUsers, + activityLog, + + // Admin state + serverInfo, + systemHealth, + + // Methods + connect, + disconnect, + sendMessage, + broadcastMessage, + sendDirectMessage, + sendRoomMessage, + getServerInfo, + getOnlineUsers, + testConnection, + sendHeartbeat, + executeDatabaseQuery, + triggerNotification, + getStats, + getMonitoringData, + executeAdminCommand, + getSystemHealth, + clearMessages, + clearActivityLog, + getMessagesByType, + getRecentMessages, + cleanup, + + // Computed + isMessageLimitReached, + shouldShowMessageWarning, + connectionHealthColor, + connectionHealthText, + }; +}; diff --git a/examples/clientsocket/composables/useWebSocket.ts b/examples/clientsocket/composables/useWebSocket.ts new file mode 100644 index 0000000..7478c16 --- /dev/null +++ b/examples/clientsocket/composables/useWebSocket.ts @@ -0,0 +1,1073 @@ +import { ref, computed, reactive, nextTick } from "vue"; +import type { + WebSocketMessage, + ConnectionState, + WebSocketConfig, + MessageHistory, + ConnectionStats, + MonitoringData, + ClientInfo, + OnlineUser, + ActivityLog, +} from "../types/websocket"; + +export const useWebSocket = () => { + // Check if we're in browser environment + const isBrowser = process.client; + + const ws = ref(null); + const isConnected = ref(false); + const isConnecting = ref(false); + const connectionStatus = ref< + "disconnected" | "connecting" | "connected" | "error" + >("disconnected"); + + const connectionState = reactive({ + isConnected: false, + isConnecting: false, + connectionStatus: "disconnected", + clientId: null, + staticId: null, + currentRoom: null, + userId: "anonymous", + ipAddress: null, + connectionStartTime: null, + lastPingTime: null, + connectionLatency: 0, + connectionHealth: "poor", + reconnectAttempts: 0, + messagesReceived: 0, + messagesSent: 0, + uptime: "00:00:00", + }); + + const config = reactive({ + wsUrl: "ws://localhost:8080/api/v1/ws", + userId: "anonymous", + room: "default", + staticId: "", + useIPBasedId: false, + autoReconnect: true, + heartbeatEnabled: true, + maxReconnectAttempts: 10, + reconnectDelay: 1000, + maxReconnectDelay: 30000, + heartbeatInterval: 30000, + heartbeatTimeout: 5000, + maxMissedHeartbeats: 3, + maxMessages: 1000, + messageWarningThreshold: 800, + actionThrottle: 100, + }); + + const messages = ref([]); + const stats = ref(null); + const monitoringData = ref(null); + const onlineUsers = ref([]); + const activityLog = ref([]); + + let reconnectTimeout: number | null = null; + let heartbeatInterval: number | null = null; + let heartbeatTimeout: number | null = null; + let missedHeartbeats = 0; + let lastHeartbeatTime = 0; + let messageCount = 0; + let connectionStartTime: number | null = null; + let uptimeInterval: number | null = null; + let isManualDisconnect = false; + let reconnectAttempts = 0; + let connectionHealth: "excellent" | "good" | "warning" | "poor" = "poor"; + + const updateConnectionStatus = ( + status: "disconnected" | "connecting" | "connected" | "error" + ) => { + connectionStatus.value = status; + connectionState.connectionStatus = status; + + switch (status) { + case "connected": + isConnected.value = true; + isConnecting.value = false; + connectionState.isConnected = true; + connectionState.isConnecting = false; + connectionState.connectionHealth = "good"; + break; + case "connecting": + isConnected.value = false; + isConnecting.value = true; + connectionState.isConnected = false; + connectionState.isConnecting = true; + connectionState.connectionHealth = "warning"; + break; + case "disconnected": + isConnected.value = false; + isConnecting.value = false; + connectionState.isConnected = false; + connectionState.isConnecting = false; + connectionState.connectionHealth = "poor"; + break; + case "error": + isConnected.value = false; + isConnecting.value = false; + connectionState.isConnected = false; + connectionState.isConnecting = false; + connectionState.connectionHealth = "poor"; + break; + } + }; + + const updateUptime = () => { + if (connectionStartTime && isConnected.value) { + const now = Date.now(); + const uptime = now - connectionStartTime; + const seconds = Math.floor(uptime / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + + connectionState.uptime = `${hours.toString().padStart(2, "0")}:${( + minutes % 60 + ) + .toString() + .padStart(2, "0")}:${(seconds % 60).toString().padStart(2, "0")}`; + } else { + connectionState.uptime = "00:00:00"; + } + }; + + const startUptimeTimer = () => { + if (uptimeInterval) { + clearInterval(uptimeInterval); + } + uptimeInterval = window.setInterval(updateUptime, 1000); + }; + + const stopUptimeTimer = () => { + if (uptimeInterval) { + clearInterval(uptimeInterval); + uptimeInterval = null; + } + }; + + // Only run WebSocket logic in browser + if (isBrowser) { + // Initialize connection state + updateConnectionStatus("disconnected"); + } + + const addMessage = ( + type: string, + data: any, + messageId?: string, + icon?: string + ) => { + if (!isBrowser) return; + + const now = new Date(); + const timeString = now.toLocaleTimeString("en-US", { + hour12: true, + hour: "numeric", + minute: "2-digit", + second: "2-digit", + }); + + // Enhanced message formatting based on type + let formattedData = data; + let displayType = type; + + switch (type) { + case "connection_info": + displayType = "info"; + formattedData = { + client_id: data.clientId, + connected_at: data.connectedAt, + ip_address: data.ipAddress, + last_ping: data.lastPingTime || "N/A", + room: data.room || "default", + static_id: data.staticId || "none", + user_id: data.userId || "anonymous", + }; + break; + + case "heartbeat": + displayType = "info"; + formattedData = `❤️ Heartbeat started\nInterval: ${ + config.heartbeatInterval / 1000 + }s`; + break; + + case "pong": + displayType = "success"; + formattedData = `🏓 Pong received\nLatency: ${connectionState.connectionLatency}ms`; + break; + + case "message": + displayType = "info"; + formattedData = `📨 Message received\n${JSON.stringify(data, null, 2)}`; + break; + + case "broadcast": + displayType = "info"; + formattedData = `📡 Broadcast received\n${data}`; + break; + + case "direct_message": + displayType = "info"; + formattedData = `📨 Direct message from ${data.from}\n${data.message}`; + break; + + case "room_message": + displayType = "info"; + formattedData = `📢 Room message from ${data.room}\n${data.message}`; + break; + + case "stats": + displayType = "info"; + formattedData = `📊 Stats updated\n${JSON.stringify(data, null, 2)}`; + break; + + case "monitoring": + displayType = "info"; + formattedData = `📈 Monitoring data\n${JSON.stringify(data, null, 2)}`; + break; + + case "online_users": + displayType = "info"; + formattedData = `👥 Online users updated\nCount: ${ + data.users?.length || 0 + }`; + break; + + case "server_info": + displayType = "info"; + formattedData = `🔧 Server info\n${JSON.stringify(data, null, 2)}`; + break; + + case "system_health": + displayType = "info"; + formattedData = `💚 System health\n${JSON.stringify(data, null, 2)}`; + break; + + default: + if (typeof data === "string") { + formattedData = data; + } else { + formattedData = JSON.stringify(data, null, 2); + } + } + + const message: MessageHistory = { + timestamp: now, + type: displayType, + data: formattedData, + messageId, + size: JSON.stringify(data).length, + icon: icon || getIconForMessageType(displayType), + timeString: timeString, + }; + + messages.value.unshift(message); + messageCount++; + + // Keep only the last maxMessages + if (messages.value.length > config.maxMessages) { + messages.value = messages.value.slice(0, config.maxMessages); + } + + // Update connection state + connectionState.messagesReceived++; + }; + + const getIconForMessageType = (type: string): string => { + switch (type) { + case "success": + return "🟢"; + case "error": + return "❌"; + case "warning": + return "⚠️"; + case "info": + return "ℹ️"; + default: + return "📝"; + } + }; + + const connectionHealthColor = computed(() => { + switch (connectionState.connectionHealth) { + case "excellent": + return "#4CAF50"; + case "good": + return "#2196F3"; + case "warning": + return "#FFC107"; + case "poor": + return "#F44336"; + default: + return "#9E9E9E"; + } + }); + + const connectionHealthText = computed(() => { + switch (connectionState.connectionHealth) { + case "excellent": + return "Excellent"; + case "good": + return "Good"; + case "warning": + return "Warning"; + case "poor": + return "Poor"; + default: + return "Unknown"; + } + }); + + // Admin functionality + const serverInfo = ref(null); + const systemHealth = ref(null); + + const executeAdminCommand = async (command: string, params: any) => { + if (!isBrowser || !ws.value) throw new Error("Not connected"); + + const message = { + type: "admin_command", + command, + params, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + return { success: true, message: "Command sent successfully" }; + }; + + const getServerInfo = async () => { + if (!isBrowser || !ws.value) throw new Error("Not connected"); + + const message = { + type: "get_server_info", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + }; + + const getSystemHealth = async () => { + if (!isBrowser || !ws.value) throw new Error("Not connected"); + + const message = { + type: "get_system_health", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + }; + + // Cleanup on unmount + const cleanup = () => { + if (!isBrowser) return; + + disconnect(); + if (reconnectTimeout) { + clearTimeout(reconnectTimeout); + } + stopHeartbeat(); + }; + + // WebSocket connection methods (only available in browser) + const connect = () => { + if (!isBrowser) return; + + // Prevent multiple connection attempts + if (isConnecting.value || isConnected.value) { + return; + } + + // Clean up existing connection + if (ws.value) { + cleanupConnection(); + } + + updateConnectionStatus("connecting"); + isManualDisconnect = false; + connectionStartTime = Date.now(); + startUptimeTimer(); + + try { + // Build WebSocket URL with parameters + let url = config.wsUrl; + const params = new URLSearchParams(); + + if (config.userId) { + params.append("user_id", config.userId); + } + if (config.room) { + params.append("room", config.room); + } + if (config.useIPBasedId) { + params.append("ip_based", "true"); + } else if (config.staticId) { + params.append("static_id", config.staticId); + } + + if (params.toString()) { + url += "?" + params.toString(); + } + + ws.value = new WebSocket(url); + + // Connection timeout + const connectionTimeout = setTimeout(() => { + if (ws.value && ws.value.readyState === WebSocket.CONNECTING) { + ws.value.close(); + updateConnectionStatus("error"); + addMessage("❌ Connection timeout after 15 seconds", "error"); + scheduleReconnect(); + } + }, 15000); + + ws.value.onopen = function (event) { + clearTimeout(connectionTimeout); + updateConnectionStatus("connected"); + reconnectAttempts = 0; + connectionHealth = "good"; + connectionState.connectionStartTime = connectionStartTime; + + addMessage( + "🟢 Connected to WebSocket server", + "success", + "Connection established successfully" + ); + + // Start heartbeat if enabled + if (config.heartbeatEnabled) { + startHeartbeat(); + } + }; + + ws.value.onmessage = function (event) { + handleIncomingMessage(event); + }; + + ws.value.onclose = function (event) { + clearTimeout(connectionTimeout); + cleanupTimers(); + updateConnectionStatus("disconnected"); + + const reason = getCloseReason(event.code); + addMessage( + `🔴 Disconnected: ${reason}`, + "error", + `Code: ${event.code}, Reason: ${event.reason || "Unknown"}` + ); + + // Auto-reconnect logic + if (!isManualDisconnect && config.autoReconnect) { + scheduleReconnect(); + } + }; + + ws.value.onerror = function (error) { + updateConnectionStatus("error"); + connectionHealth = "poor"; + addMessage("❌ WebSocket Error occurred", "error", error.toString()); + }; + } catch (error) { + updateConnectionStatus("error"); + addMessage( + "❌ Failed to create WebSocket connection", + "error", + error instanceof Error ? error.message : String(error) + ); + scheduleReconnect(); + } + }; + + const disconnect = () => { + if (!isBrowser) return; + + isManualDisconnect = true; + cleanupConnection(); + updateConnectionStatus("disconnected"); + addMessage("🔴 Manually disconnected from WebSocket", "info"); + }; + + const sendMessage = (message: any) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send message: not connected", "error"); + return; + } + + try { + const messageData = { + type: "message", + data: message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage("📤 Message sent", "info", message); + } catch (error) { + addMessage( + "❌ Failed to send message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const broadcastMessage = (message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot broadcast message: not connected", "error"); + return; + } + + try { + const messageData = { + type: "broadcast", + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage("📡 Message broadcasted", "info", message); + } catch (error) { + addMessage( + "❌ Failed to broadcast message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const sendDirectMessage = (clientId: string, message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send direct message: not connected", "error"); + return; + } + + try { + const messageData = { + type: "direct_message", + clientId, + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage(`📨 Direct message sent to ${clientId}`, "info", message); + } catch (error) { + addMessage( + "❌ Failed to send direct message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const sendRoomMessage = (room: string, message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send room message: not connected", "error"); + return; + } + + try { + const messageData = { + type: "room_message", + room, + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(messageData)); + connectionState.messagesSent++; + addMessage(`📢 Room message sent to ${room}`, "info", message); + } catch (error) { + addMessage( + "❌ Failed to send room message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const getOnlineUsers = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot get online users: not connected", "error"); + return; + } + + try { + const message = { + type: "get_online_users", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + } catch (error) { + addMessage( + "❌ Failed to request online users", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const testConnection = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot test connection: not connected", "error"); + return; + } + + try { + const message = { + type: "ping", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + addMessage("🏓 Connection test sent", "info"); + } catch (error) { + addMessage( + "❌ Failed to test connection", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const sendHeartbeat = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot send heartbeat: not connected", "error"); + return; + } + + try { + const message = { + type: "ping", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + lastHeartbeatTime = Date.now(); + } catch (error) { + addMessage( + "❌ Failed to send heartbeat", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const executeDatabaseQuery = async (query: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + throw new Error("Not connected"); + } + + try { + const message = { + type: "database_query", + query, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + return { success: true, message: "Database query sent successfully" }; + } catch (error) { + throw new Error( + `Failed to execute database query: ${ + error instanceof Error ? error.message : String(error) + }` + ); + } + }; + + const triggerNotification = async (message: string) => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + throw new Error("Not connected"); + } + + try { + const notificationData = { + type: "notification", + message, + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(notificationData)); + return { success: true, message: "Notification sent successfully" }; + } catch (error) { + throw new Error( + `Failed to trigger notification: ${ + error instanceof Error ? error.message : String(error) + }` + ); + } + }; + + const getStats = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot get stats: not connected", "error"); + return; + } + + try { + const message = { + type: "get_stats", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + } catch (error) { + addMessage( + "❌ Failed to request stats", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const getMonitoringData = () => { + if (!isBrowser || !ws.value || ws.value.readyState !== WebSocket.OPEN) { + addMessage("❌ Cannot get monitoring data: not connected", "error"); + return; + } + + try { + const message = { + type: "get_monitoring", + timestamp: Date.now(), + }; + + ws.value.send(JSON.stringify(message)); + } catch (error) { + addMessage( + "❌ Failed to request monitoring data", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const clearMessages = () => { + if (!isBrowser) return; + messages.value = []; + messageCount = 0; + }; + + const clearActivityLog = () => { + if (!isBrowser) return; + activityLog.value = []; + }; + + const getMessagesByType = (type: string) => { + return messages.value.filter((msg) => msg.type === type); + }; + + const getRecentMessages = (count: number = 10) => { + return messages.value.slice(0, count); + }; + + const stopHeartbeat = () => { + if (!isBrowser) return; + + if (heartbeatInterval) { + clearInterval(heartbeatInterval); + heartbeatInterval = null; + } + if (heartbeatTimeout) { + clearTimeout(heartbeatTimeout); + heartbeatTimeout = null; + } + missedHeartbeats = 0; + }; + + const cleanupConnection = () => { + if (!isBrowser) return; + + if (ws.value) { + ws.value.close(); + ws.value = null; + } + cleanupTimers(); + }; + + const cleanupTimers = () => { + if (!isBrowser) return; + + if (reconnectTimeout) { + clearTimeout(reconnectTimeout); + reconnectTimeout = null; + } + stopHeartbeat(); + stopUptimeTimer(); + }; + + const scheduleReconnect = () => { + if (!isBrowser || !config.autoReconnect) return; + + if (reconnectAttempts >= config.maxReconnectAttempts) { + addMessage("❌ Max reconnection attempts reached", "error"); + return; + } + + reconnectAttempts++; + const delay = Math.min( + config.reconnectDelay * Math.pow(2, reconnectAttempts - 1), + config.maxReconnectDelay + ); + + addMessage( + `🔄 Reconnecting in ${delay}ms (attempt ${reconnectAttempts}/${config.maxReconnectAttempts})`, + "info" + ); + + reconnectTimeout = window.setTimeout(() => { + connect(); + }, delay); + }; + + const startHeartbeat = () => { + if (!isBrowser || !config.heartbeatEnabled) return; + + stopHeartbeat(); + + heartbeatInterval = window.setInterval(() => { + if (ws.value && ws.value.readyState === WebSocket.OPEN) { + const heartbeatMessage = { + type: "ping", + timestamp: Date.now(), + }; + ws.value.send(JSON.stringify(heartbeatMessage)); + lastHeartbeatTime = Date.now(); + + // Set timeout for heartbeat response + heartbeatTimeout = window.setTimeout(() => { + missedHeartbeats++; + if (missedHeartbeats >= config.maxMissedHeartbeats) { + addMessage( + "❌ Heartbeat timeout - connection unhealthy", + "warning" + ); + connectionHealth = "warning"; + } + }, config.heartbeatTimeout); + } + }, config.heartbeatInterval); + }; + + const handleIncomingMessage = (event: MessageEvent) => { + if (!isBrowser) return; + + try { + const data = JSON.parse(event.data); + + // Handle heartbeat response + if (data.type === "pong") { + missedHeartbeats = 0; + connectionHealth = "excellent"; + const latency = Date.now() - lastHeartbeatTime; + connectionState.connectionLatency = latency; + return; + } + + // Handle connection info + if (data.type === "connection_info") { + connectionState.clientId = data.clientId; + connectionState.staticId = data.staticId; + connectionState.currentRoom = data.room; + connectionState.userId = data.userId; + connectionState.ipAddress = data.ipAddress; + return; + } + + // Handle welcome message (server sends connection info in welcome message) + if (data.type === "welcome") { + console.log("Received welcome message:", data); + + // Map server snake_case fields to camelCase for Vue components + if (data.client_id) { + connectionState.clientId = data.client_id; + console.log("Set clientId:", data.client_id); + } + if (data.static_id) { + connectionState.staticId = data.static_id; + console.log("Set staticId:", data.static_id); + } + if (data.room) { + connectionState.currentRoom = data.room; + console.log("Set currentRoom:", data.room); + } + if (data.user_id) { + connectionState.userId = data.user_id; + console.log("Set userId:", data.user_id); + } + if (data.ip_address) { + connectionState.ipAddress = data.ip_address; + console.log("Set ipAddress:", data.ip_address); + } + if (data.connected_at) { + connectionState.connectionStartTime = data.connected_at * 1000; + console.log("Set connectionStartTime:", data.connected_at * 1000); + } + + // Force reactive update by triggering a change + connectionState.connectionHealth = + connectionState.connectionHealth === "good" ? "excellent" : "good"; + + console.log("Updated connectionState:", connectionState); + addMessage( + "🟢 Connection established successfully", + "success", + "Welcome message processed" + ); + return; + } + + // Handle stats + if (data.type === "stats") { + stats.value = data; + return; + } + + // Handle monitoring data + if (data.type === "monitoring") { + monitoringData.value = data; + return; + } + + // Handle online users + if (data.type === "online_users") { + onlineUsers.value = data.users; + return; + } + + // Handle activity log + if (data.type === "activity") { + activityLog.value.unshift(data); + if (activityLog.value.length > 100) { + activityLog.value = activityLog.value.slice(0, 100); + } + return; + } + + // Handle server info + if (data.type === "server_info") { + serverInfo.value = data; + return; + } + + // Handle system health + if (data.type === "system_health") { + systemHealth.value = data; + return; + } + + // Handle regular messages + addMessage(data.type || "message", data, data.messageId); + } catch (error) { + addMessage( + "❌ Failed to parse message", + "error", + error instanceof Error ? error.message : String(error) + ); + } + }; + + const getCloseReason = (code: number): string => { + switch (code) { + case 1000: + return "Normal closure"; + case 1001: + return "Going away"; + case 1002: + return "Protocol error"; + case 1003: + return "Unsupported data"; + case 1004: + return "Reserved"; + case 1005: + return "No status received"; + case 1006: + return "Abnormal closure"; + case 1007: + return "Invalid frame payload data"; + case 1008: + return "Policy violation"; + case 1009: + return "Message too big"; + case 1010: + return "Mandatory extension"; + case 1011: + return "Internal server error"; + case 1012: + return "Service restart"; + case 1013: + return "Try again later"; + case 1014: + return "Bad gateway"; + case 1015: + return "TLS handshake"; + default: + return "Unknown reason"; + } + }; + + const isMessageLimitReached = computed(() => { + return messages.value.length >= config.maxMessages; + }); + + const shouldShowMessageWarning = computed(() => { + return messages.value.length >= config.messageWarningThreshold; + }); + + return { + // State + ws, + isConnected, + isConnecting, + connectionStatus, + connectionState, + config, + messages, + stats, + monitoringData, + onlineUsers, + activityLog, + + // Admin state + serverInfo, + systemHealth, + + // Methods + connect, + disconnect, + sendMessage, + broadcastMessage, + sendDirectMessage, + sendRoomMessage, + getServerInfo, + getOnlineUsers, + testConnection, + sendHeartbeat, + executeDatabaseQuery, + triggerNotification, + getStats, + getMonitoringData, + executeAdminCommand, + getSystemHealth, + clearMessages, + clearActivityLog, + getMessagesByType, + getRecentMessages, + cleanup, + + // Computed + isMessageLimitReached, + shouldShowMessageWarning, + connectionHealthColor, + connectionHealthText, + }; +}; diff --git a/examples/clientsocket/composables/useWebSocket.ts.backup b/examples/clientsocket/composables/useWebSocket.ts.backup new file mode 100644 index 0000000..e90e363 --- /dev/null +++ b/examples/clientsocket/composables/useWebSocket.ts.backup @@ -0,0 +1,318 @@ +import { ref, computed, reactive, nextTick } from 'vue' +import type { + WebSocketMessage, + ConnectionState, + WebSocketConfig, + MessageHistory, + ConnectionStats, + MonitoringData, + ClientInfo, + OnlineUser, + ActivityLog +} from '../types/websocket' + +export const useWebSocket = () => { + // Check if we're in browser environment + const isBrowser = process.client + + const ws = ref(null) + const isConnected = ref(false) + const isConnecting = ref(false) + const connectionStatus = ref<'disconnected' | 'connecting' | 'connected' | 'error'>('disconnected') + + const connectionState = reactive({ + isConnected: false, + isConnecting: false, + connectionStatus: 'disconnected', + clientId: null, + staticId: null, + currentRoom: null, + userId: 'anonymous', + ipAddress: null, + connectionStartTime: null, + lastPingTime: null, + connectionLatency: 0, + connectionHealth: 'poor', + reconnectAttempts: 0, + messagesReceived: 0, + messagesSent: 0, + uptime: '00:00:00' + }) + + const config = reactive({ + wsUrl: 'ws://localhost:8080/api/v1/ws', + userId: 'anonymous', + room: 'default', + staticId: '', + useIPBasedId: false, + autoReconnect: true, + heartbeatEnabled: true, + maxReconnectAttempts: 10, + reconnectDelay: 1000, + maxReconnectDelay: 30000, + heartbeatInterval: 30000, + heartbeatTimeout: 5000, + maxMissedHeartbeats: 3, + maxMessages: 1000, + messageWarningThreshold: 800, + actionThrottle: 100 + }) + + const messages = ref([]) + const stats = ref(null) + const monitoringData = ref(null) + const onlineUsers = ref([]) + const activityLog = ref([]) + + let reconnectTimeout: number | null = null + let heartbeatInterval: number | null = null + let heartbeatTimeout: number | null = null + let missedHeartbeats = 0 + let lastHeartbeatTime = 0 + let messageCount = 0 + + // Only run WebSocket logic in browser + if (isBrowser) { + // WebSocket connection logic here + } + + const addMessage = (type: string, data: any, messageId?: string) => { + if (!isBrowser) return + + const message: MessageHistory = { + timestamp: new Date(), + type, + data, + messageId, + size: JSON.stringify(data).length + } + + messages.value.unshift(message) + messageCount++ + + // Keep only the last maxMessages + if (messages.value.length > config.maxMessages) { + messages.value = messages.value.slice(0, config.maxMessages) + } + + // Update connection state + connectionState.messagesReceived++ + } + + const connectionHealthColor = computed(() => { + switch (connectionState.connectionHealth) { + case 'excellent': return '#4CAF50' + case 'good': return '#2196F3' + case 'warning': return '#FFC107' + case 'poor': return '#F44336' + default: return '#9E9E9E' + } + }) + + const connectionHealthText = computed(() => { + switch (connectionState.connectionHealth) { + case 'excellent': return 'Excellent' + case 'good': return 'Good' + case 'warning': return 'Warning' + case 'poor': return 'Poor' + default: return 'Unknown' + } + }) + + // Admin functionality + const serverInfo = ref(null) + const systemHealth = ref(null) + + const executeAdminCommand = async (command: string, params: any) => { + if (!isBrowser || !ws.value) throw new Error('Not connected') + + const message = { + type: 'admin_command', + command, + params, + timestamp: Date.now() + } + + ws.value.send(JSON.stringify(message)) + return { success: true, message: 'Command sent successfully' } + } + + const getServerInfo = async () => { + if (!isBrowser || !ws.value) throw new Error('Not connected') + + const message = { + type: 'get_server_info', + timestamp: Date.now() + } + + ws.value.send(JSON.stringify(message)) + } + + const getSystemHealth = async () => { + if (!isBrowser || !ws.value) throw new Error('Not connected') + + const message = { + type: 'get_system_health', + timestamp: Date.now() + } + + ws.value.send(JSON.stringify(message)) + } + + // Cleanup on unmount + const cleanup = () => { + if (!isBrowser) return + + disconnect() + if (reconnectTimeout) { + clearTimeout(reconnectTimeout) + } + stopHeartbeat() + } + + // WebSocket connection methods (only available in browser) + const connect = () => { + if (!isBrowser) return + // WebSocket connection logic + } + + const disconnect = () => { + if (!isBrowser) return + // WebSocket disconnection logic + } + + const sendMessage = (message: any) => { + if (!isBrowser || !ws.value) return + // Send message logic + } + + const broadcastMessage = (message: string) => { + if (!isBrowser || !ws.value) return + // Broadcast message logic + } + + const sendDirectMessage = (clientId: string, message: string) => { + if (!isBrowser || !ws.value) return + // Direct message logic + } + + const sendRoomMessage = (room: string, message: string) => { + if (!isBrowser || !ws.value) return + // Room message logic + } + + const getOnlineUsers = () => { + if (!isBrowser || !ws.value) return + // Get online users logic + } + + const testConnection = () => { + if (!isBrowser || !ws.value) return + // Test connection logic + } + + const sendHeartbeat = () => { + if (!isBrowser || !ws.value) return + // Send heartbeat logic + } + + const executeDatabaseQuery = async (query: string) => { + if (!isBrowser || !ws.value) return + // Database query logic + } + + const triggerNotification = async (message: string) => { + if (!isBrowser || !ws.value) return + // Notification logic + } + + const getStats = () => { + if (!isBrowser || !ws.value) return + // Get stats logic + } + + const getMonitoringData = () => { + if (!isBrowser || !ws.value) return + // Get monitoring data logic + } + + const clearMessages = () => { + if (!isBrowser) return + messages.value = [] + messageCount = 0 + } + + const clearActivityLog = () => { + if (!isBrowser) return + activityLog.value = [] + } + + const getMessagesByType = (type: string) => { + return messages.value.filter(msg => msg.type === type) + } + + const getRecentMessages = (count: number = 10) => { + return messages.value.slice(0, count) + } + + const stopHeartbeat = () => { + if (!isBrowser) return + // Stop heartbeat logic + } + + const isMessageLimitReached = computed(() => { + return messages.value.length >= config.maxMessages + }) + + const shouldShowMessageWarning = computed(() => { + return messages.value.length >= config.messageWarningThreshold + }) + + return { + // State + ws, + isConnected, + isConnecting, + connectionStatus, + connectionState, + config, + messages, + stats, + monitoringData, + onlineUsers, + activityLog, + + // Admin state + serverInfo, + systemHealth, + + // Methods + connect, + disconnect, + sendMessage, + broadcastMessage, + sendDirectMessage, + sendRoomMessage, + getServerInfo, + getOnlineUsers, + testConnection, + sendHeartbeat, + executeDatabaseQuery, + triggerNotification, + getStats, + getMonitoringData, + executeAdminCommand, + getSystemHealth, + clearMessages, + clearActivityLog, + getMessagesByType, + getRecentMessages, + cleanup, + + // Computed + isMessageLimitReached, + shouldShowMessageWarning, + connectionHealthColor, + connectionHealthText + } +} diff --git a/examples/clientsocket/nuxt.config.ts b/examples/clientsocket/nuxt.config.ts new file mode 100644 index 0000000..dd716db --- /dev/null +++ b/examples/clientsocket/nuxt.config.ts @@ -0,0 +1,22 @@ +export default defineNuxtConfig({ + devtools: { enabled: true }, + modules: [], + css: ['~/assets/css/main.css', 'vuetify/styles'], + build: { + transpile: ['vuetify'] + }, + runtimeConfig: { + public: { + wsUrl: 'ws://localhost:8080/api/v1/ws' + } + }, + typescript: { + typeCheck: false + }, + vite: { + define: { + global: 'globalThis' + } + }, + compatibilityDate: '2024-04-03' +}) diff --git a/examples/clientsocket/package-lock.json b/examples/clientsocket/package-lock.json new file mode 100644 index 0000000..2723d6d --- /dev/null +++ b/examples/clientsocket/package-lock.json @@ -0,0 +1,11504 @@ +{ + "name": "nuxt3-websocket-client", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "nuxt3-websocket-client", + "hasInstallScript": true, + "dependencies": { + "@mdi/font": "^7.3.67", + "@nuxtjs/vuetify": "^1.12.3", + "highlight.js": "^11.9.0", + "pinia": "^2.1.7", + "vue3-highlightjs": "^1.0.5", + "vuetify": "^3.4.0" + }, + "devDependencies": { + "@nuxt/devtools": "latest", + "nuxt": "^3.8.0", + "vue": "^3.3.8", + "vue-router": "^4.2.5", + "vue-tsc": "^3.0.8" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", + "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", + "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.4", + "@babel/types": "^7.28.4", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.3.tgz", + "integrity": "sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.28.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", + "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.4" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typescript": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.0.tgz", + "integrity": "sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", + "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", + "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@cloudflare/kv-asset-handler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.0.tgz", + "integrity": "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "mime": "^3.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@cloudflare/kv-asset-handler/node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", + "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", + "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.10.tgz", + "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", + "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.10.tgz", + "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", + "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", + "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", + "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", + "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", + "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", + "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", + "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", + "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", + "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", + "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", + "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", + "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", + "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", + "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", + "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", + "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", + "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", + "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", + "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", + "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", + "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", + "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@ioredis/commands": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.4.0.tgz", + "integrity": "sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-2.0.0.tgz", + "integrity": "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "consola": "^3.2.3", + "detect-libc": "^2.0.0", + "https-proxy-agent": "^7.0.5", + "node-fetch": "^2.6.7", + "nopt": "^8.0.0", + "semver": "^7.5.3", + "tar": "^7.4.0" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/detect-libc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/@mdi/font": { + "version": "7.4.47", + "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz", + "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==", + "license": "Apache-2.0" + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.5.tgz", + "integrity": "sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@tybys/wasm-util": "^0.10.1" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nuxt/cli": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/@nuxt/cli/-/cli-3.28.0.tgz", + "integrity": "sha512-WQ751WxWLBIeH3TDFt/LWQ2znyAKxpR5+gpv80oerwnVQs4GKajAfR6dIgExXZkjaPUHEFv2lVD9vM+frbprzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "c12": "^3.2.0", + "citty": "^0.1.6", + "clipboardy": "^4.0.0", + "confbox": "^0.2.2", + "consola": "^3.4.2", + "defu": "^6.1.4", + "exsolve": "^1.0.7", + "fuse.js": "^7.1.0", + "get-port-please": "^3.2.0", + "giget": "^2.0.0", + "h3": "^1.15.4", + "httpxy": "^0.1.7", + "jiti": "^2.5.1", + "listhen": "^1.9.0", + "nypm": "^0.6.1", + "ofetch": "^1.4.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "scule": "^1.3.0", + "semver": "^7.7.2", + "std-env": "^3.9.0", + "tinyexec": "^1.0.1", + "ufo": "^1.6.1", + "youch": "^4.1.0-beta.11" + }, + "bin": { + "nuxi": "bin/nuxi.mjs", + "nuxi-ng": "bin/nuxi.mjs", + "nuxt": "bin/nuxi.mjs", + "nuxt-cli": "bin/nuxi.mjs" + }, + "engines": { + "node": "^16.10.0 || >=18.0.0" + } + }, + "node_modules/@nuxt/cli/node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nuxt/devalue": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@nuxt/devalue/-/devalue-2.0.2.tgz", + "integrity": "sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nuxt/devtools": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@nuxt/devtools/-/devtools-2.6.5.tgz", + "integrity": "sha512-Xh9XF1SzCTL5Zj6EULqsN2UjiNj4zWuUpS69rGAy5C55UTaj+Wn46IkDc6Q0+EKkGI279zlG6SzPRFawqPPUEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nuxt/devtools-kit": "2.6.5", + "@nuxt/devtools-wizard": "2.6.5", + "@nuxt/kit": "^3.19.2", + "@vue/devtools-core": "^7.7.7", + "@vue/devtools-kit": "^7.7.7", + "birpc": "^2.5.0", + "consola": "^3.4.2", + "destr": "^2.0.5", + "error-stack-parser-es": "^1.0.5", + "execa": "^8.0.1", + "fast-npm-meta": "^0.4.6", + "get-port-please": "^3.2.0", + "hookable": "^5.5.3", + "image-meta": "^0.2.1", + "is-installed-globally": "^1.0.0", + "launch-editor": "^2.11.1", + "local-pkg": "^1.1.2", + "magicast": "^0.3.5", + "nypm": "^0.6.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.3.0", + "semver": "^7.7.2", + "simple-git": "^3.28.0", + "sirv": "^3.0.2", + "structured-clone-es": "^1.0.0", + "tinyglobby": "^0.2.15", + "vite-plugin-inspect": "^11.3.3", + "vite-plugin-vue-tracer": "^1.0.0", + "which": "^5.0.0", + "ws": "^8.18.3" + }, + "bin": { + "devtools": "cli.mjs" + }, + "peerDependencies": { + "vite": ">=6.0" + } + }, + "node_modules/@nuxt/devtools-kit": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@nuxt/devtools-kit/-/devtools-kit-2.6.5.tgz", + "integrity": "sha512-t+NxoENyzJ8KZDrnbVYv3FJI5VXqSi6X4w6ZsuIIh0aKABu6+6k9nR/LoEhrM0oekn/2LDhA0NmsRZyzCXt2xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nuxt/kit": "^3.19.2", + "execa": "^8.0.1" + }, + "peerDependencies": { + "vite": ">=6.0" + } + }, + "node_modules/@nuxt/devtools-wizard": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@nuxt/devtools-wizard/-/devtools-wizard-2.6.5.tgz", + "integrity": "sha512-nYYGxT4lmQDvfHL6qolNWLu0QTavsdN/98F57falPuvdgs5ev1NuYsC12hXun+5ENcnigEcoM9Ij92qopBgqmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^3.4.2", + "diff": "^8.0.2", + "execa": "^8.0.1", + "magicast": "^0.3.5", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "prompts": "^2.4.2", + "semver": "^7.7.2" + }, + "bin": { + "devtools-wizard": "cli.mjs" + } + }, + "node_modules/@nuxt/devtools/node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nuxt/kit": { + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-3.19.2.tgz", + "integrity": "sha512-+QiqO0WcIxsKLUqXdVn3m4rzTRm2fO9MZgd330utCAaagGmHsgiMJp67kE14boJEPutnikfz3qOmrzBnDIHUUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "c12": "^3.2.0", + "consola": "^3.4.2", + "defu": "^6.1.4", + "destr": "^2.0.5", + "errx": "^0.1.0", + "exsolve": "^1.0.7", + "ignore": "^7.0.5", + "jiti": "^2.5.1", + "klona": "^2.0.6", + "knitwork": "^1.2.0", + "mlly": "^1.8.0", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "rc9": "^2.1.2", + "scule": "^1.3.0", + "semver": "^7.7.2", + "std-env": "^3.9.0", + "tinyglobby": "^0.2.15", + "ufo": "^1.6.1", + "unctx": "^2.4.1", + "unimport": "^5.2.0", + "untyped": "^2.0.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/@nuxt/telemetry": { + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/@nuxt/telemetry/-/telemetry-2.6.6.tgz", + "integrity": "sha512-Zh4HJLjzvm3Cq9w6sfzIFyH9ozK5ePYVfCUzzUQNiZojFsI2k1QkSBrVI9BGc6ArKXj/O6rkI6w7qQ+ouL8Cag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nuxt/kit": "^3.15.4", + "citty": "^0.1.6", + "consola": "^3.4.2", + "destr": "^2.0.3", + "dotenv": "^16.4.7", + "git-url-parse": "^16.0.1", + "is-docker": "^3.0.0", + "ofetch": "^1.4.1", + "package-manager-detector": "^1.1.0", + "pathe": "^2.0.3", + "rc9": "^2.1.2", + "std-env": "^3.8.1" + }, + "bin": { + "nuxt-telemetry": "bin/nuxt-telemetry.mjs" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/@nuxt/vite-builder": { + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/@nuxt/vite-builder/-/vite-builder-3.19.2.tgz", + "integrity": "sha512-SESdHAKWy63RKG3uqrBEJvTbfkmEsiggmDEqjEwhBP2fe0E6bGTmLpX/Eh4AuOLbZuZOmir984OHFiM/Q/MLhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nuxt/kit": "3.19.2", + "@rollup/plugin-replace": "^6.0.2", + "@vitejs/plugin-vue": "^6.0.1", + "@vitejs/plugin-vue-jsx": "^5.1.1", + "autoprefixer": "^10.4.21", + "consola": "^3.4.2", + "cssnano": "^7.1.1", + "defu": "^6.1.4", + "esbuild": "^0.25.9", + "escape-string-regexp": "^5.0.0", + "exsolve": "^1.0.7", + "externality": "^1.0.2", + "get-port-please": "^3.2.0", + "h3": "^1.15.4", + "jiti": "^2.5.1", + "knitwork": "^1.2.0", + "magic-string": "^0.30.19", + "mlly": "^1.8.0", + "mocked-exports": "^0.1.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.0.0", + "pkg-types": "^2.3.0", + "postcss": "^8.5.6", + "rollup-plugin-visualizer": "^6.0.3", + "std-env": "^3.9.0", + "ufo": "^1.6.1", + "unenv": "^2.0.0-rc.21", + "vite": "^7.1.5", + "vite-node": "^3.2.4", + "vite-plugin-checker": "^0.10.3", + "vue-bundle-renderer": "^2.1.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vue": "^3.3.4" + } + }, + "node_modules/@nuxtjs/vuetify": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/@nuxtjs/vuetify/-/vuetify-1.12.3.tgz", + "integrity": "sha512-6uVL3cfESMB00eVjJTNkyU4jvuPTGPn1yteo7lQTH6v+fxHcPaOgvzVYHIKSHIz1DecuOiB5c9b+YjsRP5+C8A==", + "license": "MIT", + "dependencies": { + "deepmerge": "^4.2.2", + "sass": "~1.32.13", + "sass-loader": "^10.2.0", + "vuetify": "^2.6", + "vuetify-loader": "^1.7.3" + } + }, + "node_modules/@nuxtjs/vuetify/node_modules/@vue/compiler-sfc": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz", + "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==", + "peer": true, + "dependencies": { + "@babel/parser": "^7.23.5", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, + "node_modules/@nuxtjs/vuetify/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/@nuxtjs/vuetify/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@nuxtjs/vuetify/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/@nuxtjs/vuetify/node_modules/sass": { + "version": "1.32.13", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.13.tgz", + "integrity": "sha512-dEgI9nShraqP7cXQH+lEXVf73WOPCse0QlFzSD8k+1TcOxCMwVXfQlr0jtoluZysQOyJGnfr21dLvYKDJq8HkA==", + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/@nuxtjs/vuetify/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@nuxtjs/vuetify/node_modules/vue": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", + "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", + "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", + "license": "MIT", + "peer": true, + "dependencies": { + "@vue/compiler-sfc": "2.7.16", + "csstype": "^3.1.0" + } + }, + "node_modules/@nuxtjs/vuetify/node_modules/vuetify": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.6.13.tgz", + "integrity": "sha512-HhJi52IzhfrmWFwcYFUiA1GRIzz9smbR06Lj61Ml5HgD5PBcMiDywUnNPVid1VsXO4qWpBU6kO3O89uTxH1yzw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/johnleider" + }, + "peerDependencies": { + "vue": "^2.6.4" + } + }, + "node_modules/@nuxtjs/vuetify/node_modules/vuetify-loader": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/vuetify-loader/-/vuetify-loader-1.9.2.tgz", + "integrity": "sha512-8PP2w7aAs/rjA+Izec6qY7sHVb75MNrGQrDOTZJ5IEnvl+NiFhVpU2iWdRDZ3eMS842cWxSWStvkr+KJJKy+Iw==", + "license": "MIT", + "dependencies": { + "acorn": "^8.4.1", + "acorn-walk": "^8.2.0", + "decache": "^4.6.0", + "file-loader": "^6.2.0", + "loader-utils": "^2.0.0" + }, + "peerDependencies": { + "gm": "^1.23.0", + "pug": "^2.0.0 || ^3.0.0", + "sharp": "*", + "vue": "^2.7.2", + "vuetify": "^1.3.0 || ^2.0.0", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "gm": { + "optional": true + }, + "pug": { + "optional": true + }, + "sharp": { + "optional": true + } + } + }, + "node_modules/@oxc-minify/binding-android-arm64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-android-arm64/-/binding-android-arm64-0.87.0.tgz", + "integrity": "sha512-ZbJmAfXvNAamOSnXId3BiM3DiuzlD1isqKjtmRFb/hpvChHHA23FSPrFcO16w+ugZKg33sZ93FinFkKtlC4hww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-darwin-arm64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-darwin-arm64/-/binding-darwin-arm64-0.87.0.tgz", + "integrity": "sha512-ewmNsTY8YbjWOI8+EOWKTVATOYvG4Qq4zQHH5VFBeqhQPVusY1ORD6Ei+BijVKrnlbpjibLlkTl8IWqXCGK89A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-darwin-x64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-darwin-x64/-/binding-darwin-x64-0.87.0.tgz", + "integrity": "sha512-qDH4w4EYttSC3Cs2VCh+CiMYKrcL2SNmnguBZXoUXe/RNk3csM+RhgcwdpX687xGvOhTFhH5PCIA84qh3ZpIbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-freebsd-x64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-freebsd-x64/-/binding-freebsd-x64-0.87.0.tgz", + "integrity": "sha512-5kxjHlSev2A09rDeITk+LMHxSrU3Iu8pUb0Zp4m+ul8FKlB9FrvFkAYwbctin6g47O98s3Win7Ewhy0w8JaiUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm-gnueabihf": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.87.0.tgz", + "integrity": "sha512-NjbGXnNaAl5EgyonaDg2cPyH2pTf5a/+AP/5SRCJ0KetpXV22ZSUCvcy04Yt4QqjMcDs+WnJaGVxwx15Ofr6Gw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm-musleabihf": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.87.0.tgz", + "integrity": "sha512-llAjfCA0iV2LMMl+LTR3JhqAc2iQmj+DTKd0VWOrbNOuNczeE9D5kJFkqYplD73LrkuqxrX9oDeUjjeLdVBPXw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm64-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.87.0.tgz", + "integrity": "sha512-tf2Shom09AaSmu7U1hYYcEFF/cd+20HtmQ8eyGsRkqD5bqUj6lDu8TNSU9FWZ9tcZ83NzyFMwXZWHyeeIIbpxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm64-musl": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.87.0.tgz", + "integrity": "sha512-pgWeYfSprtpnJVea9Q5eI6Eo80lDGlMw2JdcSMXmShtBjEhBl6bvDNHlV+6kNfh7iT65y/uC6FR8utFrRghu8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-linux-riscv64-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.87.0.tgz", + "integrity": "sha512-O1QPczlT+lqNZVeKOdFxxL+s1RIlnixaJYFLrcqDcRyn82MGKLz7sAenBTFRQoIfLnSxtMGL6dqHOefYkQx7Cg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-linux-s390x-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.87.0.tgz", + "integrity": "sha512-tcwt3ZUWOKfNLXN2edxFVHMlIuPvbuyMaKmRopgljSCfFcNHWhfTNlxlvmECRNhuQ91EcGwte6F1dwoeMCNd7A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-linux-x64-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.87.0.tgz", + "integrity": "sha512-Xf4AXF14KXUzSnfgTcFLFSM0TykJhFw14+xwNvlAb6WdqXAKlMrz9joIAezc8dkW1NNscCVTsqBUPJ4RhvCM1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-linux-x64-musl": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-x64-musl/-/binding-linux-x64-musl-0.87.0.tgz", + "integrity": "sha512-LIqvpx9UihEW4n9QbEljDnfUdAWqhr6dRqmzSFwVAeLZRUECluLCDdsdwemrC/aZkvnisA4w0LFcFr3HmeTLJg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-wasm32-wasi": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-wasm32-wasi/-/binding-wasm32-wasi-0.87.0.tgz", + "integrity": "sha512-h0xluvc+YryfH5G5dndjGHuA/D4Kp85EkPMxqoOjNudOKDCtdobEaC9horhCqnOOQ0lgn+PGFl3w8u4ToOuRrA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-win32-arm64-msvc": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.87.0.tgz", + "integrity": "sha512-fgxSx+TUc7e2rNtRAMnhHrjqh1e8p/JKmWxRZXtkILveMr/TOHGiDis7U3JJbwycmTZ+HSsJ/PNFQl+tKzmDxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-win32-x64-msvc": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.87.0.tgz", + "integrity": "sha512-K6TTrlitEJgD0FGIW2r0t3CIJNqBkzHT97h49gZLS24ey2UG1zKt27iSHkpXMJYDiG97ZD2yv3pSph1ctMlFXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-parser/binding-android-arm64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.87.0.tgz", + "integrity": "sha512-3APxTyYaAjpW5zifjzfsPgoIa4YHwA5GBjtgLRQpGVXCykXBIEbUTokoAs411ZuOwS3sdTVXBTGAdziXRd8rUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-arm64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.87.0.tgz", + "integrity": "sha512-99e8E76M+k3Gtwvs5EU3VTs2hQkJmvnrl/eu7HkBUc9jLFHA4nVjYSgukMuqahWe270udUYEPRfcWKmoE1Nukg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-x64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.87.0.tgz", + "integrity": "sha512-2rRo6Dz560/4ot5Q0KPUTEunEObkP8mDC9mMiH0RJk1FiOb9c+xpPbkYoUHNKuVMm8uIoiBCxIAbPtBhs9QaXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-freebsd-x64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.87.0.tgz", + "integrity": "sha512-uR+WZAvWkFQPVoeqXgQFr7iy+3hEI295qTbQ4ujmklgM5eTX3YgMFoIV00Stloxfd1irSDDSaK7ySnnzF6mRJg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.87.0.tgz", + "integrity": "sha512-Emm1NpVGKbwzQOIZJI8ZuZu0z8FAd5xscqdS6qpDFpDdEMxk6ab7o3nM8V09RhNCORAzeUlk4TBHQ2Crzjd50A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.87.0.tgz", + "integrity": "sha512-1PPCxRZSJXzQaqc8y+wH7EqPgSfQ/JU3pK6WTN/1SUe/8paNVSKKqk175a8BbRVxGUtPnwEG89pi+xfPTSE7GA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.87.0.tgz", + "integrity": "sha512-fcnnsfcyLamJOMVKq+BQ8dasb8gRnZtNpCUfZhaEFAdXQ7J2RmZreFzlygcn80iti0V7c5LejcjHbF4IdK3GAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-musl": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.87.0.tgz", + "integrity": "sha512-tBPkSPgRSSbmrje8CUovISi/Hj/tWjZJ3n/qnrjx2B+u86hWtwLsngtPDQa5d4seSyDaHSx6tNEUcH7+g5Ee0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.87.0.tgz", + "integrity": "sha512-z4UKGM4wv2wEAQAlx2pBq6+pDJw5J/5oDEXqW6yBSLbWLjLDo4oagmRSE3+giOWteUa+0FVJ+ypq4iYxBkYSWg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-s390x-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.87.0.tgz", + "integrity": "sha512-6W1ENe/nZtr2TBnrEzmdGEraEAdZOiH3YoUNNeQWuqwLkmpoHTJJdclieToPe/l2IKJ4WL3FsSLSGHE8yt/OEg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.87.0.tgz", + "integrity": "sha512-s3kB/Ii3X3IOZ27Iu7wx2zYkIcDO22Emu32SNC6kkUSy09dPBc1yaW14TnAkPMe/rvtuzR512JPWj3iGpl+Dng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-musl": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.87.0.tgz", + "integrity": "sha512-3+M9hfrZSDi4+Uy4Ll3rtOuVG3IHDQlj027jgtmAAHJK1eqp4CQfC7rrwE+LFUqUwX+KD2GwlxR+eHyyEf5Gbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-wasm32-wasi": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.87.0.tgz", + "integrity": "sha512-2jgeEeOa4GbQQg2Et/gFTgs5wKS/+CxIg+CN2mMOJ4EqbmvUVeGiumO01oFOWTYnJy1oONwIocBzrnMuvOcItA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-parser/binding-win32-arm64-msvc": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.87.0.tgz", + "integrity": "sha512-KZp9poaBaVvuFM0TrsHCDOjPQK5eMDXblz21boMhKHGW5/bOlkMlg3CYn5j0f67FkK68NSdNKREMxmibBeXllQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-parser/binding-win32-x64-msvc": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.87.0.tgz", + "integrity": "sha512-86uisngtp/8XdcerIKxMyJTqgDSTJatkfpylpUH0d96W8Bb9E+bVvM2fIIhLWB0Eb03PeY2BdIT7DNIln9TnHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.87.0.tgz", + "integrity": "sha512-ipZFWVGE9fADBVXXWJWY/cxpysc41Gt5upKDeb32F6WMgFyO7XETUMVq8UuREKCih+Km5E6p2VhEvf6Fuhey6g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@oxc-transform/binding-android-arm64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-android-arm64/-/binding-android-arm64-0.87.0.tgz", + "integrity": "sha512-B7W6J8T9cS054LUGLfYkYz8bz5+t+4yPftZ67Bn6MJ03okMLnbbEfm1bID1tqcP5tJwMurTILVy/dQfDYDcMgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-darwin-arm64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-darwin-arm64/-/binding-darwin-arm64-0.87.0.tgz", + "integrity": "sha512-HImW3xOPx7FHKqfC5WfE82onhRfnWQUiB7R+JgYrk+7NR404h3zANSPzu3V/W9lbDxlmHTcqoD2LKbNC5j0TQA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-darwin-x64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-darwin-x64/-/binding-darwin-x64-0.87.0.tgz", + "integrity": "sha512-MDbgugi6mvuPTfS78E2jyozm7493Kuqmpc5r406CsUdEsXlnsF+xvmKlrW9ZIkisO74dD+HWouSiDtNyPQHjlw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-freebsd-x64": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-freebsd-x64/-/binding-freebsd-x64-0.87.0.tgz", + "integrity": "sha512-N0M5D/4haJw7BMn2WZ3CWz0WkdLyoK1+3KxOyCv2CPedMCxx6eQay2AtJxSzj9tjVU1+ukbSb2fDO24JIJGsVA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm-gnueabihf": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.87.0.tgz", + "integrity": "sha512-PubObCNOUOzm1S+P0yn7S+/6xRLbSPMqhgrb73L3p+J1Z20fv/FYVg0kFd36Yho24TSC/byOkebEZWAtxCasWw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm-musleabihf": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.87.0.tgz", + "integrity": "sha512-Nk2d/FS7sMCmCl99vHojzigakjDPamkjOXs2i+H71o/NqytS0pk3M+tXat8M3IGpeLJIEszA5Mv+dcq731nlYA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm64-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.87.0.tgz", + "integrity": "sha512-BxFkIcso2V1+FCDoU+KctxvJzSQVSnEZ5EEQ8O3Up9EoFVQRnZ8ktXvqYj2Oqvc4IYPskLPsKUgc9gdK8wGhUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm64-musl": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.87.0.tgz", + "integrity": "sha512-MZ1/TNaebhXK73j1UDfwyBFnAy0tT3n6otOkhlt1vlJwqboUS/D7E/XrCZmAuHIfVPxAXRPovkl7kfxLB43SKw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-linux-riscv64-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.87.0.tgz", + "integrity": "sha512-JCWE6n4Hicu0FVbvmLdH/dS8V6JykOUsbrbDYm6JwFlHr4eFTTlS2B+mh5KPOxcdeOlv/D/XRnvMJ6WGYs25EA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-linux-s390x-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.87.0.tgz", + "integrity": "sha512-n2NTgM+3PqFagJV9UXRDNOmYesF+TO9SF9FeHqwVmW893ayef9KK+vfWAAhvOYHXYaKWT5XoHd87ODD7nruyhw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-linux-x64-gnu": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.87.0.tgz", + "integrity": "sha512-ZOKW3wx0bW2O7jGdOzr8DyLZqX2C36sXvJdsHj3IueZZ//d/NjLZqEiUKz+q0JlERHtCVKShQ5PLaCx7NpuqNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-linux-x64-musl": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-x64-musl/-/binding-linux-x64-musl-0.87.0.tgz", + "integrity": "sha512-eIspx/JqkVMPK1CAYEOo2J8o49s4ZTf+32MSMUknIN2ZS1fvRmWS0D/xFFaLP/9UGhdrXRIPbn/iSYEA8JnV/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-wasm32-wasi": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-wasm32-wasi/-/binding-wasm32-wasi-0.87.0.tgz", + "integrity": "sha512-4uRjJQnt/+kmJUIC6Iwzn+MqqZhLP1zInPtDwgL37KI4VuUewUQWoL+sggMssMEgm7ZJwOPoZ6piuSWwMgOqgQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.3" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-win32-arm64-msvc": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.87.0.tgz", + "integrity": "sha512-l/qSi4/N5W1yXKU9+1gWGo0tBoRpp4zvHYrpsbq3zbefPL4VYdA0gKF7O10/ZQVkYylzxiVh2zpYO34/FbZdIg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-win32-x64-msvc": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.87.0.tgz", + "integrity": "sha512-jG/MhMjfSdyj5KyhnwNWr4mnAlAsz+gNUYpjQ+UXWsfsoB3f8HqbsTkG02RBtNa/IuVQYvYYVf1eIimNN3gBEQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-wasm": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-wasm/-/watcher-wasm-2.5.1.tgz", + "integrity": "sha512-RJxlQQLkaMMIuWRozy+z2vEqbaQlCuaCgVZIUCzQLYggY22LZbP5Y1+ia+FD724Ids9e+XIyOLXLrLgQSHIthw==", + "bundleDependencies": [ + "napi-wasm" + ], + "dev": true, + "license": "MIT", + "dependencies": { + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "napi-wasm": "^1.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-wasm/node_modules/napi-wasm": { + "version": "1.1.0", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@poppinss/colors": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.5.tgz", + "integrity": "sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.1.5" + } + }, + "node_modules/@poppinss/colors/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@poppinss/dumper": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.4.tgz", + "integrity": "sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@sindresorhus/is": "^7.0.2", + "supports-color": "^10.0.0" + } + }, + "node_modules/@poppinss/exception": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.2.tgz", + "integrity": "sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-alias": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-alias/-/plugin-alias-5.1.1.tgz", + "integrity": "sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "28.0.6", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.6.tgz", + "integrity": "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "fdir": "^6.2.0", + "is-reference": "1.2.1", + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=16.0.0 || 14 >= 14.17" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-inject": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", + "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-inject/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-json": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", + "integrity": "sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz", + "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.2.tgz", + "integrity": "sha512-7QaYCf8bqF04dOy7w/eHmJeNExxTYwvKAmlSAH/EaWWUzbT0h5sbF6bktFoX/0F/0qwng5/dWFMyf3gzaM8DsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.1.tgz", + "integrity": "sha512-sifE8uDpDvortUdi3xFevQ9WN5L3orrglg7iO/DhIpSVCwJOxBs9k9JzCC76KEZkLY4UkHWj+KESdFhlsNmDLw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.1.tgz", + "integrity": "sha512-s83W/rRAPshsyzH9cS0CPKZVLlo2GGRt/1BocbR64DIyr2tMN1f2OZEjbFUnkAA2ewfbd+9waSYS0vbrlsG3qg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.1.tgz", + "integrity": "sha512-lJkbZBREVUY9Vdw6DrzCysWv9Trcl7SyNxPRQMqvt6V/xmQC140aOcSkyWzwQ9t+s3ojvvWYZMpSazAbSTNfSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.1.tgz", + "integrity": "sha512-cw852iGDmvuXeOz2lwpocEL9wkHg3TBZRdAbwmra/YJ5KVxaj7nDdYJ9P0OAVxsbsKa0hFML+dwRHA02kB8Q+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.1.tgz", + "integrity": "sha512-nLezpaKL1jY63BunCbeA7B7B/5i4DQifNRBfzZ0+p3BxRejeKdzP7T3rfD5YpNy3+RysFy8Zw3EAnvXyrbZzqQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.1.tgz", + "integrity": "sha512-USdXZmfo+t4DoUC02UotEf7e6ADsaQ1pvOtOZV2iT2wEmB6y7iMJA0MsIZTbp27enq9v+YK43s3ztYPVy0T2bA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.1.tgz", + "integrity": "sha512-n3YunK17pY3BuZhLNTcRCT83JkFRfBKnG4R2vROUZvxLJlYkIQXfDGQRVZ7ZZBp1INxXm4fzT4jrd6Tm5DMZ7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.1.tgz", + "integrity": "sha512-45geWgFvA+SKw49tRkHI7xBizBZc6bismWIg+zqwK1OZN0hqMXe39BExVu45o768KDoM7XGoZ1pDE9opiHKKag==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.1.tgz", + "integrity": "sha512-7m2ybyIOd5j/U43JSfMblwiZG69yAfuvg6TXhHvOtoQMjw6Or48FmgUxyAZ4ZzH7isxfMyr8M26m0pBkoAIEdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.1.tgz", + "integrity": "sha512-qnmMzRpkKG1T1EzKVtA/8Q0YAYalRN+h+WzWcbyD0SqjVwxmqrPj/TuuH30TwUp6X2UaUhfWSHccMgF+T6jDpw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.1.tgz", + "integrity": "sha512-5Fc7jWzggy8RXJTew+8FoUXwpvJIuwOcYEMSJxs/9MB+oG/C4NRM23Xg+vW173sQz0H6RSViMmoKJih/hVQQow==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.1.tgz", + "integrity": "sha512-DxnsniAn/iv23PtQhOU0l+cXAG3IvWkzEOc9t4THzWJs/NKpF955GnbYKo6PwqwlcbxO/ARn4B8IMg4ghW+DOw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.1.tgz", + "integrity": "sha512-xAlxc3PeGHNpLmisSs8UpFm/A8aPOVeoHhWePEH0rDVFCC4uwWx4W1ecq/oYT2gjkRtVBxD1GjjNYJQrN9fX4A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.1.tgz", + "integrity": "sha512-b5xbekmUtAkPY3TqrYMvbAltNNmpMApdMDxjYiaUQ8k1ep0iS/900CJEZq/RPd5gXF59Lp+me1wXbkW1xpxw4g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.1.tgz", + "integrity": "sha512-CcNQx6CuvJH/SMt3dElyqrCK7BCCAOQtdobJIVhJ7AaA5nrE0RkNHTVzDyXkYqkgoMjuF2p0tEchX7YuOeal4w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.1.tgz", + "integrity": "sha512-xsKzVShwurM4JjGyMo/n4lb13mzpfDmg0yWiMlO65XSkhIpWnGnE4z66y9leVALb3M7sWiNluCKUv2ZZ0DWy1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.1.tgz", + "integrity": "sha512-AtzCeCyU6wYbJq7akOX3oZmc1pcY6yNYYC+HbjAcnjB63hXc22AX6nWtoU9TOJw3EQRxCLIubwGmnSrk66khpQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.1.tgz", + "integrity": "sha512-pZb5K1hqS6MmdSgNUfWIzemPNNwmg5n7HhZHSyClwGd/IoQCiTjUGs09O/lxOZLHlltqUyVl0Y/4dcd8j90FEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.1.tgz", + "integrity": "sha512-A6hkNBmS3yahy06sFIouOjC5MO/ciPSBxdbWdGIk7ue3lhR1wJ9mJ27kZFK/N8ZOLwO1YdymYhhfI3gGHHpliA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.1.tgz", + "integrity": "sha512-HRNyKIYDpuC7FIVJ8kH1RFGoEp4beASrjKksx3f2Oa82pLxNVhBIM1gC7WEd7z9djZ0OW6o9qhXFo7gAU4QCWw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.1.tgz", + "integrity": "sha512-rkpnc4BKw8QoP9yynwLJqjVgmkko8yjqEHHYlUPv/xznRb3mQ7iN7fpc5fOqCFtYCeEyilBAun5a4wKLLKYX2g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.1.tgz", + "integrity": "sha512-ZzNEDNx/4sWP94UNAc6OfVNJFM2G4vz6IcIhBJv8BYyLeGNQldV5Dn22+i8Y7yn4a7unFjdAX/1nwNBfc7tUcg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sindresorhus/is": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.1.0.tgz", + "integrity": "sha512-7F/yz2IphV39hiS2zB4QYVkivrptHHh0K8qJJd9HhuWSdvf8AN7NpebW3CcDZDBQsUPMoDKWsY2WWgW7bqOcfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@speed-highlight/core": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.7.tgz", + "integrity": "sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.5.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", + "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.12.0" + } + }, + "node_modules/@types/parse-path": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/parse-path/-/parse-path-7.0.3.tgz", + "integrity": "sha512-LriObC2+KYZD3FzCrgWGv/qufdUy4eXrxcLgQMfYXgPbLIecKIsVBaQgUPmxSSLcjmYbDTQbMgr6qr6l/eb7Bg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@unhead/vue": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.0.17.tgz", + "integrity": "sha512-jzmGZYeMAhETV6qfetmLbZzUjjx1TjdNvFSobeFZb73D7dwD9wl/nOAx36qq+TvjZsLJdF5PQWToz2oDGAUqCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookable": "^5.5.3", + "unhead": "2.0.17" + }, + "funding": { + "url": "https://github.com/sponsors/harlan-zw" + }, + "peerDependencies": { + "vue": ">=3.5.18" + } + }, + "node_modules/@vercel/nft": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@vercel/nft/-/nft-0.30.1.tgz", + "integrity": "sha512-2mgJZv4AYBFkD/nJ4QmiX5Ymxi+AisPLPcS/KPXVqniyQNqKXX+wjieAbDXQP3HcogfEbpHoRMs49Cd4pfkk8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mapbox/node-pre-gyp": "^2.0.0", + "@rollup/pluginutils": "^5.1.3", + "acorn": "^8.6.0", + "acorn-import-attributes": "^1.9.5", + "async-sema": "^3.1.1", + "bindings": "^1.4.0", + "estree-walker": "2.0.2", + "glob": "^10.4.5", + "graceful-fs": "^4.2.9", + "node-gyp-build": "^4.2.2", + "picomatch": "^4.0.2", + "resolve-from": "^5.0.0" + }, + "bin": { + "nft": "out/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vercel/nft/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitejs/plugin-vue-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-5.1.1.tgz", + "integrity": "sha512-uQkfxzlF8SGHJJVH966lFTdjM/lGcwJGzwAHpVqAPDD/QcsqoUGa+q31ox1BrUfi+FLP2ChVp7uLXE3DkHyDdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.3", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/plugin-transform-typescript": "^7.28.0", + "@rolldown/pluginutils": "^1.0.0-beta.34", + "@vue/babel-plugin-jsx": "^1.5.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.0.0" + } + }, + "node_modules/@vitejs/plugin-vue-jsx/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.39", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.39.tgz", + "integrity": "sha512-GkTtNCV8ObWbq3LrJStPBv9jkRPct8WlwotVjx3aU0RwfH3LyheixWK9Zhaj22C4EQj/TJxYyetoX+uOn/MWKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/language-core": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.23.tgz", + "integrity": "sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/source-map": "2.4.23" + } + }, + "node_modules/@volar/source-map": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.23.tgz", + "integrity": "sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@volar/typescript": { + "version": "2.4.23", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.23.tgz", + "integrity": "sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "path-browserify": "^1.0.1", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/@vue-macros/common": { + "version": "3.0.0-beta.16", + "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.0.0-beta.16.tgz", + "integrity": "sha512-8O2gWxWFiaoNkk7PGi0+p7NPGe/f8xJ3/INUufvje/RZOs7sJvlI1jnR4lydtRFa/mU0ylMXUXXjSK0fHDEYTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/compiler-sfc": "^3.5.17", + "ast-kit": "^2.1.1", + "local-pkg": "^1.1.1", + "magic-string-ast": "^1.0.0", + "unplugin-utils": "^0.2.4" + }, + "engines": { + "node": ">=20.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/vue-macros" + }, + "peerDependencies": { + "vue": "^2.7.0 || ^3.2.25" + }, + "peerDependenciesMeta": { + "vue": { + "optional": true + } + } + }, + "node_modules/@vue/babel-helper-vue-transform-on": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", + "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/babel-plugin-jsx": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz", + "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.2", + "@vue/babel-helper-vue-transform-on": "1.5.0", + "@vue/babel-plugin-resolve-type": "1.5.0", + "@vue/shared": "^3.5.18" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + } + } + }, + "node_modules/@vue/babel-plugin-resolve-type": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz", + "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/parser": "^7.28.0", + "@vue/compiler-sfc": "^3.5.18" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz", + "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/shared": "3.5.21", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-core/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz", + "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz", + "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "@vue/compiler-core": "3.5.21", + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.18", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz", + "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/compiler-vue2": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz", + "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==", + "dev": true, + "license": "MIT", + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, + "node_modules/@vue/devtools-core": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-7.7.7.tgz", + "integrity": "sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.7", + "@vue/devtools-shared": "^7.7.7", + "mitt": "^3.0.1", + "nanoid": "^5.1.0", + "pathe": "^2.0.3", + "vite-hot-client": "^2.0.4" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", + "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.7", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-kit/node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", + "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, + "node_modules/@vue/language-core": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.0.8.tgz", + "integrity": "sha512-eYs6PF7bxoPYvek9qxceo1BCwFbJZYqJll+WaYC8o8ec60exqj+n+QRGGiJHSeUfYp0hDxARbMdxMq/fbPgU5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/language-core": "2.4.23", + "@vue/compiler-dom": "^3.5.0", + "@vue/compiler-vue2": "^2.7.16", + "@vue/shared": "^3.5.0", + "alien-signals": "^2.0.5", + "muggle-string": "^0.4.1", + "path-browserify": "^1.0.1", + "picomatch": "^4.0.2" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz", + "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz", + "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/shared": "3.5.21" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz", + "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.21", + "@vue/runtime-core": "3.5.21", + "@vue/shared": "3.5.21", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz", + "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "vue": "3.5.21" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz", + "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==", + "license": "MIT" + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.14.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "license": "Apache-2.0", + "peer": true + }, + "node_modules/abbrev": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", + "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "peer": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT", + "peer": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/alien-signals": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-2.0.7.tgz", + "integrity": "sha512-wE7y3jmYeb0+h6mr5BOovuqhFv22O/MV9j5p0ndJsa7z1zJNPGQ4ph5pQk/kTTCWRC3xsA4SmtwmkzQO+7NCNg==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.1.0.tgz", + "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ast-kit": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.1.2.tgz", + "integrity": "sha512-cl76xfBQM6pztbrFWRnxbrDm9EOqDr1BF6+qQnnDZG2Co2LjyUktkN9GTJfBAfdae+DbT2nJf2nCGAdDDN7W2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.0", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=20.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/ast-walker-scope": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.8.2.tgz", + "integrity": "sha512-3pYeLyDZ6nJew9QeBhS4Nly02269Dkdk32+zdbbKmL6n4ZuaGorwwA+xx12xgOciA8BF1w9x+dlH7oUkFTW91w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.3", + "ast-kit": "^2.1.2" + }, + "engines": { + "node": ">=20.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/b4a": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.1.tgz", + "integrity": "sha512-ZovbrBV0g6JxK5cGUF1Suby1vLfKjv4RWi8IxoaO/Mon8BDD9I21RxjHFtgQ+kskJqLAVyQZly3uMBui+vhc8Q==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.7.0.tgz", + "integrity": "sha512-b3N5eTW1g7vXkw+0CXh/HazGTcO5KYuu/RCNaJbDMPI6LHDi+7qe8EmxKUVe1sUbY2KZOVZFyj62x0OEz9qyAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.6.tgz", + "integrity": "sha512-wrH5NNqren/QMtKUEEJf7z86YjfqW/2uw3IL3/xpqZUC95SSVIFXYQeeGjL6FT/X68IROu6RMehZQS5foy2BXw==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/birpc": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.6.1.tgz", + "integrity": "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.26.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", + "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.3", + "caniuse-lite": "^1.0.30001741", + "electron-to-chromium": "^1.5.218", + "node-releases": "^2.0.21", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c12": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.0.tgz", + "integrity": "sha512-K9ZkuyeJQeqLEyqldbYLG3wjqwpw4BVaAqvmxq3GYKK0b1A/yYQdIcJxkzAOWcNVWhJpRXAPfZFueekiY/L8Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^17.2.2", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.5.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.0.0", + "pkg-types": "^2.3.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/dotenv": { + "version": "17.2.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz", + "integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", + "engines": { + "node": "*" + } + }, + "node_modules/caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001743", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001743.tgz", + "integrity": "sha512-e6Ojr7RV14Un7dz6ASD0aZDmQPT/A+eZU+nuTNfjqmRrmkmQlnTNWH0SKmqagx9PeW87UVqapSurtAXifmtdmw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/chownr": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/clipboardy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz", + "integrity": "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^8.0.1", + "is-wsl": "^3.1.0", + "is64bit": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/compatx": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/compatx/-/compatx-0.2.0.tgz", + "integrity": "sha512-6gLRNt4ygsi5NyMVhceOCFv14CIdDFN7fQjX1U4+47qVE/+kjPoXMK65KWK+dWxmFzMTuKazoQ9sch6pM0p5oA==", + "dev": true, + "license": "MIT" + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/compress-commons/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/confbox": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz", + "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cookie-es": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.0.tgz", + "integrity": "sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/croner": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/croner/-/croner-9.1.0.tgz", + "integrity": "sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crossws": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", + "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", + "dev": true, + "license": "MIT", + "dependencies": { + "uncrypto": "^0.1.3" + } + }, + "node_modules/css-declaration-sorter": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.3.0.tgz", + "integrity": "sha512-LQF6N/3vkAMYF4xoHLJfG718HRJh34Z8BnNhd6bosOMIVjMlhuZK5++oZa3uYAgrI5+7x2o27gUqTR2U/KjUOQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14 || ^16 || >=18" + }, + "peerDependencies": { + "postcss": "^8.0.9" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssnano": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-7.1.1.tgz", + "integrity": "sha512-fm4D8ti0dQmFPeF8DXSAA//btEmqCOgAc/9Oa3C1LW94h5usNrJEfrON7b4FkPZgnDEn6OUs5NdxiJZmAtGOpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-preset-default": "^7.0.9", + "lilconfig": "^3.1.3" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/cssnano" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/cssnano-preset-default": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-7.0.9.tgz", + "integrity": "sha512-tCD6AAFgYBOVpMBX41KjbvRh9c2uUjLXRyV7KHSIrwHiq5Z9o0TFfUCoM3TwVrRsRteN3sVXGNvjVNxYzkpTsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.1", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^5.0.1", + "postcss-calc": "^10.1.1", + "postcss-colormin": "^7.0.4", + "postcss-convert-values": "^7.0.7", + "postcss-discard-comments": "^7.0.4", + "postcss-discard-duplicates": "^7.0.2", + "postcss-discard-empty": "^7.0.1", + "postcss-discard-overridden": "^7.0.1", + "postcss-merge-longhand": "^7.0.5", + "postcss-merge-rules": "^7.0.6", + "postcss-minify-font-values": "^7.0.1", + "postcss-minify-gradients": "^7.0.1", + "postcss-minify-params": "^7.0.4", + "postcss-minify-selectors": "^7.0.5", + "postcss-normalize-charset": "^7.0.1", + "postcss-normalize-display-values": "^7.0.1", + "postcss-normalize-positions": "^7.0.1", + "postcss-normalize-repeat-style": "^7.0.1", + "postcss-normalize-string": "^7.0.1", + "postcss-normalize-timing-functions": "^7.0.1", + "postcss-normalize-unicode": "^7.0.4", + "postcss-normalize-url": "^7.0.1", + "postcss-normalize-whitespace": "^7.0.1", + "postcss-ordered-values": "^7.0.2", + "postcss-reduce-initial": "^7.0.4", + "postcss-reduce-transforms": "^7.0.1", + "postcss-svgo": "^7.1.0", + "postcss-unique-selectors": "^7.0.4" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/cssnano-utils": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-5.0.1.tgz", + "integrity": "sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "~2.2.0" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/db0": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/db0/-/db0-0.3.2.tgz", + "integrity": "sha512-xzWNQ6jk/+NtdfLyXEipbX55dmDSeteLFt/ayF+wZUU5bzKgmrDOxmInUTbyVRp46YwnJdkDA1KhB7WIXFofJw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@electric-sql/pglite": "*", + "@libsql/client": "*", + "better-sqlite3": "*", + "drizzle-orm": "*", + "mysql2": "*", + "sqlite3": "*" + }, + "peerDependenciesMeta": { + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "drizzle-orm": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decache": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/decache/-/decache-4.6.2.tgz", + "integrity": "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==", + "license": "MIT", + "dependencies": { + "callsite": "^1.0.0" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/devalue": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.3.2.tgz", + "integrity": "sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==", + "dev": true, + "license": "MIT" + }, + "node_modules/diff": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", + "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-prop": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz", + "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^4.18.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true, + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.222", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.222.tgz", + "integrity": "sha512-gA7psSwSwQRE60CEoLz6JBCQPIxNeuzB2nL8vE03GK/OHxlvykbLyeiumQy1iH5C2f3YbRAZpGCMT12a/9ih9w==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", + "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/errx": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/errx/-/errx-0.1.0.tgz", + "integrity": "sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz", + "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.10", + "@esbuild/android-arm": "0.25.10", + "@esbuild/android-arm64": "0.25.10", + "@esbuild/android-x64": "0.25.10", + "@esbuild/darwin-arm64": "0.25.10", + "@esbuild/darwin-x64": "0.25.10", + "@esbuild/freebsd-arm64": "0.25.10", + "@esbuild/freebsd-x64": "0.25.10", + "@esbuild/linux-arm": "0.25.10", + "@esbuild/linux-arm64": "0.25.10", + "@esbuild/linux-ia32": "0.25.10", + "@esbuild/linux-loong64": "0.25.10", + "@esbuild/linux-mips64el": "0.25.10", + "@esbuild/linux-ppc64": "0.25.10", + "@esbuild/linux-riscv64": "0.25.10", + "@esbuild/linux-s390x": "0.25.10", + "@esbuild/linux-x64": "0.25.10", + "@esbuild/netbsd-arm64": "0.25.10", + "@esbuild/netbsd-x64": "0.25.10", + "@esbuild/openbsd-arm64": "0.25.10", + "@esbuild/openbsd-x64": "0.25.10", + "@esbuild/openharmony-arm64": "0.25.10", + "@esbuild/sunos-x64": "0.25.10", + "@esbuild/win32-arm64": "0.25.10", + "@esbuild/win32-ia32": "0.25.10", + "@esbuild/win32-x64": "0.25.10" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "license": "BSD-2-Clause", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exsolve": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.7.tgz", + "integrity": "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/externality": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/externality/-/externality-1.0.2.tgz", + "integrity": "sha512-LyExtJWKxtgVzmgtEHyQtLFpw1KFhQphF9nTG8TpAIVkiI/xQ3FJh75tRFLYl4hkn7BNIIdLJInuDAavX35pMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^5.14.1", + "mlly": "^1.3.0", + "pathe": "^1.1.1", + "ufo": "^1.1.2" + } + }, + "node_modules/externality/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT" + }, + "node_modules/fast-npm-meta": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/fast-npm-meta/-/fast-npm-meta-0.4.6.tgz", + "integrity": "sha512-zbBBOAOlzxfrU4WSnbCHk/nR6Vf32lSEPxDEvNOR08Z5DSZ/A6qJu0rqrHVcexBTd1hc2gim998xnqF/R1PuEw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/fuse.js": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz", + "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-port-please": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port-please/-/get-port-please-3.2.0.tgz", + "integrity": "sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/git-up": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/git-up/-/git-up-8.1.1.tgz", + "integrity": "sha512-FDenSF3fVqBYSaJoYy1KSc2wosx0gCvKP+c+PRBht7cAaiCeQlBtfBDX9vgnNOHmdePlSFITVcn4pFfcgNvx3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-ssh": "^1.4.0", + "parse-url": "^9.2.0" + } + }, + "node_modules/git-url-parse": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-16.1.0.tgz", + "integrity": "sha512-cPLz4HuK86wClEW7iDdeAKcCVlWXmrLpb2L+G9goW0Z1dtpNS6BXXSOckUTlJT/LDQViE1QZKstNORzHsLnobw==", + "dev": true, + "license": "MIT", + "dependencies": { + "git-up": "^8.1.0" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause", + "peer": true + }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/gzip-size": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-7.0.0.tgz", + "integrity": "sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/h3": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.4.tgz", + "integrity": "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie-es": "^1.2.2", + "crossws": "^0.3.5", + "defu": "^6.1.4", + "destr": "^2.0.5", + "iron-webcrypto": "^1.2.1", + "node-mock-http": "^1.0.2", + "radix3": "^1.1.2", + "ufo": "^1.6.1", + "uncrypto": "^0.1.3" + } + }, + "node_modules/h3/node_modules/cookie-es": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", + "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-shutdown": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/http-shutdown/-/http-shutdown-1.2.2.tgz", + "integrity": "sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/httpxy": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/httpxy/-/httpxy-0.1.7.tgz", + "integrity": "sha512-pXNx8gnANKAndgga5ahefxc++tJvNL87CXoRwxn1cJE2ZkWEojF3tNfQIEhZX/vfpt+wzeAzpUI4qkediX1MLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-meta": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/image-meta/-/image-meta-0.2.1.tgz", + "integrity": "sha512-K6acvFaelNxx8wc2VjbIzXKDVB0Khs0QT35U6NkGfTdCmjLNcO2945m7RFNR9/RPVFm48hq7QPzK8uGH18HCGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/immutable": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "license": "MIT", + "optional": true, + "peer": true + }, + "node_modules/impound": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/impound/-/impound-1.0.0.tgz", + "integrity": "sha512-8lAJ+1Arw2sMaZ9HE2ZmL5zOcMnt18s6+7Xqgq2aUVy4P1nlzAyPtzCDxsk51KVFwHEEdc6OWvUyqwHwhRYaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "exsolve": "^1.0.5", + "mocked-exports": "^0.1.1", + "pathe": "^2.0.3", + "unplugin": "^2.3.2", + "unplugin-utils": "^0.2.4" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ioredis": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.7.0.tgz", + "integrity": "sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ioredis/commands": "^1.3.0", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/iron-webcrypto": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", + "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/brc-dd" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-installed-globally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-1.0.0.tgz", + "integrity": "sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-directory": "^4.0.1", + "is-path-inside": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/is-ssh": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.1.tgz", + "integrity": "sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "protocols": "^2.0.1" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is64bit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is64bit/-/is64bit-2.0.0.tgz", + "integrity": "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "system-architecture": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.0.tgz", + "integrity": "sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT", + "peer": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/knitwork": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/knitwork/-/knitwork-1.2.0.tgz", + "integrity": "sha512-xYSH7AvuQ6nXkq42x0v5S8/Iry+cfulBz/DJQzhIyESdLD7425jXsPy4vn5cCXU+HhRN2kVw51Vd1K6/By4BQg==", + "dev": true, + "license": "MIT" + }, + "node_modules/launch-editor": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.1.tgz", + "integrity": "sha512-SEET7oNfgSaB6Ym0jufAdCeo3meJVeCaaDyzRygy0xsp2BFKCprcfHljTq4QkzTLUxEKkFK6OK4811YM2oSrRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.1", + "shell-quote": "^1.8.3" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lightningcss": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", + "dev": true, + "license": "MPL-2.0", + "optional": true, + "peer": true, + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss/node_modules/detect-libc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.0.tgz", + "integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/listhen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/listhen/-/listhen-1.9.0.tgz", + "integrity": "sha512-I8oW2+QL5KJo8zXNWX046M134WchxsXC7SawLPvRQpogCbkyQIaFxPE89A2HiwR7vAK2Dm2ERBAmyjTYGYEpBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/watcher": "^2.4.1", + "@parcel/watcher-wasm": "^2.4.1", + "citty": "^0.1.6", + "clipboardy": "^4.0.0", + "consola": "^3.2.3", + "crossws": ">=0.2.0 <0.4.0", + "defu": "^6.1.4", + "get-port-please": "^3.1.2", + "h3": "^1.12.0", + "http-shutdown": "^1.2.2", + "jiti": "^2.1.2", + "mlly": "^1.7.1", + "node-forge": "^1.3.1", + "pathe": "^1.1.2", + "std-env": "^3.7.0", + "ufo": "^1.5.4", + "untun": "^0.1.3", + "uqr": "^0.1.2" + }, + "bin": { + "listen": "bin/listhen.mjs", + "listhen": "bin/listhen.mjs" + } + }, + "node_modules/listhen/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/local-pkg": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", + "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-regexp": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/magic-regexp/-/magic-regexp-0.10.0.tgz", + "integrity": "sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12", + "mlly": "^1.7.2", + "regexp-tree": "^0.1.27", + "type-level-regexp": "~0.1.17", + "ufo": "^1.5.4", + "unplugin": "^2.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.19", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", + "integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magic-string-ast": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-1.0.2.tgz", + "integrity": "sha512-8ngQgLhcT0t3YBdn9CGkZqCYlvwW9pm7aWJwd7AxseVWf1RU8ZHCQvG1mt3N5vvUme+pXTcHB8G/7fE666U8Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.17" + }, + "engines": { + "node": ">=20.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" + } + }, + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mime": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", + "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", + "bin": { + "mime": "bin/cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "node_modules/mlly/node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mlly/node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/mocked-exports": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/mocked-exports/-/mocked-exports-0.1.1.tgz", + "integrity": "sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/muggle-string": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz", + "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "node_modules/nanotar": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/nanotar/-/nanotar-0.2.0.tgz", + "integrity": "sha512-9ca1h0Xjvo9bEkE4UOxgAzLV0jHKe6LMaxo37ND2DAhhAtd0j8pR1Wxz+/goMrZO8AEZTWCmyaOsFI/W5AdpCQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/nitropack": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/nitropack/-/nitropack-2.12.6.tgz", + "integrity": "sha512-DEq31s0SP4/Z5DIoVBRo9DbWFPWwIoYD4cQMEz7eE+iJMiAP+1k9A3B9kcc6Ihc0jDJmfUcHYyh6h2XlynCx6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cloudflare/kv-asset-handler": "^0.4.0", + "@rollup/plugin-alias": "^5.1.1", + "@rollup/plugin-commonjs": "^28.0.6", + "@rollup/plugin-inject": "^5.0.5", + "@rollup/plugin-json": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.1", + "@rollup/plugin-replace": "^6.0.2", + "@rollup/plugin-terser": "^0.4.4", + "@vercel/nft": "^0.30.1", + "archiver": "^7.0.1", + "c12": "^3.2.0", + "chokidar": "^4.0.3", + "citty": "^0.1.6", + "compatx": "^0.2.0", + "confbox": "^0.2.2", + "consola": "^3.4.2", + "cookie-es": "^2.0.0", + "croner": "^9.1.0", + "crossws": "^0.3.5", + "db0": "^0.3.2", + "defu": "^6.1.4", + "destr": "^2.0.5", + "dot-prop": "^9.0.0", + "esbuild": "^0.25.9", + "escape-string-regexp": "^5.0.0", + "etag": "^1.8.1", + "exsolve": "^1.0.7", + "globby": "^14.1.0", + "gzip-size": "^7.0.0", + "h3": "^1.15.4", + "hookable": "^5.5.3", + "httpxy": "^0.1.7", + "ioredis": "^5.7.0", + "jiti": "^2.5.1", + "klona": "^2.0.6", + "knitwork": "^1.2.0", + "listhen": "^1.9.0", + "magic-string": "^0.30.19", + "magicast": "^0.3.5", + "mime": "^4.0.7", + "mlly": "^1.8.0", + "node-fetch-native": "^1.6.7", + "node-mock-http": "^1.0.3", + "ofetch": "^1.4.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.0.0", + "pkg-types": "^2.3.0", + "pretty-bytes": "^7.0.1", + "radix3": "^1.1.2", + "rollup": "^4.50.1", + "rollup-plugin-visualizer": "^6.0.3", + "scule": "^1.3.0", + "semver": "^7.7.2", + "serve-placeholder": "^2.0.2", + "serve-static": "^2.2.0", + "source-map": "^0.7.6", + "std-env": "^3.9.0", + "ufo": "^1.6.1", + "ultrahtml": "^1.6.0", + "uncrypto": "^0.1.3", + "unctx": "^2.4.1", + "unenv": "^2.0.0-rc.21", + "unimport": "^5.2.0", + "unplugin-utils": "^0.3.0", + "unstorage": "^1.17.1", + "untyped": "^2.0.0", + "unwasm": "^0.3.11", + "youch": "^4.1.0-beta.11", + "youch-core": "^0.3.3" + }, + "bin": { + "nitro": "dist/cli/index.mjs", + "nitropack": "dist/cli/index.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "xml2js": "^0.6.2" + }, + "peerDependenciesMeta": { + "xml2js": { + "optional": true + } + } + }, + "node_modules/nitropack/node_modules/unplugin-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.0.tgz", + "integrity": "sha512-JLoggz+PvLVMJo+jZt97hdIIIZ2yTzGgft9e9q8iMrC4ewufl62ekeW7mixBghonn2gVb/ICjyvlmOCUBnJLQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-mock-http": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.3.tgz", + "integrity": "sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "license": "MIT" + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nuxt": { + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/nuxt/-/nuxt-3.19.2.tgz", + "integrity": "sha512-z4ouGRMOWqZ1xaZ+HdRBRVlZcKSoDBpRxQ30GJ2dllraZMC/gNpTGuY32H3xP5b4R29b8uYcK+B8LFQoRHpO8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nuxt/cli": "^3.28.0", + "@nuxt/devalue": "^2.0.2", + "@nuxt/devtools": "^2.6.3", + "@nuxt/kit": "3.19.2", + "@nuxt/schema": "3.19.2", + "@nuxt/telemetry": "^2.6.6", + "@nuxt/vite-builder": "3.19.2", + "@unhead/vue": "^2.0.14", + "@vue/shared": "^3.5.21", + "c12": "^3.2.0", + "chokidar": "^4.0.3", + "compatx": "^0.2.0", + "consola": "^3.4.2", + "cookie-es": "^2.0.0", + "defu": "^6.1.4", + "destr": "^2.0.5", + "devalue": "^5.3.2", + "errx": "^0.1.0", + "esbuild": "^0.25.9", + "escape-string-regexp": "^5.0.0", + "estree-walker": "^3.0.3", + "exsolve": "^1.0.7", + "h3": "^1.15.4", + "hookable": "^5.5.3", + "ignore": "^7.0.5", + "impound": "^1.0.0", + "jiti": "^2.5.1", + "klona": "^2.0.6", + "knitwork": "^1.2.0", + "magic-string": "^0.30.19", + "mlly": "^1.8.0", + "mocked-exports": "^0.1.1", + "nanotar": "^0.2.0", + "nitropack": "^2.12.5", + "nypm": "^0.6.1", + "ofetch": "^1.4.1", + "ohash": "^2.0.11", + "on-change": "^5.0.1", + "oxc-minify": "^0.87.0", + "oxc-parser": "^0.87.0", + "oxc-transform": "^0.87.0", + "oxc-walker": "^0.5.2", + "pathe": "^2.0.3", + "perfect-debounce": "^2.0.0", + "pkg-types": "^2.3.0", + "radix3": "^1.1.2", + "scule": "^1.3.0", + "semver": "^7.7.2", + "std-env": "^3.9.0", + "tinyglobby": "^0.2.15", + "ufo": "^1.6.1", + "ultrahtml": "^1.6.0", + "uncrypto": "^0.1.3", + "unctx": "^2.4.1", + "unimport": "^5.2.0", + "unplugin": "^2.3.10", + "unplugin-vue-router": "^0.15.0", + "unstorage": "^1.17.1", + "untyped": "^2.0.0", + "vue": "^3.5.21", + "vue-bundle-renderer": "^2.1.2", + "vue-devtools-stub": "^0.1.0", + "vue-router": "^4.5.1" + }, + "bin": { + "nuxi": "bin/nuxt.mjs", + "nuxt": "bin/nuxt.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@parcel/watcher": "^2.1.0", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "peerDependenciesMeta": { + "@parcel/watcher": { + "optional": true + }, + "@types/node": { + "optional": true + } + } + }, + "node_modules/nuxt/node_modules/@nuxt/schema": { + "version": "3.19.2", + "resolved": "https://registry.npmjs.org/@nuxt/schema/-/schema-3.19.2.tgz", + "integrity": "sha512-kMN2oIfrsMc8ACrRweYRG7Q44/KuHG5y7L+4szQhfOgN78OiYkxiM/nSsLH0K2bJq8Eavg+WGfgACj4Lsy+YqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/shared": "^3.5.21", + "consola": "^3.4.2", + "defu": "^6.1.4", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "std-env": "^3.9.0", + "ufo": "1.6.1" + }, + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/nypm": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.2.tgz", + "integrity": "sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.2", + "pathe": "^2.0.3", + "pkg-types": "^2.3.0", + "tinyexec": "^1.0.1" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": "^14.16.0 || >=16.10.0" + } + }, + "node_modules/ofetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.4.1.tgz", + "integrity": "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==", + "dev": true, + "license": "MIT", + "dependencies": { + "destr": "^2.0.3", + "node-fetch-native": "^1.6.4", + "ufo": "^1.5.4" + } + }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/on-change": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/on-change/-/on-change-5.0.1.tgz", + "integrity": "sha512-n7THCP7RkyReRSLkJb8kUWoNsxUIBxTkIp3JKno+sEz6o/9AJ3w3P9fzQkITEkMwyTKJjZciF3v/pVoouxZZMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/on-change?sponsor=1" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", + "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open/node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open/node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/oxc-minify": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/oxc-minify/-/oxc-minify-0.87.0.tgz", + "integrity": "sha512-+UHWp6+0mdq0S2rEsZx9mqgL6JnG9ogO+CU17XccVrPUFtISFcZzk/biTn1JdBYFQ3kztof19pv8blMtgStQ2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-minify/binding-android-arm64": "0.87.0", + "@oxc-minify/binding-darwin-arm64": "0.87.0", + "@oxc-minify/binding-darwin-x64": "0.87.0", + "@oxc-minify/binding-freebsd-x64": "0.87.0", + "@oxc-minify/binding-linux-arm-gnueabihf": "0.87.0", + "@oxc-minify/binding-linux-arm-musleabihf": "0.87.0", + "@oxc-minify/binding-linux-arm64-gnu": "0.87.0", + "@oxc-minify/binding-linux-arm64-musl": "0.87.0", + "@oxc-minify/binding-linux-riscv64-gnu": "0.87.0", + "@oxc-minify/binding-linux-s390x-gnu": "0.87.0", + "@oxc-minify/binding-linux-x64-gnu": "0.87.0", + "@oxc-minify/binding-linux-x64-musl": "0.87.0", + "@oxc-minify/binding-wasm32-wasi": "0.87.0", + "@oxc-minify/binding-win32-arm64-msvc": "0.87.0", + "@oxc-minify/binding-win32-x64-msvc": "0.87.0" + } + }, + "node_modules/oxc-parser": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.87.0.tgz", + "integrity": "sha512-uc47XrtHwkBoES4HFgwgfH9sqwAtJXgAIBq4fFBMZ4hWmgVZoExyn+L4g4VuaecVKXkz1bvlaHcfwHAJPQb5Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "^0.87.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-parser/binding-android-arm64": "0.87.0", + "@oxc-parser/binding-darwin-arm64": "0.87.0", + "@oxc-parser/binding-darwin-x64": "0.87.0", + "@oxc-parser/binding-freebsd-x64": "0.87.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.87.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.87.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.87.0", + "@oxc-parser/binding-linux-arm64-musl": "0.87.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.87.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.87.0", + "@oxc-parser/binding-linux-x64-gnu": "0.87.0", + "@oxc-parser/binding-linux-x64-musl": "0.87.0", + "@oxc-parser/binding-wasm32-wasi": "0.87.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.87.0", + "@oxc-parser/binding-win32-x64-msvc": "0.87.0" + } + }, + "node_modules/oxc-transform": { + "version": "0.87.0", + "resolved": "https://registry.npmjs.org/oxc-transform/-/oxc-transform-0.87.0.tgz", + "integrity": "sha512-dt6INKWY2DKbSc8yR9VQoqBsCjPQ3z/SKv882UqlwFve+K38xtpi2avDlvNd35SpHUwDLDFoV3hMX0U3qOSaaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-transform/binding-android-arm64": "0.87.0", + "@oxc-transform/binding-darwin-arm64": "0.87.0", + "@oxc-transform/binding-darwin-x64": "0.87.0", + "@oxc-transform/binding-freebsd-x64": "0.87.0", + "@oxc-transform/binding-linux-arm-gnueabihf": "0.87.0", + "@oxc-transform/binding-linux-arm-musleabihf": "0.87.0", + "@oxc-transform/binding-linux-arm64-gnu": "0.87.0", + "@oxc-transform/binding-linux-arm64-musl": "0.87.0", + "@oxc-transform/binding-linux-riscv64-gnu": "0.87.0", + "@oxc-transform/binding-linux-s390x-gnu": "0.87.0", + "@oxc-transform/binding-linux-x64-gnu": "0.87.0", + "@oxc-transform/binding-linux-x64-musl": "0.87.0", + "@oxc-transform/binding-wasm32-wasi": "0.87.0", + "@oxc-transform/binding-win32-arm64-msvc": "0.87.0", + "@oxc-transform/binding-win32-x64-msvc": "0.87.0" + } + }, + "node_modules/oxc-walker": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/oxc-walker/-/oxc-walker-0.5.2.tgz", + "integrity": "sha512-XYoZqWwApSKUmSDEFeOKdy3Cdh95cOcSU8f7yskFWE4Rl3cfL5uwyY+EV7Brk9mdNLy+t5SseJajd6g7KncvlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-regexp": "^0.10.0" + }, + "peerDependencies": { + "oxc-parser": ">=0.72.0" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/package-manager-detector": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", + "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-path": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse-path/-/parse-path-7.1.0.tgz", + "integrity": "sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "protocols": "^2.0.0" + } + }, + "node_modules/parse-url": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/parse-url/-/parse-url-9.2.0.tgz", + "integrity": "sha512-bCgsFI+GeGWPAvAiUv63ZorMeif3/U0zaXABGJbOWt5OH2KCaPHF6S+0ok4aqM9RuIPGyZdx9tR9l13PsW4AYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/parse-path": "^7.0.0", + "parse-path": "^7.0.0" + }, + "engines": { + "node": ">=14.13.0" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.0.0.tgz", + "integrity": "sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pinia": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", + "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.3", + "vue-demi": "^0.14.10" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-calc": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-10.1.1.tgz", + "integrity": "sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12 || ^20.9 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.38" + } + }, + "node_modules/postcss-colormin": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-7.0.4.tgz", + "integrity": "sha512-ziQuVzQZBROpKpfeDwmrG+Vvlr0YWmY/ZAk99XD+mGEBuEojoFekL41NCsdhyNUtZI7DPOoIWIR7vQQK9xwluw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.1", + "caniuse-api": "^3.0.0", + "colord": "^2.9.3", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-convert-values": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-7.0.7.tgz", + "integrity": "sha512-HR9DZLN04Xbe6xugRH6lS4ZQH2zm/bFh/ZyRkpedZozhvh+awAfbA0P36InO4fZfDhvYfNJeNvlTf1sjwGbw/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-discard-comments": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-7.0.4.tgz", + "integrity": "sha512-6tCUoql/ipWwKtVP/xYiFf1U9QgJ0PUvxN7pTcsQ8Ns3Fnwq1pU5D5s1MhT/XySeLq6GXNvn37U46Ded0TckWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.1.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-discard-duplicates": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-7.0.2.tgz", + "integrity": "sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-discard-empty": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-7.0.1.tgz", + "integrity": "sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-discard-overridden": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-7.0.1.tgz", + "integrity": "sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-merge-longhand": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-7.0.5.tgz", + "integrity": "sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "stylehacks": "^7.0.5" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-merge-rules": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-7.0.6.tgz", + "integrity": "sha512-2jIPT4Tzs8K87tvgCpSukRQ2jjd+hH6Bb8rEEOUDmmhOeTcqDg5fEFK8uKIu+Pvc3//sm3Uu6FRqfyv7YF7+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.1", + "caniuse-api": "^3.0.0", + "cssnano-utils": "^5.0.1", + "postcss-selector-parser": "^7.1.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-minify-font-values": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-7.0.1.tgz", + "integrity": "sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-minify-gradients": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-7.0.1.tgz", + "integrity": "sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "colord": "^2.9.3", + "cssnano-utils": "^5.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-minify-params": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-7.0.4.tgz", + "integrity": "sha512-3OqqUddfH8c2e7M35W6zIwv7jssM/3miF9cbCSb1iJiWvtguQjlxZGIHK9JRmc8XAKmE2PFGtHSM7g/VcW97sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.1", + "cssnano-utils": "^5.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-minify-selectors": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-7.0.5.tgz", + "integrity": "sha512-x2/IvofHcdIrAm9Q+p06ZD1h6FPcQ32WtCRVodJLDR+WMn8EVHI1kvLxZuGKz/9EY5nAmI6lIQIrpo4tBy5+ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "postcss-selector-parser": "^7.1.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-normalize-charset": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-7.0.1.tgz", + "integrity": "sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-normalize-display-values": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-7.0.1.tgz", + "integrity": "sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-normalize-positions": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-7.0.1.tgz", + "integrity": "sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-normalize-repeat-style": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-7.0.1.tgz", + "integrity": "sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-normalize-string": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-7.0.1.tgz", + "integrity": "sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-normalize-timing-functions": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-7.0.1.tgz", + "integrity": "sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-normalize-unicode": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-7.0.4.tgz", + "integrity": "sha512-LvIURTi1sQoZqj8mEIE8R15yvM+OhbR1avynMtI9bUzj5gGKR/gfZFd8O7VMj0QgJaIFzxDwxGl/ASMYAkqO8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-normalize-url": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-7.0.1.tgz", + "integrity": "sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-normalize-whitespace": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-7.0.1.tgz", + "integrity": "sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-ordered-values": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-7.0.2.tgz", + "integrity": "sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssnano-utils": "^5.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-reduce-initial": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-7.0.4.tgz", + "integrity": "sha512-rdIC9IlMBn7zJo6puim58Xd++0HdbvHeHaPgXsimMfG1ijC5A9ULvNLSE0rUKVJOvNMcwewW4Ga21ngyJjY/+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.1", + "caniuse-api": "^3.0.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-reduce-transforms": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-7.0.1.tgz", + "integrity": "sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-selector-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-svgo": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-7.1.0.tgz", + "integrity": "sha512-KnAlfmhtoLz6IuU3Sij2ycusNs4jPW+QoFE5kuuUOK8awR6tMxZQrs5Ey3BUz7nFCzT3eqyFgqkyrHiaU2xx3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0", + "svgo": "^4.0.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >= 18" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-unique-selectors": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-7.0.4.tgz", + "integrity": "sha512-pmlZjsmEAG7cHd7uK3ZiNSW6otSZ13RHuZ/4cDN/bVglS5EpF2r2oxY99SuOHa8m7AWoBCelTS3JPpzsIs8skQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^7.1.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/pretty-bytes": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-7.1.0.tgz", + "integrity": "sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/protocols": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.2.tgz", + "integrity": "sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/radix3": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", + "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dev": true, + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexp-tree": { + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", + "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", + "dev": true, + "license": "MIT", + "bin": { + "regexp-tree": "bin/regexp-tree" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.52.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.1.tgz", + "integrity": "sha512-/vFSi3I+ya/D75UZh5GxLc/6UQ+KoKPEvL9autr1yGcaeWzXBQr1tTXmNDS4FImFCPwBAvVe7j9YzR8PQ5rfqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.52.1", + "@rollup/rollup-android-arm64": "4.52.1", + "@rollup/rollup-darwin-arm64": "4.52.1", + "@rollup/rollup-darwin-x64": "4.52.1", + "@rollup/rollup-freebsd-arm64": "4.52.1", + "@rollup/rollup-freebsd-x64": "4.52.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.1", + "@rollup/rollup-linux-arm-musleabihf": "4.52.1", + "@rollup/rollup-linux-arm64-gnu": "4.52.1", + "@rollup/rollup-linux-arm64-musl": "4.52.1", + "@rollup/rollup-linux-loong64-gnu": "4.52.1", + "@rollup/rollup-linux-ppc64-gnu": "4.52.1", + "@rollup/rollup-linux-riscv64-gnu": "4.52.1", + "@rollup/rollup-linux-riscv64-musl": "4.52.1", + "@rollup/rollup-linux-s390x-gnu": "4.52.1", + "@rollup/rollup-linux-x64-gnu": "4.52.1", + "@rollup/rollup-linux-x64-musl": "4.52.1", + "@rollup/rollup-openharmony-arm64": "4.52.1", + "@rollup/rollup-win32-arm64-msvc": "4.52.1", + "@rollup/rollup-win32-ia32-msvc": "4.52.1", + "@rollup/rollup-win32-x64-gnu": "4.52.1", + "@rollup/rollup-win32-x64-msvc": "4.52.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup-plugin-visualizer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-6.0.3.tgz", + "integrity": "sha512-ZU41GwrkDcCpVoffviuM9Clwjy5fcUxlz0oMoTXTYsK+tcIFzbdacnrr2n8TXcHxbGKKXtOdjxM2HUS4HjkwIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "open": "^8.0.0", + "picomatch": "^4.0.2", + "source-map": "^0.7.4", + "yargs": "^17.5.1" + }, + "bin": { + "rollup-plugin-visualizer": "dist/bin/cli.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "rolldown": "1.x || ^1.0.0-beta", + "rollup": "2.x || 3.x || 4.x" + }, + "peerDependenciesMeta": { + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.93.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.93.1.tgz", + "integrity": "sha512-wLAeLB7IksO2u+cCfhHqcy7/2ZUMPp/X2oV6+LjmweTqgjhOKrkaE/Q1wljxtco5EcOcupZ4c981X0gpk5Tiag==", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/sass-loader": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.5.2.tgz", + "integrity": "sha512-vMUoSNOUKJILHpcNCCyD23X34gve1TS7Rjd9uXHeKqhvBG39x6XbswFDtpbTElj6XdMFezoWhkh5vtKudf2cgQ==", + "license": "MIT", + "dependencies": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "webpack": "^4.36.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/sax": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz", + "integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==", + "dev": true, + "license": "ISC" + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/scule": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz", + "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-placeholder": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/serve-placeholder/-/serve-placeholder-2.0.2.tgz", + "integrity": "sha512-/TMG8SboeiQbZJWRlfTCqMs2DD3SZgWp0kDQePz9yUuCnDfDh/92gf7/PxGhzXTKBIPASIHxFcZndoNbp6QOLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-git": { + "version": "3.28.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.28.0.tgz", + "integrity": "sha512-Rs/vQRwsn1ILH1oBUy8NucJlXmnnLeLCfcvbSehkPzbv3wwoFWIdtfd6Ndo6ZPhlPsCZ60CPI4rxurnwAa+a2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smob": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", + "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", + "dev": true, + "license": "MIT" + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/structured-clone-es": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/structured-clone-es/-/structured-clone-es-1.0.0.tgz", + "integrity": "sha512-FL8EeKFFyNQv5cMnXI31CIMCsFarSVI2bF0U0ImeNE3g/F1IvJQyqzOXxPBRXiwQfyBTlbNe88jh1jFW0O/jiQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/stylehacks": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.6.tgz", + "integrity": "sha512-iitguKivmsueOmTO0wmxURXBP8uqOO+zikLGZ7Mm9e/94R4w5T999Js2taS/KBOnQ/wdC3jN3vNSrkGDrlnqQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.25.1", + "postcss-selector-parser": "^7.1.0" + }, + "engines": { + "node": "^18.12.0 || ^20.9.0 || >=22.0" + }, + "peerDependencies": { + "postcss": "^8.4.32" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svgo": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.0.tgz", + "integrity": "sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^11.1.0", + "css-select": "^5.1.0", + "css-tree": "^3.0.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.1.1", + "sax": "^1.4.1" + }, + "bin": { + "svgo": "bin/svgo.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" + } + }, + "node_modules/system-architecture": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz", + "integrity": "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tapable": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz", + "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tar": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.4.tgz", + "integrity": "sha512-O1z7ajPkjTgEgmTGz0v9X4eqeEXTDREPTO77pVC1Nbs86feBU1Zhdg+edzavPmYW1olxkwsqA2v4uOw6E8LeDg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/terser": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", + "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", + "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "jest-worker": "^27.4.5", + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT", + "peer": true + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT" + }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", + "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-level-regexp": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/type-level-regexp/-/type-level-regexp-0.1.17.tgz", + "integrity": "sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "devOptional": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ultrahtml": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz", + "integrity": "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uncrypto": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", + "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/unctx": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/unctx/-/unctx-2.4.1.tgz", + "integrity": "sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.14.0", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17", + "unplugin": "^2.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.12.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", + "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "license": "MIT", + "peer": true + }, + "node_modules/unenv": { + "version": "2.0.0-rc.21", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.21.tgz", + "integrity": "sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "exsolve": "^1.0.7", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "ufo": "^1.6.1" + } + }, + "node_modules/unhead": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/unhead/-/unhead-2.0.17.tgz", + "integrity": "sha512-xX3PCtxaE80khRZobyWCVxeFF88/Tg9eJDcJWY9us727nsTC7C449B8BUfVBmiF2+3LjPcmqeoB2iuMs0U4oJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hookable": "^5.5.3" + }, + "funding": { + "url": "https://github.com/sponsors/harlan-zw" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/unimport": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/unimport/-/unimport-5.3.0.tgz", + "integrity": "sha512-cty7t1DESgm0OPfCy9oyn5u9B5t0tMW6tH6bXTjAGIO3SkJsbg/DXYHjrPrUKqultqbAAoltAfYsuu/FEDocjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "escape-string-regexp": "^5.0.0", + "estree-walker": "^3.0.3", + "local-pkg": "^1.1.2", + "magic-string": "^0.30.19", + "mlly": "^1.8.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "pkg-types": "^2.3.0", + "scule": "^1.3.0", + "strip-literal": "^3.0.0", + "tinyglobby": "^0.2.15", + "unplugin": "^2.3.10", + "unplugin-utils": "^0.3.0" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unimport/node_modules/unplugin-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.0.tgz", + "integrity": "sha512-JLoggz+PvLVMJo+jZt97hdIIIZ2yTzGgft9e9q8iMrC4ewufl62ekeW7mixBghonn2gVb/ICjyvlmOCUBnJLQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.10.tgz", + "integrity": "sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "acorn": "^8.15.0", + "picomatch": "^4.0.3", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/unplugin-utils": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.2.5.tgz", + "integrity": "sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/unplugin-vue-router": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/unplugin-vue-router/-/unplugin-vue-router-0.15.0.tgz", + "integrity": "sha512-PyGehCjd9Ny9h+Uer4McbBjjib3lHihcyUEILa7pHKl6+rh8N7sFyw4ZkV+N30Oq2zmIUG7iKs3qpL0r+gXAaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue-macros/common": "3.0.0-beta.16", + "@vue/language-core": "^3.0.1", + "ast-walker-scope": "^0.8.1", + "chokidar": "^4.0.3", + "json5": "^2.2.3", + "local-pkg": "^1.1.1", + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "muggle-string": "^0.4.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "scule": "^1.3.0", + "tinyglobby": "^0.2.14", + "unplugin": "^2.3.5", + "unplugin-utils": "^0.2.4", + "yaml": "^2.8.0" + }, + "peerDependencies": { + "@vue/compiler-sfc": "^3.5.17", + "vue-router": "^4.5.1" + }, + "peerDependenciesMeta": { + "vue-router": { + "optional": true + } + } + }, + "node_modules/unstorage": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.17.1.tgz", + "integrity": "sha512-KKGwRTT0iVBCErKemkJCLs7JdxNVfqTPc/85ae1XES0+bsHbc/sFBfVi5kJp156cc51BHinIH2l3k0EZ24vOBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "^3.1.3", + "chokidar": "^4.0.3", + "destr": "^2.0.5", + "h3": "^1.15.4", + "lru-cache": "^10.4.3", + "node-fetch-native": "^1.6.7", + "ofetch": "^1.4.1", + "ufo": "^1.6.1" + }, + "peerDependencies": { + "@azure/app-configuration": "^1.8.0", + "@azure/cosmos": "^4.2.0", + "@azure/data-tables": "^13.3.0", + "@azure/identity": "^4.6.0", + "@azure/keyvault-secrets": "^4.9.0", + "@azure/storage-blob": "^12.26.0", + "@capacitor/preferences": "^6.0.3 || ^7.0.0", + "@deno/kv": ">=0.9.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.34.3", + "@vercel/blob": ">=0.27.1", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1.0.1", + "aws4fetch": "^1.0.20", + "db0": ">=0.2.1", + "idb-keyval": "^6.2.1", + "ioredis": "^5.4.2", + "uploadthing": "^7.4.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/unstorage/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/untun": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/untun/-/untun-0.1.3.tgz", + "integrity": "sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.5", + "consola": "^3.2.3", + "pathe": "^1.1.1" + }, + "bin": { + "untun": "bin/untun.mjs" + } + }, + "node_modules/untun/node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/untyped": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/untyped/-/untyped-2.0.0.tgz", + "integrity": "sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "defu": "^6.1.4", + "jiti": "^2.4.2", + "knitwork": "^1.2.0", + "scule": "^1.3.0" + }, + "bin": { + "untyped": "dist/cli.mjs" + } + }, + "node_modules/unwasm": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/unwasm/-/unwasm-0.3.11.tgz", + "integrity": "sha512-Vhp5gb1tusSQw5of/g3Q697srYgMXvwMgXMjcG4ZNga02fDX9coxJ9fAb0Ci38hM2Hv/U1FXRPGgjP2BYqhNoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "knitwork": "^1.2.0", + "magic-string": "^0.30.17", + "mlly": "^1.7.4", + "pathe": "^2.0.3", + "pkg-types": "^2.2.0", + "unplugin": "^2.3.6" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uqr": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/uqr/-/uqr-0.1.2.tgz", + "integrity": "sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz", + "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-dev-rpc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz", + "integrity": "sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==", + "dev": true, + "license": "MIT", + "dependencies": { + "birpc": "^2.4.0", + "vite-hot-client": "^2.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0" + } + }, + "node_modules/vite-hot-client": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.1.0.tgz", + "integrity": "sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-plugin-checker": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.10.3.tgz", + "integrity": "sha512-f4sekUcDPF+T+GdbbE8idb1i2YplBAoH+SfRS0e/WRBWb2rYb1Jf5Pimll0Rj+3JgIYWwG2K5LtBPCXxoibkLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "chokidar": "^4.0.3", + "npm-run-path": "^6.0.0", + "picocolors": "^1.1.1", + "picomatch": "^4.0.3", + "strip-ansi": "^7.1.0", + "tiny-invariant": "^1.3.3", + "tinyglobby": "^0.2.14", + "vscode-uri": "^3.1.0" + }, + "engines": { + "node": ">=14.16" + }, + "peerDependencies": { + "@biomejs/biome": ">=1.7", + "eslint": ">=7", + "meow": "^13.2.0", + "optionator": "^0.9.4", + "stylelint": ">=16", + "typescript": "*", + "vite": ">=2.0.0", + "vls": "*", + "vti": "*", + "vue-tsc": "~2.2.10 || ^3.0.0" + }, + "peerDependenciesMeta": { + "@biomejs/biome": { + "optional": true + }, + "eslint": { + "optional": true + }, + "meow": { + "optional": true + }, + "optionator": { + "optional": true + }, + "stylelint": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vls": { + "optional": true + }, + "vti": { + "optional": true + }, + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/vite-plugin-checker/node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-checker/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-inspect": { + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.3.3.tgz", + "integrity": "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansis": "^4.1.0", + "debug": "^4.4.1", + "error-stack-parser-es": "^1.0.5", + "ohash": "^2.0.11", + "open": "^10.2.0", + "perfect-debounce": "^2.0.0", + "sirv": "^3.0.1", + "unplugin-utils": "^0.3.0", + "vite-dev-rpc": "^1.1.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + } + } + }, + "node_modules/vite-plugin-inspect/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-inspect/node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vite-plugin-inspect/node_modules/unplugin-utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.0.tgz", + "integrity": "sha512-JLoggz+PvLVMJo+jZt97hdIIIZ2yTzGgft9e9q8iMrC4ewufl62ekeW7mixBghonn2gVb/ICjyvlmOCUBnJLQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, + "node_modules/vite-plugin-vue-tracer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/vite-plugin-vue-tracer/-/vite-plugin-vue-tracer-1.0.0.tgz", + "integrity": "sha512-a+UB9IwGx5uwS4uG/a9kM6fCMnxONDkOTbgCUbhFpiGhqfxrrC1+9BibV7sWwUnwj1Dg6MnRxG0trLgUZslDXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^3.0.3", + "exsolve": "^1.0.7", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "source-map-js": "^1.2.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0", + "vue": "^3.5.0" + } + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz", + "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.21", + "@vue/compiler-sfc": "3.5.21", + "@vue/runtime-dom": "3.5.21", + "@vue/server-renderer": "3.5.21", + "@vue/shared": "3.5.21" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vue-bundle-renderer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vue-bundle-renderer/-/vue-bundle-renderer-2.1.2.tgz", + "integrity": "sha512-M4WRBO/O/7G9phGaGH9AOwOnYtY9ZpPoDVpBpRzR2jO5rFL9mgIlQIgums2ljCTC2HL1jDXFQc//CzWcAQHgAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ufo": "^1.6.1" + } + }, + "node_modules/vue-demi": { + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", + "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, + "node_modules/vue-devtools-stub": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/vue-devtools-stub/-/vue-devtools-stub-0.1.0.tgz", + "integrity": "sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vue-router": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/vue-tsc": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-3.0.8.tgz", + "integrity": "sha512-H9yg/m6ywykmWS+pIAEs65v2FrVm5uOA0a0dHkX6Sx8dNg1a1m4iudt/6eGa9fAenmNHGlLFN9XpWQb8i5sU1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@volar/typescript": "2.4.23", + "@vue/language-core": "3.0.8" + }, + "bin": { + "vue-tsc": "bin/vue-tsc.js" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/vue3-highlightjs": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/vue3-highlightjs/-/vue3-highlightjs-1.0.5.tgz", + "integrity": "sha512-Q4YNPXu0X5VMBnwPVOk+IQf1Ohp9jFdMitEAmzaz8qVVefcQpN6Dx4BnDGKxja3TLDVF+EgL136wC8YzmoCX9w==", + "license": "ISC", + "dependencies": { + "highlight.js": "^10.3.2" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue3-highlightjs/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/vuetify": { + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.10.2.tgz", + "integrity": "sha512-uuAm+uj170ZWdS0aiomhgtdYOR3/I2yzynskBfYyFB1Fn/HKvlvPDaU3ISZnf0GHIJQENdj7eLg/wTChrIwEQw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/johnleider" + }, + "peerDependencies": { + "typescript": ">=4.7", + "vite-plugin-vuetify": ">=2.1.0", + "vue": "^3.5.0", + "webpack-plugin-vuetify": ">=3.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vite-plugin-vuetify": { + "optional": true + }, + "webpack-plugin-vuetify": { + "optional": true + } + } + }, + "node_modules/watchpack": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", + "license": "MIT", + "peer": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/webpack": { + "version": "5.101.3", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz", + "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.24.0", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.17.3", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^4.3.2", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.11", + "watchpack": "^2.4.1", + "webpack-sources": "^3.3.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT", + "peer": true + }, + "node_modules/webpack/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", + "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/youch": { + "version": "4.1.0-beta.11", + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.11.tgz", + "integrity": "sha512-sQi6PERyO/mT8w564ojOVeAlYTtVQmC2GaktQAf+IdI75/GKIggosBuvyVXvEV+FATAT6RbLdIjFoiIId4ozoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@poppinss/dumper": "^0.6.4", + "@speed-highlight/core": "^1.2.7", + "cookie": "^1.0.2", + "youch-core": "^0.3.3" + } + }, + "node_modules/youch-core": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/exception": "^1.2.2", + "error-stack-parser-es": "^1.0.5" + } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/examples/clientsocket/package.json b/examples/clientsocket/package.json new file mode 100644 index 0000000..8a35587 --- /dev/null +++ b/examples/clientsocket/package.json @@ -0,0 +1,27 @@ +{ + "name": "nuxt3-websocket-client", + "private": true, + "type": "module", + "scripts": { + "build": "nuxt build", + "dev": "nuxt dev", + "generate": "nuxt generate", + "preview": "nuxt preview", + "postinstall": "nuxt prepare" + }, + "devDependencies": { + "@nuxt/devtools": "latest", + "nuxt": "^3.8.0", + "vue": "^3.3.8", + "vue-router": "^4.2.5", + "vue-tsc": "^3.0.8" + }, + "dependencies": { + "@mdi/font": "^7.3.67", + "@nuxtjs/vuetify": "^1.12.3", + "highlight.js": "^11.9.0", + "pinia": "^2.1.7", + "vue3-highlightjs": "^1.0.5", + "vuetify": "^3.4.0" + } +} diff --git a/examples/clientsocket/pages/index.vue b/examples/clientsocket/pages/index.vue new file mode 100644 index 0000000..536c44d --- /dev/null +++ b/examples/clientsocket/pages/index.vue @@ -0,0 +1,14 @@ + + + + + diff --git a/examples/clientsocket/plugins/vuetify.js b/examples/clientsocket/plugins/vuetify.js new file mode 100644 index 0000000..a1aa056 --- /dev/null +++ b/examples/clientsocket/plugins/vuetify.js @@ -0,0 +1,15 @@ +import { createVuetify } from 'vuetify' +import * as components from 'vuetify/components' +import * as directives from 'vuetify/directives' + +export default defineNuxtPlugin((nuxtApp) => { + const vuetify = createVuetify({ + components, + directives, + theme: { + defaultTheme: 'light' + } + }) + + nuxtApp.vueApp.use(vuetify) +}) diff --git a/examples/clientsocket/public/favicon.ico b/examples/clientsocket/public/favicon.ico new file mode 100644 index 0000000..18993ad Binary files /dev/null and b/examples/clientsocket/public/favicon.ico differ diff --git a/examples/clientsocket/public/robots.txt b/examples/clientsocket/public/robots.txt new file mode 100644 index 0000000..0ad279c --- /dev/null +++ b/examples/clientsocket/public/robots.txt @@ -0,0 +1,2 @@ +User-Agent: * +Disallow: diff --git a/examples/clientsocket/tsconfig.json b/examples/clientsocket/tsconfig.json new file mode 100644 index 0000000..4b34df1 --- /dev/null +++ b/examples/clientsocket/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "./.nuxt/tsconfig.json" +} diff --git a/examples/clientsocket/types/websocket.ts b/examples/clientsocket/types/websocket.ts new file mode 100644 index 0000000..98720db --- /dev/null +++ b/examples/clientsocket/types/websocket.ts @@ -0,0 +1,182 @@ +export interface WebSocketMessage { + type: string; + data: any; + timestamp?: number; + client_id?: string; + message_id?: string; +} + +export interface ConnectionInfo { + client_id: string; + static_id: string; + ip_address: string; + room: string; + user_id: string; + connected_at: number; + id_type: string; +} + +export interface ClientInfo { + id: string; + static_id: string; + ip_address: string; + user_id: string; + room: string; + connected_at: number; + last_ping: number; + is_active?: boolean; +} + +export interface OnlineUser { + client_id: string; + static_id: string; + user_id: string; + room: string; + ip_address: string; + connected_at: number; + last_ping: number; +} + +export interface ConnectionStats { + connected_clients: number; + unique_ips: number; + static_clients: number; + active_rooms: number; + ip_distribution: Record; + room_distribution: Record; + message_queue_size: number; + queue_workers: number; + uptime: number; + timestamp: number; +} + +export interface SystemHealth { + databases: any; + available_dbs: string[]; + websocket_status: string; + uptime_seconds: number; +} + +export interface PerformanceMetrics { + messages_per_second: number; + average_latency_ms: number; + error_rate_percent: number; + memory_usage_bytes: number; +} + +export interface MonitoringData { + stats: ConnectionStats; + recent_activity: ActivityLog[]; + system_health: SystemHealth; + performance: PerformanceMetrics; +} + +export interface ActivityLog { + timestamp: number; + event: string; + client_id: string; + details: string; +} + +export interface MessageHistory { + timestamp: Date; + type: string; + data: any; + messageId?: string; + size: number; + icon?: string; + timeString?: string; +} + +export interface ConnectionState { + isConnected: boolean; + isConnecting: boolean; + connectionStatus: "disconnected" | "connecting" | "connected" | "error"; + clientId: string | null; + staticId: string | null; + currentRoom: string | null; + userId: string; + ipAddress: string | null; + connectionStartTime: number | null; + lastPingTime: number | null; + connectionLatency: number; + connectionHealth: "poor" | "warning" | "good" | "excellent"; + reconnectAttempts: number; + messagesReceived: number; + messagesSent: number; + uptime: string; +} + +export interface WebSocketConfig { + wsUrl: string; + userId: string; + room: string; + staticId?: string; + useIPBasedId?: boolean; + autoReconnect: boolean; + heartbeatEnabled: boolean; + maxReconnectAttempts: number; + reconnectDelay: number; + maxReconnectDelay: number; + heartbeatInterval: number; + heartbeatTimeout: number; + maxMissedHeartbeats: number; + maxMessages: number; + messageWarningThreshold: number; + actionThrottle: number; +} + +export type MessageType = + | "welcome" + | "broadcast" + | "direct_message" + | "room_message" + | "ping" + | "pong" + | "heartbeat" + | "heartbeat_ack" + | "connection_test" + | "connection_test_result" + | "get_online_users" + | "online_users" + | "get_server_info" + | "server_info" + | "error" + | "message_received" + | "broadcast_sent" + | "direct_message_sent" + | "room_message_sent" + | "db_insert" + | "db_query" + | "db_custom_query" + | "query_result" + | "admin_kick_client" + | "admin_kill_server" + | "get_server_stats" + | "get_system_health" + | "admin_clear_logs" + | "get_stats" + | "get_room_info" + | "join_room" + | "leave_room" + | "database_change" + | "data_stream" + | "server_heartbeat" + | "system_status" + | "clients_by_ip" + | "client_info" + | "get_clients_by_ip" + | "get_client_info" + | "health_check" + | "database_list" + | "connection_stats" + | "trigger_notification" + | "notification_sent" + | "API_TEST" + | "manual_test" + | "retribusi_created" + | "retribusi_updated" + | "retribusi_deleted" + | "peserta_changes" + | "retribusi_changes" + | "system_changes"; diff --git a/go.mod b/go.mod index 6477df7..9dfc2b9 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/gin-contrib/cors v1.7.6 github.com/go-playground/validator/v10 v10.27.0 github.com/go-redis/redis_rate/v10 v10.0.1 + github.com/gorilla/websocket v1.5.1 github.com/jmoiron/sqlx v1.4.0 github.com/joho/godotenv v1.5.1 github.com/lib/pq v1.10.9 diff --git a/go.sum b/go.sum index 4fc4172..5725319 100644 --- a/go.sum +++ b/go.sum @@ -106,6 +106,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= diff --git a/internal/config/config.go b/internal/config/config.go index 0c6c7c5..94e5152 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -26,6 +26,7 @@ type Config struct { Bpjs BpjsConfig SatuSehat SatuSehatConfig Swagger SwaggerConfig + WebSocket WebSocketConfig // Tambahkan ini Security SecurityConfig Validator *validator.Validate } @@ -128,6 +129,38 @@ type SatuSehatConfig struct { Timeout time.Duration `json:"timeout"` } +type WebSocketConfig struct { + // Timeout configurations + ReadTimeout time.Duration `json:"read_timeout"` + WriteTimeout time.Duration `json:"write_timeout"` + PingInterval time.Duration `json:"ping_interval"` + PongTimeout time.Duration `json:"pong_timeout"` + HandshakeTimeout time.Duration `json:"handshake_timeout"` + + // Buffer sizes + ReadBufferSize int `json:"read_buffer_size"` + WriteBufferSize int `json:"write_buffer_size"` + ChannelBufferSize int `json:"channel_buffer_size"` + MessageQueueSize int `json:"message_queue_size"` + + // Connection limits + MaxMessageSize int `json:"max_message_size"` + QueueWorkers int `json:"queue_workers"` + + // Monitoring + ActivityLogSize int `json:"activity_log_size"` + CleanupInterval time.Duration `json:"cleanup_interval"` + InactiveTimeout time.Duration `json:"inactive_timeout"` + + // Server info + ServerID string `json:"server_id"` + + // Features + EnableCompression bool `json:"enable_compression"` + EnableMetrics bool `json:"enable_metrics"` + EnableMonitoring bool `json:"enable_monitoring"` +} + // SecurityConfig berisi semua pengaturan untuk middleware keamanan type SecurityConfig struct { // CORS @@ -222,6 +255,38 @@ func LoadConfig() *Config { KFAURL: getEnv("BRIDGING_SATUSEHAT_KFA_URL", "https://api-satusehat.kemkes.go.id/kfa-v2"), Timeout: parseDuration(getEnv("BRIDGING_SATUSEHAT_TIMEOUT", "30s")), }, + // Configuration for WebSocket server + WebSocket: WebSocketConfig{ // Tambahkan ini + // Timeout configurations + ReadTimeout: parseDuration(getEnv("WS_READ_TIMEOUT", "300s")), + WriteTimeout: parseDuration(getEnv("WS_WRITE_TIMEOUT", "30s")), + PingInterval: parseDuration(getEnv("WS_PING_INTERVAL", "60s")), + PongTimeout: parseDuration(getEnv("WS_PONG_TIMEOUT", "70s")), + HandshakeTimeout: parseDuration(getEnv("WS_HANDSHAKE_TIMEOUT", "45s")), + + // Buffer sizes + ReadBufferSize: getEnvAsInt("WS_READ_BUFFER_SIZE", 8192), + WriteBufferSize: getEnvAsInt("WS_WRITE_BUFFER_SIZE", 8192), + ChannelBufferSize: getEnvAsInt("WS_CHANNEL_BUFFER_SIZE", 512), + MessageQueueSize: getEnvAsInt("WS_MESSAGE_QUEUE_SIZE", 5000), + + // Connection limits + MaxMessageSize: getEnvAsInt("WS_MAX_MESSAGE_SIZE", 8192), + QueueWorkers: getEnvAsInt("WS_QUEUE_WORKERS", 10), + + // Monitoring + ActivityLogSize: getEnvAsInt("WS_ACTIVITY_LOG_SIZE", 1000), + CleanupInterval: parseDuration(getEnv("WS_CLEANUP_INTERVAL", "2m")), + InactiveTimeout: parseDuration(getEnv("WS_INACTIVE_TIMEOUT", "5m")), + + // Server info + ServerID: getEnv("WS_SERVER_ID", "api-service-v1"), + + // Features + EnableCompression: getEnvAsBool("WS_ENABLE_COMPRESSION", true), + EnableMetrics: getEnvAsBool("WS_ENABLE_METRICS", true), + EnableMonitoring: getEnvAsBool("WS_ENABLE_MONITORING", true), + }, Swagger: SwaggerConfig{ Title: getEnv("SWAGGER_TITLE", "SERVICE API"), Description: getEnv("SWAGGER_DESCRIPTION", "CUSTUM SERVICE API"), @@ -1314,5 +1379,194 @@ func (c *Config) Validate() error { if len(errs) > 0 { return fmt.Errorf("configuration validation failed: %s", strings.Join(errs, "; ")) } + + // Validate WebSocket configuration + if c.WebSocket.ReadTimeout <= 0 { + log.Fatal("WebSocket Read Timeout must be greater than 0") + } + if c.WebSocket.WriteTimeout <= 0 { + log.Fatal("WebSocket Write Timeout must be greater than 0") + } + if c.WebSocket.PingInterval <= 0 { + log.Fatal("WebSocket Ping Interval must be greater than 0") + } + if c.WebSocket.PongTimeout <= 0 { + log.Fatal("WebSocket Pong Timeout must be greater than 0") + } + if c.WebSocket.ReadBufferSize <= 0 { + log.Fatal("WebSocket Read Buffer Size must be greater than 0") + } + if c.WebSocket.WriteBufferSize <= 0 { + log.Fatal("WebSocket Write Buffer Size must be greater than 0") + } + if c.WebSocket.ChannelBufferSize <= 0 { + log.Fatal("WebSocket Channel Buffer Size must be greater than 0") + } + if c.WebSocket.MessageQueueSize <= 0 { + log.Fatal("WebSocket Message Queue Size must be greater than 0") + } + if c.WebSocket.MaxMessageSize <= 0 { + log.Fatal("WebSocket Max Message Size must be greater than 0") + } + if c.WebSocket.QueueWorkers <= 0 { + log.Fatal("WebSocket Queue Workers must be greater than 0") + } + if c.WebSocket.ActivityLogSize <= 0 { + log.Fatal("WebSocket Activity Log Size must be greater than 0") + } + if c.WebSocket.CleanupInterval <= 0 { + log.Fatal("WebSocket Cleanup Interval must be greater than 0") + } + if c.WebSocket.InactiveTimeout <= 0 { + log.Fatal("WebSocket Inactive Timeout must be greater than 0") + } + if c.WebSocket.ServerID == "" { + log.Fatal("WebSocket Server ID is required") + } + return nil } + +//** WebSocket **// + +// DefaultWebSocketConfig mengembalikan konfigurasi default untuk WebSocket +func DefaultWebSocketConfig() WebSocketConfig { + return WebSocketConfig{ + // Timeout configurations + ReadTimeout: 300 * time.Second, + WriteTimeout: 30 * time.Second, + PingInterval: 60 * time.Second, + PongTimeout: 70 * time.Second, + HandshakeTimeout: 45 * time.Second, + + // Buffer sizes + ReadBufferSize: 8192, + WriteBufferSize: 8192, + ChannelBufferSize: 512, + MessageQueueSize: 5000, + + // Connection limits + MaxMessageSize: 8192, + QueueWorkers: 10, + + // Monitoring + ActivityLogSize: 1000, + CleanupInterval: 2 * time.Minute, + InactiveTimeout: 5 * time.Minute, + + // Server info + ServerID: "api-service-v1", + + // Features + EnableCompression: true, + EnableMetrics: true, + EnableMonitoring: true, + } +} + +// HighPerformanceWebSocketConfig mengembalikan konfigurasi untuk performa tinggi +func HighPerformanceWebSocketConfig() WebSocketConfig { + return WebSocketConfig{ + // Timeout configurations + ReadTimeout: 300 * time.Second, + WriteTimeout: 30 * time.Second, + PingInterval: 30 * time.Second, + PongTimeout: 40 * time.Second, + HandshakeTimeout: 30 * time.Second, + + // Buffer sizes + ReadBufferSize: 16384, + WriteBufferSize: 16384, + ChannelBufferSize: 1024, + MessageQueueSize: 10000, + + // Connection limits + MaxMessageSize: 16384, + QueueWorkers: 20, + + // Monitoring + ActivityLogSize: 2000, + CleanupInterval: 1 * time.Minute, + InactiveTimeout: 3 * time.Minute, + + // Server info + ServerID: "api-service-hp", + + // Features + EnableCompression: true, + EnableMetrics: true, + EnableMonitoring: true, + } +} + +// LowResourceWebSocketConfig mengembalikan konfigurasi untuk sumber daya terbatas +func LowResourceWebSocketConfig() WebSocketConfig { + return WebSocketConfig{ + // Timeout configurations + ReadTimeout: 300 * time.Second, + WriteTimeout: 30 * time.Second, + PingInterval: 120 * time.Second, + PongTimeout: 130 * time.Second, + HandshakeTimeout: 60 * time.Second, + + // Buffer sizes + ReadBufferSize: 4096, + WriteBufferSize: 4096, + ChannelBufferSize: 256, + MessageQueueSize: 2500, + + // Connection limits + MaxMessageSize: 4096, + QueueWorkers: 5, + + // Monitoring + ActivityLogSize: 500, + CleanupInterval: 5 * time.Minute, + InactiveTimeout: 10 * time.Minute, + + // Server info + ServerID: "api-service-lr", + + // Features + EnableCompression: false, + EnableMetrics: false, + EnableMonitoring: true, + } +} + +// CustomWebSocketConfig memungkinkan kustomisasi konfigurasi +func CustomWebSocketConfig( + readTimeout, writeTimeout, pingInterval, pongTimeout time.Duration, + readBufferSize, writeBufferSize, channelBufferSize, messageQueueSize int, + maxMessageSize, queueWorkers int, + activityLogSize int, + cleanupInterval, inactiveTimeout time.Duration, + serverID string, + enableCompression, enableMetrics, enableMonitoring bool, +) WebSocketConfig { + return WebSocketConfig{ + ReadTimeout: readTimeout, + WriteTimeout: writeTimeout, + PingInterval: pingInterval, + PongTimeout: pongTimeout, + HandshakeTimeout: 45 * time.Second, + + ReadBufferSize: readBufferSize, + WriteBufferSize: writeBufferSize, + ChannelBufferSize: channelBufferSize, + MessageQueueSize: messageQueueSize, + + MaxMessageSize: maxMessageSize, + QueueWorkers: queueWorkers, + + ActivityLogSize: activityLogSize, + CleanupInterval: cleanupInterval, + InactiveTimeout: inactiveTimeout, + + ServerID: serverID, + + EnableCompression: enableCompression, + EnableMetrics: enableMetrics, + EnableMonitoring: enableMonitoring, + } +} diff --git a/internal/handlers/patient/ms_patient.go b/internal/handlers/patient/ms_patient.go index e60093e..2ac6ca0 100644 --- a/internal/handlers/patient/ms_patient.go +++ b/internal/handlers/patient/ms_patient.go @@ -6,13 +6,13 @@ import ( models "api-service/internal/models" patientModels "api-service/internal/models/patient" queryUtils "api-service/internal/utils/query" - "api-service/internal/utils/validation" "api-service/pkg/logger" "context" "database/sql" "fmt" "net/http" "strconv" + "strings" "sync" "time" @@ -36,7 +36,7 @@ func init() { patientonce.Do(func() { patientdb = database.New(config.LoadConfig()) patientvalidate = validator.New() - patientvalidate.RegisterValidation("ms_patient_status", validatePatientStatus) + patientvalidate.RegisterValidation("patient_status", validatePatientStatus) if patientdb == nil { logger.Fatal("Failed to initialize database connection") } @@ -63,12 +63,12 @@ func (e *CacheEntry) IsExpired() bool { return time.Now().After(e.ExpiresAt) } -// PatientInMemoryCache implements a simple in-memory cache with TTL +// InMemoryCache implements a simple in-memory cache with TTL type InMemoryCache struct { items sync.Map } -// NewPatientInMemoryCache creates a new in-memory cache +// NewInMemoryCache creates a new in-memory cache func NewInMemoryCache() *InMemoryCache { return &InMemoryCache{} } @@ -91,11 +91,10 @@ func (c *InMemoryCache) Get(key string) (interface{}, bool) { // Set stores an item in the cache with a TTL func (c *InMemoryCache) Set(key string, value interface{}, ttl time.Duration) { - entry := &CacheEntry{ + c.items.Store(key, &CacheEntry{ Data: value, ExpiresAt: time.Now().Add(ttl), - } - c.items.Store(key, entry) + }) } // Delete removes an item from the cache @@ -106,7 +105,7 @@ func (c *InMemoryCache) Delete(key string) { // DeleteByPrefix removes all items with a specific prefix func (c *InMemoryCache) DeleteByPrefix(prefix string) { c.items.Range(func(key, value interface{}) bool { - if keyStr, ok := key.(string); ok && len(keyStr) >= len(prefix) && keyStr[:len(prefix)] == prefix { + if keyStr, ok := key.(string); ok && strings.HasPrefix(keyStr, prefix) { c.items.Delete(key) } return true @@ -121,7 +120,6 @@ func (c *InMemoryCache) DeleteByPrefix(prefix string) { type PatientHandler struct { db database.Service queryBuilder *queryUtils.QueryBuilder - validator *validation.DynamicValidator cache *InMemoryCache } @@ -134,39 +132,43 @@ func NewPatientHandler() *PatientHandler { "birth_date", "address", "active", "fk_sd_provinsi_id", "fk_sd_kabupaten_kota_id", "fk_sd_kecamatan_id", "fk_sd_kelurahan_id", "ds_sd_provinsi", "ds_sd_kabupaten_kota", "ds_sd_kecamatan", "ds_sd_kelurahan", + // Attachment fields + "id", "name", "file_name", "directory", "active", "fk_ms_patient_id", + // Payment type fields + "id", "name", "number", "active", "fk_ms_patient_id", "fk_ref_payment_type", }) return &PatientHandler{ db: patientdb, queryBuilder: queryBuilder, - validator: validation.NewDynamicValidator(queryBuilder), cache: NewInMemoryCache(), } } // ============================================================================= -// HANDLER ENDPOINTS +// HANDLER ENDPOINTS (READ-ONLY) // ============================================================================= // GetPatient godoc -// @Summary Get patient with pagination and optional aggregation -// @Description Returns a paginated list of patients with optional summary statistics +// @Summary Get Patients List +// @Description Get list of patients with pagination and filters, including their attachments and payment types. // @Tags Patient // @Accept json // @Produce json // @Param limit query int false "Limit (max 100)" default(10) // @Param offset query int false "Offset" default(0) +// @Param active query string false "Filter by active status (true/false)" +// @Param search query string false "Search in medical_record_number, name, or phone_number" // @Param include_summary query bool false "Include aggregation summary" default(false) -// @Param status query string false "Filter by status" -// @Param search query string false "Search in multiple fields" // @Success 200 {object} patientModels.PatientGetResponse "Success response" // @Failure 400 {object} models.ErrorResponse "Bad request" // @Failure 500 {object} models.ErrorResponse "Internal server error" // @Router /patient [get] -func (h *PatientHandler) GetMs_patient(c *gin.Context) { +func (h *PatientHandler) GetPatient(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 120*time.Second) defer cancel() + // Build base query with LEFT JOINs to fetch related data in a single trip query := queryUtils.DynamicQuery{ From: "master.ms_patient", Aliases: "mp", @@ -187,20 +189,49 @@ func (h *PatientHandler) GetMs_patient(c *gin.Context) { {Expression: "mp.ds_sd_kabupaten_kota", Alias: "ds_sd_kabupaten_kota"}, {Expression: "mp.ds_sd_kecamatan", Alias: "ds_sd_kecamatan"}, {Expression: "mp.ds_sd_kelurahan", Alias: "ds_sd_kelurahan"}, + // Attachment fields + {Expression: "ma.id", Alias: "attachment_id"}, + {Expression: "ma.name", Alias: "attachment_name"}, + {Expression: "ma.file_name", Alias: "attachment_file_name"}, + {Expression: "ma.directory", Alias: "attachment_directory"}, + {Expression: "ma.active", Alias: "attachment_active"}, + // Payment type fields + {Expression: "mpt.id", Alias: "payment_type_id"}, + {Expression: "mpt.name", Alias: "payment_type_name"}, + {Expression: "mpt.number", Alias: "payment_type_number"}, + {Expression: "mpt.active", Alias: "payment_type_active"}, }, Sort: []queryUtils.SortField{ - {Column: "mp.id", Order: "ASC"}, + {Column: "mp.name", Order: "ASC"}, + }, + Joins: []queryUtils.Join{ + { + Type: "LEFT", + Table: "master.ms_patient_attachment", + Alias: "ma", + OnConditions: queryUtils.FilterGroup{ + Filters: []queryUtils.DynamicFilter{ + {Column: "ma.fk_ms_patient_id", Operator: queryUtils.OpEqual, Value: "mp.id"}, + }, + }, + }, + { + Type: "LEFT", + Table: "master.ms_patient_payment_type", + Alias: "mpt", + OnConditions: queryUtils.FilterGroup{ + Filters: []queryUtils.DynamicFilter{ + {Column: "mpt.fk_ms_patient_id", Operator: queryUtils.OpEqual, Value: "mp.id"}, + }, + }, + }, }, } // Parse pagination - if limit, err := strconv.Atoi(c.DefaultQuery("limit", "10")); err == nil && limit > 0 && limit <= 100 { - query.Limit = limit - } - if offset, err := strconv.Atoi(c.DefaultQuery("offset", "0")); err == nil && offset >= 0 { - query.Offset = offset - } + h.parsePagination(c, &query) + // Get database connection dbConn, err := h.db.GetSQLXDB("db_antrean") if err != nil { h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError) @@ -209,8 +240,13 @@ func (h *PatientHandler) GetMs_patient(c *gin.Context) { // Parse filters var filters []queryUtils.DynamicFilter - if active := c.Query("active"); active != "" && models.IsValidStatus(active) { - filters = append(filters, queryUtils.DynamicFilter{Column: "active", Operator: queryUtils.OpEqual, Value: active}) + if active := c.Query("active"); active != "" { + if b, err := strconv.ParseBool(active); err == nil { + filters = append(filters, queryUtils.DynamicFilter{Column: "mp.active", Operator: queryUtils.OpEqual, Value: b}) + } else { + h.respondError(c, "Invalid 'active' value; must be true or false", fmt.Errorf("invalid active value: %s", active), http.StatusBadRequest) + return + } } // Handle search with caching @@ -224,9 +260,9 @@ func (h *PatientHandler) GetMs_patient(c *gin.Context) { search = search[:50] } - cacheKey = fmt.Sprintf("ms_patient:search:%s:%d:%d", search, query.Limit, query.Offset) + cacheKey = fmt.Sprintf("patient:search:%s:%d:%d", search, query.Limit, query.Offset) searchFilters = []queryUtils.DynamicFilter{ - {Column: "mp.medical_record_number", Operator: queryUtils.OpEqual, Value: "" + search + ""}, + {Column: "mp.medical_record_number", Operator: queryUtils.OpILike, Value: "%" + search + "%"}, {Column: "mp.name", Operator: queryUtils.OpILike, Value: "%" + search + "%"}, {Column: "mp.phone_number", Operator: queryUtils.OpILike, Value: "%" + search + "%"}, } @@ -252,7 +288,7 @@ func (h *PatientHandler) GetMs_patient(c *gin.Context) { meta := h.calculateMeta(query.Limit, query.Offset, len(patients)) response := patientModels.PatientGetResponse{ - Message: "Data ms_patient berhasil diambil (dari cache)", + Message: "Data patient berhasil diambil (dari cache)", Data: patients, Meta: meta, } @@ -282,20 +318,20 @@ func (h *PatientHandler) GetMs_patient(c *gin.Context) { return } - // Process results - ms_patient := h.processQueryResults(results) + // Process results into structured format + patients := h.processQueryResults(results) - // Get total count + // Get total count for pagination metadata total, err := h.getTotalCount(ctx, dbConn, query) if err != nil { h.logAndRespondError(c, "Failed to get total count", err, http.StatusInternalServerError) return } - // Cache results if search - if useCache && len(ms_patient) > 0 { - h.cache.Set(cacheKey, ms_patient, 15*time.Minute) - logger.Info("Cached search results", map[string]interface{}{"search": search, "count": len(ms_patient)}) + // Cache results if this was a search query + if useCache && len(patients) > 0 { + h.cache.Set(cacheKey, patients, 15*time.Minute) + logger.Info("Cached search results", map[string]interface{}{"search": search, "count": len(patients)}) } // Get aggregate data if requested @@ -308,11 +344,11 @@ func (h *PatientHandler) GetMs_patient(c *gin.Context) { } } - // Build response + // Build and send response meta := h.calculateMeta(query.Limit, query.Offset, total) response := patientModels.PatientGetResponse{ - Message: "Data ms_patient berhasil diambil", - Data: ms_patient, + Message: "Data patient berhasil diambil", + Data: patients, Meta: meta, } @@ -324,8 +360,8 @@ func (h *PatientHandler) GetMs_patient(c *gin.Context) { } // GetPatientByIDPost godoc -// @Summary Get patient by ID using POST request -// @Description Retrieves a patient record by ID using POST request +// @Summary Get Patient by ID +// @Description Retrieves a single patient record by its ID, including attachments and payment types. // @Tags Patient // @Accept json // @Produce json @@ -335,111 +371,13 @@ func (h *PatientHandler) GetMs_patient(c *gin.Context) { // @Failure 404 {object} models.ErrorResponse "Patient not found" // @Failure 500 {object} models.ErrorResponse "Internal server error" // @Router /patient/detail [post] -// func (h *PatientHandler) GetPatientByIDPost(c *gin.Context) { -// var req patientModels.PatientPostRequest -// if err := c.ShouldBindJSON(&req); err != nil { -// h.respondError(c, "Invalid request body", err, http.StatusBadRequest) -// return -// } - -// if err := patientvalidate.Struct(&req); err != nil { -// h.respondError(c, "Validation failed", err, http.StatusBadRequest) -// return -// } - -// dbConn, err := h.db.GetSQLXDB("db_antrean") -// if err != nil { -// h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError) -// return -// } - -// ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second) -// defer cancel() - -// // Check cache first -// cacheKey := fmt.Sprintf("patient:id:%d", req.ID) -// if cachedData, found := h.cache.Get(cacheKey); found { -// if patient, ok := cachedData.(*patientModels.Patient); ok { -// response := patientModels.PatientCreateResponse{ -// Message: "patient details retrieved successfully (dari cache)", -// Data: patient, -// } -// c.JSON(http.StatusOK, response) -// return -// } -// } - -// query := queryUtils.DynamicQuery{ -// From: "master.ms_patient", -// Aliases: "mp", -// Fields: []queryUtils.SelectField{ -// {Expression: "mp.id", Alias: "id"}, -// {Expression: "mp.name", Alias: "name"}, -// {Expression: "mp.medical_record_number", Alias: "medical_record_number"}, -// {Expression: "mp.phone_number", Alias: "phone_number"}, -// {Expression: "mp.gender", Alias: "gender"}, -// {Expression: "mp.birth_date", Alias: "birth_date"}, -// {Expression: "mp.address", Alias: "address"}, -// {Expression: "mp.active", Alias: "active"}, -// {Expression: "mp.fk_sd_provinsi_id", Alias: "fk_sd_provinsi_id"}, -// {Expression: "mp.fk_sd_kabupaten_kota_id", Alias: "fk_sd_kabupaten_kota_id"}, -// {Expression: "mp.fk_sd_kecamatan_id", Alias: "fk_sd_kecamatan_id"}, -// {Expression: "mp.fk_sd_kelurahan_id", Alias: "fk_sd_kelurahan_id"}, -// {Expression: "mp.ds_sd_provinsi", Alias: "ds_sd_provinsi"}, -// {Expression: "mp.ds_sd_kabupaten_kota", Alias: "ds_sd_kabupaten_kota"}, -// {Expression: "mp.ds_sd_kecamatan", Alias: "ds_sd_kecamatan"}, -// {Expression: "mp.ds_sd_kelurahan", Alias: "ds_sd_kelurahan"}, -// }, -// Filters: []queryUtils.FilterGroup{{ -// Filters: []queryUtils.DynamicFilter{ -// {Column: "mp.id", Operator: queryUtils.OpEqual, Value: req.ID}, -// }, -// LogicOp: "AND", -// }}, -// Limit: 1, -// } - -// var patient patientModels.Patient -// err = h.queryBuilder.ExecuteQueryRow(ctx, dbConn, query, &patient) -// if err != nil { -// if err.Error() == "sql: no rows in result set" { -// h.respondError(c, "Patient not found", err, http.StatusNotFound) -// } else { -// h.logAndRespondError(c, "Failed to get patient", err, http.StatusInternalServerError) -// } -// return -// } - -// // Cache the result -// h.cache.Set(cacheKey, &patient, 30*time.Minute) - -// response := patientModels.PatientCreateResponse{ -// Message: "patient details retrieved successfully", -// Data: &patient, -// } - -// c.JSON(http.StatusOK, response) -// } - -// CreatePatient godoc -// @Summary Create patient -// @Description Creates a new patient record -// @Tags Patient -// @Accept json -// @Produce json -// @Param request body patientModels.PatientCreateRequest true "Patient creation request" -// @Success 201 {object} patientModels.PatientCreateResponse "Patient created successfully" -// @Failure 400 {object} models.ErrorResponse "Bad request or validation error" -// @Failure 500 {object} models.ErrorResponse "Internal server error" -// @Router /patient [post] -func (h *PatientHandler) CreateMs_patient(c *gin.Context) { - var req patientModels.PatientCreateRequest +func (h *PatientHandler) GetPatientByIDPost(c *gin.Context) { + var req patientModels.PatientPostRequest if err := c.ShouldBindJSON(&req); err != nil { h.respondError(c, "Invalid request body", err, http.StatusBadRequest) return } - // Validate request if err := patientvalidate.Struct(&req); err != nil { h.respondError(c, "Validation failed", err, http.StatusBadRequest) return @@ -454,650 +392,221 @@ func (h *PatientHandler) CreateMs_patient(c *gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second) defer cancel() - // Validate id must be unique - if req.ID != nil { - rule := validation.NewUniqueFieldRule( - "master.ms_patient", - "id", - queryUtils.DynamicFilter{ - Column: "active", - Operator: queryUtils.OpNotEqual, - Value: false, - }, - ) - - dataToValidate := map[string]interface{}{"id": *req.ID} - isDuplicate, err := h.validator.Validate(ctx, dbConn, rule, dataToValidate) - if err != nil { - h.logAndRespondError(c, "Failed to validate id", err, http.StatusInternalServerError) - return - } - - if isDuplicate { - h.respondError(c, "id already exists", fmt.Errorf("duplicate id: %d", *req.ID), http.StatusConflict) + // Check cache first for fast lookups + cacheKey := fmt.Sprintf("patient:medical_record_number:%s", req.MedicalRecordNumber) + if cachedData, found := h.cache.Get(cacheKey); found { + if patient, ok := cachedData.(*patientModels.Patient); ok { + response := patientModels.PatientCreateResponse{ + Message: "Patient details retrieved successfully (dari cache)", + Data: patient, + } + c.JSON(http.StatusOK, response) return } } - // Validate medical_record_number must be unique - if req.MedicalRecordNumber != nil { - rule := validation.NewUniqueFieldRule( - "master.ms_patient", - "medical_record_number", - queryUtils.DynamicFilter{ - Column: "active", - Operator: queryUtils.OpNotEqual, - Value: false, - }, - ) - - dataToValidate := map[string]interface{}{"medical_record_number": req.MedicalRecordNumber} - isDuplicate, err := h.validator.Validate(ctx, dbConn, rule, dataToValidate) - if err != nil { - h.logAndRespondError(c, "Failed to validate medical_record_number", err, http.StatusInternalServerError) - return - } - - if isDuplicate { - h.respondError(c, "medical_record_number already exists", fmt.Errorf("duplicate medical_record_number: %s", *req.MedicalRecordNumber), http.StatusConflict) - return - } - } - - data := queryUtils.InsertData{ - Columns: []string{ - "name", "medical_record_number", "phone_number", "gender", - "birth_date", "address", "active", "fk_sd_provinsi_id", - "fk_sd_kabupaten_kota_id", "fk_sd_kecamatan_id", "fk_sd_kelurahan_id", - "ds_sd_provinsi", "ds_sd_kabupaten_kota", "ds_sd_kecamatan", "ds_sd_kelurahan", + // Build query with joins to get all related data in one go + query := queryUtils.DynamicQuery{ + From: "master.ms_patient", + Aliases: "mp", + Fields: []queryUtils.SelectField{ + {Expression: "mp.id", Alias: "id"}, + {Expression: "mp.name", Alias: "name"}, + {Expression: "mp.medical_record_number", Alias: "medical_record_number"}, + {Expression: "mp.phone_number", Alias: "phone_number"}, + {Expression: "mp.gender", Alias: "gender"}, + {Expression: "mp.birth_date", Alias: "birth_date"}, + {Expression: "mp.address", Alias: "address"}, + {Expression: "mp.active", Alias: "active"}, + {Expression: "mp.fk_sd_provinsi_id", Alias: "fk_sd_provinsi_id"}, + {Expression: "mp.fk_sd_kabupaten_kota_id", Alias: "fk_sd_kabupaten_kota_id"}, + {Expression: "mp.fk_sd_kecamatan_id", Alias: "fk_sd_kecamatan_id"}, + {Expression: "mp.fk_sd_kelurahan_id", Alias: "fk_sd_kelurahan_id"}, + {Expression: "mp.ds_sd_provinsi", Alias: "ds_sd_provinsi"}, + {Expression: "mp.ds_sd_kabupaten_kota", Alias: "ds_sd_kabupaten_kota"}, + {Expression: "mp.ds_sd_kecamatan", Alias: "ds_sd_kecamatan"}, + {Expression: "mp.ds_sd_kelurahan", Alias: "ds_sd_kelurahan"}, + // Attachment fields + {Expression: "ma.id", Alias: "attachment_id"}, + {Expression: "ma.name", Alias: "attachment_name"}, + {Expression: "ma.file_name", Alias: "attachment_file_name"}, + {Expression: "ma.directory", Alias: "attachment_directory"}, + {Expression: "ma.active", Alias: "attachment_active"}, + // Payment type fields + {Expression: "mpt.id", Alias: "payment_type_id"}, + {Expression: "mpt.name", Alias: "payment_type_name"}, + {Expression: "mpt.number", Alias: "payment_type_number"}, + {Expression: "mpt.active", Alias: "payment_type_active"}, }, - Values: []interface{}{ - req.Name, req.MedicalRecordNumber, req.PhoneNumber, req.Gender, - req.BirthDate, req.Address, req.Active, req.FKSdProvinsiID, - req.FKSdKabupatenKotaID, req.FKSdKecamatanID, req.FKSdKelurahanID, - req.DsSdProvinsi, req.DsSdKabupatenKota, req.DsSdKecamatan, req.DsSdKelurahan, - }, - } - returningCols := []string{ - "id", "name", "medical_record_number", "phone_number", "gender", - "birth_date", "address", "active", "fk_sd_provinsi_id", - "fk_sd_kabupaten_kota_id", "fk_sd_kecamatan_id", "fk_sd_kelurahan_id", - "ds_sd_provinsi", "ds_sd_kabupaten_kota", "ds_sd_kecamatan", "ds_sd_kelurahan", - } - - sql, args, err := h.queryBuilder.BuildInsertQuery("master.ms_patient", data, returningCols...) - if err != nil { - h.logAndRespondError(c, "Failed to build insert query", err, http.StatusInternalServerError) - return - } - - var dataMs_patient patientModels.Patient - err = dbConn.GetContext(ctx, &dataMs_patient, sql, args...) - if err != nil { - h.logAndRespondError(c, "Failed to create patient", err, http.StatusInternalServerError) - return - } - - h.invalidateRelatedCache() - response := patientModels.PatientCreateResponse{ - Message: "Patient berhasil dibuat", - Data: &dataMs_patient, - } - - c.JSON(http.StatusCreated, response) -} - -// UpdatePatient godoc -// @Summary Update patient -// @Description Updates an existing patient record -// @Tags Patient -// @Accept json -// @Produce json -// @Param id path string true "Medical Record Number" -// @Param request body patientModels.PatientUpdateRequest true "Patient update request" -// @Success 200 {object} patientModels.PatientUpdateResponse "Patient updated successfully" -// @Failure 400 {object} models.ErrorResponse "Bad request or validation error" -// @Failure 404 {object} models.ErrorResponse "Patient not found" -// @Failure 500 {object} models.ErrorResponse "Internal server error" -// @Router /patient/{id} [put] -func (h *PatientHandler) UpdateMs_patient(c *gin.Context) { - medical_record_number := c.Param("medical_record_number") - if medical_record_number == "" { - h.respondError(c, "Invalid medical_record_number format", fmt.Errorf("medical_record_number cannot be empty"), http.StatusBadRequest) - return - } - - var req patientModels.PatientUpdateRequest - if err := c.ShouldBindJSON(&req); err != nil { - h.respondError(c, "Invalid request body", err, http.StatusBadRequest) - return - } - - // Set medical_record_number from path parameter (string) - req.MedicalRecordNumber = &medical_record_number - - // Validate request - if err := patientvalidate.Struct(&req); err != nil { - h.respondError(c, "Validation failed", err, http.StatusBadRequest) - return - } - - // Get old data for cache invalidation - var oldData patientModels.Patient - dbConn, err := h.db.GetSQLXDB("db_antrean") - if err == nil { - ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second) - defer cancel() - - query := queryUtils.DynamicQuery{ - From: "master.ms_patient", - Aliases: "mp", - Fields: []queryUtils.SelectField{{Expression: "*"}}, - Filters: []queryUtils.FilterGroup{{ - Filters: []queryUtils.DynamicFilter{ - {Column: "mp.medical_record_number", Operator: queryUtils.OpEqual, Value: medical_record_number}, + Joins: []queryUtils.Join{ + { + Type: "LEFT", + Table: "master.ms_patient_attachment", + Alias: "ma", + OnConditions: queryUtils.FilterGroup{ + Filters: []queryUtils.DynamicFilter{ + {Column: "ma.fk_ms_patient_id", Operator: queryUtils.OpEqual, Value: "mp.id"}, + }, + }, + }, + { + Type: "LEFT", + Table: "master.ms_patient_payment_type", + Alias: "mpt", + OnConditions: queryUtils.FilterGroup{ + Filters: []queryUtils.DynamicFilter{ + {Column: "mpt.fk_ms_patient_id", Operator: queryUtils.OpEqual, Value: "mp.id"}, + }, }, - LogicOp: "AND", - }}, - Limit: 1, - } - - err = h.queryBuilder.ExecuteQueryRow(ctx, dbConn, query, &oldData) - if err != nil { - logger.Error("Failed to fetch old data for cache invalidation", map[string]interface{}{"error": err.Error(), "medical_record_number": medical_record_number}) - } - } - - dbConn, err = h.db.GetSQLXDB("db_antrean") - if err != nil { - h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError) - return - } - - ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second) - defer cancel() - - // Validate medical_record_number must be unique - if req.MedicalRecordNumber != nil { - rule := validation.ValidationRule{ - TableName: "role_access.rol_pages", - UniqueColumns: []string{"medical_record_number"}, - Conditions: []queryUtils.DynamicFilter{ - {Column: "active", Operator: queryUtils.OpNotEqual, Value: false}, }, - ExcludeIDColumn: "medical_record_number", - ExcludeIDValue: medical_record_number, - } - - dataToValidate := map[string]interface{}{"medical_record_number": req.MedicalRecordNumber} - isDuplicate, err := h.validator.Validate(ctx, dbConn, rule, dataToValidate) - if err != nil { - h.logAndRespondError(c, "Failed to validate medical_record_number", err, http.StatusInternalServerError) - return - } - - if isDuplicate { - h.respondError(c, "medical_record_number already exists", fmt.Errorf("duplicate medical_record_number: %s", *req.MedicalRecordNumber), http.StatusConflict) - return - } - } - - updateData := queryUtils.UpdateData{ - Columns: []string{ - "name", "medical_record_number", "phone_number", "gender", - "birth_date", "address", "active", "fk_sd_provinsi_id", - "fk_sd_kabupaten_kota_id", "fk_sd_kecamatan_id", "fk_sd_kelurahan_id", - "ds_sd_provinsi", "ds_sd_kabupaten_kota", "ds_sd_kecamatan", "ds_sd_kelurahan", }, - Values: []interface{}{ - req.Name, req.MedicalRecordNumber, req.PhoneNumber, req.Gender, - req.BirthDate, req.Address, req.Active, req.FKSdProvinsiID, - req.FKSdKabupatenKotaID, req.FKSdKecamatanID, req.FKSdKelurahanID, - req.DsSdProvinsi, req.DsSdKabupatenKota, req.DsSdKecamatan, req.DsSdKelurahan, - }, - } - filters := []queryUtils.FilterGroup{{ - Filters: []queryUtils.DynamicFilter{ - {Column: "medical_record_number", Operator: queryUtils.OpEqual, Value: req.MedicalRecordNumber}, - }, - LogicOp: "AND", - }} - returningCols := []string{ - "id", "name", "medical_record_number", "phone_number", "gender", - "birth_date", "address", "active", "fk_sd_provinsi_id", - "fk_sd_kabupaten_kota_id", "fk_sd_kecamatan_id", "fk_sd_kelurahan_id", - "ds_sd_provinsi", "ds_sd_kabupaten_kota", "ds_sd_kecamatan", "ds_sd_kelurahan", + Filters: []queryUtils.FilterGroup{{ + Filters: []queryUtils.DynamicFilter{ + {Column: "mp.medical_record_number", Operator: queryUtils.OpEqual, Value: req.MedicalRecordNumber}, + }, + LogicOp: "AND", + }}, + Limit: 1, } - sql, args, err := h.queryBuilder.BuildUpdateQuery("master.ms_patient", updateData, filters, returningCols...) - if err != nil { - h.logAndRespondError(c, "Failed to build update query", err, http.StatusInternalServerError) - return - } - - var dataMs_patient patientModels.Patient - err = dbConn.GetContext(ctx, &dataMs_patient, sql, args...) - if err != nil { - if err.Error() == "sql: no rows in result set" { + var results []map[string]interface{} + if err := h.queryBuilder.ExecuteQuery(ctx, dbConn, query, &results); err != nil { + if err == sql.ErrNoRows { h.respondError(c, "Patient not found", err, http.StatusNotFound) } else { - h.logAndRespondError(c, "Failed to update patient", err, http.StatusInternalServerError) + h.logAndRespondError(c, "Failed to get patient", err, http.StatusInternalServerError) } return } - // Invalidate cache - cacheKey := fmt.Sprintf("patient:medical_record_number:%s", medical_record_number) - h.cache.Delete(cacheKey) - - if oldData.ID != 0 { - h.invalidateRelatedCache() + patients := h.processQueryResults(results) + if len(patients) == 0 { + h.respondError(c, "Patient not found", fmt.Errorf("no rows"), http.StatusNotFound) + return } - response := patientModels.PatientUpdateResponse{ - Message: "Patient berhasil diperbarui", - Data: &dataMs_patient, + patient := patients[0] + + // Cache the result for future requests + h.cache.Set(cacheKey, &patient, 30*time.Minute) + + response := patientModels.PatientCreateResponse{ + Message: "Patient details retrieved successfully", + Data: &patient, } c.JSON(http.StatusOK, response) } -// DeletePatient godoc -// @Summary Delete patient -// @Description Soft deletes a patient by setting status to 'deleted' -// @Tags Patient -// @Accept json -// @Produce json -// @Param id path string true "Medical Record Number" -// @Success 200 {object} patientModels.PatientDeleteResponse "Patient deleted successfully" -// @Failure 400 {object} models.ErrorResponse "Invalid ID format" -// @Failure 404 {object} models.ErrorResponse "Patient not found" -// @Failure 500 {object} models.ErrorResponse "Internal server error" -// @Router /patient/{id} [delete] -func (h *PatientHandler) DeleteMs_patient(c *gin.Context) { - medical_record_number := c.Param("medical_record_number") - if medical_record_number == "" { - h.respondError(c, "Invalid medical_record_number format", fmt.Errorf("medical_record_number cannot be empty"), http.StatusBadRequest) - return +// ============================================================================= +// HELPER FUNCTIONS (READ-ONLY) +// ============================================================================= + +// parsePagination parses pagination parameters from the request +func (h *PatientHandler) parsePagination(c *gin.Context, query *queryUtils.DynamicQuery) { + if limit, err := strconv.Atoi(c.DefaultQuery("limit", "10")); err == nil && limit > 0 && limit <= 100 { + query.Limit = limit } - - // Get data for cache invalidation - var dataToDelete patientModels.Patient - dbConn, err := h.db.GetSQLXDB("db_antrean") - if err == nil { - ctx, cancel := context.WithTimeout(c.Request.Context(), 5*time.Second) - defer cancel() - - query := queryUtils.DynamicQuery{ - From: "master.ms_patient", - Aliases: "mp", - Fields: []queryUtils.SelectField{{Expression: "*"}}, - Filters: []queryUtils.FilterGroup{{ - Filters: []queryUtils.DynamicFilter{ - {Column: "mp.medical_record_number", Operator: queryUtils.OpEqual, Value: medical_record_number}, - }, - LogicOp: "AND", - }}, - Limit: 1, - } - - err = h.queryBuilder.ExecuteQueryRow(ctx, dbConn, query, &dataToDelete) - if err != nil { - logger.Error("Failed to fetch data for cache invalidation", map[string]interface{}{"error": err.Error(), "medical_record_number": medical_record_number}) - } + if offset, err := strconv.Atoi(c.DefaultQuery("offset", "0")); err == nil && offset >= 0 { + query.Offset = offset } - - dbConn, err = h.db.GetSQLXDB("db_antrean") - if err != nil { - h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError) - return - } - - ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second) - defer cancel() - - // Soft delete by changing active status - updateData := queryUtils.UpdateData{ - Columns: []string{"active"}, - Values: []interface{}{false}, - } - filters := []queryUtils.FilterGroup{{ - Filters: []queryUtils.DynamicFilter{ - {Column: "medical_record_number", Operator: queryUtils.OpEqual, Value: medical_record_number}, - {Column: "active", Operator: queryUtils.OpNotEqual, Value: false}, - }, - LogicOp: "AND", - }} - - result, err := h.queryBuilder.ExecuteUpdate(ctx, dbConn, "master.ms_patient", updateData, filters) - if err != nil { - h.logAndRespondError(c, "Failed to delete patient", err, http.StatusInternalServerError) - return - } - - rowsAffected, err := result.RowsAffected() - if err != nil { - h.logAndRespondError(c, "Failed to get affected rows", err, http.StatusInternalServerError) - return - } - if rowsAffected == 0 { - h.respondError(c, "Patient not found", sql.ErrNoRows, http.StatusNotFound) - return - } - - // Invalidate cache - cacheKey := fmt.Sprintf("patient:medical_record_number:%s", medical_record_number) - h.cache.Delete(cacheKey) - - if dataToDelete.ID != 0 { - h.invalidateRelatedCache() - } - - response := patientModels.PatientDeleteResponse{ - Message: "Patient berhasil dihapus", - MedicalRecordNumber: medical_record_number, - } - - c.JSON(http.StatusOK, response) } -// GetPatientStats godoc -// @Summary Get patient statistics -// @Description Returns comprehensive statistics about patient data -// @Tags Patient -// @Accept json -// @Produce json -// @Param status query string false "Filter statistics by status" -// @Success 200 {object} models.AggregateData "Statistics data" -// @Failure 500 {object} models.ErrorResponse "Internal server error" -// @Router /api/v1/patients/stats [get] -// func (h *PatientHandler) GetPatientStats(c *gin.Context) { -// dbConn, err := h.db.GetSQLXDB("db_antrean") -// if err != nil { -// h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError) -// return -// } - -// ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second) -// defer cancel() - -// // Parse filters -// var filters []queryUtils.DynamicFilter -// if status := c.Query("status"); status != "" && models.IsValidStatus(status) { -// filters = append(filters, queryUtils.DynamicFilter{Column: "active", Operator: queryUtils.OpEqual, Value: status}) -// } - -// var filterGroups []queryUtils.FilterGroup -// if len(filters) > 0 { -// filterGroups = append(filterGroups, queryUtils.FilterGroup{Filters: filters, LogicOp: "AND"}) -// } - -// aggregateData, err := h.getAggregateData(ctx, dbConn, filterGroups) -// if err != nil { -// h.logAndRespondError(c, "Failed to get statistics", err, http.StatusInternalServerError) -// return -// } - -// c.JSON(http.StatusOK, gin.H{ -// "message": "Statistik patient berhasil diambil", -// "data": aggregateData, -// }) -// } - -// SearchPatientAdvanced provides advanced search capabilities -// func (h *PatientHandler) SearchPatientAdvanced(c *gin.Context) { -// ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second) -// defer cancel() - -// // Parse complex search parameters -// searchQuery := c.Query("norm") -// if searchQuery == "" { -// // If no search query provided, return all records with default sorting -// query := queryUtils.DynamicQuery{ -// From: "master.ms_patient", -// Aliases: "mp", -// Fields: []queryUtils.SelectField{ -// {Expression: "mp.id", Alias: "id"}, -// {Expression: "mp.name", Alias: "name"}, -// {Expression: "mp.medical_record_number", Alias: "medical_record_number"}, -// {Expression: "mp.phone_number", Alias: "phone_number"}, -// {Expression: "mp.gender", Alias: "gender"}, -// {Expression: "mp.birth_date", Alias: "birth_date"}, -// {Expression: "mp.address", Alias: "address"}, -// {Expression: "mp.active", Alias: "active"}, -// }, -// Filters: []queryUtils.FilterGroup{{ -// Filters: []queryUtils.DynamicFilter{ -// {Column: "mp.active", Operator: queryUtils.OpEqual, Value: true}, -// }, -// LogicOp: "AND", -// }}, -// Sort: []queryUtils.SortField{{ -// Column: "mp.id", -// Order: "ASC", -// }}, -// Limit: 20, -// Offset: 0, -// } - -// // Parse pagination if provided -// if limit := c.Query("limit"); limit != "" { -// if l, err := strconv.Atoi(limit); err == nil && l > 0 && l <= 100 { -// query.Limit = l -// } -// } - -// if offset := c.Query("offset"); offset != "" { -// if o, err := strconv.Atoi(offset); err == nil && o >= 0 { -// query.Offset = o -// } -// } - -// // Get database connection -// dbConn, err := h.db.GetSQLXDB("db_antrean") -// if err != nil { -// h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError) -// return -// } - -// // Execute query to get all records -// var results []map[string]interface{} -// err = h.queryBuilder.ExecuteQuery(ctx, dbConn, query, &results) -// if err != nil { -// h.logAndRespondError(c, "Failed to fetch data", err, http.StatusInternalServerError) -// return -// } - -// patients := h.processQueryResults(results) -// total, err := h.getTotalCount(ctx, dbConn, query) -// if err != nil { -// h.logAndRespondError(c, "Failed to get total count", err, http.StatusInternalServerError) -// return -// } - -// // Build response -// meta := h.calculateMeta(query.Limit, query.Offset, total) -// response := patientModels.PatientGetResponse{ -// Message: "All records retrieved (no search query provided)", -// Data: patients, -// Meta: meta, -// } - -// c.JSON(http.StatusOK, response) -// return -// } - -// // Build dynamic query for search -// filters := []queryUtils.DynamicFilter{ -// {Column: "mp.medical_record_number", Operator: queryUtils.OpContains, Value: searchQuery}, -// {Column: "mp.active", Operator: queryUtils.OpEqual, Value: true}, -// } - -// query := queryUtils.DynamicQuery{ -// From: "master.ms_patient", -// Aliases: "mp", -// Fields: []queryUtils.SelectField{ -// {Expression: "mp.id", Alias: "id"}, -// {Expression: "mp.name", Alias: "name"}, -// {Expression: "mp.medical_record_number", Alias: "medical_record_number"}, -// {Expression: "mp.phone_number", Alias: "phone_number"}, -// {Expression: "mp.gender", Alias: "gender"}, -// {Expression: "mp.birth_date", Alias: "birth_date"}, -// {Expression: "mp.address", Alias: "address"}, -// {Expression: "mp.active", Alias: "active"}, -// }, -// Filters: []queryUtils.FilterGroup{{ -// Filters: filters, -// LogicOp: "AND", -// }}, -// Sort: []queryUtils.SortField{{ -// Column: "mp.id", -// Order: "ASC", -// }}, -// Limit: 20, -// Offset: 0, -// } - -// // Parse pagination if provided -// if limit := c.Query("limit"); limit != "" { -// if l, err := strconv.Atoi(limit); err == nil && l > 0 && l <= 100 { -// query.Limit = l -// } -// } -// if offset := c.Query("offset"); offset != "" { -// if o, err := strconv.Atoi(offset); err == nil && o >= 0 { -// query.Offset = o -// } -// } - -// // Get database connection -// dbConn, err := h.db.GetSQLXDB("db_antrean") -// if err != nil { -// h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError) -// return -// } - -// // Execute search -// var results []map[string]interface{} -// err = h.queryBuilder.ExecuteQuery(ctx, dbConn, query, &results) -// if err != nil { -// h.logAndRespondError(c, "Search failed", err, http.StatusInternalServerError) -// return -// } - -// patients := h.processQueryResults(results) -// total, err := h.getTotalCount(ctx, dbConn, query) -// if err != nil { -// h.logAndRespondError(c, "Failed to get total count", err, http.StatusInternalServerError) -// return -// } - -// // Build response -// meta := h.calculateMeta(query.Limit, query.Offset, total) -// response := patientModels.PatientGetResponse{ -// Message: fmt.Sprintf("Search results for '%s'", searchQuery), -// Data: patients, -// Meta: meta, -// } - -// c.JSON(http.StatusOK, response) -// } - -// ============================================================================= -// HELPER FUNCTIONS -// ============================================================================= - +// processQueryResults processes the flat query results into a structured nested format func (h *PatientHandler) processQueryResults(results []map[string]interface{}) []patientModels.Patient { - patients := make([]patientModels.Patient, 0, len(results)) + patientsMap := make(map[int64]*patientModels.Patient) + order := make([]int64, 0, len(results)) for _, result := range results { - patient := patientModels.Patient{ - ID: getInt64(result, "id"), - Name: getNullString(result, "name"), - MedicalRecordNumber: getNullString(result, "medical_record_number"), - PhoneNumber: getNullString(result, "phone_number"), - Gender: getNullString(result, "gender"), - BirthDate: getNullTime(result, "birth_date"), - Address: getNullString(result, "address"), - Active: getNullBool(result, "active"), - FKSdProvinsiID: getNullableInt32(result, "fk_sd_provinsi_id"), - FKSdKabupatenKotaID: getNullableInt32(result, "fk_sd_kabupaten_kota_id"), - FKSdKecamatanID: getNullableInt32(result, "fk_sd_kecamatan_id"), - FKSdKelurahanID: getNullableInt32(result, "fk_sd_kelurahan_id"), - DsSdProvinsi: getNullString(result, "ds_sd_provinsi"), - DsSdKabupatenKota: getNullString(result, "ds_sd_kabupaten_kota"), - DsSdKecamatan: getNullString(result, "ds_sd_kecamatan"), - DsSdKelurahan: getNullString(result, "ds_sd_kelurahan"), + patientID := getInt64(result, "id") + + patient, exists := patientsMap[patientID] + if !exists { + patient = &patientModels.Patient{ + ID: patientID, + Name: getNullString(result, "name"), + MedicalRecordNumber: getNullString(result, "medical_record_number"), + PhoneNumber: getNullString(result, "phone_number"), + Gender: getNullString(result, "gender"), + BirthDate: getNullTime(result, "birth_date"), + Address: getNullString(result, "address"), + Active: getNullBool(result, "active"), + FKSdProvinsiID: getNullableInt32(result, "fk_sd_provinsi_id"), + FKSdKabupatenKotaID: getNullableInt32(result, "fk_sd_kabupaten_kota_id"), + FKSdKecamatanID: getNullableInt32(result, "fk_sd_kecamatan_id"), + FKSdKelurahanID: getNullableInt32(result, "fk_sd_kelurahan_id"), + DsSdProvinsi: getNullString(result, "ds_sd_provinsi"), + DsSdKabupatenKota: getNullString(result, "ds_sd_kabupaten_kota"), + DsSdKecamatan: getNullString(result, "ds_sd_kecamatan"), + DsSdKelurahan: getNullString(result, "ds_sd_kelurahan"), + Attachments: []patientModels.PatientAttachment{}, + PaymentTypes: []patientModels.PatientPaymentType{}, + } + patientsMap[patientID] = patient + order = append(order, patientID) + } + + // Process and add attachment if it exists + if attachmentID := getInt64(result, "attachment_id"); attachmentID != 0 { + if !h.hasAttachment(patient, attachmentID) { + patient.Attachments = append(patient.Attachments, patientModels.PatientAttachment{ + ID: attachmentID, + Name: getNullString(result, "attachment_name"), + FileName: getNullString(result, "attachment_file_name"), + Directory: getNullString(result, "attachment_directory"), + Active: getNullBool(result, "attachment_active"), + FKMsPatientID: getNullableInt32(result, "fk_ms_patient_id"), + }) + } + } + + // Process and add payment type if it exists + if paymentTypeID := getInt64(result, "payment_type_id"); paymentTypeID != 0 { + if !h.hasPaymentType(patient, paymentTypeID) { + patient.PaymentTypes = append(patient.PaymentTypes, patientModels.PatientPaymentType{ + ID: paymentTypeID, + Name: getNullString(result, "payment_type_name"), + Number: getNullString(result, "payment_type_number"), + Active: getNullBool(result, "payment_type_active"), + FKMsPatientID: getNullableInt32(result, "fk_ms_patient_id"), + FKRefPaymentType: getNullableInt32(result, "fk_ref_payment_type"), + }) + } + } + } + + // Convert map to slice while preserving order + patients := make([]patientModels.Patient, 0, len(patientsMap)) + for _, id := range order { + if p, ok := patientsMap[id]; ok { + patients = append(patients, *p) } - patients = append(patients, patient) } return patients } -// Helper functions for extracting values from map[string]interface{} -func getInt64(m map[string]interface{}, key string) int64 { - if val, ok := m[key]; ok { - switch v := val.(type) { - case int64: - return v - case int: - return int64(v) - case float64: - return int64(v) +// hasAttachment checks if an attachment already exists in the patient's slice +func (h *PatientHandler) hasAttachment(patient *patientModels.Patient, attachmentID int64) bool { + for _, att := range patient.Attachments { + if att.ID == attachmentID { + return true } } - return 0 + return false } -func getString(m map[string]interface{}, key string) string { - if val, ok := m[key]; ok { - if v, ok := val.(string); ok { - return v +// hasPaymentType checks if a payment type already exists in the patient's slice +func (h *PatientHandler) hasPaymentType(patient *patientModels.Patient, paymentTypeID int64) bool { + for _, pt := range patient.PaymentTypes { + if pt.ID == paymentTypeID { + return true } } - return "" -} - -func getNullString(m map[string]interface{}, key string) sql.NullString { - if val, ok := m[key]; ok { - if ns, ok := val.(sql.NullString); ok { - return ns - } - if s, ok := val.(string); ok { - return sql.NullString{String: s, Valid: true} - } - } - return sql.NullString{Valid: false} -} - -func getNullableInt32(m map[string]interface{}, key string) models.NullableInt32 { - if val, ok := m[key]; ok { - if v, ok := val.(models.NullableInt32); ok { - return v - } - } - return models.NullableInt32{} -} - -func getNullBool(m map[string]interface{}, key string) sql.NullBool { - if val, ok := m[key]; ok { - if v, ok := val.(sql.NullBool); ok { - return v - } - } - return sql.NullBool{} -} - -func getNullTime(m map[string]interface{}, key string) sql.NullTime { - if val, ok := m[key]; ok { - if v, ok := val.(sql.NullTime); ok { - return v - } - } - return sql.NullTime{} + return false } +// getTotalCount gets the total count of records matching the query func (h *PatientHandler) getTotalCount(ctx context.Context, dbConn *sqlx.DB, query queryUtils.DynamicQuery) (int, error) { countQuery := queryUtils.DynamicQuery{ From: query.From, @@ -1114,13 +623,7 @@ func (h *PatientHandler) getTotalCount(ctx context.Context, dbConn *sqlx.DB, que return int(count), nil } -func (h *PatientHandler) invalidateRelatedCache() { - h.cache.DeleteByPrefix("patient:search:") - h.cache.DeleteByPrefix("patient:dynamic:") - h.cache.DeleteByPrefix("patient:stats:") - h.cache.DeleteByPrefix("patient:id:") -} - +// getAggregateData gets aggregate data for the patients func (h *PatientHandler) getAggregateData(ctx context.Context, dbConn *sqlx.DB, filterGroups []queryUtils.FilterGroup) (*models.AggregateData, error) { aggregate := &models.AggregateData{ ByStatus: make(map[string]int), @@ -1260,11 +763,13 @@ func (h *PatientHandler) getAggregateData(ctx context.Context, dbConn *sqlx.DB, return aggregate, nil } +// logAndRespondError logs an error and sends an error response func (h *PatientHandler) logAndRespondError(c *gin.Context, message string, err error, statusCode int) { logger.Error(message, map[string]interface{}{"error": err.Error(), "status_code": statusCode}) h.respondError(c, message, err, statusCode) } +// respondError sends an error response func (h *PatientHandler) respondError(c *gin.Context, message string, err error, statusCode int) { errorMessage := message if gin.Mode() == gin.ReleaseMode { @@ -1273,6 +778,7 @@ func (h *PatientHandler) respondError(c *gin.Context, message string, err error, c.JSON(statusCode, models.ErrorResponse{Error: errorMessage, Code: statusCode, Message: err.Error(), Timestamp: time.Now()}) } +// calculateMeta calculates pagination metadata func (h *PatientHandler) calculateMeta(limit, offset, total int) models.MetaResponse { totalPages, currentPage := 0, 1 if limit > 0 { @@ -1284,3 +790,71 @@ func (h *PatientHandler) calculateMeta(limit, offset, total int) models.MetaResp CurrentPage: currentPage, HasNext: offset+limit < total, HasPrev: offset > 0, } } + +// ============================================================================= +// UTILITY FUNCTIONS (DATA EXTRACTION) +// ============================================================================= + +// getInt64 safely extracts an int64 from a map[string]interface{} +func getInt64(m map[string]interface{}, key string) int64 { + if val, ok := m[key]; ok { + switch v := val.(type) { + case int64: + return v + case int: + return int64(v) + case float64: + return int64(v) + } + } + return 0 +} + +// getNullString safely extracts a sql.NullString from a map[string]interface{} +func getNullString(m map[string]interface{}, key string) sql.NullString { + if val, ok := m[key]; ok { + if ns, ok := val.(sql.NullString); ok { + return ns + } + if s, ok := val.(string); ok { + return sql.NullString{String: s, Valid: true} + } + } + return sql.NullString{Valid: false} +} + +// getNullableInt32 safely extracts a models.NullableInt32 from a map[string]interface{} +func getNullableInt32(m map[string]interface{}, key string) models.NullableInt32 { + if val, ok := m[key]; ok { + if v, ok := val.(models.NullableInt32); ok { + return v + } + } + return models.NullableInt32{} +} + +// getNullBool safely extracts a sql.NullBool from a map[string]interface{} +func getNullBool(m map[string]interface{}, key string) sql.NullBool { + if val, ok := m[key]; ok { + if nb, ok := val.(sql.NullBool); ok { + return nb + } + if b, ok := val.(bool); ok { + return sql.NullBool{Bool: b, Valid: true} + } + } + return sql.NullBool{Valid: false} +} + +// getNullTime safely extracts a sql.NullTime from a map[string]interface{} +func getNullTime(m map[string]interface{}, key string) sql.NullTime { + if val, ok := m[key]; ok { + if nt, ok := val.(sql.NullTime); ok { + return nt + } + if t, ok := val.(time.Time); ok { + return sql.NullTime{Time: t, Valid: true} + } + } + return sql.NullTime{Valid: false} +} diff --git a/internal/handlers/websocket/websocket.go b/internal/handlers/websocket/websocket.go new file mode 100644 index 0000000..798f842 --- /dev/null +++ b/internal/handlers/websocket/websocket.go @@ -0,0 +1,205 @@ +package websocket + +import ( + "api-service/internal/config" + ws "api-service/internal/services/websocket" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" +) + +// WebSocketHandler menangani koneksi WebSocket +type WebSocketHandler struct { + hub *ws.Hub + broadcaster *ws.Broadcaster + config config.WebSocketConfig + upgrader websocket.Upgrader + monitoringManager *ws.MonitoringManager +} + +// NewWebSocketHandler membuat handler WebSocket baru +func NewWebSocketHandler(hub *ws.Hub, broadcaster *ws.Broadcaster, config config.WebSocketConfig) *WebSocketHandler { + return &WebSocketHandler{ + hub: hub, + broadcaster: broadcaster, + config: config, + monitoringManager: ws.NewMonitoringManager(hub), + upgrader: websocket.Upgrader{ + ReadBufferSize: config.ReadBufferSize, + WriteBufferSize: config.WriteBufferSize, + CheckOrigin: func(r *http.Request) bool { + // Dalam production, implementasikan validasi origin yang proper + return true + }, + }, + } +} + +// HandleWebSocket menangani upgrade koneksi ke WebSocket +func (h *WebSocketHandler) HandleWebSocket(c *gin.Context) { + // Ambil parameter dari query atau header + clientID := c.Query("client_id") + if clientID == "" { + clientID = fmt.Sprintf("client_%d", time.Now().UnixNano()) + } + + staticID := c.Query("static_id") + userID := c.Query("user_id") + room := c.Query("room") + ipAddress := c.ClientIP() + + // Upgrade koneksi HTTP ke WebSocket + conn, err := h.upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": "Failed to upgrade connection", + "details": err.Error(), + }) + return + } + + // Buat klien WebSocket baru + client := ws.NewClient(clientID, staticID, userID, room, ipAddress, conn, h.hub, h.config) + + // Daftarkan klien ke hub + h.hub.RegisterChannel() <- client + + // Jalankan goroutine untuk membaca dan menulis + go client.ReadPump() + go client.WritePump() +} + +// GetWebSocketStats mengembalikan statistik WebSocket +func (h *WebSocketHandler) GetWebSocketStats(c *gin.Context) { + stats := h.hub.GetStats() + c.JSON(http.StatusOK, stats) +} + +// TestWebSocketConnection endpoint untuk test koneksi WebSocket +func (h *WebSocketHandler) TestWebSocketConnection(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "WebSocket service is running", + "status": "active", + "endpoint": "/api/v1/ws", + "parameters": gin.H{ + "client_id": "optional, auto-generated if not provided", + "static_id": "optional, for persistent client identification", + "user_id": "optional, for user identification", + "room": "optional, for room-based messaging", + }, + "example": "ws://meninjar.dev.rssa.id:8070/api/v1/ws?client_id=test_client&room=test_room", + "timestamp": time.Now().Unix(), + }) +} + +// GetDetailedStats mengembalikan statistik detail WebSocket +func (h *WebSocketHandler) GetDetailedStats(c *gin.Context) { + detailedStats := h.monitoringManager.GetDetailedStats() + c.JSON(http.StatusOK, gin.H{ + "admin_stats": detailedStats, + "timestamp": time.Now().Unix(), + }) +} + +// DisconnectClient memutuskan koneksi klien tertentu +func (h *WebSocketHandler) DisconnectClient(c *gin.Context) { + clientID := c.Param("clientId") + success := h.monitoringManager.DisconnectClient(clientID) + c.JSON(http.StatusOK, gin.H{ + "status": "force disconnect attempted", + "client_id": clientID, + "success": success, + "timestamp": time.Now().Unix(), + }) +} + +// CleanupInactiveClients membersihkan klien tidak aktif +func (h *WebSocketHandler) CleanupInactiveClients(c *gin.Context) { + var req struct { + InactiveMinutes int `json:"inactive_minutes"` + Force bool `json:"force"` + } + if err := c.ShouldBindJSON(&req); err != nil { + req.InactiveMinutes = 10 + req.Force = false + } + + cleanedCount := h.monitoringManager.CleanupInactiveClients(time.Duration(req.InactiveMinutes) * time.Minute) + c.JSON(http.StatusOK, gin.H{ + "status": "admin cleanup completed", + "cleaned_clients": cleanedCount, + "inactive_minutes": req.InactiveMinutes, + "force": req.Force, + "timestamp": time.Now().Unix(), + }) +} + +// SendMessageToClient forwards a message to a specific connected client via an HTTP POST request. +// This version normalizes the data and uses the custom broadcaster method for clean delivery. +func (h *WebSocketHandler) SendMessageToClient(c *gin.Context) { + // Define a flexible struct to parse the incoming HTTP request body + var req struct { + Data interface{} `json:"data"` + ToClient string `json:"to_client"` + Type string `json:"type"` + } + + // Parse the JSON from the HTTP request body + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // Normalize data into map[string]interface{} for consistency + var dataMap map[string]interface{} + switch v := req.Data.(type) { + case map[string]interface{}: + dataMap = v + case string: + // If data is a JSON string, try to unmarshal it + if err := json.Unmarshal([]byte(v), &dataMap); err != nil { + // If unmarshaling fails, treat it as a raw string + dataMap = map[string]interface{}{"raw": v} + } + default: + // For any other type, wrap it in a map + dataMap = map[string]interface{}{"data": v} + } + + // Get the target client ID, checking the body first, then the query parameter + to := req.ToClient + if to == "" { + to = c.Query("to") + } + if to == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": "target client id required (field 'to' or query param 'to')"}) + return + } + + msgType := req.Type + if msgType == "" { + msgType = "direct_message" + } + + // Use the existing broadcaster instance from the Hub. + // DO NOT create a new broadcaster on every request. + h.broadcaster.CustomBroadcastClientToClient( + "server", // fromClientID, indicating the message is from the server itself + to, // toClientID + msgType, // messageType, as specified in the request + dataMap, // data payload, already normalized + ) + + // Respond with a detailed confirmation + c.JSON(http.StatusOK, gin.H{ + "status": "sent", + "to_client": to, + "type": msgType, + "data": dataMap, + "timestamp": time.Now().Unix(), + }) +} diff --git a/internal/models/pages/rol_pages.go b/internal/models/pages/rol_pages.go index 5832eb7..72dd458 100644 --- a/internal/models/pages/rol_pages.go +++ b/internal/models/pages/rol_pages.go @@ -247,8 +247,8 @@ type PagesUpdateResponse struct { // Update request type PagesUpdateRequest struct { - IDs *int `json:"id" validate:"omitempty"` // used when updating via JSON array (bulk) - ID *int `json:"-" validate:"omitempty"` // required for single update via URL param + IDs *int `json:"id" validate:"omitempty"` // used when updating via JSON array (bulk) + ID *int `json:"-" validate:"omitempty,min=1"` // required for single update via URL param // Status string `json:"status" validate:"required,oneof=draft active inactive"` Name *string `json:"name" validate:"omitempty,min=1,max=20"` Icon *string `json:"icon" validate:"omitempty,min=1,max=100"` diff --git a/internal/models/patient/ms_patient.go b/internal/models/patient/ms_patient.go index 9001241..d687ab2 100644 --- a/internal/models/patient/ms_patient.go +++ b/internal/models/patient/ms_patient.go @@ -106,6 +106,26 @@ type Patient struct { DsSdKabupatenKota sql.NullString `json:"ds_sd_kabupaten_kota,omitempty" db:"ds_sd_kabupaten_kota"` DsSdKecamatan sql.NullString `json:"ds_sd_kecamatan,omitempty" db:"ds_sd_kecamatan"` DsSdKelurahan sql.NullString `json:"ds_sd_kelurahan,omitempty" db:"ds_sd_kelurahan"` + Attachments []PatientAttachment `json:"attachments,omitempty"` + PaymentTypes []PatientPaymentType `json:"payment_types,omitempty"` +} + +type PatientPaymentType struct { + ID int64 `json:"id" db:"id"` + Name sql.NullString `json:"name,omitempty" db:"name"` + Number sql.NullString `json:"number,omitempty" db:"number"` + Active sql.NullBool `json:"active,omitempty" db:"active"` + FKMsPatientID models.NullableInt32 `json:"fk_ms_patient_id,omitempty" db:"fk_ms_patient_id"` + FKRefPaymentType models.NullableInt32 `json:"fk_ref_payment_type,omitempty" db:"fk_ref_payment_type"` +} + +type PatientAttachment struct { + ID int64 `json:"id" db:"id"` + Name sql.NullString `json:"name,omitempty" db:"name"` + FileName sql.NullString `json:"file_name,omitempty" db:"file_name"` + Directory sql.NullString `json:"directory,omitempty" db:"directory"` + Active sql.NullBool `json:"active,omitempty" db:"active"` + FKMsPatientID models.NullableInt32 `json:"fk_ms_patient_id,omitempty" db:"fk_ms_patient_id"` } // Custom JSON marshaling untuk Patient agar NULL values tidak muncul di response @@ -186,6 +206,71 @@ func (r Patient) MarshalJSON() ([]byte, error) { return json.Marshal(aux) } +func (r PatientPaymentType) MarshalJSON() ([]byte, error) { + type Alias PatientPaymentType + aux := &struct { + *Alias + Name *string `json:"name,omitempty"` + Number *string `json:"number,omitempty"` + Active *bool `json:"active,omitempty"` + FKMsPatientID *int `json:"fk_ms_patient_id,omitempty"` + FKRefPaymentType *int `json:"fk_ref_payment_type,omitempty"` + }{ + Alias: (*Alias)(&r), + } + + if r.Name.Valid { + aux.Name = &r.Name.String + } + if r.Number.Valid { + aux.Number = &r.Number.String + } + if r.Active.Valid { + aux.Active = &r.Active.Bool + } + if r.FKMsPatientID.Valid { + fkmp := int(r.FKMsPatientID.Int32) + aux.FKMsPatientID = &fkmp + } + if r.FKRefPaymentType.Valid { + fkrpt := int(r.FKRefPaymentType.Int32) + aux.FKRefPaymentType = &fkrpt + } + return json.Marshal(aux) +} + +func (r PatientAttachment) MarshalJSON() ([]byte, error) { + type Alias PatientAttachment + aux := &struct { + *Alias + Name *string `json:"name,omitempty"` + FileName *string `json:"file_name,omitempty"` + Directory *string `json:"directory,omitempty"` + Active *bool `json:"active,omitempty"` + FKMsPatientID *int `json:"fk_ms_patient_id,omitempty"` + }{ + Alias: (*Alias)(&r), + } + + if r.Name.Valid { + aux.Name = &r.Name.String + } + if r.FileName.Valid { + aux.FileName = &r.FileName.String + } + if r.Directory.Valid { + aux.Directory = &r.Directory.String + } + if r.Active.Valid { + aux.Active = &r.Active.Bool + } + if r.FKMsPatientID.Valid { + fkmp := int(r.FKMsPatientID.Int32) + aux.FKMsPatientID = &fkmp + } + return json.Marshal(aux) +} + // Helper methods untuk mendapatkan nilai yang aman func (r *Patient) GetName() string { if r.Name.Valid { @@ -210,26 +295,29 @@ type PatientGetResponse struct { // Request struct untuk create type PatientCreateRequest struct { - ID *int `json:"id"` - Name *string `json:"name" validate:"min=1,max=100"` - MedicalRecordNumber *string `json:"medical_record_number" validate:"min=1,max=20"` - PhoneNumber *string `json:"phone_number" validate:"min=1,max=20"` - Gender *string `json:"gender" validate:"max=1"` - BirthDate *time.Time `json:"birth_date"` - Address *string `json:"address" validate:"min=1,max=255"` - Active *bool `json:"active"` - FKSdProvinsiID *int32 `json:"fk_sd_provinsi_id"` - FKSdKabupatenKotaID *int32 `json:"fk_sd_kabupaten_kota_id"` - FKSdKecamatanID *int32 `json:"fk_sd_kecamatan_id"` - FKSdKelurahanID *int32 `json:"fk_sd_kelurahan_id"` - DsSdProvinsi *string `json:"ds_sd_provinsi" validate:"min=1,max=255"` - DsSdKabupatenKota *string `json:"ds_sd_kabupaten_kota" validate:"min=1,max=255"` - DsSdKecamatan *string `json:"ds_sd_kecamatan" validate:"min=1,max=255"` - DsSdKelurahan *string `json:"ds_sd_kelurahan" validate:"min=1,max=255"` + ID *int `json:"id"` + Name *string `json:"name" validate:"min=1,max=100"` + MedicalRecordNumber *string `json:"medical_record_number" validate:"min=1,max=20"` + PhoneNumber *string `json:"phone_number" validate:"min=1,max=20"` + Gender *string `json:"gender" validate:"max=1"` + BirthDate *time.Time `json:"birth_date"` + Address *string `json:"address" validate:"min=1,max=255"` + Active *bool `json:"active"` + FKSdProvinsiID *int32 `json:"fk_sd_provinsi_id"` + FKSdKabupatenKotaID *int32 `json:"fk_sd_kabupaten_kota_id"` + FKSdKecamatanID *int32 `json:"fk_sd_kecamatan_id"` + FKSdKelurahanID *int32 `json:"fk_sd_kelurahan_id"` + DsSdProvinsi *string `json:"ds_sd_provinsi" validate:"min=1,max=255"` + DsSdKabupatenKota *string `json:"ds_sd_kabupaten_kota" validate:"min=1,max=255"` + DsSdKecamatan *string `json:"ds_sd_kecamatan" validate:"min=1,max=255"` + DsSdKelurahan *string `json:"ds_sd_kelurahan" validate:"min=1,max=255"` + Attachments []PatientAttachmentCreateRequest `json:"attachments,omitempty"` + Payments []PatientPaymentTypeCreateRequest `json:"payment_types,omitempty"` } type PatientPostRequest struct { - ID int64 `json:"id" validate:"required,min=1"` + // ID int64 `json:"id" validate:"required,min=1"` + MedicalRecordNumber string `json:"medical_record_number" validate:"required,min=1"` } // Response struct untuk create @@ -240,22 +328,56 @@ type PatientCreateResponse struct { // Update request type PatientUpdateRequest struct { - ID *int `json:"id" validate:"required,min=1"` - Name *string `json:"name" validate:"min=1,max=100"` - MedicalRecordNumber *string `json:"medical_record_number" validate:"min=1,max=20"` - PhoneNumber *string `json:"phone_number" validate:"min=1,max=20"` - Gender *string `json:"gender" validate:"max=1"` - BirthDate *time.Time `json:"birth_date"` - Address *string `json:"address" validate:"min=1,max=255"` - Active *bool `json:"active"` - FKSdProvinsiID *int32 `json:"fk_sd_provinsi_id"` - FKSdKabupatenKotaID *int32 `json:"fk_sd_kabupaten_kota_id"` - FKSdKecamatanID *int32 `json:"fk_sd_kecamatan_id"` - FKSdKelurahanID *int32 `json:"fk_sd_kelurahan_id"` - DsSdProvinsi *string `json:"ds_sd_provinsi" validate:"max=255"` - DsSdKabupatenKota *string `json:"ds_sd_kabupaten_kota" validate:"max=255"` - DsSdKecamatan *string `json:"ds_sd_kecamatan" validate:"max=255"` - DsSdKelurahan *string `json:"ds_sd_kelurahan" validate:"max=255"` + ID *int `json:"-" validate:"required,min=1"` + Name *string `json:"name" validate:"min=1,max=100"` + MedicalRecordNumber *string `json:"medical_record_number" validate:"min=1,max=20"` + PhoneNumber *string `json:"phone_number" validate:"min=1,max=20"` + Gender *string `json:"gender" validate:"max=1"` + BirthDate *time.Time `json:"birth_date"` + Address *string `json:"address" validate:"min=1,max=255"` + Active *bool `json:"active"` + FKSdProvinsiID *int32 `json:"fk_sd_provinsi_id"` + FKSdKabupatenKotaID *int32 `json:"fk_sd_kabupaten_kota_id"` + FKSdKecamatanID *int32 `json:"fk_sd_kecamatan_id"` + FKSdKelurahanID *int32 `json:"fk_sd_kelurahan_id"` + DsSdProvinsi *string `json:"ds_sd_provinsi" validate:"max=255"` + DsSdKabupatenKota *string `json:"ds_sd_kabupaten_kota" validate:"max=255"` + DsSdKecamatan *string `json:"ds_sd_kecamatan" validate:"max=255"` + DsSdKelurahan *string `json:"ds_sd_kelurahan" validate:"max=255"` + Attachments []PatientAttachmentCreateRequest `json:"attachments,omitempty"` + Payments []PatientPaymentTypeCreateRequest `json:"payment_types,omitempty"` +} + +type PatientAttachmentCreateRequest struct { + // ID *int `json:"id,omitempty"` + Name *string `json:"name" validate:"omitempty,min=1,max=20"` + FileName *string `json:"file_name" validate:"omitempty,min=1,max=20"` + Directory *string `json:"directory" validate:"omitempty,min=1,max=255"` + Active *bool `json:"active" validate:"omitempty"` +} + +type PatientPaymentTypeCreateRequest struct { + // ID *int `json:"id,omitempty"` + Name *string `json:"name" validate:"omitempty,min=1,max=20"` + Number *string `json:"number" validate:"omitempty,min=1,max=20"` + Active *bool `json:"active" validate:"omitempty"` + FkRefPaymentType *int `json:"fk_ref_payment_type" validate:"omitempty,min=1"` +} + +type PatientAttachmentUpdateRequest struct { + ID *int `json:"id" validate:"required"` + Name *string `json:"name" validate:"omitempty,min=1,max=20"` + FileName *string `json:"file_name" validate:"omitempty,min=1,max=20"` + Directory *string `json:"directory" validate:"omitempty,min=1,max=255"` + Active *bool `json:"active" validate:"omitempty"` +} + +type PatientPaymentTypeUpdateRequest struct { + ID *int `json:"id" validate:"required"` + Name *string `json:"name" validate:"omitempty,min=1,max=20"` + Number *string `json:"number" validate:"omitempty,min=1,max=20"` + Active *bool `json:"active" validate:"omitempty"` + FkRefPaymentType *int `json:"fk_ref_payment_type" validate:"omitempty,min=1"` } // Response struct untuk update diff --git a/internal/routes/v1/routes.go b/internal/routes/v1/routes.go index e3957f1..c30b86f 100644 --- a/internal/routes/v1/routes.go +++ b/internal/routes/v1/routes.go @@ -4,15 +4,14 @@ import ( "api-service/internal/config" "api-service/internal/database" authHandlers "api-service/internal/handlers/auth" - componentRolcomponentHandlers "api-service/internal/handlers/component" healthcheckHandlers "api-service/internal/handlers/healthcheck" pagesRolpagesHandlers "api-service/internal/handlers/pages" pasienPasienHandlers "api-service/internal/handlers/pasien" patientMspatientHandlers "api-service/internal/handlers/patient" - permissionRolpermissionHandlers "api-service/internal/handlers/permission" - retribusiHandlers "api-service/internal/handlers/retribusi" + websocketHandlers "api-service/internal/handlers/websocket" "api-service/internal/middleware" services "api-service/internal/services/auth" + websocketServices "api-service/internal/services/websocket" "api-service/pkg/logger" "net/http" "strings" @@ -86,16 +85,18 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine { }) sistem.GET("/info", func(c *gin.Context) { c.JSON(200, gin.H{ - "service": "API Service v1.0.0", - "websocket_active": true, - "databases": dbService.ListDBs(), - "timestamp": time.Now().Unix(), + "service": "API Service v1.0.0", + "websocket_active": true, + "connected_clients": 0, + "databases": dbService.ListDBs(), + "timestamp": time.Now().Unix(), }) }) } // --- API v1 GROUP --- v1 := apiGroup.Group("/v1") + { // ============================================================================= // PUBLIC ROUTES (No Authentication Required) @@ -110,22 +111,22 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine { v1.POST("/token/generate", tokenHandler.GenerateToken) v1.POST("/token/generate-direct", tokenHandler.GenerateTokenDirect) - retribusiHandler := retribusiHandlers.NewRetribusiHandler() - retribusiGroup := v1.Group("/retribusi") - { - retribusiGroup.GET("", retribusiHandler.GetRetribusi) - retribusiGroup.GET("/dynamic", retribusiHandler.GetRetribusiDynamic) - retribusiGroup.GET("/id/:id", retribusiHandler.GetRetribusiByID) - retribusiGroup.POST("", func(c *gin.Context) { - retribusiHandler.CreateRetribusi(c) - }) - retribusiGroup.PUT("/id/:id", func(c *gin.Context) { - retribusiHandler.UpdateRetribusi(c) - }) - retribusiGroup.DELETE("/id/:id", func(c *gin.Context) { - retribusiHandler.DeleteRetribusi(c) - }) - } + // retribusiHandler := retribusiHandlers.NewRetribusiHandler() + // retribusiGroup := v1.Group("/retribusi") + // { + // retribusiGroup.GET("", retribusiHandler.GetRetribusi) + // retribusiGroup.GET("/dynamic", retribusiHandler.GetRetribusiDynamic) + // retribusiGroup.GET("/id/:id", retribusiHandler.GetRetribusiByID) + // retribusiGroup.POST("", func(c *gin.Context) { + // retribusiHandler.CreateRetribusi(c) + // }) + // retribusiGroup.PUT("/id/:id", func(c *gin.Context) { + // retribusiHandler.UpdateRetribusi(c) + // }) + // retribusiGroup.DELETE("/id/:id", func(c *gin.Context) { + // retribusiHandler.DeleteRetribusi(c) + // }) + // } // Pasien endpoints pasienPasienHandler := pasienPasienHandlers.NewPasienHandler() pasienPasienGroup := v1.Group("/pasien") @@ -151,34 +152,55 @@ func RegisterRoutes(cfg *config.Config) *gin.Engine { pagesRolpagesGroup.DELETE("/:id", pagesRolpagesHandler.DeleteRol_pages) } + // Initialize WebSocket hub with database service + websocketHub := websocketServices.NewHub(cfg.WebSocket) + go websocketHub.Run() // Start WebSocket hub in background + + // -- Start broadcaster (needed for server -> client messaging) -- + broadcaster := websocketServices.NewBroadcaster(websocketHub) + broadcaster.StartServerBroadcasters() + + // Initialize WebSocket handler + websocketHandler := websocketHandlers.NewWebSocketHandler(websocketHub, broadcaster, cfg.WebSocket) + + // WebSocket routes + v1.GET("/ws", websocketHandler.HandleWebSocket) + v1.GET("/ws/test", websocketHandler.TestWebSocketConnection) + v1.GET("/ws/stats", websocketHandler.GetWebSocketStats) + + // Public endpoint to send message to a specific client (no auth) + v1.POST("/ws", websocketHandler.SendMessageToClient) + // Rol_component endpoints - componentRolcomponentHandler := componentRolcomponentHandlers.NewRol_componentHandler() - componentRolcomponentGroup := v1.Group("/component") - { - componentRolcomponentGroup.GET("/", componentRolcomponentHandler.GetRol_components) - componentRolcomponentGroup.POST("/", componentRolcomponentHandler.CreateRol_component) - componentRolcomponentGroup.PUT("/:id", componentRolcomponentHandler.UpdateRol_component) - componentRolcomponentGroup.DELETE("/:id", componentRolcomponentHandler.DeleteRol_component) - } + // componentRolcomponentHandler := componentRolcomponentHandlers.NewRol_componentHandler() + // componentRolcomponentGroup := v1.Group("/component") + // { + // componentRolcomponentGroup.GET("/", componentRolcomponentHandler.GetRol_components) + // componentRolcomponentGroup.POST("/", componentRolcomponentHandler.CreateRol_component) + // componentRolcomponentGroup.PUT("/:id", componentRolcomponentHandler.UpdateRol_component) + // componentRolcomponentGroup.DELETE("/:id", componentRolcomponentHandler.DeleteRol_component) + // } // Rol_permission endpoints - permissionRolpermissionHandler := permissionRolpermissionHandlers.NewRol_permissionHandler() - permissionRolpermissionGroup := v1.Group("/permission") - { - permissionRolpermissionGroup.GET("/", permissionRolpermissionHandler.GetRol_permissions) - permissionRolpermissionGroup.POST("/", permissionRolpermissionHandler.CreateRol_permission) - permissionRolpermissionGroup.PUT("/:id", permissionRolpermissionHandler.UpdateRol_permission) - permissionRolpermissionGroup.DELETE("/:id", permissionRolpermissionHandler.DeleteRol_permission) - } + // permissionRolpermissionHandler := permissionRolpermissionHandlers.NewRol_permissionHandler() + // permissionRolpermissionGroup := v1.Group("/permission") + // { + // permissionRolpermissionGroup.GET("/", permissionRolpermissionHandler.GetRol_permissions) + // permissionRolpermissionGroup.POST("/", permissionRolpermissionHandler.CreateRol_permission) + // permissionRolpermissionGroup.PUT("/:id", permissionRolpermissionHandler.UpdateRol_permission) + // permissionRolpermissionGroup.DELETE("/:id", permissionRolpermissionHandler.DeleteRol_permission) + // } // Ms_patient endpoints patientMspatientHandler := patientMspatientHandlers.NewPatientHandler() patientMspatientGroup := v1.Group("/patient") { - patientMspatientGroup.GET("/", patientMspatientHandler.GetMs_patient) - patientMspatientGroup.POST("/", patientMspatientHandler.CreateMs_patient) - patientMspatientGroup.PUT("/:medical_record_number", patientMspatientHandler.UpdateMs_patient) - patientMspatientGroup.DELETE("/:medical_record_number", patientMspatientHandler.DeleteMs_patient) + // patientMspatientGroup.GET("/", patientMspatientHandler.GetMs_patient) + // patientMspatientGroup.POST("/", patientMspatientHandler.CreateMs_patient) + // patientMspatientGroup.PUT("/:medical_record_number", patientMspatientHandler.UpdateMs_patient) + // patientMspatientGroup.DELETE("/:medical_record_number", patientMspatientHandler.DeleteMs_patient) + patientMspatientGroup.GET("/", patientMspatientHandler.GetPatient) + patientMspatientGroup.POST("/detail", patientMspatientHandler.GetPatientByIDPost) } // ============================================================================= diff --git a/internal/services/websocket/broadcaster.go b/internal/services/websocket/broadcaster.go new file mode 100644 index 0000000..807d158 --- /dev/null +++ b/internal/services/websocket/broadcaster.go @@ -0,0 +1,219 @@ +package websocket + +import ( + "time" +) + +// Broadcaster mengelola broadcasting pesan +type Broadcaster struct { + hub *Hub +} + +// NewBroadcaster membuat broadcaster baru +func NewBroadcaster(hub *Hub) *Broadcaster { + return &Broadcaster{hub: hub} +} + +// StartServerBroadcasters memulai broadcaster server +func (b *Broadcaster) StartServerBroadcasters() { + // Heartbeat broadcaster + // go b.startHeartbeatBroadcaster() + + // System notification broadcaster + go b.startSystemNotificationBroadcaster() + + // Data stream broadcaster + // go b.startDataStreamBroadcaster() +} + +// startHeartbeatBroadcaster memulai broadcaster heartbeat +func (b *Broadcaster) startHeartbeatBroadcaster() { + ticker := time.NewTicker(30 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + b.hub.mu.RLock() + connectedClients := len(b.hub.clients) + uniqueIPs := len(b.hub.clientsByIP) + staticClients := len(b.hub.clientsByStatic) + b.hub.mu.RUnlock() + + b.BroadcastMessage("server_heartbeat", map[string]interface{}{ + "message": "Server heartbeat", + "connected_clients": connectedClients, + "unique_ips": uniqueIPs, + "static_clients": staticClients, + "timestamp": time.Now().Unix(), + "server_id": b.hub.config.ServerID, + }) + case <-b.hub.ctx.Done(): + return + } + } +} + +// startSystemNotificationBroadcaster memulai broadcaster notifikasi sistem +func (b *Broadcaster) startSystemNotificationBroadcaster() { + ticker := time.NewTicker(5 * time.Minute) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + dbHealth := b.hub.dbService.Health() + b.BroadcastMessage("system_status", map[string]interface{}{ + "type": "system_notification", + "database": dbHealth, + "timestamp": time.Now().Unix(), + "uptime": time.Since(b.hub.startTime).String(), + }) + case <-b.hub.ctx.Done(): + return + } + } +} + +// startDataStreamBroadcaster memulai broadcaster aliran data +func (b *Broadcaster) startDataStreamBroadcaster() { + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + counter := 0 + + for { + select { + case <-ticker.C: + counter++ + b.BroadcastMessage("data_stream", map[string]interface{}{ + "id": counter, + "value": counter * 10, + "timestamp": time.Now().Unix(), + "type": "real_time_data", + }) + case <-b.hub.ctx.Done(): + return + } + } +} + +// BroadcastMessage mengirim pesan ke semua klien +func (b *Broadcaster) BroadcastMessage(messageType string, data interface{}) { + msg := NewWebSocketMessage(MessageType(messageType), data, "", "") + + select { + case b.hub.messageQueue <- msg: + default: + // Antrian penuh, abaikan pesan + } +} + +// BroadcastToRoom mengirim pesan ke ruangan tertentu +func (b *Broadcaster) BroadcastToRoom(room string, messageType string, data interface{}) { + msg := NewWebSocketMessage( + MessageType(messageType), + map[string]interface{}{ + "room": room, + "data": data, + }, + "", + room, + ) + + select { + case b.hub.messageQueue <- msg: + default: + // Antrian penuh, abaikan pesan + } +} + +// SendToClient mengirim pesan ke klien tertentu +func (b *Broadcaster) SendToClient(clientID string, messageType string, data interface{}) { + msg := NewWebSocketMessage(MessageType(messageType), data, clientID, "") + + select { + case b.hub.messageQueue <- msg: + default: + // Antrian penuh, abaikan pesan + } +} + +// SendToClientByStaticID mengirim pesan ke klien berdasarkan ID statis +func (b *Broadcaster) SendToClientByStaticID(staticID string, messageType string, data interface{}) bool { + b.hub.mu.RLock() + client, exists := b.hub.clientsByStatic[staticID] + b.hub.mu.RUnlock() + + if !exists { + return false + } + + b.SendToClient(client.ID, messageType, data) + return true +} + +// BroadcastToIP mengirim pesan ke semua klien dari IP tertentu +func (b *Broadcaster) BroadcastToIP(ipAddress string, messageType string, data interface{}) int { + b.hub.mu.RLock() + clients := b.hub.clientsByIP[ipAddress] + b.hub.mu.RUnlock() + + count := 0 + for _, client := range clients { + b.SendToClient(client.ID, messageType, data) + count++ + } + + return count +} + +// BroadcastClientToClient mengirim pesan dari satu klien ke klien lain +func (b *Broadcaster) BroadcastClientToClient(fromClientID, toClientID string, messageType string, data interface{}) { + message := NewWebSocketMessage(MessageType(messageType), map[string]interface{}{ + "from_client_id": fromClientID, + "data": data, + }, toClientID, "") + b.hub.broadcast <- message +} + +// BroadcastServerToClient mengirim pesan dari server ke klien tertentu +func (b *Broadcaster) BroadcastServerToClient(clientID string, messageType string, data interface{}) { + message := NewWebSocketMessage(MessageType(messageType), data, clientID, "") + b.hub.broadcast <- message +} + +// BroadcastServerToRoom mengirim pesan dari server ke ruangan tertentu +func (b *Broadcaster) BroadcastServerToRoom(room string, messageType string, data interface{}) { + message := NewWebSocketMessage(MessageType(messageType), data, "", room) + b.hub.broadcast <- message +} + +// BroadcastServerToAll mengirim pesan dari server ke semua klien +func (b *Broadcaster) BroadcastServerToAll(messageType string, data interface{}) { + message := NewWebSocketMessage(MessageType(messageType), data, "", "") + b.hub.broadcast <- message +} + +// CustomBroadcastClientToClient sends a message from one client to another. +// It intelligently adds the 'from_client_id' to the payload to avoid unnecessary nesting. +// If the payload is a map, the ID is added directly. Otherwise, the payload is wrapped. +func (b *Broadcaster) CustomBroadcastClientToClient(from, to, msgType string, data interface{}) { + // Create the message with the data directly. + msg := NewWebSocketMessage(MessageType(msgType), data, to, "") + + // Ensure from_client_id is available at the same level as the payload. + // If the payload is a map[string]interface{}, add the key directly. + // If not, wrap it once as "data" to avoid losing information. + if m, ok := msg.Data.(map[string]interface{}); ok { + m["from_client_id"] = from + msg.Data = m + } else { + msg.Data = map[string]interface{}{ + "data": msg.Data, + "from_client_id": from, + } + } + + // Send the message to the hub for delivery. + b.hub.broadcast <- msg +} diff --git a/internal/services/websocket/client.go b/internal/services/websocket/client.go new file mode 100644 index 0000000..96ed7ca --- /dev/null +++ b/internal/services/websocket/client.go @@ -0,0 +1,728 @@ +package websocket + +import ( + "api-service/internal/config" + "context" + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/google/uuid" + "github.com/gorilla/websocket" +) + +// Client mewakili koneksi klien WebSocket +type Client struct { + // Identifikasi + ID string + StaticID string + UserID string + Room string + + // Koneksi + IPAddress string + Conn *websocket.Conn + Send chan WebSocketMessage + + // Hub + Hub *Hub + + // Context + ctx context.Context + cancel context.CancelFunc + + // Status + lastPing time.Time + lastPong time.Time + connectedAt time.Time + isActive bool + + // Sinkronisasi + mu sync.RWMutex +} + +// NewClient membuat klien baru +func NewClient( + id, staticID, userID, room, ipAddress string, + conn *websocket.Conn, + hub *Hub, + config config.WebSocketConfig, +) *Client { + ctx, cancel := context.WithCancel(hub.ctx) + + return &Client{ + ID: id, + StaticID: staticID, + UserID: userID, + Room: room, + IPAddress: ipAddress, + Conn: conn, + Send: make(chan WebSocketMessage, config.ChannelBufferSize), + Hub: hub, + ctx: ctx, + cancel: cancel, + lastPing: time.Now(), + connectedAt: time.Now(), + isActive: true, + } +} + +// readPup menangani pembacaan pesan dari klien +func (c *Client) readPump() { + defer func() { + c.Hub.unregister <- c + c.Conn.Close() + }() + + // Konfigurasi koneksi + c.Conn.SetReadLimit(int64(c.Hub.config.MaxMessageSize)) + c.resetReadDeadline() + + // Setup ping/pong handlers + c.Conn.SetPingHandler(func(message string) error { + c.resetReadDeadline() + return c.Conn.WriteControl(websocket.PongMessage, []byte(message), time.Now().Add(c.Hub.config.WriteTimeout)) + }) + + c.Conn.SetPongHandler(func(message string) error { + c.mu.Lock() + c.lastPong = time.Now() + c.isActive = true + c.mu.Unlock() + c.resetReadDeadline() + return nil + }) + + for { + select { + case <-c.ctx.Done(): + return + default: + _, message, err := c.Conn.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, + websocket.CloseGoingAway, + websocket.CloseAbnormalClosure, + websocket.CloseNormalClosure) { + c.Hub.errorCount++ + } + return + } + + // Reset deadline setiap kali ada pesan masuk + c.resetReadDeadline() + c.updateLastActivity() + + // Parse pesan + var msg WebSocketMessage + if err := json.Unmarshal(message, &msg); err != nil { + c.sendErrorResponse("Invalid message format", err.Error()) + continue + } + + // Set metadata pesan + msg.Timestamp = time.Now() + msg.ClientID = c.ID + if msg.MessageID == "" { + msg.MessageID = uuid.New().String() + } + + // Proses pesan menggunakan registry + c.handleMessage(msg) + } + } +} + +// writePump menangani pengiriman pesan ke klien +func (c *Client) writePump() { + ticker := time.NewTicker(c.Hub.config.PingInterval) + defer func() { + ticker.Stop() + c.Conn.Close() + }() + + for { + select { + case message, ok := <-c.Send: + c.Conn.SetWriteDeadline(time.Now().Add(c.Hub.config.WriteTimeout)) + if !ok { + c.Conn.WriteMessage(websocket.CloseMessage, []byte{}) + return + } + + if err := c.Conn.WriteJSON(message); err != nil { + c.Hub.errorCount++ + return + } + + case <-ticker.C: + // Kirim ping dan periksa respons + if err := c.sendPing(); err != nil { + c.Hub.errorCount++ + return + } + + // Periksa timeout pong + if c.isPongTimeout() { + return + } + + case <-c.ctx.Done(): + return + } + } +} + +// handleMessage memproses pesan masuk +func (c *Client) handleMessage(msg WebSocketMessage) { + // Dapatkan handler dari registry + handler, exists := c.Hub.messageRegistry.GetHandler(msg.Type) + if !exists { + // Handler tidak ditemukan, gunakan handler default + c.handleDefaultMessage(msg) + return + } + + // Jalankan handler + if err := handler.HandleMessage(c, msg); err != nil { + c.sendErrorResponse("Error handling message", err.Error()) + c.Hub.errorCount++ + } +} + +// handleDefaultMessage menangani pesan tanpa handler khusus +func (c *Client) handleDefaultMessage(msg WebSocketMessage) { + switch msg.Type { + case "broadcast": + c.Hub.broadcast <- msg + c.sendDirectResponse("broadcast_sent", "Message broadcasted to all clients") + + case "direct_message": + c.handleDirectMessage(msg) + + case "ping": + c.handlePing(msg) + + case "pong": + // Pong sudah ditangani di level koneksi + break + + case "heartbeat": + c.handleHeartbeat(msg) + + case "connection_test": + c.handleConnectionTest(msg) + + case "get_online_users": + c.sendOnlineUsers() + + case "join_room": + c.handleJoinRoom(msg) + + case "leave_room": + c.handleLeaveRoom(msg) + + case "get_room_info": + c.handleGetRoomInfo(msg) + + case "database_query", "db_query": + c.handleDatabaseQuery(msg) + + case "db_insert": + c.handleDatabaseInsert(msg) + + case "db_custom_query": + c.handleDatabaseCustomQuery(msg) + + case "get_stats": + c.handleGetStats(msg) + + case "get_server_stats": + c.handleGetServerStats(msg) + + case "get_system_health": + c.handleGetSystemHealth(msg) + + case "admin_kick_client": + c.handleAdminKickClient(msg) + + case "admin_kill_server": + c.handleAdminKillServer(msg) + + case "admin_clear_logs": + c.handleAdminClearLogs(msg) + + default: + c.sendDirectResponse("message_received", fmt.Sprintf("Message received: %v", msg.Data)) + c.Hub.broadcast <- msg + } +} + +// Metode helper lainnya... +func (c *Client) resetReadDeadline() { + c.Conn.SetReadDeadline(time.Now().Add(c.Hub.config.ReadTimeout)) +} + +func (c *Client) updateLastActivity() { + c.mu.Lock() + defer c.mu.Unlock() + c.lastPing = time.Now() + c.isActive = true +} + +func (c *Client) sendPing() error { + c.Conn.SetWriteDeadline(time.Now().Add(c.Hub.config.WriteTimeout)) + if err := c.Conn.WriteMessage(websocket.PingMessage, nil); err != nil { + return err + } + + c.mu.Lock() + c.lastPing = time.Now() + c.mu.Unlock() + + return nil +} + +func (c *Client) isPongTimeout() bool { + c.mu.RLock() + defer c.mu.RUnlock() + + lastActivity := c.lastPong + if lastActivity.IsZero() { + lastActivity = c.lastPing + } + + return time.Since(lastActivity) > c.Hub.config.PongTimeout +} + +func (c *Client) isClientActive() bool { + c.mu.RLock() + defer c.mu.RUnlock() + return c.isActive && time.Since(c.lastPing) < c.Hub.config.PongTimeout +} + +func (c *Client) sendDirectResponse(messageType string, data interface{}) { + response := NewWebSocketMessage(MessageType(messageType), data, c.ID, c.Room) + + select { + case c.Send <- response: + default: + // Channel penuh, abaikan pesan + } +} + +func (c *Client) sendErrorResponse(error, details string) { + c.sendDirectResponse("error", map[string]interface{}{ + "error": error, + "details": details, + }) +} + +func (c *Client) gracefulClose() { + c.mu.Lock() + c.isActive = false + c.mu.Unlock() + + // Kirim pesan close + c.Conn.SetWriteDeadline(time.Now().Add(c.Hub.config.WriteTimeout)) + c.Conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) + + // Batalkan context + c.cancel() +} + +// ReadPump mengekspos metode readPump untuk handler +func (c *Client) ReadPump() { + c.readPump() +} + +// WritePump mengekspos metode writePump untuk handler +func (c *Client) WritePump() { + c.writePump() +} + +// handleDirectMessage menangani pesan direct message +func (c *Client) handleDirectMessage(msg WebSocketMessage) { + data, ok := msg.Data.(map[string]interface{}) + if !ok { + c.sendErrorResponse("Invalid direct message format", "Data must be an object") + return + } + + targetClientID, ok := data["target_client_id"].(string) + if !ok || targetClientID == "" { + c.sendErrorResponse("Invalid target client ID", "target_client_id is required") + return + } + + message, ok := data["message"].(string) + if !ok || message == "" { + c.sendErrorResponse("Invalid message content", "message is required") + return + } + + // Buat pesan direct message + directMsg := NewWebSocketMessage(DirectMessage, map[string]interface{}{ + "from_client_id": c.ID, + "from_user_id": c.UserID, + "message": message, + "timestamp": msg.Timestamp, + }, targetClientID, "") + + // Kirim ke target client + select { + case c.Hub.broadcast <- directMsg: + c.sendDirectResponse("direct_message_sent", map[string]interface{}{ + "target_client_id": targetClientID, + "message": message, + }) + default: + c.sendErrorResponse("Failed to send direct message", "Message queue is full") + } +} + +// handlePing menangani ping message +func (c *Client) handlePing(msg WebSocketMessage) { + // Kirim pong response + pongMsg := NewWebSocketMessage(PongMessage, map[string]interface{}{ + "timestamp": msg.Timestamp.Unix(), + "client_id": c.ID, + }, c.ID, "") + + select { + case c.Send <- pongMsg: + default: + // Channel penuh, abaikan + } +} + +// handleHeartbeat menangani heartbeat message +func (c *Client) handleHeartbeat(msg WebSocketMessage) { + // Kirim heartbeat acknowledgment + heartbeatAck := NewWebSocketMessage(MessageType("heartbeat_ack"), map[string]interface{}{ + "timestamp": time.Now().Unix(), + "client_uptime": time.Since(c.connectedAt).Seconds(), + "server_uptime": time.Since(c.Hub.startTime).Seconds(), + "client_id": c.ID, + }, c.ID, "") + + select { + case c.Send <- heartbeatAck: + default: + // Channel penuh, abaikan + } +} + +// handleConnectionTest menangani connection test +func (c *Client) handleConnectionTest(msg WebSocketMessage) { + // Kirim connection test result + testResult := NewWebSocketMessage(MessageType("connection_test_result"), map[string]interface{}{ + "timestamp": time.Now().Unix(), + "client_id": c.ID, + "connection_status": "healthy", + "latency_ms": 0, // Could be calculated if ping timestamp is provided + "uptime_seconds": time.Since(c.connectedAt).Seconds(), + }, c.ID, "") + + select { + case c.Send <- testResult: + default: + // Channel penuh, abaikan + } +} + +// handleJoinRoom menangani join room +func (c *Client) handleJoinRoom(msg WebSocketMessage) { + data, ok := msg.Data.(map[string]interface{}) + if !ok { + c.sendErrorResponse("Invalid join room format", "Data must be an object") + return + } + + roomName, ok := data["room"].(string) + if !ok || roomName == "" { + c.sendErrorResponse("Invalid room name", "room is required") + return + } + + // Update client room + oldRoom := c.Room + c.Room = roomName + + // Update hub room mapping + c.Hub.mu.Lock() + if oldRoom != "" { + if roomClients, exists := c.Hub.rooms[oldRoom]; exists { + delete(roomClients, c) + if len(roomClients) == 0 { + delete(c.Hub.rooms, oldRoom) + } + } + } + + if c.Hub.rooms[roomName] == nil { + c.Hub.rooms[roomName] = make(map[*Client]bool) + } + c.Hub.rooms[roomName][c] = true + c.Hub.mu.Unlock() + + // Log activity + c.Hub.logActivity("client_join_room", c.ID, fmt.Sprintf("Room: %s", roomName)) + + // Kirim response + c.sendDirectResponse("room_joined", map[string]interface{}{ + "room": roomName, + "previous_room": oldRoom, + }) +} + +// handleLeaveRoom menangani leave room +func (c *Client) handleLeaveRoom(msg WebSocketMessage) { + oldRoom := c.Room + + if oldRoom == "" { + c.sendErrorResponse("Not in any room", "Client is not currently in a room") + return + } + + // Update hub room mapping + c.Hub.mu.Lock() + if roomClients, exists := c.Hub.rooms[oldRoom]; exists { + delete(roomClients, c) + if len(roomClients) == 0 { + delete(c.Hub.rooms, oldRoom) + } + } + c.Hub.mu.Unlock() + + // Clear client room + c.Room = "" + + // Log activity + c.Hub.logActivity("client_leave_room", c.ID, fmt.Sprintf("Room: %s", oldRoom)) + + // Kirim response + c.sendDirectResponse("room_left", map[string]interface{}{ + "room": oldRoom, + }) +} + +// handleGetRoomInfo menangani get room info +func (c *Client) handleGetRoomInfo(msg WebSocketMessage) { + c.Hub.mu.RLock() + defer c.Hub.mu.RUnlock() + + roomInfo := make(map[string]interface{}) + + // Info ruangan saat ini + if c.Room != "" { + if roomClients, exists := c.Hub.rooms[c.Room]; exists { + clientIDs := make([]string, 0, len(roomClients)) + for client := range roomClients { + clientIDs = append(clientIDs, client.ID) + } + roomInfo["current_room"] = map[string]interface{}{ + "name": c.Room, + "client_count": len(roomClients), + "clients": clientIDs, + } + } + } + + // Info semua ruangan + allRooms := make(map[string]int) + for roomName, clients := range c.Hub.rooms { + allRooms[roomName] = len(clients) + } + roomInfo["all_rooms"] = allRooms + roomInfo["total_rooms"] = len(c.Hub.rooms) + + c.sendDirectResponse("room_info", roomInfo) +} + +// handleDatabaseInsert menangani database insert +func (c *Client) handleDatabaseInsert(msg WebSocketMessage) { + data, ok := msg.Data.(map[string]interface{}) + if !ok { + c.sendErrorResponse("Invalid database insert format", "Data must be an object") + return + } + + table, ok := data["table"].(string) + if !ok || table == "" { + c.sendErrorResponse("Invalid table name", "table is required") + return + } + + insertData, ok := data["data"].(map[string]interface{}) + if !ok { + c.sendErrorResponse("Invalid insert data", "data must be an object") + return + } + + // For now, just acknowledge the insert request + // In a real implementation, you would perform the actual database insert + c.sendDirectResponse("db_insert_result", map[string]interface{}{ + "table": table, + "status": "acknowledged", + "message": "Insert request received (not implemented)", + "data": insertData, // Use the variable to avoid unused error + }) +} + +// handleDatabaseCustomQuery menangani custom database query +func (c *Client) handleDatabaseCustomQuery(msg WebSocketMessage) { + data, ok := msg.Data.(map[string]interface{}) + if !ok { + c.sendErrorResponse("Invalid database query format", "Data must be an object") + return + } + + database, ok := data["database"].(string) + if !ok || database == "" { + database = "default" + } + + query, ok := data["query"].(string) + if !ok || query == "" { + c.sendErrorResponse("Invalid query", "query is required") + return + } + + // For now, just acknowledge the query request + // In a real implementation, you would execute the query + c.sendDirectResponse("db_query_result", map[string]interface{}{ + "database": database, + "query": query, + "status": "acknowledged", + "message": "Query request received (not implemented)", + }) +} + +// handleGetStats menangani get stats +func (c *Client) handleGetStats(msg WebSocketMessage) { + stats := c.Hub.GetStats() + c.sendDirectResponse("stats", stats) +} + +// handleGetServerStats menangani get server stats +func (c *Client) handleGetServerStats(msg WebSocketMessage) { + // Create monitoring manager instance + monitoringManager := NewMonitoringManager(c.Hub) + detailedStats := monitoringManager.GetDetailedStats() + c.sendDirectResponse("server_stats", detailedStats) +} + +// handleGetSystemHealth menangani get system health +func (c *Client) handleGetSystemHealth(msg WebSocketMessage) { + systemHealth := make(map[string]interface{}) + if c.Hub.dbService != nil { + systemHealth["databases"] = c.Hub.dbService.Health() + systemHealth["available_dbs"] = c.Hub.dbService.ListDBs() + } + systemHealth["websocket_status"] = "healthy" + systemHealth["uptime_seconds"] = time.Since(c.Hub.startTime).Seconds() + + c.sendDirectResponse("system_health", systemHealth) +} + +// handleAdminKickClient menangani admin kick client +func (c *Client) handleAdminKickClient(msg WebSocketMessage) { + // This should be protected by authentication, but for now just acknowledge + data, ok := msg.Data.(map[string]interface{}) + if !ok { + c.sendErrorResponse("Invalid admin command format", "Data must be an object") + return + } + + targetClientID, ok := data["client_id"].(string) + if !ok || targetClientID == "" { + c.sendErrorResponse("Invalid target client ID", "client_id is required") + return + } + + // For now, just acknowledge the kick request + // In a real implementation, you would check admin permissions and kick the client + c.sendDirectResponse("admin_command_result", map[string]interface{}{ + "command": "kick_client", + "target_client_id": targetClientID, + "status": "acknowledged", + "message": "Kick request received (not implemented)", + }) +} + +// handleAdminKillServer menangani admin kill server +func (c *Client) handleAdminKillServer(msg WebSocketMessage) { + // This should be protected by authentication, but for now just acknowledge + c.sendDirectResponse("admin_command_result", map[string]interface{}{ + "command": "kill_server", + "status": "acknowledged", + "message": "Kill server request received (not implemented - would require admin auth)", + }) +} + +// handleAdminClearLogs menangani admin clear logs +func (c *Client) handleAdminClearLogs(msg WebSocketMessage) { + // This should be protected by authentication, but for now just acknowledge + c.sendDirectResponse("admin_command_result", map[string]interface{}{ + "command": "clear_logs", + "status": "acknowledged", + "message": "Clear logs request received (not implemented - would require admin auth)", + }) +} + +// Implementasi metode lainnya... +func (c *Client) handleDatabaseQuery(msg WebSocketMessage) { + // Implementasi yang sama seperti sebelumnya + data, ok := msg.Data.(map[string]interface{}) + if !ok { + c.sendErrorResponse("Invalid database query format", "Data must be an object") + return + } + + table, ok := data["table"].(string) + if !ok || table == "" { + c.sendErrorResponse("Invalid table name", "table is required") + return + } + + // For now, just acknowledge the query request + c.sendDirectResponse("db_query_result", map[string]interface{}{ + "table": table, + "status": "acknowledged", + "message": "Query request received (not implemented)", + }) +} + +func (c *Client) sendOnlineUsers() { + c.Hub.mu.RLock() + defer c.Hub.mu.RUnlock() + + users := make([]map[string]interface{}, 0, len(c.Hub.clients)) + for client := range c.Hub.clients { + user := map[string]interface{}{ + "id": client.ID, + "static_id": client.StaticID, + "user_id": client.UserID, + "room": client.Room, + "ip_address": client.IPAddress, + "connected_at": client.connectedAt, + "last_ping": client.lastPing, + "last_pong": client.lastPong, + "is_active": client.isClientActive(), + } + users = append(users, user) + } + + response := NewWebSocketMessage(OnlineUsersMessage, map[string]interface{}{ + "users": users, + "count": len(users), + }, c.ID, "") + + select { + case c.Send <- response: + default: + // Channel penuh, abaikan + } +} diff --git a/internal/services/websocket/handlers.go b/internal/services/websocket/handlers.go new file mode 100644 index 0000000..62051bd --- /dev/null +++ b/internal/services/websocket/handlers.go @@ -0,0 +1,621 @@ +package websocket + +import ( + "context" + "fmt" + "strings" + "time" +) + +// DatabaseHandler menangani operasi database +type DatabaseHandler struct { + hub *Hub +} + +func NewDatabaseHandler(hub *Hub) *DatabaseHandler { + return &DatabaseHandler{hub: hub} +} + +func (h *DatabaseHandler) HandleMessage(client *Client, message WebSocketMessage) error { + switch message.Type { + case DatabaseInsertMessage: + return h.handleDatabaseInsert(client, message) + case DatabaseQueryMessage: + return h.handleDatabaseQuery(client, message) + case DatabaseCustomQueryMessage: + return h.handleDatabaseCustomQuery(client, message) + default: + return fmt.Errorf("unsupported database message type: %s", message.Type) + } +} + +func (h *DatabaseHandler) MessageType() MessageType { + return DatabaseInsertMessage // Primary type for registration +} + +func (h *DatabaseHandler) handleDatabaseInsert(client *Client, message WebSocketMessage) error { + data, ok := message.Data.(map[string]interface{}) + if !ok { + client.sendErrorResponse("Invalid database insert format", "Data must be an object") + return nil + } + + table, ok := data["table"].(string) + if !ok || table == "" { + client.sendErrorResponse("Invalid table name", "table is required") + return nil + } + + insertData, ok := data["data"].(map[string]interface{}) + if !ok { + client.sendErrorResponse("Invalid insert data", "data must be an object") + return nil + } + + // Perform actual database insert + if h.hub.dbService != nil { + // Get database connection + db, err := h.hub.GetDatabaseConnection("postgres_satudata") + if err != nil { + client.sendErrorResponse("Database connection error", err.Error()) + return nil + } + + // Build insert query + columns := make([]string, 0, len(insertData)) + values := make([]interface{}, 0, len(insertData)) + placeholders := make([]string, 0, len(insertData)) + + i := 1 + for col, val := range insertData { + columns = append(columns, col) + values = append(values, val) + placeholders = append(placeholders, fmt.Sprintf("$%d", i)) + i++ + } + + query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", + table, + strings.Join(columns, ", "), + strings.Join(placeholders, ", ")) + + // Execute insert + ctx, cancel := context.WithTimeout(client.ctx, 30*time.Second) + defer cancel() + + result, err := db.ExecContext(ctx, query, values...) + if err != nil { + client.sendErrorResponse("Database insert error", err.Error()) + return nil + } + + rowsAffected, _ := result.RowsAffected() + client.sendDirectResponse("db_insert_result", map[string]interface{}{ + "table": table, + "status": "success", + "rows_affected": rowsAffected, + "message": "Data inserted successfully", + }) + } else { + client.sendErrorResponse("Database service not available", "Database service is not initialized") + } + + return nil +} + +func (h *DatabaseHandler) handleDatabaseQuery(client *Client, message WebSocketMessage) error { + data, ok := message.Data.(map[string]interface{}) + if !ok { + client.sendErrorResponse("Invalid database query format", "Data must be an object") + return nil + } + + table, ok := data["table"].(string) + if !ok || table == "" { + client.sendErrorResponse("Invalid table name", "table is required") + return nil + } + + // Execute query + results, err := h.hub.ExecuteDatabaseQuery("postgres_satudata", fmt.Sprintf("SELECT * FROM %s LIMIT 100", table)) + if err != nil { + client.sendErrorResponse("Database query error", err.Error()) + return nil + } + + client.sendDirectResponse("db_query_result", map[string]interface{}{ + "table": table, + "status": "success", + "results": results, + "count": len(results), + }) + + return nil +} + +func (h *DatabaseHandler) handleDatabaseCustomQuery(client *Client, message WebSocketMessage) error { + data, ok := message.Data.(map[string]interface{}) + if !ok { + client.sendErrorResponse("Invalid database query format", "Data must be an object") + return nil + } + + database, ok := data["database"].(string) + if !ok || database == "" { + database = "postgres_satudata" + } + + query, ok := data["query"].(string) + if !ok || query == "" { + client.sendErrorResponse("Invalid query", "query is required") + return nil + } + + // Execute custom query + results, err := h.hub.ExecuteDatabaseQuery(database, query) + if err != nil { + client.sendErrorResponse("Database query error", err.Error()) + return nil + } + + client.sendDirectResponse("db_query_result", map[string]interface{}{ + "database": database, + "query": query, + "status": "success", + "results": results, + "count": len(results), + }) + + return nil +} + +// AdminHandler menangani operasi admin +type AdminHandler struct { + hub *Hub +} + +func NewAdminHandler(hub *Hub) *AdminHandler { + return &AdminHandler{hub: hub} +} + +func (h *AdminHandler) HandleMessage(client *Client, message WebSocketMessage) error { + switch message.Type { + case AdminKickClientMessage: + return h.handleAdminKickClient(client, message) + case AdminKillServerMessage: + return h.handleAdminKillServer(client, message) + case AdminClearLogsMessage: + return h.handleAdminClearLogs(client, message) + default: + return fmt.Errorf("unsupported admin message type: %s", message.Type) + } +} + +func (h *AdminHandler) MessageType() MessageType { + return AdminKickClientMessage // Primary type for registration +} + +func (h *AdminHandler) handleAdminKickClient(client *Client, message WebSocketMessage) error { + data, ok := message.Data.(map[string]interface{}) + if !ok { + client.sendErrorResponse("Invalid admin command format", "Data must be an object") + return nil + } + + targetClientID, ok := data["client_id"].(string) + if !ok || targetClientID == "" { + client.sendErrorResponse("Invalid target client ID", "client_id is required") + return nil + } + + // Check if target client exists + h.hub.mu.RLock() + targetClient, exists := h.hub.clientsByID[targetClientID] + h.hub.mu.RUnlock() + + if !exists { + client.sendErrorResponse("Client not found", fmt.Sprintf("Client %s not found", targetClientID)) + return nil + } + + // Log activity + h.hub.logActivity("admin_kick_client", client.ID, fmt.Sprintf("Kicked client: %s", targetClientID)) + + // Disconnect the target client + targetClient.cancel() + targetClient.Conn.Close() + + client.sendDirectResponse("admin_command_result", map[string]interface{}{ + "command": "kick_client", + "target_client_id": targetClientID, + "status": "success", + "message": "Client kicked successfully", + }) + + return nil +} + +func (h *AdminHandler) handleAdminKillServer(client *Client, message WebSocketMessage) error { + // Log activity + h.hub.logActivity("admin_kill_server", client.ID, "Server kill signal received") + + // For testing purposes, just acknowledge (don't actually kill server) + client.sendDirectResponse("admin_command_result", map[string]interface{}{ + "command": "kill_server", + "status": "acknowledged", + "message": "Kill server signal received (test mode - server not actually killed)", + }) + + return nil +} + +func (h *AdminHandler) handleAdminClearLogs(client *Client, message WebSocketMessage) error { + // Clear activity logs + h.hub.activityMu.Lock() + h.hub.activityLog = make([]ActivityLog, 0, h.hub.config.ActivityLogSize) + h.hub.activityMu.Unlock() + + // Log the clear action + h.hub.logActivity("admin_clear_logs", client.ID, "Activity logs cleared") + + client.sendDirectResponse("admin_command_result", map[string]interface{}{ + "command": "clear_logs", + "status": "success", + "message": "Server logs cleared successfully", + }) + + return nil +} + +// RoomHandler menangani operasi ruangan +type RoomHandler struct { + hub *Hub +} + +func NewRoomHandler(hub *Hub) *RoomHandler { + return &RoomHandler{hub: hub} +} + +func (h *RoomHandler) HandleMessage(client *Client, message WebSocketMessage) error { + switch message.Type { + case JoinRoomMessage: + return h.handleJoinRoom(client, message) + case LeaveRoomMessage: + return h.handleLeaveRoom(client, message) + case GetRoomInfoMessage: + return h.handleGetRoomInfo(client, message) + default: + return fmt.Errorf("unsupported room message type: %s", message.Type) + } +} + +func (h *RoomHandler) MessageType() MessageType { + return JoinRoomMessage // Primary type for registration +} + +func (h *RoomHandler) handleJoinRoom(client *Client, message WebSocketMessage) error { + data, ok := message.Data.(map[string]interface{}) + if !ok { + client.sendErrorResponse("Invalid join room format", "Data must be an object") + return nil + } + + roomName, ok := data["room"].(string) + if !ok || roomName == "" { + client.sendErrorResponse("Invalid room name", "room is required") + return nil + } + + // Update client room + oldRoom := client.Room + client.Room = roomName + + // Update hub room mapping + h.hub.mu.Lock() + if oldRoom != "" { + if roomClients, exists := h.hub.rooms[oldRoom]; exists { + delete(roomClients, client) + if len(roomClients) == 0 { + delete(h.hub.rooms, oldRoom) + } + } + } + + if h.hub.rooms[roomName] == nil { + h.hub.rooms[roomName] = make(map[*Client]bool) + } + h.hub.rooms[roomName][client] = true + h.hub.mu.Unlock() + + // Log activity + h.hub.logActivity("client_join_room", client.ID, fmt.Sprintf("Room: %s", roomName)) + + // Notify other clients in the room + h.hub.broadcaster.BroadcastServerToRoom(roomName, "user_joined_room", map[string]interface{}{ + "client_id": client.ID, + "user_id": client.UserID, + "room": roomName, + "joined_at": time.Now().Unix(), + }) + + // Send response + client.sendDirectResponse("room_joined", map[string]interface{}{ + "room": roomName, + "previous_room": oldRoom, + }) + + return nil +} + +func (h *RoomHandler) handleLeaveRoom(client *Client, message WebSocketMessage) error { + oldRoom := client.Room + + if oldRoom == "" { + client.sendErrorResponse("Not in any room", "Client is not currently in a room") + return nil + } + + // Update hub room mapping + h.hub.mu.Lock() + if roomClients, exists := h.hub.rooms[oldRoom]; exists { + delete(roomClients, client) + if len(roomClients) == 0 { + delete(h.hub.rooms, oldRoom) + } + } + h.hub.mu.Unlock() + + // Clear client room + client.Room = "" + + // Log activity + h.hub.logActivity("client_leave_room", client.ID, fmt.Sprintf("Room: %s", oldRoom)) + + // Notify other clients in the room + h.hub.broadcaster.BroadcastServerToRoom(oldRoom, "user_left_room", map[string]interface{}{ + "client_id": client.ID, + "user_id": client.UserID, + "room": oldRoom, + "left_at": time.Now().Unix(), + }) + + // Send response + client.sendDirectResponse("room_left", map[string]interface{}{ + "room": oldRoom, + }) + + return nil +} + +func (h *RoomHandler) handleGetRoomInfo(client *Client, message WebSocketMessage) error { + h.hub.mu.RLock() + defer h.hub.mu.RUnlock() + + roomInfo := make(map[string]interface{}) + + // Info ruangan saat ini + if client.Room != "" { + if roomClients, exists := h.hub.rooms[client.Room]; exists { + clientIDs := make([]string, 0, len(roomClients)) + for client := range roomClients { + clientIDs = append(clientIDs, client.ID) + } + roomInfo["current_room"] = map[string]interface{}{ + "name": client.Room, + "client_count": len(roomClients), + "clients": clientIDs, + } + } + } + + // Info semua ruangan + allRooms := make(map[string]int) + for roomName, clients := range h.hub.rooms { + allRooms[roomName] = len(clients) + } + roomInfo["all_rooms"] = allRooms + roomInfo["total_rooms"] = len(h.hub.rooms) + + client.sendDirectResponse("room_info", roomInfo) + + return nil +} + +// MonitoringHandler menangani operasi monitoring +type MonitoringHandler struct { + hub *Hub +} + +func NewMonitoringHandler(hub *Hub) *MonitoringHandler { + return &MonitoringHandler{hub: hub} +} + +func (h *MonitoringHandler) HandleMessage(client *Client, message WebSocketMessage) error { + switch message.Type { + case GetStatsMessage: + return h.handleGetStats(client, message) + case GetServerStatsMessage: + return h.handleGetServerStats(client, message) + case GetSystemHealthMessage: + return h.handleGetSystemHealth(client, message) + default: + return fmt.Errorf("unsupported monitoring message type: %s", message.Type) + } +} + +func (h *MonitoringHandler) MessageType() MessageType { + return GetStatsMessage // Primary type for registration +} + +func (h *MonitoringHandler) handleGetStats(client *Client, message WebSocketMessage) error { + stats := h.hub.GetStats() + client.sendDirectResponse("stats", stats) + return nil +} + +func (h *MonitoringHandler) handleGetServerStats(client *Client, message WebSocketMessage) error { + // Create monitoring manager instance + monitoringManager := NewMonitoringManager(h.hub) + detailedStats := monitoringManager.GetDetailedStats() + client.sendDirectResponse("server_stats", detailedStats) + return nil +} + +func (h *MonitoringHandler) handleGetSystemHealth(client *Client, message WebSocketMessage) error { + systemHealth := make(map[string]interface{}) + if h.hub.dbService != nil { + systemHealth["databases"] = h.hub.dbService.Health() + systemHealth["available_dbs"] = h.hub.dbService.ListDBs() + } + systemHealth["websocket_status"] = "healthy" + systemHealth["uptime_seconds"] = time.Since(h.hub.startTime).Seconds() + + client.sendDirectResponse("system_health", systemHealth) + return nil +} + +// ConnectionHandler menangani operasi koneksi +type ConnectionHandler struct { + hub *Hub +} + +func NewConnectionHandler(hub *Hub) *ConnectionHandler { + return &ConnectionHandler{hub: hub} +} + +func (h *ConnectionHandler) HandleMessage(client *Client, message WebSocketMessage) error { + switch message.Type { + case PingMessage: + return h.handlePing(client, message) + case PongMessage: + return h.handlePong(client, message) + case HeartbeatMessage: + return h.handleHeartbeat(client, message) + case ConnectionTestMessage: + return h.handleConnectionTest(client, message) + case OnlineUsersMessage: + return h.handleGetOnlineUsers(client, message) + default: + return fmt.Errorf("unsupported connection message type: %s", message.Type) + } +} + +func (h *ConnectionHandler) MessageType() MessageType { + return PingMessage // Primary type for registration +} + +func (h *ConnectionHandler) handlePing(client *Client, message WebSocketMessage) error { + // Send pong response + pongMsg := NewWebSocketMessage(PongMessage, map[string]interface{}{ + "timestamp": message.Timestamp.Unix(), + "client_id": client.ID, + }, client.ID, "") + + select { + case client.Send <- pongMsg: + default: + // Channel penuh, abaikan + } + + return nil +} + +func (h *ConnectionHandler) handlePong(client *Client, message WebSocketMessage) error { + // Pong sudah ditangani di level koneksi, tapi kita bisa log aktivitas + h.hub.logActivity("pong_received", client.ID, "Pong message received") + return nil +} + +func (h *ConnectionHandler) handleHeartbeat(client *Client, message WebSocketMessage) error { + // Send heartbeat acknowledgment + heartbeatAck := NewWebSocketMessage(MessageType("heartbeat_ack"), map[string]interface{}{ + "timestamp": time.Now().Unix(), + "client_uptime": time.Since(client.connectedAt).Seconds(), + "server_uptime": time.Since(h.hub.startTime).Seconds(), + "client_id": client.ID, + }, client.ID, "") + + select { + case client.Send <- heartbeatAck: + default: + // Channel penuh, abaikan + } + + return nil +} + +func (h *ConnectionHandler) handleConnectionTest(client *Client, message WebSocketMessage) error { + // Send connection test result + testResult := NewWebSocketMessage(MessageType("connection_test_result"), map[string]interface{}{ + "timestamp": time.Now().Unix(), + "client_id": client.ID, + "connection_status": "healthy", + "latency_ms": 0, // Could be calculated if ping timestamp is provided + "uptime_seconds": time.Since(client.connectedAt).Seconds(), + }, client.ID, "") + + select { + case client.Send <- testResult: + default: + // Channel penuh, abaikan + } + + return nil +} + +func (h *ConnectionHandler) handleGetOnlineUsers(client *Client, message WebSocketMessage) error { + h.hub.mu.RLock() + defer h.hub.mu.RUnlock() + + users := make([]map[string]interface{}, 0, len(h.hub.clients)) + for client := range h.hub.clients { + user := map[string]interface{}{ + "id": client.ID, + "static_id": client.StaticID, + "user_id": client.UserID, + "room": client.Room, + "ip_address": client.IPAddress, + "connected_at": client.connectedAt, + "last_ping": client.lastPing, + "last_pong": client.lastPong, + "is_active": client.isClientActive(), + } + users = append(users, user) + } + + response := NewWebSocketMessage(OnlineUsersMessage, map[string]interface{}{ + "users": users, + "count": len(users), + }, client.ID, "") + + select { + case client.Send <- response: + default: + // Channel penuh, abaikan + } + + return nil +} + +// SetupDefaultHandlers sets up all default message handlers +func SetupDefaultHandlers(registry *MessageRegistry, hub *Hub) { + // Register database handler + dbHandler := NewDatabaseHandler(hub) + registry.RegisterHandler(dbHandler) + + // Register admin handler + adminHandler := NewAdminHandler(hub) + registry.RegisterHandler(adminHandler) + + // Register room handler + roomHandler := NewRoomHandler(hub) + registry.RegisterHandler(roomHandler) + + // Register monitoring handler + monitoringHandler := NewMonitoringHandler(hub) + registry.RegisterHandler(monitoringHandler) + + // Register connection handler + connectionHandler := NewConnectionHandler(hub) + registry.RegisterHandler(connectionHandler) +} diff --git a/internal/services/websocket/hub.go b/internal/services/websocket/hub.go new file mode 100644 index 0000000..95c01a9 --- /dev/null +++ b/internal/services/websocket/hub.go @@ -0,0 +1,471 @@ +package websocket + +import ( + "api-service/internal/config" + "api-service/internal/database" + "context" + "database/sql" + "encoding/json" + "fmt" + "sync" + "time" +) + +// Hub mengelola semua klien WebSocket +type Hub struct { + // Konfigurasi + config config.WebSocketConfig + + // Klien + clients map[*Client]bool + clientsByID map[string]*Client + clientsByIP map[string][]*Client + clientsByStatic map[string]*Client + + // Ruangan + rooms map[string]map[*Client]bool + + // Channel komunikasi + broadcast chan WebSocketMessage + register chan *Client + unregister chan *Client + messageQueue chan WebSocketMessage + + // Sinkronisasi + mu sync.RWMutex + + // Context + ctx context.Context + cancel context.CancelFunc + + // Layanan eksternal + dbService database.Service + + // Monitoring + startTime time.Time + messageCount int64 + errorCount int64 + activityLog []ActivityLog + activityMu sync.RWMutex + + // Registry handler pesan + messageRegistry *MessageRegistry + broadcaster *Broadcaster +} + +// ActivityLog menyimpan log aktivitas +type ActivityLog struct { + Timestamp time.Time `json:"timestamp"` + Event string `json:"event"` + ClientID string `json:"client_id"` + Details string `json:"details"` +} + +// DatabaseService mendefinisikan interface untuk layanan database +type DatabaseService interface { + Health() map[string]interface{} + ListDBs() []string + ListenForChanges(ctx context.Context, dbName string, channels []string, callback func(channel, payload string)) error + NotifyChange(dbName, channel, payload string) error + GetDB(name string) (*sql.DB, error) + GetPrimaryDB(name string) (*sql.DB, error) +} + +// Global database service instance +var ( + dbService database.Service + once sync.Once +) + +// Initialize the database connection +func init() { + once.Do(func() { + dbService = database.New(config.LoadConfig()) + if dbService == nil { + panic("Failed to initialize database connection") + } + }) +} + +// NewHub membuat hub baru dengan konfigurasi yang diberikan +func NewHub(config config.WebSocketConfig) *Hub { + ctx, cancel := context.WithCancel(context.Background()) + + hub := &Hub{ + + config: config, + clients: make(map[*Client]bool), + clientsByID: make(map[string]*Client), + clientsByIP: make(map[string][]*Client), + clientsByStatic: make(map[string]*Client), + rooms: make(map[string]map[*Client]bool), + broadcast: make(chan WebSocketMessage, 1000), + register: make(chan *Client), + unregister: make(chan *Client), + messageQueue: make(chan WebSocketMessage, config.MessageQueueSize), + ctx: ctx, + cancel: cancel, + dbService: dbService, + startTime: time.Now(), + activityLog: make([]ActivityLog, 0, config.ActivityLogSize), + messageRegistry: NewMessageRegistry(), + } + + // Setup default message handlers + // SetupDefaultHandlers(hub.messageRegistry) + + // Setup database change listeners + hub.setupDatabaseListeners() + + return hub +} + +// Run menjalankan loop utama hub +func (h *Hub) Run() { + // Start queue workers + for i := 0; i < h.config.QueueWorkers; i++ { + go h.queueWorker(i) + } + + for { + select { + case client := <-h.register: + h.registerClient(client) + + case client := <-h.unregister: + h.unregisterClient(client) + + case message := <-h.broadcast: + h.messageCount++ + h.broadcastToClients(message) + + case <-h.ctx.Done(): + return + } + } +} + +// queueWorker memproses pesan dari antrian +func (h *Hub) queueWorker(workerID int) { + for { + select { + case message := <-h.messageQueue: + h.broadcast <- message + case <-h.ctx.Done(): + return + } + } +} + +// registerClient mendaftarkan klien baru +func (h *Hub) registerClient(client *Client) { + h.mu.Lock() + defer h.mu.Unlock() + + // Register di peta klien utama + h.clients[client] = true + + // Register berdasarkan ID + h.clientsByID[client.ID] = client + + // Register berdasarkan ID statis + if client.StaticID != "" { + h.clientsByStatic[client.StaticID] = client + } + + // Register berdasarkan IP + if h.clientsByIP[client.IPAddress] == nil { + h.clientsByIP[client.IPAddress] = make([]*Client, 0) + } + h.clientsByIP[client.IPAddress] = append(h.clientsByIP[client.IPAddress], client) + + // Register di ruangan + if client.Room != "" { + if h.rooms[client.Room] == nil { + h.rooms[client.Room] = make(map[*Client]bool) + } + h.rooms[client.Room][client] = true + } + + // Log aktivitas + h.logActivity("client_connected", client.ID, + fmt.Sprintf("IP: %s, Static: %s, Room: %s", client.IPAddress, client.StaticID, client.Room)) + + // --- MODIFIED: Send a detailed welcome message to the new client --- + + // Determine the type of ID for the client's information + idType := "generated" // Default type + if client.StaticID != "" { + idType = "static" + } else if client.UserID != "" { + idType = "user" + } + + welcomeMsg := NewWebSocketMessage(WelcomeMessage, map[string]interface{}{ + "message": "Welcome to the WebSocket server!", + "client_id": client.ID, + "static_id": client.StaticID, + "user_id": client.UserID, + "ip_address": client.IPAddress, + "room": client.Room, + "id_type": idType, // "generated", "static", or "user" + }, client.ID, "") + + select { + case client.Send <- welcomeMsg: + // Message sent successfully + default: + // Channel is full, unlikely for a new client + } +} + +// unregisterClient menghapus klien +func (h *Hub) unregisterClient(client *Client) { + h.mu.Lock() + defer h.mu.Unlock() + + if _, ok := h.clients[client]; ok { + // Hapus dari peta klien utama + delete(h.clients, client) + close(client.Send) + + // Hapus dari peta berdasarkan ID + delete(h.clientsByID, client.ID) + + // Hapus dari peta berdasarkan ID statis + if client.StaticID != "" { + delete(h.clientsByStatic, client.StaticID) + } + + // Hapus dari peta berdasarkan IP + if ipClients, exists := h.clientsByIP[client.IPAddress]; exists { + for i, c := range ipClients { + if c == client { + h.clientsByIP[client.IPAddress] = append(ipClients[:i], ipClients[i+1:]...) + break + } + } + // Jika tidak ada lagi klien dari IP ini, hapus entri IP + if len(h.clientsByIP[client.IPAddress]) == 0 { + delete(h.clientsByIP, client.IPAddress) + } + } + + // Hapus dari ruangan + if client.Room != "" { + if room, exists := h.rooms[client.Room]; exists { + delete(room, client) + if len(room) == 0 { + delete(h.rooms, client.Room) + } + } + } + } + + // Log aktivitas + h.logActivity("client_disconnected", client.ID, + fmt.Sprintf("IP: %s, Duration: %v", client.IPAddress, time.Since(client.connectedAt))) + + client.cancel() +} + +// broadcastToClients mengirim pesan ke klien yang sesuai +func (h *Hub) broadcastToClients(message WebSocketMessage) { + h.mu.RLock() + defer h.mu.RUnlock() + + if message.ClientID != "" { + // Kirim ke klien tertentu + if client, exists := h.clientsByID[message.ClientID]; exists { + select { + case client.Send <- message: + default: + go func() { + h.unregister <- client + }() + } + } + return + } + + // Periksa apakah ini pesan ruangan + if message.Room != "" { + if room, roomExists := h.rooms[message.Room]; roomExists { + for client := range room { + select { + case client.Send <- message: + default: + go func(c *Client) { + h.unregister <- c + }(client) + } + } + } + return + } + + // Broadcast ke semua klien + for client := range h.clients { + select { + case client.Send <- message: + default: + go func(c *Client) { + h.unregister <- c + }(client) + } + } +} + +// logActivity mencatat aktivitas +func (h *Hub) logActivity(event, clientID, details string) { + h.activityMu.Lock() + defer h.activityMu.Unlock() + + activity := ActivityLog{ + Timestamp: time.Now(), + Event: event, + ClientID: clientID, + Details: details, + } + + h.activityLog = append(h.activityLog, activity) + + // Pertahankan hanya aktivitas terakhir sesuai konfigurasi + if len(h.activityLog) > h.config.ActivityLogSize { + h.activityLog = h.activityLog[1:] + } +} + +// GetConfig mengembalikan konfigurasi hub +func (h *Hub) GetConfig() config.WebSocketConfig { + return h.config +} + +// GetMessageRegistry mengembalikan registry pesan +func (h *Hub) GetMessageRegistry() *MessageRegistry { + return h.messageRegistry +} + +// RegisterChannel mengembalikan channel register untuk handler +func (h *Hub) RegisterChannel() chan *Client { + return h.register +} + +// GetStats mengembalikan statistik hub +func (h *Hub) GetStats() map[string]interface{} { + h.mu.RLock() + defer h.mu.RUnlock() + + return map[string]interface{}{ + "connected_clients": len(h.clients), + "unique_ips": len(h.clientsByIP), + "static_clients": len(h.clientsByStatic), + "rooms": len(h.rooms), + "message_count": h.messageCount, + "error_count": h.errorCount, + "uptime": time.Since(h.startTime).String(), + "server_id": h.config.ServerID, + "timestamp": time.Now().Unix(), + } +} + +// setupDatabaseListeners sets up database change listeners for real-time updates +func (h *Hub) setupDatabaseListeners() { + // Listen for changes on retribusi table + channels := []string{"retribusi_changes", "data_changes"} + + err := h.dbService.ListenForChanges(h.ctx, "postgres_satudata", channels, func(channel, payload string) { + h.handleDatabaseChange(channel, payload) + }) + + if err != nil { + h.logActivity("database_listener_error", "", fmt.Sprintf("Failed to setup listeners: %v", err)) + } else { + h.logActivity("database_listener_started", "", "Database change listeners initialized") + } +} + +// handleDatabaseChange processes database change notifications +func (h *Hub) handleDatabaseChange(channel, payload string) { + h.logActivity("database_change", "", fmt.Sprintf("Channel: %s, Payload: %s", channel, payload)) + + // Parse the payload to determine what changed + var changeData map[string]interface{} + if err := json.Unmarshal([]byte(payload), &changeData); err != nil { + h.logActivity("database_change_parse_error", "", fmt.Sprintf("Failed to parse payload: %v", err)) + return + } + + // Create WebSocket message for broadcasting + message := WebSocketMessage{ + Type: DatabaseChangeMessage, + Data: changeData, + ClientID: "", + Room: "data_updates", // Broadcast to data_updates room + } + + // Broadcast the change to all connected clients in the data_updates room + h.broadcast <- message +} + +// GetDatabaseConnection returns a database connection for the specified database +func (h *Hub) GetDatabaseConnection(dbName string) (*sql.DB, error) { + return h.dbService.GetDB(dbName) +} + +// ExecuteDatabaseQuery executes a database query and returns results +func (h *Hub) ExecuteDatabaseQuery(dbName, query string, args ...interface{}) ([]map[string]interface{}, error) { + db, err := h.GetDatabaseConnection(dbName) + if err != nil { + return nil, fmt.Errorf("failed to get database connection: %w", err) + } + + ctx, cancel := context.WithTimeout(h.ctx, 30*time.Second) + defer cancel() + + rows, err := db.QueryContext(ctx, query, args...) + if err != nil { + return nil, fmt.Errorf("failed to execute query: %w", err) + } + defer rows.Close() + + columns, err := rows.Columns() + if err != nil { + return nil, fmt.Errorf("failed to get columns: %w", err) + } + + var results []map[string]interface{} + + for rows.Next() { + values := make([]interface{}, len(columns)) + valuePtrs := make([]interface{}, len(columns)) + for i := range values { + valuePtrs[i] = &values[i] + } + + if err := rows.Scan(valuePtrs...); err != nil { + return nil, fmt.Errorf("failed to scan row: %w", err) + } + + row := make(map[string]interface{}) + for i, col := range columns { + val := values[i] + if b, ok := val.([]byte); ok { + val = string(b) + } + row[col] = val + } + results = append(results, row) + } + + if err := rows.Err(); err != nil { + return nil, fmt.Errorf("rows iteration error: %w", err) + } + + return results, nil +} + +// NotifyDatabaseChange sends a notification to the database for real-time updates +func (h *Hub) NotifyDatabaseChange(dbName, channel, payload string) error { + return h.dbService.NotifyChange(dbName, channel, payload) +} diff --git a/internal/services/websocket/message.go b/internal/services/websocket/message.go new file mode 100644 index 0000000..9952b3e --- /dev/null +++ b/internal/services/websocket/message.go @@ -0,0 +1,109 @@ +package websocket + +import ( + "time" + + "github.com/google/uuid" +) + +// MessageType mendefinisikan tipe-tipe pesan yang valid +type MessageType string + +const ( + // Pesan koneksi + WelcomeMessage MessageType = "welcome" + ErrorMessage MessageType = "error" + DisconnectMessage MessageType = "disconnect" + + // Pesan kontrol + PingMessage MessageType = "ping" + PongMessage MessageType = "pong" + HeartbeatMessage MessageType = "heartbeat" + ConnectionTestMessage MessageType = "connection_test" + + // Pesan informasi + ServerInfoMessage MessageType = "server_info" + ClientInfoMessage MessageType = "client_info" + OnlineUsersMessage MessageType = "online_users" + + // Pesan komunikasi + DirectMessage MessageType = "direct_message" + RoomMessage MessageType = "room_message" + BroadcastMessage MessageType = "broadcast" + + // Pesan database + DatabaseQueryMessage MessageType = "database_query" + DatabaseChangeMessage MessageType = "database_change" + DatabaseInsertMessage MessageType = "db_insert" + DatabaseCustomQueryMessage MessageType = "db_custom_query" + + // Pesan monitoring + SystemStatusMessage MessageType = "system_status" + DataStreamMessage MessageType = "data_stream" + + // Pesan room management + JoinRoomMessage MessageType = "join_room" + LeaveRoomMessage MessageType = "leave_room" + GetRoomInfoMessage MessageType = "get_room_info" + + // Pesan admin + AdminKickClientMessage MessageType = "admin_kick_client" + AdminKillServerMessage MessageType = "admin_kill_server" + GetServerStatsMessage MessageType = "get_server_stats" + GetSystemHealthMessage MessageType = "get_system_health" + AdminClearLogsMessage MessageType = "admin_clear_logs" + + // Pesan utility + GetStatsMessage MessageType = "get_stats" +) + +// WebSocketMessage menyimpan struktur pesan WebSocket +type WebSocketMessage struct { + Type MessageType `json:"type"` + Data interface{} `json:"data"` + Timestamp time.Time `json:"timestamp"` + ClientID string `json:"client_id,omitempty"` + MessageID string `json:"message_id,omitempty"` + Room string `json:"room,omitempty"` +} + +// NewWebSocketMessage membuat pesan WebSocket baru +func NewWebSocketMessage(msgType MessageType, data interface{}, clientID, room string) WebSocketMessage { + return WebSocketMessage{ + Type: msgType, + Data: data, + Timestamp: time.Now(), + ClientID: clientID, + MessageID: uuid.New().String(), + Room: room, + } +} + +// MessageHandler mendefinisikan interface untuk handler pesan +type MessageHandler interface { + HandleMessage(client *Client, message WebSocketMessage) error + MessageType() MessageType +} + +// MessageRegistry menyimpan registry untuk handler pesan +type MessageRegistry struct { + handlers map[MessageType]MessageHandler +} + +// NewMessageRegistry membuat registry pesan baru +func NewMessageRegistry() *MessageRegistry { + return &MessageRegistry{ + handlers: make(map[MessageType]MessageHandler), + } +} + +// RegisterHandler mendaftarkan handler untuk tipe pesan tertentu +func (r *MessageRegistry) RegisterHandler(handler MessageHandler) { + r.handlers[handler.MessageType()] = handler +} + +// GetHandler mendapatkan handler untuk tipe pesan tertentu +func (r *MessageRegistry) GetHandler(msgType MessageType) (MessageHandler, bool) { + handler, exists := r.handlers[msgType] + return handler, exists +} diff --git a/internal/services/websocket/monitor.go b/internal/services/websocket/monitor.go new file mode 100644 index 0000000..ec9c8f6 --- /dev/null +++ b/internal/services/websocket/monitor.go @@ -0,0 +1,211 @@ +package websocket + +import ( + "fmt" + "time" +) + +// DetailedStats menyimpan statistik detail WebSocket +type DetailedStats struct { + ConnectedClients int `json:"connected_clients"` + UniqueIPs int `json:"unique_ips"` + StaticClients int `json:"static_clients"` + ActiveRooms int `json:"active_rooms"` + IPDistribution map[string]int `json:"ip_distribution"` + RoomDistribution map[string]int `json:"room_distribution"` + MessageQueueSize int `json:"message_queue_size"` + QueueWorkers int `json:"queue_workers"` + Uptime time.Duration `json:"uptime_seconds"` + Timestamp int64 `json:"timestamp"` +} + +// ClientInfo menyimpan informasi klien +type ClientInfo struct { + ID string `json:"id"` + StaticID string `json:"static_id"` + IPAddress string `json:"ip_address"` + UserID string `json:"user_id"` + Room string `json:"room"` + ConnectedAt time.Time `json:"connected_at"` + LastPing time.Time `json:"last_ping"` + LastPong time.Time `json:"last_pong"` + IsActive bool `json:"is_active"` +} + +// MonitoringData menyimpan data monitoring lengkap +type MonitoringData struct { + Stats DetailedStats `json:"stats"` + RecentActivity []ActivityLog `json:"recent_activity"` + SystemHealth map[string]interface{} `json:"system_health"` + Performance PerformanceMetrics `json:"performance"` +} + +// PerformanceMetrics menyimpan metrik performa +type PerformanceMetrics struct { + MessagesPerSecond float64 `json:"messages_per_second"` + AverageLatency float64 `json:"average_latency_ms"` + ErrorRate float64 `json:"error_rate_percent"` + MemoryUsage int64 `json:"memory_usage_bytes"` +} + +// MonitoringManager mengelola monitoring WebSocket +type MonitoringManager struct { + hub *Hub +} + +// NewMonitoringManager membuat manajer monitoring baru +func NewMonitoringManager(hub *Hub) *MonitoringManager { + return &MonitoringManager{hub: hub} +} + +// GetDetailedStats mengembalikan statistik detail +func (m *MonitoringManager) GetDetailedStats() DetailedStats { + m.hub.mu.RLock() + defer m.hub.mu.RUnlock() + + // Hitung distribusi IP + ipDistribution := make(map[string]int) + for ip, clients := range m.hub.clientsByIP { + ipDistribution[ip] = len(clients) + } + + // Hitung distribusi ruangan + roomDistribution := make(map[string]int) + for room, clients := range m.hub.rooms { + roomDistribution[room] = len(clients) + } + + return DetailedStats{ + ConnectedClients: len(m.hub.clients), + UniqueIPs: len(m.hub.clientsByIP), + StaticClients: len(m.hub.clientsByStatic), + ActiveRooms: len(m.hub.rooms), + IPDistribution: ipDistribution, + RoomDistribution: roomDistribution, + MessageQueueSize: len(m.hub.messageQueue), + QueueWorkers: m.hub.config.QueueWorkers, + Uptime: time.Since(m.hub.startTime), + Timestamp: time.Now().Unix(), + } +} + +// GetAllClients mengembalikan semua klien yang terhubung +func (m *MonitoringManager) GetAllClients() []ClientInfo { + m.hub.mu.RLock() + defer m.hub.mu.RUnlock() + + var clients []ClientInfo + for client := range m.hub.clients { + clientInfo := ClientInfo{ + ID: client.ID, + StaticID: client.StaticID, + IPAddress: client.IPAddress, + UserID: client.UserID, + Room: client.Room, + ConnectedAt: client.connectedAt, + LastPing: client.lastPing, + LastPong: client.lastPong, + IsActive: client.isClientActive(), + } + clients = append(clients, clientInfo) + } + + return clients +} + +// GetMonitoringData mengembalikan data monitoring lengkap +func (m *MonitoringManager) GetMonitoringData() MonitoringData { + stats := m.GetDetailedStats() + + m.hub.activityMu.RLock() + recentActivity := make([]ActivityLog, 0) + // Dapatkan 100 aktivitas terakhir + start := len(m.hub.activityLog) - 100 + if start < 0 { + start = 0 + } + for i := start; i < len(m.hub.activityLog); i++ { + recentActivity = append(recentActivity, m.hub.activityLog[i]) + } + m.hub.activityMu.RUnlock() + + // Dapatkan kesehatan sistem dari layanan database + systemHealth := make(map[string]interface{}) + if m.hub.dbService != nil { + systemHealth["databases"] = m.hub.dbService.Health() + systemHealth["available_dbs"] = m.hub.dbService.ListDBs() + } + systemHealth["websocket_status"] = "healthy" + systemHealth["uptime_seconds"] = time.Since(m.hub.startTime).Seconds() + + // Hitung metrik performa + uptime := time.Since(m.hub.startTime) + var messagesPerSecond float64 + var errorRate float64 + + if uptime.Seconds() > 0 { + messagesPerSecond = float64(m.hub.messageCount) / uptime.Seconds() + } + + if m.hub.messageCount > 0 { + errorRate = (float64(m.hub.errorCount) / float64(m.hub.messageCount)) * 100 + } + + performance := PerformanceMetrics{ + MessagesPerSecond: messagesPerSecond, + AverageLatency: 2.5, // Nilai mock - implementasi pelacakan latensi aktual + ErrorRate: errorRate, + MemoryUsage: 0, // Nilai mock - implementasi pelacakan memori aktual + } + + return MonitoringData{ + Stats: stats, + RecentActivity: recentActivity, + SystemHealth: systemHealth, + Performance: performance, + } +} + +// CleanupInactiveClients membersihkan klien tidak aktif +func (m *MonitoringManager) CleanupInactiveClients(inactiveTimeout time.Duration) int { + m.hub.mu.RLock() + var inactiveClients []*Client + cutoff := time.Now().Add(-inactiveTimeout) + + for client := range m.hub.clients { + if client.lastPing.Before(cutoff) { + inactiveClients = append(inactiveClients, client) + } + } + m.hub.mu.RUnlock() + + // Putuskan koneksi klien tidak aktif + for _, client := range inactiveClients { + m.hub.logActivity("cleanup_disconnect", client.ID, + fmt.Sprintf("Inactive for %v", time.Since(client.lastPing))) + client.cancel() + client.Conn.Close() + } + + return len(inactiveClients) +} + +// DisconnectClient memutuskan koneksi klien tertentu +func (m *MonitoringManager) DisconnectClient(clientID string) bool { + m.hub.mu.RLock() + client, exists := m.hub.clientsByID[clientID] + m.hub.mu.RUnlock() + + if !exists { + return false + } + + // Log aktivitas + m.hub.logActivity("force_disconnect", clientID, "Client disconnected by admin") + + // Batalkan context dan tutup koneksi + client.cancel() + client.Conn.Close() + + return true +}