diff --git a/.env b/.env index 68d29e4..f3148d7 100644 --- a/.env +++ b/.env @@ -1,4 +1,11 @@ APP_NAME=Laravel +MINIO_ACCESS_KEY_ID=N7uKWNiKTmuMK8T3uxL8 +MINIO_SECRET_ACCESS_KEY=pANDWUYWAGJymyuRrWyx7WVduYrOyOtjaLcl9Xi6 +MINIO_DEFAULT_REGION=us-east-1 +MINIO_BUCKET=file +MINIO_ENDPOINT=https://storage.manjapro.net +MINIO_USE_PATH_STYLE_ENDPOINT=true +MINIO_CA_BUNDLE="C:/Program Files/Git/usr/ssl/certs/ca-bundle.crt" APP_ENV=local APP_KEY=base64:nAnc26mWBQ848KLUo97mNg1/iXHA09c9oSrOLW9CWQ0= APP_DEBUG=true diff --git a/.env.example b/.env.example index 35db1dd..a8b21b9 100644 --- a/.env.example +++ b/.env.example @@ -62,4 +62,12 @@ AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false +MINIO_ACCESS_KEY_ID= +MINIO_SECRET_ACCESS_KEY= +MINIO_DEFAULT_REGION=us-east-1 +MINIO_BUCKET= +MINIO_ENDPOINT= +MINIO_USE_PATH_STYLE_ENDPOINT=true +MINIO_CA_BUNDLE= + VITE_APP_NAME="${APP_NAME}" diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..315265e --- /dev/null +++ b/TODO.md @@ -0,0 +1,8 @@ +# TODO - Menu Group Available Menus & Restriction + +- [x] Add service method to fetch allowed menu IDs for authenticated user +- [x] Add service method to provide available menu list for a target menu group based on authenticated user access +- [x] Restrict `syncMenus` so assigned menu IDs must be subset of authenticated user allowed menus (except master user) +- [x] Add controller endpoint for `GET /menu-groups/{menu_group}/available-menus` +- [x] Register route for `available-menus` endpoint in `routes/api.php` +- [ ] Run API testing (critical/thorough as requested) diff --git a/api.json b/api.json new file mode 100644 index 0000000..a6a7a4f --- /dev/null +++ b/api.json @@ -0,0 +1,8895 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Laravel", + "version": "0.0.1" + }, + "servers": [ + { + "url": "http://localhost/api" + } + ], + "security": [ + { + "http": [] + } + ], + "paths": { + "/audit-logs": { + "get": { + "operationId": "auditLog.index", + "tags": [ + "AuditLog" + ], + "parameters": [ + { + "name": "module", + "in": "query", + "description": "Filter berdasarkan nama modul audit.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 100 + } + }, + { + "name": "action", + "in": "query", + "description": "Filter berdasarkan nama action audit.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 100 + } + }, + { + "name": "user_id", + "in": "query", + "description": "Filter berdasarkan ID user pelaku.", + "schema": { + "type": [ + "integer", + "null" + ] + } + }, + { + "name": "tenant_id", + "in": "query", + "description": "Filter berdasarkan ID tenant.", + "schema": { + "type": [ + "integer", + "null" + ] + } + }, + { + "name": "page", + "in": "query", + "description": "Nomor halaman yang ingin diambil.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Jumlah data per halaman, maksimal 100.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Data audit log berhasil diambil" + }, + "data": { + "type": "string" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/audit-logs/{id}": { + "get": { + "operationId": "auditLog.show", + "tags": [ + "AuditLog" + ], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Detail audit log berhasil diambil" + }, + "data": { + "$ref": "#/components/schemas/AuditLogResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/login": { + "post": { + "operationId": "auth.login", + "tags": [ + "Auth" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "required": [ + "login", + "password" + ] + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "token": { + "type": "string" + }, + "user": { + "anyOf": [ + { + "$ref": "#/components/schemas/User" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "success", + "token", + "user" + ] + } + } + } + }, + "403": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string", + "const": "Akun belum aktif. Silakan verifikasi terlebih dahulu." + } + }, + "required": [ + "success", + "message" + ] + } + } + } + }, + "401": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string", + "const": "Username/email atau password salah." + } + }, + "required": [ + "success", + "message" + ] + } + } + } + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + }, + "security": [] + } + }, + "/register": { + "post": { + "operationId": "auth.register", + "tags": [ + "Auth" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterRequest" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string", + "const": "Register berhasil, silakan verifikasi kode." + }, + "data": { + "type": "object", + "properties": { + "user": { + "$ref": "#/components/schemas/User" + }, + "verification": { + "type": "object", + "properties": { + "channel": { + "type": [ + "string", + "null" + ] + }, + "target": { + "type": [ + "string", + "null" + ] + }, + "code": { + "type": "string" + }, + "expires_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "channel", + "target", + "code", + "expires_at" + ] + } + }, + "required": [ + "user", + "verification" + ] + } + }, + "required": [ + "success", + "message", + "data" + ] + } + } + } + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + }, + "security": [] + } + }, + "/register/verify": { + "post": { + "operationId": "auth.verifyRegistration", + "tags": [ + "Auth" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VerifyRegistrationRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string", + "const": "Verifikasi berhasil, user sudah aktif." + }, + "data": { + "$ref": "#/components/schemas/User" + } + }, + "required": [ + "success", + "message", + "data" + ] + } + } + } + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + }, + "security": [] + } + }, + "/register/resend-code": { + "post": { + "operationId": "auth.resendVerificationCode", + "tags": [ + "Auth" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResendVerificationCodeRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "message": { + "type": "string", + "const": "Kode verifikasi baru berhasil dibuat." + }, + "data": { + "type": "object", + "properties": { + "verification": { + "type": "object", + "properties": { + "channel": { + "type": [ + "string", + "null" + ] + }, + "target": { + "type": [ + "string", + "null" + ] + }, + "code": { + "type": "string" + }, + "expires_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "channel", + "target", + "code", + "expires_at" + ] + } + }, + "required": [ + "verification" + ] + } + }, + "required": [ + "success", + "message", + "data" + ] + } + } + } + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + }, + "security": [] + } + }, + "/materials": { + "get": { + "operationId": "material.index", + "tags": [ + "Material" + ], + "parameters": [ + { + "name": "search", + "in": "query", + "description": "Kata kunci untuk mencari barcode atau nama material.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + } + }, + { + "name": "user_id", + "in": "query", + "description": "Filter material berdasarkan ID user/teknisi.", + "schema": { + "type": [ + "integer", + "null" + ] + } + }, + { + "name": "type", + "in": "query", + "description": "Filter jenis material.", + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "serialized", + "consumable" + ] + } + }, + { + "name": "status", + "in": "query", + "description": "Filter status material.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 50 + } + }, + { + "name": "page", + "in": "query", + "description": "Nomor halaman yang ingin diambil.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Jumlah data per halaman, maksimal 100.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "List material user berhasil diambil" + }, + "data": { + "type": "string" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/materials/assign": { + "post": { + "operationId": "material.assign", + "tags": [ + "Material" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AssignMaterialRequest" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 201 + }, + "message": { + "type": "string", + "const": "Material berhasil di-assign" + }, + "data": { + "$ref": "#/components/schemas/MaterialResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/materials/transfer": { + "post": { + "operationId": "material.transfer", + "tags": [ + "Material" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TransferMaterialRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Material berhasil di-transfer" + }, + "data": { + "$ref": "#/components/schemas/MaterialResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/materials/return": { + "post": { + "operationId": "material.return", + "tags": [ + "Material" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReturnMaterialRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Material berhasil direturn" + }, + "data": { + "$ref": "#/components/schemas/MaterialResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/materials/user/{user_id}": { + "get": { + "operationId": "material.listByUser", + "tags": [ + "Material" + ], + "parameters": [ + { + "name": "user_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "List material user berhasil diambil" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MaterialResource" + } + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/materials/history/{barcode_id}": { + "get": { + "operationId": "material.history", + "tags": [ + "Material" + ], + "parameters": [ + { + "name": "barcode_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "History material berhasil diambil" + }, + "data": { + "type": "string" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/me/menus": { + "get": { + "operationId": "menu.myMenus", + "tags": [ + "Menu" + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Menu berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "is_master": { + "type": "boolean" + }, + "access_level": { + "type": "string" + }, + "tenant_id": { + "type": "string" + }, + "menus": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "parent_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "component": { + "type": "string" + }, + "sort_order": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "children": { + "type": "array", + "items": {} + } + }, + "required": [ + "id", + "parent_id", + "name", + "slug", + "url", + "icon", + "component", + "sort_order", + "permissions", + "children" + ] + } + } + }, + "required": [ + "is_master", + "access_level", + "tenant_id", + "menus" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/menu-groups": { + "get": { + "operationId": "menu-groups.index", + "tags": [ + "MenuGroup" + ], + "parameters": [ + { + "name": "search", + "in": "query", + "description": "Kata kunci untuk mencari nama atau deskripsi menu group.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + } + }, + { + "name": "is_active", + "in": "query", + "description": "Filter status aktif menu group.", + "schema": { + "type": [ + "boolean", + "null" + ] + } + }, + { + "name": "page", + "in": "query", + "description": "Nomor halaman yang ingin diambil.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Jumlah data per halaman, maksimal 100.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Data Menu Group berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "current_page": { + "type": "integer", + "minimum": 1 + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MenuGroup" + } + }, + "first_page_url": { + "type": [ + "string", + "null" + ] + }, + "from": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "last_page_url": { + "type": [ + "string", + "null" + ] + }, + "last_page": { + "type": "integer", + "minimum": 1 + }, + "links": { + "type": "array", + "description": "Generated paginator links.", + "items": { + "type": "object", + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "label": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": [ + "url", + "label", + "active" + ] + } + }, + "next_page_url": { + "type": [ + "string", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ], + "description": "Base path for paginator generated URLs." + }, + "per_page": { + "type": "integer", + "description": "Number of items shown per page.", + "minimum": 0 + }, + "prev_page_url": { + "type": [ + "string", + "null" + ] + }, + "to": { + "type": [ + "integer", + "null" + ], + "description": "Number of the last item in the slice.", + "minimum": 1 + }, + "total": { + "type": "integer", + "description": "Total number of items being paginated.", + "minimum": 0 + } + }, + "required": [ + "current_page", + "data", + "first_page_url", + "from", + "last_page_url", + "last_page", + "links", + "next_page_url", + "path", + "per_page", + "prev_page_url", + "to", + "total" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "post": { + "operationId": "menu-groups.store", + "tags": [ + "MenuGroup" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreMenuGroupRequest" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 201 + }, + "message": { + "type": "string", + "const": "Menu Group berhasil dibuat" + }, + "data": { + "$ref": "#/components/schemas/MenuGroupResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/menu-groups/{menu_group}": { + "get": { + "operationId": "menu-groups.show", + "tags": [ + "MenuGroup" + ], + "parameters": [ + { + "name": "menu_group", + "in": "path", + "required": true, + "description": "The menu group ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Detail Menu Group berhasil diambil" + }, + "data": { + "$ref": "#/components/schemas/MenuGroupResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + }, + "put": { + "operationId": "menu-groups.update", + "tags": [ + "MenuGroup" + ], + "parameters": [ + { + "name": "menu_group", + "in": "path", + "required": true, + "description": "The menu group ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMenuGroupRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Menu Group berhasil diupdate" + }, + "data": { + "$ref": "#/components/schemas/MenuGroupResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "delete": { + "operationId": "menu-groups.destroy", + "tags": [ + "MenuGroup" + ], + "parameters": [ + { + "name": "menu_group", + "in": "path", + "required": true, + "description": "The menu group ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Menu Group berhasil dihapus" + } + }, + "required": [ + "success", + "code", + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/menu-groups/{menu_group}/menus": { + "get": { + "operationId": "menuGroup.menus", + "tags": [ + "MenuGroup" + ], + "parameters": [ + { + "name": "menu_group", + "in": "path", + "required": true, + "description": "The menu group ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Mapping menu group berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "menu_group_id": { + "type": "integer" + }, + "menu_group": { + "$ref": "#/components/schemas/MenuGroupResource" + }, + "menus": { + "type": "array", + "items": { + "type": "object", + "properties": { + "menu_id": { + "type": "integer" + }, + "parent_id": { + "type": [ + "integer", + "null" + ] + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "component": { + "type": "string" + }, + "sort_order": { + "type": "integer" + }, + "permissions": { + "type": "object", + "properties": { + "can_view": { + "type": "boolean" + }, + "can_create": { + "type": "boolean" + }, + "can_update": { + "type": "boolean" + }, + "can_delete": { + "type": "boolean" + }, + "can_approve": { + "type": "boolean" + }, + "can_export": { + "type": "boolean" + } + }, + "required": [ + "can_view", + "can_create", + "can_update", + "can_delete", + "can_approve", + "can_export" + ] + } + }, + "required": [ + "menu_id", + "parent_id", + "name", + "slug", + "url", + "icon", + "component", + "sort_order", + "permissions" + ] + } + } + }, + "required": [ + "menu_group_id", + "menu_group", + "menus" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + }, + "put": { + "operationId": "menuGroup.syncMenus", + "tags": [ + "MenuGroup" + ], + "parameters": [ + { + "name": "menu_group", + "in": "path", + "required": true, + "description": "The menu group ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SyncMenuGroupMenusRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Mapping menu group berhasil disimpan" + }, + "data": { + "type": "object", + "properties": { + "menu_group_id": { + "type": "integer" + }, + "menu_group": { + "$ref": "#/components/schemas/MenuGroupResource" + }, + "menus": { + "type": "array", + "items": { + "type": "object", + "properties": { + "menu_id": { + "type": "integer" + }, + "parent_id": { + "type": [ + "integer", + "null" + ] + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "component": { + "type": "string" + }, + "sort_order": { + "type": "integer" + }, + "permissions": { + "type": "object", + "properties": { + "can_view": { + "type": "boolean" + }, + "can_create": { + "type": "boolean" + }, + "can_update": { + "type": "boolean" + }, + "can_delete": { + "type": "boolean" + }, + "can_approve": { + "type": "boolean" + }, + "can_export": { + "type": "boolean" + } + }, + "required": [ + "can_view", + "can_create", + "can_update", + "can_delete", + "can_approve", + "can_export" + ] + } + }, + "required": [ + "menu_id", + "parent_id", + "name", + "slug", + "url", + "icon", + "component", + "sort_order", + "permissions" + ] + } + } + }, + "required": [ + "menu_group_id", + "menu_group", + "menus" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/menu-groups/{menu_group}/available-menus": { + "get": { + "operationId": "menuGroup.availableMenus", + "tags": [ + "MenuGroup" + ], + "parameters": [ + { + "name": "menu_group", + "in": "path", + "required": true, + "description": "The menu group ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Daftar menu tersedia berdasarkan akses user berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "menu_group_id": { + "type": "integer" + }, + "menu_group": { + "$ref": "#/components/schemas/MenuGroupResource" + }, + "menus": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "object", + "properties": { + "menu_id": { + "type": "integer" + }, + "parent_id": { + "type": [ + "integer", + "null" + ] + }, + "name": { + "type": "string" + }, + "sort_order": { + "type": "integer" + }, + "permissions": { + "type": "object", + "properties": { + "can_view": { + "type": "boolean" + }, + "can_create": { + "type": "boolean" + }, + "can_update": { + "type": "boolean" + }, + "can_delete": { + "type": "boolean" + }, + "can_approve": { + "type": "boolean" + }, + "can_export": { + "type": "boolean" + } + }, + "required": [ + "can_view", + "can_create", + "can_update", + "can_delete", + "can_approve", + "can_export" + ] + } + }, + "required": [ + "menu_id", + "parent_id", + "name", + "sort_order", + "permissions" + ] + } + }, + { + "type": "array", + "items": { + "type": "object", + "properties": { + "menu_id": { + "type": "integer" + }, + "parent_id": { + "type": [ + "integer", + "null" + ] + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "component": { + "type": "string" + }, + "sort_order": { + "type": "integer" + }, + "permissions": { + "type": "object", + "properties": { + "can_view": { + "type": "boolean" + }, + "can_create": { + "type": "boolean" + }, + "can_update": { + "type": "boolean" + }, + "can_delete": { + "type": "boolean" + }, + "can_approve": { + "type": "boolean" + }, + "can_export": { + "type": "boolean" + } + }, + "required": [ + "can_view", + "can_create", + "can_update", + "can_delete", + "can_approve", + "can_export" + ] + } + }, + "required": [ + "menu_id", + "parent_id", + "name", + "slug", + "url", + "icon", + "component", + "sort_order", + "permissions" + ] + } + } + ] + } + }, + "required": [ + "menu_group_id", + "menu_group", + "menus" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + } + } + } + }, + "/users/{user}/menu-groups": { + "get": { + "operationId": "menuGroup.userAssignments", + "tags": [ + "MenuGroup" + ], + "parameters": [ + { + "name": "user", + "in": "path", + "required": true, + "description": "The user ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Assignment user menu group berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "assignments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "menu_group_id": { + "type": "integer" + }, + "menu_group_name": { + "type": "string" + }, + "menu_group_is_active": { + "type": "boolean" + }, + "tenant_id": { + "type": [ + "integer", + "null" + ] + }, + "tenant_name": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + }, + "required": [ + "menu_group_id", + "menu_group_name", + "menu_group_is_active", + "tenant_id", + "tenant_name", + "created_at", + "updated_at" + ] + } + } + }, + "required": [ + "user_id", + "assignments" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "403": { + "description": "An error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error overview.", + "example": "" + } + }, + "required": [ + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + }, + "put": { + "operationId": "menuGroup.syncUserAssignments", + "tags": [ + "MenuGroup" + ], + "parameters": [ + { + "name": "user", + "in": "path", + "required": true, + "description": "The user ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SyncUserMenuGroupsRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Assignment user menu group berhasil disimpan" + }, + "data": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "assignments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "menu_group_id": { + "type": "integer" + }, + "menu_group_name": { + "type": "string" + }, + "menu_group_is_active": { + "type": "boolean" + }, + "tenant_id": { + "type": [ + "integer", + "null" + ] + }, + "tenant_name": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "updated_at": { + "type": "string" + } + }, + "required": [ + "menu_group_id", + "menu_group_name", + "menu_group_is_active", + "tenant_id", + "tenant_name", + "created_at", + "updated_at" + ] + } + } + }, + "required": [ + "user_id", + "assignments" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "403": { + "description": "An error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error overview.", + "example": "" + } + }, + "required": [ + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/menus": { + "get": { + "operationId": "menus.index", + "tags": [ + "MenuList" + ], + "parameters": [ + { + "name": "search", + "in": "query", + "description": "Kata kunci untuk mencari nama, slug, atau URL menu.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + } + }, + { + "name": "is_active", + "in": "query", + "description": "Filter status aktif menu.", + "schema": { + "type": [ + "boolean", + "null" + ] + } + }, + { + "name": "page", + "in": "query", + "description": "Nomor halaman yang ingin diambil.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Jumlah data per halaman, maksimal 100.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Data Menu List berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "current_page": { + "type": "integer", + "minimum": 1 + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MenuList" + } + }, + "first_page_url": { + "type": [ + "string", + "null" + ] + }, + "from": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "last_page_url": { + "type": [ + "string", + "null" + ] + }, + "last_page": { + "type": "integer", + "minimum": 1 + }, + "links": { + "type": "array", + "description": "Generated paginator links.", + "items": { + "type": "object", + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "label": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": [ + "url", + "label", + "active" + ] + } + }, + "next_page_url": { + "type": [ + "string", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ], + "description": "Base path for paginator generated URLs." + }, + "per_page": { + "type": "integer", + "description": "Number of items shown per page.", + "minimum": 0 + }, + "prev_page_url": { + "type": [ + "string", + "null" + ] + }, + "to": { + "type": [ + "integer", + "null" + ], + "description": "Number of the last item in the slice.", + "minimum": 1 + }, + "total": { + "type": "integer", + "description": "Total number of items being paginated.", + "minimum": 0 + } + }, + "required": [ + "current_page", + "data", + "first_page_url", + "from", + "last_page_url", + "last_page", + "links", + "next_page_url", + "path", + "per_page", + "prev_page_url", + "to", + "total" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "post": { + "operationId": "menus.store", + "tags": [ + "MenuList" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreMenuListRequest" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 201 + }, + "message": { + "type": "string", + "const": "Menu List berhasil dibuat" + }, + "data": { + "$ref": "#/components/schemas/MenuListResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/menus/{menu_list}": { + "get": { + "operationId": "menus.show", + "tags": [ + "MenuList" + ], + "parameters": [ + { + "name": "menu_list", + "in": "path", + "required": true, + "description": "The menu list ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Detail Menu List berhasil diambil" + }, + "data": { + "$ref": "#/components/schemas/MenuListResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + }, + "put": { + "operationId": "menus.update", + "tags": [ + "MenuList" + ], + "parameters": [ + { + "name": "menu_list", + "in": "path", + "required": true, + "description": "The menu list ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateMenuListRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Menu List berhasil diupdate" + }, + "data": { + "$ref": "#/components/schemas/MenuListResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "delete": { + "operationId": "menus.destroy", + "tags": [ + "MenuList" + ], + "parameters": [ + { + "name": "menu_list", + "in": "path", + "required": true, + "description": "The menu list ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Menu List berhasil dihapus" + } + }, + "required": [ + "success", + "code", + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/me/profile": { + "get": { + "operationId": "profile.show", + "tags": [ + "Profile" + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Profile berhasil diambil" + }, + "data": { + "$ref": "#/components/schemas/UserResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + }, + "put": { + "operationId": "profile.update", + "tags": [ + "Profile" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProfileRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Profile berhasil diperbarui" + }, + "data": { + "$ref": "#/components/schemas/UserResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tenants": { + "get": { + "operationId": "tenants.index", + "tags": [ + "Tenant" + ], + "parameters": [ + { + "name": "search", + "in": "query", + "description": "Kata kunci untuk mencari kode, nama, atau email tenant.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + } + }, + { + "name": "status", + "in": "query", + "description": "Filter status tenant.", + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "active", + "inactive" + ] + } + }, + { + "name": "page", + "in": "query", + "description": "Nomor halaman yang ingin diambil.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Jumlah data per halaman, maksimal 100.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Data Tenant berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "current_page": { + "type": "integer", + "minimum": 1 + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Tenant" + } + }, + "first_page_url": { + "type": [ + "string", + "null" + ] + }, + "from": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "last_page_url": { + "type": [ + "string", + "null" + ] + }, + "last_page": { + "type": "integer", + "minimum": 1 + }, + "links": { + "type": "array", + "description": "Generated paginator links.", + "items": { + "type": "object", + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "label": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": [ + "url", + "label", + "active" + ] + } + }, + "next_page_url": { + "type": [ + "string", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ], + "description": "Base path for paginator generated URLs." + }, + "per_page": { + "type": "integer", + "description": "Number of items shown per page.", + "minimum": 0 + }, + "prev_page_url": { + "type": [ + "string", + "null" + ] + }, + "to": { + "type": [ + "integer", + "null" + ], + "description": "Number of the last item in the slice.", + "minimum": 1 + }, + "total": { + "type": "integer", + "description": "Total number of items being paginated.", + "minimum": 0 + } + }, + "required": [ + "current_page", + "data", + "first_page_url", + "from", + "last_page_url", + "last_page", + "links", + "next_page_url", + "path", + "per_page", + "prev_page_url", + "to", + "total" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "post": { + "operationId": "tenants.store", + "tags": [ + "Tenant" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreTenantRequest" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 201 + }, + "message": { + "type": "string", + "const": "Tenant berhasil dibuat" + }, + "data": { + "$ref": "#/components/schemas/TenantResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tenants/{tenant}": { + "get": { + "operationId": "tenants.show", + "tags": [ + "Tenant" + ], + "parameters": [ + { + "name": "tenant", + "in": "path", + "required": true, + "description": "The tenant ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Detail Tenant berhasil diambil" + }, + "data": { + "$ref": "#/components/schemas/TenantResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + }, + "put": { + "operationId": "tenants.update", + "tags": [ + "Tenant" + ], + "parameters": [ + { + "name": "tenant", + "in": "path", + "required": true, + "description": "The tenant ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTenantRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Tenant berhasil diupdate" + }, + "data": { + "$ref": "#/components/schemas/TenantResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "delete": { + "operationId": "tenants.destroy", + "tags": [ + "Tenant" + ], + "parameters": [ + { + "name": "tenant", + "in": "path", + "required": true, + "description": "The tenant ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Tenant berhasil dihapus" + } + }, + "required": [ + "success", + "code", + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/tickets": { + "get": { + "operationId": "tickets.index", + "tags": [ + "Ticket" + ], + "parameters": [ + { + "name": "search", + "in": "query", + "description": "Kata kunci untuk mencari nomor, judul, atau deskripsi ticket.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + } + }, + { + "name": "status", + "in": "query", + "description": "Filter berdasarkan status ticket.", + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "draft", + "open", + "approved", + "assigned", + "progress", + "pending", + "resolved", + "closed", + "cancelled", + "rejected" + ] + } + }, + { + "name": "priority", + "in": "query", + "description": "Filter berdasarkan prioritas ticket.", + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "low", + "medium", + "high", + "critical" + ] + } + }, + { + "name": "ticket_type_id", + "in": "query", + "description": "Filter berdasarkan ID tipe ticket.", + "schema": { + "type": [ + "integer", + "null" + ] + } + }, + { + "name": "customer_id", + "in": "query", + "description": "Filter berdasarkan ID customer.", + "schema": { + "type": [ + "integer", + "null" + ] + } + }, + { + "name": "page", + "in": "query", + "description": "Nomor halaman yang ingin diambil.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Jumlah data per halaman, maksimal 100.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Data Ticket berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "current_page": { + "type": "integer", + "minimum": 1 + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Ticket" + } + }, + "first_page_url": { + "type": [ + "string", + "null" + ] + }, + "from": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "last_page_url": { + "type": [ + "string", + "null" + ] + }, + "last_page": { + "type": "integer", + "minimum": 1 + }, + "links": { + "type": "array", + "description": "Generated paginator links.", + "items": { + "type": "object", + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "label": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": [ + "url", + "label", + "active" + ] + } + }, + "next_page_url": { + "type": [ + "string", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ], + "description": "Base path for paginator generated URLs." + }, + "per_page": { + "type": "integer", + "description": "Number of items shown per page.", + "minimum": 0 + }, + "prev_page_url": { + "type": [ + "string", + "null" + ] + }, + "to": { + "type": [ + "integer", + "null" + ], + "description": "Number of the last item in the slice.", + "minimum": 1 + }, + "total": { + "type": "integer", + "description": "Total number of items being paginated.", + "minimum": 0 + } + }, + "required": [ + "current_page", + "data", + "first_page_url", + "from", + "last_page_url", + "last_page", + "links", + "next_page_url", + "path", + "per_page", + "prev_page_url", + "to", + "total" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "post": { + "operationId": "tickets.store", + "tags": [ + "Ticket" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreTicketRequest" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 201 + }, + "message": { + "type": "string", + "const": "Ticket berhasil dibuat" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tickets/{ticket}": { + "get": { + "operationId": "tickets.show", + "tags": [ + "Ticket" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Success" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + }, + "put": { + "operationId": "tickets.update", + "tags": [ + "Ticket" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket berhasil diperbarui" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "delete": { + "operationId": "tickets.destroy", + "tags": [ + "Ticket" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket berhasil dibatalkan" + } + }, + "required": [ + "success", + "code", + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/ticket-incident-types": { + "get": { + "operationId": "ticket-incident-types.index", + "tags": [ + "TicketIncidentType" + ], + "parameters": [ + { + "name": "search", + "in": "query", + "description": "Kata kunci untuk mencari nama incident type.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + } + }, + { + "name": "is_active", + "in": "query", + "description": "Filter status aktif incident type.", + "schema": { + "type": [ + "boolean", + "null" + ] + } + }, + { + "name": "page", + "in": "query", + "description": "Nomor halaman yang ingin diambil.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Jumlah data per halaman, maksimal 100.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Data Incident Type berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "current_page": { + "type": "integer", + "minimum": 1 + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TicketIncidentType" + } + }, + "first_page_url": { + "type": [ + "string", + "null" + ] + }, + "from": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "last_page_url": { + "type": [ + "string", + "null" + ] + }, + "last_page": { + "type": "integer", + "minimum": 1 + }, + "links": { + "type": "array", + "description": "Generated paginator links.", + "items": { + "type": "object", + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "label": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": [ + "url", + "label", + "active" + ] + } + }, + "next_page_url": { + "type": [ + "string", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ], + "description": "Base path for paginator generated URLs." + }, + "per_page": { + "type": "integer", + "description": "Number of items shown per page.", + "minimum": 0 + }, + "prev_page_url": { + "type": [ + "string", + "null" + ] + }, + "to": { + "type": [ + "integer", + "null" + ], + "description": "Number of the last item in the slice.", + "minimum": 1 + }, + "total": { + "type": "integer", + "description": "Total number of items being paginated.", + "minimum": 0 + } + }, + "required": [ + "current_page", + "data", + "first_page_url", + "from", + "last_page_url", + "last_page", + "links", + "next_page_url", + "path", + "per_page", + "prev_page_url", + "to", + "total" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "post": { + "operationId": "ticket-incident-types.store", + "tags": [ + "TicketIncidentType" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreTicketIncidentTypeRequest" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 201 + }, + "message": { + "type": "string", + "const": "Incident Type berhasil dibuat" + }, + "data": { + "$ref": "#/components/schemas/TicketIncidentTypeResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/ticket-incident-types/{ticketIncidentType}": { + "get": { + "operationId": "ticket-incident-types.show", + "tags": [ + "TicketIncidentType" + ], + "parameters": [ + { + "name": "ticketIncidentType", + "in": "path", + "required": true, + "description": "The ticket incident type ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Success" + }, + "data": { + "$ref": "#/components/schemas/TicketIncidentTypeResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + }, + "put": { + "operationId": "ticket-incident-types.update", + "tags": [ + "TicketIncidentType" + ], + "parameters": [ + { + "name": "ticketIncidentType", + "in": "path", + "required": true, + "description": "The ticket incident type ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTicketIncidentTypeRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Incident Type berhasil diupdate" + }, + "data": { + "$ref": "#/components/schemas/TicketIncidentTypeResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "delete": { + "operationId": "ticket-incident-types.destroy", + "tags": [ + "TicketIncidentType" + ], + "parameters": [ + { + "name": "ticketIncidentType", + "in": "path", + "required": true, + "description": "The ticket incident type ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Incident Type berhasil dihapus" + } + }, + "required": [ + "success", + "code", + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/ticket-types": { + "get": { + "operationId": "ticket-types.index", + "tags": [ + "TicketType" + ], + "parameters": [ + { + "name": "search", + "in": "query", + "description": "Kata kunci untuk mencari kode atau nama tipe ticket.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + } + }, + { + "name": "page", + "in": "query", + "description": "Nomor halaman yang ingin diambil.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Jumlah data per halaman, maksimal 100.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Data Ticket Type berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "current_page": { + "type": "integer", + "minimum": 1 + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TicketType" + } + }, + "first_page_url": { + "type": [ + "string", + "null" + ] + }, + "from": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "last_page_url": { + "type": [ + "string", + "null" + ] + }, + "last_page": { + "type": "integer", + "minimum": 1 + }, + "links": { + "type": "array", + "description": "Generated paginator links.", + "items": { + "type": "object", + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "label": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": [ + "url", + "label", + "active" + ] + } + }, + "next_page_url": { + "type": [ + "string", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ], + "description": "Base path for paginator generated URLs." + }, + "per_page": { + "type": "integer", + "description": "Number of items shown per page.", + "minimum": 0 + }, + "prev_page_url": { + "type": [ + "string", + "null" + ] + }, + "to": { + "type": [ + "integer", + "null" + ], + "description": "Number of the last item in the slice.", + "minimum": 1 + }, + "total": { + "type": "integer", + "description": "Total number of items being paginated.", + "minimum": 0 + } + }, + "required": [ + "current_page", + "data", + "first_page_url", + "from", + "last_page_url", + "last_page", + "links", + "next_page_url", + "path", + "per_page", + "prev_page_url", + "to", + "total" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "post": { + "operationId": "ticket-types.store", + "tags": [ + "TicketType" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreTicketTypeRequest" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 201 + }, + "message": { + "type": "string", + "const": "Ticket Type berhasil dibuat" + }, + "data": { + "$ref": "#/components/schemas/TicketTypeResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/ticket-types/{ticket_type}": { + "get": { + "operationId": "ticket-types.show", + "tags": [ + "TicketType" + ], + "parameters": [ + { + "name": "ticket_type", + "in": "path", + "required": true, + "description": "The ticket type ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Detail Ticket Type berhasil diambil" + }, + "data": { + "$ref": "#/components/schemas/TicketTypeResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + }, + "put": { + "operationId": "ticket-types.update", + "tags": [ + "TicketType" + ], + "parameters": [ + { + "name": "ticket_type", + "in": "path", + "required": true, + "description": "The ticket type ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateTicketTypeRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket Type berhasil diupdate" + }, + "data": { + "$ref": "#/components/schemas/TicketTypeResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "delete": { + "operationId": "ticket-types.destroy", + "tags": [ + "TicketType" + ], + "parameters": [ + { + "name": "ticket_type", + "in": "path", + "required": true, + "description": "The ticket type ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket Type berhasil dihapus" + } + }, + "required": [ + "success", + "code", + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/tickets/{ticket}/workflow": { + "get": { + "operationId": "ticketWorkflow.history", + "summary": "History Workflow", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Workflow berhasil diambil" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/TicketLog" + } + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/tickets/{ticket}/assign": { + "post": { + "operationId": "ticketWorkflow.assign", + "summary": "Assign Teknisi", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AssignTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Teknisi berhasil ditugaskan" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tickets/{ticket}/reassign": { + "post": { + "operationId": "ticketWorkflow.reassign", + "summary": "Reassign Teknisi", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReassignTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Penugasan berhasil diperbarui" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tickets/{ticket}/approve": { + "post": { + "operationId": "ticketWorkflow.approve", + "summary": "Approve", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApproveTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket berhasil disetujui" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tickets/{ticket}/reject": { + "post": { + "operationId": "ticketWorkflow.reject", + "summary": "Reject", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RejectTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket berhasil ditolak" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tickets/{ticket}/start": { + "post": { + "operationId": "ticketWorkflow.start", + "summary": "Start Work", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StartTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Pekerjaan dimulai" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tickets/{ticket}/pending": { + "post": { + "operationId": "ticketWorkflow.pending", + "summary": "Pending", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PendingTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket pending" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tickets/{ticket}/resume": { + "post": { + "operationId": "ticketWorkflow.resume", + "summary": "Resume", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket dilanjutkan" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + }, + "/tickets/{ticket}/resolve": { + "post": { + "operationId": "ticketWorkflow.resolve", + "summary": "Resolve", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ResolveTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket berhasil diselesaikan" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tickets/{ticket}/close": { + "post": { + "operationId": "ticketWorkflow.close", + "summary": "Close", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CloseTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket berhasil ditutup" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tickets/{ticket}/cancel": { + "post": { + "operationId": "ticketWorkflow.cancel", + "summary": "Cancel", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CancelTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket berhasil dibatalkan" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/tickets/{ticket}/escalate": { + "post": { + "operationId": "ticketWorkflow.escalate", + "summary": "Escalate", + "tags": [ + "TicketWorkflow" + ], + "parameters": [ + { + "name": "ticket", + "in": "path", + "required": true, + "description": "The ticket ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EscalateTicketRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Ticket berhasil dieskalasi" + }, + "data": { + "$ref": "#/components/schemas/TicketResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/users": { + "get": { + "operationId": "users.index", + "tags": [ + "User" + ], + "parameters": [ + { + "name": "search", + "in": "query", + "description": "Kata kunci untuk mencari nama, username, email, atau nomor WhatsApp.", + "schema": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + } + }, + { + "name": "status", + "in": "query", + "description": "Filter status akun user.", + "schema": { + "type": [ + "string", + "null" + ], + "enum": [ + "active", + "inactive", + "suspended" + ] + } + }, + { + "name": "access_level", + "in": "query", + "description": "Filter level akses user.", + "schema": { + "$ref": "#/components/schemas/UserAccessLevel" + } + }, + { + "name": "page", + "in": "query", + "description": "Nomor halaman yang ingin diambil.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + } + }, + { + "name": "per_page", + "in": "query", + "description": "Jumlah data per halaman, maksimal 100.", + "schema": { + "type": [ + "integer", + "null" + ], + "minimum": 1, + "maximum": 100 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Data User berhasil diambil" + }, + "data": { + "type": "object", + "properties": { + "current_page": { + "type": "integer", + "minimum": 1 + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + }, + "first_page_url": { + "type": [ + "string", + "null" + ] + }, + "from": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "last_page_url": { + "type": [ + "string", + "null" + ] + }, + "last_page": { + "type": "integer", + "minimum": 1 + }, + "links": { + "type": "array", + "description": "Generated paginator links.", + "items": { + "type": "object", + "properties": { + "url": { + "type": [ + "string", + "null" + ] + }, + "label": { + "type": "string" + }, + "active": { + "type": "boolean" + } + }, + "required": [ + "url", + "label", + "active" + ] + } + }, + "next_page_url": { + "type": [ + "string", + "null" + ] + }, + "path": { + "type": [ + "string", + "null" + ], + "description": "Base path for paginator generated URLs." + }, + "per_page": { + "type": "integer", + "description": "Number of items shown per page.", + "minimum": 0 + }, + "prev_page_url": { + "type": [ + "string", + "null" + ] + }, + "to": { + "type": [ + "integer", + "null" + ], + "description": "Number of the last item in the slice.", + "minimum": 1 + }, + "total": { + "type": "integer", + "description": "Total number of items being paginated.", + "minimum": 0 + } + }, + "required": [ + "current_page", + "data", + "first_page_url", + "from", + "last_page_url", + "last_page", + "links", + "next_page_url", + "path", + "per_page", + "prev_page_url", + "to", + "total" + ] + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "post": { + "operationId": "users.store", + "tags": [ + "User" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/StoreUserRequest" + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 201 + }, + "message": { + "type": "string", + "const": "User berhasil dibuat" + }, + "data": { + "$ref": "#/components/schemas/UserResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + } + }, + "/users/{user}": { + "get": { + "operationId": "users.show", + "tags": [ + "User" + ], + "parameters": [ + { + "name": "user", + "in": "path", + "required": true, + "description": "The user ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "Detail User berhasil diambil" + }, + "data": { + "$ref": "#/components/schemas/UserResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "403": { + "description": "An error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error overview.", + "example": "" + } + }, + "required": [ + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + }, + "put": { + "operationId": "users.update", + "tags": [ + "User" + ], + "parameters": [ + { + "name": "user", + "in": "path", + "required": true, + "description": "The user ID", + "schema": { + "type": "integer" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateUserRequest" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "User berhasil diupdate" + }, + "data": { + "$ref": "#/components/schemas/UserResource" + } + }, + "required": [ + "success", + "code", + "message", + "data" + ] + } + } + } + }, + "403": { + "description": "An error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error overview.", + "example": "" + } + }, + "required": [ + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + }, + "422": { + "$ref": "#/components/responses/ValidationException" + } + } + }, + "delete": { + "operationId": "users.destroy", + "tags": [ + "User" + ], + "parameters": [ + { + "name": "user", + "in": "path", + "required": true, + "description": "The user ID", + "schema": { + "type": "integer" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "code": { + "type": "integer", + "const": 200 + }, + "message": { + "type": "string", + "const": "User berhasil dihapus" + } + }, + "required": [ + "success", + "code", + "message" + ] + } + } + } + }, + "403": { + "description": "An error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error overview.", + "example": "" + } + }, + "required": [ + "message" + ] + } + } + } + }, + "404": { + "$ref": "#/components/responses/ModelNotFoundException" + }, + "401": { + "$ref": "#/components/responses/AuthenticationException" + } + } + } + } + }, + "components": { + "securitySchemes": { + "http": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "ApproveTicketRequest": { + "type": "object", + "properties": { + "notes": { + "type": [ + "string", + "null" + ], + "maxLength": 1000 + } + }, + "title": "ApproveTicketRequest" + }, + "AssignMaterialRequest": { + "type": "object", + "properties": { + "user_id": { + "type": "integer" + }, + "barcode_id": { + "type": "string" + }, + "material_name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "serialized", + "consumable" + ] + }, + "serial_number": { + "type": [ + "string", + "null" + ] + }, + "qty": { + "type": [ + "number", + "null" + ] + }, + "unit": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "user_id", + "barcode_id", + "material_name", + "type" + ], + "title": "AssignMaterialRequest" + }, + "AssignTicketRequest": { + "type": "object", + "properties": { + "technicians": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "leader_id": { + "type": "integer" + } + }, + "required": [ + "technicians", + "leader_id" + ], + "title": "AssignTicketRequest" + }, + "AuditLogResource": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "user_id": { + "type": [ + "integer", + "null" + ] + }, + "tenant_id": { + "type": [ + "integer", + "null" + ] + }, + "module": { + "type": "string" + }, + "action": { + "type": "string" + }, + "table_name": { + "type": [ + "string", + "null" + ] + }, + "record_id": { + "type": [ + "integer", + "null" + ] + }, + "old_values": { + "type": [ + "array", + "null" + ], + "items": {} + }, + "new_values": { + "type": [ + "array", + "null" + ], + "items": {} + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "ip_address": { + "type": [ + "string", + "null" + ] + }, + "created_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "id", + "user_id", + "tenant_id", + "module", + "action", + "table_name", + "record_id", + "old_values", + "new_values", + "description", + "ip_address", + "created_at" + ], + "title": "AuditLogResource" + }, + "CancelTicketRequest": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "maxLength": 2000 + } + }, + "required": [ + "reason" + ], + "title": "CancelTicketRequest" + }, + "CloseTicketRequest": { + "type": "object", + "properties": { + "notes": { + "type": [ + "string", + "null" + ], + "maxLength": 2000 + } + }, + "title": "CloseTicketRequest" + }, + "EscalateTicketRequest": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "maxLength": 2000 + }, + "level": { + "type": [ + "string", + "null" + ], + "enum": [ + "L1", + "L2", + "L3", + "NOC" + ] + } + }, + "required": [ + "reason" + ], + "title": "EscalateTicketRequest" + }, + "MaterialResource": { + "type": "object", + "properties": { + "barcode_id": { + "type": "string", + "description": "universal" + }, + "material_name": { + "type": "string" + }, + "user_id": { + "type": "string" + }, + "type": { + "type": "string", + "description": "detect type model", + "enum": [ + "serialized", + "consumable" + ] + }, + "serial_number": { + "type": "string", + "description": "serialized only" + }, + "qty": { + "type": "object", + "description": "consumable only", + "properties": { + "received": { + "type": [ + "string", + "null" + ] + }, + "used": { + "type": [ + "string", + "null" + ] + }, + "remaining": { + "type": [ + "string", + "null" + ] + }, + "unit": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "received", + "used", + "remaining", + "unit" + ] + }, + "status": { + "type": [ + "string", + "null" + ], + "description": "status (beda logic tapi tetap dipakai keduanya)" + }, + "assigned_at": { + "type": [ + "string", + "null" + ], + "description": "optional tracking info" + } + }, + "required": [ + "barcode_id", + "material_name", + "user_id", + "type", + "status", + "assigned_at" + ], + "title": "MaterialResource" + }, + "MenuGroup": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "is_active": { + "type": "boolean" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "tenant_id": { + "type": [ + "integer", + "null" + ] + }, + "is_system": { + "type": "boolean" + } + }, + "required": [ + "id", + "name", + "description", + "is_active", + "created_at", + "updated_at", + "tenant_id", + "is_system" + ], + "title": "MenuGroup" + }, + "MenuGroupResource": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "tenant_id": { + "type": [ + "integer", + "null" + ] + }, + "name": { + "type": "string" + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "is_system": { + "type": "boolean" + }, + "is_active": { + "type": "boolean" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "tenant_id", + "name", + "description", + "is_system", + "is_active", + "created_at", + "updated_at" + ], + "title": "MenuGroupResource" + }, + "MenuList": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "parent_id": { + "type": [ + "integer", + "null" + ] + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "url": { + "type": [ + "string", + "null" + ] + }, + "icon": { + "type": [ + "string", + "null" + ] + }, + "component": { + "type": [ + "string", + "null" + ] + }, + "sort_order": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "parent_id", + "name", + "slug", + "url", + "icon", + "component", + "sort_order", + "is_active", + "created_at", + "updated_at" + ], + "title": "MenuList" + }, + "MenuListResource": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "parent_id": { + "type": [ + "integer", + "null" + ] + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "url": { + "type": [ + "string", + "null" + ] + }, + "icon": { + "type": [ + "string", + "null" + ] + }, + "component": { + "type": [ + "string", + "null" + ] + }, + "sort_order": { + "type": "integer" + }, + "is_active": { + "type": "boolean" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "parent_id", + "name", + "slug", + "url", + "icon", + "component", + "sort_order", + "is_active", + "created_at", + "updated_at" + ], + "title": "MenuListResource" + }, + "PendingTicketRequest": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "maxLength": 1000 + } + }, + "required": [ + "reason" + ], + "title": "PendingTicketRequest" + }, + "ReassignTicketRequest": { + "type": "object", + "properties": { + "leader_id": { + "type": "integer" + }, + "technicians": { + "type": "array", + "items": { + "type": "integer" + }, + "minItems": 1 + } + }, + "required": [ + "leader_id", + "technicians" + ], + "title": "ReassignTicketRequest" + }, + "RegisterRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 100 + }, + "username": { + "type": [ + "string", + "null" + ], + "maxLength": 50 + }, + "email": { + "type": "string", + "format": "email", + "maxLength": 100 + }, + "whatsapp_number": { + "type": "string", + "maxLength": 20 + }, + "password": { + "type": "string", + "minLength": 8, + "maxLength": 100 + }, + "verification_channel": { + "type": "string", + "enum": [ + "email", + "whatsapp" + ] + }, + "verification_target": { + "type": "string", + "maxLength": 100 + } + }, + "required": [ + "name", + "email", + "whatsapp_number", + "password", + "verification_channel", + "verification_target" + ], + "title": "RegisterRequest" + }, + "RejectTicketRequest": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "maxLength": 1000 + } + }, + "required": [ + "reason" + ], + "title": "RejectTicketRequest" + }, + "ResendVerificationCodeRequest": { + "type": "object", + "properties": { + "verification_channel": { + "type": "string", + "enum": [ + "email", + "whatsapp" + ] + }, + "verification_target": { + "type": "string", + "maxLength": 100 + } + }, + "required": [ + "verification_channel", + "verification_target" + ], + "title": "ResendVerificationCodeRequest" + }, + "ResolveTicketRequest": { + "type": "object", + "properties": { + "notes": { + "type": "string", + "maxLength": 2000 + } + }, + "required": [ + "notes" + ], + "title": "ResolveTicketRequest" + }, + "ReturnMaterialRequest": { + "type": "object", + "properties": { + "barcode_id": { + "type": "string" + }, + "user_id": { + "type": "integer" + } + }, + "required": [ + "barcode_id", + "user_id" + ], + "title": "ReturnMaterialRequest" + }, + "StartTicketRequest": { + "type": "object", + "properties": { + "latitude": { + "type": [ + "number", + "null" + ] + }, + "longitude": { + "type": [ + "number", + "null" + ] + }, + "notes": { + "type": [ + "string", + "null" + ], + "maxLength": 1000 + } + }, + "title": "StartTicketRequest" + }, + "StoreMenuGroupRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 100 + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "is_active": { + "type": [ + "boolean", + "null" + ] + } + }, + "required": [ + "name" + ], + "title": "StoreMenuGroupRequest" + }, + "StoreMenuListRequest": { + "type": "object", + "properties": { + "parent_id": { + "type": [ + "integer", + "null" + ] + }, + "name": { + "type": "string", + "maxLength": 255 + }, + "slug": { + "type": "string", + "maxLength": 255 + }, + "url": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + }, + "icon": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + }, + "component": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + }, + "sort_order": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "is_active": { + "type": [ + "boolean", + "null" + ] + } + }, + "required": [ + "name", + "slug" + ], + "title": "StoreMenuListRequest" + }, + "StoreTenantRequest": { + "type": "object", + "properties": { + "tenant_code": { + "type": "string", + "maxLength": 100 + }, + "tenant_name": { + "type": "string", + "maxLength": 255 + }, + "phone": { + "type": [ + "string", + "null" + ], + "maxLength": 50 + }, + "email": { + "type": [ + "string", + "null" + ], + "format": "email", + "maxLength": 255 + }, + "address": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": [ + "string", + "null" + ], + "enum": [ + "active", + "inactive" + ] + } + }, + "required": [ + "tenant_code", + "tenant_name" + ], + "title": "StoreTenantRequest" + }, + "StoreTicketIncidentTypeRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 255 + }, + "is_active": { + "type": "boolean" + } + }, + "required": [ + "name" + ], + "title": "StoreTicketIncidentTypeRequest" + }, + "StoreTicketRequest": { + "type": "object", + "properties": { + "ticket_type_id": { + "type": "integer" + }, + "customer_id": { + "type": [ + "integer", + "null" + ] + }, + "title": { + "type": "string", + "maxLength": 255 + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "priority": { + "type": "string", + "enum": [ + "low", + "medium", + "high", + "critical" + ] + } + }, + "required": [ + "ticket_type_id", + "title", + "priority" + ], + "title": "StoreTicketRequest" + }, + "StoreTicketTypeRequest": { + "type": "object", + "properties": { + "code": { + "type": "string", + "maxLength": 50 + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "need_approval": { + "type": [ + "boolean", + "null" + ] + }, + "sla_minutes": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "require_photo": { + "type": [ + "boolean", + "null" + ] + }, + "require_material": { + "type": [ + "boolean", + "null" + ] + }, + "need_customer": { + "type": [ + "boolean", + "null" + ] + }, + "need_incident_type": { + "type": [ + "boolean", + "null" + ] + } + }, + "required": [ + "code", + "name" + ], + "title": "StoreTicketTypeRequest" + }, + "StoreUserRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 255 + }, + "username": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + }, + "email": { + "type": "string", + "format": "email", + "maxLength": 255 + }, + "whatsapp_number": { + "type": [ + "string", + "null" + ], + "maxLength": 20 + }, + "password": { + "type": "string", + "minLength": 8 + }, + "is_master": { + "type": [ + "boolean", + "null" + ] + }, + "access_level": { + "$ref": "#/components/schemas/UserAccessLevel" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive", + "suspended" + ] + }, + "user_profile": { + "type": "object", + "properties": { + "nik": { + "type": [ + "string", + "null" + ], + "maxLength": 30 + }, + "address": { + "type": [ + "string", + "null" + ] + }, + "provinsi_id": { + "type": [ + "integer", + "null" + ] + }, + "kabupaten_id": { + "type": [ + "integer", + "null" + ] + }, + "kecamatan_id": { + "type": [ + "integer", + "null" + ] + }, + "desa_id": { + "type": [ + "integer", + "null" + ] + } + } + }, + "user_tenants": { + "type": [ + "array", + "null" + ], + "items": { + "type": "object", + "properties": { + "tenant_id": { + "type": "integer" + }, + "is_default": { + "type": [ + "boolean", + "null" + ] + } + } + } + }, + "user_menu_groups": { + "type": [ + "array", + "null" + ], + "items": { + "type": "object", + "properties": { + "menu_group_id": { + "type": "integer" + }, + "tenant_id": { + "type": [ + "integer", + "null" + ] + } + } + } + } + }, + "required": [ + "name", + "email", + "password", + "status" + ], + "title": "StoreUserRequest" + }, + "SyncMenuGroupMenusRequest": { + "type": "object", + "properties": { + "menus": { + "type": "array", + "items": { + "type": "object", + "properties": { + "menu_id": { + "type": "integer" + }, + "can_view": { + "type": [ + "boolean", + "null" + ] + }, + "can_create": { + "type": [ + "boolean", + "null" + ] + }, + "can_update": { + "type": [ + "boolean", + "null" + ] + }, + "can_delete": { + "type": [ + "boolean", + "null" + ] + }, + "can_approve": { + "type": [ + "boolean", + "null" + ] + }, + "can_export": { + "type": [ + "boolean", + "null" + ] + } + }, + "required": [ + "menu_id" + ] + }, + "minItems": 1 + } + }, + "required": [ + "menus" + ], + "title": "SyncMenuGroupMenusRequest" + }, + "SyncUserMenuGroupsRequest": { + "type": "object", + "properties": { + "assignments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "menu_group_id": { + "type": "integer" + }, + "tenant_id": { + "type": [ + "integer", + "null" + ] + } + }, + "required": [ + "menu_group_id" + ] + }, + "minItems": 1 + } + }, + "required": [ + "assignments" + ], + "title": "SyncUserMenuGroupsRequest" + }, + "Tenant": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "tenant_code": { + "type": "string" + }, + "tenant_name": { + "type": "string" + }, + "phone": { + "type": [ + "string", + "null" + ] + }, + "email": { + "type": [ + "string", + "null" + ] + }, + "address": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "tenant_code", + "tenant_name", + "phone", + "email", + "address", + "status", + "created_at", + "updated_at" + ], + "title": "Tenant" + }, + "TenantResource": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "tenant_code": { + "type": "string" + }, + "tenant_name": { + "type": "string" + }, + "phone": { + "type": [ + "string", + "null" + ] + }, + "email": { + "type": [ + "string", + "null" + ] + }, + "address": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "tenant_code", + "tenant_name", + "phone", + "email", + "address", + "status", + "created_at", + "updated_at" + ], + "title": "TenantResource" + }, + "Ticket": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "ticket_no": { + "type": "string" + }, + "ticket_type_id": { + "type": "integer" + }, + "tenant_id": { + "type": "integer" + }, + "customer_id": { + "type": [ + "integer", + "null" + ] + }, + "title": { + "type": "string" + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "priority": { + "type": "string" + }, + "status": { + "type": "string" + }, + "sla_minutes": { + "type": [ + "integer", + "null" + ] + }, + "created_by": { + "type": "integer" + }, + "approved_by": { + "type": [ + "integer", + "null" + ] + }, + "approved_at": { + "type": [ + "string", + "null" + ] + }, + "rejected_by": { + "type": [ + "integer", + "null" + ] + }, + "rejected_at": { + "type": [ + "string", + "null" + ] + }, + "rejection_reason": { + "type": [ + "string", + "null" + ] + }, + "assigned_at": { + "type": [ + "string", + "null" + ] + }, + "resolved_at": { + "type": [ + "string", + "null" + ] + }, + "closed_at": { + "type": [ + "string", + "null" + ] + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "ticket_no", + "ticket_type_id", + "tenant_id", + "customer_id", + "title", + "description", + "priority", + "status", + "sla_minutes", + "created_by", + "approved_by", + "approved_at", + "rejected_by", + "rejected_at", + "rejection_reason", + "assigned_at", + "resolved_at", + "closed_at", + "created_at", + "updated_at" + ], + "title": "Ticket" + }, + "TicketIncidentType": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "deleted": { + "type": "string" + }, + "deleted_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "name", + "is_active", + "created_at", + "updated_at", + "deleted", + "deleted_at" + ], + "title": "TicketIncidentType" + }, + "TicketIncidentTypeResource": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "is_active": { + "type": "boolean" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "name", + "is_active", + "created_at", + "updated_at" + ], + "title": "TicketIncidentTypeResource" + }, + "TicketLog": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "ticket_id": { + "type": "integer" + }, + "user_id": { + "type": "integer" + }, + "activity": { + "type": "string" + }, + "notes": { + "type": [ + "string", + "null" + ] + }, + "latitude": { + "type": [ + "string", + "null" + ] + }, + "longitude": { + "type": [ + "string", + "null" + ] + }, + "created_at": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "id", + "ticket_id", + "user_id", + "activity", + "notes", + "latitude", + "longitude", + "created_at" + ], + "title": "TicketLog" + }, + "TicketResource": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "ticket_no": { + "type": "string" + }, + "ticket_type_id": { + "type": "integer" + }, + "customer_id": { + "type": [ + "integer", + "null" + ] + }, + "title": { + "type": "string" + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "priority": { + "type": "string" + }, + "status": { + "type": "string" + }, + "created_by": { + "type": "integer" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "ticket_no", + "ticket_type_id", + "customer_id", + "title", + "description", + "priority", + "status", + "created_by", + "created_at", + "updated_at" + ], + "title": "TicketResource" + }, + "TicketType": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "code": { + "type": "string" + }, + "name": { + "type": "string" + }, + "need_approval": { + "type": "boolean" + }, + "sla_minutes": { + "type": [ + "integer", + "null" + ] + }, + "require_photo": { + "type": "boolean" + }, + "require_material": { + "type": "boolean" + }, + "need_customer": { + "type": "boolean" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "need_incident_type": { + "type": "boolean" + } + }, + "required": [ + "id", + "code", + "name", + "need_approval", + "sla_minutes", + "require_photo", + "require_material", + "need_customer", + "created_at", + "updated_at", + "need_incident_type" + ], + "title": "TicketType" + }, + "TicketTypeResource": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "code": { + "type": "string" + }, + "name": { + "type": "string" + }, + "need_approval": { + "type": "boolean" + }, + "sla_minutes": { + "type": [ + "integer", + "null" + ] + }, + "require_photo": { + "type": "boolean" + }, + "require_material": { + "type": "boolean" + }, + "need_customer": { + "type": "boolean" + }, + "need_incident_type": { + "type": "boolean" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "code", + "name", + "need_approval", + "sla_minutes", + "require_photo", + "require_material", + "need_customer", + "need_incident_type", + "created_at", + "updated_at" + ], + "title": "TicketTypeResource" + }, + "TransferMaterialRequest": { + "type": "object", + "properties": { + "barcode_id": { + "type": "string" + }, + "from_user_id": { + "type": "integer" + }, + "to_user_id": { + "type": "integer" + } + }, + "required": [ + "barcode_id", + "from_user_id", + "to_user_id" + ], + "title": "TransferMaterialRequest" + }, + "UpdateMenuGroupRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 100 + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "is_active": { + "type": [ + "boolean", + "null" + ] + } + }, + "required": [ + "name" + ], + "title": "UpdateMenuGroupRequest" + }, + "UpdateMenuListRequest": { + "type": "object", + "properties": { + "parent_id": { + "type": [ + "integer", + "null" + ] + }, + "name": { + "type": "string", + "maxLength": 255 + }, + "slug": { + "type": "string", + "maxLength": 255 + }, + "url": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + }, + "icon": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + }, + "component": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + }, + "sort_order": { + "type": [ + "integer", + "null" + ], + "minimum": 0 + }, + "is_active": { + "type": [ + "boolean", + "null" + ] + } + }, + "required": [ + "name", + "slug" + ], + "title": "UpdateMenuListRequest" + }, + "UpdateProfileRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 255 + }, + "username": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + }, + "email": { + "type": "string", + "format": "email", + "maxLength": 255 + }, + "whatsapp_number": { + "type": [ + "string", + "null" + ], + "maxLength": 20 + }, + "current_password": { + "type": [ + "string", + "null" + ] + }, + "password": { + "type": [ + "string", + "null" + ], + "minLength": 8 + }, + "password_confirmation": { + "type": [ + "string", + "null" + ], + "minLength": 8 + }, + "user_profile": { + "type": "object", + "properties": { + "nik": { + "type": [ + "string", + "null" + ], + "maxLength": 30 + }, + "address": { + "type": [ + "string", + "null" + ] + }, + "provinsi_id": { + "type": [ + "integer", + "null" + ] + }, + "kabupaten_id": { + "type": [ + "integer", + "null" + ] + }, + "kecamatan_id": { + "type": [ + "integer", + "null" + ] + }, + "desa_id": { + "type": [ + "integer", + "null" + ] + } + } + } + }, + "required": [ + "name", + "email" + ], + "title": "UpdateProfileRequest" + }, + "UpdateTenantRequest": { + "type": "object", + "properties": { + "tenant_code": { + "type": "string", + "maxLength": 100 + }, + "tenant_name": { + "type": "string", + "maxLength": 255 + }, + "phone": { + "type": [ + "string", + "null" + ], + "maxLength": 50 + }, + "email": { + "type": [ + "string", + "null" + ], + "format": "email", + "maxLength": 255 + }, + "address": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + } + }, + "title": "UpdateTenantRequest" + }, + "UpdateTicketIncidentTypeRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 255 + }, + "is_active": { + "type": "boolean" + } + }, + "required": [ + "name", + "is_active" + ], + "title": "UpdateTicketIncidentTypeRequest" + }, + "UpdateTicketRequest": { + "type": "object", + "properties": { + "ticket_type_id": { + "type": "integer" + }, + "customer_id": { + "type": [ + "integer", + "null" + ] + }, + "title": { + "type": "string", + "maxLength": 255 + }, + "description": { + "type": [ + "string", + "null" + ] + }, + "priority": { + "type": "string", + "enum": [ + "low", + "medium", + "high", + "critical" + ] + }, + "status": { + "type": "string", + "enum": [ + "draft", + "open", + "approved", + "assigned", + "progress", + "pending", + "resolved", + "closed", + "cancelled", + "rejected" + ] + } + }, + "title": "UpdateTicketRequest" + }, + "UpdateTicketTypeRequest": { + "type": "object", + "properties": { + "code": { + "type": "string", + "maxLength": 50 + }, + "name": { + "type": "string", + "maxLength": 100 + }, + "need_approval": { + "type": [ + "boolean", + "null" + ] + }, + "sla_minutes": { + "type": [ + "integer", + "null" + ], + "minimum": 1 + }, + "require_photo": { + "type": [ + "boolean", + "null" + ] + }, + "require_material": { + "type": [ + "boolean", + "null" + ] + }, + "need_customer": { + "type": [ + "boolean", + "null" + ] + }, + "need_incident_type": { + "type": [ + "boolean", + "null" + ] + } + }, + "required": [ + "code", + "name" + ], + "title": "UpdateTicketTypeRequest" + }, + "UpdateUserRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 255 + }, + "username": { + "type": [ + "string", + "null" + ], + "maxLength": 255 + }, + "email": { + "type": "string", + "format": "email", + "maxLength": 255 + }, + "whatsapp_number": { + "type": [ + "string", + "null" + ], + "maxLength": 20 + }, + "password": { + "type": [ + "string", + "null" + ], + "minLength": 8 + }, + "is_master": { + "type": [ + "boolean", + "null" + ] + }, + "access_level": { + "$ref": "#/components/schemas/UserAccessLevel" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive", + "suspended" + ] + }, + "user_profile": { + "type": "object", + "properties": { + "nik": { + "type": [ + "string", + "null" + ], + "maxLength": 30 + }, + "address": { + "type": [ + "string", + "null" + ] + }, + "provinsi_id": { + "type": [ + "integer", + "null" + ] + }, + "kabupaten_id": { + "type": [ + "integer", + "null" + ] + }, + "kecamatan_id": { + "type": [ + "integer", + "null" + ] + }, + "desa_id": { + "type": [ + "integer", + "null" + ] + } + } + }, + "user_tenants": { + "type": [ + "array", + "null" + ], + "items": { + "type": "object", + "properties": { + "tenant_id": { + "type": "integer" + }, + "is_default": { + "type": [ + "boolean", + "null" + ] + } + } + } + }, + "user_menu_groups": { + "type": [ + "array", + "null" + ], + "items": { + "type": "object", + "properties": { + "menu_group_id": { + "type": "integer" + }, + "tenant_id": { + "type": [ + "integer", + "null" + ] + } + } + } + } + }, + "title": "UpdateUserRequest" + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "email_verified_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "username": { + "type": [ + "string", + "null" + ] + }, + "status": { + "type": "string" + }, + "last_login_at": { + "type": [ + "string", + "null" + ] + }, + "is_master": { + "type": "boolean" + }, + "verification_channel": { + "type": [ + "string", + "null" + ] + }, + "verification_target": { + "type": [ + "string", + "null" + ] + }, + "verification_code": { + "type": [ + "string", + "null" + ] + }, + "verification_code_expires_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "verified_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "whatsapp_number": { + "type": [ + "string", + "null" + ] + }, + "deleted_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "access_level": { + "$ref": "#/components/schemas/UserAccessLevel" + } + }, + "required": [ + "id", + "name", + "email", + "email_verified_at", + "created_at", + "updated_at", + "username", + "status", + "last_login_at", + "is_master", + "verification_channel", + "verification_target", + "verification_code", + "verification_code_expires_at", + "verified_at", + "whatsapp_number", + "deleted_at", + "access_level" + ], + "title": "User" + }, + "UserAccessLevel": { + "type": "string", + "enum": [ + "customer", + "staff", + "tenant_owner", + "master_admin" + ], + "title": "UserAccessLevel" + }, + "UserResource": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "username": { + "type": [ + "string", + "null" + ] + }, + "email": { + "type": "string" + }, + "whatsapp_number": { + "type": [ + "string", + "null" + ] + }, + "is_master": { + "type": "boolean" + }, + "access_level": { + "type": "string" + }, + "status": { + "type": "string" + }, + "verification_channel": { + "type": [ + "string", + "null" + ] + }, + "verification_target": { + "type": [ + "string", + "null" + ] + }, + "verified_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "user_profile": { + "type": [ + "object", + "null" + ], + "properties": { + "id": { + "type": "integer" + }, + "user_id": { + "type": "integer" + }, + "nik": { + "type": [ + "string", + "null" + ] + }, + "address": { + "type": [ + "string", + "null" + ] + }, + "provinsi_id": { + "type": [ + "integer", + "null" + ] + }, + "kabupaten_id": { + "type": [ + "integer", + "null" + ] + }, + "kecamatan_id": { + "type": [ + "integer", + "null" + ] + }, + "desa_id": { + "type": [ + "integer", + "null" + ] + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + } + }, + "required": [ + "id", + "user_id", + "nik", + "address", + "provinsi_id", + "kabupaten_id", + "kecamatan_id", + "desa_id", + "created_at", + "updated_at" + ] + }, + "user_tenants": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "user_id": { + "type": "integer" + }, + "tenant_id": { + "type": "integer" + }, + "is_default": { + "type": "boolean" + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "tenant": { + "type": [ + "object", + "null" + ], + "properties": { + "id": { + "type": "integer" + }, + "tenant_code": { + "type": "string" + }, + "tenant_name": { + "type": "string" + } + }, + "required": [ + "id", + "tenant_code", + "tenant_name" + ] + } + }, + "required": [ + "id", + "user_id", + "tenant_id", + "is_default", + "created_at", + "updated_at", + "tenant" + ] + } + }, + "user_menu_groups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "integer" + }, + "user_id": { + "type": "integer" + }, + "menu_group_id": { + "type": "integer" + }, + "tenant_id": { + "type": [ + "integer", + "null" + ] + }, + "created_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "updated_at": { + "type": [ + "string", + "null" + ], + "format": "date-time" + }, + "menu_group": { + "type": [ + "object", + "null" + ], + "properties": { + "id": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "menu_group_name": { + "type": [ + "string", + "null" + ] + } + }, + "required": [ + "id", + "name", + "menu_group_name" + ] + }, + "tenant": { + "type": [ + "object", + "null" + ], + "properties": { + "id": { + "type": "integer" + }, + "tenant_code": { + "type": "string" + }, + "tenant_name": { + "type": "string" + } + }, + "required": [ + "id", + "tenant_code", + "tenant_name" + ] + } + }, + "required": [ + "id", + "user_id", + "menu_group_id", + "tenant_id", + "created_at", + "updated_at", + "menu_group", + "tenant" + ] + } + } + }, + "required": [ + "id", + "name", + "username", + "email", + "whatsapp_number", + "is_master", + "access_level", + "status", + "verification_channel", + "verification_target", + "verified_at", + "created_at", + "updated_at" + ], + "title": "UserResource" + }, + "VerifyRegistrationRequest": { + "type": "object", + "properties": { + "verification_channel": { + "type": "string", + "enum": [ + "email", + "whatsapp" + ] + }, + "verification_target": { + "type": "string", + "maxLength": 100 + }, + "verification_code": { + "type": "string" + } + }, + "required": [ + "verification_channel", + "verification_target", + "verification_code" + ], + "title": "VerifyRegistrationRequest" + } + }, + "responses": { + "ValidationException": { + "description": "Validation error", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Errors overview." + }, + "errors": { + "type": "object", + "description": "A detailed description of each field that failed validation.", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "required": [ + "message", + "errors" + ] + } + } + } + }, + "AuthenticationException": { + "description": "Unauthenticated", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error overview." + } + }, + "required": [ + "message" + ] + } + } + } + }, + "ModelNotFoundException": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Error overview." + } + }, + "required": [ + "message" + ] + } + } + } + } + } + } +} \ No newline at end of file diff --git a/app/Enums/UserAccessLevel.php b/app/Enums/UserAccessLevel.php new file mode 100644 index 0000000..7b5f130 --- /dev/null +++ b/app/Enums/UserAccessLevel.php @@ -0,0 +1,21 @@ + 1, + self::STAFF => 2, + self::TENANT_OWNER => 3, + self::MASTER_ADMIN => 4, + }; + } +} diff --git a/app/Http/Controllers/Audit/AuditLogController.php b/app/Http/Controllers/Audit/AuditLogController.php index eb3b59b..ca6d5f5 100644 --- a/app/Http/Controllers/Audit/AuditLogController.php +++ b/app/Http/Controllers/Audit/AuditLogController.php @@ -22,8 +22,9 @@ class AuditLogController extends ApiController $request->validated() ); - return $this->success( - AuditLogResource::collection($data), + return $this->successPaginated( + $data, + AuditLogResource::class, 'Data audit log berhasil diambil' ); } @@ -37,4 +38,4 @@ class AuditLogController extends ApiController 'Detail audit log berhasil diambil' ); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index a7e74e5..9107f30 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -2,37 +2,131 @@ namespace App\Http\Controllers; +use App\Http\Requests\Auth\RegisterRequest; +use App\Http\Requests\Auth\ResendVerificationCodeRequest; +use App\Http\Requests\Auth\VerifyRegistrationRequest; use App\Models\User; +use App\Services\Auth\AuthService; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; class AuthController extends Controller { - + public function __construct( + protected AuthService $authService + ) {} + + public function register(RegisterRequest $request) + { + $result = $this->authService->register( + $request->validated() + ); + + return response()->json([ + 'success' => true, + 'message' => 'Register berhasil, silakan verifikasi kode.', + 'data' => [ + 'user' => $result['user'], + 'verification' => $result['verification'], + ], + ], 201); + } + + public function verifyRegistration( + VerifyRegistrationRequest $request + ) { + $user = $this->authService->verifyRegistration( + $request->validated() + ); + + return response()->json([ + 'success' => true, + 'message' => 'Verifikasi berhasil, user sudah aktif.', + 'data' => $user, + ]); + } + + public function resendVerificationCode( + ResendVerificationCodeRequest $request + ) { + $verification = $this->authService->resendVerificationCode( + $request->validated() + ); + + return response()->json([ + 'success' => true, + 'message' => 'Kode verifikasi baru berhasil dibuat.', + 'data' => [ + 'verification' => $verification, + ], + ]); + } + public function login(Request $request) { $request->validate([ - 'email' => 'required|email', - 'password' => 'required' + 'login' => 'required|string', + 'password' => 'required|string', ]); - $user = User::where('email', $request->email)->first(); + $user = User::where('email', $request->login) + ->orWhere('username', $request->login) + ->first(); if (!$user || !Hash::check($request->password, $user->password)) { - return response()->json([ 'success' => false, - 'message' => 'Email atau password salah' + 'message' => 'Username/email atau password salah.' ], 401); + } + if ($user->status !== 'active') { + return response()->json([ + 'success' => false, + 'message' => 'Akun belum aktif. Silakan verifikasi terlebih dahulu.' + ], 403); } $token = $user->createToken('services-core')->plainTextToken; return response()->json([ 'success' => true, - 'token' => $token, - 'user' => $user + 'token' => $token, + 'user' => $user, ]); } + + // public function login(Request $request) + // { + // $request->validate([ + // 'email' => 'required|email', + // 'password' => 'required' + // ]); + + // $user = User::where('email', $request->email)->first(); + + // if (!$user || !Hash::check($request->password, $user->password)) { + + // return response()->json([ + // 'success' => false, + // 'message' => 'Email atau password salah' + // ], 401); + + // } + + // if ($user->status !== 'active') { + // return response()->json([ + // 'success' => false, + // 'message' => 'Akun belum aktif. Silakan verifikasi terlebih dahulu.' + // ], 403); + // } + + // $token = $user->createToken('services-core')->plainTextToken; + + // return response()->json([ + // 'success' => true, + // 'token' => $token, + // 'user' => $user + // ]); + // } } \ No newline at end of file diff --git a/app/Http/Controllers/File/StoredFileController.php b/app/Http/Controllers/File/StoredFileController.php new file mode 100644 index 0000000..9e9fc56 --- /dev/null +++ b/app/Http/Controllers/File/StoredFileController.php @@ -0,0 +1,63 @@ +successPaginated( + $this->service->list( + $request->validated(), + $request->user(), + $request->attributes->get('tenant_id') + ), + StoredFileResource::class, + 'Daftar file berhasil diambil' + ); + } + + public function store(StoreFileRequest $request) + { + $file = $this->service->store( + $request->file('file'), + $request->user(), + $request->attributes->get('tenant_id'), + $request->validated('category', 'general') + ); + + return $this->created(new StoredFileResource($file), 'File berhasil diunggah'); + } + + public function show(Request $request, StoredFile $storedFile) + { + $this->service->authorizeAccess($storedFile, $request->user()); + + return $this->success(new StoredFileResource($storedFile), 'Detail file berhasil diambil'); + } + + public function temporaryUrl(Request $request, StoredFile $storedFile) + { + return $this->success( + $this->service->temporaryUrl($storedFile, $request->user()), + 'URL sementara berhasil dibuat' + ); + } + + public function destroy(Request $request, StoredFile $storedFile) + { + $this->service->delete($storedFile, $request->user()); + + return $this->deleted('File berhasil dihapus'); + } +} diff --git a/app/Http/Controllers/Material/MaterialController.php b/app/Http/Controllers/Material/MaterialController.php index 850ed0a..3554860 100644 --- a/app/Http/Controllers/Material/MaterialController.php +++ b/app/Http/Controllers/Material/MaterialController.php @@ -4,11 +4,11 @@ namespace App\Http\Controllers\Material; use App\Http\Controllers\ApiController; use App\Http\Requests\Material\AssignMaterialRequest; -use App\Http\Requests\Material\TransferMaterialRequest; +use App\Http\Requests\Material\IndexMaterialRequest; use App\Http\Requests\Material\ReturnMaterialRequest; +use App\Http\Requests\Material\TransferMaterialRequest; use App\Http\Resources\Material\MaterialResource; use App\Services\Material\MaterialService; -use Illuminate\Http\Request; class MaterialController extends ApiController { @@ -19,20 +19,9 @@ class MaterialController extends ApiController $this->service = $service; } - public function index(Request $request) + public function index(IndexMaterialRequest $request) { - $filters = $request->only([ - 'search', - 'technician_id', - 'warehouse_id', - 'type', - 'page', - 'per_page', - 'sort_by', - 'sort_order', - ]); - - $data = $this->service->list($filters); + $data = $this->service->list($request->validated()); return $this->successPaginated( $data, @@ -76,7 +65,7 @@ class MaterialController extends ApiController $data = $this->service->listByUser($user_id); return $this->success( - \App\Http\Resources\Material\MaterialResource::collection($data), + MaterialResource::collection($data), 'List material user berhasil diambil' ); } @@ -90,4 +79,4 @@ class MaterialController extends ApiController 'History material berhasil diambil' ); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Menu/MenuController.php b/app/Http/Controllers/Menu/MenuController.php new file mode 100644 index 0000000..55e3bd2 --- /dev/null +++ b/app/Http/Controllers/Menu/MenuController.php @@ -0,0 +1,146 @@ +user(); + $tenantId = request()->attributes->get('tenant_id'); + + if (! $user) { + return $this->error('Unauthenticated', 401); + } + + if ($user->isMasterAdmin()) { + $menus = MenuList::query() + ->where('is_active', true) + ->orderBy('sort_order') + ->orderBy('id') + ->get() + ->map(function (MenuList $menu) { + return [ + 'id' => $menu->id, + 'parent_id' => $menu->parent_id, + 'name' => $menu->name, + 'slug' => $menu->slug, + 'url' => $menu->url, + 'icon' => $menu->icon, + 'component' => $menu->component, + 'sort_order' => $menu->sort_order, + 'permissions' => [ + 'can_view' => true, + 'can_create' => true, + 'can_update' => true, + 'can_delete' => true, + 'can_approve' => true, + 'can_export' => true, + ], + ]; + }); + } else { + $menuRows = DB::table('menu_lists as m') + ->join('menu_group_menus as mgm', 'mgm.menu_id', '=', 'm.id') + ->join('user_menu_groups as umg', 'umg.menu_group_id', '=', 'mgm.menu_group_id') + ->join('menu_groups as mg', 'mg.id', '=', 'umg.menu_group_id') + ->where('umg.user_id', $user->id) + ->where('umg.tenant_id', $tenantId) + ->where('mg.is_active', true) + ->where('m.is_active', true) + ->where('mgm.can_view', true) + ->groupBy( + 'm.id', + 'm.parent_id', + 'm.name', + 'm.slug', + 'm.url', + 'm.icon', + 'm.component', + 'm.sort_order' + ) + ->select( + 'm.id', + 'm.parent_id', + 'm.name', + 'm.slug', + 'm.url', + 'm.icon', + 'm.component', + 'm.sort_order', + DB::raw('MAX(CASE WHEN mgm.can_view THEN 1 ELSE 0 END) as can_view'), + DB::raw('MAX(CASE WHEN mgm.can_create THEN 1 ELSE 0 END) as can_create'), + DB::raw('MAX(CASE WHEN mgm.can_update THEN 1 ELSE 0 END) as can_update'), + DB::raw('MAX(CASE WHEN mgm.can_delete THEN 1 ELSE 0 END) as can_delete'), + DB::raw('MAX(CASE WHEN mgm.can_approve THEN 1 ELSE 0 END) as can_approve'), + DB::raw('MAX(CASE WHEN mgm.can_export THEN 1 ELSE 0 END) as can_export') + ) + ->orderBy('m.sort_order') + ->orderBy('m.id') + ->get(); + + $menus = $menuRows->map(function ($row) { + return [ + 'id' => (int) $row->id, + 'parent_id' => $row->parent_id ? (int) $row->parent_id : null, + 'name' => $row->name, + 'slug' => $row->slug, + 'url' => $row->url, + 'icon' => $row->icon, + 'component' => $row->component, + 'sort_order' => (int) $row->sort_order, + 'permissions' => [ + 'can_view' => (bool) $row->can_view, + 'can_create' => (bool) $row->can_create, + 'can_update' => (bool) $row->can_update, + 'can_delete' => (bool) $row->can_delete, + 'can_approve' => (bool) $row->can_approve, + 'can_export' => (bool) $row->can_export, + ], + ]; + }); + } + + $tree = $this->buildMenuTree($menus); + + return $this->success( + [ + 'is_master' => (bool) $user->is_master, + 'access_level' => $user->access_level->value, + 'tenant_id' => $tenantId, + 'menus' => $tree, + ], + 'Menu berhasil diambil' + ); + } + + private function buildMenuTree(Collection $menus, ?int $parentId = null): array + { + return $menus + ->filter(fn ($menu) => $menu['parent_id'] === $parentId) + ->sortBy('sort_order') + ->values() + ->map(function ($menu) use ($menus) { + $children = $this->buildMenuTree($menus, $menu['id']); + + return [ + 'id' => $menu['id'], + 'parent_id' => $menu['parent_id'], + 'name' => $menu['name'], + 'slug' => $menu['slug'], + 'url' => $menu['url'], + 'icon' => $menu['icon'], + 'component' => $menu['component'], + 'sort_order' => $menu['sort_order'], + 'permissions' => $menu['permissions'], + 'children' => $children, + ]; + }) + ->all(); + } +} diff --git a/app/Http/Controllers/Menu/MenuGroupController.php b/app/Http/Controllers/Menu/MenuGroupController.php new file mode 100644 index 0000000..d74e3a1 --- /dev/null +++ b/app/Http/Controllers/Menu/MenuGroupController.php @@ -0,0 +1,206 @@ +service->list( + $request->validated(), + $request->user(), + $request->attributes->get('tenant_id') + ); + + return $this->successPaginated( + $data, + MenuGroupResource::class, + 'Data Menu Group berhasil diambil' + ); + } + + public function store(StoreMenuGroupRequest $request) + { + $data = $this->service->create( + $request->validated(), + $request->user(), + $request->attributes->get('tenant_id') + ); + + return $this->created( + new MenuGroupResource($data), + 'Menu Group berhasil dibuat' + ); + } + + public function show(MenuGroup $menu_group) + { + $this->authorizeGroup($menu_group, false); + + return $this->success( + new MenuGroupResource($menu_group->load('tenant')), + 'Detail Menu Group berhasil diambil' + ); + } + + public function update( + UpdateMenuGroupRequest $request, + MenuGroup $menu_group + ) { + $this->authorizeGroup($menu_group, true); + $data = $this->service->update( + $menu_group, + $request->validated(), + $request->user(), + $request->attributes->get('tenant_id') + ); + + return $this->updated( + new MenuGroupResource($data), + 'Menu Group berhasil diupdate' + ); + } + + public function destroy(MenuGroup $menu_group) + { + $this->authorizeGroup($menu_group, true); + $this->service->delete($menu_group); + + return $this->deleted( + 'Menu Group berhasil dihapus' + ); + } + + public function menus(MenuGroup $menu_group) + { + $this->authorizeGroup($menu_group, false); + $data = $this->service->listMenus($menu_group); + + return $this->success( + [ + 'menu_group_id' => $menu_group->id, + 'menu_group' => new MenuGroupResource($menu_group), + 'menus' => $data, + ], + 'Mapping menu group berhasil diambil' + ); + } + + public function availableMenus(MenuGroup $menu_group) + { + $this->authorizeGroup($menu_group, false); + $user = auth()->user(); + + if (! $user) { + return $this->error('Unauthenticated', 401); + } + + $data = $this->service->listAvailableMenusForGroup( + $menu_group, + $user + ); + + return $this->success( + [ + 'menu_group_id' => $menu_group->id, + 'menu_group' => new MenuGroupResource($menu_group), + 'menus' => $data, + ], + 'Daftar menu tersedia berdasarkan akses user berhasil diambil' + ); + } + + public function syncMenus( + SyncMenuGroupMenusRequest $request, + MenuGroup $menu_group + ) { + $this->authorizeGroup($menu_group, true); + $user = auth()->user(); + + if (! $user) { + return $this->error('Unauthenticated', 401); + } + + $this->service->syncMenus( + $menu_group, + $request->validated('menus'), + $user + ); + + return $this->success( + [ + 'menu_group_id' => $menu_group->id, + 'menu_group' => new MenuGroupResource($menu_group->fresh('tenant')), + 'menus' => $this->service->listMenus($menu_group), + ], + 'Mapping menu group berhasil disimpan' + ); + } + + public function userAssignments(User $user) + { + $tenantId = request()->attributes->get('tenant_id'); + abort_unless($this->access->canManageUser(auth()->user(), $user, $tenantId), 403); + + return $this->success( + [ + 'user_id' => $user->id, + 'assignments' => $this->service->listUserAssignments($user, $tenantId), + ], + 'Assignment user menu group berhasil diambil' + ); + } + + public function syncUserAssignments( + SyncUserMenuGroupsRequest $request, + User $user + ) { + $tenantId = $request->attributes->get('tenant_id'); + abort_unless($this->access->canManageUser($request->user(), $user, $tenantId), 403); + + $this->service->syncUserAssignments( + $user, + $request->validated('assignments'), + $tenantId + ); + + return $this->success( + [ + 'user_id' => $user->id, + 'assignments' => $this->service->listUserAssignments($user, $tenantId), + ], + 'Assignment user menu group berhasil disimpan' + ); + } + + private function authorizeGroup(MenuGroup $menuGroup, bool $write): void + { + $user = auth()->user(); + $tenantId = request()->attributes->get('tenant_id'); + + if ($user->isMasterAdmin()) { + return; + } + + $allowed = (int) $menuGroup->tenant_id === (int) $tenantId; + + abort_unless($allowed, 403); + } +} diff --git a/app/Http/Controllers/Menu/MenuListController.php b/app/Http/Controllers/Menu/MenuListController.php new file mode 100644 index 0000000..5b0045f --- /dev/null +++ b/app/Http/Controllers/Menu/MenuListController.php @@ -0,0 +1,75 @@ +service->list( + $request->validated() + ); + + return $this->successPaginated( + $data, + MenuListResource::class, + 'Data Menu List berhasil diambil' + ); + } + + public function store(StoreMenuListRequest $request) + { + $data = $this->service->create( + $request->validated() + ); + + return $this->created( + new MenuListResource($data), + 'Menu List berhasil dibuat' + ); + } + + public function show(MenuList $menu_list) + { + return $this->success( + new MenuListResource($menu_list), + 'Detail Menu List berhasil diambil' + ); + } + + public function update( + UpdateMenuListRequest $request, + MenuList $menu_list + ) { + $data = $this->service->update( + $menu_list, + $request->validated() + ); + + return $this->updated( + new MenuListResource($data), + 'Menu List berhasil diupdate' + ); + } + + public function destroy(MenuList $menu_list) + { + $this->service->delete($menu_list); + + return $this->deleted( + 'Menu List berhasil dihapus' + ); + } +} diff --git a/app/Http/Controllers/Nas/NasResourceController.php b/app/Http/Controllers/Nas/NasResourceController.php new file mode 100644 index 0000000..8da3761 --- /dev/null +++ b/app/Http/Controllers/Nas/NasResourceController.php @@ -0,0 +1,72 @@ +service->list($this->resource($request), $request->validated(), $request->user(), $this->tenantId($request)); + + return $this->successPaginated($data, NasResource::class, 'Data NAS berhasil diambil'); + } + + public function store(StoreNasResourceRequest $request) + { + $data = $this->service->create($this->resource($request), $request->validated(), $request->user(), $this->tenantId($request)); + + return $this->created(new NasResource($data), 'Data NAS berhasil dibuat'); + } + + public function show(Request $request, int $id) + { + $data = $this->service->find($this->resource($request), $id); + $this->authorizeTenant($request, $data->tenant_id); + + return $this->success(new NasResource($data), 'Detail NAS berhasil diambil'); + } + + public function update(UpdateNasResourceRequest $request, int $id) + { + $resource = $this->resource($request); + $model = $this->service->find($resource, $id); + $this->authorizeTenant($request, $model->tenant_id); + $data = $this->service->update($resource, $model, $request->validated(), $request->user()); + + return $this->updated(new NasResource($data), 'Data NAS berhasil diupdate'); + } + + public function destroy(Request $request, int $id) + { + $model = $this->service->find($this->resource($request), $id); + $this->authorizeTenant($request, $model->tenant_id); + $this->service->delete($model); + + return $this->deleted('Data NAS berhasil dihapus'); + } + + private function resource(Request $request): string + { + return $request->route()->defaults['nas_resource']; + } + + private function tenantId(Request $request): ?int + { + return $request->attributes->get('tenant_id'); + } + + private function authorizeTenant(Request $request, int $tenantId): void + { + abort_unless($request->user()->isMasterAdmin() || $tenantId === $this->tenantId($request), 403); + } +} diff --git a/app/Http/Controllers/Profile/ProfileController.php b/app/Http/Controllers/Profile/ProfileController.php new file mode 100644 index 0000000..76ef9ca --- /dev/null +++ b/app/Http/Controllers/Profile/ProfileController.php @@ -0,0 +1,38 @@ +success( + new UserResource($this->service->show($request->user())), + 'Profile berhasil diambil' + ); + } + + public function update(UpdateProfileRequest $request) + { + $user = $this->service->update( + $request->user(), + $request->safe()->except(['current_password', 'password_confirmation']), + $request->user() + ); + + return $this->updated( + new UserResource($user), + 'Profile berhasil diperbarui' + ); + } +} diff --git a/app/Http/Controllers/Tenant/TenantController.php b/app/Http/Controllers/Tenant/TenantController.php new file mode 100644 index 0000000..33c633b --- /dev/null +++ b/app/Http/Controllers/Tenant/TenantController.php @@ -0,0 +1,93 @@ +service->list( + $request->validated(), + $request->user() + ); + + return $this->successPaginated( + $data, + TenantResource::class, + 'Data Tenant berhasil diambil' + ); + } + + public function store(StoreTenantRequest $request) + { + $data = $this->service->create( + $request->validated() + ); + + return $this->created( + new TenantResource($data), + 'Tenant berhasil dibuat' + ); + } + + public function show(Request $request, Tenant $tenant) + { + $this->authorizeTenantAccess($request, $tenant); + + return $this->success( + new TenantResource($tenant), + 'Detail Tenant berhasil diambil' + ); + } + + public function update( + UpdateTenantRequest $request, + Tenant $tenant + ) { + $data = $this->service->update( + $tenant, + $request->validated() + ); + + return $this->updated( + new TenantResource($data), + 'Tenant berhasil diupdate' + ); + } + + public function destroy(Tenant $tenant) + { + $this->service->delete($tenant); + + return $this->deleted( + 'Tenant berhasil dihapus' + ); + } + + private function authorizeTenantAccess(Request $request, Tenant $tenant): void + { + $user = $request->user(); + + if ($user->isMasterAdmin()) { + return; + } + + abort_unless( + $user->userTenants()->where('tenant_id', $tenant->id)->exists(), + 403 + ); + } +} diff --git a/app/Http/Controllers/Ticket/TicketController.php b/app/Http/Controllers/Ticket/TicketController.php index eb83329..06fd169 100644 --- a/app/Http/Controllers/Ticket/TicketController.php +++ b/app/Http/Controllers/Ticket/TicketController.php @@ -3,12 +3,12 @@ namespace App\Http\Controllers\Ticket; use App\Http\Controllers\ApiController; +use App\Http\Requests\Ticket\IndexTicketRequest; use App\Http\Requests\Ticket\StoreTicketRequest; use App\Http\Requests\Ticket\UpdateTicketRequest; use App\Http\Resources\Ticket\TicketResource; use App\Models\Ticket; use App\Services\Ticket\TicketService; -use Illuminate\Http\Request; class TicketController extends ApiController { @@ -16,13 +16,42 @@ class TicketController extends ApiController protected TicketService $service ) {} - public function index(Request $request) + public function index(IndexTicketRequest $request) { - $tickets = Ticket::latest()->paginate( - $request->get('per_page', 10) - ); + $filters = $request->validated(); + $tickets = Ticket::query() + ->when( + ! empty($filters['search']), + fn ($query) => $query->where(function ($searchQuery) use ($filters) { + $searchQuery->where('ticket_no', 'ILIKE', "%{$filters['search']}%") + ->orWhere('title', 'ILIKE', "%{$filters['search']}%") + ->orWhere('description', 'ILIKE', "%{$filters['search']}%"); + }) + ) + ->when( + ! empty($filters['status']), + fn ($query) => $query->where('status', $filters['status']) + ) + ->when( + ! empty($filters['priority']), + fn ($query) => $query->where('priority', $filters['priority']) + ) + ->when( + ! empty($filters['ticket_type_id']), + fn ($query) => $query->where('ticket_type_id', $filters['ticket_type_id']) + ) + ->when( + ! empty($filters['customer_id']), + fn ($query) => $query->where('customer_id', $filters['customer_id']) + ) + ->latest() + ->paginate($filters['per_page'] ?? 10); - return $this->success($tickets); + return $this->successPaginated( + $tickets, + TicketResource::class, + 'Data Ticket berhasil diambil' + ); } public function store(StoreTicketRequest $request) @@ -67,4 +96,4 @@ class TicketController extends ApiController 'Ticket berhasil dibatalkan' ); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Ticket/TicketIncidentTypeController.php b/app/Http/Controllers/Ticket/TicketIncidentTypeController.php index 878a109..d1aa5f3 100644 --- a/app/Http/Controllers/Ticket/TicketIncidentTypeController.php +++ b/app/Http/Controllers/Ticket/TicketIncidentTypeController.php @@ -3,12 +3,13 @@ namespace App\Http\Controllers\Ticket; use App\Http\Controllers\ApiController; -use App\Models\TicketIncidentType; -use Illuminate\Http\JsonResponse; -use App\Services\Ticket\TicketIncidentTypeService; -use App\Http\Resources\TicketIncidentType\TicketIncidentTypeResource; +use App\Http\Requests\TicketIncidentType\IndexTicketIncidentTypeRequest; use App\Http\Requests\TicketIncidentType\StoreTicketIncidentTypeRequest; use App\Http\Requests\TicketIncidentType\UpdateTicketIncidentTypeRequest; +use App\Http\Resources\TicketIncidentType\TicketIncidentTypeResource; +use App\Models\TicketIncidentType; +use App\Services\Ticket\TicketIncidentTypeService; +use Illuminate\Http\JsonResponse; class TicketIncidentTypeController extends ApiController { @@ -16,12 +17,12 @@ class TicketIncidentTypeController extends ApiController private TicketIncidentTypeService $service ) {} - public function index(): JsonResponse + public function index(IndexTicketIncidentTypeRequest $request): JsonResponse { - return $this->success( - TicketIncidentTypeResource::collection( - $this->service->getAll() - ) + return $this->successPaginated( + $this->service->list($request->validated()), + TicketIncidentTypeResource::class, + 'Data Incident Type berhasil diambil' ); } @@ -74,4 +75,4 @@ class TicketIncidentTypeController extends ApiController 'Incident Type berhasil dihapus' ); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Ticket/TicketTypeController.php b/app/Http/Controllers/Ticket/TicketTypeController.php index 7bfd968..9ce4c0c 100644 --- a/app/Http/Controllers/Ticket/TicketTypeController.php +++ b/app/Http/Controllers/Ticket/TicketTypeController.php @@ -3,12 +3,12 @@ namespace App\Http\Controllers\Ticket; use App\Http\Controllers\ApiController; +use App\Http\Requests\TicketType\IndexTicketTypeRequest; use App\Http\Requests\TicketType\StoreTicketTypeRequest; use App\Http\Requests\TicketType\UpdateTicketTypeRequest; use App\Http\Resources\TicketType\TicketTypeResource; use App\Models\TicketType; use App\Services\Ticket\TicketTypeService; -use Illuminate\Http\Request; class TicketTypeController extends ApiController { @@ -16,14 +16,15 @@ class TicketTypeController extends ApiController protected TicketTypeService $service ) {} - public function index(Request $request) + public function index(IndexTicketTypeRequest $request) { $data = $this->service->list( - $request->all() + $request->validated() ); - return $this->success( - TicketTypeResource::collection($data), + return $this->successPaginated( + $data, + TicketTypeResource::class, 'Data Ticket Type berhasil diambil' ); } @@ -71,4 +72,4 @@ class TicketTypeController extends ApiController 'Ticket Type berhasil dihapus' ); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/User/UserController.php b/app/Http/Controllers/User/UserController.php new file mode 100644 index 0000000..7f38f5b --- /dev/null +++ b/app/Http/Controllers/User/UserController.php @@ -0,0 +1,95 @@ +service->list( + $request->validated(), + $request->user(), + $request->attributes->get('tenant_id') + ); + + return $this->successPaginated( + $data, + UserResource::class, + 'Data User berhasil diambil' + ); + } + + public function store(StoreUserRequest $request) + { + $data = $this->service->create( + $request->validated(), + $request->user() + ); + + return $this->created( + new UserResource($data), + 'User berhasil dibuat' + ); + } + + public function show(Request $request, User $user) + { + abort_unless( + $this->access->canManageUser($request->user(), $user, $request->attributes->get('tenant_id')), + 403 + ); + + $data = $this->service->show($user); + + return $this->success( + new UserResource($data), + 'Detail User berhasil diambil' + ); + } + + public function update(UpdateUserRequest $request, User $user) + { + abort_unless( + $this->access->canManageUser($request->user(), $user, $request->attributes->get('tenant_id')), + 403 + ); + + $data = $this->service->update( + $user, + $request->validated(), + $request->user() + ); + + return $this->updated( + new UserResource($data), + 'User berhasil diupdate' + ); + } + + public function destroy(Request $request, User $user) + { + abort_unless( + $this->access->canManageUser($request->user(), $user, $request->attributes->get('tenant_id')), + 403 + ); + + $this->service->delete($user); + + return $this->deleted('User berhasil dihapus'); + } +} diff --git a/app/Http/Controllers/Wilayah/WilayahController.php b/app/Http/Controllers/Wilayah/WilayahController.php new file mode 100644 index 0000000..fd7c31a --- /dev/null +++ b/app/Http/Controllers/Wilayah/WilayahController.php @@ -0,0 +1,106 @@ +successPaginated( + $this->service->list($tingkat, $request->validated()), + WilayahResource::class, + "Daftar {$tingkat} berhasil diambil" + ); + } + + public function store(StoreWilayahRequest $request, string $tingkat) + { + return $this->created( + new WilayahResource($this->service->create($tingkat, $request->validated())), + ucfirst($tingkat).' berhasil dibuat' + ); + } + + public function show(int $wilayah, string $tingkat) + { + return $this->success( + new WilayahResource($this->service->find($tingkat, $wilayah)), + 'Detail wilayah berhasil diambil' + ); + } + + public function update(UpdateWilayahRequest $request, int $wilayah, string $tingkat) + { + return $this->updated( + new WilayahResource($this->service->update($tingkat, $wilayah, $request->validated())), + ucfirst($tingkat).' berhasil diperbarui' + ); + } + + public function destroy(int $wilayah, string $tingkat) + { + $this->service->delete($tingkat, $wilayah); + + return $this->deleted(ucfirst($tingkat).' berhasil dihapus'); + } + + public function alamatLengkap(AlamatLengkapRequest $request) + { + return $this->success( + $this->service->alamatLengkap($request->validated()), + 'Alamat wilayah lengkap berhasil diambil' + ); + } + + public function provinsiOptions(LookupWilayahRequest $request) + { + return $this->success( + $this->service->options('provinsi', $request->validated()), + 'Pilihan provinsi berhasil diambil' + ); + } + + public function kabupatenOptions( + LookupWilayahRequest $request, + WilayahProvinsi $provinsi + ) { + return $this->success( + $this->service->options('kabupaten', $request->validated(), $provinsi->id), + 'Pilihan kabupaten berhasil diambil' + ); + } + + public function kecamatanOptions( + LookupWilayahRequest $request, + WilayahKabupaten $kabupaten + ) { + return $this->success( + $this->service->options('kecamatan', $request->validated(), $kabupaten->id), + 'Pilihan kecamatan berhasil diambil' + ); + } + + public function desaOptions( + LookupWilayahRequest $request, + WilayahKecamatan $kecamatan + ) { + return $this->success( + $this->service->options('desa', $request->validated(), $kecamatan->id), + 'Pilihan desa berhasil diambil' + ); + } +} diff --git a/app/Http/Middleware/EnsureAccessLevel.php b/app/Http/Middleware/EnsureAccessLevel.php new file mode 100644 index 0000000..ef8f674 --- /dev/null +++ b/app/Http/Middleware/EnsureAccessLevel.php @@ -0,0 +1,25 @@ +user(); + + if (! $user || (! $user->isMasterAdmin() && ! in_array($user->access_level->value, $levels, true))) { + return response()->json([ + 'success' => false, + 'code' => 403, + 'message' => 'Anda tidak memiliki level akses yang diperlukan.', + ], 403); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/EnsureMenuPermission.php b/app/Http/Middleware/EnsureMenuPermission.php new file mode 100644 index 0000000..bc93ac1 --- /dev/null +++ b/app/Http/Middleware/EnsureMenuPermission.php @@ -0,0 +1,31 @@ +user(); + $tenantId = $request->attributes->get('tenant_id'); + + if (! $user || ! $this->access->hasMenuPermission($user, $menuSlug, $permission, $tenantId)) { + return response()->json([ + 'success' => false, + 'code' => 403, + 'message' => 'Anda tidak memiliki permission untuk tindakan ini.', + ], 403); + } + + return $next($request); + } +} diff --git a/app/Http/Middleware/EnsureMenuResourcePermission.php b/app/Http/Middleware/EnsureMenuResourcePermission.php new file mode 100644 index 0000000..ea5cc5d --- /dev/null +++ b/app/Http/Middleware/EnsureMenuResourcePermission.php @@ -0,0 +1,28 @@ +method()) { + 'GET', 'HEAD' => 'view', + 'POST' => 'create', + 'PUT', 'PATCH' => 'update', + 'DELETE' => 'delete', + default => null, + }; + + if (! $permission) { + abort(405); + } + + return app(EnsureMenuPermission::class) + ->handle($request, $next, $menuSlug, $permission); + } +} diff --git a/app/Http/Middleware/ResolveTenantContext.php b/app/Http/Middleware/ResolveTenantContext.php new file mode 100644 index 0000000..ffd3717 --- /dev/null +++ b/app/Http/Middleware/ResolveTenantContext.php @@ -0,0 +1,35 @@ +user(); + $requested = $request->header('X-Tenant-ID'); + $requestedTenantId = is_numeric($requested) ? (int) $requested : null; + $tenantId = $this->access->tenantId($user, $requestedTenantId); + + if ($requestedTenantId && ! $tenantId) { + return response()->json([ + 'success' => false, + 'code' => 403, + 'message' => 'User tidak memiliki akses ke tenant yang dipilih.', + ], 403); + } + + $request->attributes->set('tenant_id', $tenantId); + + return $next($request); + } +} diff --git a/app/Http/Requests/Audit/AuditLogFilterRequest.php b/app/Http/Requests/Audit/AuditLogFilterRequest.php index 3295005..b4d3def 100644 --- a/app/Http/Requests/Audit/AuditLogFilterRequest.php +++ b/app/Http/Requests/Audit/AuditLogFilterRequest.php @@ -23,33 +23,48 @@ class AuditLogFilterRequest extends FormRequest public function rules(): array { return [ - + /** Filter berdasarkan nama modul audit. */ 'module' => [ 'nullable', - 'string' + 'string', + 'max:100', ], + /** Filter berdasarkan nama action audit. */ 'action' => [ 'nullable', - 'string' + 'string', + 'max:100', ], + /** Filter berdasarkan ID user pelaku. */ 'user_id' => [ 'nullable', - 'integer' + 'integer', + 'exists:users,id', ], + /** Filter berdasarkan ID tenant. */ 'tenant_id' => [ 'nullable', - 'integer' + 'integer', + 'exists:tenants,id', ], + /** Nomor halaman yang ingin diambil. */ + 'page' => [ + 'nullable', + 'integer', + 'min:1', + ], + + /** Jumlah data per halaman, maksimal 100. */ 'per_page' => [ 'nullable', 'integer', 'min:1', - 'max:100' - ] + 'max:100', + ], ]; } } diff --git a/app/Http/Requests/Auth/RegisterRequest.php b/app/Http/Requests/Auth/RegisterRequest.php new file mode 100644 index 0000000..d27021c --- /dev/null +++ b/app/Http/Requests/Auth/RegisterRequest.php @@ -0,0 +1,27 @@ + ['required', 'string', 'max:100'], + 'username' => ['nullable', 'string', 'max:50', 'unique:users,username'], + 'email' => ['required', 'email', 'max:100', 'unique:users,email'], + 'whatsapp_number' => ['required', 'string', 'max:20'], + 'password' => ['required', 'string', 'min:8', 'max:100'], + 'verification_channel' => ['required', Rule::in(['email', 'whatsapp'])], + 'verification_target' => ['required', 'string', 'max:100'], + ]; + } +} diff --git a/app/Http/Requests/Auth/ResendVerificationCodeRequest.php b/app/Http/Requests/Auth/ResendVerificationCodeRequest.php new file mode 100644 index 0000000..ab36250 --- /dev/null +++ b/app/Http/Requests/Auth/ResendVerificationCodeRequest.php @@ -0,0 +1,22 @@ + ['required', Rule::in(['email', 'whatsapp'])], + 'verification_target' => ['required', 'string', 'max:100'], + ]; + } +} diff --git a/app/Http/Requests/Auth/VerifyRegistrationRequest.php b/app/Http/Requests/Auth/VerifyRegistrationRequest.php new file mode 100644 index 0000000..e06dd99 --- /dev/null +++ b/app/Http/Requests/Auth/VerifyRegistrationRequest.php @@ -0,0 +1,23 @@ + ['required', Rule::in(['email', 'whatsapp'])], + 'verification_target' => ['required', 'string', 'max:100'], + 'verification_code' => ['required', 'digits:6'], + ]; + } +} diff --git a/app/Http/Requests/File/IndexStoredFileRequest.php b/app/Http/Requests/File/IndexStoredFileRequest.php new file mode 100644 index 0000000..c23dd12 --- /dev/null +++ b/app/Http/Requests/File/IndexStoredFileRequest.php @@ -0,0 +1,23 @@ + ['nullable', 'string', 'max:255'], + 'category' => ['nullable', 'string', 'max:80'], + 'mime_type' => ['nullable', 'string', 'max:150'], + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + ]; + } +} diff --git a/app/Http/Requests/File/StoreFileRequest.php b/app/Http/Requests/File/StoreFileRequest.php new file mode 100644 index 0000000..43fe169 --- /dev/null +++ b/app/Http/Requests/File/StoreFileRequest.php @@ -0,0 +1,35 @@ + [ + 'required', + 'file', + 'max:20480', + 'mimes:jpg,jpeg,png,webp,gif,pdf,doc,docx,xls,xlsx,csv,txt,zip', + ], + 'category' => ['nullable', 'string', 'max:80', 'regex:/^[a-z0-9_-]+$/'], + ]; + } + + public function messages(): array + { + return [ + 'file.max' => 'Ukuran file maksimal 20 MB.', + 'file.mimes' => 'Jenis file tidak didukung.', + 'category.regex' => 'Kategori hanya boleh berisi huruf kecil, angka, garis bawah, atau tanda hubung.', + ]; + } +} diff --git a/app/Http/Requests/Material/IndexMaterialRequest.php b/app/Http/Requests/Material/IndexMaterialRequest.php new file mode 100644 index 0000000..820476a --- /dev/null +++ b/app/Http/Requests/Material/IndexMaterialRequest.php @@ -0,0 +1,32 @@ + ['nullable', 'string', 'max:255'], + /** Filter material berdasarkan ID user/teknisi. */ + 'user_id' => ['nullable', 'integer', 'exists:users,id'], + /** Filter jenis material. */ + 'type' => ['nullable', Rule::in(['serialized', 'consumable'])], + /** Filter status material. */ + 'status' => ['nullable', 'string', 'max:50'], + /** Nomor halaman yang ingin diambil. */ + 'page' => ['nullable', 'integer', 'min:1'], + /** Jumlah data per halaman, maksimal 100. */ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + ]; + } +} diff --git a/app/Http/Requests/MenuGroup/IndexMenuGroupRequest.php b/app/Http/Requests/MenuGroup/IndexMenuGroupRequest.php new file mode 100644 index 0000000..2196df7 --- /dev/null +++ b/app/Http/Requests/MenuGroup/IndexMenuGroupRequest.php @@ -0,0 +1,32 @@ + ['nullable', 'string', 'max:255'], + /** Filter status aktif menu group. */ + 'is_active' => ['nullable', 'boolean'], + /** Nomor halaman yang ingin diambil. */ + 'page' => ['nullable', 'integer', 'min:1'], + /** Jumlah data per halaman, maksimal 100. */ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + /** Kolom pengurutan data. */ + 'sort_by' => ['nullable', Rule::in(['id', 'name', 'description', 'is_active', 'is_system', 'created_at'])], + /** Arah pengurutan data. */ + 'sort_direction' => ['nullable', Rule::in(['asc', 'desc'])], + ]; + } +} diff --git a/app/Http/Requests/MenuGroup/StoreMenuGroupRequest.php b/app/Http/Requests/MenuGroup/StoreMenuGroupRequest.php new file mode 100644 index 0000000..da95ed2 --- /dev/null +++ b/app/Http/Requests/MenuGroup/StoreMenuGroupRequest.php @@ -0,0 +1,62 @@ +|string> + */ + public function rules(): array + { + $isMaster = $this->user()?->isMasterAdmin() ?? false; + $tenantId = $isMaster + ? $this->input('tenant_id') + : $this->attributes->get('tenant_id'); + + return [ + 'tenant_id' => [ + $isMaster ? 'nullable' : 'prohibited', + 'integer', + 'exists:tenants,id', + ], + 'name' => [ + 'required', + 'string', + 'max:100', + Rule::unique('menu_groups', 'name') + ->where(fn ($query) => $tenantId + ? $query->where('tenant_id', $tenantId) + : $query->whereNull('tenant_id')), + ], + 'description' => [ + 'nullable', + 'string', + ], + 'is_active' => [ + 'nullable', + 'boolean', + ], + ]; + } + + protected function prepareForValidation(): void + { + $this->merge([ + 'is_active' => filter_var( + $this->is_active, + FILTER_VALIDATE_BOOLEAN, + FILTER_NULL_ON_FAILURE + ), + ]); + } +} diff --git a/app/Http/Requests/MenuGroup/SyncMenuGroupMenusRequest.php b/app/Http/Requests/MenuGroup/SyncMenuGroupMenusRequest.php new file mode 100644 index 0000000..10dc8e6 --- /dev/null +++ b/app/Http/Requests/MenuGroup/SyncMenuGroupMenusRequest.php @@ -0,0 +1,58 @@ +|string> + */ + public function rules(): array + { + return [ + 'menus' => [ + 'required', + 'array', + 'min:1', + ], + 'menus.*.menu_id' => [ + 'required', + 'integer', + 'distinct', + 'exists:menu_lists,id', + ], + 'menus.*.can_view' => [ + 'nullable', + 'boolean', + ], + 'menus.*.can_create' => [ + 'nullable', + 'boolean', + ], + 'menus.*.can_update' => [ + 'nullable', + 'boolean', + ], + 'menus.*.can_delete' => [ + 'nullable', + 'boolean', + ], + 'menus.*.can_approve' => [ + 'nullable', + 'boolean', + ], + 'menus.*.can_export' => [ + 'nullable', + 'boolean', + ], + ]; + } +} diff --git a/app/Http/Requests/MenuGroup/SyncUserMenuGroupsRequest.php b/app/Http/Requests/MenuGroup/SyncUserMenuGroupsRequest.php new file mode 100644 index 0000000..8b98bd2 --- /dev/null +++ b/app/Http/Requests/MenuGroup/SyncUserMenuGroupsRequest.php @@ -0,0 +1,61 @@ +|string> + */ + public function rules(): array + { + return [ + 'assignments' => [ + 'required', + 'array', + 'min:1', + ], + 'assignments.*.menu_group_id' => [ + 'required', + 'integer', + 'exists:menu_groups,id', + ], + 'assignments.*.tenant_id' => [ + 'nullable', + 'integer', + 'exists:tenants,id', + ], + ]; + } + + public function withValidator($validator): void + { + $validator->after(function ($validator) { + $assignments = $this->input('assignments', []); + $seen = []; + + foreach ($assignments as $index => $assignment) { + $menuGroupId = $assignment['menu_group_id'] ?? null; + $tenantId = $assignment['tenant_id'] ?? null; + $key = $menuGroupId.'|'.($tenantId ?? 'null'); + + if (isset($seen[$key])) { + $validator->errors()->add( + "assignments.$index", + 'Kombinasi menu_group_id dan tenant_id harus unik.' + ); + } + + $seen[$key] = true; + } + }); + } +} diff --git a/app/Http/Requests/MenuGroup/UpdateMenuGroupRequest.php b/app/Http/Requests/MenuGroup/UpdateMenuGroupRequest.php new file mode 100644 index 0000000..e5c33f0 --- /dev/null +++ b/app/Http/Requests/MenuGroup/UpdateMenuGroupRequest.php @@ -0,0 +1,66 @@ +|string> + */ + public function rules(): array + { + $menuGroupId = $this->route('menu_group')?->id; + $isMaster = $this->user()?->isMasterAdmin() ?? false; + $tenantId = $isMaster + ? ($this->exists('tenant_id') + ? $this->input('tenant_id') + : $this->route('menu_group')?->tenant_id) + : $this->route('menu_group')?->tenant_id; + + return [ + 'tenant_id' => [ + $isMaster ? 'nullable' : 'prohibited', + 'integer', + 'exists:tenants,id', + ], + 'name' => [ + 'required', + 'string', + 'max:100', + Rule::unique('menu_groups', 'name') + ->where(fn ($query) => $tenantId + ? $query->where('tenant_id', $tenantId) + : $query->whereNull('tenant_id')) + ->ignore($menuGroupId), + ], + 'description' => [ + 'nullable', + 'string', + ], + 'is_active' => [ + 'nullable', + 'boolean', + ], + ]; + } + + protected function prepareForValidation(): void + { + $this->merge([ + 'is_active' => filter_var( + $this->is_active, + FILTER_VALIDATE_BOOLEAN, + FILTER_NULL_ON_FAILURE + ), + ]); + } +} diff --git a/app/Http/Requests/MenuList/IndexMenuListRequest.php b/app/Http/Requests/MenuList/IndexMenuListRequest.php new file mode 100644 index 0000000..cc8ff78 --- /dev/null +++ b/app/Http/Requests/MenuList/IndexMenuListRequest.php @@ -0,0 +1,27 @@ + ['nullable', 'string', 'max:255'], + /** Filter status aktif menu. */ + 'is_active' => ['nullable', 'boolean'], + /** Nomor halaman yang ingin diambil. */ + 'page' => ['nullable', 'integer', 'min:1'], + /** Jumlah data per halaman, maksimal 100. */ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + ]; + } +} diff --git a/app/Http/Requests/MenuList/StoreMenuListRequest.php b/app/Http/Requests/MenuList/StoreMenuListRequest.php new file mode 100644 index 0000000..ccf29a1 --- /dev/null +++ b/app/Http/Requests/MenuList/StoreMenuListRequest.php @@ -0,0 +1,81 @@ +|string> + */ + public function rules(): array + { + return [ + 'parent_id' => [ + 'nullable', + 'integer', + Rule::exists('menu_lists', 'id'), + ], + + 'name' => [ + 'required', + 'string', + 'max:255', + ], + + 'slug' => [ + 'required', + 'string', + 'max:255', + 'unique:menu_lists,slug', + ], + + 'url' => [ + 'nullable', + 'string', + 'max:255', + ], + + 'icon' => [ + 'nullable', + 'string', + 'max:255', + ], + + 'component' => [ + 'nullable', + 'string', + 'max:255', + ], + + 'sort_order' => [ + 'nullable', + 'integer', + 'min:0', + ], + + 'is_active' => [ + 'nullable', + 'boolean', + ], + ]; + } + + protected function prepareForValidation(): void + { + $this->merge([ + 'is_active' => filter_var( + $this->is_active, + FILTER_VALIDATE_BOOLEAN + ), + ]); + } +} diff --git a/app/Http/Requests/MenuList/UpdateMenuListRequest.php b/app/Http/Requests/MenuList/UpdateMenuListRequest.php new file mode 100644 index 0000000..bc84bd9 --- /dev/null +++ b/app/Http/Requests/MenuList/UpdateMenuListRequest.php @@ -0,0 +1,83 @@ +|string> + */ + public function rules(): array + { + return [ + 'parent_id' => [ + 'nullable', + 'integer', + Rule::exists('menu_lists', 'id'), + 'different:menu_list.id', + ], + + 'name' => [ + 'required', + 'string', + 'max:255', + ], + + 'slug' => [ + 'required', + 'string', + 'max:255', + Rule::unique('menu_lists', 'slug') + ->ignore($this->menu_list->id), + ], + + 'url' => [ + 'nullable', + 'string', + 'max:255', + ], + + 'icon' => [ + 'nullable', + 'string', + 'max:255', + ], + + 'component' => [ + 'nullable', + 'string', + 'max:255', + ], + + 'sort_order' => [ + 'nullable', + 'integer', + 'min:0', + ], + + 'is_active' => [ + 'nullable', + 'boolean', + ], + ]; + } + + protected function prepareForValidation(): void + { + $this->merge([ + 'is_active' => filter_var( + $this->is_active, + FILTER_VALIDATE_BOOLEAN + ), + ]); + } +} diff --git a/app/Http/Requests/Nas/IndexNasResourceRequest.php b/app/Http/Requests/Nas/IndexNasResourceRequest.php new file mode 100644 index 0000000..b9e295f --- /dev/null +++ b/app/Http/Requests/Nas/IndexNasResourceRequest.php @@ -0,0 +1,28 @@ + ['nullable', 'string', 'max:255'], + 'status' => ['nullable', Rule::in(['active', 'inactive'])], + 'connection_type' => ['nullable', Rule::in(['api', 'radius'])], + 'service_type' => ['nullable', Rule::in(['ppp', 'hotspot', 'radius'])], + 'page' => ['nullable', 'integer', 'min:1'], + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + 'sort_by' => ['nullable', Rule::in(['id', 'name', 'host', 'connection_type', 'service_type', 'vendor', 'device_type', 'status', 'created_at'])], + 'sort_direction' => ['nullable', Rule::in(['asc', 'desc'])], + ]; + } +} diff --git a/app/Http/Requests/Nas/StoreNasResourceRequest.php b/app/Http/Requests/Nas/StoreNasResourceRequest.php new file mode 100644 index 0000000..d020c64 --- /dev/null +++ b/app/Http/Requests/Nas/StoreNasResourceRequest.php @@ -0,0 +1,75 @@ +commonRules(), match ($this->route()->defaults['nas_resource'] ?? null) { + 'mikrotik' => [ + 'connection_type' => ['required', Rule::in(['api', 'radius'])], + 'host' => ['required', 'string', 'max:255'], + 'api_port' => ['nullable', 'integer', 'between:1,65535'], + 'api_username' => ['nullable', 'string', 'max:255'], + 'api_password' => ['nullable', 'string', 'max:1000'], + 'radius_auth_port' => ['nullable', 'integer', 'between:1,65535'], + 'radius_accounting_port' => ['nullable', 'integer', 'between:1,65535'], + 'radius_secret' => ['nullable', 'string', 'max:1000'], + 'timeout' => ['nullable', 'integer', 'between:1,300'], + ], + 'package-profile' => [ + 'service_type' => ['required', Rule::in(['ppp', 'hotspot', 'radius'])], + 'external_profile_name' => ['nullable', 'string', 'max:255'], + 'download_kbps' => ['nullable', 'integer', 'min:0'], + 'upload_kbps' => ['nullable', 'integer', 'min:0'], + 'local_address' => ['nullable', 'string', 'max:255'], + 'remote_address_pool' => ['nullable', 'string', 'max:255'], + 'session_timeout' => ['nullable', 'integer', 'min:0'], + 'idle_timeout' => ['nullable', 'integer', 'min:0'], + 'shared_users' => ['nullable', 'integer', 'min:1'], + 'price' => ['nullable', 'numeric', 'min:0'], + ], + 'olt' => [ + 'vendor' => ['nullable', 'string', 'max:255'], + 'model' => ['nullable', 'string', 'max:255'], + 'host' => ['required', 'string', 'max:255'], + 'snmp_port' => ['nullable', 'integer', 'between:1,65535'], + 'snmp_version' => ['required', Rule::in(['v1', 'v2c', 'v3'])], + 'community' => ['nullable', 'string', 'max:1000'], + 'security_level' => ['nullable', Rule::in(['noAuthNoPriv', 'authNoPriv', 'authPriv'])], + 'security_name' => ['nullable', 'string', 'max:255'], + 'auth_protocol' => ['nullable', Rule::in(['MD5', 'SHA', 'SHA224', 'SHA256', 'SHA384', 'SHA512'])], + 'auth_password' => ['nullable', 'string', 'max:1000'], + 'privacy_protocol' => ['nullable', Rule::in(['DES', 'AES', 'AES192', 'AES256'])], + 'privacy_password' => ['nullable', 'string', 'max:1000'], + 'timeout' => ['nullable', 'integer', 'between:1,300'], + ], + 'webfig' => [ + 'device_type' => ['nullable', 'string', 'max:100'], + 'url' => ['required', 'url:http,https', 'max:2000'], + 'username' => ['nullable', 'string', 'max:255'], + 'password' => ['nullable', 'string', 'max:1000'], + ], + default => [], + }); + } + + protected function commonRules(): array + { + return [ + 'tenant_id' => ['nullable', 'integer', 'exists:tenants,id'], + 'name' => ['required', 'string', 'max:255'], + 'status' => ['nullable', Rule::in(['active', 'inactive'])], + 'notes' => ['nullable', 'string'], + ]; + } +} diff --git a/app/Http/Requests/Nas/UpdateNasResourceRequest.php b/app/Http/Requests/Nas/UpdateNasResourceRequest.php new file mode 100644 index 0000000..ae8df2c --- /dev/null +++ b/app/Http/Requests/Nas/UpdateNasResourceRequest.php @@ -0,0 +1,20 @@ +map(function ($rules) { + $rules = is_array($rules) ? $rules : [$rules]; + if (($index = array_search('required', $rules, true)) !== false) { + $rules[$index] = 'sometimes'; + } + + return $rules; + }) + ->all(); + } +} diff --git a/app/Http/Requests/Profile/UpdateProfileRequest.php b/app/Http/Requests/Profile/UpdateProfileRequest.php new file mode 100644 index 0000000..7be3884 --- /dev/null +++ b/app/Http/Requests/Profile/UpdateProfileRequest.php @@ -0,0 +1,95 @@ +user()->id; + + return [ + 'name' => ['required', 'string', 'max:255'], + 'username' => [ + 'nullable', + 'string', + 'max:255', + Rule::unique('users', 'username')->ignore($userId), + ], + 'email' => [ + 'required', + 'email', + 'max:255', + Rule::unique('users', 'email')->ignore($userId), + ], + 'whatsapp_number' => ['nullable', 'string', 'max:20'], + 'profile_photo_file_id' => ['nullable', 'integer', 'exists:stored_files,id'], + 'current_password' => ['nullable', 'required_with:password', 'string'], + 'password' => ['nullable', 'string', 'min:8', 'confirmed'], + + 'user_profile' => ['nullable', 'array'], + 'user_profile.nik' => ['nullable', 'string', 'max:30'], + 'user_profile.address' => ['nullable', 'string'], + 'user_profile.provinsi_id' => ['nullable', 'integer', 'exists:wilayah_provinsi,id'], + 'user_profile.kabupaten_id' => [ + 'nullable', + 'integer', + Rule::exists('wilayah_kabupaten', 'id')->where( + 'provinsi_id', + $this->input('user_profile.provinsi_id') + ), + ], + 'user_profile.kecamatan_id' => [ + 'nullable', + 'integer', + Rule::exists('wilayah_kecamatan', 'id')->where( + 'kabupaten_id', + $this->input('user_profile.kabupaten_id') + ), + ], + 'user_profile.desa_id' => [ + 'nullable', + 'integer', + Rule::exists('wilayah_desa', 'id')->where( + 'kecamatan_id', + $this->input('user_profile.kecamatan_id') + ), + ], + ]; + } + + public function after(): array + { + return [ + function (Validator $validator) { + if ($this->filled('password') + && ! Hash::check((string) $this->input('current_password'), $this->user()->password)) { + $validator->errors()->add( + 'current_password', + 'Password saat ini tidak sesuai.' + ); + } + + if ($this->filled('profile_photo_file_id')) { + $file = StoredFile::find($this->integer('profile_photo_file_id')); + + if ($file) { + app(StoredFileService::class)->validateProfilePhoto($file, $this->user()); + } + } + }, + ]; + } +} diff --git a/app/Http/Requests/Tenant/IndexTenantRequest.php b/app/Http/Requests/Tenant/IndexTenantRequest.php new file mode 100644 index 0000000..1319d3f --- /dev/null +++ b/app/Http/Requests/Tenant/IndexTenantRequest.php @@ -0,0 +1,32 @@ + ['nullable', 'string', 'max:255'], + /** Filter status tenant. */ + 'status' => ['nullable', Rule::in(['active', 'inactive'])], + /** Nomor halaman yang ingin diambil. */ + 'page' => ['nullable', 'integer', 'min:1'], + /** Jumlah data per halaman, maksimal 100. */ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + /** Kolom pengurutan data. */ + 'sort_by' => ['nullable', Rule::in(['id', 'tenant_code', 'tenant_name', 'email', 'status', 'created_at'])], + /** Arah pengurutan data. */ + 'sort_direction' => ['nullable', Rule::in(['asc', 'desc'])], + ]; + } +} diff --git a/app/Http/Requests/Tenant/StoreTenantRequest.php b/app/Http/Requests/Tenant/StoreTenantRequest.php new file mode 100644 index 0000000..6fa5a8c --- /dev/null +++ b/app/Http/Requests/Tenant/StoreTenantRequest.php @@ -0,0 +1,52 @@ +|string> + */ + public function rules(): array + { + return [ + 'tenant_code' => [ + 'required', + 'string', + 'max:100', + 'unique:tenants,tenant_code', + ], + 'tenant_name' => [ + 'required', + 'string', + 'max:255', + ], + 'phone' => [ + 'nullable', + 'string', + 'max:50', + ], + 'email' => [ + 'nullable', + 'email', + 'max:255', + ], + 'address' => [ + 'nullable', + 'string', + ], + 'status' => [ + 'nullable', + 'in:active,inactive', + ], + ]; + } +} diff --git a/app/Http/Requests/Tenant/UpdateTenantRequest.php b/app/Http/Requests/Tenant/UpdateTenantRequest.php new file mode 100644 index 0000000..2dbd9ef --- /dev/null +++ b/app/Http/Requests/Tenant/UpdateTenantRequest.php @@ -0,0 +1,58 @@ +|string> + */ + public function rules(): array + { + $tenantId = $this->route('tenant')?->id; + + return [ + 'tenant_code' => [ + 'sometimes', + 'required', + 'string', + 'max:100', + Rule::unique('tenants', 'tenant_code')->ignore($tenantId), + ], + 'tenant_name' => [ + 'sometimes', + 'required', + 'string', + 'max:255', + ], + 'phone' => [ + 'nullable', + 'string', + 'max:50', + ], + 'email' => [ + 'nullable', + 'email', + 'max:255', + ], + 'address' => [ + 'nullable', + 'string', + ], + 'status' => [ + 'sometimes', + 'required', + 'in:active,inactive', + ], + ]; + } +} diff --git a/app/Http/Requests/Ticket/IndexTicketRequest.php b/app/Http/Requests/Ticket/IndexTicketRequest.php new file mode 100644 index 0000000..705cbe8 --- /dev/null +++ b/app/Http/Requests/Ticket/IndexTicketRequest.php @@ -0,0 +1,37 @@ + ['nullable', 'string', 'max:255'], + /** Filter berdasarkan status ticket. */ + 'status' => ['nullable', Rule::in([ + 'draft', 'open', 'approved', 'assigned', 'progress', 'pending', + 'resolved', 'closed', 'cancelled', 'rejected', + ])], + /** Filter berdasarkan prioritas ticket. */ + 'priority' => ['nullable', Rule::in(['low', 'medium', 'high', 'critical'])], + /** Filter berdasarkan ID tipe ticket. */ + 'ticket_type_id' => ['nullable', 'integer', 'exists:ticket_types,id'], + /** Filter berdasarkan ID customer. */ + 'customer_id' => ['nullable', 'integer'], + /** Nomor halaman yang ingin diambil. */ + 'page' => ['nullable', 'integer', 'min:1'], + /** Jumlah data per halaman, maksimal 100. */ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + ]; + } +} diff --git a/app/Http/Requests/TicketIncidentType/IndexTicketIncidentTypeRequest.php b/app/Http/Requests/TicketIncidentType/IndexTicketIncidentTypeRequest.php new file mode 100644 index 0000000..49186b0 --- /dev/null +++ b/app/Http/Requests/TicketIncidentType/IndexTicketIncidentTypeRequest.php @@ -0,0 +1,27 @@ + ['nullable', 'string', 'max:255'], + /** Filter status aktif incident type. */ + 'is_active' => ['nullable', 'boolean'], + /** Nomor halaman yang ingin diambil. */ + 'page' => ['nullable', 'integer', 'min:1'], + /** Jumlah data per halaman, maksimal 100. */ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + ]; + } +} diff --git a/app/Http/Requests/TicketType/IndexTicketTypeRequest.php b/app/Http/Requests/TicketType/IndexTicketTypeRequest.php new file mode 100644 index 0000000..341309a --- /dev/null +++ b/app/Http/Requests/TicketType/IndexTicketTypeRequest.php @@ -0,0 +1,25 @@ + ['nullable', 'string', 'max:255'], + /** Nomor halaman yang ingin diambil. */ + 'page' => ['nullable', 'integer', 'min:1'], + /** Jumlah data per halaman, maksimal 100. */ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + ]; + } +} diff --git a/app/Http/Requests/User/IndexUserRequest.php b/app/Http/Requests/User/IndexUserRequest.php new file mode 100644 index 0000000..a00f00e --- /dev/null +++ b/app/Http/Requests/User/IndexUserRequest.php @@ -0,0 +1,35 @@ + ['nullable', 'string', 'max:255'], + /** Filter status akun user. */ + 'status' => ['nullable', Rule::in(['active', 'inactive', 'suspended'])], + /** Filter level akses user. */ + 'access_level' => ['nullable', Rule::enum(UserAccessLevel::class)], + /** Nomor halaman yang ingin diambil. */ + 'page' => ['nullable', 'integer', 'min:1'], + /** Jumlah data per halaman, maksimal 100. */ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + /** Kolom pengurutan data. */ + 'sort_by' => ['nullable', Rule::in(['id', 'name', 'username', 'email', 'whatsapp_number', 'access_level', 'status', 'created_at'])], + /** Arah pengurutan data. */ + 'sort_direction' => ['nullable', Rule::in(['asc', 'desc'])], + ]; + } +} diff --git a/app/Http/Requests/User/StoreUserRequest.php b/app/Http/Requests/User/StoreUserRequest.php new file mode 100644 index 0000000..a7a5602 --- /dev/null +++ b/app/Http/Requests/User/StoreUserRequest.php @@ -0,0 +1,66 @@ + ['required', 'string', 'max:255'], + 'username' => ['nullable', 'string', 'max:255', 'unique:users,username'], + 'email' => ['required', 'email', 'max:255', 'unique:users,email'], + 'whatsapp_number' => ['nullable', 'string', 'max:20'], + 'password' => ['required', 'string', 'min:8'], + 'is_master' => ['nullable', 'boolean'], + 'access_level' => ['nullable', Rule::enum(UserAccessLevel::class)], + 'status' => ['required', Rule::in(['active', 'inactive', 'suspended'])], + + 'user_profile' => ['nullable', 'array'], + 'user_profile.nik' => ['nullable', 'string', 'max:30'], + 'user_profile.address' => ['nullable', 'string'], + 'user_profile.provinsi_id' => ['nullable', 'integer', 'exists:wilayah_provinsi,id'], + 'user_profile.kabupaten_id' => [ + 'nullable', + 'integer', + Rule::exists('wilayah_kabupaten', 'id')->where( + 'provinsi_id', + $this->input('user_profile.provinsi_id') + ), + ], + 'user_profile.kecamatan_id' => [ + 'nullable', + 'integer', + Rule::exists('wilayah_kecamatan', 'id')->where( + 'kabupaten_id', + $this->input('user_profile.kabupaten_id') + ), + ], + 'user_profile.desa_id' => [ + 'nullable', + 'integer', + Rule::exists('wilayah_desa', 'id')->where( + 'kecamatan_id', + $this->input('user_profile.kecamatan_id') + ), + ], + + 'user_tenants' => ['nullable', 'array'], + 'user_tenants.*.tenant_id' => ['required_with:user_tenants', 'integer', 'exists:tenants,id'], + 'user_tenants.*.is_default' => ['nullable', 'boolean'], + + 'user_menu_groups' => ['nullable', 'array'], + 'user_menu_groups.*.menu_group_id' => ['required_with:user_menu_groups', 'integer', 'exists:menu_groups,id'], + 'user_menu_groups.*.tenant_id' => ['nullable', 'integer', 'exists:tenants,id'], + ]; + } +} diff --git a/app/Http/Requests/User/UpdateUserRequest.php b/app/Http/Requests/User/UpdateUserRequest.php new file mode 100644 index 0000000..06ad2a9 --- /dev/null +++ b/app/Http/Requests/User/UpdateUserRequest.php @@ -0,0 +1,79 @@ +route('user')?->id ?? $this->route('user'); + + return [ + 'name' => ['sometimes', 'required', 'string', 'max:255'], + 'username' => [ + 'nullable', + 'string', + 'max:255', + Rule::unique('users', 'username')->ignore($userId), + ], + 'email' => [ + 'sometimes', + 'required', + 'email', + 'max:255', + Rule::unique('users', 'email')->ignore($userId), + ], + 'whatsapp_number' => ['nullable', 'string', 'max:20'], + 'password' => ['nullable', 'string', 'min:8'], + 'is_master' => ['nullable', 'boolean'], + 'access_level' => ['sometimes', 'required', Rule::enum(UserAccessLevel::class)], + 'status' => ['sometimes', 'required', Rule::in(['active', 'inactive', 'suspended'])], + + 'user_profile' => ['nullable', 'array'], + 'user_profile.nik' => ['nullable', 'string', 'max:30'], + 'user_profile.address' => ['nullable', 'string'], + 'user_profile.provinsi_id' => ['nullable', 'integer', 'exists:wilayah_provinsi,id'], + 'user_profile.kabupaten_id' => [ + 'nullable', + 'integer', + Rule::exists('wilayah_kabupaten', 'id')->where( + 'provinsi_id', + $this->input('user_profile.provinsi_id') + ), + ], + 'user_profile.kecamatan_id' => [ + 'nullable', + 'integer', + Rule::exists('wilayah_kecamatan', 'id')->where( + 'kabupaten_id', + $this->input('user_profile.kabupaten_id') + ), + ], + 'user_profile.desa_id' => [ + 'nullable', + 'integer', + Rule::exists('wilayah_desa', 'id')->where( + 'kecamatan_id', + $this->input('user_profile.kecamatan_id') + ), + ], + + 'user_tenants' => ['nullable', 'array'], + 'user_tenants.*.tenant_id' => ['required_with:user_tenants', 'integer', 'exists:tenants,id'], + 'user_tenants.*.is_default' => ['nullable', 'boolean'], + + 'user_menu_groups' => ['nullable', 'array'], + 'user_menu_groups.*.menu_group_id' => ['required_with:user_menu_groups', 'integer', 'exists:menu_groups,id'], + 'user_menu_groups.*.tenant_id' => ['nullable', 'integer', 'exists:tenants,id'], + ]; + } +} diff --git a/app/Http/Requests/Wilayah/AlamatLengkapRequest.php b/app/Http/Requests/Wilayah/AlamatLengkapRequest.php new file mode 100644 index 0000000..f9c78fa --- /dev/null +++ b/app/Http/Requests/Wilayah/AlamatLengkapRequest.php @@ -0,0 +1,23 @@ + ['nullable', 'required_without_all:kecamatan_id,kabupaten_id,provinsi_id', 'integer', 'exists:wilayah_desa,id'], + 'kecamatan_id' => ['nullable', 'required_without_all:desa_id,kabupaten_id,provinsi_id', 'integer', 'exists:wilayah_kecamatan,id'], + 'kabupaten_id' => ['nullable', 'required_without_all:desa_id,kecamatan_id,provinsi_id', 'integer', 'exists:wilayah_kabupaten,id'], + 'provinsi_id' => ['nullable', 'required_without_all:desa_id,kecamatan_id,kabupaten_id', 'integer', 'exists:wilayah_provinsi,id'], + ]; + } +} diff --git a/app/Http/Requests/Wilayah/IndexWilayahRequest.php b/app/Http/Requests/Wilayah/IndexWilayahRequest.php new file mode 100644 index 0000000..011f164 --- /dev/null +++ b/app/Http/Requests/Wilayah/IndexWilayahRequest.php @@ -0,0 +1,36 @@ + ['nullable', 'string', 'max:255'], + /** Filter kabupaten berdasarkan provinsi. */ + 'provinsi_id' => ['nullable', 'integer', 'exists:wilayah_provinsi,id'], + /** Filter kecamatan berdasarkan kabupaten. */ + 'kabupaten_id' => ['nullable', 'integer', 'exists:wilayah_kabupaten,id'], + /** Filter desa berdasarkan kecamatan. */ + 'kecamatan_id' => ['nullable', 'integer', 'exists:wilayah_kecamatan,id'], + /** Nomor halaman. */ + 'page' => ['nullable', 'integer', 'min:1'], + /** Jumlah data per halaman, maksimal 100. */ + 'per_page' => ['nullable', 'integer', 'min:1', 'max:100'], + /** Kolom pengurutan data. */ + 'sort_by' => ['nullable', Rule::in(['id', 'kode', 'nama', 'created_at'])], + /** Arah pengurutan data. */ + 'sort_direction' => ['nullable', Rule::in(['asc', 'desc'])], + ]; + } +} diff --git a/app/Http/Requests/Wilayah/LookupWilayahRequest.php b/app/Http/Requests/Wilayah/LookupWilayahRequest.php new file mode 100644 index 0000000..dc42a19 --- /dev/null +++ b/app/Http/Requests/Wilayah/LookupWilayahRequest.php @@ -0,0 +1,21 @@ + ['nullable', 'string', 'max:100'], + ]; + } +} diff --git a/app/Http/Requests/Wilayah/StoreWilayahRequest.php b/app/Http/Requests/Wilayah/StoreWilayahRequest.php new file mode 100644 index 0000000..f0dfde8 --- /dev/null +++ b/app/Http/Requests/Wilayah/StoreWilayahRequest.php @@ -0,0 +1,40 @@ +route('tingkat'); + $table = "wilayah_{$tingkat}"; + + return [ + 'kode' => ['required', 'string', 'max:20', Rule::unique($table, 'kode')], + 'nama' => ['required', 'string', 'max:255'], + 'provinsi_id' => [ + Rule::requiredIf($tingkat === 'kabupaten'), + 'integer', + 'exists:wilayah_provinsi,id', + ], + 'kabupaten_id' => [ + Rule::requiredIf($tingkat === 'kecamatan'), + 'integer', + 'exists:wilayah_kabupaten,id', + ], + 'kecamatan_id' => [ + Rule::requiredIf($tingkat === 'desa'), + 'integer', + 'exists:wilayah_kecamatan,id', + ], + ]; + } +} diff --git a/app/Http/Requests/Wilayah/UpdateWilayahRequest.php b/app/Http/Requests/Wilayah/UpdateWilayahRequest.php new file mode 100644 index 0000000..837b876 --- /dev/null +++ b/app/Http/Requests/Wilayah/UpdateWilayahRequest.php @@ -0,0 +1,52 @@ +route('tingkat'); + $table = "wilayah_{$tingkat}"; + + return [ + 'kode' => [ + 'sometimes', + 'required', + 'string', + 'max:20', + Rule::unique($table, 'kode')->ignore($this->route('wilayah')), + ], + 'nama' => ['sometimes', 'required', 'string', 'max:255'], + 'provinsi_id' => [ + Rule::excludeIf($tingkat !== 'kabupaten'), + 'sometimes', + 'required', + 'integer', + 'exists:wilayah_provinsi,id', + ], + 'kabupaten_id' => [ + Rule::excludeIf($tingkat !== 'kecamatan'), + 'sometimes', + 'required', + 'integer', + 'exists:wilayah_kabupaten,id', + ], + 'kecamatan_id' => [ + Rule::excludeIf($tingkat !== 'desa'), + 'sometimes', + 'required', + 'integer', + 'exists:wilayah_kecamatan,id', + ], + ]; + } +} diff --git a/app/Http/Resources/File/StoredFileResource.php b/app/Http/Resources/File/StoredFileResource.php new file mode 100644 index 0000000..de45a88 --- /dev/null +++ b/app/Http/Resources/File/StoredFileResource.php @@ -0,0 +1,24 @@ + $this->id, + 'uuid' => $this->uuid, + 'tenant_id' => $this->tenant_id, + 'original_name' => $this->original_name, + 'extension' => $this->extension, + 'mime_type' => $this->mime_type, + 'size' => $this->size, + 'category' => $this->category, + 'created_at' => $this->created_at, + ]; + } +} diff --git a/app/Http/Resources/MenuGroup/MenuGroupResource.php b/app/Http/Resources/MenuGroup/MenuGroupResource.php new file mode 100644 index 0000000..bc95b10 --- /dev/null +++ b/app/Http/Resources/MenuGroup/MenuGroupResource.php @@ -0,0 +1,31 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'tenant_id' => $this->tenant_id, + 'name' => $this->name, + 'description' => $this->description, + 'is_system' => (bool) $this->is_system, + 'is_active' => (bool) $this->is_active, + 'tenant' => $this->whenLoaded('tenant', fn () => $this->tenant ? [ + 'id' => $this->tenant->id, + 'tenant_code' => $this->tenant->tenant_code, + 'tenant_name' => $this->tenant->tenant_name, + ] : null), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/app/Http/Resources/MenuList/MenuListResource.php b/app/Http/Resources/MenuList/MenuListResource.php new file mode 100644 index 0000000..005e5e0 --- /dev/null +++ b/app/Http/Resources/MenuList/MenuListResource.php @@ -0,0 +1,31 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'parent_id' => $this->parent_id, + 'name' => $this->name, + 'slug' => $this->slug, + 'url' => $this->url, + 'icon' => $this->icon, + 'component' => $this->component, + 'sort_order' => $this->sort_order, + 'is_active' => $this->is_active, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/app/Http/Resources/Nas/NasResource.php b/app/Http/Resources/Nas/NasResource.php new file mode 100644 index 0000000..1bc984c --- /dev/null +++ b/app/Http/Resources/Nas/NasResource.php @@ -0,0 +1,36 @@ +resource->attributesToArray(); + unset($data['api_password'], $data['radius_secret'], $data['community'], $data['auth_password'], $data['privacy_password'], $data['password']); + + $data['tenant'] = $this->whenLoaded('tenant', fn () => [ + 'id' => $this->tenant->id, + 'tenant_code' => $this->tenant->tenant_code, + 'tenant_name' => $this->tenant->tenant_name, + ]); + + foreach ([ + 'api_password' => 'has_api_password', + 'radius_secret' => 'has_radius_secret', + 'community' => 'has_community', + 'auth_password' => 'has_auth_password', + 'privacy_password' => 'has_privacy_password', + 'password' => 'has_password', + ] as $column => $flag) { + if (array_key_exists($column, $this->resource->getAttributes())) { + $data[$flag] = filled($this->resource->getRawOriginal($column)); + } + } + + return $data; + } +} diff --git a/app/Http/Resources/Tenant/TenantResource.php b/app/Http/Resources/Tenant/TenantResource.php new file mode 100644 index 0000000..8d7b4a4 --- /dev/null +++ b/app/Http/Resources/Tenant/TenantResource.php @@ -0,0 +1,29 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'tenant_code' => $this->tenant_code, + 'tenant_name' => $this->tenant_name, + 'phone' => $this->phone, + 'email' => $this->email, + 'address' => $this->address, + 'status' => $this->status, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} diff --git a/app/Http/Resources/User/UserResource.php b/app/Http/Resources/User/UserResource.php new file mode 100644 index 0000000..cf4031d --- /dev/null +++ b/app/Http/Resources/User/UserResource.php @@ -0,0 +1,94 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name, + 'username' => $this->username, + 'email' => $this->email, + 'whatsapp_number' => $this->whatsapp_number, + 'profile_photo' => $this->whenLoaded( + 'profilePhoto', + fn () => $this->profilePhoto ? new StoredFileResource($this->profilePhoto) : null + ), + 'is_master' => (bool) $this->is_master, + 'access_level' => $this->access_level->value, + 'status' => $this->status, + 'verification_channel' => $this->verification_channel, + 'verification_target' => $this->verification_target, + 'verified_at' => $this->verified_at, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + + 'user_profile' => $this->whenLoaded('userProfile', function () { + return $this->userProfile ? [ + 'id' => $this->userProfile->id, + 'user_id' => $this->userProfile->user_id, + 'nik' => $this->userProfile->nik, + 'address' => $this->userProfile->address, + 'provinsi_id' => $this->userProfile->provinsi_id, + 'kabupaten_id' => $this->userProfile->kabupaten_id, + 'kecamatan_id' => $this->userProfile->kecamatan_id, + 'desa_id' => $this->userProfile->desa_id, + 'created_at' => $this->userProfile->created_at, + 'updated_at' => $this->userProfile->updated_at, + ] : null; + }), + + 'user_tenants' => $this->whenLoaded('userTenants', function () { + return $this->userTenants->map(function ($item) { + return [ + 'id' => $item->id, + 'user_id' => $item->user_id, + 'tenant_id' => $item->tenant_id, + 'is_default' => (bool) $item->is_default, + 'created_at' => $item->created_at, + 'updated_at' => $item->updated_at, + 'tenant' => $item->relationLoaded('tenant') && $item->tenant ? [ + 'id' => $item->tenant->id, + 'tenant_code' => $item->tenant->tenant_code ?? null, + 'tenant_name' => $item->tenant->tenant_name ?? null, + ] : null, + ]; + })->values(); + }), + + 'user_menu_groups' => $this->whenLoaded('userMenuGroups', function () { + return $this->userMenuGroups->map(function ($item) { + return [ + 'id' => $item->id, + 'user_id' => $item->user_id, + 'menu_group_id' => $item->menu_group_id, + 'tenant_id' => $item->tenant_id, + 'created_at' => $item->created_at, + 'updated_at' => $item->updated_at, + 'menu_group' => $item->relationLoaded('menuGroup') && $item->menuGroup ? [ + 'id' => $item->menuGroup->id, + 'name' => $item->menuGroup->name ?? null, + 'menu_group_name' => $item->menuGroup->menu_group_name ?? null, + ] : null, + 'tenant' => $item->relationLoaded('tenant') && $item->tenant ? [ + 'id' => $item->tenant->id, + 'tenant_code' => $item->tenant->tenant_code ?? null, + 'tenant_name' => $item->tenant->tenant_name ?? null, + ] : null, + ]; + })->values(); + }), + ]; + } +} diff --git a/app/Http/Resources/Wilayah/WilayahResource.php b/app/Http/Resources/Wilayah/WilayahResource.php new file mode 100644 index 0000000..b24567f --- /dev/null +++ b/app/Http/Resources/Wilayah/WilayahResource.php @@ -0,0 +1,70 @@ + $this->id, + 'kode' => $this->kode, + 'nama' => $this->nama, + 'tingkat' => match (true) { + $this->resource instanceof WilayahDesa => 'desa', + $this->resource instanceof WilayahKecamatan => 'kecamatan', + $this->resource instanceof WilayahKabupaten => 'kabupaten', + $this->resource instanceof WilayahProvinsi => 'provinsi', + }, + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + + if ($this->resource instanceof WilayahKabupaten) { + $data['provinsi_id'] = $this->provinsi_id; + $data['provinsi'] = $this->whenLoaded('provinsi', fn () => $this->node($this->provinsi)); + } + + if ($this->resource instanceof WilayahKecamatan) { + $data['kabupaten_id'] = $this->kabupaten_id; + $data['kabupaten'] = $this->whenLoaded('kabupaten', fn () => $this->node($this->kabupaten)); + $data['provinsi'] = $this->when( + $this->relationLoaded('kabupaten') && $this->kabupaten?->relationLoaded('provinsi'), + fn () => $this->node($this->kabupaten?->provinsi) + ); + } + + if ($this->resource instanceof WilayahDesa) { + $data['kecamatan_id'] = $this->kecamatan_id; + $data['kecamatan'] = $this->whenLoaded('kecamatan', fn () => $this->node($this->kecamatan)); + $data['kabupaten'] = $this->when( + $this->relationLoaded('kecamatan') && $this->kecamatan?->relationLoaded('kabupaten'), + fn () => $this->node($this->kecamatan?->kabupaten) + ); + $data['provinsi'] = $this->when( + $this->relationLoaded('kecamatan') + && $this->kecamatan?->relationLoaded('kabupaten') + && $this->kecamatan?->kabupaten?->relationLoaded('provinsi'), + fn () => $this->node($this->kecamatan?->kabupaten?->provinsi) + ); + } + + return $data; + } + + private function node($wilayah): ?array + { + return $wilayah ? [ + 'id' => $wilayah->id, + 'kode' => $wilayah->kode, + 'nama' => $wilayah->nama, + ] : null; + } +} diff --git a/app/Models/MenuGroup.php b/app/Models/MenuGroup.php new file mode 100644 index 0000000..b9d826b --- /dev/null +++ b/app/Models/MenuGroup.php @@ -0,0 +1,50 @@ + 'boolean', + 'is_system' => 'boolean', + ]; + + public function menus() + { + return $this->belongsToMany( + MenuList::class, + 'menu_group_menus', + 'menu_group_id', + 'menu_id' + )->withPivot([ + 'can_view', + 'can_create', + 'can_update', + 'can_delete', + 'can_approve', + 'can_export', + ])->withTimestamps(); + } + + public function userMenuGroups() + { + return $this->hasMany(UserMenuGroup::class, 'menu_group_id'); + } + + public function tenant() + { + return $this->belongsTo(Tenant::class); + } +} diff --git a/app/Models/MenuGroupMenu.php b/app/Models/MenuGroupMenu.php new file mode 100644 index 0000000..b5e4263 --- /dev/null +++ b/app/Models/MenuGroupMenu.php @@ -0,0 +1,40 @@ + 'boolean', + 'can_create' => 'boolean', + 'can_update' => 'boolean', + 'can_delete' => 'boolean', + 'can_approve' => 'boolean', + 'can_export' => 'boolean', + ]; + + public function menuGroup() + { + return $this->belongsTo(MenuGroup::class, 'menu_group_id'); + } + + public function menu() + { + return $this->belongsTo(MenuList::class, 'menu_id'); + } +} diff --git a/app/Models/MenuList.php b/app/Models/MenuList.php new file mode 100644 index 0000000..bbe5fa8 --- /dev/null +++ b/app/Models/MenuList.php @@ -0,0 +1,62 @@ + 'boolean', + 'sort_order' => 'integer', + ]; + + /* + |-------------------------------------------------------------------------- + | Relationships + |-------------------------------------------------------------------------- + */ + + public function parent() + { + return $this->belongsTo(self::class, 'parent_id'); + } + + public function children() + { + return $this->hasMany(self::class, 'parent_id'); + } + + public function menuGroups() + { + return $this->belongsToMany( + MenuGroup::class, + 'menu_group_menus', + 'menu_id', + 'menu_group_id' + )->withPivot([ + 'can_view', + 'can_create', + 'can_update', + 'can_delete', + 'can_approve', + 'can_export', + ])->withTimestamps(); + } +} diff --git a/app/Models/NasMikrotik.php b/app/Models/NasMikrotik.php new file mode 100644 index 0000000..b0eb353 --- /dev/null +++ b/app/Models/NasMikrotik.php @@ -0,0 +1,22 @@ + 'encrypted', 'radius_secret' => 'encrypted']; + } + + public function tenant() + { + return $this->belongsTo(Tenant::class); + } +} diff --git a/app/Models/NasOlt.php b/app/Models/NasOlt.php new file mode 100644 index 0000000..c4cf790 --- /dev/null +++ b/app/Models/NasOlt.php @@ -0,0 +1,26 @@ + 'encrypted', + 'auth_password' => 'encrypted', + 'privacy_password' => 'encrypted', + ]; + } + + public function tenant() + { + return $this->belongsTo(Tenant::class); + } +} diff --git a/app/Models/NasPackageProfile.php b/app/Models/NasPackageProfile.php new file mode 100644 index 0000000..982fac0 --- /dev/null +++ b/app/Models/NasPackageProfile.php @@ -0,0 +1,20 @@ + 'decimal:2']; + } + + public function tenant() + { + return $this->belongsTo(Tenant::class); + } +} diff --git a/app/Models/NasWebfigDevice.php b/app/Models/NasWebfigDevice.php new file mode 100644 index 0000000..0088996 --- /dev/null +++ b/app/Models/NasWebfigDevice.php @@ -0,0 +1,22 @@ + 'encrypted']; + } + + public function tenant() + { + return $this->belongsTo(Tenant::class); + } +} diff --git a/app/Models/StoredFile.php b/app/Models/StoredFile.php new file mode 100644 index 0000000..19af4d4 --- /dev/null +++ b/app/Models/StoredFile.php @@ -0,0 +1,47 @@ + 'integer']; + } + + public function getRouteKeyName(): string + { + return 'uuid'; + } + + public function uploader(): BelongsTo + { + return $this->belongsTo(User::class, 'uploaded_by'); + } + + public function tenant(): BelongsTo + { + return $this->belongsTo(Tenant::class); + } +} diff --git a/app/Models/Tenant.php b/app/Models/Tenant.php new file mode 100644 index 0000000..2447e7c --- /dev/null +++ b/app/Models/Tenant.php @@ -0,0 +1,17 @@ + */ - use HasFactory, Notifiable; use HasApiTokens; + + /** @use HasFactory */ + use HasFactory, Notifiable; + + use SoftDeletes; // use Audittable; /** @@ -24,8 +30,19 @@ class User extends Authenticatable */ protected $fillable = [ 'name', + 'username', 'email', + 'whatsapp_number', + 'profile_photo_file_id', 'password', + 'is_master', + 'access_level', + 'status', + 'verification_channel', + 'verification_target', + 'verification_code', + 'verification_code_expires_at', + 'verified_at', ]; /** @@ -48,24 +65,55 @@ class User extends Authenticatable return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', + 'is_master' => 'boolean', + 'access_level' => UserAccessLevel::class, + 'verification_code_expires_at' => 'datetime', + 'verified_at' => 'datetime', ]; } - public function login(Request $request) + public function menuGroups() { - $user = User::where('email', $request->email)->first(); + return $this->belongsToMany( + MenuGroup::class, + 'user_menu_groups', + 'user_id', + 'menu_group_id' + )->withPivot(['tenant_id'])->withTimestamps(); + } - if (!$user || !Hash::check($request->password, $user->password)) { - return response()->json([ - 'message' => 'Login gagal' - ], 401); - } + public function userProfile() + { + return $this->hasOne(UserProfile::class, 'user_id'); + } - $token = $user->createToken('services-core')->plainTextToken; + public function profilePhoto() + { + return $this->belongsTo(StoredFile::class, 'profile_photo_file_id'); + } - return response()->json([ - 'token' => $token, - 'user' => $user - ]); + public function userTenants() + { + return $this->hasMany(UserTenant::class, 'user_id'); + } + + public function userMenuGroups() + { + return $this->hasMany(UserMenuGroup::class, 'user_id'); + } + + public function isMasterAdmin(): bool + { + return $this->is_master || $this->access_level === UserAccessLevel::MASTER_ADMIN; + } + + public function isTenantOwner(): bool + { + return $this->access_level === UserAccessLevel::TENANT_OWNER; } } + +// { +// "email": "master@services-core.local", +// "password": "Master@12345" +// } diff --git a/app/Models/UserMenuGroup.php b/app/Models/UserMenuGroup.php new file mode 100644 index 0000000..c5ed719 --- /dev/null +++ b/app/Models/UserMenuGroup.php @@ -0,0 +1,31 @@ +belongsTo(User::class, 'user_id'); + } + + public function menuGroup() + { + return $this->belongsTo(MenuGroup::class, 'menu_group_id'); + } + + public function tenant() + { + return $this->belongsTo(Tenant::class, 'tenant_id'); + } +} diff --git a/app/Models/UserProfile.php b/app/Models/UserProfile.php new file mode 100644 index 0000000..02c00fc --- /dev/null +++ b/app/Models/UserProfile.php @@ -0,0 +1,25 @@ +belongsTo(User::class, 'user_id'); + } +} diff --git a/app/Models/UserTenant.php b/app/Models/UserTenant.php new file mode 100644 index 0000000..3295a68 --- /dev/null +++ b/app/Models/UserTenant.php @@ -0,0 +1,30 @@ + 'boolean', + ]; + + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } + + public function tenant() + { + return $this->belongsTo(Tenant::class, 'tenant_id'); + } +} diff --git a/app/Models/WilayahDesa.php b/app/Models/WilayahDesa.php new file mode 100644 index 0000000..1fce0a8 --- /dev/null +++ b/app/Models/WilayahDesa.php @@ -0,0 +1,18 @@ +belongsTo(WilayahKecamatan::class, 'kecamatan_id'); + } +} diff --git a/app/Models/WilayahKabupaten.php b/app/Models/WilayahKabupaten.php new file mode 100644 index 0000000..5579628 --- /dev/null +++ b/app/Models/WilayahKabupaten.php @@ -0,0 +1,24 @@ +belongsTo(WilayahProvinsi::class, 'provinsi_id'); + } + + public function kecamatan(): HasMany + { + return $this->hasMany(WilayahKecamatan::class, 'kabupaten_id'); + } +} diff --git a/app/Models/WilayahKecamatan.php b/app/Models/WilayahKecamatan.php new file mode 100644 index 0000000..73282c5 --- /dev/null +++ b/app/Models/WilayahKecamatan.php @@ -0,0 +1,24 @@ +belongsTo(WilayahKabupaten::class, 'kabupaten_id'); + } + + public function desa(): HasMany + { + return $this->hasMany(WilayahDesa::class, 'kecamatan_id'); + } +} diff --git a/app/Models/WilayahProvinsi.php b/app/Models/WilayahProvinsi.php new file mode 100644 index 0000000..0c843e9 --- /dev/null +++ b/app/Models/WilayahProvinsi.php @@ -0,0 +1,18 @@ +hasMany(WilayahKabupaten::class, 'provinsi_id'); + } +} diff --git a/app/Services/Access/AccessService.php b/app/Services/Access/AccessService.php new file mode 100644 index 0000000..b845cfa --- /dev/null +++ b/app/Services/Access/AccessService.php @@ -0,0 +1,75 @@ +isMasterAdmin() || $user->userTenants()->where('tenant_id', $requestedTenantId)->exists()) { + return $requestedTenantId; + } + + return null; + } + + return $user->userTenants() + ->orderByDesc('is_default') + ->orderBy('id') + ->value('tenant_id'); + } + + public function hasMenuPermission( + User $user, + string $menuSlug, + string $permission, + ?int $tenantId + ): bool { + if ($user->isMasterAdmin()) { + return true; + } + + $column = 'can_'.$permission; + $allowed = [ + 'can_view', + 'can_create', + 'can_update', + 'can_delete', + 'can_approve', + 'can_export', + ]; + + if (! in_array($column, $allowed, true) || ! $tenantId) { + return false; + } + + return DB::table('user_menu_groups as umg') + ->join('menu_groups as mg', 'mg.id', '=', 'umg.menu_group_id') + ->join('menu_group_menus as mgm', 'mgm.menu_group_id', '=', 'mg.id') + ->join('menu_lists as ml', 'ml.id', '=', 'mgm.menu_id') + ->where('umg.user_id', $user->id) + ->where('umg.tenant_id', $tenantId) + ->where('mg.is_active', true) + ->where('ml.is_active', true) + ->where('ml.slug', $menuSlug) + ->where("mgm.$column", true) + ->exists(); + } + + public function canManageUser(User $actor, User $target, ?int $tenantId): bool + { + if ($actor->isMasterAdmin()) { + return true; + } + + if (! $actor->isTenantOwner() || ! $tenantId || $target->isMasterAdmin()) { + return false; + } + + return $target->userTenants()->where('tenant_id', $tenantId)->exists(); + } +} diff --git a/app/Services/Auth/AuthService.php b/app/Services/Auth/AuthService.php new file mode 100644 index 0000000..8404231 --- /dev/null +++ b/app/Services/Auth/AuthService.php @@ -0,0 +1,117 @@ +generateCode(); + + $user = User::create([ + 'name' => $data['name'], + 'username' => $data['username'] ?? null, + 'email' => $data['email'], + 'whatsapp_number' => $data['whatsapp_number'], + 'password' => Hash::make($data['password']), + 'is_master' => false, + 'status' => 'inactive', + 'verification_channel' => $data['verification_channel'], + 'verification_target' => $data['verification_target'], + 'verification_code' => $code, + 'verification_code_expires_at' => now()->addMinutes(10), + 'verified_at' => null, + ]); + + return [ + 'user' => $user, + 'verification' => [ + 'channel' => $user->verification_channel, + 'target' => $user->verification_target, + 'code' => $code, + 'expires_at' => $user->verification_code_expires_at, + ], + ]; + } + + public function verifyRegistration(array $data): User + { + $user = User::query() + ->where('verification_channel', $data['verification_channel']) + ->where('verification_target', $data['verification_target']) + ->first(); + + if (!$user) { + throw ValidationException::withMessages([ + 'verification_target' => 'Target verifikasi tidak ditemukan.', + ]); + } + + if (empty($user->verification_code) || $user->verification_code !== $data['verification_code']) { + throw ValidationException::withMessages([ + 'verification_code' => 'Kode verifikasi tidak valid.', + ]); + } + + if ( + empty($user->verification_code_expires_at) || + now()->greaterThan($user->verification_code_expires_at) + ) { + throw ValidationException::withMessages([ + 'verification_code' => 'Kode verifikasi sudah kadaluarsa.', + ]); + } + + $user->update([ + 'status' => 'active', + 'verified_at' => now(), + 'verification_code' => null, + 'verification_code_expires_at' => null, + ]); + + return $user->fresh(); + } + + public function resendVerificationCode(array $data): array + { + $user = User::query() + ->where('verification_channel', $data['verification_channel']) + ->where('verification_target', $data['verification_target']) + ->first(); + + if (!$user) { + throw ValidationException::withMessages([ + 'verification_target' => 'Target verifikasi tidak ditemukan.', + ]); + } + + if ($user->status === 'active') { + throw ValidationException::withMessages([ + 'user' => 'User sudah aktif dan tidak memerlukan verifikasi ulang.', + ]); + } + + $code = $this->generateCode(); + + $user->update([ + 'verification_code' => $code, + 'verification_code_expires_at' => now()->addMinutes(10), + ]); + + return [ + 'channel' => $user->verification_channel, + 'target' => $user->verification_target, + 'code' => $code, + 'expires_at' => $user->verification_code_expires_at, + ]; + } + + protected function generateCode(): string + { + return str_pad((string) random_int(0, 999999), 6, '0', STR_PAD_LEFT); + } +} diff --git a/app/Services/File/StoredFileService.php b/app/Services/File/StoredFileService.php new file mode 100644 index 0000000..dab6351 --- /dev/null +++ b/app/Services/File/StoredFileService.php @@ -0,0 +1,117 @@ +when(! $user->isMasterAdmin(), function ($query) use ($user, $tenantId) { + $query->where(function ($scope) use ($user, $tenantId) { + $scope->where('uploaded_by', $user->id) + ->when($tenantId, fn ($tenantScope) => $tenantScope->orWhere('tenant_id', $tenantId)); + }); + }) + ->when($filters['search'] ?? null, function ($query, $search) { + $query->where('original_name', 'like', "%{$search}%"); + }) + ->when($filters['category'] ?? null, fn ($query, $category) => $query->where('category', $category)) + ->when($filters['mime_type'] ?? null, fn ($query, $mimeType) => $query->where('mime_type', $mimeType)) + ->latest('id') + ->paginate($filters['per_page'] ?? 10); + } + + public function store(UploadedFile $upload, User $user, ?int $tenantId, string $category): StoredFile + { + $uuid = (string) Str::uuid(); + $extension = strtolower($upload->getClientOriginalExtension() ?: $upload->extension() ?: 'bin'); + $scope = $tenantId ? "tenants/{$tenantId}" : 'global'; + $objectKey = "{$scope}/{$category}/".now()->format('Y/m')."/{$uuid}.{$extension}"; + $disk = Storage::disk('minio'); + + $disk->putFileAs(dirname($objectKey), $upload, basename($objectKey), [ + 'visibility' => 'private', + 'ContentType' => $upload->getMimeType() ?: 'application/octet-stream', + ]); + + try { + return DB::transaction(fn () => StoredFile::create([ + 'uuid' => $uuid, + 'tenant_id' => $tenantId, + 'uploaded_by' => $user->id, + 'disk' => 'minio', + 'bucket' => (string) config('filesystems.disks.minio.bucket'), + 'object_key' => $objectKey, + 'original_name' => $upload->getClientOriginalName(), + 'extension' => $extension, + 'mime_type' => $upload->getMimeType(), + 'size' => $upload->getSize(), + 'category' => $category, + 'visibility' => 'private', + ])); + } catch (\Throwable $exception) { + $disk->delete($objectKey); + throw $exception; + } + } + + public function temporaryUrl(StoredFile $file, User $user): array + { + $this->authorizeAccess($file, $user); + $expiresAt = now()->addMinutes(10); + + return [ + 'url' => Storage::disk($file->disk)->temporaryUrl( + $file->object_key, + $expiresAt, + ['ResponseContentDisposition' => 'inline; filename="'.addslashes($file->original_name).'"'] + ), + 'expires_at' => $expiresAt->toIso8601String(), + ]; + } + + public function delete(StoredFile $file, User $user): void + { + if (! $user->isMasterAdmin() && $file->uploaded_by !== $user->id) { + abort(403, 'File hanya dapat dihapus oleh pengunggah atau Master Admin.'); + } + + Storage::disk($file->disk)->delete($file->object_key); + $file->delete(); + } + + public function authorizeAccess(StoredFile $file, User $user): void + { + if ($user->isMasterAdmin() || $file->uploaded_by === $user->id) { + return; + } + + $canAccessTenant = $file->tenant_id && $user->userTenants() + ->where('tenant_id', $file->tenant_id) + ->exists(); + + abort_unless($canAccessTenant, 403, 'Anda tidak memiliki akses ke file ini.'); + } + + public function validateProfilePhoto(StoredFile $file, User $user): void + { + if ($file->uploaded_by !== $user->id + || $file->category !== 'profile' + || ! str_starts_with((string) $file->mime_type, 'image/') + || $file->size > 5 * 1024 * 1024) { + throw ValidationException::withMessages([ + 'profile_photo_file_id' => 'Foto profil harus berupa file gambar kategori profile yang Anda unggah sendiri.', + ]); + } + } +} diff --git a/app/Services/Material/MaterialService.php b/app/Services/Material/MaterialService.php index 4fc2725..390db27 100644 --- a/app/Services/Material/MaterialService.php +++ b/app/Services/Material/MaterialService.php @@ -2,10 +2,9 @@ namespace App\Services\Material; -use App\Models\TechnicianMaterial; -use App\Models\TechnicianConsumable; use App\Models\MaterialMovement; -use Carbon\Carbon; +use App\Models\TechnicianConsumable; +use App\Models\TechnicianMaterial; use DB; class MaterialService @@ -52,11 +51,36 @@ class MaterialService DB::raw("'consumable' as type"), ]); - if (!empty($filters['user_id'])) { + if (! empty($filters['user_id'])) { $serialized->where('user_id', $filters['user_id']); $consumable->where('user_id', $filters['user_id']); } + if (! empty($filters['search'])) { + $search = $filters['search']; + $serialized->where(function ($query) use ($search) { + $query->where('barcode_id', 'ILIKE', "%$search%") + ->orWhere('material_name', 'ILIKE', "%$search%"); + }); + $consumable->where(function ($query) use ($search) { + $query->where('barcode_id', 'ILIKE', "%$search%") + ->orWhere('material_name', 'ILIKE', "%$search%"); + }); + } + + if (! empty($filters['status'])) { + $serialized->where('status', $filters['status']); + $consumable->where('status', $filters['status']); + } + + if (($filters['type'] ?? null) === 'serialized') { + $consumable->whereRaw('1 = 0'); + } + + if (($filters['type'] ?? null) === 'consumable') { + $serialized->whereRaw('1 = 0'); + } + $query = $serialized->unionAll($consumable); return DB::query() @@ -64,6 +88,7 @@ class MaterialService ->orderByDesc('created_at') ->paginate($filters['per_page'] ?? 15); } + /* |-------------------------------------------------------------------------- | ASSIGN MATERIAL (AUTO DETECT TYPE) @@ -73,7 +98,7 @@ class MaterialService { return DB::transaction(function () use ($data) { - $type = $data['type']; + $type = $data['type']; // type dari API Gudang: serialized / consumable /* @@ -154,7 +179,7 @@ class MaterialService if ($serialized) { $serialized->update([ - 'user_id' => $data['to_user_id'] + 'user_id' => $data['to_user_id'], ]); MaterialMovement::create([ @@ -173,7 +198,7 @@ class MaterialService $consumable = TechnicianConsumable::where('barcode_id', $barcode)->first(); $consumable->update([ - 'user_id' => $data['to_user_id'] + 'user_id' => $data['to_user_id'], ]); MaterialMovement::create([ @@ -207,7 +232,7 @@ class MaterialService $serialized->update([ 'user_id' => null, - 'status' => 'returned' + 'status' => 'returned', ]); MaterialMovement::create([ @@ -226,7 +251,7 @@ class MaterialService $consumable->update([ 'user_id' => null, - 'status' => 'returned' + 'status' => 'returned', ]); MaterialMovement::create([ @@ -249,6 +274,7 @@ class MaterialService ->get() ->map(function ($item) { $item->type = 'serialized'; + return $item; }); @@ -256,6 +282,7 @@ class MaterialService ->get() ->map(function ($item) { $item->type = 'consumable'; + return $item; }); @@ -272,15 +299,15 @@ class MaterialService ->map(function ($item) { return [ - 'barcode_id' => $item->barcode_id, - 'type' => $item->movement_type, - 'from_user_id' => $item->from_user_id, - 'to_user_id' => $item->to_user_id, - 'ticket_id' => $item->ticket_id, - 'qty' => $item->qty, - 'description' => $item->description, - 'created_at' => $item->created_at, + 'barcode_id' => $item->barcode_id, + 'type' => $item->movement_type, + 'from_user_id' => $item->from_user_id, + 'to_user_id' => $item->to_user_id, + 'ticket_id' => $item->ticket_id, + 'qty' => $item->qty, + 'description' => $item->description, + 'created_at' => $item->created_at, ]; }); } -} \ No newline at end of file +} diff --git a/app/Services/Menu/MenuGroupService.php b/app/Services/Menu/MenuGroupService.php new file mode 100644 index 0000000..778528c --- /dev/null +++ b/app/Services/Menu/MenuGroupService.php @@ -0,0 +1,392 @@ +with('tenant') + ->when( + $actor && ! $actor->isMasterAdmin(), + fn ($q) => $q->where('tenant_id', $tenantId) + ) + ->when( + ! empty($filters['search']), + fn ($q) => $q->where(function ($x) use ($filters) { + $x->where('name', 'ILIKE', "%{$filters['search']}%") + ->orWhere('description', 'ILIKE', "%{$filters['search']}%"); + }) + ) + ->when( + array_key_exists('is_active', $filters) && $filters['is_active'] !== null && $filters['is_active'] !== '', + fn ($q) => $q->where('is_active', filter_var($filters['is_active'], FILTER_VALIDATE_BOOLEAN)) + ) + ->orderBy( + $filters['sort_by'] ?? 'id', + $filters['sort_direction'] ?? 'desc' + ) + ->paginate($filters['per_page'] ?? 10); + } + + public function create(array $data, User $actor, ?int $tenantId): MenuGroup + { + if (! array_key_exists('is_active', $data) || $data['is_active'] === null) { + $data['is_active'] = true; + } + + $scopeTenantId = $actor->isMasterAdmin() + ? ($data['tenant_id'] ?? null) + : $tenantId; + + if (! $actor->isMasterAdmin() && ! $scopeTenantId) { + throw ValidationException::withMessages([ + 'tenant_id' => 'Tenant aktif wajib tersedia untuk membuat Menu Group.', + ]); + } + + $data['tenant_id'] = $scopeTenantId; + $data['is_system'] = $actor->isMasterAdmin() && ! $scopeTenantId; + + return MenuGroup::create($data)->load('tenant'); + } + + public function update( + MenuGroup $menuGroup, + array $data, + User $actor, + ?int $tenantId + ): MenuGroup { + if (array_key_exists('is_active', $data) && $data['is_active'] === null) { + unset($data['is_active']); + } + + if ($actor->isMasterAdmin()) { + if (array_key_exists('tenant_id', $data)) { + $data['is_system'] = ! $data['tenant_id']; + } + } else { + unset($data['tenant_id'], $data['is_system']); + if ((int) $menuGroup->tenant_id !== (int) $tenantId) { + abort(403); + } + } + + $menuGroup->update($data); + + return $menuGroup->fresh('tenant'); + } + + public function delete(MenuGroup $menuGroup): bool + { + $hasMenuMappings = $menuGroup->menus()->exists(); + $hasUserMappings = $menuGroup->userMenuGroups()->exists(); + + if ($hasMenuMappings || $hasUserMappings) { + throw ValidationException::withMessages([ + 'menu_group' => 'Menu Group tidak dapat dihapus karena masih memiliki relasi menu/user.', + ]); + } + + return $menuGroup->delete(); + } + + public function listMenus(MenuGroup $menuGroup): array + { + $rows = DB::table('menu_lists as m') + ->leftJoin('menu_group_menus as mgm', function ($join) use ($menuGroup) { + $join->on('mgm.menu_id', '=', 'm.id') + ->where('mgm.menu_group_id', '=', $menuGroup->id); + }) + ->where('m.is_active', true) + ->select( + 'm.id', + 'm.parent_id', + 'm.name', + 'm.slug', + 'm.url', + 'm.icon', + 'm.component', + 'm.sort_order', + DB::raw('COALESCE(mgm.can_view, false) as can_view'), + DB::raw('COALESCE(mgm.can_create, false) as can_create'), + DB::raw('COALESCE(mgm.can_update, false) as can_update'), + DB::raw('COALESCE(mgm.can_delete, false) as can_delete'), + DB::raw('COALESCE(mgm.can_approve, false) as can_approve'), + DB::raw('COALESCE(mgm.can_export, false) as can_export') + ) + ->orderBy('m.sort_order') + ->orderBy('m.id') + ->get(); + + return $rows->map(function ($row) { + return [ + 'menu_id' => (int) $row->id, + 'parent_id' => $row->parent_id ? (int) $row->parent_id : null, + 'name' => $row->name, + 'slug' => $row->slug, + 'url' => $row->url, + 'icon' => $row->icon, + 'component' => $row->component, + 'sort_order' => (int) $row->sort_order, + 'permissions' => [ + 'can_view' => (bool) $row->can_view, + 'can_create' => (bool) $row->can_create, + 'can_update' => (bool) $row->can_update, + 'can_delete' => (bool) $row->can_delete, + 'can_approve' => (bool) $row->can_approve, + 'can_export' => (bool) $row->can_export, + ], + 'available_permissions' => [ + 'can_view' => true, + 'can_create' => true, + 'can_update' => true, + 'can_delete' => true, + 'can_approve' => true, + 'can_export' => true, + ], + ]; + })->all(); + } + + public function syncMenus(MenuGroup $menuGroup, array $menus, ?User $actor = null): void + { + if ($actor && ! $actor->isMasterAdmin()) { + $allowed = $this->getAllowedMenuPermissionsForUser( + $actor, + request()->attributes->get('tenant_id') + ); + + foreach ($menus as $menu) { + $actorPermissions = $allowed[(int) $menu['menu_id']] ?? null; + + if (! $actorPermissions) { + throw ValidationException::withMessages([ + 'menus' => 'Terdapat menu di luar hak akses user login.', + ]); + } + + foreach (['view', 'create', 'update', 'delete', 'approve', 'export'] as $permission) { + if (($menu["can_$permission"] ?? false) && ! $actorPermissions["can_$permission"]) { + throw ValidationException::withMessages([ + 'menus' => "Permission can_$permission tidak boleh melebihi akses Anda.", + ]); + } + } + } + } + + $syncData = []; + + foreach ($menus as $menu) { + $syncData[$menu['menu_id']] = [ + 'can_view' => (bool) ($menu['can_view'] ?? false), + 'can_create' => (bool) ($menu['can_create'] ?? false), + 'can_update' => (bool) ($menu['can_update'] ?? false), + 'can_delete' => (bool) ($menu['can_delete'] ?? false), + 'can_approve' => (bool) ($menu['can_approve'] ?? false), + 'can_export' => (bool) ($menu['can_export'] ?? false), + 'updated_at' => now(), + 'created_at' => now(), + ]; + } + + $menuGroup->menus()->sync($syncData); + } + + public function listAvailableMenusForGroup(MenuGroup $menuGroup, User $actor): array + { + if ($actor->isMasterAdmin()) { + return $this->listMenus($menuGroup); + } + + $allowedPermissions = $this->getAllowedMenuPermissionsForUser( + $actor, + request()->attributes->get('tenant_id') + ); + $allowedMenuIds = array_keys($allowedPermissions); + + $rows = DB::table('menu_lists as m') + ->leftJoin('menu_group_menus as mgm', function ($join) use ($menuGroup) { + $join->on('mgm.menu_id', '=', 'm.id') + ->where('mgm.menu_group_id', '=', $menuGroup->id); + }) + ->where('m.is_active', true) + ->whereIn('m.id', $allowedMenuIds) + ->select( + 'm.id', + 'm.parent_id', + 'm.name', + 'm.sort_order', + DB::raw('COALESCE(mgm.can_view, false) as can_view'), + DB::raw('COALESCE(mgm.can_create, false) as can_create'), + DB::raw('COALESCE(mgm.can_update, false) as can_update'), + DB::raw('COALESCE(mgm.can_delete, false) as can_delete'), + DB::raw('COALESCE(mgm.can_approve, false) as can_approve'), + DB::raw('COALESCE(mgm.can_export, false) as can_export') + ) + ->orderBy('m.sort_order') + ->orderBy('m.id') + ->get(); + + return $rows->map(function ($row) use ($allowedPermissions) { + $available = $allowedPermissions[(int) $row->id]; + + return [ + 'menu_id' => (int) $row->id, + 'parent_id' => $row->parent_id ? (int) $row->parent_id : null, + 'name' => $row->name, + 'sort_order' => (int) $row->sort_order, + 'permissions' => [ + 'can_view' => (bool) $row->can_view, + 'can_create' => (bool) $row->can_create, + 'can_update' => (bool) $row->can_update, + 'can_delete' => (bool) $row->can_delete, + 'can_approve' => (bool) $row->can_approve, + 'can_export' => (bool) $row->can_export, + ], + 'available_permissions' => $available, + ]; + })->all(); + } + + public function getAllowedMenuIdsForUser(User $user, ?int $tenantId = null): array + { + if ($user->isMasterAdmin()) { + return DB::table('menu_lists') + ->where('is_active', true) + ->pluck('id') + ->map(fn ($id) => (int) $id) + ->all(); + } + + return DB::table('menu_group_menus as mgm') + ->join('user_menu_groups as umg', 'umg.menu_group_id', '=', 'mgm.menu_group_id') + ->join('menu_lists as m', 'm.id', '=', 'mgm.menu_id') + ->where('umg.user_id', $user->id) + ->where('umg.tenant_id', $tenantId) + ->where('m.is_active', true) + ->where(function ($q) { + $q->where('mgm.can_view', true) + ->orWhere('mgm.can_create', true) + ->orWhere('mgm.can_update', true) + ->orWhere('mgm.can_delete', true) + ->orWhere('mgm.can_approve', true) + ->orWhere('mgm.can_export', true); + }) + ->distinct() + ->pluck('mgm.menu_id') + ->map(fn ($id) => (int) $id) + ->all(); + } + + public function getAllowedMenuPermissionsForUser(User $user, ?int $tenantId): array + { + return DB::table('menu_group_menus as mgm') + ->join('user_menu_groups as umg', 'umg.menu_group_id', '=', 'mgm.menu_group_id') + ->join('menu_groups as mg', 'mg.id', '=', 'mgm.menu_group_id') + ->join('menu_lists as m', 'm.id', '=', 'mgm.menu_id') + ->where('umg.user_id', $user->id) + ->where('umg.tenant_id', $tenantId) + ->where('mg.is_active', true) + ->where('m.is_active', true) + ->groupBy('mgm.menu_id') + ->select( + 'mgm.menu_id', + DB::raw('MAX(CASE WHEN mgm.can_view THEN 1 ELSE 0 END) as can_view'), + DB::raw('MAX(CASE WHEN mgm.can_create THEN 1 ELSE 0 END) as can_create'), + DB::raw('MAX(CASE WHEN mgm.can_update THEN 1 ELSE 0 END) as can_update'), + DB::raw('MAX(CASE WHEN mgm.can_delete THEN 1 ELSE 0 END) as can_delete'), + DB::raw('MAX(CASE WHEN mgm.can_approve THEN 1 ELSE 0 END) as can_approve'), + DB::raw('MAX(CASE WHEN mgm.can_export THEN 1 ELSE 0 END) as can_export') + ) + ->get() + ->mapWithKeys(fn ($row) => [(int) $row->menu_id => [ + 'can_view' => (bool) $row->can_view, + 'can_create' => (bool) $row->can_create, + 'can_update' => (bool) $row->can_update, + 'can_delete' => (bool) $row->can_delete, + 'can_approve' => (bool) $row->can_approve, + 'can_export' => (bool) $row->can_export, + ]]) + ->all(); + } + + public function listUserAssignments(User $user, ?int $tenantId = null): array + { + return DB::table('user_menu_groups as umg') + ->join('menu_groups as mg', 'mg.id', '=', 'umg.menu_group_id') + ->leftJoin('tenants as t', 't.id', '=', 'umg.tenant_id') + ->where('umg.user_id', $user->id) + ->when($tenantId, fn ($q) => $q->where('umg.tenant_id', $tenantId)) + ->select( + 'umg.menu_group_id', + 'mg.name as menu_group_name', + 'mg.is_active as menu_group_is_active', + 'umg.tenant_id', + 't.tenant_name as tenant_name', + 'umg.created_at', + 'umg.updated_at' + ) + ->orderBy('umg.menu_group_id') + ->orderBy('umg.tenant_id') + ->get() + ->map(function ($row) { + return [ + 'menu_group_id' => (int) $row->menu_group_id, + 'menu_group_name' => $row->menu_group_name, + 'menu_group_is_active' => (bool) $row->menu_group_is_active, + 'tenant_id' => $row->tenant_id ? (int) $row->tenant_id : null, + 'tenant_name' => $row->tenant_name, + 'created_at' => $row->created_at, + 'updated_at' => $row->updated_at, + ]; + }) + ->all(); + } + + public function syncUserAssignments(User $user, array $assignments, ?int $tenantId = null): void + { + $rows = []; + + foreach ($assignments as $assignment) { + if ($tenantId && (int) ($assignment['tenant_id'] ?? 0) !== $tenantId) { + throw ValidationException::withMessages([ + 'assignments' => 'Assignment hanya boleh dibuat pada tenant aktif.', + ]); + } + + $group = MenuGroup::find($assignment['menu_group_id']); + if ($tenantId && $group && (int) $group->tenant_id !== $tenantId) { + throw ValidationException::withMessages([ + 'assignments' => 'Menu group bukan milik tenant aktif.', + ]); + } + + $rows[] = [ + 'user_id' => $user->id, + 'menu_group_id' => (int) $assignment['menu_group_id'], + 'tenant_id' => $assignment['tenant_id'] ?? null, + 'created_at' => now(), + 'updated_at' => now(), + ]; + } + + DB::transaction(function () use ($user, $rows) { + DB::table('user_menu_groups') + ->where('user_id', $user->id) + ->when($tenantId, fn ($q) => $q->where('tenant_id', $tenantId)) + ->delete(); + + if (! empty($rows)) { + DB::table('user_menu_groups')->insert($rows); + } + }); + } +} diff --git a/app/Services/Menu/MenuListService.php b/app/Services/Menu/MenuListService.php new file mode 100644 index 0000000..354e635 --- /dev/null +++ b/app/Services/Menu/MenuListService.php @@ -0,0 +1,47 @@ +when( + !empty($filters['search']), + fn($q) => $q->where(function ($x) use ($filters) { + $x->where('name', 'ILIKE', "%{$filters['search']}%") + ->orWhere('slug', 'ILIKE', "%{$filters['search']}%") + ->orWhere('url', 'ILIKE', "%{$filters['search']}%"); + }) + ) + ->when( + isset($filters['is_active']) && $filters['is_active'] !== '', + fn($q) => $q->where( + 'is_active', + filter_var($filters['is_active'], FILTER_VALIDATE_BOOLEAN) + ) + ) + ->latest('id') + ->paginate($filters['per_page'] ?? 10); + } + + public function create(array $data): MenuList + { + return MenuList::create($data); + } + + public function update(MenuList $menuList, array $data): MenuList + { + $menuList->update($data); + + return $menuList->fresh(); + } + + public function delete(MenuList $menuList): bool + { + return $menuList->delete(); + } +} diff --git a/app/Services/Nas/NasResourceService.php b/app/Services/Nas/NasResourceService.php new file mode 100644 index 0000000..c0c420e --- /dev/null +++ b/app/Services/Nas/NasResourceService.php @@ -0,0 +1,103 @@ + NasMikrotik::class, + 'package-profile' => NasPackageProfile::class, + 'olt' => NasOlt::class, + 'webfig' => NasWebfigDevice::class, + ]; + + private const SECRET_FIELDS = [ + 'mikrotik' => ['api_password', 'radius_secret'], + 'olt' => ['community', 'auth_password', 'privacy_password'], + 'webfig' => ['password'], + 'package-profile' => [], + ]; + + public function list(string $resource, array $filters, User $actor, ?int $tenantId) + { + $model = $this->model($resource); + + return $model::query() + ->with('tenant:id,tenant_code,tenant_name') + ->when(! $actor->isMasterAdmin(), fn ($q) => $q->where('tenant_id', $tenantId)) + ->when($actor->isMasterAdmin() && $tenantId, fn ($q) => $q->where('tenant_id', $tenantId)) + ->when(! empty($filters['status']), fn ($q) => $q->where('status', $filters['status'])) + ->when(! empty($filters['connection_type']) && $resource === 'mikrotik', fn ($q) => $q->where('connection_type', $filters['connection_type'])) + ->when(! empty($filters['service_type']) && $resource === 'package-profile', fn ($q) => $q->where('service_type', $filters['service_type'])) + ->when(! empty($filters['search']), function ($q) use ($filters, $resource) { + $columns = match ($resource) { + 'mikrotik' => ['name', 'host', 'api_username'], + 'package-profile' => ['name', 'external_profile_name'], + 'olt' => ['name', 'host', 'vendor', 'model'], + 'webfig' => ['name', 'url', 'device_type'], + }; + $q->where(function ($search) use ($columns, $filters) { + foreach ($columns as $index => $column) { + $method = $index === 0 ? 'where' : 'orWhere'; + $search->{$method}($column, 'ILIKE', "%{$filters['search']}%"); + } + }); + }) + ->orderBy($filters['sort_by'] ?? 'id', $filters['sort_direction'] ?? 'desc') + ->paginate($filters['per_page'] ?? 10); + } + + public function find(string $resource, int $id): Model + { + $model = $this->model($resource); + + return $model::with('tenant:id,tenant_code,tenant_name')->findOrFail($id); + } + + public function create(string $resource, array $data, User $actor, ?int $tenantId): Model + { + $data['tenant_id'] = $actor->isMasterAdmin() ? ($data['tenant_id'] ?? $tenantId) : $tenantId; + if (! $data['tenant_id']) { + throw ValidationException::withMessages(['tenant_id' => 'Tenant wajib dipilih.']); + } + $data['status'] ??= 'active'; + $model = $this->model($resource); + + return $model::create($data)->load('tenant:id,tenant_code,tenant_name'); + } + + public function update(string $resource, Model $model, array $data, User $actor): Model + { + if (! $actor->isMasterAdmin()) { + unset($data['tenant_id']); + } + foreach (self::SECRET_FIELDS[$resource] as $field) { + if (array_key_exists($field, $data) && blank($data[$field])) { + unset($data[$field]); + } + } + $model->update($data); + + return $model->fresh()->load('tenant:id,tenant_code,tenant_name'); + } + + public function delete(Model $model): bool + { + return $model->delete(); + } + + private function model(string $resource): string + { + abort_unless(isset(self::MODELS[$resource]), 404); + + return self::MODELS[$resource]; + } +} diff --git a/app/Services/Tenant/TenantService.php b/app/Services/Tenant/TenantService.php new file mode 100644 index 0000000..bf8246f --- /dev/null +++ b/app/Services/Tenant/TenantService.php @@ -0,0 +1,59 @@ +when( + $actor && ! $actor->isMasterAdmin(), + fn ($q) => $q->whereIn( + 'id', + $actor->userTenants()->select('tenant_id') + ) + ) + ->when( + ! empty($filters['search']), + fn ($q) => $q->where(function ($x) use ($filters) { + $x->where('tenant_code', 'ILIKE', "%{$filters['search']}%") + ->orWhere('tenant_name', 'ILIKE', "%{$filters['search']}%") + ->orWhere('email', 'ILIKE', "%{$filters['search']}%"); + }) + ) + ->when( + ! empty($filters['status']), + fn ($q) => $q->where('status', $filters['status']) + ) + ->orderBy( + $filters['sort_by'] ?? 'id', + $filters['sort_direction'] ?? 'desc' + ) + ->paginate($filters['per_page'] ?? 10); + } + + public function create(array $data): Tenant + { + if (! array_key_exists('status', $data) || empty($data['status'])) { + $data['status'] = 'active'; + } + + return Tenant::create($data); + } + + public function update(Tenant $tenant, array $data): Tenant + { + $tenant->update($data); + + return $tenant->fresh(); + } + + public function delete(Tenant $tenant): bool + { + return $tenant->delete(); + } +} diff --git a/app/Services/Ticket/TicketIncidentTypeService.php b/app/Services/Ticket/TicketIncidentTypeService.php index e637fda..941be28 100644 --- a/app/Services/Ticket/TicketIncidentTypeService.php +++ b/app/Services/Ticket/TicketIncidentTypeService.php @@ -6,16 +6,29 @@ use App\Models\TicketIncidentType; class TicketIncidentTypeService { - public function getAll() + public function list(array $filters = []) { - return TicketIncidentType::orderBy('name')->get(); + return TicketIncidentType::query() + ->when( + ! empty($filters['search']), + fn ($query) => $query->where('name', 'ILIKE', "%{$filters['search']}%") + ) + ->when( + array_key_exists('is_active', $filters) && $filters['is_active'] !== '', + fn ($query) => $query->where( + 'is_active', + filter_var($filters['is_active'], FILTER_VALIDATE_BOOLEAN) + ) + ) + ->orderBy('name') + ->paginate($filters['per_page'] ?? 10); } public function create(array $data): TicketIncidentType { return TicketIncidentType::create([ 'name' => $data['name'], - 'is_active' => $data['is_active'] ?? true + 'is_active' => $data['is_active'] ?? true, ]); } @@ -25,7 +38,7 @@ class TicketIncidentTypeService ): TicketIncidentType { $ticketIncidentType->update([ 'name' => $data['name'], - 'is_active' => $data['is_active'] + 'is_active' => $data['is_active'], ]); return $ticketIncidentType->fresh(); @@ -35,4 +48,4 @@ class TicketIncidentTypeService { $ticketIncidentType->delete(); } -} \ No newline at end of file +} diff --git a/app/Services/User/UserService.php b/app/Services/User/UserService.php new file mode 100644 index 0000000..91685f1 --- /dev/null +++ b/app/Services/User/UserService.php @@ -0,0 +1,281 @@ +with(['profilePhoto', 'userProfile', 'userTenants.tenant', 'userMenuGroups.menuGroup', 'userMenuGroups.tenant']) + ->when( + ! empty($filters['search']), + fn ($q) => $q->where(function ($x) use ($filters) { + $x->where('name', 'ILIKE', "%{$filters['search']}%") + ->orWhere('username', 'ILIKE', "%{$filters['search']}%") + ->orWhere('email', 'ILIKE', "%{$filters['search']}%") + ->orWhere('whatsapp_number', 'ILIKE', "%{$filters['search']}%"); + }) + ) + ->when( + isset($filters['status']) && $filters['status'] !== '', + fn ($q) => $q->where('status', $filters['status']) + ) + ->when( + ! empty($filters['access_level']), + fn ($q) => $q->where('access_level', $filters['access_level']) + ) + ->when( + $actor && ! $actor->isMasterAdmin(), + fn ($q) => $q->whereHas( + 'userTenants', + fn ($tenantQuery) => $tenantQuery->where('tenant_id', $tenantId) + )->where('is_master', false) + ) + ->orderBy( + $filters['sort_by'] ?? 'id', + $filters['sort_direction'] ?? 'desc' + ) + ->paginate($filters['per_page'] ?? 10); + } + + public function create(array $payload, ?User $actor = null): User + { + $this->guardPrivilegeChange($payload, $actor); + $this->guardTenantPayload($payload, $actor); + + if ($actor && ! $actor->isMasterAdmin() && empty($payload['user_tenants'])) { + throw ValidationException::withMessages([ + 'user_tenants' => 'Tenant Owner wajib membuat user pada tenant aktif.', + ]); + } + + return DB::transaction(function () use ($payload) { + $baseData = Arr::only($payload, [ + 'name', + 'username', + 'email', + 'whatsapp_number', + 'profile_photo_file_id', + 'password', + 'is_master', + 'access_level', + 'status', + ]); + + $user = User::create($baseData); + + $this->syncProfile($user, $payload['user_profile'] ?? null); + $this->syncTenants($user, $payload['user_tenants'] ?? []); + $this->syncMenuGroups($user, $payload['user_menu_groups'] ?? []); + + return $user->fresh([ + 'userProfile', + 'profilePhoto', + 'userTenants.tenant', + 'userMenuGroups.menuGroup', + 'userMenuGroups.tenant', + ]); + }); + } + + public function show(User $user): User + { + return $user->load([ + 'userProfile', + 'profilePhoto', + 'userTenants.tenant', + 'userMenuGroups.menuGroup', + 'userMenuGroups.tenant', + ]); + } + + public function update(User $user, array $payload, ?User $actor = null): User + { + $this->guardPrivilegeChange($payload, $actor, $user); + $this->guardTenantPayload($payload, $actor); + + return DB::transaction(function () use ($user, $payload) { + $baseData = Arr::only($payload, [ + 'name', + 'username', + 'email', + 'whatsapp_number', + 'profile_photo_file_id', + 'is_master', + 'access_level', + 'status', + ]); + + if (! empty($payload['password'])) { + $baseData['password'] = $payload['password']; + } + + if (! empty($baseData)) { + $user->update($baseData); + } + + if (array_key_exists('user_profile', $payload)) { + $this->syncProfile($user, $payload['user_profile']); + } + + if (array_key_exists('user_tenants', $payload)) { + $this->syncTenants($user, $payload['user_tenants'] ?? []); + } + + if (array_key_exists('user_menu_groups', $payload)) { + $this->syncMenuGroups($user, $payload['user_menu_groups'] ?? []); + } + + return $user->fresh([ + 'userProfile', + 'profilePhoto', + 'userTenants.tenant', + 'userMenuGroups.menuGroup', + 'userMenuGroups.tenant', + ]); + }); + } + + public function delete(User $user): void + { + DB::transaction(function () use ($user) { + $user->userProfile()->delete(); + $user->userTenants()->delete(); + $user->userMenuGroups()->delete(); + $user->delete(); + }); + } + + protected function syncProfile(User $user, ?array $profile): void + { + if ($profile === null) { + return; + } + + $data = Arr::only($profile, [ + 'nik', + 'address', + 'provinsi_id', + 'kabupaten_id', + 'kecamatan_id', + 'desa_id', + ]); + + $user->userProfile()->updateOrCreate( + ['user_id' => $user->id], + $data + ); + } + + protected function syncTenants(User $user, array $rows): void + { + $normalized = collect($rows) + ->map(fn ($row) => [ + 'tenant_id' => $row['tenant_id'] ?? null, + 'is_default' => (bool) ($row['is_default'] ?? false), + ]) + ->filter(fn ($row) => ! empty($row['tenant_id'])) + ->unique('tenant_id') + ->values() + ->all(); + + $defaultSet = false; + foreach ($normalized as $index => $row) { + if ($row['is_default'] && ! $defaultSet) { + $defaultSet = true; + } else { + $normalized[$index]['is_default'] = false; + } + } + + if (! $defaultSet && count($normalized) > 0) { + $normalized[0]['is_default'] = true; + } + + $user->userTenants()->delete(); + + if (! empty($normalized)) { + $user->userTenants()->createMany($normalized); + } + } + + protected function syncMenuGroups(User $user, array $rows): void + { + $normalized = collect($rows) + ->map(fn ($row) => [ + 'menu_group_id' => $row['menu_group_id'] ?? null, + 'tenant_id' => $row['tenant_id'] ?? null, + ]) + ->filter(fn ($row) => ! empty($row['menu_group_id'])) + ->unique(fn ($row) => ($row['menu_group_id'] ?? '').'|'.($row['tenant_id'] ?? '')) + ->values() + ->all(); + + $user->userMenuGroups()->delete(); + + if (! empty($normalized)) { + $user->userMenuGroups()->createMany($normalized); + } + } + + protected function guardPrivilegeChange(array &$payload, ?User $actor, ?User $target = null): void + { + if (! $actor) { + unset($payload['is_master'], $payload['access_level']); + + return; + } + + if ($actor->isMasterAdmin()) { + if (($payload['access_level'] ?? null) === UserAccessLevel::MASTER_ADMIN->value) { + $payload['is_master'] = true; + } elseif (array_key_exists('access_level', $payload)) { + $payload['is_master'] = false; + } + + return; + } + + if ($target?->isMasterAdmin()) { + throw ValidationException::withMessages([ + 'user' => 'Master Admin hanya dapat diubah oleh Master Admin.', + ]); + } + + if (($payload['is_master'] ?? false) + || ($payload['access_level'] ?? null) === UserAccessLevel::MASTER_ADMIN->value + || ($payload['access_level'] ?? null) === UserAccessLevel::TENANT_OWNER->value) { + throw ValidationException::withMessages([ + 'access_level' => 'Anda tidak dapat memberikan level akses ini.', + ]); + } + + unset($payload['is_master']); + } + + protected function guardTenantPayload(array $payload, ?User $actor): void + { + if (! $actor || $actor->isMasterAdmin()) { + return; + } + + $tenantId = request()->attributes->get('tenant_id'); + + foreach (['user_tenants', 'user_menu_groups'] as $key) { + foreach ($payload[$key] ?? [] as $row) { + if ((int) ($row['tenant_id'] ?? 0) !== (int) $tenantId) { + throw ValidationException::withMessages([ + $key => 'Assignment hanya boleh dibuat untuk tenant yang sedang aktif.', + ]); + } + } + } + } +} diff --git a/app/Services/Wilayah/WilayahService.php b/app/Services/Wilayah/WilayahService.php new file mode 100644 index 0000000..f21aeef --- /dev/null +++ b/app/Services/Wilayah/WilayahService.php @@ -0,0 +1,203 @@ + WilayahProvinsi::class, + 'kabupaten' => WilayahKabupaten::class, + 'kecamatan' => WilayahKecamatan::class, + 'desa' => WilayahDesa::class, + ]; + + private const PARENTS = [ + 'kabupaten' => 'provinsi_id', + 'kecamatan' => 'kabupaten_id', + 'desa' => 'kecamatan_id', + ]; + + private const RELATIONS = [ + 'provinsi' => [], + 'kabupaten' => ['provinsi'], + 'kecamatan' => ['kabupaten.provinsi'], + 'desa' => ['kecamatan.kabupaten.provinsi'], + ]; + + public function list(string $tingkat, array $filters = []) + { + $model = $this->model($tingkat); + + return $model::query() + ->with(self::RELATIONS[$tingkat]) + ->when($filters['search'] ?? null, function ($query, $search) { + $value = mb_strtolower($search); + $query->where(function ($scope) use ($value) { + $scope->whereRaw('LOWER(kode) LIKE ?', ["%{$value}%"]) + ->orWhereRaw('LOWER(nama) LIKE ?', ["%{$value}%"]); + }); + }) + ->when( + isset(self::PARENTS[$tingkat]) && ! empty($filters[self::PARENTS[$tingkat]]), + fn ($query) => $query->where( + self::PARENTS[$tingkat], + $filters[self::PARENTS[$tingkat]] + ) + ) + ->orderBy( + $filters['sort_by'] ?? 'nama', + $filters['sort_direction'] ?? 'asc' + ) + ->paginate($filters['per_page'] ?? 10); + } + + public function options(string $tingkat, array $filters = [], ?int $parentId = null) + { + $model = $this->model($tingkat); + $parentColumn = self::PARENTS[$tingkat] ?? null; + + return $model::query() + ->select(['id', 'kode', 'nama']) + ->when( + $parentColumn && $parentId, + fn ($query) => $query->where($parentColumn, $parentId) + ) + ->when($filters['search'] ?? null, function ($query, $search) { + $value = mb_strtolower($search); + $query->where(function ($scope) use ($value) { + $scope->whereRaw('LOWER(kode) LIKE ?', ["%{$value}%"]) + ->orWhereRaw('LOWER(nama) LIKE ?', ["%{$value}%"]); + }); + }) + ->orderBy('nama') + ->get(); + } + + public function create(string $tingkat, array $data): Model + { + $model = $this->model($tingkat); + + return $model::create($this->payload($tingkat, $data)) + ->load(self::RELATIONS[$tingkat]); + } + + public function find(string $tingkat, int $id): Model + { + $model = $this->model($tingkat); + + return $model::with(self::RELATIONS[$tingkat])->findOrFail($id); + } + + public function update(string $tingkat, int $id, array $data): Model + { + $wilayah = $this->find($tingkat, $id); + $wilayah->update($this->payload($tingkat, $data)); + + return $wilayah->fresh(self::RELATIONS[$tingkat]); + } + + public function delete(string $tingkat, int $id): void + { + $wilayah = $this->find($tingkat, $id); + $childRelation = match ($tingkat) { + 'provinsi' => 'kabupaten', + 'kabupaten' => 'kecamatan', + 'kecamatan' => 'desa', + default => null, + }; + + if ($childRelation && $wilayah->{$childRelation}()->exists()) { + throw ValidationException::withMessages([ + 'wilayah' => "Wilayah {$tingkat} tidak dapat dihapus karena masih memiliki data di bawahnya.", + ]); + } + + $wilayah->delete(); + } + + public function alamatLengkap(array $filters): array + { + if (! empty($filters['desa_id'])) { + $desa = WilayahDesa::with('kecamatan.kabupaten.provinsi')->findOrFail($filters['desa_id']); + + return $this->addressPayload( + $desa->kecamatan?->kabupaten?->provinsi, + $desa->kecamatan?->kabupaten, + $desa->kecamatan, + $desa + ); + } + + if (! empty($filters['kecamatan_id'])) { + $kecamatan = WilayahKecamatan::with('kabupaten.provinsi')->findOrFail($filters['kecamatan_id']); + + return $this->addressPayload( + $kecamatan->kabupaten?->provinsi, + $kecamatan->kabupaten, + $kecamatan + ); + } + + if (! empty($filters['kabupaten_id'])) { + $kabupaten = WilayahKabupaten::with('provinsi')->findOrFail($filters['kabupaten_id']); + + return $this->addressPayload($kabupaten->provinsi, $kabupaten); + } + + $provinsi = WilayahProvinsi::findOrFail($filters['provinsi_id']); + + return $this->addressPayload($provinsi); + } + + private function model(string $tingkat): string + { + abort_unless(isset(self::MODELS[$tingkat]), 404, 'Tingkat wilayah tidak ditemukan.'); + + return self::MODELS[$tingkat]; + } + + private function payload(string $tingkat, array $data): array + { + $fields = ['kode', 'nama']; + if (isset(self::PARENTS[$tingkat])) { + $fields[] = self::PARENTS[$tingkat]; + } + + return collect($data)->only($fields)->all(); + } + + private function addressPayload( + ?WilayahProvinsi $provinsi, + ?WilayahKabupaten $kabupaten = null, + ?WilayahKecamatan $kecamatan = null, + ?WilayahDesa $desa = null + ): array { + $node = fn ($item) => $item ? [ + 'id' => $item->id, + 'kode' => $item->kode, + 'nama' => $item->nama, + ] : null; + + $parts = array_filter([ + $desa ? "Desa/Kelurahan {$desa->nama}" : null, + $kecamatan ? "Kecamatan {$kecamatan->nama}" : null, + $kabupaten?->nama, + $provinsi ? $provinsi->nama : null, + ]); + + return [ + 'desa' => $node($desa), + 'kecamatan' => $node($kecamatan), + 'kabupaten' => $node($kabupaten), + 'provinsi' => $node($provinsi), + 'alamat_wilayah' => implode(', ', $parts), + ]; + } +} diff --git a/app/Traits/ApiResponse.php b/app/Traits/ApiResponse.php index d55f528..e4c0a17 100644 --- a/app/Traits/ApiResponse.php +++ b/app/Traits/ApiResponse.php @@ -3,7 +3,6 @@ namespace App\Traits; use Illuminate\Contracts\Pagination\LengthAwarePaginator; -use Illuminate\Http\Resources\Json\JsonResource; trait ApiResponse { @@ -11,9 +10,9 @@ trait ApiResponse { return response()->json([ 'success' => true, - 'code' => $code, + 'code' => $code, 'message' => $message, - 'data' => $data + 'data' => $data, ], $code); } @@ -21,9 +20,9 @@ trait ApiResponse { return response()->json([ 'success' => true, - 'code' => 201, + 'code' => 201, 'message' => $message, - 'data' => $data + 'data' => $data, ], 201); } @@ -31,9 +30,9 @@ trait ApiResponse { return response()->json([ 'success' => true, - 'code' => 200, + 'code' => 200, 'message' => $message, - 'data' => $data + 'data' => $data, ]); } @@ -41,8 +40,8 @@ trait ApiResponse { return response()->json([ 'success' => true, - 'code' => 200, - 'message' => $message + 'code' => 200, + 'message' => $message, ]); } @@ -50,8 +49,8 @@ trait ApiResponse { return response()->json([ 'success' => false, - 'code' => $code, - 'message' => $message + 'code' => $code, + 'message' => $message, ], $code); } @@ -59,9 +58,9 @@ trait ApiResponse { return response()->json([ 'success' => false, - 'code' => 422, + 'code' => 422, 'message' => 'Validation Error', - 'errors' => $errors + 'errors' => $errors, ], 422); } @@ -69,8 +68,8 @@ trait ApiResponse { return response()->json([ 'success' => false, - 'code' => 404, - 'message' => $message + 'code' => 404, + 'message' => $message, ], 404); } @@ -80,38 +79,10 @@ trait ApiResponse string $message = 'Success', int $code = 200 ) { - return response()->json([ - 'success' => true, - 'code' => $code, - 'message' => $message, + $data->through( + fn ($item) => (new $resource($item))->resolve(request()) + ); - 'data' => $resource::collection($data->items()), - - 'pagination' => [ - 'current_page' => $data->currentPage(), - 'per_page' => $data->perPage(), - 'from' => $data->firstItem(), - 'to' => $data->lastItem(), - 'last_page' => $data->lastPage(), - 'total' => $data->total(), - - 'has_more' => $data->hasMorePages(), - 'count' => count($data->items()), - - 'next_page' => $data->nextPageUrl(), - 'prev_page' => $data->previousPageUrl(), - 'first_page' => $data->url(1), - 'last_page_url'=> $data->url($data->lastPage()), - ], - - 'filters' => [ - 'page' => (int) request()->input('page', 1), - 'per_page' => (int) request()->input('per_page', 15), - 'search' => request()->input('search'), - 'sort_by' => request()->input('sort_by'), - 'sort_order' => request()->input('sort_order', 'desc'), - 'type' => request()->input('type'), - ] - ], $code); + return $this->success($data, $message, $code); } -} \ No newline at end of file +} diff --git a/bootstrap/app.php b/bootstrap/app.php index b4b52b4..c838bd8 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,5 +1,9 @@ withMiddleware(function (Middleware $middleware) { - // + $middleware->alias([ + 'access.level' => EnsureAccessLevel::class, + 'menu.permission' => EnsureMenuPermission::class, + 'menu.resource' => EnsureMenuResourcePermission::class, + 'tenant.context' => ResolveTenantContext::class, + ]); }) ->withExceptions(function (Exceptions $exceptions) { // diff --git a/composer.json b/composer.json index 46c7ec7..0218501 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,8 @@ "php": "^8.2", "laravel/framework": "^12.0", "laravel/sanctum": "^4.3", - "laravel/tinker": "^2.10.1" + "laravel/tinker": "^2.10.1", + "league/flysystem-aws-s3-v3": "^3.0" }, "require-dev": { "dedoc/scramble": "^0.13.28", diff --git a/composer.lock b/composer.lock index 946f1f9..28fa52b 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,159 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "dc5dc467fbab2c5682c8e676f4c2ffb4", + "content-hash": "43e4a419e3226169717b5475e08cf4d8", "packages": [ + { + "name": "aws/aws-crt-php", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/awslabs/aws-crt-php.git", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality." + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "AWS SDK Common Runtime Team", + "email": "aws-sdk-common-runtime@amazon.com" + } + ], + "description": "AWS Common Runtime for PHP", + "homepage": "https://github.com/awslabs/aws-crt-php", + "keywords": [ + "amazon", + "aws", + "crt", + "sdk" + ], + "support": { + "issues": "https://github.com/awslabs/aws-crt-php/issues", + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7" + }, + "time": "2024-10-18T22:15:13+00:00" + }, + { + "name": "aws/aws-sdk-php", + "version": "3.389.0", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "e6e6649e58826c7edaa9f546f444461a25a22719" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e6e6649e58826c7edaa9f546f444461a25a22719", + "reference": "e6e6649e58826c7edaa9f546f444461a25a22719", + "shasum": "" + }, + "require": { + "aws/aws-crt-php": "^1.2.3", + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^7.8.2 || ^8.0", + "guzzlehttp/promises": "^2.0.3 || ^3.0", + "guzzlehttp/psr7": "^2.6.3 || ^3.0", + "mtdowling/jmespath.php": "^2.9.1", + "php": ">=8.1", + "psr/http-message": "^1.0 || ^2.0", + "symfony/filesystem": "^v5.4.45 || ^v6.4.3 || ^v7.1.0 || ^v8.0.0" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "composer/composer": "^2.7.8", + "dms/phpunit-arraysubset-asserts": "^v0.5.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-sockets": "*", + "phpunit/phpunit": "^10.0", + "psr/cache": "^2.0 || ^3.0", + "psr/simple-cache": "^2.0 || ^3.0", + "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0", + "yoast/phpunit-polyfills": "^2.0" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-pcntl": "To use client-side monitoring", + "ext-sockets": "To use client-side monitoring" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Aws\\": "src/" + }, + "exclude-from-classmap": [ + "src/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "https://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "https://aws.amazon.com/sdk-for-php", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "support": { + "forum": "https://github.com/aws/aws-sdk-php/discussions", + "issues": "https://github.com/aws/aws-sdk-php/issues", + "source": "https://github.com/aws/aws-sdk-php/tree/3.389.0" + }, + "time": "2026-07-24T18:05:53+00:00" + }, { "name": "brick/math", "version": "0.14.8", @@ -643,26 +794,26 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.11.2", + "version": "7.15.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "bf5f35ad4b774b9d7c5766c02035e865e7e3fdab" + "reference": "61443dfb33c62f308ee8add20f45b4d6e4bf8d2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/bf5f35ad4b774b9d7c5766c02035e865e7e3fdab", - "reference": "bf5f35ad4b774b9d7c5766c02035e865e7e3fdab", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/61443dfb33c62f308ee8add20f45b4d6e4bf8d2f", + "reference": "61443dfb33c62f308ee8add20f45b4d6e4bf8d2f", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^2.5", - "guzzlehttp/psr7": "^2.11", + "guzzlehttp/promises": "^2.5.1", + "guzzlehttp/psr7": "^2.13", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.24" + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-client-implementation": "1.0" @@ -670,8 +821,8 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "guzzle/client-integration-tests": "3.0.2", - "guzzlehttp/test-server": "^0.5", + "guzzle/client-integration-tests": "3.0.3", + "guzzlehttp/test-server": "^0.7", "php-http/message-factory": "^1.1", "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" @@ -751,7 +902,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.11.2" + "source": "https://github.com/guzzle/guzzle/tree/7.15.1" }, "funding": [ { @@ -767,20 +918,20 @@ "type": "tidelift" } ], - "time": "2026-06-12T21:49:57+00:00" + "time": "2026-07-18T11:23:11+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.5.0", + "version": "2.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "4360e982f87f5f258bf872d094647791db2f4c8e" + "reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e", - "reference": "4360e982f87f5f258bf872d094647791db2f4c8e", + "url": "https://api.github.com/repos/guzzle/promises/zipball/9ad1e4fc607446a055b95870c7f668e93b5cff29", + "reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29", "shasum": "" }, "require": { @@ -835,7 +986,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.5.0" + "source": "https://github.com/guzzle/promises/tree/2.5.1" }, "funding": [ { @@ -851,20 +1002,20 @@ "type": "tidelift" } ], - "time": "2026-06-02T12:23:43+00:00" + "time": "2026-07-08T15:48:39+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.11.1", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "640e2897bbee822dbc8af761d49e1a29b1f2a6b1" + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/640e2897bbee822dbc8af761d49e1a29b1f2a6b1", - "reference": "640e2897bbee822dbc8af761d49e1a29b1f2a6b1", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4", + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4", "shasum": "" }, "require": { @@ -873,7 +1024,7 @@ "psr/http-message": "^1.1 || ^2.0", "ralouphie/getallheaders": "^3.0", "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.24" + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-factory-implementation": "1.0", @@ -954,7 +1105,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.11.1" + "source": "https://github.com/guzzle/psr7/tree/2.13.0" }, "funding": [ { @@ -970,7 +1121,7 @@ "type": "tidelift" } ], - "time": "2026-06-12T21:50:12+00:00" + "time": "2026-07-16T22:23:49+00:00" }, { "name": "guzzlehttp/uri-template", @@ -1720,16 +1871,16 @@ }, { "name": "league/flysystem", - "version": "3.34.0", + "version": "3.35.2", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e" + "reference": "b277b5dc3d56650b68904117124e79c851e12376" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e", - "reference": "2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/b277b5dc3d56650b68904117124e79c851e12376", + "reference": "b277b5dc3d56650b68904117124e79c851e12376", "shasum": "" }, "require": { @@ -1797,9 +1948,64 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.34.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.35.2" }, - "time": "2026-05-14T10:28:08+00:00" + "time": "2026-07-06T14:42:07+00:00" + }, + { + "name": "league/flysystem-aws-s3-v3", + "version": "3.35.2", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "8475ef9adfc6498b85469e2abec6fe3118cd08c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/8475ef9adfc6498b85469e2abec6fe3118cd08c4", + "reference": "8475ef9adfc6498b85469e2abec6fe3118cd08c4", + "shasum": "" + }, + "require": { + "aws/aws-sdk-php": "^3.371.5", + "league/flysystem": "^3.10.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "League\\Flysystem\\AwsS3V3\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "AWS S3 filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "aws", + "file", + "files", + "filesystem", + "s3", + "storage" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.35.2" + }, + "time": "2026-07-01T23:25:49+00:00" }, { "name": "league/flysystem-local", @@ -1852,16 +2058,16 @@ }, { "name": "league/mime-type-detection", - "version": "1.16.0", + "version": "1.17.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", - "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/f5f47eff7c48ed1003069a2ca67f316fb4021c76", + "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76", "shasum": "" }, "require": { @@ -1871,7 +2077,7 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^3.2", "phpstan/phpstan": "^0.12.68", - "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0 || ^11.0 || ^12.0" }, "type": "library", "autoload": { @@ -1892,7 +2098,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.17.0" }, "funding": [ { @@ -1904,7 +2110,7 @@ "type": "tidelift" } ], - "time": "2024-09-21T08:32:55+00:00" + "time": "2026-07-09T11:49:27+00:00" }, { "name": "league/uri", @@ -2191,6 +2397,72 @@ ], "time": "2026-01-02T08:56:05+00:00" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.9.2", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "2157c5e50e813ec6a96c1eed3be7f64a20fb32a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/2157c5e50e813ec6a96c1eed3be7f64a20fb32a8", + "reference": "2157c5e50e813ec6a96c1eed3be7f64a20fb32a8", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^3.0.3", + "phpunit/phpunit": "^8.5.52" + }, + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.9-dev" + } + }, + "autoload": { + "files": [ + "src/JmesPath.php" + ], + "psr-4": { + "JmesPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.9.2" + }, + "time": "2026-07-06T18:56:19+00:00" + }, { "name": "nesbot/carbon", "version": "3.12.3", @@ -3610,16 +3882,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { @@ -3657,7 +3929,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, "funding": [ { @@ -3677,7 +3949,7 @@ "type": "tidelift" } ], - "time": "2026-04-13T15:52:40+00:00" + "time": "2026-06-05T06:23:12+00:00" }, { "name": "symfony/error-handler", @@ -3926,6 +4198,76 @@ ], "time": "2026-01-05T13:30:16+00:00" }, + { + "name": "symfony/filesystem", + "version": "v7.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d721ea61b4a5fba8c5b6e7c1feda19efea144b50", + "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-11T16:38:44+00:00" + }, { "name": "symfony/finder", "version": "v7.4.8", diff --git a/config/filesystems.php b/config/filesystems.php index 3d671bd..c4b74e2 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -60,6 +60,21 @@ return [ 'report' => false, ], + 'minio' => [ + 'driver' => 's3', + 'key' => env('MINIO_ACCESS_KEY_ID'), + 'secret' => env('MINIO_SECRET_ACCESS_KEY'), + 'region' => env('MINIO_DEFAULT_REGION', 'us-east-1'), + 'bucket' => env('MINIO_BUCKET'), + 'endpoint' => env('MINIO_ENDPOINT'), + 'use_path_style_endpoint' => env('MINIO_USE_PATH_STYLE_ENDPOINT', true), + 'http' => [ + 'verify' => env('MINIO_CA_BUNDLE') ?: true, + ], + 'throw' => true, + 'report' => true, + ], + ], /* diff --git a/database/migrations/2026_07_02_000001_alter_users_table_add_is_master.php b/database/migrations/2026_07_02_000001_alter_users_table_add_is_master.php new file mode 100644 index 0000000..0eaa91a --- /dev/null +++ b/database/migrations/2026_07_02_000001_alter_users_table_add_is_master.php @@ -0,0 +1,30 @@ +boolean('is_master') + ->default(false) + ->after('password'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('is_master'); + }); + } +}; diff --git a/database/migrations/2026_07_02_120000_alter_users_table_add_verification_fields.php b/database/migrations/2026_07_02_120000_alter_users_table_add_verification_fields.php new file mode 100644 index 0000000..4bb56cc --- /dev/null +++ b/database/migrations/2026_07_02_120000_alter_users_table_add_verification_fields.php @@ -0,0 +1,56 @@ +string('verification_channel')->nullable()->after('status'); + } + + if (!Schema::hasColumn('users', 'verification_target')) { + $table->string('verification_target')->nullable()->after('verification_channel'); + } + + if (!Schema::hasColumn('users', 'verification_code')) { + $table->string('verification_code')->nullable()->after('verification_target'); + } + + if (!Schema::hasColumn('users', 'verification_code_expires_at')) { + $table->timestamp('verification_code_expires_at')->nullable()->after('verification_code'); + } + + if (!Schema::hasColumn('users', 'verified_at')) { + $table->timestamp('verified_at')->nullable()->after('verification_code_expires_at'); + } + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $columns = []; + + foreach ([ + 'verification_channel', + 'verification_target', + 'verification_code', + 'verification_code_expires_at', + 'verified_at', + ] as $column) { + if (Schema::hasColumn('users', $column)) { + $columns[] = $column; + } + } + + if (!empty($columns)) { + $table->dropColumn($columns); + } + }); + } +}; diff --git a/database/migrations/2026_07_03_131439_add_whatsapp_number_to_users_table.php b/database/migrations/2026_07_03_131439_add_whatsapp_number_to_users_table.php new file mode 100644 index 0000000..0a1eb43 --- /dev/null +++ b/database/migrations/2026_07_03_131439_add_whatsapp_number_to_users_table.php @@ -0,0 +1,22 @@ +string('whatsapp_number', 20)->nullable()->after('email'); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('whatsapp_number'); + }); + } +}; \ No newline at end of file diff --git a/database/migrations/2026_07_18_000001_add_deleted_at_to_users_table.php b/database/migrations/2026_07_18_000001_add_deleted_at_to_users_table.php new file mode 100644 index 0000000..abaf11c --- /dev/null +++ b/database/migrations/2026_07_18_000001_add_deleted_at_to_users_table.php @@ -0,0 +1,32 @@ +softDeletes(); + } + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + if (Schema::hasColumn('users', 'deleted_at')) { + $table->dropSoftDeletes(); + } + }); + } +}; diff --git a/database/migrations/2026_07_26_000001_add_access_level_to_users_table.php b/database/migrations/2026_07_26_000001_add_access_level_to_users_table.php new file mode 100644 index 0000000..63500a2 --- /dev/null +++ b/database/migrations/2026_07_26_000001_add_access_level_to_users_table.php @@ -0,0 +1,30 @@ +string('access_level', 30) + ->default('customer') + ->after('is_master') + ->index(); + }); + + DB::table('users') + ->where('is_master', true) + ->update(['access_level' => 'master_admin']); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('access_level'); + }); + } +}; diff --git a/database/migrations/2026_07_26_000002_add_tenant_scope_to_menu_groups_table.php b/database/migrations/2026_07_26_000002_add_tenant_scope_to_menu_groups_table.php new file mode 100644 index 0000000..5fddb6e --- /dev/null +++ b/database/migrations/2026_07_26_000002_add_tenant_scope_to_menu_groups_table.php @@ -0,0 +1,37 @@ +dropUnique(['name']); + $table->foreignId('tenant_id') + ->nullable() + ->after('id') + ->constrained() + ->cascadeOnDelete(); + $table->boolean('is_system')->default(false)->after('description'); + $table->unique(['tenant_id', 'name']); + }); + + DB::table('menu_groups') + ->whereNull('tenant_id') + ->update(['is_system' => true]); + } + + public function down(): void + { + Schema::table('menu_groups', function (Blueprint $table) { + $table->dropUnique(['tenant_id', 'name']); + $table->dropConstrainedForeignId('tenant_id'); + $table->dropColumn('is_system'); + $table->unique('name'); + }); + } +}; diff --git a/database/migrations/2026_07_26_000003_drop_phone_from_user_profiles_table.php b/database/migrations/2026_07_26_000003_drop_phone_from_user_profiles_table.php new file mode 100644 index 0000000..3f4a9cc --- /dev/null +++ b/database/migrations/2026_07_26_000003_drop_phone_from_user_profiles_table.php @@ -0,0 +1,22 @@ +dropColumn('phone'); + }); + } + + public function down(): void + { + Schema::table('user_profiles', function (Blueprint $table) { + $table->string('phone', 30)->nullable()->after('nik'); + }); + } +}; diff --git a/database/migrations/2026_07_26_000004_create_stored_files_table.php b/database/migrations/2026_07_26_000004_create_stored_files_table.php new file mode 100644 index 0000000..a09b74d --- /dev/null +++ b/database/migrations/2026_07_26_000004_create_stored_files_table.php @@ -0,0 +1,34 @@ +id(); + $table->uuid('uuid')->unique(); + $table->foreignId('tenant_id')->nullable()->constrained('tenants')->nullOnDelete(); + $table->foreignId('uploaded_by')->nullable()->constrained('users')->nullOnDelete(); + $table->string('disk', 50)->default('minio'); + $table->string('bucket'); + $table->string('object_key', 1024)->unique(); + $table->string('original_name'); + $table->string('extension', 20)->nullable(); + $table->string('mime_type', 150)->nullable(); + $table->unsignedBigInteger('size'); + $table->string('category', 80)->default('general')->index(); + $table->string('visibility', 20)->default('private'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + public function down(): void + { + Schema::dropIfExists('stored_files'); + } +}; diff --git a/database/migrations/2026_07_26_000005_add_profile_photo_file_id_to_users_table.php b/database/migrations/2026_07_26_000005_add_profile_photo_file_id_to_users_table.php new file mode 100644 index 0000000..a9d753f --- /dev/null +++ b/database/migrations/2026_07_26_000005_add_profile_photo_file_id_to_users_table.php @@ -0,0 +1,26 @@ +foreignId('profile_photo_file_id') + ->nullable() + ->after('whatsapp_number') + ->constrained('stored_files') + ->nullOnDelete(); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropConstrainedForeignId('profile_photo_file_id'); + }); + } +}; diff --git a/database/migrations/2026_07_27_120000_create_nas_management_tables.php b/database/migrations/2026_07_27_120000_create_nas_management_tables.php new file mode 100644 index 0000000..eaac773 --- /dev/null +++ b/database/migrations/2026_07_27_120000_create_nas_management_tables.php @@ -0,0 +1,99 @@ +id(); + $table->foreignId('tenant_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->string('connection_type', 20); + $table->string('host'); + $table->unsignedSmallInteger('api_port')->nullable()->default(8728); + $table->string('api_username')->nullable(); + $table->text('api_password')->nullable(); + $table->unsignedSmallInteger('radius_auth_port')->nullable()->default(1812); + $table->unsignedSmallInteger('radius_accounting_port')->nullable()->default(1813); + $table->text('radius_secret')->nullable(); + $table->unsignedSmallInteger('timeout')->default(10); + $table->string('status', 20)->default('active'); + $table->text('notes')->nullable(); + $table->timestamps(); + $table->unique(['tenant_id', 'name']); + $table->index(['tenant_id', 'connection_type', 'status']); + }); + + Schema::create('nas_package_profiles', function (Blueprint $table) { + $table->id(); + $table->foreignId('tenant_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->string('service_type', 20); + $table->string('external_profile_name')->nullable(); + $table->unsignedBigInteger('download_kbps')->nullable(); + $table->unsignedBigInteger('upload_kbps')->nullable(); + $table->string('local_address')->nullable(); + $table->string('remote_address_pool')->nullable(); + $table->unsignedInteger('session_timeout')->nullable(); + $table->unsignedInteger('idle_timeout')->nullable(); + $table->unsignedInteger('shared_users')->default(1); + $table->decimal('price', 15, 2)->default(0); + $table->string('status', 20)->default('active'); + $table->text('notes')->nullable(); + $table->timestamps(); + $table->unique(['tenant_id', 'name']); + $table->index(['tenant_id', 'service_type', 'status']); + }); + + Schema::create('nas_olts', function (Blueprint $table) { + $table->id(); + $table->foreignId('tenant_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->string('vendor')->nullable(); + $table->string('model')->nullable(); + $table->string('host'); + $table->unsignedSmallInteger('snmp_port')->default(161); + $table->string('snmp_version', 10)->default('v2c'); + $table->text('community')->nullable(); + $table->string('security_level', 30)->nullable(); + $table->string('security_name')->nullable(); + $table->string('auth_protocol', 10)->nullable(); + $table->text('auth_password')->nullable(); + $table->string('privacy_protocol', 10)->nullable(); + $table->text('privacy_password')->nullable(); + $table->unsignedSmallInteger('timeout')->default(5); + $table->string('status', 20)->default('active'); + $table->text('notes')->nullable(); + $table->timestamps(); + $table->unique(['tenant_id', 'name']); + $table->index(['tenant_id', 'status']); + }); + + Schema::create('nas_webfig_devices', function (Blueprint $table) { + $table->id(); + $table->foreignId('tenant_id')->constrained()->cascadeOnDelete(); + $table->string('name'); + $table->string('device_type')->nullable(); + $table->text('url'); + $table->string('username')->nullable(); + $table->text('password')->nullable(); + $table->string('status', 20)->default('active'); + $table->text('notes')->nullable(); + $table->timestamps(); + $table->unique(['tenant_id', 'name']); + $table->index(['tenant_id', 'status']); + }); + } + + public function down(): void + { + Schema::dropIfExists('nas_webfig_devices'); + Schema::dropIfExists('nas_olts'); + Schema::dropIfExists('nas_package_profiles'); + Schema::dropIfExists('nas_mikrotiks'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index cb1dd0e..81a570e 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -16,11 +16,17 @@ class DatabaseSeeder extends Seeder // User::factory(10)->create(); $this->call([ TicketTypeSeeder::class, + MenuListSeeder::class, ]); - User::factory()->create([ - 'name' => 'Test User', - 'email' => 'test@example.com', - ]); + User::query()->updateOrCreate( + ['email' => 'master@services-core.local'], + [ + 'name' => 'Master User', + 'password' => 'Master@12345', + 'is_master' => true, + 'access_level' => 'master_admin', + ] + ); } } diff --git a/database/seeders/MenuListSeeder.php b/database/seeders/MenuListSeeder.php new file mode 100644 index 0000000..526af97 --- /dev/null +++ b/database/seeders/MenuListSeeder.php @@ -0,0 +1,224 @@ + 'users'], + [ + 'parent_id' => null, + 'name' => 'Pengguna', + 'url' => null, + 'icon' => 'cil-user', + 'component' => null, + 'sort_order' => 1, + 'is_active' => true, + ] + ); + + MenuList::updateOrCreate( + ['slug' => 'users-user'], + [ + 'parent_id' => $users->id, + 'name' => 'Data Pengguna', + 'url' => '/users', + 'icon' => 'cil-people', + 'component' => null, + 'sort_order' => 1, + 'is_active' => true, + ] + ); + + MenuList::updateOrCreate( + ['slug' => 'users-group-menus'], + [ + 'parent_id' => $users->id, + 'name' => 'Grup Menu', + 'url' => '/group-menus', + 'icon' => 'cil-list-rich', + 'component' => null, + 'sort_order' => 2, + 'is_active' => true, + ] + ); + + MenuList::updateOrCreate( + ['slug' => 'tenants'], + [ + 'parent_id' => null, + 'name' => 'Tenant', + 'url' => '/tenants', + 'icon' => 'cil-layers', + 'component' => null, + 'sort_order' => 2, + 'is_active' => true, + ] + ); + + $ticketing = MenuList::updateOrCreate( + ['slug' => 'ticketing'], + [ + 'parent_id' => null, + 'name' => 'Ticketing', + 'url' => null, + 'icon' => 'cil-description', + 'component' => null, + 'sort_order' => 3, + 'is_active' => true, + ] + ); + + MenuList::updateOrCreate( + ['slug' => 'ticketing-material'], + [ + 'parent_id' => $ticketing->id, + 'name' => 'Material', + 'url' => '/materials', + 'icon' => 'cil-layers', + 'component' => null, + 'sort_order' => 1, + 'is_active' => true, + ] + ); + + MenuList::updateOrCreate( + ['slug' => 'ticketing-ticket'], + [ + 'parent_id' => $ticketing->id, + 'name' => 'Tiket', + 'url' => '/tickets', + 'icon' => 'cil-ticket', + 'component' => null, + 'sort_order' => 2, + 'is_active' => true, + ] + ); + + MenuList::updateOrCreate( + ['slug' => 'ticketing-approval'], + [ + 'parent_id' => $ticketing->id, + 'name' => 'Persetujuan', + 'url' => '/tickets/approval', + 'icon' => 'cil-check-circle', + 'component' => null, + 'sort_order' => 3, + 'is_active' => true, + ] + ); + + MenuList::updateOrCreate( + ['slug' => 'ticketing-rejected'], + [ + 'parent_id' => $ticketing->id, + 'name' => 'Ditolak', + 'url' => '/tickets/rejected', + 'icon' => 'cil-x-circle', + 'component' => null, + 'sort_order' => 4, + 'is_active' => true, + ] + ); + + MenuList::updateOrCreate( + ['slug' => 'ticketing-ticket-types'], + [ + 'parent_id' => $ticketing->id, + 'name' => 'Jenis Tiket', + 'url' => '/ticket-types', + 'icon' => 'cil-tags', + 'component' => null, + 'sort_order' => 5, + 'is_active' => true, + ] + ); + + MenuList::updateOrCreate( + ['slug' => 'ticketing-ticket-incidents'], + [ + 'parent_id' => $ticketing->id, + 'name' => 'Insiden Tiket', + 'url' => '/ticket-incident-types', + 'icon' => 'cil-warning', + 'component' => null, + 'sort_order' => 6, + 'is_active' => true, + ] + ); + + $wilayah = MenuList::updateOrCreate( + ['slug' => 'wilayah'], + [ + 'parent_id' => null, + 'name' => 'Wilayah', + 'url' => null, + 'icon' => 'cil-location-pin', + 'component' => null, + 'sort_order' => 4, + 'is_active' => true, + ] + ); + + foreach ([ + ['slug' => 'wilayah-desa', 'name' => 'Desa', 'url' => '/wilayah/desa', 'sort_order' => 1], + ['slug' => 'wilayah-kecamatan', 'name' => 'Kecamatan', 'url' => '/wilayah/kecamatan', 'sort_order' => 2], + ['slug' => 'wilayah-kabupaten', 'name' => 'Kabupaten', 'url' => '/wilayah/kabupaten', 'sort_order' => 3], + ['slug' => 'wilayah-provinsi', 'name' => 'Provinsi', 'url' => '/wilayah/provinsi', 'sort_order' => 4], + ] as $item) { + MenuList::updateOrCreate( + ['slug' => $item['slug']], + [ + 'parent_id' => $wilayah->id, + 'name' => $item['name'], + 'url' => $item['url'], + 'icon' => 'cil-location-pin', + 'component' => null, + 'sort_order' => $item['sort_order'], + 'is_active' => true, + ] + ); + } + + $nas = MenuList::updateOrCreate( + ['slug' => 'nas'], + [ + 'parent_id' => null, + 'name' => 'NAS', + 'url' => null, + 'icon' => 'cil-settings', + 'component' => null, + 'sort_order' => 5, + 'is_active' => true, + ] + ); + + foreach ([ + ['slug' => 'nas-mikrotik', 'name' => 'Mikrotik', 'url' => '/nas/mikrotik', 'icon' => 'cil-settings', 'sort_order' => 1], + ['slug' => 'nas-package-profiles', 'name' => 'Profile Paket', 'url' => '/nas/package-profiles', 'icon' => 'cil-speedometer', 'sort_order' => 2], + ['slug' => 'nas-olt', 'name' => 'OLT', 'url' => '/nas/olt', 'icon' => 'cil-layers', 'sort_order' => 3], + ['slug' => 'nas-webfig', 'name' => 'Webfig', 'url' => '/nas/webfig', 'icon' => 'cil-laptop', 'sort_order' => 4], + ] as $item) { + MenuList::updateOrCreate( + ['slug' => $item['slug']], + [ + 'parent_id' => $nas->id, + 'name' => $item['name'], + 'url' => $item['url'], + 'icon' => $item['icon'], + 'component' => null, + 'sort_order' => $item['sort_order'], + 'is_active' => true, + ] + ); + } + } +} diff --git a/phpunit.xml b/phpunit.xml index 506b9a3..61c031c 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -22,8 +22,8 @@ - - + + diff --git a/routes/api.php b/routes/api.php index f379454..e84b576 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,80 +1,128 @@ group(function () { -// Route::prefix('tickets')->group(function () { -// Route::get('/', [TicketController::class, 'index']); -// Route::post('/', [TicketController::class, 'store']); -// }); -Route::get('/generate-token', function () { + foreach ([ + 'mikrotiks' => ['resource' => 'mikrotik', 'menu' => 'nas-mikrotik'], + 'package-profiles' => ['resource' => 'package-profile', 'menu' => 'nas-package-profiles'], + 'olts' => ['resource' => 'olt', 'menu' => 'nas-olt'], + 'webfig-devices' => ['resource' => 'webfig', 'menu' => 'nas-webfig'], + ] as $path => $nas) { + Route::prefix("nas/{$path}") + ->middleware("menu.resource:{$nas['menu']}") + ->controller(NasResourceController::class) + ->group(function () use ($nas, $path) { + Route::get('/', 'index') + ->name("nas.{$path}.index") + ->defaults('nas_resource', $nas['resource']); + Route::post('/', 'store') + ->name("nas.{$path}.store") + ->defaults('nas_resource', $nas['resource']); + Route::get('/{id}', 'show') + ->name("nas.{$path}.show") + ->whereNumber('id') + ->defaults('nas_resource', $nas['resource']); + Route::put('/{id}', 'update') + ->name("nas.{$path}.update") + ->whereNumber('id') + ->defaults('nas_resource', $nas['resource']); + Route::patch('/{id}', 'update') + ->name("nas.{$path}.patch") + ->whereNumber('id') + ->defaults('nas_resource', $nas['resource']); + Route::delete('/{id}', 'destroy') + ->name("nas.{$path}.destroy") + ->whereNumber('id') + ->defaults('nas_resource', $nas['resource']); + }); + } - $user = \App\Models\User::find(1); + Route::apiResource('tickets', TicketController::class) + ->middleware('menu.resource:ticketing-ticket'); - return $user->createToken( - 'master-token' - )->plainTextToken; - -}); - -Route::middleware('auth:sanctum')->group(function () { - - Route::apiResource('tickets', TicketController::class); - - Route::apiResource('ticket-types', TicketTypeController::class); - Route::apiResource('ticket-incident-types', TicketIncidentTypeController::class); + Route::apiResource('ticket-types', TicketTypeController::class) + ->middleware('menu.resource:ticketing-ticket-types'); + Route::apiResource('ticket-incident-types', TicketIncidentTypeController::class) + ->middleware('menu.resource:ticketing-ticket-incidents'); Route::prefix('tickets/{ticket}') ->controller(TicketWorkflowController::class) ->group(function () { - Route::get('/workflow', 'history'); + Route::get('/workflow', 'history') + ->middleware('menu.permission:ticketing-ticket,view'); - Route::post('/assign', 'assign'); - Route::post('/reassign', 'reassign'); + Route::post('/assign', 'assign') + ->middleware('menu.permission:ticketing-ticket,update'); + Route::post('/reassign', 'reassign') + ->middleware('menu.permission:ticketing-ticket,update'); - Route::post('/approve', 'approve'); - Route::post('/reject', 'reject'); + Route::post('/approve', 'approve') + ->middleware('menu.permission:ticketing-approval,approve'); + Route::post('/reject', 'reject') + ->middleware('menu.permission:ticketing-approval,approve'); - Route::post('/start', 'start'); + Route::post('/start', 'start') + ->middleware('menu.permission:ticketing-ticket,update'); - Route::post('/pending', 'pending'); - Route::post('/resume', 'resume'); + Route::post('/pending', 'pending') + ->middleware('menu.permission:ticketing-ticket,update'); + Route::post('/resume', 'resume') + ->middleware('menu.permission:ticketing-ticket,update'); - Route::post('/resolve', 'resolve'); + Route::post('/resolve', 'resolve') + ->middleware('menu.permission:ticketing-ticket,update'); - Route::post('/close', 'close'); + Route::post('/close', 'close') + ->middleware('menu.permission:ticketing-ticket,update'); - Route::post('/cancel', 'cancel'); + Route::post('/cancel', 'cancel') + ->middleware('menu.permission:ticketing-ticket,delete'); - Route::post('/escalate', 'escalate'); + Route::post('/escalate', 'escalate') + ->middleware('menu.permission:ticketing-ticket,update'); }); - + Route::prefix('materials') + ->middleware('menu.resource:ticketing-material') ->controller(MaterialController::class) ->group(function () { - Route::get('/', 'index'); - Route::post('/assign', 'assign'); - Route::post('/transfer', 'transfer'); - Route::post('/return', 'return'); + Route::get('/', 'index'); + Route::post('/assign', 'assign'); + Route::post('/transfer', 'transfer'); + Route::post('/return', 'return'); - Route::get('/user/{user_id}', 'listByUser'); - Route::get('/history/{barcode_id}', 'history'); + Route::get('/user/{user_id}', 'listByUser'); + Route::get('/history/{barcode_id}', 'history'); - }); + }); Route::prefix('audit-logs') + ->middleware('access.level:tenant_owner') ->controller(AuditLogController::class) ->group(function () { @@ -83,4 +131,110 @@ Route::middleware('auth:sanctum')->group(function () { Route::get('/{id}', 'show'); }); + Route::prefix('me') + ->group(function () { + Route::get('/menus', [MenuController::class, 'myMenus']); + Route::get('/profile', [ProfileController::class, 'show']); + Route::put('/profile', [ProfileController::class, 'update']); + }); + + Route::prefix('files')->controller(StoredFileController::class)->group(function () { + Route::get('/', 'index'); + Route::post('/', 'store'); + Route::get('/{storedFile}', 'show'); + Route::get('/{storedFile}/temporary-url', 'temporaryUrl'); + Route::delete('/{storedFile}', 'destroy'); + }); + + Route::get('wilayah/alamat-lengkap', [WilayahController::class, 'alamatLengkap']) + ->name('wilayah.alamat-lengkap'); + Route::prefix('wilayah/options')->controller(WilayahController::class)->group(function () { + Route::get('/provinsi', 'provinsiOptions')->name('wilayah.options.provinsi'); + Route::get('/kabupaten/{provinsi}', 'kabupatenOptions')->name('wilayah.options.kabupaten'); + Route::get('/kecamatan/{kabupaten}', 'kecamatanOptions')->name('wilayah.options.kecamatan'); + Route::get('/desa/{kecamatan}', 'desaOptions')->name('wilayah.options.desa'); + }); + + foreach ([ + 'desa' => 'wilayah-desa', + 'kecamatan' => 'wilayah-kecamatan', + 'kabupaten' => 'wilayah-kabupaten', + 'provinsi' => 'wilayah-provinsi', + ] as $tingkat => $menuSlug) { + $readRoutes = [ + Route::get("wilayah/{$tingkat}", [WilayahController::class, 'index']) + ->name("wilayah.{$tingkat}.index"), + Route::get("wilayah/{$tingkat}/{wilayah}", [WilayahController::class, 'show']) + ->name("wilayah.{$tingkat}.show") + ->whereNumber('wilayah'), + ]; + $writeRoutes = [ + Route::post("wilayah/{$tingkat}", [WilayahController::class, 'store']) + ->name("wilayah.{$tingkat}.store"), + Route::put("wilayah/{$tingkat}/{wilayah}", [WilayahController::class, 'update']) + ->name("wilayah.{$tingkat}.update") + ->whereNumber('wilayah'), + Route::patch("wilayah/{$tingkat}/{wilayah}", [WilayahController::class, 'update']) + ->name("wilayah.{$tingkat}.patch") + ->whereNumber('wilayah'), + Route::delete("wilayah/{$tingkat}/{wilayah}", [WilayahController::class, 'destroy']) + ->name("wilayah.{$tingkat}.destroy") + ->whereNumber('wilayah'), + ]; + + foreach ($readRoutes as $wilayahRoute) { + $wilayahRoute->defaults('tingkat', $tingkat); + } + + foreach ($writeRoutes as $wilayahRoute) { + $wilayahRoute->defaults('tingkat', $tingkat) + ->middleware("menu.resource:{$menuSlug}"); + } + } + + Route::apiResource('menus', MenuListController::class) + ->parameters(['menus' => 'menu_list']) + ->middleware('access.level:master_admin'); + Route::apiResource('menu-groups', MenuGroupController::class) + ->middleware([ + 'access.level:tenant_owner', + 'menu.resource:users-group-menus', + ]); + Route::apiResource('tenants', TenantController::class) + ->only(['index', 'show']) + ->middleware('access.level:tenant_owner'); + Route::apiResource('tenants', TenantController::class) + ->except(['index', 'show']) + ->middleware('access.level:master_admin'); + Route::apiResource('users', UserController::class) + ->middleware([ + 'access.level:tenant_owner', + 'menu.resource:users-user', + ]); + + Route::prefix('menu-groups/{menu_group}') + ->middleware([ + 'access.level:tenant_owner', + 'menu.permission:users-group-menus,update', + ]) + ->controller(MenuGroupController::class) + ->group(function () { + + Route::get('/menus', 'menus'); + Route::get('/available-menus', 'availableMenus'); + Route::put('/menus', 'syncMenus'); + }); + + Route::prefix('users/{user}') + ->middleware([ + 'access.level:tenant_owner', + 'menu.permission:users-group-menus,update', + ]) + ->controller(MenuGroupController::class) + ->group(function () { + + Route::get('/menu-groups', 'userAssignments'); + Route::put('/menu-groups', 'syncUserAssignments'); + }); + }); diff --git a/tests/Feature/MenuAccessTest.php b/tests/Feature/MenuAccessTest.php new file mode 100644 index 0000000..a2004db --- /dev/null +++ b/tests/Feature/MenuAccessTest.php @@ -0,0 +1,329 @@ +staffWithTicketPermission(); + Sanctum::actingAs($user); + + $response = $this->withHeader('X-Tenant-ID', $tenant->id) + ->getJson('/api/me/menus'); + + $response + ->assertOk() + ->assertJsonPath('data.tenant_id', $tenant->id) + ->assertJsonPath('data.access_level', 'staff') + ->assertJsonPath('data.menus.0.slug', 'ticketing-ticket'); + } + + public function test_user_cannot_select_a_tenant_they_do_not_belong_to(): void + { + [$user] = $this->staffWithTicketPermission(); + $otherTenant = Tenant::create([ + 'tenant_code' => 'OTHER', + 'tenant_name' => 'Other Tenant', + ]); + Sanctum::actingAs($user); + + $this->withHeader('X-Tenant-ID', $otherTenant->id) + ->getJson('/api/me/menus') + ->assertForbidden() + ->assertJsonPath('message', 'User tidak memiliki akses ke tenant yang dipilih.'); + } + + public function test_tenant_owner_cannot_promote_a_user_to_master_admin(): void + { + [$owner, $tenant] = $this->staffWithTicketPermission('tenant_owner'); + $target = User::factory()->create(['access_level' => 'staff']); + $target->userTenants()->create([ + 'tenant_id' => $tenant->id, + 'is_default' => true, + ]); + + $usersMenu = MenuList::create([ + 'name' => 'Data Pengguna', + 'slug' => 'users-user', + 'url' => '/users', + 'is_active' => true, + ]); + $group = $owner->userMenuGroups()->first()->menuGroup; + $group->menus()->attach($usersMenu->id, [ + 'can_view' => true, + 'can_update' => true, + ]); + Sanctum::actingAs($owner); + + $this->withHeader('X-Tenant-ID', $tenant->id) + ->patchJson("/api/users/{$target->id}", [ + 'access_level' => 'master_admin', + ]) + ->assertUnprocessable() + ->assertJsonValidationErrors('access_level'); + } + + public function test_index_response_uses_the_same_paginator_contract_as_tickets(): void + { + $master = User::factory()->create([ + 'is_master' => true, + 'access_level' => 'master_admin', + ]); + Tenant::create([ + 'tenant_code' => 'PAGINATED', + 'tenant_name' => 'Pagination Test', + ]); + Sanctum::actingAs($master); + + $this->getJson('/api/tenants?per_page=10') + ->assertOk() + ->assertJsonStructure([ + 'success', + 'code', + 'message', + 'data' => [ + 'current_page', + 'data', + 'first_page_url', + 'from', + 'last_page', + 'last_page_url', + 'links', + 'next_page_url', + 'path', + 'per_page', + 'prev_page_url', + 'to', + 'total', + ], + ]) + ->assertJsonPath('data.current_page', 1) + ->assertJsonPath('data.per_page', 10) + ->assertJsonPath('data.data.0.tenant_code', 'PAGINATED'); + } + + public function test_index_query_parameters_are_validated(): void + { + $master = User::factory()->create([ + 'is_master' => true, + 'access_level' => 'master_admin', + ]); + Sanctum::actingAs($master); + + $this->getJson('/api/tenants?page=0&per_page=101&status=unknown') + ->assertUnprocessable() + ->assertJsonValidationErrors(['page', 'per_page', 'status']); + } + + public function test_tenant_owner_only_receives_tenants_assigned_to_the_user(): void + { + [$owner, $assignedTenant] = $this->staffWithTicketPermission('tenant_owner'); + $secondAssignedTenant = Tenant::create([ + 'tenant_code' => 'OWNER-SECOND', + 'tenant_name' => 'Second Assigned Tenant', + ]); + $owner->userTenants()->create([ + 'tenant_id' => $secondAssignedTenant->id, + 'is_default' => false, + ]); + $otherTenant = Tenant::create([ + 'tenant_code' => 'OWNER-OTHER', + 'tenant_name' => 'Unassigned Tenant', + ]); + Sanctum::actingAs($owner); + + $response = $this->withHeader('X-Tenant-ID', $assignedTenant->id) + ->getJson('/api/tenants?per_page=100') + ->assertOk(); + + $tenantIds = collect($response->json('data.data'))->pluck('id'); + + $this->assertTrue($tenantIds->contains($assignedTenant->id)); + $this->assertTrue($tenantIds->contains($secondAssignedTenant->id)); + $this->assertFalse($tenantIds->contains($otherTenant->id)); + + $this->withHeader('X-Tenant-ID', $assignedTenant->id) + ->getJson("/api/tenants/{$secondAssignedTenant->id}") + ->assertOk(); + + $this->withHeader('X-Tenant-ID', $assignedTenant->id) + ->getJson("/api/tenants/{$otherTenant->id}") + ->assertForbidden(); + } + + public function test_authenticated_user_can_view_and_update_only_their_own_profile(): void + { + $user = User::factory()->create([ + 'name' => 'Nama Lama', + 'access_level' => 'staff', + ]); + Sanctum::actingAs($user); + + $this->getJson('/api/me/profile') + ->assertOk() + ->assertJsonPath('data.id', $user->id) + ->assertJsonPath('data.access_level', 'staff'); + + $this->putJson('/api/me/profile', [ + 'name' => 'Nama Baru', + 'username' => 'nama.baru', + 'email' => $user->email, + 'whatsapp_number' => '08123456789', + 'access_level' => 'master_admin', + 'is_master' => true, + 'user_profile' => [ + 'nik' => '1234567890', + 'address' => 'Alamat baru', + ], + ]) + ->assertOk() + ->assertJsonPath('data.name', 'Nama Baru') + ->assertJsonPath('data.user_profile.nik', '1234567890') + ->assertJsonPath('data.access_level', 'staff') + ->assertJsonPath('data.is_master', false); + + $user->refresh(); + $this->assertSame('staff', $user->access_level->value); + $this->assertFalse($user->is_master); + } + + public function test_tenant_owner_creates_and_only_lists_groups_in_the_active_tenant(): void + { + [$owner, $tenant] = $this->staffWithTicketPermission('tenant_owner'); + $otherTenant = Tenant::create([ + 'tenant_code' => 'OTHER-GROUP', + 'tenant_name' => 'Other Group Tenant', + ]); + $otherTenantGroup = MenuGroup::create([ + 'tenant_id' => $otherTenant->id, + 'name' => 'Other Tenant Group', + 'is_active' => true, + ]); + $globalSystemGroup = MenuGroup::create([ + 'tenant_id' => null, + 'name' => 'Global System Group', + 'is_system' => true, + 'is_active' => true, + ]); + $menu = MenuList::create([ + 'name' => 'Group Menus', + 'slug' => 'users-group-menus', + 'url' => '/group-menus', + 'is_active' => true, + ]); + $owner->userMenuGroups()->first()->menuGroup->menus()->attach($menu->id, [ + 'can_view' => true, + 'can_create' => true, + ]); + Sanctum::actingAs($owner); + + $this->withHeader('X-Tenant-ID', $tenant->id) + ->postJson('/api/menu-groups', [ + 'name' => 'Teknisi Tenant', + 'description' => 'Akses teknisi', + 'is_active' => true, + ]) + ->assertCreated() + ->assertJsonPath('data.tenant_id', $tenant->id) + ->assertJsonPath('data.is_system', false) + ->assertJsonPath('data.tenant.id', $tenant->id); + + $response = $this->withHeader('X-Tenant-ID', $tenant->id) + ->getJson('/api/menu-groups?per_page=100') + ->assertOk(); + + $this->assertNotContains( + 'Other Tenant Group', + collect($response->json('data.data'))->pluck('name')->all() + ); + $this->assertNotContains( + 'Global System Group', + collect($response->json('data.data'))->pluck('name')->all() + ); + + $this->withHeader('X-Tenant-ID', $tenant->id) + ->getJson("/api/menu-groups/{$otherTenantGroup->id}") + ->assertForbidden(); + + $this->withHeader('X-Tenant-ID', $tenant->id) + ->getJson("/api/menu-groups/{$globalSystemGroup->id}") + ->assertForbidden(); + } + + public function test_master_admin_can_create_global_and_tenant_menu_groups(): void + { + $master = User::factory()->create([ + 'is_master' => true, + 'access_level' => 'master_admin', + ]); + $tenant = Tenant::create([ + 'tenant_code' => 'MASTER-SCOPE', + 'tenant_name' => 'Master Scope Tenant', + ]); + Sanctum::actingAs($master); + + $this->postJson('/api/menu-groups', [ + 'name' => 'Global System Group', + 'is_active' => true, + 'tenant_id' => null, + ]) + ->assertCreated() + ->assertJsonPath('data.tenant_id', null) + ->assertJsonPath('data.is_system', true); + + $this->postJson('/api/menu-groups', [ + 'name' => 'Tenant Scoped Group', + 'is_active' => true, + 'tenant_id' => $tenant->id, + ]) + ->assertCreated() + ->assertJsonPath('data.tenant_id', $tenant->id) + ->assertJsonPath('data.is_system', false) + ->assertJsonPath('data.tenant.tenant_name', 'Master Scope Tenant'); + } + + private function staffWithTicketPermission(string $level = 'staff'): array + { + $tenant = Tenant::create([ + 'tenant_code' => fake()->unique()->bothify('TEN-###'), + 'tenant_name' => fake()->company(), + ]); + $user = User::factory()->create(['access_level' => $level]); + $user->userTenants()->create([ + 'tenant_id' => $tenant->id, + 'is_default' => true, + ]); + $menu = MenuList::create([ + 'name' => 'Tiket', + 'slug' => 'ticketing-ticket', + 'url' => '/tickets', + 'is_active' => true, + ]); + $group = MenuGroup::create([ + 'tenant_id' => $tenant->id, + 'name' => 'Staff '.$tenant->id, + 'is_active' => true, + ]); + $group->menus()->attach($menu->id, [ + 'can_view' => true, + 'can_update' => true, + ]); + $user->userMenuGroups()->create([ + 'menu_group_id' => $group->id, + 'tenant_id' => $tenant->id, + ]); + + return [$user, $tenant]; + } +} diff --git a/tests/Feature/NasResourceServiceTest.php b/tests/Feature/NasResourceServiceTest.php new file mode 100644 index 0000000..1bf389c --- /dev/null +++ b/tests/Feature/NasResourceServiceTest.php @@ -0,0 +1,88 @@ + 'NAS-A', 'tenant_name' => 'NAS A']); + $otherTenant = Tenant::create(['tenant_code' => 'NAS-B', 'tenant_name' => 'NAS B']); + $user = User::factory()->create(['access_level' => 'tenant_owner']); + $user->userTenants()->create(['tenant_id' => $tenant->id, 'is_default' => true]); + + NasMikrotik::create([ + 'tenant_id' => $tenant->id, + 'name' => 'Router A', + 'connection_type' => 'api', + 'host' => '10.0.0.1', + ]); + NasMikrotik::create([ + 'tenant_id' => $otherTenant->id, + 'name' => 'Router B', + 'connection_type' => 'api', + 'host' => '10.0.0.2', + ]); + + $result = app(NasResourceService::class)->list( + 'mikrotik', + ['per_page' => 100], + $user, + $tenant->id + ); + + $this->assertSame(['Router A'], $result->getCollection()->pluck('name')->all()); + } + + public function test_mikrotik_credentials_are_encrypted_and_blank_update_preserves_them(): void + { + $tenant = Tenant::create(['tenant_code' => 'NAS-SECRET', 'tenant_name' => 'NAS Secret']); + $master = User::factory()->create(['is_master' => true, 'access_level' => 'master_admin']); + $service = app(NasResourceService::class); + + $mikrotik = $service->create('mikrotik', [ + 'tenant_id' => $tenant->id, + 'name' => 'Secure Router', + 'connection_type' => 'radius', + 'host' => '10.10.10.1', + 'radius_secret' => 'plain-secret', + ], $master, null); + + $this->assertNotSame('plain-secret', $mikrotik->getRawOriginal('radius_secret')); + $this->assertSame('plain-secret', $mikrotik->radius_secret); + + $service->update('mikrotik', $mikrotik, [ + 'name' => 'Secure Router Updated', + 'radius_secret' => '', + ], $master); + + $this->assertSame('plain-secret', $mikrotik->fresh()->radius_secret); + } + + public function test_nas_route_resolves_its_resource_configuration(): void + { + $tenant = Tenant::create(['tenant_code' => 'NAS-ROUTE', 'tenant_name' => 'NAS Route']); + $master = User::factory()->create(['is_master' => true, 'access_level' => 'master_admin']); + Sanctum::actingAs($master); + + $this->postJson('/api/nas/mikrotiks', [ + 'tenant_id' => $tenant->id, + 'name' => 'Route Router', + 'connection_type' => 'api', + 'host' => '192.168.88.1', + ]) + ->assertCreated() + ->assertJsonPath('data.name', 'Route Router') + ->assertJsonPath('data.connection_type', 'api'); + } +} diff --git a/tests/Feature/StoredFileApiTest.php b/tests/Feature/StoredFileApiTest.php new file mode 100644 index 0000000..e94e37f --- /dev/null +++ b/tests/Feature/StoredFileApiTest.php @@ -0,0 +1,86 @@ +create([ + 'access_level' => UserAccessLevel::CUSTOMER, + 'is_master' => false, + ]); + + $uploadResponse = $this->actingAs($user) + ->post('/api/files', [ + 'file' => UploadedFile::fake()->create('avatar.png', 100, 'image/png'), + 'category' => 'profile', + ]) + ->assertCreated() + ->assertJsonPath('success', true) + ->assertJsonPath('data.category', 'profile'); + + $fileId = $uploadResponse->json('data.id'); + + $this->actingAs($user) + ->putJson('/api/me/profile', [ + 'name' => $user->name, + 'username' => $user->username, + 'email' => $user->email, + 'profile_photo_file_id' => $fileId, + 'user_profile' => null, + ]) + ->assertOk() + ->assertJsonPath('data.profile_photo.id', $fileId); + + $this->assertDatabaseHas('users', [ + 'id' => $user->id, + 'profile_photo_file_id' => $fileId, + ]); + + $storedFile = StoredFile::findOrFail($fileId); + Storage::disk('minio')->assertExists($storedFile->object_key); + } + + public function test_user_cannot_use_another_users_file_as_profile_photo(): void + { + $owner = User::factory()->create(); + $otherUser = User::factory()->create(); + $file = StoredFile::create([ + 'uuid' => fake()->uuid(), + 'uploaded_by' => $owner->id, + 'disk' => 'minio', + 'bucket' => 'file', + 'object_key' => 'profiles/test.png', + 'original_name' => 'test.png', + 'extension' => 'png', + 'mime_type' => 'image/png', + 'size' => 100, + 'category' => 'profile', + 'visibility' => 'private', + ]); + + $this->actingAs($otherUser) + ->putJson('/api/me/profile', [ + 'name' => $otherUser->name, + 'username' => $otherUser->username, + 'email' => $otherUser->email, + 'profile_photo_file_id' => $file->id, + 'user_profile' => null, + ]) + ->assertUnprocessable() + ->assertJsonValidationErrors('profile_photo_file_id'); + } +} diff --git a/tests/Feature/WilayahApiTest.php b/tests/Feature/WilayahApiTest.php new file mode 100644 index 0000000..db20a77 --- /dev/null +++ b/tests/Feature/WilayahApiTest.php @@ -0,0 +1,106 @@ +create([ + 'is_master' => true, + 'access_level' => UserAccessLevel::MASTER_ADMIN, + ]); + + $provinsiId = $this->actingAs($user) + ->postJson('/api/wilayah/provinsi', ['kode' => '11', 'nama' => 'Aceh']) + ->assertCreated() + ->json('data.id'); + + $kabupatenId = $this->postJson('/api/wilayah/kabupaten', [ + 'kode' => '11.01', + 'nama' => 'Kabupaten Simeulue', + 'provinsi_id' => $provinsiId, + ])->assertCreated()->json('data.id'); + + $kecamatanId = $this->postJson('/api/wilayah/kecamatan', [ + 'kode' => '11.01.01', + 'nama' => 'Teupah Selatan', + 'kabupaten_id' => $kabupatenId, + ])->assertCreated()->json('data.id'); + + $desaId = $this->postJson('/api/wilayah/desa', [ + 'kode' => '11.01.01.2001', + 'nama' => 'Latiung', + 'kecamatan_id' => $kecamatanId, + ])->assertCreated()->json('data.id'); + + $this->getJson("/api/wilayah/alamat-lengkap?desa_id={$desaId}") + ->assertOk() + ->assertJsonPath('data.desa.nama', 'Latiung') + ->assertJsonPath('data.kecamatan.nama', 'Teupah Selatan') + ->assertJsonPath('data.kabupaten.nama', 'Kabupaten Simeulue') + ->assertJsonPath('data.provinsi.nama', 'Aceh') + ->assertJsonPath( + 'data.alamat_wilayah', + 'Desa/Kelurahan Latiung, Kecamatan Teupah Selatan, Kabupaten Simeulue, Provinsi Aceh' + ); + } + + public function test_child_region_requires_its_direct_parent(): void + { + $user = User::factory()->create([ + 'is_master' => true, + 'access_level' => UserAccessLevel::MASTER_ADMIN, + ]); + + $this->actingAs($user) + ->postJson('/api/wilayah/desa', [ + 'kode' => '01', + 'nama' => 'Desa Tanpa Kecamatan', + ]) + ->assertUnprocessable() + ->assertJsonValidationErrors('kecamatan_id'); + } + + public function test_parent_with_children_cannot_be_deleted(): void + { + $user = User::factory()->create([ + 'is_master' => true, + 'access_level' => UserAccessLevel::MASTER_ADMIN, + ]); + $provinsi = WilayahProvinsi::create(['kode' => '12', 'nama' => 'Sumatera Utara']); + $kabupaten = WilayahKabupaten::create([ + 'provinsi_id' => $provinsi->id, + 'kode' => '12.01', + 'nama' => 'Tapanuli Tengah', + ]); + $kecamatan = WilayahKecamatan::create([ + 'kabupaten_id' => $kabupaten->id, + 'kode' => '12.01.01', + 'nama' => 'Barus', + ]); + WilayahDesa::create([ + 'kecamatan_id' => $kecamatan->id, + 'kode' => '12.01.01.2001', + 'nama' => 'Kampung Mudik', + ]); + + $this->actingAs($user) + ->deleteJson("/api/wilayah/provinsi/{$provinsi->id}") + ->assertUnprocessable() + ->assertJsonValidationErrors('wilayah'); + + $this->assertDatabaseHas('wilayah_provinsi', ['id' => $provinsi->id]); + } +} diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index c22f3b4..fb4aded 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,27 +6,1385 @@ $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( - 'App\\Docs\\OpenApiSpec' => $baseDir . '/app/Docs/OpenApiSpec.php', + 'AWS\\CRT\\Auth\\AwsCredentials' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Auth/AwsCredentials.php', + 'AWS\\CRT\\Auth\\CredentialsProvider' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Auth/CredentialsProvider.php', + 'AWS\\CRT\\Auth\\Signable' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Auth/Signable.php', + 'AWS\\CRT\\Auth\\SignatureType' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Auth/SignatureType.php', + 'AWS\\CRT\\Auth\\SignedBodyHeaderType' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Auth/SignedBodyHeaderType.php', + 'AWS\\CRT\\Auth\\Signing' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Auth/Signing.php', + 'AWS\\CRT\\Auth\\SigningAlgorithm' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Auth/SigningAlgorithm.php', + 'AWS\\CRT\\Auth\\SigningConfigAWS' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Auth/SigningConfigAWS.php', + 'AWS\\CRT\\Auth\\SigningResult' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Auth/SigningResult.php', + 'AWS\\CRT\\Auth\\StaticCredentialsProvider' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Auth/StaticCredentialsProvider.php', + 'AWS\\CRT\\CRT' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/CRT.php', + 'AWS\\CRT\\HTTP\\Headers' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/HTTP/Headers.php', + 'AWS\\CRT\\HTTP\\Message' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/HTTP/Message.php', + 'AWS\\CRT\\HTTP\\Request' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/HTTP/Request.php', + 'AWS\\CRT\\HTTP\\Response' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/HTTP/Response.php', + 'AWS\\CRT\\IO\\EventLoopGroup' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/IO/EventLoopGroup.php', + 'AWS\\CRT\\IO\\InputStream' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/IO/InputStream.php', + 'AWS\\CRT\\Internal\\Encoding' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Internal/Encoding.php', + 'AWS\\CRT\\Internal\\Extension' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Internal/Extension.php', + 'AWS\\CRT\\Log' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Log.php', + 'AWS\\CRT\\NativeResource' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/NativeResource.php', + 'AWS\\CRT\\OptionValue' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Options.php', + 'AWS\\CRT\\Options' => $vendorDir . '/aws/aws-crt-php/src/AWS/CRT/Options.php', + 'App\\Enums\\TicketStatus' => $baseDir . '/app/Enums/TicketStatus.php', + 'App\\Enums\\UserAccessLevel' => $baseDir . '/app/Enums/UserAccessLevel.php', 'App\\Http\\Controllers\\ApiController' => $baseDir . '/app/Http/Controllers/ApiController.php', + 'App\\Http\\Controllers\\Audit\\AuditLogController' => $baseDir . '/app/Http/Controllers/Audit/AuditLogController.php', 'App\\Http\\Controllers\\AuthController' => $baseDir . '/app/Http/Controllers/AuthController.php', 'App\\Http\\Controllers\\Controller' => $baseDir . '/app/Http/Controllers/Controller.php', - 'App\\Http\\Controllers\\TicketController' => $baseDir . '/app/Http/Controllers/TicketController.php', + 'App\\Http\\Controllers\\Material\\MaterialController' => $baseDir . '/app/Http/Controllers/Material/MaterialController.php', + 'App\\Http\\Controllers\\Menu\\MenuController' => $baseDir . '/app/Http/Controllers/Menu/MenuController.php', + 'App\\Http\\Controllers\\Menu\\MenuGroupController' => $baseDir . '/app/Http/Controllers/Menu/MenuGroupController.php', + 'App\\Http\\Controllers\\Menu\\MenuListController' => $baseDir . '/app/Http/Controllers/Menu/MenuListController.php', + 'App\\Http\\Controllers\\Profile\\ProfileController' => $baseDir . '/app/Http/Controllers/Profile/ProfileController.php', + 'App\\Http\\Controllers\\Tenant\\TenantController' => $baseDir . '/app/Http/Controllers/Tenant/TenantController.php', 'App\\Http\\Controllers\\Ticket\\TicketController' => $baseDir . '/app/Http/Controllers/Ticket/TicketController.php', + 'App\\Http\\Controllers\\Ticket\\TicketIncidentTypeController' => $baseDir . '/app/Http/Controllers/Ticket/TicketIncidentTypeController.php', + 'App\\Http\\Controllers\\Ticket\\TicketTypeController' => $baseDir . '/app/Http/Controllers/Ticket/TicketTypeController.php', + 'App\\Http\\Controllers\\Ticket\\TicketWorkflowController' => $baseDir . '/app/Http/Controllers/Ticket/TicketWorkflowController.php', + 'App\\Http\\Controllers\\User\\UserController' => $baseDir . '/app/Http/Controllers/User/UserController.php', + 'App\\Http\\Middleware\\EnsureAccessLevel' => $baseDir . '/app/Http/Middleware/EnsureAccessLevel.php', + 'App\\Http\\Middleware\\EnsureMenuPermission' => $baseDir . '/app/Http/Middleware/EnsureMenuPermission.php', + 'App\\Http\\Middleware\\EnsureMenuResourcePermission' => $baseDir . '/app/Http/Middleware/EnsureMenuResourcePermission.php', + 'App\\Http\\Middleware\\ResolveTenantContext' => $baseDir . '/app/Http/Middleware/ResolveTenantContext.php', + 'App\\Http\\Requests\\Audit\\AuditLogFilterRequest' => $baseDir . '/app/Http/Requests/Audit/AuditLogFilterRequest.php', + 'App\\Http\\Requests\\Auth\\RegisterRequest' => $baseDir . '/app/Http/Requests/Auth/RegisterRequest.php', + 'App\\Http\\Requests\\Auth\\ResendVerificationCodeRequest' => $baseDir . '/app/Http/Requests/Auth/ResendVerificationCodeRequest.php', + 'App\\Http\\Requests\\Auth\\VerifyRegistrationRequest' => $baseDir . '/app/Http/Requests/Auth/VerifyRegistrationRequest.php', + 'App\\Http\\Requests\\Material\\AssignMaterialRequest' => $baseDir . '/app/Http/Requests/Material/AssignMaterialRequest.php', + 'App\\Http\\Requests\\Material\\IndexMaterialRequest' => $baseDir . '/app/Http/Requests/Material/IndexMaterialRequest.php', + 'App\\Http\\Requests\\Material\\ReturnMaterialRequest' => $baseDir . '/app/Http/Requests/Material/ReturnMaterialRequest.php', + 'App\\Http\\Requests\\Material\\TransferMaterialRequest' => $baseDir . '/app/Http/Requests/Material/TransferMaterialRequest.php', + 'App\\Http\\Requests\\MenuGroup\\IndexMenuGroupRequest' => $baseDir . '/app/Http/Requests/MenuGroup/IndexMenuGroupRequest.php', + 'App\\Http\\Requests\\MenuGroup\\StoreMenuGroupRequest' => $baseDir . '/app/Http/Requests/MenuGroup/StoreMenuGroupRequest.php', + 'App\\Http\\Requests\\MenuGroup\\SyncMenuGroupMenusRequest' => $baseDir . '/app/Http/Requests/MenuGroup/SyncMenuGroupMenusRequest.php', + 'App\\Http\\Requests\\MenuGroup\\SyncUserMenuGroupsRequest' => $baseDir . '/app/Http/Requests/MenuGroup/SyncUserMenuGroupsRequest.php', + 'App\\Http\\Requests\\MenuGroup\\UpdateMenuGroupRequest' => $baseDir . '/app/Http/Requests/MenuGroup/UpdateMenuGroupRequest.php', + 'App\\Http\\Requests\\MenuList\\IndexMenuListRequest' => $baseDir . '/app/Http/Requests/MenuList/IndexMenuListRequest.php', + 'App\\Http\\Requests\\MenuList\\StoreMenuListRequest' => $baseDir . '/app/Http/Requests/MenuList/StoreMenuListRequest.php', + 'App\\Http\\Requests\\MenuList\\UpdateMenuListRequest' => $baseDir . '/app/Http/Requests/MenuList/UpdateMenuListRequest.php', + 'App\\Http\\Requests\\Profile\\UpdateProfileRequest' => $baseDir . '/app/Http/Requests/Profile/UpdateProfileRequest.php', + 'App\\Http\\Requests\\Tenant\\IndexTenantRequest' => $baseDir . '/app/Http/Requests/Tenant/IndexTenantRequest.php', + 'App\\Http\\Requests\\Tenant\\StoreTenantRequest' => $baseDir . '/app/Http/Requests/Tenant/StoreTenantRequest.php', + 'App\\Http\\Requests\\Tenant\\UpdateTenantRequest' => $baseDir . '/app/Http/Requests/Tenant/UpdateTenantRequest.php', + 'App\\Http\\Requests\\TicketIncidentType\\IndexTicketIncidentTypeRequest' => $baseDir . '/app/Http/Requests/TicketIncidentType/IndexTicketIncidentTypeRequest.php', + 'App\\Http\\Requests\\TicketIncidentType\\StoreTicketIncidentTypeRequest' => $baseDir . '/app/Http/Requests/TicketIncidentType/StoreTicketIncidentTypeRequest.php', + 'App\\Http\\Requests\\TicketIncidentType\\UpdateTicketIncidentTypeRequest' => $baseDir . '/app/Http/Requests/TicketIncidentType/UpdateTicketIncidentTypeRequest.php', + 'App\\Http\\Requests\\TicketType\\IndexTicketTypeRequest' => $baseDir . '/app/Http/Requests/TicketType/IndexTicketTypeRequest.php', + 'App\\Http\\Requests\\TicketType\\StoreTicketTypeRequest' => $baseDir . '/app/Http/Requests/TicketType/StoreTicketTypeRequest.php', + 'App\\Http\\Requests\\TicketType\\UpdateTicketTypeRequest' => $baseDir . '/app/Http/Requests/TicketType/UpdateTicketTypeRequest.php', + 'App\\Http\\Requests\\Ticket\\ApproveTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/ApproveTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\AssignTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/AssignTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\CancelTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/CancelTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\CloseTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/CloseTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\EscalateTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/EscalateTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\IndexTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/IndexTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\PendingTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/PendingTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\ReassignTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/ReassignTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\RejectTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/RejectTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\ResolveTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/ResolveTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\StartTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/StartTicketRequest.php', 'App\\Http\\Requests\\Ticket\\StoreTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/StoreTicketRequest.php', 'App\\Http\\Requests\\Ticket\\UpdateTicketRequest' => $baseDir . '/app/Http/Requests/Ticket/UpdateTicketRequest.php', - 'App\\Http\\Resources\\TicketResource' => $baseDir . '/app/Http/Resources/TicketResource.php', + 'App\\Http\\Requests\\User\\IndexUserRequest' => $baseDir . '/app/Http/Requests/User/IndexUserRequest.php', + 'App\\Http\\Requests\\User\\StoreUserRequest' => $baseDir . '/app/Http/Requests/User/StoreUserRequest.php', + 'App\\Http\\Requests\\User\\UpdateUserRequest' => $baseDir . '/app/Http/Requests/User/UpdateUserRequest.php', + 'App\\Http\\Resources\\Audit\\AuditLogResource' => $baseDir . '/app/Http/Resources/Audit/AuditLogResource.php', + 'App\\Http\\Resources\\Material\\MaterialResource' => $baseDir . '/app/Http/Resources/Material/MaterialResource.php', + 'App\\Http\\Resources\\MenuGroup\\MenuGroupResource' => $baseDir . '/app/Http/Resources/MenuGroup/MenuGroupResource.php', + 'App\\Http\\Resources\\MenuList\\MenuListResource' => $baseDir . '/app/Http/Resources/MenuList/MenuListResource.php', + 'App\\Http\\Resources\\Tenant\\TenantResource' => $baseDir . '/app/Http/Resources/Tenant/TenantResource.php', + 'App\\Http\\Resources\\TicketIncidentType\\TicketIncidentTypeResource' => $baseDir . '/app/Http/Resources/TicketIncidentType/TicketIncidentTypeResource.php', + 'App\\Http\\Resources\\TicketType\\TicketTypeResource' => $baseDir . '/app/Http/Resources/TicketType/TicketTypeResource.php', + 'App\\Http\\Resources\\Ticket\\TicketResource' => $baseDir . '/app/Http/Resources/Ticket/TicketResource.php', + 'App\\Http\\Resources\\User\\UserResource' => $baseDir . '/app/Http/Resources/User/UserResource.php', + 'App\\Models\\AuditLog' => $baseDir . '/app/Models/AuditLog.php', + 'App\\Models\\MaterialMovement' => $baseDir . '/app/Models/MaterialMovement.php', + 'App\\Models\\MenuGroup' => $baseDir . '/app/Models/MenuGroup.php', + 'App\\Models\\MenuGroupMenu' => $baseDir . '/app/Models/MenuGroupMenu.php', + 'App\\Models\\MenuList' => $baseDir . '/app/Models/MenuList.php', + 'App\\Models\\TechnicianConsumable' => $baseDir . '/app/Models/TechnicianConsumable.php', + 'App\\Models\\TechnicianMaterial' => $baseDir . '/app/Models/TechnicianMaterial.php', + 'App\\Models\\Tenant' => $baseDir . '/app/Models/Tenant.php', 'App\\Models\\Ticket' => $baseDir . '/app/Models/Ticket.php', 'App\\Models\\TicketApproval' => $baseDir . '/app/Models/TicketApproval.php', 'App\\Models\\TicketAssignment' => $baseDir . '/app/Models/TicketAssignment.php', 'App\\Models\\TicketFile' => $baseDir . '/app/Models/TicketFile.php', + 'App\\Models\\TicketIncidentType' => $baseDir . '/app/Models/TicketIncidentType.php', 'App\\Models\\TicketLog' => $baseDir . '/app/Models/TicketLog.php', 'App\\Models\\TicketMaterial' => $baseDir . '/app/Models/TicketMaterial.php', 'App\\Models\\TicketType' => $baseDir . '/app/Models/TicketType.php', 'App\\Models\\User' => $baseDir . '/app/Models/User.php', + 'App\\Models\\UserMenuGroup' => $baseDir . '/app/Models/UserMenuGroup.php', + 'App\\Models\\UserProfile' => $baseDir . '/app/Models/UserProfile.php', + 'App\\Models\\UserTenant' => $baseDir . '/app/Models/UserTenant.php', 'App\\Providers\\AppServiceProvider' => $baseDir . '/app/Providers/AppServiceProvider.php', + 'App\\Services\\Access\\AccessService' => $baseDir . '/app/Services/Access/AccessService.php', + 'App\\Services\\Audit\\AuditLogService' => $baseDir . '/app/Services/Audit/AuditLogService.php', + 'App\\Services\\Auth\\AuthService' => $baseDir . '/app/Services/Auth/AuthService.php', + 'App\\Services\\Material\\MaterialService' => $baseDir . '/app/Services/Material/MaterialService.php', + 'App\\Services\\Menu\\MenuGroupService' => $baseDir . '/app/Services/Menu/MenuGroupService.php', + 'App\\Services\\Menu\\MenuListService' => $baseDir . '/app/Services/Menu/MenuListService.php', + 'App\\Services\\Tenant\\TenantService' => $baseDir . '/app/Services/Tenant/TenantService.php', + 'App\\Services\\Ticket\\TicketIncidentTypeService' => $baseDir . '/app/Services/Ticket/TicketIncidentTypeService.php', 'App\\Services\\Ticket\\TicketService' => $baseDir . '/app/Services/Ticket/TicketService.php', + 'App\\Services\\Ticket\\TicketTypeService' => $baseDir . '/app/Services/Ticket/TicketTypeService.php', + 'App\\Services\\Ticket\\TicketWorkflowService' => $baseDir . '/app/Services/Ticket/TicketWorkflowService.php', + 'App\\Services\\Ticket\\TicketWorkflowValidator' => $baseDir . '/app/Services/Ticket/TicketWorkflowValidator.php', + 'App\\Services\\User\\UserService' => $baseDir . '/app/Services/User/UserService.php', 'App\\Traits\\ApiResponse' => $baseDir . '/app/Traits/ApiResponse.php', + 'App\\Traits\\Auditable' => $baseDir . '/app/Traits/Auditable.php', 'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', + 'Aws\\ACMPCA\\ACMPCAClient' => $vendorDir . '/aws/aws-sdk-php/src/ACMPCA/ACMPCAClient.php', + 'Aws\\ACMPCA\\Exception\\ACMPCAException' => $vendorDir . '/aws/aws-sdk-php/src/ACMPCA/Exception/ACMPCAException.php', + 'Aws\\AIOps\\AIOpsClient' => $vendorDir . '/aws/aws-sdk-php/src/AIOps/AIOpsClient.php', + 'Aws\\AIOps\\Exception\\AIOpsException' => $vendorDir . '/aws/aws-sdk-php/src/AIOps/Exception/AIOpsException.php', + 'Aws\\ARCRegionSwitch\\ARCRegionSwitchClient' => $vendorDir . '/aws/aws-sdk-php/src/ARCRegionSwitch/ARCRegionSwitchClient.php', + 'Aws\\ARCRegionSwitch\\Exception\\ARCRegionSwitchException' => $vendorDir . '/aws/aws-sdk-php/src/ARCRegionSwitch/Exception/ARCRegionSwitchException.php', + 'Aws\\ARCZonalShift\\ARCZonalShiftClient' => $vendorDir . '/aws/aws-sdk-php/src/ARCZonalShift/ARCZonalShiftClient.php', + 'Aws\\ARCZonalShift\\Exception\\ARCZonalShiftException' => $vendorDir . '/aws/aws-sdk-php/src/ARCZonalShift/Exception/ARCZonalShiftException.php', + 'Aws\\AbstractConfigurationProvider' => $vendorDir . '/aws/aws-sdk-php/src/AbstractConfigurationProvider.php', + 'Aws\\AccessAnalyzer\\AccessAnalyzerClient' => $vendorDir . '/aws/aws-sdk-php/src/AccessAnalyzer/AccessAnalyzerClient.php', + 'Aws\\AccessAnalyzer\\Exception\\AccessAnalyzerException' => $vendorDir . '/aws/aws-sdk-php/src/AccessAnalyzer/Exception/AccessAnalyzerException.php', + 'Aws\\Account\\AccountClient' => $vendorDir . '/aws/aws-sdk-php/src/Account/AccountClient.php', + 'Aws\\Account\\Exception\\AccountException' => $vendorDir . '/aws/aws-sdk-php/src/Account/Exception/AccountException.php', + 'Aws\\Acm\\AcmClient' => $vendorDir . '/aws/aws-sdk-php/src/Acm/AcmClient.php', + 'Aws\\Acm\\Exception\\AcmException' => $vendorDir . '/aws/aws-sdk-php/src/Acm/Exception/AcmException.php', + 'Aws\\AmplifyBackend\\AmplifyBackendClient' => $vendorDir . '/aws/aws-sdk-php/src/AmplifyBackend/AmplifyBackendClient.php', + 'Aws\\AmplifyBackend\\Exception\\AmplifyBackendException' => $vendorDir . '/aws/aws-sdk-php/src/AmplifyBackend/Exception/AmplifyBackendException.php', + 'Aws\\AmplifyUIBuilder\\AmplifyUIBuilderClient' => $vendorDir . '/aws/aws-sdk-php/src/AmplifyUIBuilder/AmplifyUIBuilderClient.php', + 'Aws\\AmplifyUIBuilder\\Exception\\AmplifyUIBuilderException' => $vendorDir . '/aws/aws-sdk-php/src/AmplifyUIBuilder/Exception/AmplifyUIBuilderException.php', + 'Aws\\Amplify\\AmplifyClient' => $vendorDir . '/aws/aws-sdk-php/src/Amplify/AmplifyClient.php', + 'Aws\\Amplify\\Exception\\AmplifyException' => $vendorDir . '/aws/aws-sdk-php/src/Amplify/Exception/AmplifyException.php', + 'Aws\\ApiGatewayManagementApi\\ApiGatewayManagementApiClient' => $vendorDir . '/aws/aws-sdk-php/src/ApiGatewayManagementApi/ApiGatewayManagementApiClient.php', + 'Aws\\ApiGatewayManagementApi\\Exception\\ApiGatewayManagementApiException' => $vendorDir . '/aws/aws-sdk-php/src/ApiGatewayManagementApi/Exception/ApiGatewayManagementApiException.php', + 'Aws\\ApiGatewayV2\\ApiGatewayV2Client' => $vendorDir . '/aws/aws-sdk-php/src/ApiGatewayV2/ApiGatewayV2Client.php', + 'Aws\\ApiGatewayV2\\Exception\\ApiGatewayV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/ApiGatewayV2/Exception/ApiGatewayV2Exception.php', + 'Aws\\ApiGateway\\ApiGatewayClient' => $vendorDir . '/aws/aws-sdk-php/src/ApiGateway/ApiGatewayClient.php', + 'Aws\\ApiGateway\\Exception\\ApiGatewayException' => $vendorDir . '/aws/aws-sdk-php/src/ApiGateway/Exception/ApiGatewayException.php', + 'Aws\\Api\\AbstractModel' => $vendorDir . '/aws/aws-sdk-php/src/Api/AbstractModel.php', + 'Aws\\Api\\ApiProvider' => $vendorDir . '/aws/aws-sdk-php/src/Api/ApiProvider.php', + 'Aws\\Api\\Cbor\\CborDecoder' => $vendorDir . '/aws/aws-sdk-php/src/Api/Cbor/CborDecoder.php', + 'Aws\\Api\\Cbor\\CborEncoder' => $vendorDir . '/aws/aws-sdk-php/src/Api/Cbor/CborEncoder.php', + 'Aws\\Api\\Cbor\\Exception\\CborException' => $vendorDir . '/aws/aws-sdk-php/src/Api/Cbor/Exception/CborException.php', + 'Aws\\Api\\DateTimeResult' => $vendorDir . '/aws/aws-sdk-php/src/Api/DateTimeResult.php', + 'Aws\\Api\\DocModel' => $vendorDir . '/aws/aws-sdk-php/src/Api/DocModel.php', + 'Aws\\Api\\ErrorParser\\AbstractErrorParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/ErrorParser/AbstractErrorParser.php', + 'Aws\\Api\\ErrorParser\\AbstractRpcV2ErrorParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/ErrorParser/AbstractRpcV2ErrorParser.php', + 'Aws\\Api\\ErrorParser\\JsonParserTrait' => $vendorDir . '/aws/aws-sdk-php/src/Api/ErrorParser/JsonParserTrait.php', + 'Aws\\Api\\ErrorParser\\JsonRpcErrorParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/ErrorParser/JsonRpcErrorParser.php', + 'Aws\\Api\\ErrorParser\\RestJsonErrorParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/ErrorParser/RestJsonErrorParser.php', + 'Aws\\Api\\ErrorParser\\RpcV2CborErrorParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/ErrorParser/RpcV2CborErrorParser.php', + 'Aws\\Api\\ErrorParser\\XmlErrorParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/ErrorParser/XmlErrorParser.php', + 'Aws\\Api\\Exception\\RpcV2CborException' => $vendorDir . '/aws/aws-sdk-php/src/Api/Exception/RpcV2CborException.php', + 'Aws\\Api\\ListShape' => $vendorDir . '/aws/aws-sdk-php/src/Api/ListShape.php', + 'Aws\\Api\\MapShape' => $vendorDir . '/aws/aws-sdk-php/src/Api/MapShape.php', + 'Aws\\Api\\Operation' => $vendorDir . '/aws/aws-sdk-php/src/Api/Operation.php', + 'Aws\\Api\\Parser\\AbstractParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/AbstractParser.php', + 'Aws\\Api\\Parser\\AbstractRestParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/AbstractRestParser.php', + 'Aws\\Api\\Parser\\AbstractRpcV2Parser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/AbstractRpcV2Parser.php', + 'Aws\\Api\\Parser\\Crc32ValidatingParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/Crc32ValidatingParser.php', + 'Aws\\Api\\Parser\\DecodingEventStreamIterator' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/DecodingEventStreamIterator.php', + 'Aws\\Api\\Parser\\EventParsingIterator' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/EventParsingIterator.php', + 'Aws\\Api\\Parser\\Exception\\ParserException' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/Exception/ParserException.php', + 'Aws\\Api\\Parser\\JsonParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/JsonParser.php', + 'Aws\\Api\\Parser\\JsonRpcParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/JsonRpcParser.php', + 'Aws\\Api\\Parser\\MetadataParserTrait' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/MetadataParserTrait.php', + 'Aws\\Api\\Parser\\NonSeekableStreamDecodingEventStreamIterator' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/NonSeekableStreamDecodingEventStreamIterator.php', + 'Aws\\Api\\Parser\\PayloadParserTrait' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/PayloadParserTrait.php', + 'Aws\\Api\\Parser\\QueryParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/QueryParser.php', + 'Aws\\Api\\Parser\\RestJsonParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/RestJsonParser.php', + 'Aws\\Api\\Parser\\RestXmlParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/RestXmlParser.php', + 'Aws\\Api\\Parser\\RpcV2CborParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/RpcV2CborParser.php', + 'Aws\\Api\\Parser\\RpcV2ParserTrait' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/RpcV2ParserTrait.php', + 'Aws\\Api\\Parser\\XmlParser' => $vendorDir . '/aws/aws-sdk-php/src/Api/Parser/XmlParser.php', + 'Aws\\Api\\Serializer\\AbstractRpcV2Serializer' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/AbstractRpcV2Serializer.php', + 'Aws\\Api\\Serializer\\Ec2ParamBuilder' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/Ec2ParamBuilder.php', + 'Aws\\Api\\Serializer\\JsonBody' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/JsonBody.php', + 'Aws\\Api\\Serializer\\JsonRpcSerializer' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/JsonRpcSerializer.php', + 'Aws\\Api\\Serializer\\QueryParamBuilder' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/QueryParamBuilder.php', + 'Aws\\Api\\Serializer\\QuerySerializer' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/QuerySerializer.php', + 'Aws\\Api\\Serializer\\RestJsonSerializer' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/RestJsonSerializer.php', + 'Aws\\Api\\Serializer\\RestSerializer' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/RestSerializer.php', + 'Aws\\Api\\Serializer\\RestXmlSerializer' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/RestXmlSerializer.php', + 'Aws\\Api\\Serializer\\RpcV2CborSerializer' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/RpcV2CborSerializer.php', + 'Aws\\Api\\Serializer\\XmlBody' => $vendorDir . '/aws/aws-sdk-php/src/Api/Serializer/XmlBody.php', + 'Aws\\Api\\Service' => $vendorDir . '/aws/aws-sdk-php/src/Api/Service.php', + 'Aws\\Api\\Shape' => $vendorDir . '/aws/aws-sdk-php/src/Api/Shape.php', + 'Aws\\Api\\ShapeMap' => $vendorDir . '/aws/aws-sdk-php/src/Api/ShapeMap.php', + 'Aws\\Api\\StructureShape' => $vendorDir . '/aws/aws-sdk-php/src/Api/StructureShape.php', + 'Aws\\Api\\SupportedProtocols' => $vendorDir . '/aws/aws-sdk-php/src/Api/SupportedProtocols.php', + 'Aws\\Api\\TimestampShape' => $vendorDir . '/aws/aws-sdk-php/src/Api/TimestampShape.php', + 'Aws\\Api\\Validator' => $vendorDir . '/aws/aws-sdk-php/src/Api/Validator.php', + 'Aws\\AppConfigData\\AppConfigDataClient' => $vendorDir . '/aws/aws-sdk-php/src/AppConfigData/AppConfigDataClient.php', + 'Aws\\AppConfigData\\Exception\\AppConfigDataException' => $vendorDir . '/aws/aws-sdk-php/src/AppConfigData/Exception/AppConfigDataException.php', + 'Aws\\AppConfig\\AppConfigClient' => $vendorDir . '/aws/aws-sdk-php/src/AppConfig/AppConfigClient.php', + 'Aws\\AppConfig\\Exception\\AppConfigException' => $vendorDir . '/aws/aws-sdk-php/src/AppConfig/Exception/AppConfigException.php', + 'Aws\\AppFabric\\AppFabricClient' => $vendorDir . '/aws/aws-sdk-php/src/AppFabric/AppFabricClient.php', + 'Aws\\AppFabric\\Exception\\AppFabricException' => $vendorDir . '/aws/aws-sdk-php/src/AppFabric/Exception/AppFabricException.php', + 'Aws\\AppIntegrationsService\\AppIntegrationsServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/AppIntegrationsService/AppIntegrationsServiceClient.php', + 'Aws\\AppIntegrationsService\\Exception\\AppIntegrationsServiceException' => $vendorDir . '/aws/aws-sdk-php/src/AppIntegrationsService/Exception/AppIntegrationsServiceException.php', + 'Aws\\AppMesh\\AppMeshClient' => $vendorDir . '/aws/aws-sdk-php/src/AppMesh/AppMeshClient.php', + 'Aws\\AppMesh\\Exception\\AppMeshException' => $vendorDir . '/aws/aws-sdk-php/src/AppMesh/Exception/AppMeshException.php', + 'Aws\\AppRegistry\\AppRegistryClient' => $vendorDir . '/aws/aws-sdk-php/src/AppRegistry/AppRegistryClient.php', + 'Aws\\AppRegistry\\Exception\\AppRegistryException' => $vendorDir . '/aws/aws-sdk-php/src/AppRegistry/Exception/AppRegistryException.php', + 'Aws\\AppRunner\\AppRunnerClient' => $vendorDir . '/aws/aws-sdk-php/src/AppRunner/AppRunnerClient.php', + 'Aws\\AppRunner\\Exception\\AppRunnerException' => $vendorDir . '/aws/aws-sdk-php/src/AppRunner/Exception/AppRunnerException.php', + 'Aws\\AppSync\\AppSyncClient' => $vendorDir . '/aws/aws-sdk-php/src/AppSync/AppSyncClient.php', + 'Aws\\AppSync\\Exception\\AppSyncException' => $vendorDir . '/aws/aws-sdk-php/src/AppSync/Exception/AppSyncException.php', + 'Aws\\Appflow\\AppflowClient' => $vendorDir . '/aws/aws-sdk-php/src/Appflow/AppflowClient.php', + 'Aws\\Appflow\\Exception\\AppflowException' => $vendorDir . '/aws/aws-sdk-php/src/Appflow/Exception/AppflowException.php', + 'Aws\\ApplicationAutoScaling\\ApplicationAutoScalingClient' => $vendorDir . '/aws/aws-sdk-php/src/ApplicationAutoScaling/ApplicationAutoScalingClient.php', + 'Aws\\ApplicationAutoScaling\\Exception\\ApplicationAutoScalingException' => $vendorDir . '/aws/aws-sdk-php/src/ApplicationAutoScaling/Exception/ApplicationAutoScalingException.php', + 'Aws\\ApplicationCostProfiler\\ApplicationCostProfilerClient' => $vendorDir . '/aws/aws-sdk-php/src/ApplicationCostProfiler/ApplicationCostProfilerClient.php', + 'Aws\\ApplicationCostProfiler\\Exception\\ApplicationCostProfilerException' => $vendorDir . '/aws/aws-sdk-php/src/ApplicationCostProfiler/Exception/ApplicationCostProfilerException.php', + 'Aws\\ApplicationDiscoveryService\\ApplicationDiscoveryServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/ApplicationDiscoveryService/ApplicationDiscoveryServiceClient.php', + 'Aws\\ApplicationDiscoveryService\\Exception\\ApplicationDiscoveryServiceException' => $vendorDir . '/aws/aws-sdk-php/src/ApplicationDiscoveryService/Exception/ApplicationDiscoveryServiceException.php', + 'Aws\\ApplicationInsights\\ApplicationInsightsClient' => $vendorDir . '/aws/aws-sdk-php/src/ApplicationInsights/ApplicationInsightsClient.php', + 'Aws\\ApplicationInsights\\Exception\\ApplicationInsightsException' => $vendorDir . '/aws/aws-sdk-php/src/ApplicationInsights/Exception/ApplicationInsightsException.php', + 'Aws\\ApplicationSignals\\ApplicationSignalsClient' => $vendorDir . '/aws/aws-sdk-php/src/ApplicationSignals/ApplicationSignalsClient.php', + 'Aws\\ApplicationSignals\\Exception\\ApplicationSignalsException' => $vendorDir . '/aws/aws-sdk-php/src/ApplicationSignals/Exception/ApplicationSignalsException.php', + 'Aws\\Appstream\\AppstreamClient' => $vendorDir . '/aws/aws-sdk-php/src/Appstream/AppstreamClient.php', + 'Aws\\Appstream\\Exception\\AppstreamException' => $vendorDir . '/aws/aws-sdk-php/src/Appstream/Exception/AppstreamException.php', + 'Aws\\Arn\\AccessPointArn' => $vendorDir . '/aws/aws-sdk-php/src/Arn/AccessPointArn.php', + 'Aws\\Arn\\AccessPointArnInterface' => $vendorDir . '/aws/aws-sdk-php/src/Arn/AccessPointArnInterface.php', + 'Aws\\Arn\\Arn' => $vendorDir . '/aws/aws-sdk-php/src/Arn/Arn.php', + 'Aws\\Arn\\ArnInterface' => $vendorDir . '/aws/aws-sdk-php/src/Arn/ArnInterface.php', + 'Aws\\Arn\\ArnParser' => $vendorDir . '/aws/aws-sdk-php/src/Arn/ArnParser.php', + 'Aws\\Arn\\Exception\\InvalidArnException' => $vendorDir . '/aws/aws-sdk-php/src/Arn/Exception/InvalidArnException.php', + 'Aws\\Arn\\ObjectLambdaAccessPointArn' => $vendorDir . '/aws/aws-sdk-php/src/Arn/ObjectLambdaAccessPointArn.php', + 'Aws\\Arn\\ResourceTypeAndIdTrait' => $vendorDir . '/aws/aws-sdk-php/src/Arn/ResourceTypeAndIdTrait.php', + 'Aws\\Arn\\S3\\AccessPointArn' => $vendorDir . '/aws/aws-sdk-php/src/Arn/S3/AccessPointArn.php', + 'Aws\\Arn\\S3\\BucketArnInterface' => $vendorDir . '/aws/aws-sdk-php/src/Arn/S3/BucketArnInterface.php', + 'Aws\\Arn\\S3\\MultiRegionAccessPointArn' => $vendorDir . '/aws/aws-sdk-php/src/Arn/S3/MultiRegionAccessPointArn.php', + 'Aws\\Arn\\S3\\OutpostsAccessPointArn' => $vendorDir . '/aws/aws-sdk-php/src/Arn/S3/OutpostsAccessPointArn.php', + 'Aws\\Arn\\S3\\OutpostsArnInterface' => $vendorDir . '/aws/aws-sdk-php/src/Arn/S3/OutpostsArnInterface.php', + 'Aws\\Arn\\S3\\OutpostsBucketArn' => $vendorDir . '/aws/aws-sdk-php/src/Arn/S3/OutpostsBucketArn.php', + 'Aws\\Artifact\\ArtifactClient' => $vendorDir . '/aws/aws-sdk-php/src/Artifact/ArtifactClient.php', + 'Aws\\Artifact\\Exception\\ArtifactException' => $vendorDir . '/aws/aws-sdk-php/src/Artifact/Exception/ArtifactException.php', + 'Aws\\Athena\\AthenaClient' => $vendorDir . '/aws/aws-sdk-php/src/Athena/AthenaClient.php', + 'Aws\\Athena\\Exception\\AthenaException' => $vendorDir . '/aws/aws-sdk-php/src/Athena/Exception/AthenaException.php', + 'Aws\\AuditManager\\AuditManagerClient' => $vendorDir . '/aws/aws-sdk-php/src/AuditManager/AuditManagerClient.php', + 'Aws\\AuditManager\\Exception\\AuditManagerException' => $vendorDir . '/aws/aws-sdk-php/src/AuditManager/Exception/AuditManagerException.php', + 'Aws\\AugmentedAIRuntime\\AugmentedAIRuntimeClient' => $vendorDir . '/aws/aws-sdk-php/src/AugmentedAIRuntime/AugmentedAIRuntimeClient.php', + 'Aws\\AugmentedAIRuntime\\Exception\\AugmentedAIRuntimeException' => $vendorDir . '/aws/aws-sdk-php/src/AugmentedAIRuntime/Exception/AugmentedAIRuntimeException.php', + 'Aws\\Auth\\AuthSchemeResolver' => $vendorDir . '/aws/aws-sdk-php/src/Auth/AuthSchemeResolver.php', + 'Aws\\Auth\\AuthSchemeResolverInterface' => $vendorDir . '/aws/aws-sdk-php/src/Auth/AuthSchemeResolverInterface.php', + 'Aws\\Auth\\AuthSelectionMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/Auth/AuthSelectionMiddleware.php', + 'Aws\\Auth\\Exception\\UnresolvedAuthSchemeException' => $vendorDir . '/aws/aws-sdk-php/src/Auth/Exception/UnresolvedAuthSchemeException.php', + 'Aws\\AutoScalingPlans\\AutoScalingPlansClient' => $vendorDir . '/aws/aws-sdk-php/src/AutoScalingPlans/AutoScalingPlansClient.php', + 'Aws\\AutoScalingPlans\\Exception\\AutoScalingPlansException' => $vendorDir . '/aws/aws-sdk-php/src/AutoScalingPlans/Exception/AutoScalingPlansException.php', + 'Aws\\AutoScaling\\AutoScalingClient' => $vendorDir . '/aws/aws-sdk-php/src/AutoScaling/AutoScalingClient.php', + 'Aws\\AutoScaling\\Exception\\AutoScalingException' => $vendorDir . '/aws/aws-sdk-php/src/AutoScaling/Exception/AutoScalingException.php', + 'Aws\\AwsClient' => $vendorDir . '/aws/aws-sdk-php/src/AwsClient.php', + 'Aws\\AwsClientInterface' => $vendorDir . '/aws/aws-sdk-php/src/AwsClientInterface.php', + 'Aws\\AwsClientTrait' => $vendorDir . '/aws/aws-sdk-php/src/AwsClientTrait.php', + 'Aws\\B2bi\\B2biClient' => $vendorDir . '/aws/aws-sdk-php/src/B2bi/B2biClient.php', + 'Aws\\B2bi\\Exception\\B2biException' => $vendorDir . '/aws/aws-sdk-php/src/B2bi/Exception/B2biException.php', + 'Aws\\BCMDashboards\\BCMDashboardsClient' => $vendorDir . '/aws/aws-sdk-php/src/BCMDashboards/BCMDashboardsClient.php', + 'Aws\\BCMDashboards\\Exception\\BCMDashboardsException' => $vendorDir . '/aws/aws-sdk-php/src/BCMDashboards/Exception/BCMDashboardsException.php', + 'Aws\\BCMDataExports\\BCMDataExportsClient' => $vendorDir . '/aws/aws-sdk-php/src/BCMDataExports/BCMDataExportsClient.php', + 'Aws\\BCMDataExports\\Exception\\BCMDataExportsException' => $vendorDir . '/aws/aws-sdk-php/src/BCMDataExports/Exception/BCMDataExportsException.php', + 'Aws\\BCMPricingCalculator\\BCMPricingCalculatorClient' => $vendorDir . '/aws/aws-sdk-php/src/BCMPricingCalculator/BCMPricingCalculatorClient.php', + 'Aws\\BCMPricingCalculator\\Exception\\BCMPricingCalculatorException' => $vendorDir . '/aws/aws-sdk-php/src/BCMPricingCalculator/Exception/BCMPricingCalculatorException.php', + 'Aws\\BCMRecommendedActions\\BCMRecommendedActionsClient' => $vendorDir . '/aws/aws-sdk-php/src/BCMRecommendedActions/BCMRecommendedActionsClient.php', + 'Aws\\BCMRecommendedActions\\Exception\\BCMRecommendedActionsException' => $vendorDir . '/aws/aws-sdk-php/src/BCMRecommendedActions/Exception/BCMRecommendedActionsException.php', + 'Aws\\BackupGateway\\BackupGatewayClient' => $vendorDir . '/aws/aws-sdk-php/src/BackupGateway/BackupGatewayClient.php', + 'Aws\\BackupGateway\\Exception\\BackupGatewayException' => $vendorDir . '/aws/aws-sdk-php/src/BackupGateway/Exception/BackupGatewayException.php', + 'Aws\\BackupSearch\\BackupSearchClient' => $vendorDir . '/aws/aws-sdk-php/src/BackupSearch/BackupSearchClient.php', + 'Aws\\BackupSearch\\Exception\\BackupSearchException' => $vendorDir . '/aws/aws-sdk-php/src/BackupSearch/Exception/BackupSearchException.php', + 'Aws\\Backup\\BackupClient' => $vendorDir . '/aws/aws-sdk-php/src/Backup/BackupClient.php', + 'Aws\\Backup\\Exception\\BackupException' => $vendorDir . '/aws/aws-sdk-php/src/Backup/Exception/BackupException.php', + 'Aws\\Batch\\BatchClient' => $vendorDir . '/aws/aws-sdk-php/src/Batch/BatchClient.php', + 'Aws\\Batch\\Exception\\BatchException' => $vendorDir . '/aws/aws-sdk-php/src/Batch/Exception/BatchException.php', + 'Aws\\BedrockAgentCoreControl\\BedrockAgentCoreControlClient' => $vendorDir . '/aws/aws-sdk-php/src/BedrockAgentCoreControl/BedrockAgentCoreControlClient.php', + 'Aws\\BedrockAgentCoreControl\\Exception\\BedrockAgentCoreControlException' => $vendorDir . '/aws/aws-sdk-php/src/BedrockAgentCoreControl/Exception/BedrockAgentCoreControlException.php', + 'Aws\\BedrockAgentCore\\BedrockAgentCoreClient' => $vendorDir . '/aws/aws-sdk-php/src/BedrockAgentCore/BedrockAgentCoreClient.php', + 'Aws\\BedrockAgentCore\\Exception\\BedrockAgentCoreException' => $vendorDir . '/aws/aws-sdk-php/src/BedrockAgentCore/Exception/BedrockAgentCoreException.php', + 'Aws\\BedrockAgentRuntime\\BedrockAgentRuntimeClient' => $vendorDir . '/aws/aws-sdk-php/src/BedrockAgentRuntime/BedrockAgentRuntimeClient.php', + 'Aws\\BedrockAgentRuntime\\Exception\\BedrockAgentRuntimeException' => $vendorDir . '/aws/aws-sdk-php/src/BedrockAgentRuntime/Exception/BedrockAgentRuntimeException.php', + 'Aws\\BedrockAgent\\BedrockAgentClient' => $vendorDir . '/aws/aws-sdk-php/src/BedrockAgent/BedrockAgentClient.php', + 'Aws\\BedrockAgent\\Exception\\BedrockAgentException' => $vendorDir . '/aws/aws-sdk-php/src/BedrockAgent/Exception/BedrockAgentException.php', + 'Aws\\BedrockDataAutomationRuntime\\BedrockDataAutomationRuntimeClient' => $vendorDir . '/aws/aws-sdk-php/src/BedrockDataAutomationRuntime/BedrockDataAutomationRuntimeClient.php', + 'Aws\\BedrockDataAutomationRuntime\\Exception\\BedrockDataAutomationRuntimeException' => $vendorDir . '/aws/aws-sdk-php/src/BedrockDataAutomationRuntime/Exception/BedrockDataAutomationRuntimeException.php', + 'Aws\\BedrockDataAutomation\\BedrockDataAutomationClient' => $vendorDir . '/aws/aws-sdk-php/src/BedrockDataAutomation/BedrockDataAutomationClient.php', + 'Aws\\BedrockDataAutomation\\Exception\\BedrockDataAutomationException' => $vendorDir . '/aws/aws-sdk-php/src/BedrockDataAutomation/Exception/BedrockDataAutomationException.php', + 'Aws\\BedrockRuntime\\BedrockRuntimeClient' => $vendorDir . '/aws/aws-sdk-php/src/BedrockRuntime/BedrockRuntimeClient.php', + 'Aws\\BedrockRuntime\\Exception\\BedrockRuntimeException' => $vendorDir . '/aws/aws-sdk-php/src/BedrockRuntime/Exception/BedrockRuntimeException.php', + 'Aws\\Bedrock\\BedrockClient' => $vendorDir . '/aws/aws-sdk-php/src/Bedrock/BedrockClient.php', + 'Aws\\Bedrock\\Exception\\BedrockException' => $vendorDir . '/aws/aws-sdk-php/src/Bedrock/Exception/BedrockException.php', + 'Aws\\BillingConductor\\BillingConductorClient' => $vendorDir . '/aws/aws-sdk-php/src/BillingConductor/BillingConductorClient.php', + 'Aws\\BillingConductor\\Exception\\BillingConductorException' => $vendorDir . '/aws/aws-sdk-php/src/BillingConductor/Exception/BillingConductorException.php', + 'Aws\\Billing\\BillingClient' => $vendorDir . '/aws/aws-sdk-php/src/Billing/BillingClient.php', + 'Aws\\Billing\\Exception\\BillingException' => $vendorDir . '/aws/aws-sdk-php/src/Billing/Exception/BillingException.php', + 'Aws\\Braket\\BraketClient' => $vendorDir . '/aws/aws-sdk-php/src/Braket/BraketClient.php', + 'Aws\\Braket\\Exception\\BraketException' => $vendorDir . '/aws/aws-sdk-php/src/Braket/Exception/BraketException.php', + 'Aws\\Budgets\\BudgetsClient' => $vendorDir . '/aws/aws-sdk-php/src/Budgets/BudgetsClient.php', + 'Aws\\Budgets\\Exception\\BudgetsException' => $vendorDir . '/aws/aws-sdk-php/src/Budgets/Exception/BudgetsException.php', + 'Aws\\CacheInterface' => $vendorDir . '/aws/aws-sdk-php/src/CacheInterface.php', + 'Aws\\Cbor\\CborDecoder' => $vendorDir . '/aws/aws-sdk-php/src/Cbor/CborDecoder.php', + 'Aws\\Cbor\\CborEncoder' => $vendorDir . '/aws/aws-sdk-php/src/Cbor/CborEncoder.php', + 'Aws\\Cbor\\Exception\\CborException' => $vendorDir . '/aws/aws-sdk-php/src/Cbor/Exception/CborException.php', + 'Aws\\Chatbot\\ChatbotClient' => $vendorDir . '/aws/aws-sdk-php/src/Chatbot/ChatbotClient.php', + 'Aws\\Chatbot\\Exception\\ChatbotException' => $vendorDir . '/aws/aws-sdk-php/src/Chatbot/Exception/ChatbotException.php', + 'Aws\\ChimeSDKIdentity\\ChimeSDKIdentityClient' => $vendorDir . '/aws/aws-sdk-php/src/ChimeSDKIdentity/ChimeSDKIdentityClient.php', + 'Aws\\ChimeSDKIdentity\\Exception\\ChimeSDKIdentityException' => $vendorDir . '/aws/aws-sdk-php/src/ChimeSDKIdentity/Exception/ChimeSDKIdentityException.php', + 'Aws\\ChimeSDKMediaPipelines\\ChimeSDKMediaPipelinesClient' => $vendorDir . '/aws/aws-sdk-php/src/ChimeSDKMediaPipelines/ChimeSDKMediaPipelinesClient.php', + 'Aws\\ChimeSDKMediaPipelines\\Exception\\ChimeSDKMediaPipelinesException' => $vendorDir . '/aws/aws-sdk-php/src/ChimeSDKMediaPipelines/Exception/ChimeSDKMediaPipelinesException.php', + 'Aws\\ChimeSDKMeetings\\ChimeSDKMeetingsClient' => $vendorDir . '/aws/aws-sdk-php/src/ChimeSDKMeetings/ChimeSDKMeetingsClient.php', + 'Aws\\ChimeSDKMeetings\\Exception\\ChimeSDKMeetingsException' => $vendorDir . '/aws/aws-sdk-php/src/ChimeSDKMeetings/Exception/ChimeSDKMeetingsException.php', + 'Aws\\ChimeSDKMessaging\\ChimeSDKMessagingClient' => $vendorDir . '/aws/aws-sdk-php/src/ChimeSDKMessaging/ChimeSDKMessagingClient.php', + 'Aws\\ChimeSDKMessaging\\Exception\\ChimeSDKMessagingException' => $vendorDir . '/aws/aws-sdk-php/src/ChimeSDKMessaging/Exception/ChimeSDKMessagingException.php', + 'Aws\\ChimeSDKVoice\\ChimeSDKVoiceClient' => $vendorDir . '/aws/aws-sdk-php/src/ChimeSDKVoice/ChimeSDKVoiceClient.php', + 'Aws\\ChimeSDKVoice\\Exception\\ChimeSDKVoiceException' => $vendorDir . '/aws/aws-sdk-php/src/ChimeSDKVoice/Exception/ChimeSDKVoiceException.php', + 'Aws\\Chime\\ChimeClient' => $vendorDir . '/aws/aws-sdk-php/src/Chime/ChimeClient.php', + 'Aws\\Chime\\Exception\\ChimeException' => $vendorDir . '/aws/aws-sdk-php/src/Chime/Exception/ChimeException.php', + 'Aws\\CleanRoomsML\\CleanRoomsMLClient' => $vendorDir . '/aws/aws-sdk-php/src/CleanRoomsML/CleanRoomsMLClient.php', + 'Aws\\CleanRoomsML\\Exception\\CleanRoomsMLException' => $vendorDir . '/aws/aws-sdk-php/src/CleanRoomsML/Exception/CleanRoomsMLException.php', + 'Aws\\CleanRooms\\CleanRoomsClient' => $vendorDir . '/aws/aws-sdk-php/src/CleanRooms/CleanRoomsClient.php', + 'Aws\\CleanRooms\\Exception\\CleanRoomsException' => $vendorDir . '/aws/aws-sdk-php/src/CleanRooms/Exception/CleanRoomsException.php', + 'Aws\\ClientResolver' => $vendorDir . '/aws/aws-sdk-php/src/ClientResolver.php', + 'Aws\\ClientSideMonitoring\\AbstractMonitoringMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/ClientSideMonitoring/AbstractMonitoringMiddleware.php', + 'Aws\\ClientSideMonitoring\\ApiCallAttemptMonitoringMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/ClientSideMonitoring/ApiCallAttemptMonitoringMiddleware.php', + 'Aws\\ClientSideMonitoring\\ApiCallMonitoringMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/ClientSideMonitoring/ApiCallMonitoringMiddleware.php', + 'Aws\\ClientSideMonitoring\\Configuration' => $vendorDir . '/aws/aws-sdk-php/src/ClientSideMonitoring/Configuration.php', + 'Aws\\ClientSideMonitoring\\ConfigurationInterface' => $vendorDir . '/aws/aws-sdk-php/src/ClientSideMonitoring/ConfigurationInterface.php', + 'Aws\\ClientSideMonitoring\\ConfigurationProvider' => $vendorDir . '/aws/aws-sdk-php/src/ClientSideMonitoring/ConfigurationProvider.php', + 'Aws\\ClientSideMonitoring\\Exception\\ConfigurationException' => $vendorDir . '/aws/aws-sdk-php/src/ClientSideMonitoring/Exception/ConfigurationException.php', + 'Aws\\ClientSideMonitoring\\MonitoringMiddlewareInterface' => $vendorDir . '/aws/aws-sdk-php/src/ClientSideMonitoring/MonitoringMiddlewareInterface.php', + 'Aws\\Cloud9\\Cloud9Client' => $vendorDir . '/aws/aws-sdk-php/src/Cloud9/Cloud9Client.php', + 'Aws\\Cloud9\\Exception\\Cloud9Exception' => $vendorDir . '/aws/aws-sdk-php/src/Cloud9/Exception/Cloud9Exception.php', + 'Aws\\CloudControlApi\\CloudControlApiClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudControlApi/CloudControlApiClient.php', + 'Aws\\CloudControlApi\\Exception\\CloudControlApiException' => $vendorDir . '/aws/aws-sdk-php/src/CloudControlApi/Exception/CloudControlApiException.php', + 'Aws\\CloudDirectory\\CloudDirectoryClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudDirectory/CloudDirectoryClient.php', + 'Aws\\CloudDirectory\\Exception\\CloudDirectoryException' => $vendorDir . '/aws/aws-sdk-php/src/CloudDirectory/Exception/CloudDirectoryException.php', + 'Aws\\CloudFormation\\CloudFormationClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudFormation/CloudFormationClient.php', + 'Aws\\CloudFormation\\Exception\\CloudFormationException' => $vendorDir . '/aws/aws-sdk-php/src/CloudFormation/Exception/CloudFormationException.php', + 'Aws\\CloudFrontKeyValueStore\\CloudFrontKeyValueStoreClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudFrontKeyValueStore/CloudFrontKeyValueStoreClient.php', + 'Aws\\CloudFrontKeyValueStore\\Exception\\CloudFrontKeyValueStoreException' => $vendorDir . '/aws/aws-sdk-php/src/CloudFrontKeyValueStore/Exception/CloudFrontKeyValueStoreException.php', + 'Aws\\CloudFront\\CloudFrontClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudFront/CloudFrontClient.php', + 'Aws\\CloudFront\\CookieSigner' => $vendorDir . '/aws/aws-sdk-php/src/CloudFront/CookieSigner.php', + 'Aws\\CloudFront\\Exception\\CloudFrontException' => $vendorDir . '/aws/aws-sdk-php/src/CloudFront/Exception/CloudFrontException.php', + 'Aws\\CloudFront\\Signer' => $vendorDir . '/aws/aws-sdk-php/src/CloudFront/Signer.php', + 'Aws\\CloudFront\\UrlSigner' => $vendorDir . '/aws/aws-sdk-php/src/CloudFront/UrlSigner.php', + 'Aws\\CloudHSMV2\\CloudHSMV2Client' => $vendorDir . '/aws/aws-sdk-php/src/CloudHSMV2/CloudHSMV2Client.php', + 'Aws\\CloudHSMV2\\Exception\\CloudHSMV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/CloudHSMV2/Exception/CloudHSMV2Exception.php', + 'Aws\\CloudHsm\\CloudHsmClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudHsm/CloudHsmClient.php', + 'Aws\\CloudHsm\\Exception\\CloudHsmException' => $vendorDir . '/aws/aws-sdk-php/src/CloudHsm/Exception/CloudHsmException.php', + 'Aws\\CloudSearchDomain\\CloudSearchDomainClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudSearchDomain/CloudSearchDomainClient.php', + 'Aws\\CloudSearchDomain\\Exception\\CloudSearchDomainException' => $vendorDir . '/aws/aws-sdk-php/src/CloudSearchDomain/Exception/CloudSearchDomainException.php', + 'Aws\\CloudSearch\\CloudSearchClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudSearch/CloudSearchClient.php', + 'Aws\\CloudSearch\\Exception\\CloudSearchException' => $vendorDir . '/aws/aws-sdk-php/src/CloudSearch/Exception/CloudSearchException.php', + 'Aws\\CloudTrailData\\CloudTrailDataClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudTrailData/CloudTrailDataClient.php', + 'Aws\\CloudTrailData\\Exception\\CloudTrailDataException' => $vendorDir . '/aws/aws-sdk-php/src/CloudTrailData/Exception/CloudTrailDataException.php', + 'Aws\\CloudTrail\\CloudTrailClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudTrail/CloudTrailClient.php', + 'Aws\\CloudTrail\\Exception\\CloudTrailException' => $vendorDir . '/aws/aws-sdk-php/src/CloudTrail/Exception/CloudTrailException.php', + 'Aws\\CloudTrail\\LogFileIterator' => $vendorDir . '/aws/aws-sdk-php/src/CloudTrail/LogFileIterator.php', + 'Aws\\CloudTrail\\LogFileReader' => $vendorDir . '/aws/aws-sdk-php/src/CloudTrail/LogFileReader.php', + 'Aws\\CloudTrail\\LogRecordIterator' => $vendorDir . '/aws/aws-sdk-php/src/CloudTrail/LogRecordIterator.php', + 'Aws\\CloudWatchEvents\\CloudWatchEventsClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudWatchEvents/CloudWatchEventsClient.php', + 'Aws\\CloudWatchEvents\\Exception\\CloudWatchEventsException' => $vendorDir . '/aws/aws-sdk-php/src/CloudWatchEvents/Exception/CloudWatchEventsException.php', + 'Aws\\CloudWatchLogs\\CloudWatchLogsClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudWatchLogs/CloudWatchLogsClient.php', + 'Aws\\CloudWatchLogs\\Exception\\CloudWatchLogsException' => $vendorDir . '/aws/aws-sdk-php/src/CloudWatchLogs/Exception/CloudWatchLogsException.php', + 'Aws\\CloudWatchRUM\\CloudWatchRUMClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudWatchRUM/CloudWatchRUMClient.php', + 'Aws\\CloudWatchRUM\\Exception\\CloudWatchRUMException' => $vendorDir . '/aws/aws-sdk-php/src/CloudWatchRUM/Exception/CloudWatchRUMException.php', + 'Aws\\CloudWatch\\CloudWatchClient' => $vendorDir . '/aws/aws-sdk-php/src/CloudWatch/CloudWatchClient.php', + 'Aws\\CloudWatch\\Exception\\CloudWatchException' => $vendorDir . '/aws/aws-sdk-php/src/CloudWatch/Exception/CloudWatchException.php', + 'Aws\\CodeArtifact\\CodeArtifactClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeArtifact/CodeArtifactClient.php', + 'Aws\\CodeArtifact\\Exception\\CodeArtifactException' => $vendorDir . '/aws/aws-sdk-php/src/CodeArtifact/Exception/CodeArtifactException.php', + 'Aws\\CodeBuild\\CodeBuildClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeBuild/CodeBuildClient.php', + 'Aws\\CodeBuild\\Exception\\CodeBuildException' => $vendorDir . '/aws/aws-sdk-php/src/CodeBuild/Exception/CodeBuildException.php', + 'Aws\\CodeCatalyst\\CodeCatalystClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeCatalyst/CodeCatalystClient.php', + 'Aws\\CodeCatalyst\\Exception\\CodeCatalystException' => $vendorDir . '/aws/aws-sdk-php/src/CodeCatalyst/Exception/CodeCatalystException.php', + 'Aws\\CodeCommit\\CodeCommitClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeCommit/CodeCommitClient.php', + 'Aws\\CodeCommit\\Exception\\CodeCommitException' => $vendorDir . '/aws/aws-sdk-php/src/CodeCommit/Exception/CodeCommitException.php', + 'Aws\\CodeConnections\\CodeConnectionsClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeConnections/CodeConnectionsClient.php', + 'Aws\\CodeConnections\\Exception\\CodeConnectionsException' => $vendorDir . '/aws/aws-sdk-php/src/CodeConnections/Exception/CodeConnectionsException.php', + 'Aws\\CodeDeploy\\CodeDeployClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeDeploy/CodeDeployClient.php', + 'Aws\\CodeDeploy\\Exception\\CodeDeployException' => $vendorDir . '/aws/aws-sdk-php/src/CodeDeploy/Exception/CodeDeployException.php', + 'Aws\\CodeGuruProfiler\\CodeGuruProfilerClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeGuruProfiler/CodeGuruProfilerClient.php', + 'Aws\\CodeGuruProfiler\\Exception\\CodeGuruProfilerException' => $vendorDir . '/aws/aws-sdk-php/src/CodeGuruProfiler/Exception/CodeGuruProfilerException.php', + 'Aws\\CodeGuruReviewer\\CodeGuruReviewerClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeGuruReviewer/CodeGuruReviewerClient.php', + 'Aws\\CodeGuruReviewer\\Exception\\CodeGuruReviewerException' => $vendorDir . '/aws/aws-sdk-php/src/CodeGuruReviewer/Exception/CodeGuruReviewerException.php', + 'Aws\\CodeGuruSecurity\\CodeGuruSecurityClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeGuruSecurity/CodeGuruSecurityClient.php', + 'Aws\\CodeGuruSecurity\\Exception\\CodeGuruSecurityException' => $vendorDir . '/aws/aws-sdk-php/src/CodeGuruSecurity/Exception/CodeGuruSecurityException.php', + 'Aws\\CodePipeline\\CodePipelineClient' => $vendorDir . '/aws/aws-sdk-php/src/CodePipeline/CodePipelineClient.php', + 'Aws\\CodePipeline\\Exception\\CodePipelineException' => $vendorDir . '/aws/aws-sdk-php/src/CodePipeline/Exception/CodePipelineException.php', + 'Aws\\CodeStarNotifications\\CodeStarNotificationsClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeStarNotifications/CodeStarNotificationsClient.php', + 'Aws\\CodeStarNotifications\\Exception\\CodeStarNotificationsException' => $vendorDir . '/aws/aws-sdk-php/src/CodeStarNotifications/Exception/CodeStarNotificationsException.php', + 'Aws\\CodeStarconnections\\CodeStarconnectionsClient' => $vendorDir . '/aws/aws-sdk-php/src/CodeStarconnections/CodeStarconnectionsClient.php', + 'Aws\\CodeStarconnections\\Exception\\CodeStarconnectionsException' => $vendorDir . '/aws/aws-sdk-php/src/CodeStarconnections/Exception/CodeStarconnectionsException.php', + 'Aws\\CognitoIdentityProvider\\CognitoIdentityProviderClient' => $vendorDir . '/aws/aws-sdk-php/src/CognitoIdentityProvider/CognitoIdentityProviderClient.php', + 'Aws\\CognitoIdentityProvider\\Exception\\CognitoIdentityProviderException' => $vendorDir . '/aws/aws-sdk-php/src/CognitoIdentityProvider/Exception/CognitoIdentityProviderException.php', + 'Aws\\CognitoIdentity\\CognitoIdentityClient' => $vendorDir . '/aws/aws-sdk-php/src/CognitoIdentity/CognitoIdentityClient.php', + 'Aws\\CognitoIdentity\\CognitoIdentityProvider' => $vendorDir . '/aws/aws-sdk-php/src/CognitoIdentity/CognitoIdentityProvider.php', + 'Aws\\CognitoIdentity\\Exception\\CognitoIdentityException' => $vendorDir . '/aws/aws-sdk-php/src/CognitoIdentity/Exception/CognitoIdentityException.php', + 'Aws\\CognitoSync\\CognitoSyncClient' => $vendorDir . '/aws/aws-sdk-php/src/CognitoSync/CognitoSyncClient.php', + 'Aws\\CognitoSync\\Exception\\CognitoSyncException' => $vendorDir . '/aws/aws-sdk-php/src/CognitoSync/Exception/CognitoSyncException.php', + 'Aws\\Command' => $vendorDir . '/aws/aws-sdk-php/src/Command.php', + 'Aws\\CommandInterface' => $vendorDir . '/aws/aws-sdk-php/src/CommandInterface.php', + 'Aws\\CommandPool' => $vendorDir . '/aws/aws-sdk-php/src/CommandPool.php', + 'Aws\\ComprehendMedical\\ComprehendMedicalClient' => $vendorDir . '/aws/aws-sdk-php/src/ComprehendMedical/ComprehendMedicalClient.php', + 'Aws\\ComprehendMedical\\Exception\\ComprehendMedicalException' => $vendorDir . '/aws/aws-sdk-php/src/ComprehendMedical/Exception/ComprehendMedicalException.php', + 'Aws\\Comprehend\\ComprehendClient' => $vendorDir . '/aws/aws-sdk-php/src/Comprehend/ComprehendClient.php', + 'Aws\\Comprehend\\Exception\\ComprehendException' => $vendorDir . '/aws/aws-sdk-php/src/Comprehend/Exception/ComprehendException.php', + 'Aws\\ComputeOptimizerAutomation\\ComputeOptimizerAutomationClient' => $vendorDir . '/aws/aws-sdk-php/src/ComputeOptimizerAutomation/ComputeOptimizerAutomationClient.php', + 'Aws\\ComputeOptimizerAutomation\\Exception\\ComputeOptimizerAutomationException' => $vendorDir . '/aws/aws-sdk-php/src/ComputeOptimizerAutomation/Exception/ComputeOptimizerAutomationException.php', + 'Aws\\ComputeOptimizer\\ComputeOptimizerClient' => $vendorDir . '/aws/aws-sdk-php/src/ComputeOptimizer/ComputeOptimizerClient.php', + 'Aws\\ComputeOptimizer\\Exception\\ComputeOptimizerException' => $vendorDir . '/aws/aws-sdk-php/src/ComputeOptimizer/Exception/ComputeOptimizerException.php', + 'Aws\\ConfigService\\ConfigServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/ConfigService/ConfigServiceClient.php', + 'Aws\\ConfigService\\Exception\\ConfigServiceException' => $vendorDir . '/aws/aws-sdk-php/src/ConfigService/Exception/ConfigServiceException.php', + 'Aws\\ConfigurationProviderInterface' => $vendorDir . '/aws/aws-sdk-php/src/ConfigurationProviderInterface.php', + 'Aws\\Configuration\\ConfigurationResolver' => $vendorDir . '/aws/aws-sdk-php/src/Configuration/ConfigurationResolver.php', + 'Aws\\ConnectCampaignService\\ConnectCampaignServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/ConnectCampaignService/ConnectCampaignServiceClient.php', + 'Aws\\ConnectCampaignService\\Exception\\ConnectCampaignServiceException' => $vendorDir . '/aws/aws-sdk-php/src/ConnectCampaignService/Exception/ConnectCampaignServiceException.php', + 'Aws\\ConnectCampaignsV2\\ConnectCampaignsV2Client' => $vendorDir . '/aws/aws-sdk-php/src/ConnectCampaignsV2/ConnectCampaignsV2Client.php', + 'Aws\\ConnectCampaignsV2\\Exception\\ConnectCampaignsV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/ConnectCampaignsV2/Exception/ConnectCampaignsV2Exception.php', + 'Aws\\ConnectCases\\ConnectCasesClient' => $vendorDir . '/aws/aws-sdk-php/src/ConnectCases/ConnectCasesClient.php', + 'Aws\\ConnectCases\\Exception\\ConnectCasesException' => $vendorDir . '/aws/aws-sdk-php/src/ConnectCases/Exception/ConnectCasesException.php', + 'Aws\\ConnectContactLens\\ConnectContactLensClient' => $vendorDir . '/aws/aws-sdk-php/src/ConnectContactLens/ConnectContactLensClient.php', + 'Aws\\ConnectContactLens\\Exception\\ConnectContactLensException' => $vendorDir . '/aws/aws-sdk-php/src/ConnectContactLens/Exception/ConnectContactLensException.php', + 'Aws\\ConnectHealth\\ConnectHealthClient' => $vendorDir . '/aws/aws-sdk-php/src/ConnectHealth/ConnectHealthClient.php', + 'Aws\\ConnectHealth\\Exception\\ConnectHealthException' => $vendorDir . '/aws/aws-sdk-php/src/ConnectHealth/Exception/ConnectHealthException.php', + 'Aws\\ConnectParticipant\\ConnectParticipantClient' => $vendorDir . '/aws/aws-sdk-php/src/ConnectParticipant/ConnectParticipantClient.php', + 'Aws\\ConnectParticipant\\Exception\\ConnectParticipantException' => $vendorDir . '/aws/aws-sdk-php/src/ConnectParticipant/Exception/ConnectParticipantException.php', + 'Aws\\ConnectWisdomService\\ConnectWisdomServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/ConnectWisdomService/ConnectWisdomServiceClient.php', + 'Aws\\ConnectWisdomService\\Exception\\ConnectWisdomServiceException' => $vendorDir . '/aws/aws-sdk-php/src/ConnectWisdomService/Exception/ConnectWisdomServiceException.php', + 'Aws\\Connect\\ConnectClient' => $vendorDir . '/aws/aws-sdk-php/src/Connect/ConnectClient.php', + 'Aws\\Connect\\Exception\\ConnectException' => $vendorDir . '/aws/aws-sdk-php/src/Connect/Exception/ConnectException.php', + 'Aws\\ControlCatalog\\ControlCatalogClient' => $vendorDir . '/aws/aws-sdk-php/src/ControlCatalog/ControlCatalogClient.php', + 'Aws\\ControlCatalog\\Exception\\ControlCatalogException' => $vendorDir . '/aws/aws-sdk-php/src/ControlCatalog/Exception/ControlCatalogException.php', + 'Aws\\ControlTower\\ControlTowerClient' => $vendorDir . '/aws/aws-sdk-php/src/ControlTower/ControlTowerClient.php', + 'Aws\\ControlTower\\Exception\\ControlTowerException' => $vendorDir . '/aws/aws-sdk-php/src/ControlTower/Exception/ControlTowerException.php', + 'Aws\\CostExplorer\\CostExplorerClient' => $vendorDir . '/aws/aws-sdk-php/src/CostExplorer/CostExplorerClient.php', + 'Aws\\CostExplorer\\Exception\\CostExplorerException' => $vendorDir . '/aws/aws-sdk-php/src/CostExplorer/Exception/CostExplorerException.php', + 'Aws\\CostOptimizationHub\\CostOptimizationHubClient' => $vendorDir . '/aws/aws-sdk-php/src/CostOptimizationHub/CostOptimizationHubClient.php', + 'Aws\\CostOptimizationHub\\Exception\\CostOptimizationHubException' => $vendorDir . '/aws/aws-sdk-php/src/CostOptimizationHub/Exception/CostOptimizationHubException.php', + 'Aws\\CostandUsageReportService\\CostandUsageReportServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/CostandUsageReportService/CostandUsageReportServiceClient.php', + 'Aws\\CostandUsageReportService\\Exception\\CostandUsageReportServiceException' => $vendorDir . '/aws/aws-sdk-php/src/CostandUsageReportService/Exception/CostandUsageReportServiceException.php', + 'Aws\\Credentials\\AssumeRoleCredentialProvider' => $vendorDir . '/aws/aws-sdk-php/src/Credentials/AssumeRoleCredentialProvider.php', + 'Aws\\Credentials\\AssumeRoleWithWebIdentityCredentialProvider' => $vendorDir . '/aws/aws-sdk-php/src/Credentials/AssumeRoleWithWebIdentityCredentialProvider.php', + 'Aws\\Credentials\\CredentialProvider' => $vendorDir . '/aws/aws-sdk-php/src/Credentials/CredentialProvider.php', + 'Aws\\Credentials\\CredentialSources' => $vendorDir . '/aws/aws-sdk-php/src/Credentials/CredentialSources.php', + 'Aws\\Credentials\\Credentials' => $vendorDir . '/aws/aws-sdk-php/src/Credentials/Credentials.php', + 'Aws\\Credentials\\CredentialsInterface' => $vendorDir . '/aws/aws-sdk-php/src/Credentials/CredentialsInterface.php', + 'Aws\\Credentials\\CredentialsUtils' => $vendorDir . '/aws/aws-sdk-php/src/Credentials/CredentialsUtils.php', + 'Aws\\Credentials\\EcsCredentialProvider' => $vendorDir . '/aws/aws-sdk-php/src/Credentials/EcsCredentialProvider.php', + 'Aws\\Credentials\\InstanceProfileProvider' => $vendorDir . '/aws/aws-sdk-php/src/Credentials/InstanceProfileProvider.php', + 'Aws\\Credentials\\LoginCredentialProvider' => $vendorDir . '/aws/aws-sdk-php/src/Credentials/LoginCredentialProvider.php', + 'Aws\\Crypto\\AbstractCryptoClient' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AbstractCryptoClient.php', + 'Aws\\Crypto\\AbstractCryptoClientV2' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AbstractCryptoClientV2.php', + 'Aws\\Crypto\\AbstractCryptoClientV3' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AbstractCryptoClientV3.php', + 'Aws\\Crypto\\AesDecryptingStream' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AesDecryptingStream.php', + 'Aws\\Crypto\\AesEncryptingStream' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AesEncryptingStream.php', + 'Aws\\Crypto\\AesGcmDecryptingStream' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AesGcmDecryptingStream.php', + 'Aws\\Crypto\\AesGcmEncryptingStream' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AesGcmEncryptingStream.php', + 'Aws\\Crypto\\AesStreamInterface' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AesStreamInterface.php', + 'Aws\\Crypto\\AesStreamInterfaceV2' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AesStreamInterfaceV2.php', + 'Aws\\Crypto\\AlgorithmConstants' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AlgorithmConstants.php', + 'Aws\\Crypto\\AlgorithmSuite' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/AlgorithmSuite.php', + 'Aws\\Crypto\\Cipher\\Cbc' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/Cipher/Cbc.php', + 'Aws\\Crypto\\Cipher\\CipherBuilderTrait' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/Cipher/CipherBuilderTrait.php', + 'Aws\\Crypto\\Cipher\\CipherMethod' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/Cipher/CipherMethod.php', + 'Aws\\Crypto\\DecryptionTrait' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/DecryptionTrait.php', + 'Aws\\Crypto\\DecryptionTraitV2' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/DecryptionTraitV2.php', + 'Aws\\Crypto\\DecryptionTraitV3' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/DecryptionTraitV3.php', + 'Aws\\Crypto\\EncryptionTrait' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/EncryptionTrait.php', + 'Aws\\Crypto\\EncryptionTraitV2' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/EncryptionTraitV2.php', + 'Aws\\Crypto\\EncryptionTraitV3' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/EncryptionTraitV3.php', + 'Aws\\Crypto\\KmsMaterialsProvider' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/KmsMaterialsProvider.php', + 'Aws\\Crypto\\KmsMaterialsProviderV2' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/KmsMaterialsProviderV2.php', + 'Aws\\Crypto\\KmsMaterialsProviderV3' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/KmsMaterialsProviderV3.php', + 'Aws\\Crypto\\MaterialsProvider' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/MaterialsProvider.php', + 'Aws\\Crypto\\MaterialsProviderInterface' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterface.php', + 'Aws\\Crypto\\MaterialsProviderInterfaceV2' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterfaceV2.php', + 'Aws\\Crypto\\MaterialsProviderInterfaceV3' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterfaceV3.php', + 'Aws\\Crypto\\MaterialsProviderV2' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/MaterialsProviderV2.php', + 'Aws\\Crypto\\MaterialsProviderV3' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/MaterialsProviderV3.php', + 'Aws\\Crypto\\MetadataEnvelope' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/MetadataEnvelope.php', + 'Aws\\Crypto\\MetadataStrategyInterface' => $vendorDir . '/aws/aws-sdk-php/src/Crypto/MetadataStrategyInterface.php', + 'Aws\\CustomerProfiles\\CustomerProfilesClient' => $vendorDir . '/aws/aws-sdk-php/src/CustomerProfiles/CustomerProfilesClient.php', + 'Aws\\CustomerProfiles\\Exception\\CustomerProfilesException' => $vendorDir . '/aws/aws-sdk-php/src/CustomerProfiles/Exception/CustomerProfilesException.php', + 'Aws\\DAX\\DAXClient' => $vendorDir . '/aws/aws-sdk-php/src/DAX/DAXClient.php', + 'Aws\\DAX\\Exception\\DAXException' => $vendorDir . '/aws/aws-sdk-php/src/DAX/Exception/DAXException.php', + 'Aws\\DLM\\DLMClient' => $vendorDir . '/aws/aws-sdk-php/src/DLM/DLMClient.php', + 'Aws\\DLM\\Exception\\DLMException' => $vendorDir . '/aws/aws-sdk-php/src/DLM/Exception/DLMException.php', + 'Aws\\DSQL\\AuthTokenGenerator' => $vendorDir . '/aws/aws-sdk-php/src/DSQL/AuthTokenGenerator.php', + 'Aws\\DSQL\\DSQLClient' => $vendorDir . '/aws/aws-sdk-php/src/DSQL/DSQLClient.php', + 'Aws\\DSQL\\Exception\\DSQLException' => $vendorDir . '/aws/aws-sdk-php/src/DSQL/Exception/DSQLException.php', + 'Aws\\DataExchange\\DataExchangeClient' => $vendorDir . '/aws/aws-sdk-php/src/DataExchange/DataExchangeClient.php', + 'Aws\\DataExchange\\Exception\\DataExchangeException' => $vendorDir . '/aws/aws-sdk-php/src/DataExchange/Exception/DataExchangeException.php', + 'Aws\\DataPipeline\\DataPipelineClient' => $vendorDir . '/aws/aws-sdk-php/src/DataPipeline/DataPipelineClient.php', + 'Aws\\DataPipeline\\Exception\\DataPipelineException' => $vendorDir . '/aws/aws-sdk-php/src/DataPipeline/Exception/DataPipelineException.php', + 'Aws\\DataSync\\DataSyncClient' => $vendorDir . '/aws/aws-sdk-php/src/DataSync/DataSyncClient.php', + 'Aws\\DataSync\\Exception\\DataSyncException' => $vendorDir . '/aws/aws-sdk-php/src/DataSync/Exception/DataSyncException.php', + 'Aws\\DataZone\\DataZoneClient' => $vendorDir . '/aws/aws-sdk-php/src/DataZone/DataZoneClient.php', + 'Aws\\DataZone\\Exception\\DataZoneException' => $vendorDir . '/aws/aws-sdk-php/src/DataZone/Exception/DataZoneException.php', + 'Aws\\DatabaseMigrationService\\DatabaseMigrationServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/DatabaseMigrationService/DatabaseMigrationServiceClient.php', + 'Aws\\DatabaseMigrationService\\Exception\\DatabaseMigrationServiceException' => $vendorDir . '/aws/aws-sdk-php/src/DatabaseMigrationService/Exception/DatabaseMigrationServiceException.php', + 'Aws\\Deadline\\DeadlineClient' => $vendorDir . '/aws/aws-sdk-php/src/Deadline/DeadlineClient.php', + 'Aws\\Deadline\\Exception\\DeadlineException' => $vendorDir . '/aws/aws-sdk-php/src/Deadline/Exception/DeadlineException.php', + 'Aws\\DefaultsMode\\Configuration' => $vendorDir . '/aws/aws-sdk-php/src/DefaultsMode/Configuration.php', + 'Aws\\DefaultsMode\\ConfigurationInterface' => $vendorDir . '/aws/aws-sdk-php/src/DefaultsMode/ConfigurationInterface.php', + 'Aws\\DefaultsMode\\ConfigurationProvider' => $vendorDir . '/aws/aws-sdk-php/src/DefaultsMode/ConfigurationProvider.php', + 'Aws\\DefaultsMode\\Exception\\ConfigurationException' => $vendorDir . '/aws/aws-sdk-php/src/DefaultsMode/Exception/ConfigurationException.php', + 'Aws\\Detective\\DetectiveClient' => $vendorDir . '/aws/aws-sdk-php/src/Detective/DetectiveClient.php', + 'Aws\\Detective\\Exception\\DetectiveException' => $vendorDir . '/aws/aws-sdk-php/src/Detective/Exception/DetectiveException.php', + 'Aws\\DevOpsAgent\\DevOpsAgentClient' => $vendorDir . '/aws/aws-sdk-php/src/DevOpsAgent/DevOpsAgentClient.php', + 'Aws\\DevOpsAgent\\Exception\\DevOpsAgentException' => $vendorDir . '/aws/aws-sdk-php/src/DevOpsAgent/Exception/DevOpsAgentException.php', + 'Aws\\DevOpsGuru\\DevOpsGuruClient' => $vendorDir . '/aws/aws-sdk-php/src/DevOpsGuru/DevOpsGuruClient.php', + 'Aws\\DevOpsGuru\\Exception\\DevOpsGuruException' => $vendorDir . '/aws/aws-sdk-php/src/DevOpsGuru/Exception/DevOpsGuruException.php', + 'Aws\\DeviceFarm\\DeviceFarmClient' => $vendorDir . '/aws/aws-sdk-php/src/DeviceFarm/DeviceFarmClient.php', + 'Aws\\DeviceFarm\\Exception\\DeviceFarmException' => $vendorDir . '/aws/aws-sdk-php/src/DeviceFarm/Exception/DeviceFarmException.php', + 'Aws\\DirectConnect\\DirectConnectClient' => $vendorDir . '/aws/aws-sdk-php/src/DirectConnect/DirectConnectClient.php', + 'Aws\\DirectConnect\\Exception\\DirectConnectException' => $vendorDir . '/aws/aws-sdk-php/src/DirectConnect/Exception/DirectConnectException.php', + 'Aws\\DirectoryServiceData\\DirectoryServiceDataClient' => $vendorDir . '/aws/aws-sdk-php/src/DirectoryServiceData/DirectoryServiceDataClient.php', + 'Aws\\DirectoryServiceData\\Exception\\DirectoryServiceDataException' => $vendorDir . '/aws/aws-sdk-php/src/DirectoryServiceData/Exception/DirectoryServiceDataException.php', + 'Aws\\DirectoryService\\DirectoryServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/DirectoryService/DirectoryServiceClient.php', + 'Aws\\DirectoryService\\Exception\\DirectoryServiceException' => $vendorDir . '/aws/aws-sdk-php/src/DirectoryService/Exception/DirectoryServiceException.php', + 'Aws\\DocDBElastic\\DocDBElasticClient' => $vendorDir . '/aws/aws-sdk-php/src/DocDBElastic/DocDBElasticClient.php', + 'Aws\\DocDBElastic\\Exception\\DocDBElasticException' => $vendorDir . '/aws/aws-sdk-php/src/DocDBElastic/Exception/DocDBElasticException.php', + 'Aws\\DocDB\\DocDBClient' => $vendorDir . '/aws/aws-sdk-php/src/DocDB/DocDBClient.php', + 'Aws\\DocDB\\Exception\\DocDBException' => $vendorDir . '/aws/aws-sdk-php/src/DocDB/Exception/DocDBException.php', + 'Aws\\DoctrineCacheAdapter' => $vendorDir . '/aws/aws-sdk-php/src/DoctrineCacheAdapter.php', + 'Aws\\DynamoDbStreams\\DynamoDbStreamsClient' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDbStreams/DynamoDbStreamsClient.php', + 'Aws\\DynamoDbStreams\\Exception\\DynamoDbStreamsException' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDbStreams/Exception/DynamoDbStreamsException.php', + 'Aws\\DynamoDb\\BinaryValue' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/BinaryValue.php', + 'Aws\\DynamoDb\\DynamoDbClient' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/DynamoDbClient.php', + 'Aws\\DynamoDb\\Exception\\DynamoDbException' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/Exception/DynamoDbException.php', + 'Aws\\DynamoDb\\LockingSessionConnection' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/LockingSessionConnection.php', + 'Aws\\DynamoDb\\Marshaler' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/Marshaler.php', + 'Aws\\DynamoDb\\NumberValue' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/NumberValue.php', + 'Aws\\DynamoDb\\SessionConnectionConfigTrait' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/SessionConnectionConfigTrait.php', + 'Aws\\DynamoDb\\SessionConnectionInterface' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/SessionConnectionInterface.php', + 'Aws\\DynamoDb\\SessionHandler' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/SessionHandler.php', + 'Aws\\DynamoDb\\SetValue' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/SetValue.php', + 'Aws\\DynamoDb\\StandardSessionConnection' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/StandardSessionConnection.php', + 'Aws\\DynamoDb\\WriteRequestBatch' => $vendorDir . '/aws/aws-sdk-php/src/DynamoDb/WriteRequestBatch.php', + 'Aws\\EBS\\EBSClient' => $vendorDir . '/aws/aws-sdk-php/src/EBS/EBSClient.php', + 'Aws\\EBS\\Exception\\EBSException' => $vendorDir . '/aws/aws-sdk-php/src/EBS/Exception/EBSException.php', + 'Aws\\EC2InstanceConnect\\EC2InstanceConnectClient' => $vendorDir . '/aws/aws-sdk-php/src/EC2InstanceConnect/EC2InstanceConnectClient.php', + 'Aws\\EC2InstanceConnect\\Exception\\EC2InstanceConnectException' => $vendorDir . '/aws/aws-sdk-php/src/EC2InstanceConnect/Exception/EC2InstanceConnectException.php', + 'Aws\\ECRPublic\\ECRPublicClient' => $vendorDir . '/aws/aws-sdk-php/src/ECRPublic/ECRPublicClient.php', + 'Aws\\ECRPublic\\Exception\\ECRPublicException' => $vendorDir . '/aws/aws-sdk-php/src/ECRPublic/Exception/ECRPublicException.php', + 'Aws\\EKSAuth\\EKSAuthClient' => $vendorDir . '/aws/aws-sdk-php/src/EKSAuth/EKSAuthClient.php', + 'Aws\\EKSAuth\\Exception\\EKSAuthException' => $vendorDir . '/aws/aws-sdk-php/src/EKSAuth/Exception/EKSAuthException.php', + 'Aws\\EKS\\EKSClient' => $vendorDir . '/aws/aws-sdk-php/src/EKS/EKSClient.php', + 'Aws\\EKS\\Exception\\EKSException' => $vendorDir . '/aws/aws-sdk-php/src/EKS/Exception/EKSException.php', + 'Aws\\EMRContainers\\EMRContainersClient' => $vendorDir . '/aws/aws-sdk-php/src/EMRContainers/EMRContainersClient.php', + 'Aws\\EMRContainers\\Exception\\EMRContainersException' => $vendorDir . '/aws/aws-sdk-php/src/EMRContainers/Exception/EMRContainersException.php', + 'Aws\\EMRServerless\\EMRServerlessClient' => $vendorDir . '/aws/aws-sdk-php/src/EMRServerless/EMRServerlessClient.php', + 'Aws\\EMRServerless\\Exception\\EMRServerlessException' => $vendorDir . '/aws/aws-sdk-php/src/EMRServerless/Exception/EMRServerlessException.php', + 'Aws\\Ec2\\Ec2Client' => $vendorDir . '/aws/aws-sdk-php/src/Ec2/Ec2Client.php', + 'Aws\\Ec2\\Exception\\Ec2Exception' => $vendorDir . '/aws/aws-sdk-php/src/Ec2/Exception/Ec2Exception.php', + 'Aws\\Ecr\\EcrClient' => $vendorDir . '/aws/aws-sdk-php/src/Ecr/EcrClient.php', + 'Aws\\Ecr\\Exception\\EcrException' => $vendorDir . '/aws/aws-sdk-php/src/Ecr/Exception/EcrException.php', + 'Aws\\Ecs\\EcsClient' => $vendorDir . '/aws/aws-sdk-php/src/Ecs/EcsClient.php', + 'Aws\\Ecs\\Exception\\EcsException' => $vendorDir . '/aws/aws-sdk-php/src/Ecs/Exception/EcsException.php', + 'Aws\\Efs\\EfsClient' => $vendorDir . '/aws/aws-sdk-php/src/Efs/EfsClient.php', + 'Aws\\Efs\\Exception\\EfsException' => $vendorDir . '/aws/aws-sdk-php/src/Efs/Exception/EfsException.php', + 'Aws\\ElastiCache\\ElastiCacheClient' => $vendorDir . '/aws/aws-sdk-php/src/ElastiCache/ElastiCacheClient.php', + 'Aws\\ElastiCache\\Exception\\ElastiCacheException' => $vendorDir . '/aws/aws-sdk-php/src/ElastiCache/Exception/ElastiCacheException.php', + 'Aws\\ElasticBeanstalk\\ElasticBeanstalkClient' => $vendorDir . '/aws/aws-sdk-php/src/ElasticBeanstalk/ElasticBeanstalkClient.php', + 'Aws\\ElasticBeanstalk\\Exception\\ElasticBeanstalkException' => $vendorDir . '/aws/aws-sdk-php/src/ElasticBeanstalk/Exception/ElasticBeanstalkException.php', + 'Aws\\ElasticLoadBalancingV2\\ElasticLoadBalancingV2Client' => $vendorDir . '/aws/aws-sdk-php/src/ElasticLoadBalancingV2/ElasticLoadBalancingV2Client.php', + 'Aws\\ElasticLoadBalancingV2\\Exception\\ElasticLoadBalancingV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/ElasticLoadBalancingV2/Exception/ElasticLoadBalancingV2Exception.php', + 'Aws\\ElasticLoadBalancing\\ElasticLoadBalancingClient' => $vendorDir . '/aws/aws-sdk-php/src/ElasticLoadBalancing/ElasticLoadBalancingClient.php', + 'Aws\\ElasticLoadBalancing\\Exception\\ElasticLoadBalancingException' => $vendorDir . '/aws/aws-sdk-php/src/ElasticLoadBalancing/Exception/ElasticLoadBalancingException.php', + 'Aws\\ElasticsearchService\\ElasticsearchServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/ElasticsearchService/ElasticsearchServiceClient.php', + 'Aws\\ElasticsearchService\\Exception\\ElasticsearchServiceException' => $vendorDir . '/aws/aws-sdk-php/src/ElasticsearchService/Exception/ElasticsearchServiceException.php', + 'Aws\\ElementalInference\\ElementalInferenceClient' => $vendorDir . '/aws/aws-sdk-php/src/ElementalInference/ElementalInferenceClient.php', + 'Aws\\ElementalInference\\Exception\\ElementalInferenceException' => $vendorDir . '/aws/aws-sdk-php/src/ElementalInference/Exception/ElementalInferenceException.php', + 'Aws\\Emr\\EmrClient' => $vendorDir . '/aws/aws-sdk-php/src/Emr/EmrClient.php', + 'Aws\\Emr\\Exception\\EmrException' => $vendorDir . '/aws/aws-sdk-php/src/Emr/Exception/EmrException.php', + 'Aws\\EndpointDiscovery\\Configuration' => $vendorDir . '/aws/aws-sdk-php/src/EndpointDiscovery/Configuration.php', + 'Aws\\EndpointDiscovery\\ConfigurationInterface' => $vendorDir . '/aws/aws-sdk-php/src/EndpointDiscovery/ConfigurationInterface.php', + 'Aws\\EndpointDiscovery\\ConfigurationProvider' => $vendorDir . '/aws/aws-sdk-php/src/EndpointDiscovery/ConfigurationProvider.php', + 'Aws\\EndpointDiscovery\\EndpointDiscoveryMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/EndpointDiscovery/EndpointDiscoveryMiddleware.php', + 'Aws\\EndpointDiscovery\\EndpointList' => $vendorDir . '/aws/aws-sdk-php/src/EndpointDiscovery/EndpointList.php', + 'Aws\\EndpointDiscovery\\Exception\\ConfigurationException' => $vendorDir . '/aws/aws-sdk-php/src/EndpointDiscovery/Exception/ConfigurationException.php', + 'Aws\\EndpointParameterMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/EndpointParameterMiddleware.php', + 'Aws\\EndpointV2\\Bdd\\BddEvaluator' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Bdd/BddEvaluator.php', + 'Aws\\EndpointV2\\Bdd\\BddNodeDecoder' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Bdd/BddNodeDecoder.php', + 'Aws\\EndpointV2\\Bdd\\BddResultResolver' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Bdd/BddResultResolver.php', + 'Aws\\EndpointV2\\Bdd\\BddRuleset' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Bdd/BddRuleset.php', + 'Aws\\EndpointV2\\EndpointDefinitionProvider' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/EndpointDefinitionProvider.php', + 'Aws\\EndpointV2\\EndpointProviderV2' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/EndpointProviderV2.php', + 'Aws\\EndpointV2\\EndpointV2Middleware' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/EndpointV2Middleware.php', + 'Aws\\EndpointV2\\EndpointV2SerializerTrait' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/EndpointV2SerializerTrait.php', + 'Aws\\EndpointV2\\Rule\\AbstractRule' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Rule/AbstractRule.php', + 'Aws\\EndpointV2\\Rule\\EndpointRule' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Rule/EndpointRule.php', + 'Aws\\EndpointV2\\Rule\\ErrorRule' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Rule/ErrorRule.php', + 'Aws\\EndpointV2\\Rule\\RuleCreator' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Rule/RuleCreator.php', + 'Aws\\EndpointV2\\Rule\\TreeRule' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Rule/TreeRule.php', + 'Aws\\EndpointV2\\Ruleset\\Ruleset' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Ruleset/Ruleset.php', + 'Aws\\EndpointV2\\Ruleset\\RulesetEndpoint' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Ruleset/RulesetEndpoint.php', + 'Aws\\EndpointV2\\Ruleset\\RulesetParameter' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Ruleset/RulesetParameter.php', + 'Aws\\EndpointV2\\Ruleset\\RulesetStandardLibrary' => $vendorDir . '/aws/aws-sdk-php/src/EndpointV2/Ruleset/RulesetStandardLibrary.php', + 'Aws\\Endpoint\\EndpointProvider' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/EndpointProvider.php', + 'Aws\\Endpoint\\Partition' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/Partition.php', + 'Aws\\Endpoint\\PartitionEndpointProvider' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/PartitionEndpointProvider.php', + 'Aws\\Endpoint\\PartitionInterface' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/PartitionInterface.php', + 'Aws\\Endpoint\\PatternEndpointProvider' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/PatternEndpointProvider.php', + 'Aws\\Endpoint\\UseDualstackEndpoint\\Configuration' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/UseDualstackEndpoint/Configuration.php', + 'Aws\\Endpoint\\UseDualstackEndpoint\\ConfigurationInterface' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/UseDualstackEndpoint/ConfigurationInterface.php', + 'Aws\\Endpoint\\UseDualstackEndpoint\\ConfigurationProvider' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/UseDualstackEndpoint/ConfigurationProvider.php', + 'Aws\\Endpoint\\UseDualstackEndpoint\\Exception\\ConfigurationException' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/UseDualstackEndpoint/Exception/ConfigurationException.php', + 'Aws\\Endpoint\\UseFipsEndpoint\\Configuration' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/UseFipsEndpoint/Configuration.php', + 'Aws\\Endpoint\\UseFipsEndpoint\\ConfigurationInterface' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/UseFipsEndpoint/ConfigurationInterface.php', + 'Aws\\Endpoint\\UseFipsEndpoint\\ConfigurationProvider' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/UseFipsEndpoint/ConfigurationProvider.php', + 'Aws\\Endpoint\\UseFipsEndpoint\\Exception\\ConfigurationException' => $vendorDir . '/aws/aws-sdk-php/src/Endpoint/UseFipsEndpoint/Exception/ConfigurationException.php', + 'Aws\\EntityResolution\\EntityResolutionClient' => $vendorDir . '/aws/aws-sdk-php/src/EntityResolution/EntityResolutionClient.php', + 'Aws\\EntityResolution\\Exception\\EntityResolutionException' => $vendorDir . '/aws/aws-sdk-php/src/EntityResolution/Exception/EntityResolutionException.php', + 'Aws\\EventBridge\\EventBridgeClient' => $vendorDir . '/aws/aws-sdk-php/src/EventBridge/EventBridgeClient.php', + 'Aws\\EventBridge\\EventBridgeEndpointMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/EventBridge/EventBridgeEndpointMiddleware.php', + 'Aws\\EventBridge\\Exception\\EventBridgeException' => $vendorDir . '/aws/aws-sdk-php/src/EventBridge/Exception/EventBridgeException.php', + 'Aws\\Evs\\EvsClient' => $vendorDir . '/aws/aws-sdk-php/src/Evs/EvsClient.php', + 'Aws\\Evs\\Exception\\EvsException' => $vendorDir . '/aws/aws-sdk-php/src/Evs/Exception/EvsException.php', + 'Aws\\Exception\\AwsException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/AwsException.php', + 'Aws\\Exception\\CommonRuntimeException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/CommonRuntimeException.php', + 'Aws\\Exception\\CouldNotCreateChecksumException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/CouldNotCreateChecksumException.php', + 'Aws\\Exception\\CredentialsException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/CredentialsException.php', + 'Aws\\Exception\\CryptoException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/CryptoException.php', + 'Aws\\Exception\\CryptoPolyfillException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/CryptoPolyfillException.php', + 'Aws\\Exception\\EventStreamDataException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/EventStreamDataException.php', + 'Aws\\Exception\\IncalculablePayloadException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/IncalculablePayloadException.php', + 'Aws\\Exception\\InvalidJsonException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/InvalidJsonException.php', + 'Aws\\Exception\\InvalidRegionException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/InvalidRegionException.php', + 'Aws\\Exception\\MultipartUploadException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/MultipartUploadException.php', + 'Aws\\Exception\\TokenException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/TokenException.php', + 'Aws\\Exception\\UnresolvedApiException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/UnresolvedApiException.php', + 'Aws\\Exception\\UnresolvedEndpointException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/UnresolvedEndpointException.php', + 'Aws\\Exception\\UnresolvedSignatureException' => $vendorDir . '/aws/aws-sdk-php/src/Exception/UnresolvedSignatureException.php', + 'Aws\\FIS\\Exception\\FISException' => $vendorDir . '/aws/aws-sdk-php/src/FIS/Exception/FISException.php', + 'Aws\\FIS\\FISClient' => $vendorDir . '/aws/aws-sdk-php/src/FIS/FISClient.php', + 'Aws\\FMS\\Exception\\FMSException' => $vendorDir . '/aws/aws-sdk-php/src/FMS/Exception/FMSException.php', + 'Aws\\FMS\\FMSClient' => $vendorDir . '/aws/aws-sdk-php/src/FMS/FMSClient.php', + 'Aws\\FSx\\Exception\\FSxException' => $vendorDir . '/aws/aws-sdk-php/src/FSx/Exception/FSxException.php', + 'Aws\\FSx\\FSxClient' => $vendorDir . '/aws/aws-sdk-php/src/FSx/FSxClient.php', + 'Aws\\FinSpaceData\\Exception\\FinSpaceDataException' => $vendorDir . '/aws/aws-sdk-php/src/FinSpaceData/Exception/FinSpaceDataException.php', + 'Aws\\FinSpaceData\\FinSpaceDataClient' => $vendorDir . '/aws/aws-sdk-php/src/FinSpaceData/FinSpaceDataClient.php', + 'Aws\\Firehose\\Exception\\FirehoseException' => $vendorDir . '/aws/aws-sdk-php/src/Firehose/Exception/FirehoseException.php', + 'Aws\\Firehose\\FirehoseClient' => $vendorDir . '/aws/aws-sdk-php/src/Firehose/FirehoseClient.php', + 'Aws\\ForecastQueryService\\Exception\\ForecastQueryServiceException' => $vendorDir . '/aws/aws-sdk-php/src/ForecastQueryService/Exception/ForecastQueryServiceException.php', + 'Aws\\ForecastQueryService\\ForecastQueryServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/ForecastQueryService/ForecastQueryServiceClient.php', + 'Aws\\ForecastService\\Exception\\ForecastServiceException' => $vendorDir . '/aws/aws-sdk-php/src/ForecastService/Exception/ForecastServiceException.php', + 'Aws\\ForecastService\\ForecastServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/ForecastService/ForecastServiceClient.php', + 'Aws\\FraudDetector\\Exception\\FraudDetectorException' => $vendorDir . '/aws/aws-sdk-php/src/FraudDetector/Exception/FraudDetectorException.php', + 'Aws\\FraudDetector\\FraudDetectorClient' => $vendorDir . '/aws/aws-sdk-php/src/FraudDetector/FraudDetectorClient.php', + 'Aws\\FreeTier\\Exception\\FreeTierException' => $vendorDir . '/aws/aws-sdk-php/src/FreeTier/Exception/FreeTierException.php', + 'Aws\\FreeTier\\FreeTierClient' => $vendorDir . '/aws/aws-sdk-php/src/FreeTier/FreeTierClient.php', + 'Aws\\GameLiftStreams\\Exception\\GameLiftStreamsException' => $vendorDir . '/aws/aws-sdk-php/src/GameLiftStreams/Exception/GameLiftStreamsException.php', + 'Aws\\GameLiftStreams\\GameLiftStreamsClient' => $vendorDir . '/aws/aws-sdk-php/src/GameLiftStreams/GameLiftStreamsClient.php', + 'Aws\\GameLift\\Exception\\GameLiftException' => $vendorDir . '/aws/aws-sdk-php/src/GameLift/Exception/GameLiftException.php', + 'Aws\\GameLift\\GameLiftClient' => $vendorDir . '/aws/aws-sdk-php/src/GameLift/GameLiftClient.php', + 'Aws\\GeoMaps\\Exception\\GeoMapsException' => $vendorDir . '/aws/aws-sdk-php/src/GeoMaps/Exception/GeoMapsException.php', + 'Aws\\GeoMaps\\GeoMapsClient' => $vendorDir . '/aws/aws-sdk-php/src/GeoMaps/GeoMapsClient.php', + 'Aws\\GeoPlaces\\Exception\\GeoPlacesException' => $vendorDir . '/aws/aws-sdk-php/src/GeoPlaces/Exception/GeoPlacesException.php', + 'Aws\\GeoPlaces\\GeoPlacesClient' => $vendorDir . '/aws/aws-sdk-php/src/GeoPlaces/GeoPlacesClient.php', + 'Aws\\GeoRoutes\\Exception\\GeoRoutesException' => $vendorDir . '/aws/aws-sdk-php/src/GeoRoutes/Exception/GeoRoutesException.php', + 'Aws\\GeoRoutes\\GeoRoutesClient' => $vendorDir . '/aws/aws-sdk-php/src/GeoRoutes/GeoRoutesClient.php', + 'Aws\\Glacier\\Exception\\GlacierException' => $vendorDir . '/aws/aws-sdk-php/src/Glacier/Exception/GlacierException.php', + 'Aws\\Glacier\\GlacierClient' => $vendorDir . '/aws/aws-sdk-php/src/Glacier/GlacierClient.php', + 'Aws\\Glacier\\MultipartUploader' => $vendorDir . '/aws/aws-sdk-php/src/Glacier/MultipartUploader.php', + 'Aws\\Glacier\\TreeHash' => $vendorDir . '/aws/aws-sdk-php/src/Glacier/TreeHash.php', + 'Aws\\GlobalAccelerator\\Exception\\GlobalAcceleratorException' => $vendorDir . '/aws/aws-sdk-php/src/GlobalAccelerator/Exception/GlobalAcceleratorException.php', + 'Aws\\GlobalAccelerator\\GlobalAcceleratorClient' => $vendorDir . '/aws/aws-sdk-php/src/GlobalAccelerator/GlobalAcceleratorClient.php', + 'Aws\\GlueDataBrew\\Exception\\GlueDataBrewException' => $vendorDir . '/aws/aws-sdk-php/src/GlueDataBrew/Exception/GlueDataBrewException.php', + 'Aws\\GlueDataBrew\\GlueDataBrewClient' => $vendorDir . '/aws/aws-sdk-php/src/GlueDataBrew/GlueDataBrewClient.php', + 'Aws\\Glue\\Exception\\GlueException' => $vendorDir . '/aws/aws-sdk-php/src/Glue/Exception/GlueException.php', + 'Aws\\Glue\\GlueClient' => $vendorDir . '/aws/aws-sdk-php/src/Glue/GlueClient.php', + 'Aws\\GreengrassV2\\Exception\\GreengrassV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/GreengrassV2/Exception/GreengrassV2Exception.php', + 'Aws\\GreengrassV2\\GreengrassV2Client' => $vendorDir . '/aws/aws-sdk-php/src/GreengrassV2/GreengrassV2Client.php', + 'Aws\\Greengrass\\Exception\\GreengrassException' => $vendorDir . '/aws/aws-sdk-php/src/Greengrass/Exception/GreengrassException.php', + 'Aws\\Greengrass\\GreengrassClient' => $vendorDir . '/aws/aws-sdk-php/src/Greengrass/GreengrassClient.php', + 'Aws\\GroundStation\\Exception\\GroundStationException' => $vendorDir . '/aws/aws-sdk-php/src/GroundStation/Exception/GroundStationException.php', + 'Aws\\GroundStation\\GroundStationClient' => $vendorDir . '/aws/aws-sdk-php/src/GroundStation/GroundStationClient.php', + 'Aws\\GuardDuty\\Exception\\GuardDutyException' => $vendorDir . '/aws/aws-sdk-php/src/GuardDuty/Exception/GuardDutyException.php', + 'Aws\\GuardDuty\\GuardDutyClient' => $vendorDir . '/aws/aws-sdk-php/src/GuardDuty/GuardDutyClient.php', + 'Aws\\HandlerList' => $vendorDir . '/aws/aws-sdk-php/src/HandlerList.php', + 'Aws\\Handler\\Guzzle\\GuzzleHandler' => $vendorDir . '/aws/aws-sdk-php/src/Handler/Guzzle/GuzzleHandler.php', + 'Aws\\Handler\\HttpHandlerError' => $vendorDir . '/aws/aws-sdk-php/src/Handler/HttpHandlerError.php', + 'Aws\\HasDataTrait' => $vendorDir . '/aws/aws-sdk-php/src/HasDataTrait.php', + 'Aws\\HasMonitoringEventsTrait' => $vendorDir . '/aws/aws-sdk-php/src/HasMonitoringEventsTrait.php', + 'Aws\\HashInterface' => $vendorDir . '/aws/aws-sdk-php/src/HashInterface.php', + 'Aws\\HashingStream' => $vendorDir . '/aws/aws-sdk-php/src/HashingStream.php', + 'Aws\\HealthLake\\Exception\\HealthLakeException' => $vendorDir . '/aws/aws-sdk-php/src/HealthLake/Exception/HealthLakeException.php', + 'Aws\\HealthLake\\HealthLakeClient' => $vendorDir . '/aws/aws-sdk-php/src/HealthLake/HealthLakeClient.php', + 'Aws\\Health\\Exception\\HealthException' => $vendorDir . '/aws/aws-sdk-php/src/Health/Exception/HealthException.php', + 'Aws\\Health\\HealthClient' => $vendorDir . '/aws/aws-sdk-php/src/Health/HealthClient.php', + 'Aws\\History' => $vendorDir . '/aws/aws-sdk-php/src/History.php', + 'Aws\\IVSRealTime\\Exception\\IVSRealTimeException' => $vendorDir . '/aws/aws-sdk-php/src/IVSRealTime/Exception/IVSRealTimeException.php', + 'Aws\\IVSRealTime\\IVSRealTimeClient' => $vendorDir . '/aws/aws-sdk-php/src/IVSRealTime/IVSRealTimeClient.php', + 'Aws\\IVS\\Exception\\IVSException' => $vendorDir . '/aws/aws-sdk-php/src/IVS/Exception/IVSException.php', + 'Aws\\IVS\\IVSClient' => $vendorDir . '/aws/aws-sdk-php/src/IVS/IVSClient.php', + 'Aws\\Iam\\Exception\\IamException' => $vendorDir . '/aws/aws-sdk-php/src/Iam/Exception/IamException.php', + 'Aws\\Iam\\IamClient' => $vendorDir . '/aws/aws-sdk-php/src/Iam/IamClient.php', + 'Aws\\IdempotencyTokenMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/IdempotencyTokenMiddleware.php', + 'Aws\\IdentityStore\\Exception\\IdentityStoreException' => $vendorDir . '/aws/aws-sdk-php/src/IdentityStore/Exception/IdentityStoreException.php', + 'Aws\\IdentityStore\\IdentityStoreClient' => $vendorDir . '/aws/aws-sdk-php/src/IdentityStore/IdentityStoreClient.php', + 'Aws\\Identity\\AwsCredentialIdentity' => $vendorDir . '/aws/aws-sdk-php/src/Identity/AwsCredentialIdentity.php', + 'Aws\\Identity\\BearerTokenIdentity' => $vendorDir . '/aws/aws-sdk-php/src/Identity/BearerTokenIdentity.php', + 'Aws\\Identity\\IdentityInterface' => $vendorDir . '/aws/aws-sdk-php/src/Identity/IdentityInterface.php', + 'Aws\\Identity\\S3\\S3ExpressIdentity' => $vendorDir . '/aws/aws-sdk-php/src/Identity/S3/S3ExpressIdentity.php', + 'Aws\\Identity\\S3\\S3ExpressIdentityProvider' => $vendorDir . '/aws/aws-sdk-php/src/Identity/S3/S3ExpressIdentityProvider.php', + 'Aws\\ImportExport\\Exception\\ImportExportException' => $vendorDir . '/aws/aws-sdk-php/src/ImportExport/Exception/ImportExportException.php', + 'Aws\\ImportExport\\ImportExportClient' => $vendorDir . '/aws/aws-sdk-php/src/ImportExport/ImportExportClient.php', + 'Aws\\InputValidationMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/InputValidationMiddleware.php', + 'Aws\\Inspector2\\Exception\\Inspector2Exception' => $vendorDir . '/aws/aws-sdk-php/src/Inspector2/Exception/Inspector2Exception.php', + 'Aws\\Inspector2\\Inspector2Client' => $vendorDir . '/aws/aws-sdk-php/src/Inspector2/Inspector2Client.php', + 'Aws\\InspectorScan\\Exception\\InspectorScanException' => $vendorDir . '/aws/aws-sdk-php/src/InspectorScan/Exception/InspectorScanException.php', + 'Aws\\InspectorScan\\InspectorScanClient' => $vendorDir . '/aws/aws-sdk-php/src/InspectorScan/InspectorScanClient.php', + 'Aws\\Inspector\\Exception\\InspectorException' => $vendorDir . '/aws/aws-sdk-php/src/Inspector/Exception/InspectorException.php', + 'Aws\\Inspector\\InspectorClient' => $vendorDir . '/aws/aws-sdk-php/src/Inspector/InspectorClient.php', + 'Aws\\Interconnect\\Exception\\InterconnectException' => $vendorDir . '/aws/aws-sdk-php/src/Interconnect/Exception/InterconnectException.php', + 'Aws\\Interconnect\\InterconnectClient' => $vendorDir . '/aws/aws-sdk-php/src/Interconnect/InterconnectClient.php', + 'Aws\\InternetMonitor\\Exception\\InternetMonitorException' => $vendorDir . '/aws/aws-sdk-php/src/InternetMonitor/Exception/InternetMonitorException.php', + 'Aws\\InternetMonitor\\InternetMonitorClient' => $vendorDir . '/aws/aws-sdk-php/src/InternetMonitor/InternetMonitorClient.php', + 'Aws\\Invoicing\\Exception\\InvoicingException' => $vendorDir . '/aws/aws-sdk-php/src/Invoicing/Exception/InvoicingException.php', + 'Aws\\Invoicing\\InvoicingClient' => $vendorDir . '/aws/aws-sdk-php/src/Invoicing/InvoicingClient.php', + 'Aws\\IoTDeviceAdvisor\\Exception\\IoTDeviceAdvisorException' => $vendorDir . '/aws/aws-sdk-php/src/IoTDeviceAdvisor/Exception/IoTDeviceAdvisorException.php', + 'Aws\\IoTDeviceAdvisor\\IoTDeviceAdvisorClient' => $vendorDir . '/aws/aws-sdk-php/src/IoTDeviceAdvisor/IoTDeviceAdvisorClient.php', + 'Aws\\IoTFleetWise\\Exception\\IoTFleetWiseException' => $vendorDir . '/aws/aws-sdk-php/src/IoTFleetWise/Exception/IoTFleetWiseException.php', + 'Aws\\IoTFleetWise\\IoTFleetWiseClient' => $vendorDir . '/aws/aws-sdk-php/src/IoTFleetWise/IoTFleetWiseClient.php', + 'Aws\\IoTJobsDataPlane\\Exception\\IoTJobsDataPlaneException' => $vendorDir . '/aws/aws-sdk-php/src/IoTJobsDataPlane/Exception/IoTJobsDataPlaneException.php', + 'Aws\\IoTJobsDataPlane\\IoTJobsDataPlaneClient' => $vendorDir . '/aws/aws-sdk-php/src/IoTJobsDataPlane/IoTJobsDataPlaneClient.php', + 'Aws\\IoTManagedIntegrations\\Exception\\IoTManagedIntegrationsException' => $vendorDir . '/aws/aws-sdk-php/src/IoTManagedIntegrations/Exception/IoTManagedIntegrationsException.php', + 'Aws\\IoTManagedIntegrations\\IoTManagedIntegrationsClient' => $vendorDir . '/aws/aws-sdk-php/src/IoTManagedIntegrations/IoTManagedIntegrationsClient.php', + 'Aws\\IoTSecureTunneling\\Exception\\IoTSecureTunnelingException' => $vendorDir . '/aws/aws-sdk-php/src/IoTSecureTunneling/Exception/IoTSecureTunnelingException.php', + 'Aws\\IoTSecureTunneling\\IoTSecureTunnelingClient' => $vendorDir . '/aws/aws-sdk-php/src/IoTSecureTunneling/IoTSecureTunnelingClient.php', + 'Aws\\IoTSiteWise\\Exception\\IoTSiteWiseException' => $vendorDir . '/aws/aws-sdk-php/src/IoTSiteWise/Exception/IoTSiteWiseException.php', + 'Aws\\IoTSiteWise\\IoTSiteWiseClient' => $vendorDir . '/aws/aws-sdk-php/src/IoTSiteWise/IoTSiteWiseClient.php', + 'Aws\\IoTThingsGraph\\Exception\\IoTThingsGraphException' => $vendorDir . '/aws/aws-sdk-php/src/IoTThingsGraph/Exception/IoTThingsGraphException.php', + 'Aws\\IoTThingsGraph\\IoTThingsGraphClient' => $vendorDir . '/aws/aws-sdk-php/src/IoTThingsGraph/IoTThingsGraphClient.php', + 'Aws\\IoTTwinMaker\\Exception\\IoTTwinMakerException' => $vendorDir . '/aws/aws-sdk-php/src/IoTTwinMaker/Exception/IoTTwinMakerException.php', + 'Aws\\IoTTwinMaker\\IoTTwinMakerClient' => $vendorDir . '/aws/aws-sdk-php/src/IoTTwinMaker/IoTTwinMakerClient.php', + 'Aws\\IoTWireless\\Exception\\IoTWirelessException' => $vendorDir . '/aws/aws-sdk-php/src/IoTWireless/Exception/IoTWirelessException.php', + 'Aws\\IoTWireless\\IoTWirelessClient' => $vendorDir . '/aws/aws-sdk-php/src/IoTWireless/IoTWirelessClient.php', + 'Aws\\IotDataPlane\\Exception\\IotDataPlaneException' => $vendorDir . '/aws/aws-sdk-php/src/IotDataPlane/Exception/IotDataPlaneException.php', + 'Aws\\IotDataPlane\\IotDataPlaneClient' => $vendorDir . '/aws/aws-sdk-php/src/IotDataPlane/IotDataPlaneClient.php', + 'Aws\\Iot\\Exception\\IotException' => $vendorDir . '/aws/aws-sdk-php/src/Iot/Exception/IotException.php', + 'Aws\\Iot\\IotClient' => $vendorDir . '/aws/aws-sdk-php/src/Iot/IotClient.php', + 'Aws\\JsonCompiler' => $vendorDir . '/aws/aws-sdk-php/src/JsonCompiler.php', + 'Aws\\KafkaConnect\\Exception\\KafkaConnectException' => $vendorDir . '/aws/aws-sdk-php/src/KafkaConnect/Exception/KafkaConnectException.php', + 'Aws\\KafkaConnect\\KafkaConnectClient' => $vendorDir . '/aws/aws-sdk-php/src/KafkaConnect/KafkaConnectClient.php', + 'Aws\\Kafka\\Exception\\KafkaException' => $vendorDir . '/aws/aws-sdk-php/src/Kafka/Exception/KafkaException.php', + 'Aws\\Kafka\\KafkaClient' => $vendorDir . '/aws/aws-sdk-php/src/Kafka/KafkaClient.php', + 'Aws\\KendraRanking\\Exception\\KendraRankingException' => $vendorDir . '/aws/aws-sdk-php/src/KendraRanking/Exception/KendraRankingException.php', + 'Aws\\KendraRanking\\KendraRankingClient' => $vendorDir . '/aws/aws-sdk-php/src/KendraRanking/KendraRankingClient.php', + 'Aws\\KeyspacesStreams\\Exception\\KeyspacesStreamsException' => $vendorDir . '/aws/aws-sdk-php/src/KeyspacesStreams/Exception/KeyspacesStreamsException.php', + 'Aws\\KeyspacesStreams\\KeyspacesStreamsClient' => $vendorDir . '/aws/aws-sdk-php/src/KeyspacesStreams/KeyspacesStreamsClient.php', + 'Aws\\Keyspaces\\Exception\\KeyspacesException' => $vendorDir . '/aws/aws-sdk-php/src/Keyspaces/Exception/KeyspacesException.php', + 'Aws\\Keyspaces\\KeyspacesClient' => $vendorDir . '/aws/aws-sdk-php/src/Keyspaces/KeyspacesClient.php', + 'Aws\\KinesisAnalyticsV2\\Exception\\KinesisAnalyticsV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/KinesisAnalyticsV2/Exception/KinesisAnalyticsV2Exception.php', + 'Aws\\KinesisAnalyticsV2\\KinesisAnalyticsV2Client' => $vendorDir . '/aws/aws-sdk-php/src/KinesisAnalyticsV2/KinesisAnalyticsV2Client.php', + 'Aws\\KinesisAnalytics\\Exception\\KinesisAnalyticsException' => $vendorDir . '/aws/aws-sdk-php/src/KinesisAnalytics/Exception/KinesisAnalyticsException.php', + 'Aws\\KinesisAnalytics\\KinesisAnalyticsClient' => $vendorDir . '/aws/aws-sdk-php/src/KinesisAnalytics/KinesisAnalyticsClient.php', + 'Aws\\KinesisVideoArchivedMedia\\Exception\\KinesisVideoArchivedMediaException' => $vendorDir . '/aws/aws-sdk-php/src/KinesisVideoArchivedMedia/Exception/KinesisVideoArchivedMediaException.php', + 'Aws\\KinesisVideoArchivedMedia\\KinesisVideoArchivedMediaClient' => $vendorDir . '/aws/aws-sdk-php/src/KinesisVideoArchivedMedia/KinesisVideoArchivedMediaClient.php', + 'Aws\\KinesisVideoMedia\\Exception\\KinesisVideoMediaException' => $vendorDir . '/aws/aws-sdk-php/src/KinesisVideoMedia/Exception/KinesisVideoMediaException.php', + 'Aws\\KinesisVideoMedia\\KinesisVideoMediaClient' => $vendorDir . '/aws/aws-sdk-php/src/KinesisVideoMedia/KinesisVideoMediaClient.php', + 'Aws\\KinesisVideoSignalingChannels\\Exception\\KinesisVideoSignalingChannelsException' => $vendorDir . '/aws/aws-sdk-php/src/KinesisVideoSignalingChannels/Exception/KinesisVideoSignalingChannelsException.php', + 'Aws\\KinesisVideoSignalingChannels\\KinesisVideoSignalingChannelsClient' => $vendorDir . '/aws/aws-sdk-php/src/KinesisVideoSignalingChannels/KinesisVideoSignalingChannelsClient.php', + 'Aws\\KinesisVideoWebRTCStorage\\Exception\\KinesisVideoWebRTCStorageException' => $vendorDir . '/aws/aws-sdk-php/src/KinesisVideoWebRTCStorage/Exception/KinesisVideoWebRTCStorageException.php', + 'Aws\\KinesisVideoWebRTCStorage\\KinesisVideoWebRTCStorageClient' => $vendorDir . '/aws/aws-sdk-php/src/KinesisVideoWebRTCStorage/KinesisVideoWebRTCStorageClient.php', + 'Aws\\KinesisVideo\\Exception\\KinesisVideoException' => $vendorDir . '/aws/aws-sdk-php/src/KinesisVideo/Exception/KinesisVideoException.php', + 'Aws\\KinesisVideo\\KinesisVideoClient' => $vendorDir . '/aws/aws-sdk-php/src/KinesisVideo/KinesisVideoClient.php', + 'Aws\\Kinesis\\Exception\\KinesisException' => $vendorDir . '/aws/aws-sdk-php/src/Kinesis/Exception/KinesisException.php', + 'Aws\\Kinesis\\KinesisClient' => $vendorDir . '/aws/aws-sdk-php/src/Kinesis/KinesisClient.php', + 'Aws\\Kms\\Exception\\KmsException' => $vendorDir . '/aws/aws-sdk-php/src/Kms/Exception/KmsException.php', + 'Aws\\Kms\\KmsClient' => $vendorDir . '/aws/aws-sdk-php/src/Kms/KmsClient.php', + 'Aws\\LakeFormation\\Exception\\LakeFormationException' => $vendorDir . '/aws/aws-sdk-php/src/LakeFormation/Exception/LakeFormationException.php', + 'Aws\\LakeFormation\\LakeFormationClient' => $vendorDir . '/aws/aws-sdk-php/src/LakeFormation/LakeFormationClient.php', + 'Aws\\LambdaCore\\Exception\\LambdaCoreException' => $vendorDir . '/aws/aws-sdk-php/src/LambdaCore/Exception/LambdaCoreException.php', + 'Aws\\LambdaCore\\LambdaCoreClient' => $vendorDir . '/aws/aws-sdk-php/src/LambdaCore/LambdaCoreClient.php', + 'Aws\\LambdaMicrovms\\Exception\\LambdaMicrovmsException' => $vendorDir . '/aws/aws-sdk-php/src/LambdaMicrovms/Exception/LambdaMicrovmsException.php', + 'Aws\\LambdaMicrovms\\LambdaMicrovmsClient' => $vendorDir . '/aws/aws-sdk-php/src/LambdaMicrovms/LambdaMicrovmsClient.php', + 'Aws\\Lambda\\Exception\\LambdaException' => $vendorDir . '/aws/aws-sdk-php/src/Lambda/Exception/LambdaException.php', + 'Aws\\Lambda\\LambdaClient' => $vendorDir . '/aws/aws-sdk-php/src/Lambda/LambdaClient.php', + 'Aws\\LaunchWizard\\Exception\\LaunchWizardException' => $vendorDir . '/aws/aws-sdk-php/src/LaunchWizard/Exception/LaunchWizardException.php', + 'Aws\\LaunchWizard\\LaunchWizardClient' => $vendorDir . '/aws/aws-sdk-php/src/LaunchWizard/LaunchWizardClient.php', + 'Aws\\LexModelBuildingService\\Exception\\LexModelBuildingServiceException' => $vendorDir . '/aws/aws-sdk-php/src/LexModelBuildingService/Exception/LexModelBuildingServiceException.php', + 'Aws\\LexModelBuildingService\\LexModelBuildingServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/LexModelBuildingService/LexModelBuildingServiceClient.php', + 'Aws\\LexModelsV2\\Exception\\LexModelsV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/LexModelsV2/Exception/LexModelsV2Exception.php', + 'Aws\\LexModelsV2\\LexModelsV2Client' => $vendorDir . '/aws/aws-sdk-php/src/LexModelsV2/LexModelsV2Client.php', + 'Aws\\LexRuntimeService\\Exception\\LexRuntimeServiceException' => $vendorDir . '/aws/aws-sdk-php/src/LexRuntimeService/Exception/LexRuntimeServiceException.php', + 'Aws\\LexRuntimeService\\LexRuntimeServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/LexRuntimeService/LexRuntimeServiceClient.php', + 'Aws\\LexRuntimeV2\\Exception\\LexRuntimeV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/LexRuntimeV2/Exception/LexRuntimeV2Exception.php', + 'Aws\\LexRuntimeV2\\LexRuntimeV2Client' => $vendorDir . '/aws/aws-sdk-php/src/LexRuntimeV2/LexRuntimeV2Client.php', + 'Aws\\LicenseManagerLinuxSubscriptions\\Exception\\LicenseManagerLinuxSubscriptionsException' => $vendorDir . '/aws/aws-sdk-php/src/LicenseManagerLinuxSubscriptions/Exception/LicenseManagerLinuxSubscriptionsException.php', + 'Aws\\LicenseManagerLinuxSubscriptions\\LicenseManagerLinuxSubscriptionsClient' => $vendorDir . '/aws/aws-sdk-php/src/LicenseManagerLinuxSubscriptions/LicenseManagerLinuxSubscriptionsClient.php', + 'Aws\\LicenseManagerUserSubscriptions\\Exception\\LicenseManagerUserSubscriptionsException' => $vendorDir . '/aws/aws-sdk-php/src/LicenseManagerUserSubscriptions/Exception/LicenseManagerUserSubscriptionsException.php', + 'Aws\\LicenseManagerUserSubscriptions\\LicenseManagerUserSubscriptionsClient' => $vendorDir . '/aws/aws-sdk-php/src/LicenseManagerUserSubscriptions/LicenseManagerUserSubscriptionsClient.php', + 'Aws\\LicenseManager\\Exception\\LicenseManagerException' => $vendorDir . '/aws/aws-sdk-php/src/LicenseManager/Exception/LicenseManagerException.php', + 'Aws\\LicenseManager\\LicenseManagerClient' => $vendorDir . '/aws/aws-sdk-php/src/LicenseManager/LicenseManagerClient.php', + 'Aws\\Lightsail\\Exception\\LightsailException' => $vendorDir . '/aws/aws-sdk-php/src/Lightsail/Exception/LightsailException.php', + 'Aws\\Lightsail\\LightsailClient' => $vendorDir . '/aws/aws-sdk-php/src/Lightsail/LightsailClient.php', + 'Aws\\LocationService\\Exception\\LocationServiceException' => $vendorDir . '/aws/aws-sdk-php/src/LocationService/Exception/LocationServiceException.php', + 'Aws\\LocationService\\LocationServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/LocationService/LocationServiceClient.php', + 'Aws\\LookoutEquipment\\Exception\\LookoutEquipmentException' => $vendorDir . '/aws/aws-sdk-php/src/LookoutEquipment/Exception/LookoutEquipmentException.php', + 'Aws\\LookoutEquipment\\LookoutEquipmentClient' => $vendorDir . '/aws/aws-sdk-php/src/LookoutEquipment/LookoutEquipmentClient.php', + 'Aws\\LruArrayCache' => $vendorDir . '/aws/aws-sdk-php/src/LruArrayCache.php', + 'Aws\\MPA\\Exception\\MPAException' => $vendorDir . '/aws/aws-sdk-php/src/MPA/Exception/MPAException.php', + 'Aws\\MPA\\MPAClient' => $vendorDir . '/aws/aws-sdk-php/src/MPA/MPAClient.php', + 'Aws\\MQ\\Exception\\MQException' => $vendorDir . '/aws/aws-sdk-php/src/MQ/Exception/MQException.php', + 'Aws\\MQ\\MQClient' => $vendorDir . '/aws/aws-sdk-php/src/MQ/MQClient.php', + 'Aws\\MTurk\\Exception\\MTurkException' => $vendorDir . '/aws/aws-sdk-php/src/MTurk/Exception/MTurkException.php', + 'Aws\\MTurk\\MTurkClient' => $vendorDir . '/aws/aws-sdk-php/src/MTurk/MTurkClient.php', + 'Aws\\MWAAServerless\\Exception\\MWAAServerlessException' => $vendorDir . '/aws/aws-sdk-php/src/MWAAServerless/Exception/MWAAServerlessException.php', + 'Aws\\MWAAServerless\\MWAAServerlessClient' => $vendorDir . '/aws/aws-sdk-php/src/MWAAServerless/MWAAServerlessClient.php', + 'Aws\\MWAA\\Exception\\MWAAException' => $vendorDir . '/aws/aws-sdk-php/src/MWAA/Exception/MWAAException.php', + 'Aws\\MWAA\\MWAAClient' => $vendorDir . '/aws/aws-sdk-php/src/MWAA/MWAAClient.php', + 'Aws\\MachineLearning\\Exception\\MachineLearningException' => $vendorDir . '/aws/aws-sdk-php/src/MachineLearning/Exception/MachineLearningException.php', + 'Aws\\MachineLearning\\MachineLearningClient' => $vendorDir . '/aws/aws-sdk-php/src/MachineLearning/MachineLearningClient.php', + 'Aws\\Macie2\\Exception\\Macie2Exception' => $vendorDir . '/aws/aws-sdk-php/src/Macie2/Exception/Macie2Exception.php', + 'Aws\\Macie2\\Macie2Client' => $vendorDir . '/aws/aws-sdk-php/src/Macie2/Macie2Client.php', + 'Aws\\MailManager\\Exception\\MailManagerException' => $vendorDir . '/aws/aws-sdk-php/src/MailManager/Exception/MailManagerException.php', + 'Aws\\MailManager\\MailManagerClient' => $vendorDir . '/aws/aws-sdk-php/src/MailManager/MailManagerClient.php', + 'Aws\\MainframeModernization\\Exception\\MainframeModernizationException' => $vendorDir . '/aws/aws-sdk-php/src/MainframeModernization/Exception/MainframeModernizationException.php', + 'Aws\\MainframeModernization\\MainframeModernizationClient' => $vendorDir . '/aws/aws-sdk-php/src/MainframeModernization/MainframeModernizationClient.php', + 'Aws\\ManagedBlockchainQuery\\Exception\\ManagedBlockchainQueryException' => $vendorDir . '/aws/aws-sdk-php/src/ManagedBlockchainQuery/Exception/ManagedBlockchainQueryException.php', + 'Aws\\ManagedBlockchainQuery\\ManagedBlockchainQueryClient' => $vendorDir . '/aws/aws-sdk-php/src/ManagedBlockchainQuery/ManagedBlockchainQueryClient.php', + 'Aws\\ManagedBlockchain\\Exception\\ManagedBlockchainException' => $vendorDir . '/aws/aws-sdk-php/src/ManagedBlockchain/Exception/ManagedBlockchainException.php', + 'Aws\\ManagedBlockchain\\ManagedBlockchainClient' => $vendorDir . '/aws/aws-sdk-php/src/ManagedBlockchain/ManagedBlockchainClient.php', + 'Aws\\ManagedGrafana\\Exception\\ManagedGrafanaException' => $vendorDir . '/aws/aws-sdk-php/src/ManagedGrafana/Exception/ManagedGrafanaException.php', + 'Aws\\ManagedGrafana\\ManagedGrafanaClient' => $vendorDir . '/aws/aws-sdk-php/src/ManagedGrafana/ManagedGrafanaClient.php', + 'Aws\\MarketplaceAgreement\\Exception\\MarketplaceAgreementException' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceAgreement/Exception/MarketplaceAgreementException.php', + 'Aws\\MarketplaceAgreement\\MarketplaceAgreementClient' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceAgreement/MarketplaceAgreementClient.php', + 'Aws\\MarketplaceCatalog\\Exception\\MarketplaceCatalogException' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceCatalog/Exception/MarketplaceCatalogException.php', + 'Aws\\MarketplaceCatalog\\MarketplaceCatalogClient' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceCatalog/MarketplaceCatalogClient.php', + 'Aws\\MarketplaceCommerceAnalytics\\Exception\\MarketplaceCommerceAnalyticsException' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceCommerceAnalytics/Exception/MarketplaceCommerceAnalyticsException.php', + 'Aws\\MarketplaceCommerceAnalytics\\MarketplaceCommerceAnalyticsClient' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceCommerceAnalytics/MarketplaceCommerceAnalyticsClient.php', + 'Aws\\MarketplaceDeployment\\Exception\\MarketplaceDeploymentException' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceDeployment/Exception/MarketplaceDeploymentException.php', + 'Aws\\MarketplaceDeployment\\MarketplaceDeploymentClient' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceDeployment/MarketplaceDeploymentClient.php', + 'Aws\\MarketplaceDiscovery\\Exception\\MarketplaceDiscoveryException' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceDiscovery/Exception/MarketplaceDiscoveryException.php', + 'Aws\\MarketplaceDiscovery\\MarketplaceDiscoveryClient' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceDiscovery/MarketplaceDiscoveryClient.php', + 'Aws\\MarketplaceEntitlementService\\Exception\\MarketplaceEntitlementServiceException' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceEntitlementService/Exception/MarketplaceEntitlementServiceException.php', + 'Aws\\MarketplaceEntitlementService\\MarketplaceEntitlementServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceEntitlementService/MarketplaceEntitlementServiceClient.php', + 'Aws\\MarketplaceMetering\\Exception\\MarketplaceMeteringException' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceMetering/Exception/MarketplaceMeteringException.php', + 'Aws\\MarketplaceMetering\\MarketplaceMeteringClient' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceMetering/MarketplaceMeteringClient.php', + 'Aws\\MarketplaceReporting\\Exception\\MarketplaceReportingException' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceReporting/Exception/MarketplaceReportingException.php', + 'Aws\\MarketplaceReporting\\MarketplaceReportingClient' => $vendorDir . '/aws/aws-sdk-php/src/MarketplaceReporting/MarketplaceReportingClient.php', + 'Aws\\MediaConnect\\Exception\\MediaConnectException' => $vendorDir . '/aws/aws-sdk-php/src/MediaConnect/Exception/MediaConnectException.php', + 'Aws\\MediaConnect\\MediaConnectClient' => $vendorDir . '/aws/aws-sdk-php/src/MediaConnect/MediaConnectClient.php', + 'Aws\\MediaConvert\\Exception\\MediaConvertException' => $vendorDir . '/aws/aws-sdk-php/src/MediaConvert/Exception/MediaConvertException.php', + 'Aws\\MediaConvert\\MediaConvertClient' => $vendorDir . '/aws/aws-sdk-php/src/MediaConvert/MediaConvertClient.php', + 'Aws\\MediaLive\\Exception\\MediaLiveException' => $vendorDir . '/aws/aws-sdk-php/src/MediaLive/Exception/MediaLiveException.php', + 'Aws\\MediaLive\\MediaLiveClient' => $vendorDir . '/aws/aws-sdk-php/src/MediaLive/MediaLiveClient.php', + 'Aws\\MediaPackageV2\\Exception\\MediaPackageV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/MediaPackageV2/Exception/MediaPackageV2Exception.php', + 'Aws\\MediaPackageV2\\MediaPackageV2Client' => $vendorDir . '/aws/aws-sdk-php/src/MediaPackageV2/MediaPackageV2Client.php', + 'Aws\\MediaPackageVod\\Exception\\MediaPackageVodException' => $vendorDir . '/aws/aws-sdk-php/src/MediaPackageVod/Exception/MediaPackageVodException.php', + 'Aws\\MediaPackageVod\\MediaPackageVodClient' => $vendorDir . '/aws/aws-sdk-php/src/MediaPackageVod/MediaPackageVodClient.php', + 'Aws\\MediaPackage\\Exception\\MediaPackageException' => $vendorDir . '/aws/aws-sdk-php/src/MediaPackage/Exception/MediaPackageException.php', + 'Aws\\MediaPackage\\MediaPackageClient' => $vendorDir . '/aws/aws-sdk-php/src/MediaPackage/MediaPackageClient.php', + 'Aws\\MediaStoreData\\Exception\\MediaStoreDataException' => $vendorDir . '/aws/aws-sdk-php/src/MediaStoreData/Exception/MediaStoreDataException.php', + 'Aws\\MediaStoreData\\MediaStoreDataClient' => $vendorDir . '/aws/aws-sdk-php/src/MediaStoreData/MediaStoreDataClient.php', + 'Aws\\MediaStore\\Exception\\MediaStoreException' => $vendorDir . '/aws/aws-sdk-php/src/MediaStore/Exception/MediaStoreException.php', + 'Aws\\MediaStore\\MediaStoreClient' => $vendorDir . '/aws/aws-sdk-php/src/MediaStore/MediaStoreClient.php', + 'Aws\\MediaTailor\\Exception\\MediaTailorException' => $vendorDir . '/aws/aws-sdk-php/src/MediaTailor/Exception/MediaTailorException.php', + 'Aws\\MediaTailor\\MediaTailorClient' => $vendorDir . '/aws/aws-sdk-php/src/MediaTailor/MediaTailorClient.php', + 'Aws\\MedicalImaging\\Exception\\MedicalImagingException' => $vendorDir . '/aws/aws-sdk-php/src/MedicalImaging/Exception/MedicalImagingException.php', + 'Aws\\MedicalImaging\\MedicalImagingClient' => $vendorDir . '/aws/aws-sdk-php/src/MedicalImaging/MedicalImagingClient.php', + 'Aws\\MemoryDB\\Exception\\MemoryDBException' => $vendorDir . '/aws/aws-sdk-php/src/MemoryDB/Exception/MemoryDBException.php', + 'Aws\\MemoryDB\\MemoryDBClient' => $vendorDir . '/aws/aws-sdk-php/src/MemoryDB/MemoryDBClient.php', + 'Aws\\MetricsBuilder' => $vendorDir . '/aws/aws-sdk-php/src/MetricsBuilder.php', + 'Aws\\Middleware' => $vendorDir . '/aws/aws-sdk-php/src/Middleware.php', + 'Aws\\MigrationHubConfig\\Exception\\MigrationHubConfigException' => $vendorDir . '/aws/aws-sdk-php/src/MigrationHubConfig/Exception/MigrationHubConfigException.php', + 'Aws\\MigrationHubConfig\\MigrationHubConfigClient' => $vendorDir . '/aws/aws-sdk-php/src/MigrationHubConfig/MigrationHubConfigClient.php', + 'Aws\\MigrationHubOrchestrator\\Exception\\MigrationHubOrchestratorException' => $vendorDir . '/aws/aws-sdk-php/src/MigrationHubOrchestrator/Exception/MigrationHubOrchestratorException.php', + 'Aws\\MigrationHubOrchestrator\\MigrationHubOrchestratorClient' => $vendorDir . '/aws/aws-sdk-php/src/MigrationHubOrchestrator/MigrationHubOrchestratorClient.php', + 'Aws\\MigrationHubRefactorSpaces\\Exception\\MigrationHubRefactorSpacesException' => $vendorDir . '/aws/aws-sdk-php/src/MigrationHubRefactorSpaces/Exception/MigrationHubRefactorSpacesException.php', + 'Aws\\MigrationHubRefactorSpaces\\MigrationHubRefactorSpacesClient' => $vendorDir . '/aws/aws-sdk-php/src/MigrationHubRefactorSpaces/MigrationHubRefactorSpacesClient.php', + 'Aws\\MigrationHubStrategyRecommendations\\Exception\\MigrationHubStrategyRecommendationsException' => $vendorDir . '/aws/aws-sdk-php/src/MigrationHubStrategyRecommendations/Exception/MigrationHubStrategyRecommendationsException.php', + 'Aws\\MigrationHubStrategyRecommendations\\MigrationHubStrategyRecommendationsClient' => $vendorDir . '/aws/aws-sdk-php/src/MigrationHubStrategyRecommendations/MigrationHubStrategyRecommendationsClient.php', + 'Aws\\MigrationHub\\Exception\\MigrationHubException' => $vendorDir . '/aws/aws-sdk-php/src/MigrationHub/Exception/MigrationHubException.php', + 'Aws\\MigrationHub\\MigrationHubClient' => $vendorDir . '/aws/aws-sdk-php/src/MigrationHub/MigrationHubClient.php', + 'Aws\\MockHandler' => $vendorDir . '/aws/aws-sdk-php/src/MockHandler.php', + 'Aws\\MonitoringEventsInterface' => $vendorDir . '/aws/aws-sdk-php/src/MonitoringEventsInterface.php', + 'Aws\\MultiRegionClient' => $vendorDir . '/aws/aws-sdk-php/src/MultiRegionClient.php', + 'Aws\\Multipart\\AbstractUploadManager' => $vendorDir . '/aws/aws-sdk-php/src/Multipart/AbstractUploadManager.php', + 'Aws\\Multipart\\AbstractUploader' => $vendorDir . '/aws/aws-sdk-php/src/Multipart/AbstractUploader.php', + 'Aws\\Multipart\\UploadState' => $vendorDir . '/aws/aws-sdk-php/src/Multipart/UploadState.php', + 'Aws\\NeptuneGraph\\Exception\\NeptuneGraphException' => $vendorDir . '/aws/aws-sdk-php/src/NeptuneGraph/Exception/NeptuneGraphException.php', + 'Aws\\NeptuneGraph\\NeptuneGraphClient' => $vendorDir . '/aws/aws-sdk-php/src/NeptuneGraph/NeptuneGraphClient.php', + 'Aws\\Neptune\\Exception\\NeptuneException' => $vendorDir . '/aws/aws-sdk-php/src/Neptune/Exception/NeptuneException.php', + 'Aws\\Neptune\\NeptuneClient' => $vendorDir . '/aws/aws-sdk-php/src/Neptune/NeptuneClient.php', + 'Aws\\Neptunedata\\Exception\\NeptunedataException' => $vendorDir . '/aws/aws-sdk-php/src/Neptunedata/Exception/NeptunedataException.php', + 'Aws\\Neptunedata\\NeptunedataClient' => $vendorDir . '/aws/aws-sdk-php/src/Neptunedata/NeptunedataClient.php', + 'Aws\\NetworkFirewall\\Exception\\NetworkFirewallException' => $vendorDir . '/aws/aws-sdk-php/src/NetworkFirewall/Exception/NetworkFirewallException.php', + 'Aws\\NetworkFirewall\\NetworkFirewallClient' => $vendorDir . '/aws/aws-sdk-php/src/NetworkFirewall/NetworkFirewallClient.php', + 'Aws\\NetworkFlowMonitor\\Exception\\NetworkFlowMonitorException' => $vendorDir . '/aws/aws-sdk-php/src/NetworkFlowMonitor/Exception/NetworkFlowMonitorException.php', + 'Aws\\NetworkFlowMonitor\\NetworkFlowMonitorClient' => $vendorDir . '/aws/aws-sdk-php/src/NetworkFlowMonitor/NetworkFlowMonitorClient.php', + 'Aws\\NetworkManager\\Exception\\NetworkManagerException' => $vendorDir . '/aws/aws-sdk-php/src/NetworkManager/Exception/NetworkManagerException.php', + 'Aws\\NetworkManager\\NetworkManagerClient' => $vendorDir . '/aws/aws-sdk-php/src/NetworkManager/NetworkManagerClient.php', + 'Aws\\NetworkMonitor\\Exception\\NetworkMonitorException' => $vendorDir . '/aws/aws-sdk-php/src/NetworkMonitor/Exception/NetworkMonitorException.php', + 'Aws\\NetworkMonitor\\NetworkMonitorClient' => $vendorDir . '/aws/aws-sdk-php/src/NetworkMonitor/NetworkMonitorClient.php', + 'Aws\\NotificationsContacts\\Exception\\NotificationsContactsException' => $vendorDir . '/aws/aws-sdk-php/src/NotificationsContacts/Exception/NotificationsContactsException.php', + 'Aws\\NotificationsContacts\\NotificationsContactsClient' => $vendorDir . '/aws/aws-sdk-php/src/NotificationsContacts/NotificationsContactsClient.php', + 'Aws\\Notifications\\Exception\\NotificationsException' => $vendorDir . '/aws/aws-sdk-php/src/Notifications/Exception/NotificationsException.php', + 'Aws\\Notifications\\NotificationsClient' => $vendorDir . '/aws/aws-sdk-php/src/Notifications/NotificationsClient.php', + 'Aws\\NovaAct\\Exception\\NovaActException' => $vendorDir . '/aws/aws-sdk-php/src/NovaAct/Exception/NovaActException.php', + 'Aws\\NovaAct\\NovaActClient' => $vendorDir . '/aws/aws-sdk-php/src/NovaAct/NovaActClient.php', + 'Aws\\OAM\\Exception\\OAMException' => $vendorDir . '/aws/aws-sdk-php/src/OAM/Exception/OAMException.php', + 'Aws\\OAM\\OAMClient' => $vendorDir . '/aws/aws-sdk-php/src/OAM/OAMClient.php', + 'Aws\\OSIS\\Exception\\OSISException' => $vendorDir . '/aws/aws-sdk-php/src/OSIS/Exception/OSISException.php', + 'Aws\\OSIS\\OSISClient' => $vendorDir . '/aws/aws-sdk-php/src/OSIS/OSISClient.php', + 'Aws\\ObservabilityAdmin\\Exception\\ObservabilityAdminException' => $vendorDir . '/aws/aws-sdk-php/src/ObservabilityAdmin/Exception/ObservabilityAdminException.php', + 'Aws\\ObservabilityAdmin\\ObservabilityAdminClient' => $vendorDir . '/aws/aws-sdk-php/src/ObservabilityAdmin/ObservabilityAdminClient.php', + 'Aws\\Odb\\Exception\\OdbException' => $vendorDir . '/aws/aws-sdk-php/src/Odb/Exception/OdbException.php', + 'Aws\\Odb\\OdbClient' => $vendorDir . '/aws/aws-sdk-php/src/Odb/OdbClient.php', + 'Aws\\Omics\\Exception\\OmicsException' => $vendorDir . '/aws/aws-sdk-php/src/Omics/Exception/OmicsException.php', + 'Aws\\Omics\\OmicsClient' => $vendorDir . '/aws/aws-sdk-php/src/Omics/OmicsClient.php', + 'Aws\\OpenSearchServerless\\Exception\\OpenSearchServerlessException' => $vendorDir . '/aws/aws-sdk-php/src/OpenSearchServerless/Exception/OpenSearchServerlessException.php', + 'Aws\\OpenSearchServerless\\OpenSearchServerlessClient' => $vendorDir . '/aws/aws-sdk-php/src/OpenSearchServerless/OpenSearchServerlessClient.php', + 'Aws\\OpenSearchService\\Exception\\OpenSearchServiceException' => $vendorDir . '/aws/aws-sdk-php/src/OpenSearchService/Exception/OpenSearchServiceException.php', + 'Aws\\OpenSearchService\\OpenSearchServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/OpenSearchService/OpenSearchServiceClient.php', + 'Aws\\Organizations\\Exception\\OrganizationsException' => $vendorDir . '/aws/aws-sdk-php/src/Organizations/Exception/OrganizationsException.php', + 'Aws\\Organizations\\OrganizationsClient' => $vendorDir . '/aws/aws-sdk-php/src/Organizations/OrganizationsClient.php', + 'Aws\\Outposts\\Exception\\OutpostsException' => $vendorDir . '/aws/aws-sdk-php/src/Outposts/Exception/OutpostsException.php', + 'Aws\\Outposts\\OutpostsClient' => $vendorDir . '/aws/aws-sdk-php/src/Outposts/OutpostsClient.php', + 'Aws\\PCS\\Exception\\PCSException' => $vendorDir . '/aws/aws-sdk-php/src/PCS/Exception/PCSException.php', + 'Aws\\PCS\\PCSClient' => $vendorDir . '/aws/aws-sdk-php/src/PCS/PCSClient.php', + 'Aws\\PI\\Exception\\PIException' => $vendorDir . '/aws/aws-sdk-php/src/PI/Exception/PIException.php', + 'Aws\\PI\\PIClient' => $vendorDir . '/aws/aws-sdk-php/src/PI/PIClient.php', + 'Aws\\PartnerCentralAccount\\Exception\\PartnerCentralAccountException' => $vendorDir . '/aws/aws-sdk-php/src/PartnerCentralAccount/Exception/PartnerCentralAccountException.php', + 'Aws\\PartnerCentralAccount\\PartnerCentralAccountClient' => $vendorDir . '/aws/aws-sdk-php/src/PartnerCentralAccount/PartnerCentralAccountClient.php', + 'Aws\\PartnerCentralBenefits\\Exception\\PartnerCentralBenefitsException' => $vendorDir . '/aws/aws-sdk-php/src/PartnerCentralBenefits/Exception/PartnerCentralBenefitsException.php', + 'Aws\\PartnerCentralBenefits\\PartnerCentralBenefitsClient' => $vendorDir . '/aws/aws-sdk-php/src/PartnerCentralBenefits/PartnerCentralBenefitsClient.php', + 'Aws\\PartnerCentralChannel\\Exception\\PartnerCentralChannelException' => $vendorDir . '/aws/aws-sdk-php/src/PartnerCentralChannel/Exception/PartnerCentralChannelException.php', + 'Aws\\PartnerCentralChannel\\PartnerCentralChannelClient' => $vendorDir . '/aws/aws-sdk-php/src/PartnerCentralChannel/PartnerCentralChannelClient.php', + 'Aws\\PartnerCentralRevenueMeasurement\\Exception\\PartnerCentralRevenueMeasurementException' => $vendorDir . '/aws/aws-sdk-php/src/PartnerCentralRevenueMeasurement/Exception/PartnerCentralRevenueMeasurementException.php', + 'Aws\\PartnerCentralRevenueMeasurement\\PartnerCentralRevenueMeasurementClient' => $vendorDir . '/aws/aws-sdk-php/src/PartnerCentralRevenueMeasurement/PartnerCentralRevenueMeasurementClient.php', + 'Aws\\PartnerCentralSelling\\Exception\\PartnerCentralSellingException' => $vendorDir . '/aws/aws-sdk-php/src/PartnerCentralSelling/Exception/PartnerCentralSellingException.php', + 'Aws\\PartnerCentralSelling\\PartnerCentralSellingClient' => $vendorDir . '/aws/aws-sdk-php/src/PartnerCentralSelling/PartnerCentralSellingClient.php', + 'Aws\\PaymentCryptographyData\\Exception\\PaymentCryptographyDataException' => $vendorDir . '/aws/aws-sdk-php/src/PaymentCryptographyData/Exception/PaymentCryptographyDataException.php', + 'Aws\\PaymentCryptographyData\\PaymentCryptographyDataClient' => $vendorDir . '/aws/aws-sdk-php/src/PaymentCryptographyData/PaymentCryptographyDataClient.php', + 'Aws\\PaymentCryptography\\Exception\\PaymentCryptographyException' => $vendorDir . '/aws/aws-sdk-php/src/PaymentCryptography/Exception/PaymentCryptographyException.php', + 'Aws\\PaymentCryptography\\PaymentCryptographyClient' => $vendorDir . '/aws/aws-sdk-php/src/PaymentCryptography/PaymentCryptographyClient.php', + 'Aws\\PcaConnectorAd\\Exception\\PcaConnectorAdException' => $vendorDir . '/aws/aws-sdk-php/src/PcaConnectorAd/Exception/PcaConnectorAdException.php', + 'Aws\\PcaConnectorAd\\PcaConnectorAdClient' => $vendorDir . '/aws/aws-sdk-php/src/PcaConnectorAd/PcaConnectorAdClient.php', + 'Aws\\PcaConnectorScep\\Exception\\PcaConnectorScepException' => $vendorDir . '/aws/aws-sdk-php/src/PcaConnectorScep/Exception/PcaConnectorScepException.php', + 'Aws\\PcaConnectorScep\\PcaConnectorScepClient' => $vendorDir . '/aws/aws-sdk-php/src/PcaConnectorScep/PcaConnectorScepClient.php', + 'Aws\\PersonalizeEvents\\Exception\\PersonalizeEventsException' => $vendorDir . '/aws/aws-sdk-php/src/PersonalizeEvents/Exception/PersonalizeEventsException.php', + 'Aws\\PersonalizeEvents\\PersonalizeEventsClient' => $vendorDir . '/aws/aws-sdk-php/src/PersonalizeEvents/PersonalizeEventsClient.php', + 'Aws\\PersonalizeRuntime\\Exception\\PersonalizeRuntimeException' => $vendorDir . '/aws/aws-sdk-php/src/PersonalizeRuntime/Exception/PersonalizeRuntimeException.php', + 'Aws\\PersonalizeRuntime\\PersonalizeRuntimeClient' => $vendorDir . '/aws/aws-sdk-php/src/PersonalizeRuntime/PersonalizeRuntimeClient.php', + 'Aws\\Personalize\\Exception\\PersonalizeException' => $vendorDir . '/aws/aws-sdk-php/src/Personalize/Exception/PersonalizeException.php', + 'Aws\\Personalize\\PersonalizeClient' => $vendorDir . '/aws/aws-sdk-php/src/Personalize/PersonalizeClient.php', + 'Aws\\PhpHash' => $vendorDir . '/aws/aws-sdk-php/src/PhpHash.php', + 'Aws\\PinpointEmail\\Exception\\PinpointEmailException' => $vendorDir . '/aws/aws-sdk-php/src/PinpointEmail/Exception/PinpointEmailException.php', + 'Aws\\PinpointEmail\\PinpointEmailClient' => $vendorDir . '/aws/aws-sdk-php/src/PinpointEmail/PinpointEmailClient.php', + 'Aws\\PinpointSMSVoiceV2\\Exception\\PinpointSMSVoiceV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/PinpointSMSVoiceV2/Exception/PinpointSMSVoiceV2Exception.php', + 'Aws\\PinpointSMSVoiceV2\\PinpointSMSVoiceV2Client' => $vendorDir . '/aws/aws-sdk-php/src/PinpointSMSVoiceV2/PinpointSMSVoiceV2Client.php', + 'Aws\\PinpointSMSVoice\\Exception\\PinpointSMSVoiceException' => $vendorDir . '/aws/aws-sdk-php/src/PinpointSMSVoice/Exception/PinpointSMSVoiceException.php', + 'Aws\\PinpointSMSVoice\\PinpointSMSVoiceClient' => $vendorDir . '/aws/aws-sdk-php/src/PinpointSMSVoice/PinpointSMSVoiceClient.php', + 'Aws\\Pinpoint\\Exception\\PinpointException' => $vendorDir . '/aws/aws-sdk-php/src/Pinpoint/Exception/PinpointException.php', + 'Aws\\Pinpoint\\PinpointClient' => $vendorDir . '/aws/aws-sdk-php/src/Pinpoint/PinpointClient.php', + 'Aws\\Pipes\\Exception\\PipesException' => $vendorDir . '/aws/aws-sdk-php/src/Pipes/Exception/PipesException.php', + 'Aws\\Pipes\\PipesClient' => $vendorDir . '/aws/aws-sdk-php/src/Pipes/PipesClient.php', + 'Aws\\Polly\\Exception\\PollyException' => $vendorDir . '/aws/aws-sdk-php/src/Polly/Exception/PollyException.php', + 'Aws\\Polly\\PollyClient' => $vendorDir . '/aws/aws-sdk-php/src/Polly/PollyClient.php', + 'Aws\\PresignUrlMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/PresignUrlMiddleware.php', + 'Aws\\Pricing\\Exception\\PricingException' => $vendorDir . '/aws/aws-sdk-php/src/Pricing/Exception/PricingException.php', + 'Aws\\Pricing\\PricingClient' => $vendorDir . '/aws/aws-sdk-php/src/Pricing/PricingClient.php', + 'Aws\\PrometheusService\\Exception\\PrometheusServiceException' => $vendorDir . '/aws/aws-sdk-php/src/PrometheusService/Exception/PrometheusServiceException.php', + 'Aws\\PrometheusService\\PrometheusServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/PrometheusService/PrometheusServiceClient.php', + 'Aws\\Proton\\Exception\\ProtonException' => $vendorDir . '/aws/aws-sdk-php/src/Proton/Exception/ProtonException.php', + 'Aws\\Proton\\ProtonClient' => $vendorDir . '/aws/aws-sdk-php/src/Proton/ProtonClient.php', + 'Aws\\Psr16CacheAdapter' => $vendorDir . '/aws/aws-sdk-php/src/Psr16CacheAdapter.php', + 'Aws\\PsrCacheAdapter' => $vendorDir . '/aws/aws-sdk-php/src/PsrCacheAdapter.php', + 'Aws\\QApps\\Exception\\QAppsException' => $vendorDir . '/aws/aws-sdk-php/src/QApps/Exception/QAppsException.php', + 'Aws\\QApps\\QAppsClient' => $vendorDir . '/aws/aws-sdk-php/src/QApps/QAppsClient.php', + 'Aws\\QBusiness\\Exception\\QBusinessException' => $vendorDir . '/aws/aws-sdk-php/src/QBusiness/Exception/QBusinessException.php', + 'Aws\\QBusiness\\QBusinessClient' => $vendorDir . '/aws/aws-sdk-php/src/QBusiness/QBusinessClient.php', + 'Aws\\QConnect\\Exception\\QConnectException' => $vendorDir . '/aws/aws-sdk-php/src/QConnect/Exception/QConnectException.php', + 'Aws\\QConnect\\QConnectClient' => $vendorDir . '/aws/aws-sdk-php/src/QConnect/QConnectClient.php', + 'Aws\\QueryCompatibleInputMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/QueryCompatibleInputMiddleware.php', + 'Aws\\QuickSight\\Exception\\QuickSightException' => $vendorDir . '/aws/aws-sdk-php/src/QuickSight/Exception/QuickSightException.php', + 'Aws\\QuickSight\\QuickSightClient' => $vendorDir . '/aws/aws-sdk-php/src/QuickSight/QuickSightClient.php', + 'Aws\\RAM\\Exception\\RAMException' => $vendorDir . '/aws/aws-sdk-php/src/RAM/Exception/RAMException.php', + 'Aws\\RAM\\RAMClient' => $vendorDir . '/aws/aws-sdk-php/src/RAM/RAMClient.php', + 'Aws\\RDSDataService\\Exception\\RDSDataServiceException' => $vendorDir . '/aws/aws-sdk-php/src/RDSDataService/Exception/RDSDataServiceException.php', + 'Aws\\RDSDataService\\RDSDataServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/RDSDataService/RDSDataServiceClient.php', + 'Aws\\RTBFabric\\Exception\\RTBFabricException' => $vendorDir . '/aws/aws-sdk-php/src/RTBFabric/Exception/RTBFabricException.php', + 'Aws\\RTBFabric\\RTBFabricClient' => $vendorDir . '/aws/aws-sdk-php/src/RTBFabric/RTBFabricClient.php', + 'Aws\\Rds\\AuthTokenGenerator' => $vendorDir . '/aws/aws-sdk-php/src/Rds/AuthTokenGenerator.php', + 'Aws\\Rds\\Exception\\RdsException' => $vendorDir . '/aws/aws-sdk-php/src/Rds/Exception/RdsException.php', + 'Aws\\Rds\\RdsClient' => $vendorDir . '/aws/aws-sdk-php/src/Rds/RdsClient.php', + 'Aws\\RecycleBin\\Exception\\RecycleBinException' => $vendorDir . '/aws/aws-sdk-php/src/RecycleBin/Exception/RecycleBinException.php', + 'Aws\\RecycleBin\\RecycleBinClient' => $vendorDir . '/aws/aws-sdk-php/src/RecycleBin/RecycleBinClient.php', + 'Aws\\RedshiftDataAPIService\\Exception\\RedshiftDataAPIServiceException' => $vendorDir . '/aws/aws-sdk-php/src/RedshiftDataAPIService/Exception/RedshiftDataAPIServiceException.php', + 'Aws\\RedshiftDataAPIService\\RedshiftDataAPIServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/RedshiftDataAPIService/RedshiftDataAPIServiceClient.php', + 'Aws\\RedshiftServerless\\Exception\\RedshiftServerlessException' => $vendorDir . '/aws/aws-sdk-php/src/RedshiftServerless/Exception/RedshiftServerlessException.php', + 'Aws\\RedshiftServerless\\RedshiftServerlessClient' => $vendorDir . '/aws/aws-sdk-php/src/RedshiftServerless/RedshiftServerlessClient.php', + 'Aws\\Redshift\\Exception\\RedshiftException' => $vendorDir . '/aws/aws-sdk-php/src/Redshift/Exception/RedshiftException.php', + 'Aws\\Redshift\\RedshiftClient' => $vendorDir . '/aws/aws-sdk-php/src/Redshift/RedshiftClient.php', + 'Aws\\Rekognition\\Exception\\RekognitionException' => $vendorDir . '/aws/aws-sdk-php/src/Rekognition/Exception/RekognitionException.php', + 'Aws\\Rekognition\\RekognitionClient' => $vendorDir . '/aws/aws-sdk-php/src/Rekognition/RekognitionClient.php', + 'Aws\\Repostspace\\Exception\\RepostspaceException' => $vendorDir . '/aws/aws-sdk-php/src/Repostspace/Exception/RepostspaceException.php', + 'Aws\\Repostspace\\RepostspaceClient' => $vendorDir . '/aws/aws-sdk-php/src/Repostspace/RepostspaceClient.php', + 'Aws\\RequestCompressionMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/RequestCompressionMiddleware.php', + 'Aws\\ResilienceHub\\Exception\\ResilienceHubException' => $vendorDir . '/aws/aws-sdk-php/src/ResilienceHub/Exception/ResilienceHubException.php', + 'Aws\\ResilienceHub\\ResilienceHubClient' => $vendorDir . '/aws/aws-sdk-php/src/ResilienceHub/ResilienceHubClient.php', + 'Aws\\Resiliencehubv2\\Exception\\Resiliencehubv2Exception' => $vendorDir . '/aws/aws-sdk-php/src/Resiliencehubv2/Exception/Resiliencehubv2Exception.php', + 'Aws\\Resiliencehubv2\\Resiliencehubv2Client' => $vendorDir . '/aws/aws-sdk-php/src/Resiliencehubv2/Resiliencehubv2Client.php', + 'Aws\\ResourceExplorer2\\Exception\\ResourceExplorer2Exception' => $vendorDir . '/aws/aws-sdk-php/src/ResourceExplorer2/Exception/ResourceExplorer2Exception.php', + 'Aws\\ResourceExplorer2\\ResourceExplorer2Client' => $vendorDir . '/aws/aws-sdk-php/src/ResourceExplorer2/ResourceExplorer2Client.php', + 'Aws\\ResourceGroupsTaggingAPI\\Exception\\ResourceGroupsTaggingAPIException' => $vendorDir . '/aws/aws-sdk-php/src/ResourceGroupsTaggingAPI/Exception/ResourceGroupsTaggingAPIException.php', + 'Aws\\ResourceGroupsTaggingAPI\\ResourceGroupsTaggingAPIClient' => $vendorDir . '/aws/aws-sdk-php/src/ResourceGroupsTaggingAPI/ResourceGroupsTaggingAPIClient.php', + 'Aws\\ResourceGroups\\Exception\\ResourceGroupsException' => $vendorDir . '/aws/aws-sdk-php/src/ResourceGroups/Exception/ResourceGroupsException.php', + 'Aws\\ResourceGroups\\ResourceGroupsClient' => $vendorDir . '/aws/aws-sdk-php/src/ResourceGroups/ResourceGroupsClient.php', + 'Aws\\ResponseContainerInterface' => $vendorDir . '/aws/aws-sdk-php/src/ResponseContainerInterface.php', + 'Aws\\Result' => $vendorDir . '/aws/aws-sdk-php/src/Result.php', + 'Aws\\ResultInterface' => $vendorDir . '/aws/aws-sdk-php/src/ResultInterface.php', + 'Aws\\ResultPaginator' => $vendorDir . '/aws/aws-sdk-php/src/ResultPaginator.php', + 'Aws\\RetryMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/RetryMiddleware.php', + 'Aws\\RetryMiddlewareV2' => $vendorDir . '/aws/aws-sdk-php/src/RetryMiddlewareV2.php', + 'Aws\\Retry\\Configuration' => $vendorDir . '/aws/aws-sdk-php/src/Retry/Configuration.php', + 'Aws\\Retry\\ConfigurationInterface' => $vendorDir . '/aws/aws-sdk-php/src/Retry/ConfigurationInterface.php', + 'Aws\\Retry\\ConfigurationProvider' => $vendorDir . '/aws/aws-sdk-php/src/Retry/ConfigurationProvider.php', + 'Aws\\Retry\\Exception\\ConfigurationException' => $vendorDir . '/aws/aws-sdk-php/src/Retry/Exception/ConfigurationException.php', + 'Aws\\Retry\\QuotaManager' => $vendorDir . '/aws/aws-sdk-php/src/Retry/QuotaManager.php', + 'Aws\\Retry\\RateLimiter' => $vendorDir . '/aws/aws-sdk-php/src/Retry/RateLimiter.php', + 'Aws\\Retry\\RetryHelperTrait' => $vendorDir . '/aws/aws-sdk-php/src/Retry/RetryHelperTrait.php', + 'Aws\\Retry\\V3\\LongPolling' => $vendorDir . '/aws/aws-sdk-php/src/Retry/V3/LongPolling.php', + 'Aws\\Retry\\V3\\OptIn' => $vendorDir . '/aws/aws-sdk-php/src/Retry/V3/OptIn.php', + 'Aws\\Retry\\V3\\QuotaManager' => $vendorDir . '/aws/aws-sdk-php/src/Retry/V3/QuotaManager.php', + 'Aws\\Retry\\V3\\RetryMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/Retry/V3/RetryMiddleware.php', + 'Aws\\RolesAnywhere\\Exception\\RolesAnywhereException' => $vendorDir . '/aws/aws-sdk-php/src/RolesAnywhere/Exception/RolesAnywhereException.php', + 'Aws\\RolesAnywhere\\RolesAnywhereClient' => $vendorDir . '/aws/aws-sdk-php/src/RolesAnywhere/RolesAnywhereClient.php', + 'Aws\\Route53Domains\\Exception\\Route53DomainsException' => $vendorDir . '/aws/aws-sdk-php/src/Route53Domains/Exception/Route53DomainsException.php', + 'Aws\\Route53Domains\\Route53DomainsClient' => $vendorDir . '/aws/aws-sdk-php/src/Route53Domains/Route53DomainsClient.php', + 'Aws\\Route53GlobalResolver\\Exception\\Route53GlobalResolverException' => $vendorDir . '/aws/aws-sdk-php/src/Route53GlobalResolver/Exception/Route53GlobalResolverException.php', + 'Aws\\Route53GlobalResolver\\Route53GlobalResolverClient' => $vendorDir . '/aws/aws-sdk-php/src/Route53GlobalResolver/Route53GlobalResolverClient.php', + 'Aws\\Route53Profiles\\Exception\\Route53ProfilesException' => $vendorDir . '/aws/aws-sdk-php/src/Route53Profiles/Exception/Route53ProfilesException.php', + 'Aws\\Route53Profiles\\Route53ProfilesClient' => $vendorDir . '/aws/aws-sdk-php/src/Route53Profiles/Route53ProfilesClient.php', + 'Aws\\Route53RecoveryCluster\\Exception\\Route53RecoveryClusterException' => $vendorDir . '/aws/aws-sdk-php/src/Route53RecoveryCluster/Exception/Route53RecoveryClusterException.php', + 'Aws\\Route53RecoveryCluster\\Route53RecoveryClusterClient' => $vendorDir . '/aws/aws-sdk-php/src/Route53RecoveryCluster/Route53RecoveryClusterClient.php', + 'Aws\\Route53RecoveryControlConfig\\Exception\\Route53RecoveryControlConfigException' => $vendorDir . '/aws/aws-sdk-php/src/Route53RecoveryControlConfig/Exception/Route53RecoveryControlConfigException.php', + 'Aws\\Route53RecoveryControlConfig\\Route53RecoveryControlConfigClient' => $vendorDir . '/aws/aws-sdk-php/src/Route53RecoveryControlConfig/Route53RecoveryControlConfigClient.php', + 'Aws\\Route53RecoveryReadiness\\Exception\\Route53RecoveryReadinessException' => $vendorDir . '/aws/aws-sdk-php/src/Route53RecoveryReadiness/Exception/Route53RecoveryReadinessException.php', + 'Aws\\Route53RecoveryReadiness\\Route53RecoveryReadinessClient' => $vendorDir . '/aws/aws-sdk-php/src/Route53RecoveryReadiness/Route53RecoveryReadinessClient.php', + 'Aws\\Route53Resolver\\Exception\\Route53ResolverException' => $vendorDir . '/aws/aws-sdk-php/src/Route53Resolver/Exception/Route53ResolverException.php', + 'Aws\\Route53Resolver\\Route53ResolverClient' => $vendorDir . '/aws/aws-sdk-php/src/Route53Resolver/Route53ResolverClient.php', + 'Aws\\Route53\\Exception\\Route53Exception' => $vendorDir . '/aws/aws-sdk-php/src/Route53/Exception/Route53Exception.php', + 'Aws\\Route53\\Route53Client' => $vendorDir . '/aws/aws-sdk-php/src/Route53/Route53Client.php', + 'Aws\\S3Control\\EndpointArnMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/S3Control/EndpointArnMiddleware.php', + 'Aws\\S3Control\\Exception\\S3ControlException' => $vendorDir . '/aws/aws-sdk-php/src/S3Control/Exception/S3ControlException.php', + 'Aws\\S3Control\\S3ControlClient' => $vendorDir . '/aws/aws-sdk-php/src/S3Control/S3ControlClient.php', + 'Aws\\S3Files\\Exception\\S3FilesException' => $vendorDir . '/aws/aws-sdk-php/src/S3Files/Exception/S3FilesException.php', + 'Aws\\S3Files\\S3FilesClient' => $vendorDir . '/aws/aws-sdk-php/src/S3Files/S3FilesClient.php', + 'Aws\\S3Outposts\\Exception\\S3OutpostsException' => $vendorDir . '/aws/aws-sdk-php/src/S3Outposts/Exception/S3OutpostsException.php', + 'Aws\\S3Outposts\\S3OutpostsClient' => $vendorDir . '/aws/aws-sdk-php/src/S3Outposts/S3OutpostsClient.php', + 'Aws\\S3Tables\\Exception\\S3TablesException' => $vendorDir . '/aws/aws-sdk-php/src/S3Tables/Exception/S3TablesException.php', + 'Aws\\S3Tables\\S3TablesClient' => $vendorDir . '/aws/aws-sdk-php/src/S3Tables/S3TablesClient.php', + 'Aws\\S3Vectors\\Exception\\S3VectorsException' => $vendorDir . '/aws/aws-sdk-php/src/S3Vectors/Exception/S3VectorsException.php', + 'Aws\\S3Vectors\\S3VectorsClient' => $vendorDir . '/aws/aws-sdk-php/src/S3Vectors/S3VectorsClient.php', + 'Aws\\S3\\AmbiguousSuccessParser' => $vendorDir . '/aws/aws-sdk-php/src/S3/AmbiguousSuccessParser.php', + 'Aws\\S3\\ApplyChecksumMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/S3/ApplyChecksumMiddleware.php', + 'Aws\\S3\\BatchDelete' => $vendorDir . '/aws/aws-sdk-php/src/S3/BatchDelete.php', + 'Aws\\S3\\BucketEndpointArnMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/S3/BucketEndpointArnMiddleware.php', + 'Aws\\S3\\BucketEndpointMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/S3/BucketEndpointMiddleware.php', + 'Aws\\S3\\CalculatesChecksumTrait' => $vendorDir . '/aws/aws-sdk-php/src/S3/CalculatesChecksumTrait.php', + 'Aws\\S3\\Crypto\\CryptoParamsTrait' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/CryptoParamsTrait.php', + 'Aws\\S3\\Crypto\\CryptoParamsTraitV2' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/CryptoParamsTraitV2.php', + 'Aws\\S3\\Crypto\\CryptoParamsTraitV3' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/CryptoParamsTraitV3.php', + 'Aws\\S3\\Crypto\\HeadersMetadataStrategy' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/HeadersMetadataStrategy.php', + 'Aws\\S3\\Crypto\\InstructionFileMetadataStrategy' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/InstructionFileMetadataStrategy.php', + 'Aws\\S3\\Crypto\\S3EncryptionClient' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionClient.php', + 'Aws\\S3\\Crypto\\S3EncryptionClientV2' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionClientV2.php', + 'Aws\\S3\\Crypto\\S3EncryptionClientV3' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionClientV3.php', + 'Aws\\S3\\Crypto\\S3EncryptionMultipartUploader' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionMultipartUploader.php', + 'Aws\\S3\\Crypto\\S3EncryptionMultipartUploaderV2' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionMultipartUploaderV2.php', + 'Aws\\S3\\Crypto\\S3EncryptionMultipartUploaderV3' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionMultipartUploaderV3.php', + 'Aws\\S3\\Crypto\\UserAgentTrait' => $vendorDir . '/aws/aws-sdk-php/src/S3/Crypto/UserAgentTrait.php', + 'Aws\\S3\\EndpointRegionHelperTrait' => $vendorDir . '/aws/aws-sdk-php/src/S3/EndpointRegionHelperTrait.php', + 'Aws\\S3\\Exception\\DeleteMultipleObjectsException' => $vendorDir . '/aws/aws-sdk-php/src/S3/Exception/DeleteMultipleObjectsException.php', + 'Aws\\S3\\Exception\\MultipartCopyAnnotationException' => $vendorDir . '/aws/aws-sdk-php/src/S3/Exception/MultipartCopyAnnotationException.php', + 'Aws\\S3\\Exception\\PermanentRedirectException' => $vendorDir . '/aws/aws-sdk-php/src/S3/Exception/PermanentRedirectException.php', + 'Aws\\S3\\Exception\\S3Exception' => $vendorDir . '/aws/aws-sdk-php/src/S3/Exception/S3Exception.php', + 'Aws\\S3\\Exception\\S3MultipartUploadException' => $vendorDir . '/aws/aws-sdk-php/src/S3/Exception/S3MultipartUploadException.php', + 'Aws\\S3\\ExpiresParsingMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/S3/ExpiresParsingMiddleware.php', + 'Aws\\S3\\GetBucketLocationParser' => $vendorDir . '/aws/aws-sdk-php/src/S3/GetBucketLocationParser.php', + 'Aws\\S3\\MultipartCopy' => $vendorDir . '/aws/aws-sdk-php/src/S3/MultipartCopy.php', + 'Aws\\S3\\MultipartUploader' => $vendorDir . '/aws/aws-sdk-php/src/S3/MultipartUploader.php', + 'Aws\\S3\\MultipartUploadingTrait' => $vendorDir . '/aws/aws-sdk-php/src/S3/MultipartUploadingTrait.php', + 'Aws\\S3\\ObjectCopier' => $vendorDir . '/aws/aws-sdk-php/src/S3/ObjectCopier.php', + 'Aws\\S3\\ObjectUploader' => $vendorDir . '/aws/aws-sdk-php/src/S3/ObjectUploader.php', + 'Aws\\S3\\Parser\\GetBucketLocationResultMutator' => $vendorDir . '/aws/aws-sdk-php/src/S3/Parser/GetBucketLocationResultMutator.php', + 'Aws\\S3\\Parser\\S3Parser' => $vendorDir . '/aws/aws-sdk-php/src/S3/Parser/S3Parser.php', + 'Aws\\S3\\Parser\\S3ResultMutator' => $vendorDir . '/aws/aws-sdk-php/src/S3/Parser/S3ResultMutator.php', + 'Aws\\S3\\Parser\\ValidateResponseChecksumResultMutator' => $vendorDir . '/aws/aws-sdk-php/src/S3/Parser/ValidateResponseChecksumResultMutator.php', + 'Aws\\S3\\PermanentRedirectMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/S3/PermanentRedirectMiddleware.php', + 'Aws\\S3\\PostObject' => $vendorDir . '/aws/aws-sdk-php/src/S3/PostObject.php', + 'Aws\\S3\\PostObjectV4' => $vendorDir . '/aws/aws-sdk-php/src/S3/PostObjectV4.php', + 'Aws\\S3\\PutObjectUrlMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/S3/PutObjectUrlMiddleware.php', + 'Aws\\S3\\RegionalEndpoint\\Configuration' => $vendorDir . '/aws/aws-sdk-php/src/S3/RegionalEndpoint/Configuration.php', + 'Aws\\S3\\RegionalEndpoint\\ConfigurationInterface' => $vendorDir . '/aws/aws-sdk-php/src/S3/RegionalEndpoint/ConfigurationInterface.php', + 'Aws\\S3\\RegionalEndpoint\\ConfigurationProvider' => $vendorDir . '/aws/aws-sdk-php/src/S3/RegionalEndpoint/ConfigurationProvider.php', + 'Aws\\S3\\RegionalEndpoint\\Exception\\ConfigurationException' => $vendorDir . '/aws/aws-sdk-php/src/S3/RegionalEndpoint/Exception/ConfigurationException.php', + 'Aws\\S3\\RetryableMalformedResponseParser' => $vendorDir . '/aws/aws-sdk-php/src/S3/RetryableMalformedResponseParser.php', + 'Aws\\S3\\S3Client' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Client.php', + 'Aws\\S3\\S3ClientInterface' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3ClientInterface.php', + 'Aws\\S3\\S3ClientTrait' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3ClientTrait.php', + 'Aws\\S3\\S3EndpointMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3EndpointMiddleware.php', + 'Aws\\S3\\S3MultiRegionClient' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3MultiRegionClient.php', + 'Aws\\S3\\S3Transfer\\AbstractMultipartDownloader' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/AbstractMultipartDownloader.php', + 'Aws\\S3\\S3Transfer\\AbstractMultipartUploader' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/AbstractMultipartUploader.php', + 'Aws\\S3\\S3Transfer\\DirectoryDownloader' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/DirectoryDownloader.php', + 'Aws\\S3\\S3Transfer\\DirectoryUploader' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/DirectoryUploader.php', + 'Aws\\S3\\S3Transfer\\Exception\\FileDownloadException' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Exception/FileDownloadException.php', + 'Aws\\S3\\S3Transfer\\Exception\\ProgressTrackerException' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Exception/ProgressTrackerException.php', + 'Aws\\S3\\S3Transfer\\Exception\\S3TransferException' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Exception/S3TransferException.php', + 'Aws\\S3\\S3Transfer\\Models\\AbstractResumableTransfer' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/AbstractResumableTransfer.php', + 'Aws\\S3\\S3Transfer\\Models\\AbstractTransferRequest' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/AbstractTransferRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\DownloadDirectoryRequest' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/DownloadDirectoryRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\DownloadDirectoryResult' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/DownloadDirectoryResult.php', + 'Aws\\S3\\S3Transfer\\Models\\DownloadFileRequest' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/DownloadFileRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\DownloadRequest' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/DownloadRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\DownloadResult' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/DownloadResult.php', + 'Aws\\S3\\S3Transfer\\Models\\ResumableDownload' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/ResumableDownload.php', + 'Aws\\S3\\S3Transfer\\Models\\ResumableUpload' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/ResumableUpload.php', + 'Aws\\S3\\S3Transfer\\Models\\ResumeDownloadRequest' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/ResumeDownloadRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\ResumeUploadRequest' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/ResumeUploadRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\S3TransferManagerConfig' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/S3TransferManagerConfig.php', + 'Aws\\S3\\S3Transfer\\Models\\UploadDirectoryRequest' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/UploadDirectoryRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\UploadDirectoryResult' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/UploadDirectoryResult.php', + 'Aws\\S3\\S3Transfer\\Models\\UploadRequest' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/UploadRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\UploadResult' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/UploadResult.php', + 'Aws\\S3\\S3Transfer\\MultipartUploader' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/MultipartUploader.php', + 'Aws\\S3\\S3Transfer\\PartGetMultipartDownloader' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/PartGetMultipartDownloader.php', + 'Aws\\S3\\S3Transfer\\Progress\\AbstractProgressBarFormat' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/AbstractProgressBarFormat.php', + 'Aws\\S3\\S3Transfer\\Progress\\AbstractTransferListener' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/AbstractTransferListener.php', + 'Aws\\S3\\S3Transfer\\Progress\\ColoredTransferProgressBarFormat' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/ColoredTransferProgressBarFormat.php', + 'Aws\\S3\\S3Transfer\\Progress\\ConsoleProgressBar' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/ConsoleProgressBar.php', + 'Aws\\S3\\S3Transfer\\Progress\\DirectoryProgressTracker' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/DirectoryProgressTracker.php', + 'Aws\\S3\\S3Transfer\\Progress\\DirectoryTransferProgressAggregator' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/DirectoryTransferProgressAggregator.php', + 'Aws\\S3\\S3Transfer\\Progress\\DirectoryTransferProgressSnapshot' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/DirectoryTransferProgressSnapshot.php', + 'Aws\\S3\\S3Transfer\\Progress\\MultiProgressBarFormat' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/MultiProgressBarFormat.php', + 'Aws\\S3\\S3Transfer\\Progress\\MultiProgressTracker' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/MultiProgressTracker.php', + 'Aws\\S3\\S3Transfer\\Progress\\PlainProgressBarFormat' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/PlainProgressBarFormat.php', + 'Aws\\S3\\S3Transfer\\Progress\\ProgressBarFactoryInterface' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/ProgressBarFactoryInterface.php', + 'Aws\\S3\\S3Transfer\\Progress\\ProgressBarInterface' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/ProgressBarInterface.php', + 'Aws\\S3\\S3Transfer\\Progress\\ProgressTrackerInterface' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/ProgressTrackerInterface.php', + 'Aws\\S3\\S3Transfer\\Progress\\SingleProgressTracker' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/SingleProgressTracker.php', + 'Aws\\S3\\S3Transfer\\Progress\\TransferListenerNotifier' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/TransferListenerNotifier.php', + 'Aws\\S3\\S3Transfer\\Progress\\TransferProgressBarFormat' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/TransferProgressBarFormat.php', + 'Aws\\S3\\S3Transfer\\Progress\\TransferProgressSnapshot' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/TransferProgressSnapshot.php', + 'Aws\\S3\\S3Transfer\\RangeGetMultipartDownloader' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/RangeGetMultipartDownloader.php', + 'Aws\\S3\\S3Transfer\\S3TransferManager' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/S3TransferManager.php', + 'Aws\\S3\\S3Transfer\\Utils\\AbstractDownloadHandler' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Utils/AbstractDownloadHandler.php', + 'Aws\\S3\\S3Transfer\\Utils\\FileDownloadHandler' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Utils/FileDownloadHandler.php', + 'Aws\\S3\\S3Transfer\\Utils\\ResumableDownloadHandlerInterface' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Utils/ResumableDownloadHandlerInterface.php', + 'Aws\\S3\\S3Transfer\\Utils\\StreamDownloadHandler' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3Transfer/Utils/StreamDownloadHandler.php', + 'Aws\\S3\\S3UriParser' => $vendorDir . '/aws/aws-sdk-php/src/S3/S3UriParser.php', + 'Aws\\S3\\SSECMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/S3/SSECMiddleware.php', + 'Aws\\S3\\StreamWrapper' => $vendorDir . '/aws/aws-sdk-php/src/S3/StreamWrapper.php', + 'Aws\\S3\\Transfer' => $vendorDir . '/aws/aws-sdk-php/src/S3/Transfer.php', + 'Aws\\S3\\UseArnRegion\\Configuration' => $vendorDir . '/aws/aws-sdk-php/src/S3/UseArnRegion/Configuration.php', + 'Aws\\S3\\UseArnRegion\\ConfigurationInterface' => $vendorDir . '/aws/aws-sdk-php/src/S3/UseArnRegion/ConfigurationInterface.php', + 'Aws\\S3\\UseArnRegion\\ConfigurationProvider' => $vendorDir . '/aws/aws-sdk-php/src/S3/UseArnRegion/ConfigurationProvider.php', + 'Aws\\S3\\UseArnRegion\\Exception\\ConfigurationException' => $vendorDir . '/aws/aws-sdk-php/src/S3/UseArnRegion/Exception/ConfigurationException.php', + 'Aws\\S3\\ValidateResponseChecksumParser' => $vendorDir . '/aws/aws-sdk-php/src/S3/ValidateResponseChecksumParser.php', + 'Aws\\SSMContacts\\Exception\\SSMContactsException' => $vendorDir . '/aws/aws-sdk-php/src/SSMContacts/Exception/SSMContactsException.php', + 'Aws\\SSMContacts\\SSMContactsClient' => $vendorDir . '/aws/aws-sdk-php/src/SSMContacts/SSMContactsClient.php', + 'Aws\\SSMGuiConnect\\Exception\\SSMGuiConnectException' => $vendorDir . '/aws/aws-sdk-php/src/SSMGuiConnect/Exception/SSMGuiConnectException.php', + 'Aws\\SSMGuiConnect\\SSMGuiConnectClient' => $vendorDir . '/aws/aws-sdk-php/src/SSMGuiConnect/SSMGuiConnectClient.php', + 'Aws\\SSMIncidents\\Exception\\SSMIncidentsException' => $vendorDir . '/aws/aws-sdk-php/src/SSMIncidents/Exception/SSMIncidentsException.php', + 'Aws\\SSMIncidents\\SSMIncidentsClient' => $vendorDir . '/aws/aws-sdk-php/src/SSMIncidents/SSMIncidentsClient.php', + 'Aws\\SSMQuickSetup\\Exception\\SSMQuickSetupException' => $vendorDir . '/aws/aws-sdk-php/src/SSMQuickSetup/Exception/SSMQuickSetupException.php', + 'Aws\\SSMQuickSetup\\SSMQuickSetupClient' => $vendorDir . '/aws/aws-sdk-php/src/SSMQuickSetup/SSMQuickSetupClient.php', + 'Aws\\SSOAdmin\\Exception\\SSOAdminException' => $vendorDir . '/aws/aws-sdk-php/src/SSOAdmin/Exception/SSOAdminException.php', + 'Aws\\SSOAdmin\\SSOAdminClient' => $vendorDir . '/aws/aws-sdk-php/src/SSOAdmin/SSOAdminClient.php', + 'Aws\\SSOOIDC\\Exception\\SSOOIDCException' => $vendorDir . '/aws/aws-sdk-php/src/SSOOIDC/Exception/SSOOIDCException.php', + 'Aws\\SSOOIDC\\SSOOIDCClient' => $vendorDir . '/aws/aws-sdk-php/src/SSOOIDC/SSOOIDCClient.php', + 'Aws\\SSO\\Exception\\SSOException' => $vendorDir . '/aws/aws-sdk-php/src/SSO/Exception/SSOException.php', + 'Aws\\SSO\\SSOClient' => $vendorDir . '/aws/aws-sdk-php/src/SSO/SSOClient.php', + 'Aws\\SageMakerFeatureStoreRuntime\\Exception\\SageMakerFeatureStoreRuntimeException' => $vendorDir . '/aws/aws-sdk-php/src/SageMakerFeatureStoreRuntime/Exception/SageMakerFeatureStoreRuntimeException.php', + 'Aws\\SageMakerFeatureStoreRuntime\\SageMakerFeatureStoreRuntimeClient' => $vendorDir . '/aws/aws-sdk-php/src/SageMakerFeatureStoreRuntime/SageMakerFeatureStoreRuntimeClient.php', + 'Aws\\SageMakerGeospatial\\Exception\\SageMakerGeospatialException' => $vendorDir . '/aws/aws-sdk-php/src/SageMakerGeospatial/Exception/SageMakerGeospatialException.php', + 'Aws\\SageMakerGeospatial\\SageMakerGeospatialClient' => $vendorDir . '/aws/aws-sdk-php/src/SageMakerGeospatial/SageMakerGeospatialClient.php', + 'Aws\\SageMakerMetrics\\Exception\\SageMakerMetricsException' => $vendorDir . '/aws/aws-sdk-php/src/SageMakerMetrics/Exception/SageMakerMetricsException.php', + 'Aws\\SageMakerMetrics\\SageMakerMetricsClient' => $vendorDir . '/aws/aws-sdk-php/src/SageMakerMetrics/SageMakerMetricsClient.php', + 'Aws\\SageMakerRuntime\\Exception\\SageMakerRuntimeException' => $vendorDir . '/aws/aws-sdk-php/src/SageMakerRuntime/Exception/SageMakerRuntimeException.php', + 'Aws\\SageMakerRuntime\\SageMakerRuntimeClient' => $vendorDir . '/aws/aws-sdk-php/src/SageMakerRuntime/SageMakerRuntimeClient.php', + 'Aws\\SageMaker\\Exception\\SageMakerException' => $vendorDir . '/aws/aws-sdk-php/src/SageMaker/Exception/SageMakerException.php', + 'Aws\\SageMaker\\SageMakerClient' => $vendorDir . '/aws/aws-sdk-php/src/SageMaker/SageMakerClient.php', + 'Aws\\SagemakerEdgeManager\\Exception\\SagemakerEdgeManagerException' => $vendorDir . '/aws/aws-sdk-php/src/SagemakerEdgeManager/Exception/SagemakerEdgeManagerException.php', + 'Aws\\SagemakerEdgeManager\\SagemakerEdgeManagerClient' => $vendorDir . '/aws/aws-sdk-php/src/SagemakerEdgeManager/SagemakerEdgeManagerClient.php', + 'Aws\\SagemakerJobRuntime\\Exception\\SagemakerJobRuntimeException' => $vendorDir . '/aws/aws-sdk-php/src/SagemakerJobRuntime/Exception/SagemakerJobRuntimeException.php', + 'Aws\\SagemakerJobRuntime\\SagemakerJobRuntimeClient' => $vendorDir . '/aws/aws-sdk-php/src/SagemakerJobRuntime/SagemakerJobRuntimeClient.php', + 'Aws\\SavingsPlans\\Exception\\SavingsPlansException' => $vendorDir . '/aws/aws-sdk-php/src/SavingsPlans/Exception/SavingsPlansException.php', + 'Aws\\SavingsPlans\\SavingsPlansClient' => $vendorDir . '/aws/aws-sdk-php/src/SavingsPlans/SavingsPlansClient.php', + 'Aws\\Scheduler\\Exception\\SchedulerException' => $vendorDir . '/aws/aws-sdk-php/src/Scheduler/Exception/SchedulerException.php', + 'Aws\\Scheduler\\SchedulerClient' => $vendorDir . '/aws/aws-sdk-php/src/Scheduler/SchedulerClient.php', + 'Aws\\Schemas\\Exception\\SchemasException' => $vendorDir . '/aws/aws-sdk-php/src/Schemas/Exception/SchemasException.php', + 'Aws\\Schemas\\SchemasClient' => $vendorDir . '/aws/aws-sdk-php/src/Schemas/SchemasClient.php', + 'Aws\\Script\\Composer\\Composer' => $vendorDir . '/aws/aws-sdk-php/src/Script/Composer/Composer.php', + 'Aws\\Sdk' => $vendorDir . '/aws/aws-sdk-php/src/Sdk.php', + 'Aws\\SecretsManager\\Exception\\SecretsManagerException' => $vendorDir . '/aws/aws-sdk-php/src/SecretsManager/Exception/SecretsManagerException.php', + 'Aws\\SecretsManager\\SecretsManagerClient' => $vendorDir . '/aws/aws-sdk-php/src/SecretsManager/SecretsManagerClient.php', + 'Aws\\SecurityAgent\\Exception\\SecurityAgentException' => $vendorDir . '/aws/aws-sdk-php/src/SecurityAgent/Exception/SecurityAgentException.php', + 'Aws\\SecurityAgent\\SecurityAgentClient' => $vendorDir . '/aws/aws-sdk-php/src/SecurityAgent/SecurityAgentClient.php', + 'Aws\\SecurityHub\\Exception\\SecurityHubException' => $vendorDir . '/aws/aws-sdk-php/src/SecurityHub/Exception/SecurityHubException.php', + 'Aws\\SecurityHub\\SecurityHubClient' => $vendorDir . '/aws/aws-sdk-php/src/SecurityHub/SecurityHubClient.php', + 'Aws\\SecurityIR\\Exception\\SecurityIRException' => $vendorDir . '/aws/aws-sdk-php/src/SecurityIR/Exception/SecurityIRException.php', + 'Aws\\SecurityIR\\SecurityIRClient' => $vendorDir . '/aws/aws-sdk-php/src/SecurityIR/SecurityIRClient.php', + 'Aws\\SecurityLake\\Exception\\SecurityLakeException' => $vendorDir . '/aws/aws-sdk-php/src/SecurityLake/Exception/SecurityLakeException.php', + 'Aws\\SecurityLake\\SecurityLakeClient' => $vendorDir . '/aws/aws-sdk-php/src/SecurityLake/SecurityLakeClient.php', + 'Aws\\ServerlessApplicationRepository\\Exception\\ServerlessApplicationRepositoryException' => $vendorDir . '/aws/aws-sdk-php/src/ServerlessApplicationRepository/Exception/ServerlessApplicationRepositoryException.php', + 'Aws\\ServerlessApplicationRepository\\ServerlessApplicationRepositoryClient' => $vendorDir . '/aws/aws-sdk-php/src/ServerlessApplicationRepository/ServerlessApplicationRepositoryClient.php', + 'Aws\\ServiceCatalog\\Exception\\ServiceCatalogException' => $vendorDir . '/aws/aws-sdk-php/src/ServiceCatalog/Exception/ServiceCatalogException.php', + 'Aws\\ServiceCatalog\\ServiceCatalogClient' => $vendorDir . '/aws/aws-sdk-php/src/ServiceCatalog/ServiceCatalogClient.php', + 'Aws\\ServiceDiscovery\\Exception\\ServiceDiscoveryException' => $vendorDir . '/aws/aws-sdk-php/src/ServiceDiscovery/Exception/ServiceDiscoveryException.php', + 'Aws\\ServiceDiscovery\\ServiceDiscoveryClient' => $vendorDir . '/aws/aws-sdk-php/src/ServiceDiscovery/ServiceDiscoveryClient.php', + 'Aws\\ServiceQuotas\\Exception\\ServiceQuotasException' => $vendorDir . '/aws/aws-sdk-php/src/ServiceQuotas/Exception/ServiceQuotasException.php', + 'Aws\\ServiceQuotas\\ServiceQuotasClient' => $vendorDir . '/aws/aws-sdk-php/src/ServiceQuotas/ServiceQuotasClient.php', + 'Aws\\SesV2\\Exception\\SesV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/SesV2/Exception/SesV2Exception.php', + 'Aws\\SesV2\\SesV2Client' => $vendorDir . '/aws/aws-sdk-php/src/SesV2/SesV2Client.php', + 'Aws\\Ses\\Exception\\SesException' => $vendorDir . '/aws/aws-sdk-php/src/Ses/Exception/SesException.php', + 'Aws\\Ses\\SesClient' => $vendorDir . '/aws/aws-sdk-php/src/Ses/SesClient.php', + 'Aws\\Sfn\\Exception\\SfnException' => $vendorDir . '/aws/aws-sdk-php/src/Sfn/Exception/SfnException.php', + 'Aws\\Sfn\\SfnClient' => $vendorDir . '/aws/aws-sdk-php/src/Sfn/SfnClient.php', + 'Aws\\Shield\\Exception\\ShieldException' => $vendorDir . '/aws/aws-sdk-php/src/Shield/Exception/ShieldException.php', + 'Aws\\Shield\\ShieldClient' => $vendorDir . '/aws/aws-sdk-php/src/Shield/ShieldClient.php', + 'Aws\\Signature\\AnonymousSignature' => $vendorDir . '/aws/aws-sdk-php/src/Signature/AnonymousSignature.php', + 'Aws\\Signature\\DpopSignature' => $vendorDir . '/aws/aws-sdk-php/src/Signature/DpopSignature.php', + 'Aws\\Signature\\S3ExpressSignature' => $vendorDir . '/aws/aws-sdk-php/src/Signature/S3ExpressSignature.php', + 'Aws\\Signature\\S3SignatureV4' => $vendorDir . '/aws/aws-sdk-php/src/Signature/S3SignatureV4.php', + 'Aws\\Signature\\SignatureInterface' => $vendorDir . '/aws/aws-sdk-php/src/Signature/SignatureInterface.php', + 'Aws\\Signature\\SignatureProvider' => $vendorDir . '/aws/aws-sdk-php/src/Signature/SignatureProvider.php', + 'Aws\\Signature\\SignatureTrait' => $vendorDir . '/aws/aws-sdk-php/src/Signature/SignatureTrait.php', + 'Aws\\Signature\\SignatureV4' => $vendorDir . '/aws/aws-sdk-php/src/Signature/SignatureV4.php', + 'Aws\\SignerData\\Exception\\SignerDataException' => $vendorDir . '/aws/aws-sdk-php/src/SignerData/Exception/SignerDataException.php', + 'Aws\\SignerData\\SignerDataClient' => $vendorDir . '/aws/aws-sdk-php/src/SignerData/SignerDataClient.php', + 'Aws\\Signin\\Exception\\SigninException' => $vendorDir . '/aws/aws-sdk-php/src/Signin/Exception/SigninException.php', + 'Aws\\Signin\\SigninClient' => $vendorDir . '/aws/aws-sdk-php/src/Signin/SigninClient.php', + 'Aws\\SimpleDBv2\\Exception\\SimpleDBv2Exception' => $vendorDir . '/aws/aws-sdk-php/src/SimpleDBv2/Exception/SimpleDBv2Exception.php', + 'Aws\\SimpleDBv2\\SimpleDBv2Client' => $vendorDir . '/aws/aws-sdk-php/src/SimpleDBv2/SimpleDBv2Client.php', + 'Aws\\SnowBall\\Exception\\SnowBallException' => $vendorDir . '/aws/aws-sdk-php/src/SnowBall/Exception/SnowBallException.php', + 'Aws\\SnowBall\\SnowBallClient' => $vendorDir . '/aws/aws-sdk-php/src/SnowBall/SnowBallClient.php', + 'Aws\\SnowDeviceManagement\\Exception\\SnowDeviceManagementException' => $vendorDir . '/aws/aws-sdk-php/src/SnowDeviceManagement/Exception/SnowDeviceManagementException.php', + 'Aws\\SnowDeviceManagement\\SnowDeviceManagementClient' => $vendorDir . '/aws/aws-sdk-php/src/SnowDeviceManagement/SnowDeviceManagementClient.php', + 'Aws\\Sns\\Exception\\SnsException' => $vendorDir . '/aws/aws-sdk-php/src/Sns/Exception/SnsException.php', + 'Aws\\Sns\\SnsClient' => $vendorDir . '/aws/aws-sdk-php/src/Sns/SnsClient.php', + 'Aws\\SocialMessaging\\Exception\\SocialMessagingException' => $vendorDir . '/aws/aws-sdk-php/src/SocialMessaging/Exception/SocialMessagingException.php', + 'Aws\\SocialMessaging\\SocialMessagingClient' => $vendorDir . '/aws/aws-sdk-php/src/SocialMessaging/SocialMessagingClient.php', + 'Aws\\Sqs\\Exception\\SqsException' => $vendorDir . '/aws/aws-sdk-php/src/Sqs/Exception/SqsException.php', + 'Aws\\Sqs\\SqsClient' => $vendorDir . '/aws/aws-sdk-php/src/Sqs/SqsClient.php', + 'Aws\\SsmSap\\Exception\\SsmSapException' => $vendorDir . '/aws/aws-sdk-php/src/SsmSap/Exception/SsmSapException.php', + 'Aws\\SsmSap\\SsmSapClient' => $vendorDir . '/aws/aws-sdk-php/src/SsmSap/SsmSapClient.php', + 'Aws\\Ssm\\Exception\\SsmException' => $vendorDir . '/aws/aws-sdk-php/src/Ssm/Exception/SsmException.php', + 'Aws\\Ssm\\SsmClient' => $vendorDir . '/aws/aws-sdk-php/src/Ssm/SsmClient.php', + 'Aws\\StorageGateway\\Exception\\StorageGatewayException' => $vendorDir . '/aws/aws-sdk-php/src/StorageGateway/Exception/StorageGatewayException.php', + 'Aws\\StorageGateway\\StorageGatewayClient' => $vendorDir . '/aws/aws-sdk-php/src/StorageGateway/StorageGatewayClient.php', + 'Aws\\StreamRequestPayloadMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/StreamRequestPayloadMiddleware.php', + 'Aws\\Sts\\Exception\\StsException' => $vendorDir . '/aws/aws-sdk-php/src/Sts/Exception/StsException.php', + 'Aws\\Sts\\RegionalEndpoints\\Configuration' => $vendorDir . '/aws/aws-sdk-php/src/Sts/RegionalEndpoints/Configuration.php', + 'Aws\\Sts\\RegionalEndpoints\\ConfigurationInterface' => $vendorDir . '/aws/aws-sdk-php/src/Sts/RegionalEndpoints/ConfigurationInterface.php', + 'Aws\\Sts\\RegionalEndpoints\\ConfigurationProvider' => $vendorDir . '/aws/aws-sdk-php/src/Sts/RegionalEndpoints/ConfigurationProvider.php', + 'Aws\\Sts\\RegionalEndpoints\\Exception\\ConfigurationException' => $vendorDir . '/aws/aws-sdk-php/src/Sts/RegionalEndpoints/Exception/ConfigurationException.php', + 'Aws\\Sts\\StsClient' => $vendorDir . '/aws/aws-sdk-php/src/Sts/StsClient.php', + 'Aws\\SupplyChain\\Exception\\SupplyChainException' => $vendorDir . '/aws/aws-sdk-php/src/SupplyChain/Exception/SupplyChainException.php', + 'Aws\\SupplyChain\\SupplyChainClient' => $vendorDir . '/aws/aws-sdk-php/src/SupplyChain/SupplyChainClient.php', + 'Aws\\SupportApp\\Exception\\SupportAppException' => $vendorDir . '/aws/aws-sdk-php/src/SupportApp/Exception/SupportAppException.php', + 'Aws\\SupportApp\\SupportAppClient' => $vendorDir . '/aws/aws-sdk-php/src/SupportApp/SupportAppClient.php', + 'Aws\\SupportAuthZ\\Exception\\SupportAuthZException' => $vendorDir . '/aws/aws-sdk-php/src/SupportAuthZ/Exception/SupportAuthZException.php', + 'Aws\\SupportAuthZ\\SupportAuthZClient' => $vendorDir . '/aws/aws-sdk-php/src/SupportAuthZ/SupportAuthZClient.php', + 'Aws\\Support\\Exception\\SupportException' => $vendorDir . '/aws/aws-sdk-php/src/Support/Exception/SupportException.php', + 'Aws\\Support\\SupportClient' => $vendorDir . '/aws/aws-sdk-php/src/Support/SupportClient.php', + 'Aws\\Sustainability\\Exception\\SustainabilityException' => $vendorDir . '/aws/aws-sdk-php/src/Sustainability/Exception/SustainabilityException.php', + 'Aws\\Sustainability\\SustainabilityClient' => $vendorDir . '/aws/aws-sdk-php/src/Sustainability/SustainabilityClient.php', + 'Aws\\Swf\\Exception\\SwfException' => $vendorDir . '/aws/aws-sdk-php/src/Swf/Exception/SwfException.php', + 'Aws\\Swf\\SwfClient' => $vendorDir . '/aws/aws-sdk-php/src/Swf/SwfClient.php', + 'Aws\\Synthetics\\Exception\\SyntheticsException' => $vendorDir . '/aws/aws-sdk-php/src/Synthetics/Exception/SyntheticsException.php', + 'Aws\\Synthetics\\SyntheticsClient' => $vendorDir . '/aws/aws-sdk-php/src/Synthetics/SyntheticsClient.php', + 'Aws\\TaxSettings\\Exception\\TaxSettingsException' => $vendorDir . '/aws/aws-sdk-php/src/TaxSettings/Exception/TaxSettingsException.php', + 'Aws\\TaxSettings\\TaxSettingsClient' => $vendorDir . '/aws/aws-sdk-php/src/TaxSettings/TaxSettingsClient.php', + 'Aws\\Textract\\Exception\\TextractException' => $vendorDir . '/aws/aws-sdk-php/src/Textract/Exception/TextractException.php', + 'Aws\\Textract\\TextractClient' => $vendorDir . '/aws/aws-sdk-php/src/Textract/TextractClient.php', + 'Aws\\TimestreamInfluxDB\\Exception\\TimestreamInfluxDBException' => $vendorDir . '/aws/aws-sdk-php/src/TimestreamInfluxDB/Exception/TimestreamInfluxDBException.php', + 'Aws\\TimestreamInfluxDB\\TimestreamInfluxDBClient' => $vendorDir . '/aws/aws-sdk-php/src/TimestreamInfluxDB/TimestreamInfluxDBClient.php', + 'Aws\\TimestreamQuery\\Exception\\TimestreamQueryException' => $vendorDir . '/aws/aws-sdk-php/src/TimestreamQuery/Exception/TimestreamQueryException.php', + 'Aws\\TimestreamQuery\\TimestreamQueryClient' => $vendorDir . '/aws/aws-sdk-php/src/TimestreamQuery/TimestreamQueryClient.php', + 'Aws\\TimestreamWrite\\Exception\\TimestreamWriteException' => $vendorDir . '/aws/aws-sdk-php/src/TimestreamWrite/Exception/TimestreamWriteException.php', + 'Aws\\TimestreamWrite\\TimestreamWriteClient' => $vendorDir . '/aws/aws-sdk-php/src/TimestreamWrite/TimestreamWriteClient.php', + 'Aws\\Tnb\\Exception\\TnbException' => $vendorDir . '/aws/aws-sdk-php/src/Tnb/Exception/TnbException.php', + 'Aws\\Tnb\\TnbClient' => $vendorDir . '/aws/aws-sdk-php/src/Tnb/TnbClient.php', + 'Aws\\Token\\BearerTokenAuthorization' => $vendorDir . '/aws/aws-sdk-php/src/Token/BearerTokenAuthorization.php', + 'Aws\\Token\\BedrockTokenProvider' => $vendorDir . '/aws/aws-sdk-php/src/Token/BedrockTokenProvider.php', + 'Aws\\Token\\ParsesIniTrait' => $vendorDir . '/aws/aws-sdk-php/src/Token/ParsesIniTrait.php', + 'Aws\\Token\\RefreshableTokenProviderInterface' => $vendorDir . '/aws/aws-sdk-php/src/Token/RefreshableTokenProviderInterface.php', + 'Aws\\Token\\SsoToken' => $vendorDir . '/aws/aws-sdk-php/src/Token/SsoToken.php', + 'Aws\\Token\\SsoTokenProvider' => $vendorDir . '/aws/aws-sdk-php/src/Token/SsoTokenProvider.php', + 'Aws\\Token\\Token' => $vendorDir . '/aws/aws-sdk-php/src/Token/Token.php', + 'Aws\\Token\\TokenAuthorization' => $vendorDir . '/aws/aws-sdk-php/src/Token/TokenAuthorization.php', + 'Aws\\Token\\TokenInterface' => $vendorDir . '/aws/aws-sdk-php/src/Token/TokenInterface.php', + 'Aws\\Token\\TokenProvider' => $vendorDir . '/aws/aws-sdk-php/src/Token/TokenProvider.php', + 'Aws\\Token\\TokenSource' => $vendorDir . '/aws/aws-sdk-php/src/Token/TokenSource.php', + 'Aws\\TraceMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/TraceMiddleware.php', + 'Aws\\TranscribeService\\Exception\\TranscribeServiceException' => $vendorDir . '/aws/aws-sdk-php/src/TranscribeService/Exception/TranscribeServiceException.php', + 'Aws\\TranscribeService\\TranscribeServiceClient' => $vendorDir . '/aws/aws-sdk-php/src/TranscribeService/TranscribeServiceClient.php', + 'Aws\\Transfer\\Exception\\TransferException' => $vendorDir . '/aws/aws-sdk-php/src/Transfer/Exception/TransferException.php', + 'Aws\\Transfer\\TransferClient' => $vendorDir . '/aws/aws-sdk-php/src/Transfer/TransferClient.php', + 'Aws\\Translate\\Exception\\TranslateException' => $vendorDir . '/aws/aws-sdk-php/src/Translate/Exception/TranslateException.php', + 'Aws\\Translate\\TranslateClient' => $vendorDir . '/aws/aws-sdk-php/src/Translate/TranslateClient.php', + 'Aws\\TrustedAdvisor\\Exception\\TrustedAdvisorException' => $vendorDir . '/aws/aws-sdk-php/src/TrustedAdvisor/Exception/TrustedAdvisorException.php', + 'Aws\\TrustedAdvisor\\TrustedAdvisorClient' => $vendorDir . '/aws/aws-sdk-php/src/TrustedAdvisor/TrustedAdvisorClient.php', + 'Aws\\UserAgentMiddleware' => $vendorDir . '/aws/aws-sdk-php/src/UserAgentMiddleware.php', + 'Aws\\Uxc\\Exception\\UxcException' => $vendorDir . '/aws/aws-sdk-php/src/Uxc/Exception/UxcException.php', + 'Aws\\Uxc\\UxcClient' => $vendorDir . '/aws/aws-sdk-php/src/Uxc/UxcClient.php', + 'Aws\\VPCLattice\\Exception\\VPCLatticeException' => $vendorDir . '/aws/aws-sdk-php/src/VPCLattice/Exception/VPCLatticeException.php', + 'Aws\\VPCLattice\\VPCLatticeClient' => $vendorDir . '/aws/aws-sdk-php/src/VPCLattice/VPCLatticeClient.php', + 'Aws\\VerifiedPermissions\\Exception\\VerifiedPermissionsException' => $vendorDir . '/aws/aws-sdk-php/src/VerifiedPermissions/Exception/VerifiedPermissionsException.php', + 'Aws\\VerifiedPermissions\\VerifiedPermissionsClient' => $vendorDir . '/aws/aws-sdk-php/src/VerifiedPermissions/VerifiedPermissionsClient.php', + 'Aws\\VoiceID\\Exception\\VoiceIDException' => $vendorDir . '/aws/aws-sdk-php/src/VoiceID/Exception/VoiceIDException.php', + 'Aws\\VoiceID\\VoiceIDClient' => $vendorDir . '/aws/aws-sdk-php/src/VoiceID/VoiceIDClient.php', + 'Aws\\WAFV2\\Exception\\WAFV2Exception' => $vendorDir . '/aws/aws-sdk-php/src/WAFV2/Exception/WAFV2Exception.php', + 'Aws\\WAFV2\\WAFV2Client' => $vendorDir . '/aws/aws-sdk-php/src/WAFV2/WAFV2Client.php', + 'Aws\\WafRegional\\Exception\\WafRegionalException' => $vendorDir . '/aws/aws-sdk-php/src/WafRegional/Exception/WafRegionalException.php', + 'Aws\\WafRegional\\WafRegionalClient' => $vendorDir . '/aws/aws-sdk-php/src/WafRegional/WafRegionalClient.php', + 'Aws\\Waf\\Exception\\WafException' => $vendorDir . '/aws/aws-sdk-php/src/Waf/Exception/WafException.php', + 'Aws\\Waf\\WafClient' => $vendorDir . '/aws/aws-sdk-php/src/Waf/WafClient.php', + 'Aws\\Waiter' => $vendorDir . '/aws/aws-sdk-php/src/Waiter.php', + 'Aws\\WellArchitected\\Exception\\WellArchitectedException' => $vendorDir . '/aws/aws-sdk-php/src/WellArchitected/Exception/WellArchitectedException.php', + 'Aws\\WellArchitected\\WellArchitectedClient' => $vendorDir . '/aws/aws-sdk-php/src/WellArchitected/WellArchitectedClient.php', + 'Aws\\Wickr\\Exception\\WickrException' => $vendorDir . '/aws/aws-sdk-php/src/Wickr/Exception/WickrException.php', + 'Aws\\Wickr\\WickrClient' => $vendorDir . '/aws/aws-sdk-php/src/Wickr/WickrClient.php', + 'Aws\\WorkDocs\\Exception\\WorkDocsException' => $vendorDir . '/aws/aws-sdk-php/src/WorkDocs/Exception/WorkDocsException.php', + 'Aws\\WorkDocs\\WorkDocsClient' => $vendorDir . '/aws/aws-sdk-php/src/WorkDocs/WorkDocsClient.php', + 'Aws\\WorkMailMessageFlow\\Exception\\WorkMailMessageFlowException' => $vendorDir . '/aws/aws-sdk-php/src/WorkMailMessageFlow/Exception/WorkMailMessageFlowException.php', + 'Aws\\WorkMailMessageFlow\\WorkMailMessageFlowClient' => $vendorDir . '/aws/aws-sdk-php/src/WorkMailMessageFlow/WorkMailMessageFlowClient.php', + 'Aws\\WorkMail\\Exception\\WorkMailException' => $vendorDir . '/aws/aws-sdk-php/src/WorkMail/Exception/WorkMailException.php', + 'Aws\\WorkMail\\WorkMailClient' => $vendorDir . '/aws/aws-sdk-php/src/WorkMail/WorkMailClient.php', + 'Aws\\WorkSpacesThinClient\\Exception\\WorkSpacesThinClientException' => $vendorDir . '/aws/aws-sdk-php/src/WorkSpacesThinClient/Exception/WorkSpacesThinClientException.php', + 'Aws\\WorkSpacesThinClient\\WorkSpacesThinClientClient' => $vendorDir . '/aws/aws-sdk-php/src/WorkSpacesThinClient/WorkSpacesThinClientClient.php', + 'Aws\\WorkSpacesWeb\\Exception\\WorkSpacesWebException' => $vendorDir . '/aws/aws-sdk-php/src/WorkSpacesWeb/Exception/WorkSpacesWebException.php', + 'Aws\\WorkSpacesWeb\\WorkSpacesWebClient' => $vendorDir . '/aws/aws-sdk-php/src/WorkSpacesWeb/WorkSpacesWebClient.php', + 'Aws\\WorkSpaces\\Exception\\WorkSpacesException' => $vendorDir . '/aws/aws-sdk-php/src/WorkSpaces/Exception/WorkSpacesException.php', + 'Aws\\WorkSpaces\\WorkSpacesClient' => $vendorDir . '/aws/aws-sdk-php/src/WorkSpaces/WorkSpacesClient.php', + 'Aws\\WorkspacesInstances\\Exception\\WorkspacesInstancesException' => $vendorDir . '/aws/aws-sdk-php/src/WorkspacesInstances/Exception/WorkspacesInstancesException.php', + 'Aws\\WorkspacesInstances\\WorkspacesInstancesClient' => $vendorDir . '/aws/aws-sdk-php/src/WorkspacesInstances/WorkspacesInstancesClient.php', + 'Aws\\WrappedHttpHandler' => $vendorDir . '/aws/aws-sdk-php/src/WrappedHttpHandler.php', + 'Aws\\XRay\\Exception\\XRayException' => $vendorDir . '/aws/aws-sdk-php/src/XRay/Exception/XRayException.php', + 'Aws\\XRay\\XRayClient' => $vendorDir . '/aws/aws-sdk-php/src/XRay/XRayClient.php', + 'Aws\\drs\\Exception\\drsException' => $vendorDir . '/aws/aws-sdk-php/src/drs/Exception/drsException.php', + 'Aws\\drs\\drsClient' => $vendorDir . '/aws/aws-sdk-php/src/drs/drsClient.php', + 'Aws\\finspace\\Exception\\finspaceException' => $vendorDir . '/aws/aws-sdk-php/src/finspace/Exception/finspaceException.php', + 'Aws\\finspace\\finspaceClient' => $vendorDir . '/aws/aws-sdk-php/src/finspace/finspaceClient.php', + 'Aws\\imagebuilder\\Exception\\imagebuilderException' => $vendorDir . '/aws/aws-sdk-php/src/imagebuilder/Exception/imagebuilderException.php', + 'Aws\\imagebuilder\\imagebuilderClient' => $vendorDir . '/aws/aws-sdk-php/src/imagebuilder/imagebuilderClient.php', + 'Aws\\ivschat\\Exception\\ivschatException' => $vendorDir . '/aws/aws-sdk-php/src/ivschat/Exception/ivschatException.php', + 'Aws\\ivschat\\ivschatClient' => $vendorDir . '/aws/aws-sdk-php/src/ivschat/ivschatClient.php', + 'Aws\\kendra\\Exception\\kendraException' => $vendorDir . '/aws/aws-sdk-php/src/kendra/Exception/kendraException.php', + 'Aws\\kendra\\kendraClient' => $vendorDir . '/aws/aws-sdk-php/src/kendra/kendraClient.php', + 'Aws\\mgn\\Exception\\mgnException' => $vendorDir . '/aws/aws-sdk-php/src/mgn/Exception/mgnException.php', + 'Aws\\mgn\\mgnClient' => $vendorDir . '/aws/aws-sdk-php/src/mgn/mgnClient.php', + 'Aws\\signer\\Exception\\signerException' => $vendorDir . '/aws/aws-sdk-php/src/signer/Exception/signerException.php', + 'Aws\\signer\\signerClient' => $vendorDir . '/aws/aws-sdk-php/src/signer/signerClient.php', 'Brick\\Math\\BigDecimal' => $vendorDir . '/brick/math/src/BigDecimal.php', 'Brick\\Math\\BigInteger' => $vendorDir . '/brick/math/src/BigInteger.php', 'Brick\\Math\\BigNumber' => $vendorDir . '/brick/math/src/BigNumber.php', @@ -151,6 +1509,8 @@ return array( 'Cron\\MonthField' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/MonthField.php', 'Database\\Factories\\UserFactory' => $baseDir . '/database/factories/UserFactory.php', 'Database\\Seeders\\DatabaseSeeder' => $baseDir . '/database/seeders/DatabaseSeeder.php', + 'Database\\Seeders\\MenuListSeeder' => $baseDir . '/database/seeders/MenuListSeeder.php', + 'Database\\Seeders\\TicketTypeSeeder' => $baseDir . '/database/seeders/TicketTypeSeeder.php', 'DateError' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateError.php', 'DateException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateException.php', 'DateInvalidOperationException' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php', @@ -1330,14 +2690,18 @@ return array( 'GuzzleHttp\\Handler\\CurlHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php', 'GuzzleHttp\\Handler\\CurlMultiHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php', 'GuzzleHttp\\Handler\\CurlShareHandleState' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlShareHandleState.php', + 'GuzzleHttp\\Handler\\CurlVersion' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/CurlVersion.php', 'GuzzleHttp\\Handler\\EasyHandle' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php', 'GuzzleHttp\\Handler\\HeaderProcessor' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php', 'GuzzleHttp\\Handler\\MockHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/MockHandler.php', 'GuzzleHttp\\Handler\\Proxy' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/Proxy.php', + 'GuzzleHttp\\Handler\\ProxyEnvironment' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/ProxyEnvironment.php', 'GuzzleHttp\\Handler\\StreamHandler' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php', + 'GuzzleHttp\\Handler\\TlsVersion' => $vendorDir . '/guzzlehttp/guzzle/src/Handler/TlsVersion.php', 'GuzzleHttp\\MessageFormatter' => $vendorDir . '/guzzlehttp/guzzle/src/MessageFormatter.php', 'GuzzleHttp\\MessageFormatterInterface' => $vendorDir . '/guzzlehttp/guzzle/src/MessageFormatterInterface.php', 'GuzzleHttp\\Middleware' => $vendorDir . '/guzzlehttp/guzzle/src/Middleware.php', + 'GuzzleHttp\\Multiplexing' => $vendorDir . '/guzzlehttp/guzzle/src/Multiplexing.php', 'GuzzleHttp\\Pool' => $vendorDir . '/guzzlehttp/guzzle/src/Pool.php', 'GuzzleHttp\\PrepareBodyMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php', 'GuzzleHttp\\Promise\\AggregateException' => $vendorDir . '/guzzlehttp/promises/src/AggregateException.php', @@ -2918,6 +4282,17 @@ return array( 'Illuminate\\View\\ViewFinderInterface' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewFinderInterface.php', 'Illuminate\\View\\ViewName' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewName.php', 'Illuminate\\View\\ViewServiceProvider' => $vendorDir . '/laravel/framework/src/Illuminate/View/ViewServiceProvider.php', + 'JmesPath\\AstRuntime' => $vendorDir . '/mtdowling/jmespath.php/src/AstRuntime.php', + 'JmesPath\\CompilerRuntime' => $vendorDir . '/mtdowling/jmespath.php/src/CompilerRuntime.php', + 'JmesPath\\DebugRuntime' => $vendorDir . '/mtdowling/jmespath.php/src/DebugRuntime.php', + 'JmesPath\\Env' => $vendorDir . '/mtdowling/jmespath.php/src/Env.php', + 'JmesPath\\FnDispatcher' => $vendorDir . '/mtdowling/jmespath.php/src/FnDispatcher.php', + 'JmesPath\\Lexer' => $vendorDir . '/mtdowling/jmespath.php/src/Lexer.php', + 'JmesPath\\Parser' => $vendorDir . '/mtdowling/jmespath.php/src/Parser.php', + 'JmesPath\\SyntaxErrorException' => $vendorDir . '/mtdowling/jmespath.php/src/SyntaxErrorException.php', + 'JmesPath\\TreeCompiler' => $vendorDir . '/mtdowling/jmespath.php/src/TreeCompiler.php', + 'JmesPath\\TreeInterpreter' => $vendorDir . '/mtdowling/jmespath.php/src/TreeInterpreter.php', + 'JmesPath\\Utils' => $vendorDir . '/mtdowling/jmespath.php/src/Utils.php', 'Laravel\\Pail\\Console\\Commands\\PailCommand' => $vendorDir . '/laravel/pail/src/Console/Commands/PailCommand.php', 'Laravel\\Pail\\Contracts\\Printer' => $vendorDir . '/laravel/pail/src/Contracts/Printer.php', 'Laravel\\Pail\\File' => $vendorDir . '/laravel/pail/src/File.php', @@ -3360,6 +4735,9 @@ return array( 'League\\Config\\MutableConfigurationInterface' => $vendorDir . '/league/config/src/MutableConfigurationInterface.php', 'League\\Config\\ReadOnlyConfiguration' => $vendorDir . '/league/config/src/ReadOnlyConfiguration.php', 'League\\Config\\SchemaBuilderInterface' => $vendorDir . '/league/config/src/SchemaBuilderInterface.php', + 'League\\Flysystem\\AwsS3V3\\AwsS3V3Adapter' => $vendorDir . '/league/flysystem-aws-s3-v3/AwsS3V3Adapter.php', + 'League\\Flysystem\\AwsS3V3\\PortableVisibilityConverter' => $vendorDir . '/league/flysystem-aws-s3-v3/PortableVisibilityConverter.php', + 'League\\Flysystem\\AwsS3V3\\VisibilityConverter' => $vendorDir . '/league/flysystem-aws-s3-v3/VisibilityConverter.php', 'League\\Flysystem\\CalculateChecksumFromStream' => $vendorDir . '/league/flysystem/src/CalculateChecksumFromStream.php', 'League\\Flysystem\\ChecksumAlgoIsNotSupported' => $vendorDir . '/league/flysystem/src/ChecksumAlgoIsNotSupported.php', 'League\\Flysystem\\ChecksumProvider' => $vendorDir . '/league/flysystem/src/ChecksumProvider.php', @@ -6190,6 +7568,14 @@ return array( 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => $vendorDir . '/symfony/event-dispatcher/EventSubscriberInterface.php', 'Symfony\\Component\\EventDispatcher\\GenericEvent' => $vendorDir . '/symfony/event-dispatcher/GenericEvent.php', 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => $vendorDir . '/symfony/filesystem/Exception/FileNotFoundException.php', + 'Symfony\\Component\\Filesystem\\Exception\\IOException' => $vendorDir . '/symfony/filesystem/Exception/IOException.php', + 'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/IOExceptionInterface.php', + 'Symfony\\Component\\Filesystem\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/filesystem/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\Filesystem\\Exception\\RuntimeException' => $vendorDir . '/symfony/filesystem/Exception/RuntimeException.php', + 'Symfony\\Component\\Filesystem\\Filesystem' => $vendorDir . '/symfony/filesystem/Filesystem.php', + 'Symfony\\Component\\Filesystem\\Path' => $vendorDir . '/symfony/filesystem/Path.php', 'Symfony\\Component\\Finder\\Comparator\\Comparator' => $vendorDir . '/symfony/finder/Comparator/Comparator.php', 'Symfony\\Component\\Finder\\Comparator\\DateComparator' => $vendorDir . '/symfony/finder/Comparator/DateComparator.php', 'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => $vendorDir . '/symfony/finder/Comparator/NumberComparator.php', @@ -7021,6 +8407,7 @@ return array( 'Termwind\\ValueObjects\\Style' => $vendorDir . '/nunomaduro/termwind/src/ValueObjects/Style.php', 'Termwind\\ValueObjects\\Styles' => $vendorDir . '/nunomaduro/termwind/src/ValueObjects/Styles.php', 'Tests\\Feature\\ExampleTest' => $baseDir . '/tests/Feature/ExampleTest.php', + 'Tests\\Feature\\MenuAccessTest' => $baseDir . '/tests/Feature/MenuAccessTest.php', 'Tests\\TestCase' => $baseDir . '/tests/TestCase.php', 'Tests\\Unit\\ExampleTest' => $baseDir . '/tests/Unit/ExampleTest.php', 'TheSeer\\Tokenizer\\Exception' => $vendorDir . '/theseer/tokenizer/src/Exception.php', diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index b43cf8f..6fa1d98 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -8,22 +8,24 @@ $baseDir = dirname($vendorDir); return array( '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', - 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php', + 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php', 'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php', - 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php', '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', + '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', '606a39d89246991a373564698c2d8383' => $vendorDir . '/symfony/polyfill-php85/bootstrap.php', '662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php', + '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '35a6ad97d21e794e7e22a17d806652e4' => $vendorDir . '/nunomaduro/termwind/src/Functions.php', - '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', + 'b067bc7112e384b61c701452d53a14a8' => $vendorDir . '/mtdowling/jmespath.php/src/JmesPath.php', '6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', '2203a247e6fda86070a5e4e07aed533a' => $vendorDir . '/symfony/clock/Resources/now.php', '09f6b20656683369174dd6fa83b7e5fb' => $vendorDir . '/symfony/polyfill-uuid/bootstrap.php', 'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php', - '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', + '8a9dc1de0ca7e01f3e08231539562f61' => $vendorDir . '/aws/aws-sdk-php/src/functions.php', '47e1160838b5e5a10346ac4084b58c23' => $vendorDir . '/laravel/prompts/src/helpers.php', '801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php', 'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 12ee813..25c95f0 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -36,6 +36,7 @@ return array( 'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'), 'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'), 'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'), + 'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'), 'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'), 'Symfony\\Component\\ErrorHandler\\' => array($vendorDir . '/symfony/error-handler'), 'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'), @@ -62,6 +63,7 @@ return array( 'League\\Uri\\' => array($vendorDir . '/league/uri', $vendorDir . '/league/uri-interfaces'), 'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'), 'League\\Flysystem\\Local\\' => array($vendorDir . '/league/flysystem-local'), + 'League\\Flysystem\\AwsS3V3\\' => array($vendorDir . '/league/flysystem-aws-s3-v3'), 'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'), 'League\\Config\\' => array($vendorDir . '/league/config/src'), 'League\\CommonMark\\' => array($vendorDir . '/league/commonmark/src'), @@ -71,6 +73,7 @@ return array( 'Laravel\\Sail\\' => array($vendorDir . '/laravel/sail/src'), 'Laravel\\Prompts\\' => array($vendorDir . '/laravel/prompts/src'), 'Laravel\\Pail\\' => array($vendorDir . '/laravel/pail/src'), + 'JmesPath\\' => array($vendorDir . '/mtdowling/jmespath.php/src'), 'Illuminate\\Support\\' => array($vendorDir . '/laravel/framework/src/Illuminate/Macroable', $vendorDir . '/laravel/framework/src/Illuminate/Collections', $vendorDir . '/laravel/framework/src/Illuminate/Conditionable', $vendorDir . '/laravel/framework/src/Illuminate/Reflection'), 'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'), 'GuzzleHttp\\UriTemplate\\' => array($vendorDir . '/guzzlehttp/uri-template/src'), @@ -94,5 +97,6 @@ return array( 'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'), 'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'), 'Brick\\Math\\' => array($vendorDir . '/brick/math/src'), + 'Aws\\' => array($vendorDir . '/aws/aws-sdk-php/src'), 'App\\' => array($baseDir . '/app', $vendorDir . '/laravel/pint/app'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index b26ed39..d0e9ac9 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -9,22 +9,24 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 public static $files = array ( '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', - 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', + 'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php', + 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php', 'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php', - 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', 'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php', '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', + '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', '606a39d89246991a373564698c2d8383' => __DIR__ . '/..' . '/symfony/polyfill-php85/bootstrap.php', '662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php', + '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '35a6ad97d21e794e7e22a17d806652e4' => __DIR__ . '/..' . '/nunomaduro/termwind/src/Functions.php', - '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', + 'b067bc7112e384b61c701452d53a14a8' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/JmesPath.php', '6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php', '2203a247e6fda86070a5e4e07aed533a' => __DIR__ . '/..' . '/symfony/clock/Resources/now.php', '09f6b20656683369174dd6fa83b7e5fb' => __DIR__ . '/..' . '/symfony/polyfill-uuid/bootstrap.php', 'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php', - '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', + '8a9dc1de0ca7e01f3e08231539562f61' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/functions.php', '47e1160838b5e5a10346ac4084b58c23' => __DIR__ . '/..' . '/laravel/prompts/src/helpers.php', '801c31d8ed748cfa537fa45402288c95' => __DIR__ . '/..' . '/psy/psysh/src/functions.php', 'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php', @@ -86,6 +88,7 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Symfony\\Component\\HttpKernel\\' => 29, 'Symfony\\Component\\HttpFoundation\\' => 33, 'Symfony\\Component\\Finder\\' => 25, + 'Symfony\\Component\\Filesystem\\' => 29, 'Symfony\\Component\\EventDispatcher\\' => 34, 'Symfony\\Component\\ErrorHandler\\' => 31, 'Symfony\\Component\\CssSelector\\' => 30, @@ -127,6 +130,7 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'League\\Uri\\' => 11, 'League\\MimeTypeDetection\\' => 25, 'League\\Flysystem\\Local\\' => 23, + 'League\\Flysystem\\AwsS3V3\\' => 25, 'League\\Flysystem\\' => 17, 'League\\Config\\' => 14, 'League\\CommonMark\\' => 18, @@ -137,6 +141,10 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Laravel\\Prompts\\' => 16, 'Laravel\\Pail\\' => 13, ), + 'J' => + array ( + 'JmesPath\\' => 9, + ), 'I' => array ( 'Illuminate\\Support\\' => 19, @@ -183,6 +191,7 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 ), 'A' => array ( + 'Aws\\' => 4, 'App\\' => 4, ), ); @@ -308,6 +317,10 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 array ( 0 => __DIR__ . '/..' . '/symfony/finder', ), + 'Symfony\\Component\\Filesystem\\' => + array ( + 0 => __DIR__ . '/..' . '/symfony/filesystem', + ), 'Symfony\\Component\\EventDispatcher\\' => array ( 0 => __DIR__ . '/..' . '/symfony/event-dispatcher', @@ -415,6 +428,10 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 array ( 0 => __DIR__ . '/..' . '/league/flysystem-local', ), + 'League\\Flysystem\\AwsS3V3\\' => + array ( + 0 => __DIR__ . '/..' . '/league/flysystem-aws-s3-v3', + ), 'League\\Flysystem\\' => array ( 0 => __DIR__ . '/..' . '/league/flysystem/src', @@ -451,6 +468,10 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 array ( 0 => __DIR__ . '/..' . '/laravel/pail/src', ), + 'JmesPath\\' => + array ( + 0 => __DIR__ . '/..' . '/mtdowling/jmespath.php/src', + ), 'Illuminate\\Support\\' => array ( 0 => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Macroable', @@ -548,6 +569,10 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 array ( 0 => __DIR__ . '/..' . '/brick/math/src', ), + 'Aws\\' => + array ( + 0 => __DIR__ . '/..' . '/aws/aws-sdk-php/src', + ), 'App\\' => array ( 0 => __DIR__ . '/../..' . '/app', @@ -556,27 +581,1385 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 ); public static $classMap = array ( - 'App\\Docs\\OpenApiSpec' => __DIR__ . '/../..' . '/app/Docs/OpenApiSpec.php', + 'AWS\\CRT\\Auth\\AwsCredentials' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Auth/AwsCredentials.php', + 'AWS\\CRT\\Auth\\CredentialsProvider' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Auth/CredentialsProvider.php', + 'AWS\\CRT\\Auth\\Signable' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Auth/Signable.php', + 'AWS\\CRT\\Auth\\SignatureType' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Auth/SignatureType.php', + 'AWS\\CRT\\Auth\\SignedBodyHeaderType' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Auth/SignedBodyHeaderType.php', + 'AWS\\CRT\\Auth\\Signing' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Auth/Signing.php', + 'AWS\\CRT\\Auth\\SigningAlgorithm' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Auth/SigningAlgorithm.php', + 'AWS\\CRT\\Auth\\SigningConfigAWS' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Auth/SigningConfigAWS.php', + 'AWS\\CRT\\Auth\\SigningResult' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Auth/SigningResult.php', + 'AWS\\CRT\\Auth\\StaticCredentialsProvider' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Auth/StaticCredentialsProvider.php', + 'AWS\\CRT\\CRT' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/CRT.php', + 'AWS\\CRT\\HTTP\\Headers' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/HTTP/Headers.php', + 'AWS\\CRT\\HTTP\\Message' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/HTTP/Message.php', + 'AWS\\CRT\\HTTP\\Request' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/HTTP/Request.php', + 'AWS\\CRT\\HTTP\\Response' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/HTTP/Response.php', + 'AWS\\CRT\\IO\\EventLoopGroup' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/IO/EventLoopGroup.php', + 'AWS\\CRT\\IO\\InputStream' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/IO/InputStream.php', + 'AWS\\CRT\\Internal\\Encoding' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Internal/Encoding.php', + 'AWS\\CRT\\Internal\\Extension' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Internal/Extension.php', + 'AWS\\CRT\\Log' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Log.php', + 'AWS\\CRT\\NativeResource' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/NativeResource.php', + 'AWS\\CRT\\OptionValue' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Options.php', + 'AWS\\CRT\\Options' => __DIR__ . '/..' . '/aws/aws-crt-php/src/AWS/CRT/Options.php', + 'App\\Enums\\TicketStatus' => __DIR__ . '/../..' . '/app/Enums/TicketStatus.php', + 'App\\Enums\\UserAccessLevel' => __DIR__ . '/../..' . '/app/Enums/UserAccessLevel.php', 'App\\Http\\Controllers\\ApiController' => __DIR__ . '/../..' . '/app/Http/Controllers/ApiController.php', + 'App\\Http\\Controllers\\Audit\\AuditLogController' => __DIR__ . '/../..' . '/app/Http/Controllers/Audit/AuditLogController.php', 'App\\Http\\Controllers\\AuthController' => __DIR__ . '/../..' . '/app/Http/Controllers/AuthController.php', 'App\\Http\\Controllers\\Controller' => __DIR__ . '/../..' . '/app/Http/Controllers/Controller.php', - 'App\\Http\\Controllers\\TicketController' => __DIR__ . '/../..' . '/app/Http/Controllers/TicketController.php', + 'App\\Http\\Controllers\\Material\\MaterialController' => __DIR__ . '/../..' . '/app/Http/Controllers/Material/MaterialController.php', + 'App\\Http\\Controllers\\Menu\\MenuController' => __DIR__ . '/../..' . '/app/Http/Controllers/Menu/MenuController.php', + 'App\\Http\\Controllers\\Menu\\MenuGroupController' => __DIR__ . '/../..' . '/app/Http/Controllers/Menu/MenuGroupController.php', + 'App\\Http\\Controllers\\Menu\\MenuListController' => __DIR__ . '/../..' . '/app/Http/Controllers/Menu/MenuListController.php', + 'App\\Http\\Controllers\\Profile\\ProfileController' => __DIR__ . '/../..' . '/app/Http/Controllers/Profile/ProfileController.php', + 'App\\Http\\Controllers\\Tenant\\TenantController' => __DIR__ . '/../..' . '/app/Http/Controllers/Tenant/TenantController.php', 'App\\Http\\Controllers\\Ticket\\TicketController' => __DIR__ . '/../..' . '/app/Http/Controllers/Ticket/TicketController.php', + 'App\\Http\\Controllers\\Ticket\\TicketIncidentTypeController' => __DIR__ . '/../..' . '/app/Http/Controllers/Ticket/TicketIncidentTypeController.php', + 'App\\Http\\Controllers\\Ticket\\TicketTypeController' => __DIR__ . '/../..' . '/app/Http/Controllers/Ticket/TicketTypeController.php', + 'App\\Http\\Controllers\\Ticket\\TicketWorkflowController' => __DIR__ . '/../..' . '/app/Http/Controllers/Ticket/TicketWorkflowController.php', + 'App\\Http\\Controllers\\User\\UserController' => __DIR__ . '/../..' . '/app/Http/Controllers/User/UserController.php', + 'App\\Http\\Middleware\\EnsureAccessLevel' => __DIR__ . '/../..' . '/app/Http/Middleware/EnsureAccessLevel.php', + 'App\\Http\\Middleware\\EnsureMenuPermission' => __DIR__ . '/../..' . '/app/Http/Middleware/EnsureMenuPermission.php', + 'App\\Http\\Middleware\\EnsureMenuResourcePermission' => __DIR__ . '/../..' . '/app/Http/Middleware/EnsureMenuResourcePermission.php', + 'App\\Http\\Middleware\\ResolveTenantContext' => __DIR__ . '/../..' . '/app/Http/Middleware/ResolveTenantContext.php', + 'App\\Http\\Requests\\Audit\\AuditLogFilterRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Audit/AuditLogFilterRequest.php', + 'App\\Http\\Requests\\Auth\\RegisterRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Auth/RegisterRequest.php', + 'App\\Http\\Requests\\Auth\\ResendVerificationCodeRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Auth/ResendVerificationCodeRequest.php', + 'App\\Http\\Requests\\Auth\\VerifyRegistrationRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Auth/VerifyRegistrationRequest.php', + 'App\\Http\\Requests\\Material\\AssignMaterialRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Material/AssignMaterialRequest.php', + 'App\\Http\\Requests\\Material\\IndexMaterialRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Material/IndexMaterialRequest.php', + 'App\\Http\\Requests\\Material\\ReturnMaterialRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Material/ReturnMaterialRequest.php', + 'App\\Http\\Requests\\Material\\TransferMaterialRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Material/TransferMaterialRequest.php', + 'App\\Http\\Requests\\MenuGroup\\IndexMenuGroupRequest' => __DIR__ . '/../..' . '/app/Http/Requests/MenuGroup/IndexMenuGroupRequest.php', + 'App\\Http\\Requests\\MenuGroup\\StoreMenuGroupRequest' => __DIR__ . '/../..' . '/app/Http/Requests/MenuGroup/StoreMenuGroupRequest.php', + 'App\\Http\\Requests\\MenuGroup\\SyncMenuGroupMenusRequest' => __DIR__ . '/../..' . '/app/Http/Requests/MenuGroup/SyncMenuGroupMenusRequest.php', + 'App\\Http\\Requests\\MenuGroup\\SyncUserMenuGroupsRequest' => __DIR__ . '/../..' . '/app/Http/Requests/MenuGroup/SyncUserMenuGroupsRequest.php', + 'App\\Http\\Requests\\MenuGroup\\UpdateMenuGroupRequest' => __DIR__ . '/../..' . '/app/Http/Requests/MenuGroup/UpdateMenuGroupRequest.php', + 'App\\Http\\Requests\\MenuList\\IndexMenuListRequest' => __DIR__ . '/../..' . '/app/Http/Requests/MenuList/IndexMenuListRequest.php', + 'App\\Http\\Requests\\MenuList\\StoreMenuListRequest' => __DIR__ . '/../..' . '/app/Http/Requests/MenuList/StoreMenuListRequest.php', + 'App\\Http\\Requests\\MenuList\\UpdateMenuListRequest' => __DIR__ . '/../..' . '/app/Http/Requests/MenuList/UpdateMenuListRequest.php', + 'App\\Http\\Requests\\Profile\\UpdateProfileRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Profile/UpdateProfileRequest.php', + 'App\\Http\\Requests\\Tenant\\IndexTenantRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Tenant/IndexTenantRequest.php', + 'App\\Http\\Requests\\Tenant\\StoreTenantRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Tenant/StoreTenantRequest.php', + 'App\\Http\\Requests\\Tenant\\UpdateTenantRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Tenant/UpdateTenantRequest.php', + 'App\\Http\\Requests\\TicketIncidentType\\IndexTicketIncidentTypeRequest' => __DIR__ . '/../..' . '/app/Http/Requests/TicketIncidentType/IndexTicketIncidentTypeRequest.php', + 'App\\Http\\Requests\\TicketIncidentType\\StoreTicketIncidentTypeRequest' => __DIR__ . '/../..' . '/app/Http/Requests/TicketIncidentType/StoreTicketIncidentTypeRequest.php', + 'App\\Http\\Requests\\TicketIncidentType\\UpdateTicketIncidentTypeRequest' => __DIR__ . '/../..' . '/app/Http/Requests/TicketIncidentType/UpdateTicketIncidentTypeRequest.php', + 'App\\Http\\Requests\\TicketType\\IndexTicketTypeRequest' => __DIR__ . '/../..' . '/app/Http/Requests/TicketType/IndexTicketTypeRequest.php', + 'App\\Http\\Requests\\TicketType\\StoreTicketTypeRequest' => __DIR__ . '/../..' . '/app/Http/Requests/TicketType/StoreTicketTypeRequest.php', + 'App\\Http\\Requests\\TicketType\\UpdateTicketTypeRequest' => __DIR__ . '/../..' . '/app/Http/Requests/TicketType/UpdateTicketTypeRequest.php', + 'App\\Http\\Requests\\Ticket\\ApproveTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/ApproveTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\AssignTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/AssignTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\CancelTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/CancelTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\CloseTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/CloseTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\EscalateTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/EscalateTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\IndexTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/IndexTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\PendingTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/PendingTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\ReassignTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/ReassignTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\RejectTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/RejectTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\ResolveTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/ResolveTicketRequest.php', + 'App\\Http\\Requests\\Ticket\\StartTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/StartTicketRequest.php', 'App\\Http\\Requests\\Ticket\\StoreTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/StoreTicketRequest.php', 'App\\Http\\Requests\\Ticket\\UpdateTicketRequest' => __DIR__ . '/../..' . '/app/Http/Requests/Ticket/UpdateTicketRequest.php', - 'App\\Http\\Resources\\TicketResource' => __DIR__ . '/../..' . '/app/Http/Resources/TicketResource.php', + 'App\\Http\\Requests\\User\\IndexUserRequest' => __DIR__ . '/../..' . '/app/Http/Requests/User/IndexUserRequest.php', + 'App\\Http\\Requests\\User\\StoreUserRequest' => __DIR__ . '/../..' . '/app/Http/Requests/User/StoreUserRequest.php', + 'App\\Http\\Requests\\User\\UpdateUserRequest' => __DIR__ . '/../..' . '/app/Http/Requests/User/UpdateUserRequest.php', + 'App\\Http\\Resources\\Audit\\AuditLogResource' => __DIR__ . '/../..' . '/app/Http/Resources/Audit/AuditLogResource.php', + 'App\\Http\\Resources\\Material\\MaterialResource' => __DIR__ . '/../..' . '/app/Http/Resources/Material/MaterialResource.php', + 'App\\Http\\Resources\\MenuGroup\\MenuGroupResource' => __DIR__ . '/../..' . '/app/Http/Resources/MenuGroup/MenuGroupResource.php', + 'App\\Http\\Resources\\MenuList\\MenuListResource' => __DIR__ . '/../..' . '/app/Http/Resources/MenuList/MenuListResource.php', + 'App\\Http\\Resources\\Tenant\\TenantResource' => __DIR__ . '/../..' . '/app/Http/Resources/Tenant/TenantResource.php', + 'App\\Http\\Resources\\TicketIncidentType\\TicketIncidentTypeResource' => __DIR__ . '/../..' . '/app/Http/Resources/TicketIncidentType/TicketIncidentTypeResource.php', + 'App\\Http\\Resources\\TicketType\\TicketTypeResource' => __DIR__ . '/../..' . '/app/Http/Resources/TicketType/TicketTypeResource.php', + 'App\\Http\\Resources\\Ticket\\TicketResource' => __DIR__ . '/../..' . '/app/Http/Resources/Ticket/TicketResource.php', + 'App\\Http\\Resources\\User\\UserResource' => __DIR__ . '/../..' . '/app/Http/Resources/User/UserResource.php', + 'App\\Models\\AuditLog' => __DIR__ . '/../..' . '/app/Models/AuditLog.php', + 'App\\Models\\MaterialMovement' => __DIR__ . '/../..' . '/app/Models/MaterialMovement.php', + 'App\\Models\\MenuGroup' => __DIR__ . '/../..' . '/app/Models/MenuGroup.php', + 'App\\Models\\MenuGroupMenu' => __DIR__ . '/../..' . '/app/Models/MenuGroupMenu.php', + 'App\\Models\\MenuList' => __DIR__ . '/../..' . '/app/Models/MenuList.php', + 'App\\Models\\TechnicianConsumable' => __DIR__ . '/../..' . '/app/Models/TechnicianConsumable.php', + 'App\\Models\\TechnicianMaterial' => __DIR__ . '/../..' . '/app/Models/TechnicianMaterial.php', + 'App\\Models\\Tenant' => __DIR__ . '/../..' . '/app/Models/Tenant.php', 'App\\Models\\Ticket' => __DIR__ . '/../..' . '/app/Models/Ticket.php', 'App\\Models\\TicketApproval' => __DIR__ . '/../..' . '/app/Models/TicketApproval.php', 'App\\Models\\TicketAssignment' => __DIR__ . '/../..' . '/app/Models/TicketAssignment.php', 'App\\Models\\TicketFile' => __DIR__ . '/../..' . '/app/Models/TicketFile.php', + 'App\\Models\\TicketIncidentType' => __DIR__ . '/../..' . '/app/Models/TicketIncidentType.php', 'App\\Models\\TicketLog' => __DIR__ . '/../..' . '/app/Models/TicketLog.php', 'App\\Models\\TicketMaterial' => __DIR__ . '/../..' . '/app/Models/TicketMaterial.php', 'App\\Models\\TicketType' => __DIR__ . '/../..' . '/app/Models/TicketType.php', 'App\\Models\\User' => __DIR__ . '/../..' . '/app/Models/User.php', + 'App\\Models\\UserMenuGroup' => __DIR__ . '/../..' . '/app/Models/UserMenuGroup.php', + 'App\\Models\\UserProfile' => __DIR__ . '/../..' . '/app/Models/UserProfile.php', + 'App\\Models\\UserTenant' => __DIR__ . '/../..' . '/app/Models/UserTenant.php', 'App\\Providers\\AppServiceProvider' => __DIR__ . '/../..' . '/app/Providers/AppServiceProvider.php', + 'App\\Services\\Access\\AccessService' => __DIR__ . '/../..' . '/app/Services/Access/AccessService.php', + 'App\\Services\\Audit\\AuditLogService' => __DIR__ . '/../..' . '/app/Services/Audit/AuditLogService.php', + 'App\\Services\\Auth\\AuthService' => __DIR__ . '/../..' . '/app/Services/Auth/AuthService.php', + 'App\\Services\\Material\\MaterialService' => __DIR__ . '/../..' . '/app/Services/Material/MaterialService.php', + 'App\\Services\\Menu\\MenuGroupService' => __DIR__ . '/../..' . '/app/Services/Menu/MenuGroupService.php', + 'App\\Services\\Menu\\MenuListService' => __DIR__ . '/../..' . '/app/Services/Menu/MenuListService.php', + 'App\\Services\\Tenant\\TenantService' => __DIR__ . '/../..' . '/app/Services/Tenant/TenantService.php', + 'App\\Services\\Ticket\\TicketIncidentTypeService' => __DIR__ . '/../..' . '/app/Services/Ticket/TicketIncidentTypeService.php', 'App\\Services\\Ticket\\TicketService' => __DIR__ . '/../..' . '/app/Services/Ticket/TicketService.php', + 'App\\Services\\Ticket\\TicketTypeService' => __DIR__ . '/../..' . '/app/Services/Ticket/TicketTypeService.php', + 'App\\Services\\Ticket\\TicketWorkflowService' => __DIR__ . '/../..' . '/app/Services/Ticket/TicketWorkflowService.php', + 'App\\Services\\Ticket\\TicketWorkflowValidator' => __DIR__ . '/../..' . '/app/Services/Ticket/TicketWorkflowValidator.php', + 'App\\Services\\User\\UserService' => __DIR__ . '/../..' . '/app/Services/User/UserService.php', 'App\\Traits\\ApiResponse' => __DIR__ . '/../..' . '/app/Traits/ApiResponse.php', + 'App\\Traits\\Auditable' => __DIR__ . '/../..' . '/app/Traits/Auditable.php', 'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php', + 'Aws\\ACMPCA\\ACMPCAClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ACMPCA/ACMPCAClient.php', + 'Aws\\ACMPCA\\Exception\\ACMPCAException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ACMPCA/Exception/ACMPCAException.php', + 'Aws\\AIOps\\AIOpsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AIOps/AIOpsClient.php', + 'Aws\\AIOps\\Exception\\AIOpsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AIOps/Exception/AIOpsException.php', + 'Aws\\ARCRegionSwitch\\ARCRegionSwitchClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ARCRegionSwitch/ARCRegionSwitchClient.php', + 'Aws\\ARCRegionSwitch\\Exception\\ARCRegionSwitchException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ARCRegionSwitch/Exception/ARCRegionSwitchException.php', + 'Aws\\ARCZonalShift\\ARCZonalShiftClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ARCZonalShift/ARCZonalShiftClient.php', + 'Aws\\ARCZonalShift\\Exception\\ARCZonalShiftException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ARCZonalShift/Exception/ARCZonalShiftException.php', + 'Aws\\AbstractConfigurationProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AbstractConfigurationProvider.php', + 'Aws\\AccessAnalyzer\\AccessAnalyzerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AccessAnalyzer/AccessAnalyzerClient.php', + 'Aws\\AccessAnalyzer\\Exception\\AccessAnalyzerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AccessAnalyzer/Exception/AccessAnalyzerException.php', + 'Aws\\Account\\AccountClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Account/AccountClient.php', + 'Aws\\Account\\Exception\\AccountException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Account/Exception/AccountException.php', + 'Aws\\Acm\\AcmClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Acm/AcmClient.php', + 'Aws\\Acm\\Exception\\AcmException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Acm/Exception/AcmException.php', + 'Aws\\AmplifyBackend\\AmplifyBackendClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AmplifyBackend/AmplifyBackendClient.php', + 'Aws\\AmplifyBackend\\Exception\\AmplifyBackendException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AmplifyBackend/Exception/AmplifyBackendException.php', + 'Aws\\AmplifyUIBuilder\\AmplifyUIBuilderClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AmplifyUIBuilder/AmplifyUIBuilderClient.php', + 'Aws\\AmplifyUIBuilder\\Exception\\AmplifyUIBuilderException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AmplifyUIBuilder/Exception/AmplifyUIBuilderException.php', + 'Aws\\Amplify\\AmplifyClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Amplify/AmplifyClient.php', + 'Aws\\Amplify\\Exception\\AmplifyException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Amplify/Exception/AmplifyException.php', + 'Aws\\ApiGatewayManagementApi\\ApiGatewayManagementApiClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApiGatewayManagementApi/ApiGatewayManagementApiClient.php', + 'Aws\\ApiGatewayManagementApi\\Exception\\ApiGatewayManagementApiException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApiGatewayManagementApi/Exception/ApiGatewayManagementApiException.php', + 'Aws\\ApiGatewayV2\\ApiGatewayV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApiGatewayV2/ApiGatewayV2Client.php', + 'Aws\\ApiGatewayV2\\Exception\\ApiGatewayV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApiGatewayV2/Exception/ApiGatewayV2Exception.php', + 'Aws\\ApiGateway\\ApiGatewayClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApiGateway/ApiGatewayClient.php', + 'Aws\\ApiGateway\\Exception\\ApiGatewayException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApiGateway/Exception/ApiGatewayException.php', + 'Aws\\Api\\AbstractModel' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/AbstractModel.php', + 'Aws\\Api\\ApiProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/ApiProvider.php', + 'Aws\\Api\\Cbor\\CborDecoder' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Cbor/CborDecoder.php', + 'Aws\\Api\\Cbor\\CborEncoder' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Cbor/CborEncoder.php', + 'Aws\\Api\\Cbor\\Exception\\CborException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Cbor/Exception/CborException.php', + 'Aws\\Api\\DateTimeResult' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/DateTimeResult.php', + 'Aws\\Api\\DocModel' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/DocModel.php', + 'Aws\\Api\\ErrorParser\\AbstractErrorParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/ErrorParser/AbstractErrorParser.php', + 'Aws\\Api\\ErrorParser\\AbstractRpcV2ErrorParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/ErrorParser/AbstractRpcV2ErrorParser.php', + 'Aws\\Api\\ErrorParser\\JsonParserTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/ErrorParser/JsonParserTrait.php', + 'Aws\\Api\\ErrorParser\\JsonRpcErrorParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/ErrorParser/JsonRpcErrorParser.php', + 'Aws\\Api\\ErrorParser\\RestJsonErrorParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/ErrorParser/RestJsonErrorParser.php', + 'Aws\\Api\\ErrorParser\\RpcV2CborErrorParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/ErrorParser/RpcV2CborErrorParser.php', + 'Aws\\Api\\ErrorParser\\XmlErrorParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/ErrorParser/XmlErrorParser.php', + 'Aws\\Api\\Exception\\RpcV2CborException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Exception/RpcV2CborException.php', + 'Aws\\Api\\ListShape' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/ListShape.php', + 'Aws\\Api\\MapShape' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/MapShape.php', + 'Aws\\Api\\Operation' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Operation.php', + 'Aws\\Api\\Parser\\AbstractParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/AbstractParser.php', + 'Aws\\Api\\Parser\\AbstractRestParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/AbstractRestParser.php', + 'Aws\\Api\\Parser\\AbstractRpcV2Parser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/AbstractRpcV2Parser.php', + 'Aws\\Api\\Parser\\Crc32ValidatingParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/Crc32ValidatingParser.php', + 'Aws\\Api\\Parser\\DecodingEventStreamIterator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/DecodingEventStreamIterator.php', + 'Aws\\Api\\Parser\\EventParsingIterator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/EventParsingIterator.php', + 'Aws\\Api\\Parser\\Exception\\ParserException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/Exception/ParserException.php', + 'Aws\\Api\\Parser\\JsonParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/JsonParser.php', + 'Aws\\Api\\Parser\\JsonRpcParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/JsonRpcParser.php', + 'Aws\\Api\\Parser\\MetadataParserTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/MetadataParserTrait.php', + 'Aws\\Api\\Parser\\NonSeekableStreamDecodingEventStreamIterator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/NonSeekableStreamDecodingEventStreamIterator.php', + 'Aws\\Api\\Parser\\PayloadParserTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/PayloadParserTrait.php', + 'Aws\\Api\\Parser\\QueryParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/QueryParser.php', + 'Aws\\Api\\Parser\\RestJsonParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/RestJsonParser.php', + 'Aws\\Api\\Parser\\RestXmlParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/RestXmlParser.php', + 'Aws\\Api\\Parser\\RpcV2CborParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/RpcV2CborParser.php', + 'Aws\\Api\\Parser\\RpcV2ParserTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/RpcV2ParserTrait.php', + 'Aws\\Api\\Parser\\XmlParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Parser/XmlParser.php', + 'Aws\\Api\\Serializer\\AbstractRpcV2Serializer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/AbstractRpcV2Serializer.php', + 'Aws\\Api\\Serializer\\Ec2ParamBuilder' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/Ec2ParamBuilder.php', + 'Aws\\Api\\Serializer\\JsonBody' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/JsonBody.php', + 'Aws\\Api\\Serializer\\JsonRpcSerializer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/JsonRpcSerializer.php', + 'Aws\\Api\\Serializer\\QueryParamBuilder' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/QueryParamBuilder.php', + 'Aws\\Api\\Serializer\\QuerySerializer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/QuerySerializer.php', + 'Aws\\Api\\Serializer\\RestJsonSerializer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/RestJsonSerializer.php', + 'Aws\\Api\\Serializer\\RestSerializer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/RestSerializer.php', + 'Aws\\Api\\Serializer\\RestXmlSerializer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/RestXmlSerializer.php', + 'Aws\\Api\\Serializer\\RpcV2CborSerializer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/RpcV2CborSerializer.php', + 'Aws\\Api\\Serializer\\XmlBody' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Serializer/XmlBody.php', + 'Aws\\Api\\Service' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Service.php', + 'Aws\\Api\\Shape' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Shape.php', + 'Aws\\Api\\ShapeMap' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/ShapeMap.php', + 'Aws\\Api\\StructureShape' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/StructureShape.php', + 'Aws\\Api\\SupportedProtocols' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/SupportedProtocols.php', + 'Aws\\Api\\TimestampShape' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/TimestampShape.php', + 'Aws\\Api\\Validator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Api/Validator.php', + 'Aws\\AppConfigData\\AppConfigDataClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppConfigData/AppConfigDataClient.php', + 'Aws\\AppConfigData\\Exception\\AppConfigDataException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppConfigData/Exception/AppConfigDataException.php', + 'Aws\\AppConfig\\AppConfigClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppConfig/AppConfigClient.php', + 'Aws\\AppConfig\\Exception\\AppConfigException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppConfig/Exception/AppConfigException.php', + 'Aws\\AppFabric\\AppFabricClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppFabric/AppFabricClient.php', + 'Aws\\AppFabric\\Exception\\AppFabricException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppFabric/Exception/AppFabricException.php', + 'Aws\\AppIntegrationsService\\AppIntegrationsServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppIntegrationsService/AppIntegrationsServiceClient.php', + 'Aws\\AppIntegrationsService\\Exception\\AppIntegrationsServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppIntegrationsService/Exception/AppIntegrationsServiceException.php', + 'Aws\\AppMesh\\AppMeshClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppMesh/AppMeshClient.php', + 'Aws\\AppMesh\\Exception\\AppMeshException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppMesh/Exception/AppMeshException.php', + 'Aws\\AppRegistry\\AppRegistryClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppRegistry/AppRegistryClient.php', + 'Aws\\AppRegistry\\Exception\\AppRegistryException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppRegistry/Exception/AppRegistryException.php', + 'Aws\\AppRunner\\AppRunnerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppRunner/AppRunnerClient.php', + 'Aws\\AppRunner\\Exception\\AppRunnerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppRunner/Exception/AppRunnerException.php', + 'Aws\\AppSync\\AppSyncClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppSync/AppSyncClient.php', + 'Aws\\AppSync\\Exception\\AppSyncException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AppSync/Exception/AppSyncException.php', + 'Aws\\Appflow\\AppflowClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Appflow/AppflowClient.php', + 'Aws\\Appflow\\Exception\\AppflowException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Appflow/Exception/AppflowException.php', + 'Aws\\ApplicationAutoScaling\\ApplicationAutoScalingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApplicationAutoScaling/ApplicationAutoScalingClient.php', + 'Aws\\ApplicationAutoScaling\\Exception\\ApplicationAutoScalingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApplicationAutoScaling/Exception/ApplicationAutoScalingException.php', + 'Aws\\ApplicationCostProfiler\\ApplicationCostProfilerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApplicationCostProfiler/ApplicationCostProfilerClient.php', + 'Aws\\ApplicationCostProfiler\\Exception\\ApplicationCostProfilerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApplicationCostProfiler/Exception/ApplicationCostProfilerException.php', + 'Aws\\ApplicationDiscoveryService\\ApplicationDiscoveryServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApplicationDiscoveryService/ApplicationDiscoveryServiceClient.php', + 'Aws\\ApplicationDiscoveryService\\Exception\\ApplicationDiscoveryServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApplicationDiscoveryService/Exception/ApplicationDiscoveryServiceException.php', + 'Aws\\ApplicationInsights\\ApplicationInsightsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApplicationInsights/ApplicationInsightsClient.php', + 'Aws\\ApplicationInsights\\Exception\\ApplicationInsightsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApplicationInsights/Exception/ApplicationInsightsException.php', + 'Aws\\ApplicationSignals\\ApplicationSignalsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApplicationSignals/ApplicationSignalsClient.php', + 'Aws\\ApplicationSignals\\Exception\\ApplicationSignalsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ApplicationSignals/Exception/ApplicationSignalsException.php', + 'Aws\\Appstream\\AppstreamClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Appstream/AppstreamClient.php', + 'Aws\\Appstream\\Exception\\AppstreamException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Appstream/Exception/AppstreamException.php', + 'Aws\\Arn\\AccessPointArn' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/AccessPointArn.php', + 'Aws\\Arn\\AccessPointArnInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/AccessPointArnInterface.php', + 'Aws\\Arn\\Arn' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/Arn.php', + 'Aws\\Arn\\ArnInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/ArnInterface.php', + 'Aws\\Arn\\ArnParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/ArnParser.php', + 'Aws\\Arn\\Exception\\InvalidArnException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/Exception/InvalidArnException.php', + 'Aws\\Arn\\ObjectLambdaAccessPointArn' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/ObjectLambdaAccessPointArn.php', + 'Aws\\Arn\\ResourceTypeAndIdTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/ResourceTypeAndIdTrait.php', + 'Aws\\Arn\\S3\\AccessPointArn' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/S3/AccessPointArn.php', + 'Aws\\Arn\\S3\\BucketArnInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/S3/BucketArnInterface.php', + 'Aws\\Arn\\S3\\MultiRegionAccessPointArn' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/S3/MultiRegionAccessPointArn.php', + 'Aws\\Arn\\S3\\OutpostsAccessPointArn' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/S3/OutpostsAccessPointArn.php', + 'Aws\\Arn\\S3\\OutpostsArnInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/S3/OutpostsArnInterface.php', + 'Aws\\Arn\\S3\\OutpostsBucketArn' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Arn/S3/OutpostsBucketArn.php', + 'Aws\\Artifact\\ArtifactClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Artifact/ArtifactClient.php', + 'Aws\\Artifact\\Exception\\ArtifactException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Artifact/Exception/ArtifactException.php', + 'Aws\\Athena\\AthenaClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Athena/AthenaClient.php', + 'Aws\\Athena\\Exception\\AthenaException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Athena/Exception/AthenaException.php', + 'Aws\\AuditManager\\AuditManagerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AuditManager/AuditManagerClient.php', + 'Aws\\AuditManager\\Exception\\AuditManagerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AuditManager/Exception/AuditManagerException.php', + 'Aws\\AugmentedAIRuntime\\AugmentedAIRuntimeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AugmentedAIRuntime/AugmentedAIRuntimeClient.php', + 'Aws\\AugmentedAIRuntime\\Exception\\AugmentedAIRuntimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AugmentedAIRuntime/Exception/AugmentedAIRuntimeException.php', + 'Aws\\Auth\\AuthSchemeResolver' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Auth/AuthSchemeResolver.php', + 'Aws\\Auth\\AuthSchemeResolverInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Auth/AuthSchemeResolverInterface.php', + 'Aws\\Auth\\AuthSelectionMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Auth/AuthSelectionMiddleware.php', + 'Aws\\Auth\\Exception\\UnresolvedAuthSchemeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Auth/Exception/UnresolvedAuthSchemeException.php', + 'Aws\\AutoScalingPlans\\AutoScalingPlansClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AutoScalingPlans/AutoScalingPlansClient.php', + 'Aws\\AutoScalingPlans\\Exception\\AutoScalingPlansException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AutoScalingPlans/Exception/AutoScalingPlansException.php', + 'Aws\\AutoScaling\\AutoScalingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AutoScaling/AutoScalingClient.php', + 'Aws\\AutoScaling\\Exception\\AutoScalingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AutoScaling/Exception/AutoScalingException.php', + 'Aws\\AwsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AwsClient.php', + 'Aws\\AwsClientInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AwsClientInterface.php', + 'Aws\\AwsClientTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/AwsClientTrait.php', + 'Aws\\B2bi\\B2biClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/B2bi/B2biClient.php', + 'Aws\\B2bi\\Exception\\B2biException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/B2bi/Exception/B2biException.php', + 'Aws\\BCMDashboards\\BCMDashboardsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BCMDashboards/BCMDashboardsClient.php', + 'Aws\\BCMDashboards\\Exception\\BCMDashboardsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BCMDashboards/Exception/BCMDashboardsException.php', + 'Aws\\BCMDataExports\\BCMDataExportsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BCMDataExports/BCMDataExportsClient.php', + 'Aws\\BCMDataExports\\Exception\\BCMDataExportsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BCMDataExports/Exception/BCMDataExportsException.php', + 'Aws\\BCMPricingCalculator\\BCMPricingCalculatorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BCMPricingCalculator/BCMPricingCalculatorClient.php', + 'Aws\\BCMPricingCalculator\\Exception\\BCMPricingCalculatorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BCMPricingCalculator/Exception/BCMPricingCalculatorException.php', + 'Aws\\BCMRecommendedActions\\BCMRecommendedActionsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BCMRecommendedActions/BCMRecommendedActionsClient.php', + 'Aws\\BCMRecommendedActions\\Exception\\BCMRecommendedActionsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BCMRecommendedActions/Exception/BCMRecommendedActionsException.php', + 'Aws\\BackupGateway\\BackupGatewayClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BackupGateway/BackupGatewayClient.php', + 'Aws\\BackupGateway\\Exception\\BackupGatewayException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BackupGateway/Exception/BackupGatewayException.php', + 'Aws\\BackupSearch\\BackupSearchClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BackupSearch/BackupSearchClient.php', + 'Aws\\BackupSearch\\Exception\\BackupSearchException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BackupSearch/Exception/BackupSearchException.php', + 'Aws\\Backup\\BackupClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Backup/BackupClient.php', + 'Aws\\Backup\\Exception\\BackupException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Backup/Exception/BackupException.php', + 'Aws\\Batch\\BatchClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Batch/BatchClient.php', + 'Aws\\Batch\\Exception\\BatchException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Batch/Exception/BatchException.php', + 'Aws\\BedrockAgentCoreControl\\BedrockAgentCoreControlClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockAgentCoreControl/BedrockAgentCoreControlClient.php', + 'Aws\\BedrockAgentCoreControl\\Exception\\BedrockAgentCoreControlException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockAgentCoreControl/Exception/BedrockAgentCoreControlException.php', + 'Aws\\BedrockAgentCore\\BedrockAgentCoreClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockAgentCore/BedrockAgentCoreClient.php', + 'Aws\\BedrockAgentCore\\Exception\\BedrockAgentCoreException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockAgentCore/Exception/BedrockAgentCoreException.php', + 'Aws\\BedrockAgentRuntime\\BedrockAgentRuntimeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockAgentRuntime/BedrockAgentRuntimeClient.php', + 'Aws\\BedrockAgentRuntime\\Exception\\BedrockAgentRuntimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockAgentRuntime/Exception/BedrockAgentRuntimeException.php', + 'Aws\\BedrockAgent\\BedrockAgentClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockAgent/BedrockAgentClient.php', + 'Aws\\BedrockAgent\\Exception\\BedrockAgentException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockAgent/Exception/BedrockAgentException.php', + 'Aws\\BedrockDataAutomationRuntime\\BedrockDataAutomationRuntimeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockDataAutomationRuntime/BedrockDataAutomationRuntimeClient.php', + 'Aws\\BedrockDataAutomationRuntime\\Exception\\BedrockDataAutomationRuntimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockDataAutomationRuntime/Exception/BedrockDataAutomationRuntimeException.php', + 'Aws\\BedrockDataAutomation\\BedrockDataAutomationClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockDataAutomation/BedrockDataAutomationClient.php', + 'Aws\\BedrockDataAutomation\\Exception\\BedrockDataAutomationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockDataAutomation/Exception/BedrockDataAutomationException.php', + 'Aws\\BedrockRuntime\\BedrockRuntimeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockRuntime/BedrockRuntimeClient.php', + 'Aws\\BedrockRuntime\\Exception\\BedrockRuntimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BedrockRuntime/Exception/BedrockRuntimeException.php', + 'Aws\\Bedrock\\BedrockClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Bedrock/BedrockClient.php', + 'Aws\\Bedrock\\Exception\\BedrockException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Bedrock/Exception/BedrockException.php', + 'Aws\\BillingConductor\\BillingConductorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BillingConductor/BillingConductorClient.php', + 'Aws\\BillingConductor\\Exception\\BillingConductorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/BillingConductor/Exception/BillingConductorException.php', + 'Aws\\Billing\\BillingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Billing/BillingClient.php', + 'Aws\\Billing\\Exception\\BillingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Billing/Exception/BillingException.php', + 'Aws\\Braket\\BraketClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Braket/BraketClient.php', + 'Aws\\Braket\\Exception\\BraketException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Braket/Exception/BraketException.php', + 'Aws\\Budgets\\BudgetsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Budgets/BudgetsClient.php', + 'Aws\\Budgets\\Exception\\BudgetsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Budgets/Exception/BudgetsException.php', + 'Aws\\CacheInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CacheInterface.php', + 'Aws\\Cbor\\CborDecoder' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Cbor/CborDecoder.php', + 'Aws\\Cbor\\CborEncoder' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Cbor/CborEncoder.php', + 'Aws\\Cbor\\Exception\\CborException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Cbor/Exception/CborException.php', + 'Aws\\Chatbot\\ChatbotClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Chatbot/ChatbotClient.php', + 'Aws\\Chatbot\\Exception\\ChatbotException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Chatbot/Exception/ChatbotException.php', + 'Aws\\ChimeSDKIdentity\\ChimeSDKIdentityClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ChimeSDKIdentity/ChimeSDKIdentityClient.php', + 'Aws\\ChimeSDKIdentity\\Exception\\ChimeSDKIdentityException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ChimeSDKIdentity/Exception/ChimeSDKIdentityException.php', + 'Aws\\ChimeSDKMediaPipelines\\ChimeSDKMediaPipelinesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ChimeSDKMediaPipelines/ChimeSDKMediaPipelinesClient.php', + 'Aws\\ChimeSDKMediaPipelines\\Exception\\ChimeSDKMediaPipelinesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ChimeSDKMediaPipelines/Exception/ChimeSDKMediaPipelinesException.php', + 'Aws\\ChimeSDKMeetings\\ChimeSDKMeetingsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ChimeSDKMeetings/ChimeSDKMeetingsClient.php', + 'Aws\\ChimeSDKMeetings\\Exception\\ChimeSDKMeetingsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ChimeSDKMeetings/Exception/ChimeSDKMeetingsException.php', + 'Aws\\ChimeSDKMessaging\\ChimeSDKMessagingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ChimeSDKMessaging/ChimeSDKMessagingClient.php', + 'Aws\\ChimeSDKMessaging\\Exception\\ChimeSDKMessagingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ChimeSDKMessaging/Exception/ChimeSDKMessagingException.php', + 'Aws\\ChimeSDKVoice\\ChimeSDKVoiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ChimeSDKVoice/ChimeSDKVoiceClient.php', + 'Aws\\ChimeSDKVoice\\Exception\\ChimeSDKVoiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ChimeSDKVoice/Exception/ChimeSDKVoiceException.php', + 'Aws\\Chime\\ChimeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Chime/ChimeClient.php', + 'Aws\\Chime\\Exception\\ChimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Chime/Exception/ChimeException.php', + 'Aws\\CleanRoomsML\\CleanRoomsMLClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CleanRoomsML/CleanRoomsMLClient.php', + 'Aws\\CleanRoomsML\\Exception\\CleanRoomsMLException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CleanRoomsML/Exception/CleanRoomsMLException.php', + 'Aws\\CleanRooms\\CleanRoomsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CleanRooms/CleanRoomsClient.php', + 'Aws\\CleanRooms\\Exception\\CleanRoomsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CleanRooms/Exception/CleanRoomsException.php', + 'Aws\\ClientResolver' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ClientResolver.php', + 'Aws\\ClientSideMonitoring\\AbstractMonitoringMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ClientSideMonitoring/AbstractMonitoringMiddleware.php', + 'Aws\\ClientSideMonitoring\\ApiCallAttemptMonitoringMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ClientSideMonitoring/ApiCallAttemptMonitoringMiddleware.php', + 'Aws\\ClientSideMonitoring\\ApiCallMonitoringMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ClientSideMonitoring/ApiCallMonitoringMiddleware.php', + 'Aws\\ClientSideMonitoring\\Configuration' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ClientSideMonitoring/Configuration.php', + 'Aws\\ClientSideMonitoring\\ConfigurationInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ClientSideMonitoring/ConfigurationInterface.php', + 'Aws\\ClientSideMonitoring\\ConfigurationProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ClientSideMonitoring/ConfigurationProvider.php', + 'Aws\\ClientSideMonitoring\\Exception\\ConfigurationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ClientSideMonitoring/Exception/ConfigurationException.php', + 'Aws\\ClientSideMonitoring\\MonitoringMiddlewareInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ClientSideMonitoring/MonitoringMiddlewareInterface.php', + 'Aws\\Cloud9\\Cloud9Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Cloud9/Cloud9Client.php', + 'Aws\\Cloud9\\Exception\\Cloud9Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Cloud9/Exception/Cloud9Exception.php', + 'Aws\\CloudControlApi\\CloudControlApiClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudControlApi/CloudControlApiClient.php', + 'Aws\\CloudControlApi\\Exception\\CloudControlApiException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudControlApi/Exception/CloudControlApiException.php', + 'Aws\\CloudDirectory\\CloudDirectoryClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudDirectory/CloudDirectoryClient.php', + 'Aws\\CloudDirectory\\Exception\\CloudDirectoryException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudDirectory/Exception/CloudDirectoryException.php', + 'Aws\\CloudFormation\\CloudFormationClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudFormation/CloudFormationClient.php', + 'Aws\\CloudFormation\\Exception\\CloudFormationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudFormation/Exception/CloudFormationException.php', + 'Aws\\CloudFrontKeyValueStore\\CloudFrontKeyValueStoreClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudFrontKeyValueStore/CloudFrontKeyValueStoreClient.php', + 'Aws\\CloudFrontKeyValueStore\\Exception\\CloudFrontKeyValueStoreException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudFrontKeyValueStore/Exception/CloudFrontKeyValueStoreException.php', + 'Aws\\CloudFront\\CloudFrontClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudFront/CloudFrontClient.php', + 'Aws\\CloudFront\\CookieSigner' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudFront/CookieSigner.php', + 'Aws\\CloudFront\\Exception\\CloudFrontException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudFront/Exception/CloudFrontException.php', + 'Aws\\CloudFront\\Signer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudFront/Signer.php', + 'Aws\\CloudFront\\UrlSigner' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudFront/UrlSigner.php', + 'Aws\\CloudHSMV2\\CloudHSMV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudHSMV2/CloudHSMV2Client.php', + 'Aws\\CloudHSMV2\\Exception\\CloudHSMV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudHSMV2/Exception/CloudHSMV2Exception.php', + 'Aws\\CloudHsm\\CloudHsmClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudHsm/CloudHsmClient.php', + 'Aws\\CloudHsm\\Exception\\CloudHsmException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudHsm/Exception/CloudHsmException.php', + 'Aws\\CloudSearchDomain\\CloudSearchDomainClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudSearchDomain/CloudSearchDomainClient.php', + 'Aws\\CloudSearchDomain\\Exception\\CloudSearchDomainException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudSearchDomain/Exception/CloudSearchDomainException.php', + 'Aws\\CloudSearch\\CloudSearchClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudSearch/CloudSearchClient.php', + 'Aws\\CloudSearch\\Exception\\CloudSearchException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudSearch/Exception/CloudSearchException.php', + 'Aws\\CloudTrailData\\CloudTrailDataClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudTrailData/CloudTrailDataClient.php', + 'Aws\\CloudTrailData\\Exception\\CloudTrailDataException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudTrailData/Exception/CloudTrailDataException.php', + 'Aws\\CloudTrail\\CloudTrailClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudTrail/CloudTrailClient.php', + 'Aws\\CloudTrail\\Exception\\CloudTrailException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudTrail/Exception/CloudTrailException.php', + 'Aws\\CloudTrail\\LogFileIterator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudTrail/LogFileIterator.php', + 'Aws\\CloudTrail\\LogFileReader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudTrail/LogFileReader.php', + 'Aws\\CloudTrail\\LogRecordIterator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudTrail/LogRecordIterator.php', + 'Aws\\CloudWatchEvents\\CloudWatchEventsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudWatchEvents/CloudWatchEventsClient.php', + 'Aws\\CloudWatchEvents\\Exception\\CloudWatchEventsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudWatchEvents/Exception/CloudWatchEventsException.php', + 'Aws\\CloudWatchLogs\\CloudWatchLogsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudWatchLogs/CloudWatchLogsClient.php', + 'Aws\\CloudWatchLogs\\Exception\\CloudWatchLogsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudWatchLogs/Exception/CloudWatchLogsException.php', + 'Aws\\CloudWatchRUM\\CloudWatchRUMClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudWatchRUM/CloudWatchRUMClient.php', + 'Aws\\CloudWatchRUM\\Exception\\CloudWatchRUMException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudWatchRUM/Exception/CloudWatchRUMException.php', + 'Aws\\CloudWatch\\CloudWatchClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudWatch/CloudWatchClient.php', + 'Aws\\CloudWatch\\Exception\\CloudWatchException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CloudWatch/Exception/CloudWatchException.php', + 'Aws\\CodeArtifact\\CodeArtifactClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeArtifact/CodeArtifactClient.php', + 'Aws\\CodeArtifact\\Exception\\CodeArtifactException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeArtifact/Exception/CodeArtifactException.php', + 'Aws\\CodeBuild\\CodeBuildClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeBuild/CodeBuildClient.php', + 'Aws\\CodeBuild\\Exception\\CodeBuildException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeBuild/Exception/CodeBuildException.php', + 'Aws\\CodeCatalyst\\CodeCatalystClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeCatalyst/CodeCatalystClient.php', + 'Aws\\CodeCatalyst\\Exception\\CodeCatalystException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeCatalyst/Exception/CodeCatalystException.php', + 'Aws\\CodeCommit\\CodeCommitClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeCommit/CodeCommitClient.php', + 'Aws\\CodeCommit\\Exception\\CodeCommitException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeCommit/Exception/CodeCommitException.php', + 'Aws\\CodeConnections\\CodeConnectionsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeConnections/CodeConnectionsClient.php', + 'Aws\\CodeConnections\\Exception\\CodeConnectionsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeConnections/Exception/CodeConnectionsException.php', + 'Aws\\CodeDeploy\\CodeDeployClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeDeploy/CodeDeployClient.php', + 'Aws\\CodeDeploy\\Exception\\CodeDeployException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeDeploy/Exception/CodeDeployException.php', + 'Aws\\CodeGuruProfiler\\CodeGuruProfilerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeGuruProfiler/CodeGuruProfilerClient.php', + 'Aws\\CodeGuruProfiler\\Exception\\CodeGuruProfilerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeGuruProfiler/Exception/CodeGuruProfilerException.php', + 'Aws\\CodeGuruReviewer\\CodeGuruReviewerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeGuruReviewer/CodeGuruReviewerClient.php', + 'Aws\\CodeGuruReviewer\\Exception\\CodeGuruReviewerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeGuruReviewer/Exception/CodeGuruReviewerException.php', + 'Aws\\CodeGuruSecurity\\CodeGuruSecurityClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeGuruSecurity/CodeGuruSecurityClient.php', + 'Aws\\CodeGuruSecurity\\Exception\\CodeGuruSecurityException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeGuruSecurity/Exception/CodeGuruSecurityException.php', + 'Aws\\CodePipeline\\CodePipelineClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodePipeline/CodePipelineClient.php', + 'Aws\\CodePipeline\\Exception\\CodePipelineException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodePipeline/Exception/CodePipelineException.php', + 'Aws\\CodeStarNotifications\\CodeStarNotificationsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeStarNotifications/CodeStarNotificationsClient.php', + 'Aws\\CodeStarNotifications\\Exception\\CodeStarNotificationsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeStarNotifications/Exception/CodeStarNotificationsException.php', + 'Aws\\CodeStarconnections\\CodeStarconnectionsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeStarconnections/CodeStarconnectionsClient.php', + 'Aws\\CodeStarconnections\\Exception\\CodeStarconnectionsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CodeStarconnections/Exception/CodeStarconnectionsException.php', + 'Aws\\CognitoIdentityProvider\\CognitoIdentityProviderClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CognitoIdentityProvider/CognitoIdentityProviderClient.php', + 'Aws\\CognitoIdentityProvider\\Exception\\CognitoIdentityProviderException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CognitoIdentityProvider/Exception/CognitoIdentityProviderException.php', + 'Aws\\CognitoIdentity\\CognitoIdentityClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CognitoIdentity/CognitoIdentityClient.php', + 'Aws\\CognitoIdentity\\CognitoIdentityProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CognitoIdentity/CognitoIdentityProvider.php', + 'Aws\\CognitoIdentity\\Exception\\CognitoIdentityException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CognitoIdentity/Exception/CognitoIdentityException.php', + 'Aws\\CognitoSync\\CognitoSyncClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CognitoSync/CognitoSyncClient.php', + 'Aws\\CognitoSync\\Exception\\CognitoSyncException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CognitoSync/Exception/CognitoSyncException.php', + 'Aws\\Command' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Command.php', + 'Aws\\CommandInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CommandInterface.php', + 'Aws\\CommandPool' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CommandPool.php', + 'Aws\\ComprehendMedical\\ComprehendMedicalClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ComprehendMedical/ComprehendMedicalClient.php', + 'Aws\\ComprehendMedical\\Exception\\ComprehendMedicalException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ComprehendMedical/Exception/ComprehendMedicalException.php', + 'Aws\\Comprehend\\ComprehendClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Comprehend/ComprehendClient.php', + 'Aws\\Comprehend\\Exception\\ComprehendException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Comprehend/Exception/ComprehendException.php', + 'Aws\\ComputeOptimizerAutomation\\ComputeOptimizerAutomationClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ComputeOptimizerAutomation/ComputeOptimizerAutomationClient.php', + 'Aws\\ComputeOptimizerAutomation\\Exception\\ComputeOptimizerAutomationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ComputeOptimizerAutomation/Exception/ComputeOptimizerAutomationException.php', + 'Aws\\ComputeOptimizer\\ComputeOptimizerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ComputeOptimizer/ComputeOptimizerClient.php', + 'Aws\\ComputeOptimizer\\Exception\\ComputeOptimizerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ComputeOptimizer/Exception/ComputeOptimizerException.php', + 'Aws\\ConfigService\\ConfigServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConfigService/ConfigServiceClient.php', + 'Aws\\ConfigService\\Exception\\ConfigServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConfigService/Exception/ConfigServiceException.php', + 'Aws\\ConfigurationProviderInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConfigurationProviderInterface.php', + 'Aws\\Configuration\\ConfigurationResolver' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Configuration/ConfigurationResolver.php', + 'Aws\\ConnectCampaignService\\ConnectCampaignServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectCampaignService/ConnectCampaignServiceClient.php', + 'Aws\\ConnectCampaignService\\Exception\\ConnectCampaignServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectCampaignService/Exception/ConnectCampaignServiceException.php', + 'Aws\\ConnectCampaignsV2\\ConnectCampaignsV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectCampaignsV2/ConnectCampaignsV2Client.php', + 'Aws\\ConnectCampaignsV2\\Exception\\ConnectCampaignsV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectCampaignsV2/Exception/ConnectCampaignsV2Exception.php', + 'Aws\\ConnectCases\\ConnectCasesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectCases/ConnectCasesClient.php', + 'Aws\\ConnectCases\\Exception\\ConnectCasesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectCases/Exception/ConnectCasesException.php', + 'Aws\\ConnectContactLens\\ConnectContactLensClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectContactLens/ConnectContactLensClient.php', + 'Aws\\ConnectContactLens\\Exception\\ConnectContactLensException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectContactLens/Exception/ConnectContactLensException.php', + 'Aws\\ConnectHealth\\ConnectHealthClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectHealth/ConnectHealthClient.php', + 'Aws\\ConnectHealth\\Exception\\ConnectHealthException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectHealth/Exception/ConnectHealthException.php', + 'Aws\\ConnectParticipant\\ConnectParticipantClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectParticipant/ConnectParticipantClient.php', + 'Aws\\ConnectParticipant\\Exception\\ConnectParticipantException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectParticipant/Exception/ConnectParticipantException.php', + 'Aws\\ConnectWisdomService\\ConnectWisdomServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectWisdomService/ConnectWisdomServiceClient.php', + 'Aws\\ConnectWisdomService\\Exception\\ConnectWisdomServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ConnectWisdomService/Exception/ConnectWisdomServiceException.php', + 'Aws\\Connect\\ConnectClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Connect/ConnectClient.php', + 'Aws\\Connect\\Exception\\ConnectException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Connect/Exception/ConnectException.php', + 'Aws\\ControlCatalog\\ControlCatalogClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ControlCatalog/ControlCatalogClient.php', + 'Aws\\ControlCatalog\\Exception\\ControlCatalogException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ControlCatalog/Exception/ControlCatalogException.php', + 'Aws\\ControlTower\\ControlTowerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ControlTower/ControlTowerClient.php', + 'Aws\\ControlTower\\Exception\\ControlTowerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ControlTower/Exception/ControlTowerException.php', + 'Aws\\CostExplorer\\CostExplorerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CostExplorer/CostExplorerClient.php', + 'Aws\\CostExplorer\\Exception\\CostExplorerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CostExplorer/Exception/CostExplorerException.php', + 'Aws\\CostOptimizationHub\\CostOptimizationHubClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CostOptimizationHub/CostOptimizationHubClient.php', + 'Aws\\CostOptimizationHub\\Exception\\CostOptimizationHubException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CostOptimizationHub/Exception/CostOptimizationHubException.php', + 'Aws\\CostandUsageReportService\\CostandUsageReportServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CostandUsageReportService/CostandUsageReportServiceClient.php', + 'Aws\\CostandUsageReportService\\Exception\\CostandUsageReportServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CostandUsageReportService/Exception/CostandUsageReportServiceException.php', + 'Aws\\Credentials\\AssumeRoleCredentialProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Credentials/AssumeRoleCredentialProvider.php', + 'Aws\\Credentials\\AssumeRoleWithWebIdentityCredentialProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Credentials/AssumeRoleWithWebIdentityCredentialProvider.php', + 'Aws\\Credentials\\CredentialProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Credentials/CredentialProvider.php', + 'Aws\\Credentials\\CredentialSources' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Credentials/CredentialSources.php', + 'Aws\\Credentials\\Credentials' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Credentials/Credentials.php', + 'Aws\\Credentials\\CredentialsInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Credentials/CredentialsInterface.php', + 'Aws\\Credentials\\CredentialsUtils' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Credentials/CredentialsUtils.php', + 'Aws\\Credentials\\EcsCredentialProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Credentials/EcsCredentialProvider.php', + 'Aws\\Credentials\\InstanceProfileProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Credentials/InstanceProfileProvider.php', + 'Aws\\Credentials\\LoginCredentialProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Credentials/LoginCredentialProvider.php', + 'Aws\\Crypto\\AbstractCryptoClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AbstractCryptoClient.php', + 'Aws\\Crypto\\AbstractCryptoClientV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AbstractCryptoClientV2.php', + 'Aws\\Crypto\\AbstractCryptoClientV3' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AbstractCryptoClientV3.php', + 'Aws\\Crypto\\AesDecryptingStream' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AesDecryptingStream.php', + 'Aws\\Crypto\\AesEncryptingStream' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AesEncryptingStream.php', + 'Aws\\Crypto\\AesGcmDecryptingStream' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AesGcmDecryptingStream.php', + 'Aws\\Crypto\\AesGcmEncryptingStream' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AesGcmEncryptingStream.php', + 'Aws\\Crypto\\AesStreamInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AesStreamInterface.php', + 'Aws\\Crypto\\AesStreamInterfaceV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AesStreamInterfaceV2.php', + 'Aws\\Crypto\\AlgorithmConstants' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AlgorithmConstants.php', + 'Aws\\Crypto\\AlgorithmSuite' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/AlgorithmSuite.php', + 'Aws\\Crypto\\Cipher\\Cbc' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/Cipher/Cbc.php', + 'Aws\\Crypto\\Cipher\\CipherBuilderTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/Cipher/CipherBuilderTrait.php', + 'Aws\\Crypto\\Cipher\\CipherMethod' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/Cipher/CipherMethod.php', + 'Aws\\Crypto\\DecryptionTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/DecryptionTrait.php', + 'Aws\\Crypto\\DecryptionTraitV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/DecryptionTraitV2.php', + 'Aws\\Crypto\\DecryptionTraitV3' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/DecryptionTraitV3.php', + 'Aws\\Crypto\\EncryptionTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/EncryptionTrait.php', + 'Aws\\Crypto\\EncryptionTraitV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/EncryptionTraitV2.php', + 'Aws\\Crypto\\EncryptionTraitV3' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/EncryptionTraitV3.php', + 'Aws\\Crypto\\KmsMaterialsProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/KmsMaterialsProvider.php', + 'Aws\\Crypto\\KmsMaterialsProviderV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/KmsMaterialsProviderV2.php', + 'Aws\\Crypto\\KmsMaterialsProviderV3' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/KmsMaterialsProviderV3.php', + 'Aws\\Crypto\\MaterialsProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/MaterialsProvider.php', + 'Aws\\Crypto\\MaterialsProviderInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterface.php', + 'Aws\\Crypto\\MaterialsProviderInterfaceV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterfaceV2.php', + 'Aws\\Crypto\\MaterialsProviderInterfaceV3' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/MaterialsProviderInterfaceV3.php', + 'Aws\\Crypto\\MaterialsProviderV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/MaterialsProviderV2.php', + 'Aws\\Crypto\\MaterialsProviderV3' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/MaterialsProviderV3.php', + 'Aws\\Crypto\\MetadataEnvelope' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/MetadataEnvelope.php', + 'Aws\\Crypto\\MetadataStrategyInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Crypto/MetadataStrategyInterface.php', + 'Aws\\CustomerProfiles\\CustomerProfilesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CustomerProfiles/CustomerProfilesClient.php', + 'Aws\\CustomerProfiles\\Exception\\CustomerProfilesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/CustomerProfiles/Exception/CustomerProfilesException.php', + 'Aws\\DAX\\DAXClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DAX/DAXClient.php', + 'Aws\\DAX\\Exception\\DAXException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DAX/Exception/DAXException.php', + 'Aws\\DLM\\DLMClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DLM/DLMClient.php', + 'Aws\\DLM\\Exception\\DLMException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DLM/Exception/DLMException.php', + 'Aws\\DSQL\\AuthTokenGenerator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DSQL/AuthTokenGenerator.php', + 'Aws\\DSQL\\DSQLClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DSQL/DSQLClient.php', + 'Aws\\DSQL\\Exception\\DSQLException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DSQL/Exception/DSQLException.php', + 'Aws\\DataExchange\\DataExchangeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DataExchange/DataExchangeClient.php', + 'Aws\\DataExchange\\Exception\\DataExchangeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DataExchange/Exception/DataExchangeException.php', + 'Aws\\DataPipeline\\DataPipelineClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DataPipeline/DataPipelineClient.php', + 'Aws\\DataPipeline\\Exception\\DataPipelineException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DataPipeline/Exception/DataPipelineException.php', + 'Aws\\DataSync\\DataSyncClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DataSync/DataSyncClient.php', + 'Aws\\DataSync\\Exception\\DataSyncException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DataSync/Exception/DataSyncException.php', + 'Aws\\DataZone\\DataZoneClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DataZone/DataZoneClient.php', + 'Aws\\DataZone\\Exception\\DataZoneException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DataZone/Exception/DataZoneException.php', + 'Aws\\DatabaseMigrationService\\DatabaseMigrationServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DatabaseMigrationService/DatabaseMigrationServiceClient.php', + 'Aws\\DatabaseMigrationService\\Exception\\DatabaseMigrationServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DatabaseMigrationService/Exception/DatabaseMigrationServiceException.php', + 'Aws\\Deadline\\DeadlineClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Deadline/DeadlineClient.php', + 'Aws\\Deadline\\Exception\\DeadlineException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Deadline/Exception/DeadlineException.php', + 'Aws\\DefaultsMode\\Configuration' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DefaultsMode/Configuration.php', + 'Aws\\DefaultsMode\\ConfigurationInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DefaultsMode/ConfigurationInterface.php', + 'Aws\\DefaultsMode\\ConfigurationProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DefaultsMode/ConfigurationProvider.php', + 'Aws\\DefaultsMode\\Exception\\ConfigurationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DefaultsMode/Exception/ConfigurationException.php', + 'Aws\\Detective\\DetectiveClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Detective/DetectiveClient.php', + 'Aws\\Detective\\Exception\\DetectiveException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Detective/Exception/DetectiveException.php', + 'Aws\\DevOpsAgent\\DevOpsAgentClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DevOpsAgent/DevOpsAgentClient.php', + 'Aws\\DevOpsAgent\\Exception\\DevOpsAgentException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DevOpsAgent/Exception/DevOpsAgentException.php', + 'Aws\\DevOpsGuru\\DevOpsGuruClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DevOpsGuru/DevOpsGuruClient.php', + 'Aws\\DevOpsGuru\\Exception\\DevOpsGuruException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DevOpsGuru/Exception/DevOpsGuruException.php', + 'Aws\\DeviceFarm\\DeviceFarmClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DeviceFarm/DeviceFarmClient.php', + 'Aws\\DeviceFarm\\Exception\\DeviceFarmException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DeviceFarm/Exception/DeviceFarmException.php', + 'Aws\\DirectConnect\\DirectConnectClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DirectConnect/DirectConnectClient.php', + 'Aws\\DirectConnect\\Exception\\DirectConnectException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DirectConnect/Exception/DirectConnectException.php', + 'Aws\\DirectoryServiceData\\DirectoryServiceDataClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DirectoryServiceData/DirectoryServiceDataClient.php', + 'Aws\\DirectoryServiceData\\Exception\\DirectoryServiceDataException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DirectoryServiceData/Exception/DirectoryServiceDataException.php', + 'Aws\\DirectoryService\\DirectoryServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DirectoryService/DirectoryServiceClient.php', + 'Aws\\DirectoryService\\Exception\\DirectoryServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DirectoryService/Exception/DirectoryServiceException.php', + 'Aws\\DocDBElastic\\DocDBElasticClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DocDBElastic/DocDBElasticClient.php', + 'Aws\\DocDBElastic\\Exception\\DocDBElasticException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DocDBElastic/Exception/DocDBElasticException.php', + 'Aws\\DocDB\\DocDBClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DocDB/DocDBClient.php', + 'Aws\\DocDB\\Exception\\DocDBException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DocDB/Exception/DocDBException.php', + 'Aws\\DoctrineCacheAdapter' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DoctrineCacheAdapter.php', + 'Aws\\DynamoDbStreams\\DynamoDbStreamsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDbStreams/DynamoDbStreamsClient.php', + 'Aws\\DynamoDbStreams\\Exception\\DynamoDbStreamsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDbStreams/Exception/DynamoDbStreamsException.php', + 'Aws\\DynamoDb\\BinaryValue' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/BinaryValue.php', + 'Aws\\DynamoDb\\DynamoDbClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/DynamoDbClient.php', + 'Aws\\DynamoDb\\Exception\\DynamoDbException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/Exception/DynamoDbException.php', + 'Aws\\DynamoDb\\LockingSessionConnection' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/LockingSessionConnection.php', + 'Aws\\DynamoDb\\Marshaler' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/Marshaler.php', + 'Aws\\DynamoDb\\NumberValue' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/NumberValue.php', + 'Aws\\DynamoDb\\SessionConnectionConfigTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/SessionConnectionConfigTrait.php', + 'Aws\\DynamoDb\\SessionConnectionInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/SessionConnectionInterface.php', + 'Aws\\DynamoDb\\SessionHandler' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/SessionHandler.php', + 'Aws\\DynamoDb\\SetValue' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/SetValue.php', + 'Aws\\DynamoDb\\StandardSessionConnection' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/StandardSessionConnection.php', + 'Aws\\DynamoDb\\WriteRequestBatch' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/DynamoDb/WriteRequestBatch.php', + 'Aws\\EBS\\EBSClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EBS/EBSClient.php', + 'Aws\\EBS\\Exception\\EBSException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EBS/Exception/EBSException.php', + 'Aws\\EC2InstanceConnect\\EC2InstanceConnectClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EC2InstanceConnect/EC2InstanceConnectClient.php', + 'Aws\\EC2InstanceConnect\\Exception\\EC2InstanceConnectException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EC2InstanceConnect/Exception/EC2InstanceConnectException.php', + 'Aws\\ECRPublic\\ECRPublicClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ECRPublic/ECRPublicClient.php', + 'Aws\\ECRPublic\\Exception\\ECRPublicException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ECRPublic/Exception/ECRPublicException.php', + 'Aws\\EKSAuth\\EKSAuthClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EKSAuth/EKSAuthClient.php', + 'Aws\\EKSAuth\\Exception\\EKSAuthException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EKSAuth/Exception/EKSAuthException.php', + 'Aws\\EKS\\EKSClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EKS/EKSClient.php', + 'Aws\\EKS\\Exception\\EKSException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EKS/Exception/EKSException.php', + 'Aws\\EMRContainers\\EMRContainersClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EMRContainers/EMRContainersClient.php', + 'Aws\\EMRContainers\\Exception\\EMRContainersException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EMRContainers/Exception/EMRContainersException.php', + 'Aws\\EMRServerless\\EMRServerlessClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EMRServerless/EMRServerlessClient.php', + 'Aws\\EMRServerless\\Exception\\EMRServerlessException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EMRServerless/Exception/EMRServerlessException.php', + 'Aws\\Ec2\\Ec2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Ec2/Ec2Client.php', + 'Aws\\Ec2\\Exception\\Ec2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Ec2/Exception/Ec2Exception.php', + 'Aws\\Ecr\\EcrClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Ecr/EcrClient.php', + 'Aws\\Ecr\\Exception\\EcrException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Ecr/Exception/EcrException.php', + 'Aws\\Ecs\\EcsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Ecs/EcsClient.php', + 'Aws\\Ecs\\Exception\\EcsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Ecs/Exception/EcsException.php', + 'Aws\\Efs\\EfsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Efs/EfsClient.php', + 'Aws\\Efs\\Exception\\EfsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Efs/Exception/EfsException.php', + 'Aws\\ElastiCache\\ElastiCacheClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElastiCache/ElastiCacheClient.php', + 'Aws\\ElastiCache\\Exception\\ElastiCacheException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElastiCache/Exception/ElastiCacheException.php', + 'Aws\\ElasticBeanstalk\\ElasticBeanstalkClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElasticBeanstalk/ElasticBeanstalkClient.php', + 'Aws\\ElasticBeanstalk\\Exception\\ElasticBeanstalkException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElasticBeanstalk/Exception/ElasticBeanstalkException.php', + 'Aws\\ElasticLoadBalancingV2\\ElasticLoadBalancingV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElasticLoadBalancingV2/ElasticLoadBalancingV2Client.php', + 'Aws\\ElasticLoadBalancingV2\\Exception\\ElasticLoadBalancingV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElasticLoadBalancingV2/Exception/ElasticLoadBalancingV2Exception.php', + 'Aws\\ElasticLoadBalancing\\ElasticLoadBalancingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElasticLoadBalancing/ElasticLoadBalancingClient.php', + 'Aws\\ElasticLoadBalancing\\Exception\\ElasticLoadBalancingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElasticLoadBalancing/Exception/ElasticLoadBalancingException.php', + 'Aws\\ElasticsearchService\\ElasticsearchServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElasticsearchService/ElasticsearchServiceClient.php', + 'Aws\\ElasticsearchService\\Exception\\ElasticsearchServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElasticsearchService/Exception/ElasticsearchServiceException.php', + 'Aws\\ElementalInference\\ElementalInferenceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElementalInference/ElementalInferenceClient.php', + 'Aws\\ElementalInference\\Exception\\ElementalInferenceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ElementalInference/Exception/ElementalInferenceException.php', + 'Aws\\Emr\\EmrClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Emr/EmrClient.php', + 'Aws\\Emr\\Exception\\EmrException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Emr/Exception/EmrException.php', + 'Aws\\EndpointDiscovery\\Configuration' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointDiscovery/Configuration.php', + 'Aws\\EndpointDiscovery\\ConfigurationInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointDiscovery/ConfigurationInterface.php', + 'Aws\\EndpointDiscovery\\ConfigurationProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointDiscovery/ConfigurationProvider.php', + 'Aws\\EndpointDiscovery\\EndpointDiscoveryMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointDiscovery/EndpointDiscoveryMiddleware.php', + 'Aws\\EndpointDiscovery\\EndpointList' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointDiscovery/EndpointList.php', + 'Aws\\EndpointDiscovery\\Exception\\ConfigurationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointDiscovery/Exception/ConfigurationException.php', + 'Aws\\EndpointParameterMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointParameterMiddleware.php', + 'Aws\\EndpointV2\\Bdd\\BddEvaluator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Bdd/BddEvaluator.php', + 'Aws\\EndpointV2\\Bdd\\BddNodeDecoder' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Bdd/BddNodeDecoder.php', + 'Aws\\EndpointV2\\Bdd\\BddResultResolver' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Bdd/BddResultResolver.php', + 'Aws\\EndpointV2\\Bdd\\BddRuleset' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Bdd/BddRuleset.php', + 'Aws\\EndpointV2\\EndpointDefinitionProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/EndpointDefinitionProvider.php', + 'Aws\\EndpointV2\\EndpointProviderV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/EndpointProviderV2.php', + 'Aws\\EndpointV2\\EndpointV2Middleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/EndpointV2Middleware.php', + 'Aws\\EndpointV2\\EndpointV2SerializerTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/EndpointV2SerializerTrait.php', + 'Aws\\EndpointV2\\Rule\\AbstractRule' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Rule/AbstractRule.php', + 'Aws\\EndpointV2\\Rule\\EndpointRule' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Rule/EndpointRule.php', + 'Aws\\EndpointV2\\Rule\\ErrorRule' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Rule/ErrorRule.php', + 'Aws\\EndpointV2\\Rule\\RuleCreator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Rule/RuleCreator.php', + 'Aws\\EndpointV2\\Rule\\TreeRule' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Rule/TreeRule.php', + 'Aws\\EndpointV2\\Ruleset\\Ruleset' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Ruleset/Ruleset.php', + 'Aws\\EndpointV2\\Ruleset\\RulesetEndpoint' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Ruleset/RulesetEndpoint.php', + 'Aws\\EndpointV2\\Ruleset\\RulesetParameter' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Ruleset/RulesetParameter.php', + 'Aws\\EndpointV2\\Ruleset\\RulesetStandardLibrary' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EndpointV2/Ruleset/RulesetStandardLibrary.php', + 'Aws\\Endpoint\\EndpointProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/EndpointProvider.php', + 'Aws\\Endpoint\\Partition' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/Partition.php', + 'Aws\\Endpoint\\PartitionEndpointProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/PartitionEndpointProvider.php', + 'Aws\\Endpoint\\PartitionInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/PartitionInterface.php', + 'Aws\\Endpoint\\PatternEndpointProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/PatternEndpointProvider.php', + 'Aws\\Endpoint\\UseDualstackEndpoint\\Configuration' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/UseDualstackEndpoint/Configuration.php', + 'Aws\\Endpoint\\UseDualstackEndpoint\\ConfigurationInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/UseDualstackEndpoint/ConfigurationInterface.php', + 'Aws\\Endpoint\\UseDualstackEndpoint\\ConfigurationProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/UseDualstackEndpoint/ConfigurationProvider.php', + 'Aws\\Endpoint\\UseDualstackEndpoint\\Exception\\ConfigurationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/UseDualstackEndpoint/Exception/ConfigurationException.php', + 'Aws\\Endpoint\\UseFipsEndpoint\\Configuration' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/UseFipsEndpoint/Configuration.php', + 'Aws\\Endpoint\\UseFipsEndpoint\\ConfigurationInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/UseFipsEndpoint/ConfigurationInterface.php', + 'Aws\\Endpoint\\UseFipsEndpoint\\ConfigurationProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/UseFipsEndpoint/ConfigurationProvider.php', + 'Aws\\Endpoint\\UseFipsEndpoint\\Exception\\ConfigurationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Endpoint/UseFipsEndpoint/Exception/ConfigurationException.php', + 'Aws\\EntityResolution\\EntityResolutionClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EntityResolution/EntityResolutionClient.php', + 'Aws\\EntityResolution\\Exception\\EntityResolutionException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EntityResolution/Exception/EntityResolutionException.php', + 'Aws\\EventBridge\\EventBridgeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EventBridge/EventBridgeClient.php', + 'Aws\\EventBridge\\EventBridgeEndpointMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EventBridge/EventBridgeEndpointMiddleware.php', + 'Aws\\EventBridge\\Exception\\EventBridgeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/EventBridge/Exception/EventBridgeException.php', + 'Aws\\Evs\\EvsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Evs/EvsClient.php', + 'Aws\\Evs\\Exception\\EvsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Evs/Exception/EvsException.php', + 'Aws\\Exception\\AwsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/AwsException.php', + 'Aws\\Exception\\CommonRuntimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/CommonRuntimeException.php', + 'Aws\\Exception\\CouldNotCreateChecksumException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/CouldNotCreateChecksumException.php', + 'Aws\\Exception\\CredentialsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/CredentialsException.php', + 'Aws\\Exception\\CryptoException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/CryptoException.php', + 'Aws\\Exception\\CryptoPolyfillException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/CryptoPolyfillException.php', + 'Aws\\Exception\\EventStreamDataException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/EventStreamDataException.php', + 'Aws\\Exception\\IncalculablePayloadException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/IncalculablePayloadException.php', + 'Aws\\Exception\\InvalidJsonException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/InvalidJsonException.php', + 'Aws\\Exception\\InvalidRegionException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/InvalidRegionException.php', + 'Aws\\Exception\\MultipartUploadException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/MultipartUploadException.php', + 'Aws\\Exception\\TokenException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/TokenException.php', + 'Aws\\Exception\\UnresolvedApiException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/UnresolvedApiException.php', + 'Aws\\Exception\\UnresolvedEndpointException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/UnresolvedEndpointException.php', + 'Aws\\Exception\\UnresolvedSignatureException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Exception/UnresolvedSignatureException.php', + 'Aws\\FIS\\Exception\\FISException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FIS/Exception/FISException.php', + 'Aws\\FIS\\FISClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FIS/FISClient.php', + 'Aws\\FMS\\Exception\\FMSException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FMS/Exception/FMSException.php', + 'Aws\\FMS\\FMSClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FMS/FMSClient.php', + 'Aws\\FSx\\Exception\\FSxException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FSx/Exception/FSxException.php', + 'Aws\\FSx\\FSxClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FSx/FSxClient.php', + 'Aws\\FinSpaceData\\Exception\\FinSpaceDataException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FinSpaceData/Exception/FinSpaceDataException.php', + 'Aws\\FinSpaceData\\FinSpaceDataClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FinSpaceData/FinSpaceDataClient.php', + 'Aws\\Firehose\\Exception\\FirehoseException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Firehose/Exception/FirehoseException.php', + 'Aws\\Firehose\\FirehoseClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Firehose/FirehoseClient.php', + 'Aws\\ForecastQueryService\\Exception\\ForecastQueryServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ForecastQueryService/Exception/ForecastQueryServiceException.php', + 'Aws\\ForecastQueryService\\ForecastQueryServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ForecastQueryService/ForecastQueryServiceClient.php', + 'Aws\\ForecastService\\Exception\\ForecastServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ForecastService/Exception/ForecastServiceException.php', + 'Aws\\ForecastService\\ForecastServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ForecastService/ForecastServiceClient.php', + 'Aws\\FraudDetector\\Exception\\FraudDetectorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FraudDetector/Exception/FraudDetectorException.php', + 'Aws\\FraudDetector\\FraudDetectorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FraudDetector/FraudDetectorClient.php', + 'Aws\\FreeTier\\Exception\\FreeTierException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FreeTier/Exception/FreeTierException.php', + 'Aws\\FreeTier\\FreeTierClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/FreeTier/FreeTierClient.php', + 'Aws\\GameLiftStreams\\Exception\\GameLiftStreamsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GameLiftStreams/Exception/GameLiftStreamsException.php', + 'Aws\\GameLiftStreams\\GameLiftStreamsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GameLiftStreams/GameLiftStreamsClient.php', + 'Aws\\GameLift\\Exception\\GameLiftException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GameLift/Exception/GameLiftException.php', + 'Aws\\GameLift\\GameLiftClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GameLift/GameLiftClient.php', + 'Aws\\GeoMaps\\Exception\\GeoMapsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GeoMaps/Exception/GeoMapsException.php', + 'Aws\\GeoMaps\\GeoMapsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GeoMaps/GeoMapsClient.php', + 'Aws\\GeoPlaces\\Exception\\GeoPlacesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GeoPlaces/Exception/GeoPlacesException.php', + 'Aws\\GeoPlaces\\GeoPlacesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GeoPlaces/GeoPlacesClient.php', + 'Aws\\GeoRoutes\\Exception\\GeoRoutesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GeoRoutes/Exception/GeoRoutesException.php', + 'Aws\\GeoRoutes\\GeoRoutesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GeoRoutes/GeoRoutesClient.php', + 'Aws\\Glacier\\Exception\\GlacierException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Glacier/Exception/GlacierException.php', + 'Aws\\Glacier\\GlacierClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Glacier/GlacierClient.php', + 'Aws\\Glacier\\MultipartUploader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Glacier/MultipartUploader.php', + 'Aws\\Glacier\\TreeHash' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Glacier/TreeHash.php', + 'Aws\\GlobalAccelerator\\Exception\\GlobalAcceleratorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GlobalAccelerator/Exception/GlobalAcceleratorException.php', + 'Aws\\GlobalAccelerator\\GlobalAcceleratorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GlobalAccelerator/GlobalAcceleratorClient.php', + 'Aws\\GlueDataBrew\\Exception\\GlueDataBrewException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GlueDataBrew/Exception/GlueDataBrewException.php', + 'Aws\\GlueDataBrew\\GlueDataBrewClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GlueDataBrew/GlueDataBrewClient.php', + 'Aws\\Glue\\Exception\\GlueException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Glue/Exception/GlueException.php', + 'Aws\\Glue\\GlueClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Glue/GlueClient.php', + 'Aws\\GreengrassV2\\Exception\\GreengrassV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GreengrassV2/Exception/GreengrassV2Exception.php', + 'Aws\\GreengrassV2\\GreengrassV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GreengrassV2/GreengrassV2Client.php', + 'Aws\\Greengrass\\Exception\\GreengrassException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Greengrass/Exception/GreengrassException.php', + 'Aws\\Greengrass\\GreengrassClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Greengrass/GreengrassClient.php', + 'Aws\\GroundStation\\Exception\\GroundStationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GroundStation/Exception/GroundStationException.php', + 'Aws\\GroundStation\\GroundStationClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GroundStation/GroundStationClient.php', + 'Aws\\GuardDuty\\Exception\\GuardDutyException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GuardDuty/Exception/GuardDutyException.php', + 'Aws\\GuardDuty\\GuardDutyClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/GuardDuty/GuardDutyClient.php', + 'Aws\\HandlerList' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/HandlerList.php', + 'Aws\\Handler\\Guzzle\\GuzzleHandler' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Handler/Guzzle/GuzzleHandler.php', + 'Aws\\Handler\\HttpHandlerError' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Handler/HttpHandlerError.php', + 'Aws\\HasDataTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/HasDataTrait.php', + 'Aws\\HasMonitoringEventsTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/HasMonitoringEventsTrait.php', + 'Aws\\HashInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/HashInterface.php', + 'Aws\\HashingStream' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/HashingStream.php', + 'Aws\\HealthLake\\Exception\\HealthLakeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/HealthLake/Exception/HealthLakeException.php', + 'Aws\\HealthLake\\HealthLakeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/HealthLake/HealthLakeClient.php', + 'Aws\\Health\\Exception\\HealthException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Health/Exception/HealthException.php', + 'Aws\\Health\\HealthClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Health/HealthClient.php', + 'Aws\\History' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/History.php', + 'Aws\\IVSRealTime\\Exception\\IVSRealTimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IVSRealTime/Exception/IVSRealTimeException.php', + 'Aws\\IVSRealTime\\IVSRealTimeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IVSRealTime/IVSRealTimeClient.php', + 'Aws\\IVS\\Exception\\IVSException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IVS/Exception/IVSException.php', + 'Aws\\IVS\\IVSClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IVS/IVSClient.php', + 'Aws\\Iam\\Exception\\IamException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Iam/Exception/IamException.php', + 'Aws\\Iam\\IamClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Iam/IamClient.php', + 'Aws\\IdempotencyTokenMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IdempotencyTokenMiddleware.php', + 'Aws\\IdentityStore\\Exception\\IdentityStoreException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IdentityStore/Exception/IdentityStoreException.php', + 'Aws\\IdentityStore\\IdentityStoreClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IdentityStore/IdentityStoreClient.php', + 'Aws\\Identity\\AwsCredentialIdentity' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Identity/AwsCredentialIdentity.php', + 'Aws\\Identity\\BearerTokenIdentity' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Identity/BearerTokenIdentity.php', + 'Aws\\Identity\\IdentityInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Identity/IdentityInterface.php', + 'Aws\\Identity\\S3\\S3ExpressIdentity' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Identity/S3/S3ExpressIdentity.php', + 'Aws\\Identity\\S3\\S3ExpressIdentityProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Identity/S3/S3ExpressIdentityProvider.php', + 'Aws\\ImportExport\\Exception\\ImportExportException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ImportExport/Exception/ImportExportException.php', + 'Aws\\ImportExport\\ImportExportClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ImportExport/ImportExportClient.php', + 'Aws\\InputValidationMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/InputValidationMiddleware.php', + 'Aws\\Inspector2\\Exception\\Inspector2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Inspector2/Exception/Inspector2Exception.php', + 'Aws\\Inspector2\\Inspector2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Inspector2/Inspector2Client.php', + 'Aws\\InspectorScan\\Exception\\InspectorScanException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/InspectorScan/Exception/InspectorScanException.php', + 'Aws\\InspectorScan\\InspectorScanClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/InspectorScan/InspectorScanClient.php', + 'Aws\\Inspector\\Exception\\InspectorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Inspector/Exception/InspectorException.php', + 'Aws\\Inspector\\InspectorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Inspector/InspectorClient.php', + 'Aws\\Interconnect\\Exception\\InterconnectException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Interconnect/Exception/InterconnectException.php', + 'Aws\\Interconnect\\InterconnectClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Interconnect/InterconnectClient.php', + 'Aws\\InternetMonitor\\Exception\\InternetMonitorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/InternetMonitor/Exception/InternetMonitorException.php', + 'Aws\\InternetMonitor\\InternetMonitorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/InternetMonitor/InternetMonitorClient.php', + 'Aws\\Invoicing\\Exception\\InvoicingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Invoicing/Exception/InvoicingException.php', + 'Aws\\Invoicing\\InvoicingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Invoicing/InvoicingClient.php', + 'Aws\\IoTDeviceAdvisor\\Exception\\IoTDeviceAdvisorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTDeviceAdvisor/Exception/IoTDeviceAdvisorException.php', + 'Aws\\IoTDeviceAdvisor\\IoTDeviceAdvisorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTDeviceAdvisor/IoTDeviceAdvisorClient.php', + 'Aws\\IoTFleetWise\\Exception\\IoTFleetWiseException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTFleetWise/Exception/IoTFleetWiseException.php', + 'Aws\\IoTFleetWise\\IoTFleetWiseClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTFleetWise/IoTFleetWiseClient.php', + 'Aws\\IoTJobsDataPlane\\Exception\\IoTJobsDataPlaneException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTJobsDataPlane/Exception/IoTJobsDataPlaneException.php', + 'Aws\\IoTJobsDataPlane\\IoTJobsDataPlaneClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTJobsDataPlane/IoTJobsDataPlaneClient.php', + 'Aws\\IoTManagedIntegrations\\Exception\\IoTManagedIntegrationsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTManagedIntegrations/Exception/IoTManagedIntegrationsException.php', + 'Aws\\IoTManagedIntegrations\\IoTManagedIntegrationsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTManagedIntegrations/IoTManagedIntegrationsClient.php', + 'Aws\\IoTSecureTunneling\\Exception\\IoTSecureTunnelingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTSecureTunneling/Exception/IoTSecureTunnelingException.php', + 'Aws\\IoTSecureTunneling\\IoTSecureTunnelingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTSecureTunneling/IoTSecureTunnelingClient.php', + 'Aws\\IoTSiteWise\\Exception\\IoTSiteWiseException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTSiteWise/Exception/IoTSiteWiseException.php', + 'Aws\\IoTSiteWise\\IoTSiteWiseClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTSiteWise/IoTSiteWiseClient.php', + 'Aws\\IoTThingsGraph\\Exception\\IoTThingsGraphException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTThingsGraph/Exception/IoTThingsGraphException.php', + 'Aws\\IoTThingsGraph\\IoTThingsGraphClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTThingsGraph/IoTThingsGraphClient.php', + 'Aws\\IoTTwinMaker\\Exception\\IoTTwinMakerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTTwinMaker/Exception/IoTTwinMakerException.php', + 'Aws\\IoTTwinMaker\\IoTTwinMakerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTTwinMaker/IoTTwinMakerClient.php', + 'Aws\\IoTWireless\\Exception\\IoTWirelessException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTWireless/Exception/IoTWirelessException.php', + 'Aws\\IoTWireless\\IoTWirelessClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IoTWireless/IoTWirelessClient.php', + 'Aws\\IotDataPlane\\Exception\\IotDataPlaneException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IotDataPlane/Exception/IotDataPlaneException.php', + 'Aws\\IotDataPlane\\IotDataPlaneClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/IotDataPlane/IotDataPlaneClient.php', + 'Aws\\Iot\\Exception\\IotException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Iot/Exception/IotException.php', + 'Aws\\Iot\\IotClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Iot/IotClient.php', + 'Aws\\JsonCompiler' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/JsonCompiler.php', + 'Aws\\KafkaConnect\\Exception\\KafkaConnectException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KafkaConnect/Exception/KafkaConnectException.php', + 'Aws\\KafkaConnect\\KafkaConnectClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KafkaConnect/KafkaConnectClient.php', + 'Aws\\Kafka\\Exception\\KafkaException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Kafka/Exception/KafkaException.php', + 'Aws\\Kafka\\KafkaClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Kafka/KafkaClient.php', + 'Aws\\KendraRanking\\Exception\\KendraRankingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KendraRanking/Exception/KendraRankingException.php', + 'Aws\\KendraRanking\\KendraRankingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KendraRanking/KendraRankingClient.php', + 'Aws\\KeyspacesStreams\\Exception\\KeyspacesStreamsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KeyspacesStreams/Exception/KeyspacesStreamsException.php', + 'Aws\\KeyspacesStreams\\KeyspacesStreamsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KeyspacesStreams/KeyspacesStreamsClient.php', + 'Aws\\Keyspaces\\Exception\\KeyspacesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Keyspaces/Exception/KeyspacesException.php', + 'Aws\\Keyspaces\\KeyspacesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Keyspaces/KeyspacesClient.php', + 'Aws\\KinesisAnalyticsV2\\Exception\\KinesisAnalyticsV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisAnalyticsV2/Exception/KinesisAnalyticsV2Exception.php', + 'Aws\\KinesisAnalyticsV2\\KinesisAnalyticsV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisAnalyticsV2/KinesisAnalyticsV2Client.php', + 'Aws\\KinesisAnalytics\\Exception\\KinesisAnalyticsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisAnalytics/Exception/KinesisAnalyticsException.php', + 'Aws\\KinesisAnalytics\\KinesisAnalyticsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisAnalytics/KinesisAnalyticsClient.php', + 'Aws\\KinesisVideoArchivedMedia\\Exception\\KinesisVideoArchivedMediaException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisVideoArchivedMedia/Exception/KinesisVideoArchivedMediaException.php', + 'Aws\\KinesisVideoArchivedMedia\\KinesisVideoArchivedMediaClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisVideoArchivedMedia/KinesisVideoArchivedMediaClient.php', + 'Aws\\KinesisVideoMedia\\Exception\\KinesisVideoMediaException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisVideoMedia/Exception/KinesisVideoMediaException.php', + 'Aws\\KinesisVideoMedia\\KinesisVideoMediaClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisVideoMedia/KinesisVideoMediaClient.php', + 'Aws\\KinesisVideoSignalingChannels\\Exception\\KinesisVideoSignalingChannelsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisVideoSignalingChannels/Exception/KinesisVideoSignalingChannelsException.php', + 'Aws\\KinesisVideoSignalingChannels\\KinesisVideoSignalingChannelsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisVideoSignalingChannels/KinesisVideoSignalingChannelsClient.php', + 'Aws\\KinesisVideoWebRTCStorage\\Exception\\KinesisVideoWebRTCStorageException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisVideoWebRTCStorage/Exception/KinesisVideoWebRTCStorageException.php', + 'Aws\\KinesisVideoWebRTCStorage\\KinesisVideoWebRTCStorageClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisVideoWebRTCStorage/KinesisVideoWebRTCStorageClient.php', + 'Aws\\KinesisVideo\\Exception\\KinesisVideoException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisVideo/Exception/KinesisVideoException.php', + 'Aws\\KinesisVideo\\KinesisVideoClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/KinesisVideo/KinesisVideoClient.php', + 'Aws\\Kinesis\\Exception\\KinesisException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Kinesis/Exception/KinesisException.php', + 'Aws\\Kinesis\\KinesisClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Kinesis/KinesisClient.php', + 'Aws\\Kms\\Exception\\KmsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Kms/Exception/KmsException.php', + 'Aws\\Kms\\KmsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Kms/KmsClient.php', + 'Aws\\LakeFormation\\Exception\\LakeFormationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LakeFormation/Exception/LakeFormationException.php', + 'Aws\\LakeFormation\\LakeFormationClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LakeFormation/LakeFormationClient.php', + 'Aws\\LambdaCore\\Exception\\LambdaCoreException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LambdaCore/Exception/LambdaCoreException.php', + 'Aws\\LambdaCore\\LambdaCoreClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LambdaCore/LambdaCoreClient.php', + 'Aws\\LambdaMicrovms\\Exception\\LambdaMicrovmsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LambdaMicrovms/Exception/LambdaMicrovmsException.php', + 'Aws\\LambdaMicrovms\\LambdaMicrovmsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LambdaMicrovms/LambdaMicrovmsClient.php', + 'Aws\\Lambda\\Exception\\LambdaException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Lambda/Exception/LambdaException.php', + 'Aws\\Lambda\\LambdaClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Lambda/LambdaClient.php', + 'Aws\\LaunchWizard\\Exception\\LaunchWizardException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LaunchWizard/Exception/LaunchWizardException.php', + 'Aws\\LaunchWizard\\LaunchWizardClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LaunchWizard/LaunchWizardClient.php', + 'Aws\\LexModelBuildingService\\Exception\\LexModelBuildingServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LexModelBuildingService/Exception/LexModelBuildingServiceException.php', + 'Aws\\LexModelBuildingService\\LexModelBuildingServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LexModelBuildingService/LexModelBuildingServiceClient.php', + 'Aws\\LexModelsV2\\Exception\\LexModelsV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LexModelsV2/Exception/LexModelsV2Exception.php', + 'Aws\\LexModelsV2\\LexModelsV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LexModelsV2/LexModelsV2Client.php', + 'Aws\\LexRuntimeService\\Exception\\LexRuntimeServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LexRuntimeService/Exception/LexRuntimeServiceException.php', + 'Aws\\LexRuntimeService\\LexRuntimeServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LexRuntimeService/LexRuntimeServiceClient.php', + 'Aws\\LexRuntimeV2\\Exception\\LexRuntimeV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LexRuntimeV2/Exception/LexRuntimeV2Exception.php', + 'Aws\\LexRuntimeV2\\LexRuntimeV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LexRuntimeV2/LexRuntimeV2Client.php', + 'Aws\\LicenseManagerLinuxSubscriptions\\Exception\\LicenseManagerLinuxSubscriptionsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LicenseManagerLinuxSubscriptions/Exception/LicenseManagerLinuxSubscriptionsException.php', + 'Aws\\LicenseManagerLinuxSubscriptions\\LicenseManagerLinuxSubscriptionsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LicenseManagerLinuxSubscriptions/LicenseManagerLinuxSubscriptionsClient.php', + 'Aws\\LicenseManagerUserSubscriptions\\Exception\\LicenseManagerUserSubscriptionsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LicenseManagerUserSubscriptions/Exception/LicenseManagerUserSubscriptionsException.php', + 'Aws\\LicenseManagerUserSubscriptions\\LicenseManagerUserSubscriptionsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LicenseManagerUserSubscriptions/LicenseManagerUserSubscriptionsClient.php', + 'Aws\\LicenseManager\\Exception\\LicenseManagerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LicenseManager/Exception/LicenseManagerException.php', + 'Aws\\LicenseManager\\LicenseManagerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LicenseManager/LicenseManagerClient.php', + 'Aws\\Lightsail\\Exception\\LightsailException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Lightsail/Exception/LightsailException.php', + 'Aws\\Lightsail\\LightsailClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Lightsail/LightsailClient.php', + 'Aws\\LocationService\\Exception\\LocationServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LocationService/Exception/LocationServiceException.php', + 'Aws\\LocationService\\LocationServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LocationService/LocationServiceClient.php', + 'Aws\\LookoutEquipment\\Exception\\LookoutEquipmentException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LookoutEquipment/Exception/LookoutEquipmentException.php', + 'Aws\\LookoutEquipment\\LookoutEquipmentClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LookoutEquipment/LookoutEquipmentClient.php', + 'Aws\\LruArrayCache' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/LruArrayCache.php', + 'Aws\\MPA\\Exception\\MPAException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MPA/Exception/MPAException.php', + 'Aws\\MPA\\MPAClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MPA/MPAClient.php', + 'Aws\\MQ\\Exception\\MQException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MQ/Exception/MQException.php', + 'Aws\\MQ\\MQClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MQ/MQClient.php', + 'Aws\\MTurk\\Exception\\MTurkException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MTurk/Exception/MTurkException.php', + 'Aws\\MTurk\\MTurkClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MTurk/MTurkClient.php', + 'Aws\\MWAAServerless\\Exception\\MWAAServerlessException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MWAAServerless/Exception/MWAAServerlessException.php', + 'Aws\\MWAAServerless\\MWAAServerlessClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MWAAServerless/MWAAServerlessClient.php', + 'Aws\\MWAA\\Exception\\MWAAException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MWAA/Exception/MWAAException.php', + 'Aws\\MWAA\\MWAAClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MWAA/MWAAClient.php', + 'Aws\\MachineLearning\\Exception\\MachineLearningException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MachineLearning/Exception/MachineLearningException.php', + 'Aws\\MachineLearning\\MachineLearningClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MachineLearning/MachineLearningClient.php', + 'Aws\\Macie2\\Exception\\Macie2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Macie2/Exception/Macie2Exception.php', + 'Aws\\Macie2\\Macie2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Macie2/Macie2Client.php', + 'Aws\\MailManager\\Exception\\MailManagerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MailManager/Exception/MailManagerException.php', + 'Aws\\MailManager\\MailManagerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MailManager/MailManagerClient.php', + 'Aws\\MainframeModernization\\Exception\\MainframeModernizationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MainframeModernization/Exception/MainframeModernizationException.php', + 'Aws\\MainframeModernization\\MainframeModernizationClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MainframeModernization/MainframeModernizationClient.php', + 'Aws\\ManagedBlockchainQuery\\Exception\\ManagedBlockchainQueryException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ManagedBlockchainQuery/Exception/ManagedBlockchainQueryException.php', + 'Aws\\ManagedBlockchainQuery\\ManagedBlockchainQueryClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ManagedBlockchainQuery/ManagedBlockchainQueryClient.php', + 'Aws\\ManagedBlockchain\\Exception\\ManagedBlockchainException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ManagedBlockchain/Exception/ManagedBlockchainException.php', + 'Aws\\ManagedBlockchain\\ManagedBlockchainClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ManagedBlockchain/ManagedBlockchainClient.php', + 'Aws\\ManagedGrafana\\Exception\\ManagedGrafanaException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ManagedGrafana/Exception/ManagedGrafanaException.php', + 'Aws\\ManagedGrafana\\ManagedGrafanaClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ManagedGrafana/ManagedGrafanaClient.php', + 'Aws\\MarketplaceAgreement\\Exception\\MarketplaceAgreementException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceAgreement/Exception/MarketplaceAgreementException.php', + 'Aws\\MarketplaceAgreement\\MarketplaceAgreementClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceAgreement/MarketplaceAgreementClient.php', + 'Aws\\MarketplaceCatalog\\Exception\\MarketplaceCatalogException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceCatalog/Exception/MarketplaceCatalogException.php', + 'Aws\\MarketplaceCatalog\\MarketplaceCatalogClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceCatalog/MarketplaceCatalogClient.php', + 'Aws\\MarketplaceCommerceAnalytics\\Exception\\MarketplaceCommerceAnalyticsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceCommerceAnalytics/Exception/MarketplaceCommerceAnalyticsException.php', + 'Aws\\MarketplaceCommerceAnalytics\\MarketplaceCommerceAnalyticsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceCommerceAnalytics/MarketplaceCommerceAnalyticsClient.php', + 'Aws\\MarketplaceDeployment\\Exception\\MarketplaceDeploymentException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceDeployment/Exception/MarketplaceDeploymentException.php', + 'Aws\\MarketplaceDeployment\\MarketplaceDeploymentClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceDeployment/MarketplaceDeploymentClient.php', + 'Aws\\MarketplaceDiscovery\\Exception\\MarketplaceDiscoveryException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceDiscovery/Exception/MarketplaceDiscoveryException.php', + 'Aws\\MarketplaceDiscovery\\MarketplaceDiscoveryClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceDiscovery/MarketplaceDiscoveryClient.php', + 'Aws\\MarketplaceEntitlementService\\Exception\\MarketplaceEntitlementServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceEntitlementService/Exception/MarketplaceEntitlementServiceException.php', + 'Aws\\MarketplaceEntitlementService\\MarketplaceEntitlementServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceEntitlementService/MarketplaceEntitlementServiceClient.php', + 'Aws\\MarketplaceMetering\\Exception\\MarketplaceMeteringException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceMetering/Exception/MarketplaceMeteringException.php', + 'Aws\\MarketplaceMetering\\MarketplaceMeteringClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceMetering/MarketplaceMeteringClient.php', + 'Aws\\MarketplaceReporting\\Exception\\MarketplaceReportingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceReporting/Exception/MarketplaceReportingException.php', + 'Aws\\MarketplaceReporting\\MarketplaceReportingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MarketplaceReporting/MarketplaceReportingClient.php', + 'Aws\\MediaConnect\\Exception\\MediaConnectException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaConnect/Exception/MediaConnectException.php', + 'Aws\\MediaConnect\\MediaConnectClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaConnect/MediaConnectClient.php', + 'Aws\\MediaConvert\\Exception\\MediaConvertException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaConvert/Exception/MediaConvertException.php', + 'Aws\\MediaConvert\\MediaConvertClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaConvert/MediaConvertClient.php', + 'Aws\\MediaLive\\Exception\\MediaLiveException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaLive/Exception/MediaLiveException.php', + 'Aws\\MediaLive\\MediaLiveClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaLive/MediaLiveClient.php', + 'Aws\\MediaPackageV2\\Exception\\MediaPackageV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaPackageV2/Exception/MediaPackageV2Exception.php', + 'Aws\\MediaPackageV2\\MediaPackageV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaPackageV2/MediaPackageV2Client.php', + 'Aws\\MediaPackageVod\\Exception\\MediaPackageVodException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaPackageVod/Exception/MediaPackageVodException.php', + 'Aws\\MediaPackageVod\\MediaPackageVodClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaPackageVod/MediaPackageVodClient.php', + 'Aws\\MediaPackage\\Exception\\MediaPackageException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaPackage/Exception/MediaPackageException.php', + 'Aws\\MediaPackage\\MediaPackageClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaPackage/MediaPackageClient.php', + 'Aws\\MediaStoreData\\Exception\\MediaStoreDataException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaStoreData/Exception/MediaStoreDataException.php', + 'Aws\\MediaStoreData\\MediaStoreDataClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaStoreData/MediaStoreDataClient.php', + 'Aws\\MediaStore\\Exception\\MediaStoreException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaStore/Exception/MediaStoreException.php', + 'Aws\\MediaStore\\MediaStoreClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaStore/MediaStoreClient.php', + 'Aws\\MediaTailor\\Exception\\MediaTailorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaTailor/Exception/MediaTailorException.php', + 'Aws\\MediaTailor\\MediaTailorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MediaTailor/MediaTailorClient.php', + 'Aws\\MedicalImaging\\Exception\\MedicalImagingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MedicalImaging/Exception/MedicalImagingException.php', + 'Aws\\MedicalImaging\\MedicalImagingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MedicalImaging/MedicalImagingClient.php', + 'Aws\\MemoryDB\\Exception\\MemoryDBException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MemoryDB/Exception/MemoryDBException.php', + 'Aws\\MemoryDB\\MemoryDBClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MemoryDB/MemoryDBClient.php', + 'Aws\\MetricsBuilder' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MetricsBuilder.php', + 'Aws\\Middleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Middleware.php', + 'Aws\\MigrationHubConfig\\Exception\\MigrationHubConfigException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MigrationHubConfig/Exception/MigrationHubConfigException.php', + 'Aws\\MigrationHubConfig\\MigrationHubConfigClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MigrationHubConfig/MigrationHubConfigClient.php', + 'Aws\\MigrationHubOrchestrator\\Exception\\MigrationHubOrchestratorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MigrationHubOrchestrator/Exception/MigrationHubOrchestratorException.php', + 'Aws\\MigrationHubOrchestrator\\MigrationHubOrchestratorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MigrationHubOrchestrator/MigrationHubOrchestratorClient.php', + 'Aws\\MigrationHubRefactorSpaces\\Exception\\MigrationHubRefactorSpacesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MigrationHubRefactorSpaces/Exception/MigrationHubRefactorSpacesException.php', + 'Aws\\MigrationHubRefactorSpaces\\MigrationHubRefactorSpacesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MigrationHubRefactorSpaces/MigrationHubRefactorSpacesClient.php', + 'Aws\\MigrationHubStrategyRecommendations\\Exception\\MigrationHubStrategyRecommendationsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MigrationHubStrategyRecommendations/Exception/MigrationHubStrategyRecommendationsException.php', + 'Aws\\MigrationHubStrategyRecommendations\\MigrationHubStrategyRecommendationsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MigrationHubStrategyRecommendations/MigrationHubStrategyRecommendationsClient.php', + 'Aws\\MigrationHub\\Exception\\MigrationHubException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MigrationHub/Exception/MigrationHubException.php', + 'Aws\\MigrationHub\\MigrationHubClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MigrationHub/MigrationHubClient.php', + 'Aws\\MockHandler' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MockHandler.php', + 'Aws\\MonitoringEventsInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MonitoringEventsInterface.php', + 'Aws\\MultiRegionClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/MultiRegionClient.php', + 'Aws\\Multipart\\AbstractUploadManager' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Multipart/AbstractUploadManager.php', + 'Aws\\Multipart\\AbstractUploader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Multipart/AbstractUploader.php', + 'Aws\\Multipart\\UploadState' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Multipart/UploadState.php', + 'Aws\\NeptuneGraph\\Exception\\NeptuneGraphException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NeptuneGraph/Exception/NeptuneGraphException.php', + 'Aws\\NeptuneGraph\\NeptuneGraphClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NeptuneGraph/NeptuneGraphClient.php', + 'Aws\\Neptune\\Exception\\NeptuneException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Neptune/Exception/NeptuneException.php', + 'Aws\\Neptune\\NeptuneClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Neptune/NeptuneClient.php', + 'Aws\\Neptunedata\\Exception\\NeptunedataException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Neptunedata/Exception/NeptunedataException.php', + 'Aws\\Neptunedata\\NeptunedataClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Neptunedata/NeptunedataClient.php', + 'Aws\\NetworkFirewall\\Exception\\NetworkFirewallException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NetworkFirewall/Exception/NetworkFirewallException.php', + 'Aws\\NetworkFirewall\\NetworkFirewallClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NetworkFirewall/NetworkFirewallClient.php', + 'Aws\\NetworkFlowMonitor\\Exception\\NetworkFlowMonitorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NetworkFlowMonitor/Exception/NetworkFlowMonitorException.php', + 'Aws\\NetworkFlowMonitor\\NetworkFlowMonitorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NetworkFlowMonitor/NetworkFlowMonitorClient.php', + 'Aws\\NetworkManager\\Exception\\NetworkManagerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NetworkManager/Exception/NetworkManagerException.php', + 'Aws\\NetworkManager\\NetworkManagerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NetworkManager/NetworkManagerClient.php', + 'Aws\\NetworkMonitor\\Exception\\NetworkMonitorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NetworkMonitor/Exception/NetworkMonitorException.php', + 'Aws\\NetworkMonitor\\NetworkMonitorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NetworkMonitor/NetworkMonitorClient.php', + 'Aws\\NotificationsContacts\\Exception\\NotificationsContactsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NotificationsContacts/Exception/NotificationsContactsException.php', + 'Aws\\NotificationsContacts\\NotificationsContactsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NotificationsContacts/NotificationsContactsClient.php', + 'Aws\\Notifications\\Exception\\NotificationsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Notifications/Exception/NotificationsException.php', + 'Aws\\Notifications\\NotificationsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Notifications/NotificationsClient.php', + 'Aws\\NovaAct\\Exception\\NovaActException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NovaAct/Exception/NovaActException.php', + 'Aws\\NovaAct\\NovaActClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/NovaAct/NovaActClient.php', + 'Aws\\OAM\\Exception\\OAMException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/OAM/Exception/OAMException.php', + 'Aws\\OAM\\OAMClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/OAM/OAMClient.php', + 'Aws\\OSIS\\Exception\\OSISException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/OSIS/Exception/OSISException.php', + 'Aws\\OSIS\\OSISClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/OSIS/OSISClient.php', + 'Aws\\ObservabilityAdmin\\Exception\\ObservabilityAdminException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ObservabilityAdmin/Exception/ObservabilityAdminException.php', + 'Aws\\ObservabilityAdmin\\ObservabilityAdminClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ObservabilityAdmin/ObservabilityAdminClient.php', + 'Aws\\Odb\\Exception\\OdbException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Odb/Exception/OdbException.php', + 'Aws\\Odb\\OdbClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Odb/OdbClient.php', + 'Aws\\Omics\\Exception\\OmicsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Omics/Exception/OmicsException.php', + 'Aws\\Omics\\OmicsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Omics/OmicsClient.php', + 'Aws\\OpenSearchServerless\\Exception\\OpenSearchServerlessException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/OpenSearchServerless/Exception/OpenSearchServerlessException.php', + 'Aws\\OpenSearchServerless\\OpenSearchServerlessClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/OpenSearchServerless/OpenSearchServerlessClient.php', + 'Aws\\OpenSearchService\\Exception\\OpenSearchServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/OpenSearchService/Exception/OpenSearchServiceException.php', + 'Aws\\OpenSearchService\\OpenSearchServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/OpenSearchService/OpenSearchServiceClient.php', + 'Aws\\Organizations\\Exception\\OrganizationsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Organizations/Exception/OrganizationsException.php', + 'Aws\\Organizations\\OrganizationsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Organizations/OrganizationsClient.php', + 'Aws\\Outposts\\Exception\\OutpostsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Outposts/Exception/OutpostsException.php', + 'Aws\\Outposts\\OutpostsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Outposts/OutpostsClient.php', + 'Aws\\PCS\\Exception\\PCSException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PCS/Exception/PCSException.php', + 'Aws\\PCS\\PCSClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PCS/PCSClient.php', + 'Aws\\PI\\Exception\\PIException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PI/Exception/PIException.php', + 'Aws\\PI\\PIClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PI/PIClient.php', + 'Aws\\PartnerCentralAccount\\Exception\\PartnerCentralAccountException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PartnerCentralAccount/Exception/PartnerCentralAccountException.php', + 'Aws\\PartnerCentralAccount\\PartnerCentralAccountClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PartnerCentralAccount/PartnerCentralAccountClient.php', + 'Aws\\PartnerCentralBenefits\\Exception\\PartnerCentralBenefitsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PartnerCentralBenefits/Exception/PartnerCentralBenefitsException.php', + 'Aws\\PartnerCentralBenefits\\PartnerCentralBenefitsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PartnerCentralBenefits/PartnerCentralBenefitsClient.php', + 'Aws\\PartnerCentralChannel\\Exception\\PartnerCentralChannelException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PartnerCentralChannel/Exception/PartnerCentralChannelException.php', + 'Aws\\PartnerCentralChannel\\PartnerCentralChannelClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PartnerCentralChannel/PartnerCentralChannelClient.php', + 'Aws\\PartnerCentralRevenueMeasurement\\Exception\\PartnerCentralRevenueMeasurementException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PartnerCentralRevenueMeasurement/Exception/PartnerCentralRevenueMeasurementException.php', + 'Aws\\PartnerCentralRevenueMeasurement\\PartnerCentralRevenueMeasurementClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PartnerCentralRevenueMeasurement/PartnerCentralRevenueMeasurementClient.php', + 'Aws\\PartnerCentralSelling\\Exception\\PartnerCentralSellingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PartnerCentralSelling/Exception/PartnerCentralSellingException.php', + 'Aws\\PartnerCentralSelling\\PartnerCentralSellingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PartnerCentralSelling/PartnerCentralSellingClient.php', + 'Aws\\PaymentCryptographyData\\Exception\\PaymentCryptographyDataException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PaymentCryptographyData/Exception/PaymentCryptographyDataException.php', + 'Aws\\PaymentCryptographyData\\PaymentCryptographyDataClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PaymentCryptographyData/PaymentCryptographyDataClient.php', + 'Aws\\PaymentCryptography\\Exception\\PaymentCryptographyException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PaymentCryptography/Exception/PaymentCryptographyException.php', + 'Aws\\PaymentCryptography\\PaymentCryptographyClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PaymentCryptography/PaymentCryptographyClient.php', + 'Aws\\PcaConnectorAd\\Exception\\PcaConnectorAdException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PcaConnectorAd/Exception/PcaConnectorAdException.php', + 'Aws\\PcaConnectorAd\\PcaConnectorAdClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PcaConnectorAd/PcaConnectorAdClient.php', + 'Aws\\PcaConnectorScep\\Exception\\PcaConnectorScepException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PcaConnectorScep/Exception/PcaConnectorScepException.php', + 'Aws\\PcaConnectorScep\\PcaConnectorScepClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PcaConnectorScep/PcaConnectorScepClient.php', + 'Aws\\PersonalizeEvents\\Exception\\PersonalizeEventsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PersonalizeEvents/Exception/PersonalizeEventsException.php', + 'Aws\\PersonalizeEvents\\PersonalizeEventsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PersonalizeEvents/PersonalizeEventsClient.php', + 'Aws\\PersonalizeRuntime\\Exception\\PersonalizeRuntimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PersonalizeRuntime/Exception/PersonalizeRuntimeException.php', + 'Aws\\PersonalizeRuntime\\PersonalizeRuntimeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PersonalizeRuntime/PersonalizeRuntimeClient.php', + 'Aws\\Personalize\\Exception\\PersonalizeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Personalize/Exception/PersonalizeException.php', + 'Aws\\Personalize\\PersonalizeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Personalize/PersonalizeClient.php', + 'Aws\\PhpHash' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PhpHash.php', + 'Aws\\PinpointEmail\\Exception\\PinpointEmailException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PinpointEmail/Exception/PinpointEmailException.php', + 'Aws\\PinpointEmail\\PinpointEmailClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PinpointEmail/PinpointEmailClient.php', + 'Aws\\PinpointSMSVoiceV2\\Exception\\PinpointSMSVoiceV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PinpointSMSVoiceV2/Exception/PinpointSMSVoiceV2Exception.php', + 'Aws\\PinpointSMSVoiceV2\\PinpointSMSVoiceV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PinpointSMSVoiceV2/PinpointSMSVoiceV2Client.php', + 'Aws\\PinpointSMSVoice\\Exception\\PinpointSMSVoiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PinpointSMSVoice/Exception/PinpointSMSVoiceException.php', + 'Aws\\PinpointSMSVoice\\PinpointSMSVoiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PinpointSMSVoice/PinpointSMSVoiceClient.php', + 'Aws\\Pinpoint\\Exception\\PinpointException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Pinpoint/Exception/PinpointException.php', + 'Aws\\Pinpoint\\PinpointClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Pinpoint/PinpointClient.php', + 'Aws\\Pipes\\Exception\\PipesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Pipes/Exception/PipesException.php', + 'Aws\\Pipes\\PipesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Pipes/PipesClient.php', + 'Aws\\Polly\\Exception\\PollyException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Polly/Exception/PollyException.php', + 'Aws\\Polly\\PollyClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Polly/PollyClient.php', + 'Aws\\PresignUrlMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PresignUrlMiddleware.php', + 'Aws\\Pricing\\Exception\\PricingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Pricing/Exception/PricingException.php', + 'Aws\\Pricing\\PricingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Pricing/PricingClient.php', + 'Aws\\PrometheusService\\Exception\\PrometheusServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PrometheusService/Exception/PrometheusServiceException.php', + 'Aws\\PrometheusService\\PrometheusServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PrometheusService/PrometheusServiceClient.php', + 'Aws\\Proton\\Exception\\ProtonException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Proton/Exception/ProtonException.php', + 'Aws\\Proton\\ProtonClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Proton/ProtonClient.php', + 'Aws\\Psr16CacheAdapter' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Psr16CacheAdapter.php', + 'Aws\\PsrCacheAdapter' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/PsrCacheAdapter.php', + 'Aws\\QApps\\Exception\\QAppsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/QApps/Exception/QAppsException.php', + 'Aws\\QApps\\QAppsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/QApps/QAppsClient.php', + 'Aws\\QBusiness\\Exception\\QBusinessException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/QBusiness/Exception/QBusinessException.php', + 'Aws\\QBusiness\\QBusinessClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/QBusiness/QBusinessClient.php', + 'Aws\\QConnect\\Exception\\QConnectException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/QConnect/Exception/QConnectException.php', + 'Aws\\QConnect\\QConnectClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/QConnect/QConnectClient.php', + 'Aws\\QueryCompatibleInputMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/QueryCompatibleInputMiddleware.php', + 'Aws\\QuickSight\\Exception\\QuickSightException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/QuickSight/Exception/QuickSightException.php', + 'Aws\\QuickSight\\QuickSightClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/QuickSight/QuickSightClient.php', + 'Aws\\RAM\\Exception\\RAMException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RAM/Exception/RAMException.php', + 'Aws\\RAM\\RAMClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RAM/RAMClient.php', + 'Aws\\RDSDataService\\Exception\\RDSDataServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RDSDataService/Exception/RDSDataServiceException.php', + 'Aws\\RDSDataService\\RDSDataServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RDSDataService/RDSDataServiceClient.php', + 'Aws\\RTBFabric\\Exception\\RTBFabricException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RTBFabric/Exception/RTBFabricException.php', + 'Aws\\RTBFabric\\RTBFabricClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RTBFabric/RTBFabricClient.php', + 'Aws\\Rds\\AuthTokenGenerator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Rds/AuthTokenGenerator.php', + 'Aws\\Rds\\Exception\\RdsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Rds/Exception/RdsException.php', + 'Aws\\Rds\\RdsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Rds/RdsClient.php', + 'Aws\\RecycleBin\\Exception\\RecycleBinException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RecycleBin/Exception/RecycleBinException.php', + 'Aws\\RecycleBin\\RecycleBinClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RecycleBin/RecycleBinClient.php', + 'Aws\\RedshiftDataAPIService\\Exception\\RedshiftDataAPIServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RedshiftDataAPIService/Exception/RedshiftDataAPIServiceException.php', + 'Aws\\RedshiftDataAPIService\\RedshiftDataAPIServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RedshiftDataAPIService/RedshiftDataAPIServiceClient.php', + 'Aws\\RedshiftServerless\\Exception\\RedshiftServerlessException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RedshiftServerless/Exception/RedshiftServerlessException.php', + 'Aws\\RedshiftServerless\\RedshiftServerlessClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RedshiftServerless/RedshiftServerlessClient.php', + 'Aws\\Redshift\\Exception\\RedshiftException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Redshift/Exception/RedshiftException.php', + 'Aws\\Redshift\\RedshiftClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Redshift/RedshiftClient.php', + 'Aws\\Rekognition\\Exception\\RekognitionException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Rekognition/Exception/RekognitionException.php', + 'Aws\\Rekognition\\RekognitionClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Rekognition/RekognitionClient.php', + 'Aws\\Repostspace\\Exception\\RepostspaceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Repostspace/Exception/RepostspaceException.php', + 'Aws\\Repostspace\\RepostspaceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Repostspace/RepostspaceClient.php', + 'Aws\\RequestCompressionMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RequestCompressionMiddleware.php', + 'Aws\\ResilienceHub\\Exception\\ResilienceHubException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResilienceHub/Exception/ResilienceHubException.php', + 'Aws\\ResilienceHub\\ResilienceHubClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResilienceHub/ResilienceHubClient.php', + 'Aws\\Resiliencehubv2\\Exception\\Resiliencehubv2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Resiliencehubv2/Exception/Resiliencehubv2Exception.php', + 'Aws\\Resiliencehubv2\\Resiliencehubv2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Resiliencehubv2/Resiliencehubv2Client.php', + 'Aws\\ResourceExplorer2\\Exception\\ResourceExplorer2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResourceExplorer2/Exception/ResourceExplorer2Exception.php', + 'Aws\\ResourceExplorer2\\ResourceExplorer2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResourceExplorer2/ResourceExplorer2Client.php', + 'Aws\\ResourceGroupsTaggingAPI\\Exception\\ResourceGroupsTaggingAPIException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResourceGroupsTaggingAPI/Exception/ResourceGroupsTaggingAPIException.php', + 'Aws\\ResourceGroupsTaggingAPI\\ResourceGroupsTaggingAPIClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResourceGroupsTaggingAPI/ResourceGroupsTaggingAPIClient.php', + 'Aws\\ResourceGroups\\Exception\\ResourceGroupsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResourceGroups/Exception/ResourceGroupsException.php', + 'Aws\\ResourceGroups\\ResourceGroupsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResourceGroups/ResourceGroupsClient.php', + 'Aws\\ResponseContainerInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResponseContainerInterface.php', + 'Aws\\Result' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Result.php', + 'Aws\\ResultInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResultInterface.php', + 'Aws\\ResultPaginator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ResultPaginator.php', + 'Aws\\RetryMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RetryMiddleware.php', + 'Aws\\RetryMiddlewareV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RetryMiddlewareV2.php', + 'Aws\\Retry\\Configuration' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/Configuration.php', + 'Aws\\Retry\\ConfigurationInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/ConfigurationInterface.php', + 'Aws\\Retry\\ConfigurationProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/ConfigurationProvider.php', + 'Aws\\Retry\\Exception\\ConfigurationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/Exception/ConfigurationException.php', + 'Aws\\Retry\\QuotaManager' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/QuotaManager.php', + 'Aws\\Retry\\RateLimiter' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/RateLimiter.php', + 'Aws\\Retry\\RetryHelperTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/RetryHelperTrait.php', + 'Aws\\Retry\\V3\\LongPolling' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/V3/LongPolling.php', + 'Aws\\Retry\\V3\\OptIn' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/V3/OptIn.php', + 'Aws\\Retry\\V3\\QuotaManager' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/V3/QuotaManager.php', + 'Aws\\Retry\\V3\\RetryMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Retry/V3/RetryMiddleware.php', + 'Aws\\RolesAnywhere\\Exception\\RolesAnywhereException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RolesAnywhere/Exception/RolesAnywhereException.php', + 'Aws\\RolesAnywhere\\RolesAnywhereClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/RolesAnywhere/RolesAnywhereClient.php', + 'Aws\\Route53Domains\\Exception\\Route53DomainsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53Domains/Exception/Route53DomainsException.php', + 'Aws\\Route53Domains\\Route53DomainsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53Domains/Route53DomainsClient.php', + 'Aws\\Route53GlobalResolver\\Exception\\Route53GlobalResolverException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53GlobalResolver/Exception/Route53GlobalResolverException.php', + 'Aws\\Route53GlobalResolver\\Route53GlobalResolverClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53GlobalResolver/Route53GlobalResolverClient.php', + 'Aws\\Route53Profiles\\Exception\\Route53ProfilesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53Profiles/Exception/Route53ProfilesException.php', + 'Aws\\Route53Profiles\\Route53ProfilesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53Profiles/Route53ProfilesClient.php', + 'Aws\\Route53RecoveryCluster\\Exception\\Route53RecoveryClusterException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53RecoveryCluster/Exception/Route53RecoveryClusterException.php', + 'Aws\\Route53RecoveryCluster\\Route53RecoveryClusterClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53RecoveryCluster/Route53RecoveryClusterClient.php', + 'Aws\\Route53RecoveryControlConfig\\Exception\\Route53RecoveryControlConfigException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53RecoveryControlConfig/Exception/Route53RecoveryControlConfigException.php', + 'Aws\\Route53RecoveryControlConfig\\Route53RecoveryControlConfigClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53RecoveryControlConfig/Route53RecoveryControlConfigClient.php', + 'Aws\\Route53RecoveryReadiness\\Exception\\Route53RecoveryReadinessException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53RecoveryReadiness/Exception/Route53RecoveryReadinessException.php', + 'Aws\\Route53RecoveryReadiness\\Route53RecoveryReadinessClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53RecoveryReadiness/Route53RecoveryReadinessClient.php', + 'Aws\\Route53Resolver\\Exception\\Route53ResolverException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53Resolver/Exception/Route53ResolverException.php', + 'Aws\\Route53Resolver\\Route53ResolverClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53Resolver/Route53ResolverClient.php', + 'Aws\\Route53\\Exception\\Route53Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53/Exception/Route53Exception.php', + 'Aws\\Route53\\Route53Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Route53/Route53Client.php', + 'Aws\\S3Control\\EndpointArnMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Control/EndpointArnMiddleware.php', + 'Aws\\S3Control\\Exception\\S3ControlException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Control/Exception/S3ControlException.php', + 'Aws\\S3Control\\S3ControlClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Control/S3ControlClient.php', + 'Aws\\S3Files\\Exception\\S3FilesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Files/Exception/S3FilesException.php', + 'Aws\\S3Files\\S3FilesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Files/S3FilesClient.php', + 'Aws\\S3Outposts\\Exception\\S3OutpostsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Outposts/Exception/S3OutpostsException.php', + 'Aws\\S3Outposts\\S3OutpostsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Outposts/S3OutpostsClient.php', + 'Aws\\S3Tables\\Exception\\S3TablesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Tables/Exception/S3TablesException.php', + 'Aws\\S3Tables\\S3TablesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Tables/S3TablesClient.php', + 'Aws\\S3Vectors\\Exception\\S3VectorsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Vectors/Exception/S3VectorsException.php', + 'Aws\\S3Vectors\\S3VectorsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3Vectors/S3VectorsClient.php', + 'Aws\\S3\\AmbiguousSuccessParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/AmbiguousSuccessParser.php', + 'Aws\\S3\\ApplyChecksumMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/ApplyChecksumMiddleware.php', + 'Aws\\S3\\BatchDelete' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/BatchDelete.php', + 'Aws\\S3\\BucketEndpointArnMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/BucketEndpointArnMiddleware.php', + 'Aws\\S3\\BucketEndpointMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/BucketEndpointMiddleware.php', + 'Aws\\S3\\CalculatesChecksumTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/CalculatesChecksumTrait.php', + 'Aws\\S3\\Crypto\\CryptoParamsTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/CryptoParamsTrait.php', + 'Aws\\S3\\Crypto\\CryptoParamsTraitV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/CryptoParamsTraitV2.php', + 'Aws\\S3\\Crypto\\CryptoParamsTraitV3' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/CryptoParamsTraitV3.php', + 'Aws\\S3\\Crypto\\HeadersMetadataStrategy' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/HeadersMetadataStrategy.php', + 'Aws\\S3\\Crypto\\InstructionFileMetadataStrategy' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/InstructionFileMetadataStrategy.php', + 'Aws\\S3\\Crypto\\S3EncryptionClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionClient.php', + 'Aws\\S3\\Crypto\\S3EncryptionClientV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionClientV2.php', + 'Aws\\S3\\Crypto\\S3EncryptionClientV3' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionClientV3.php', + 'Aws\\S3\\Crypto\\S3EncryptionMultipartUploader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionMultipartUploader.php', + 'Aws\\S3\\Crypto\\S3EncryptionMultipartUploaderV2' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionMultipartUploaderV2.php', + 'Aws\\S3\\Crypto\\S3EncryptionMultipartUploaderV3' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/S3EncryptionMultipartUploaderV3.php', + 'Aws\\S3\\Crypto\\UserAgentTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Crypto/UserAgentTrait.php', + 'Aws\\S3\\EndpointRegionHelperTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/EndpointRegionHelperTrait.php', + 'Aws\\S3\\Exception\\DeleteMultipleObjectsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Exception/DeleteMultipleObjectsException.php', + 'Aws\\S3\\Exception\\MultipartCopyAnnotationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Exception/MultipartCopyAnnotationException.php', + 'Aws\\S3\\Exception\\PermanentRedirectException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Exception/PermanentRedirectException.php', + 'Aws\\S3\\Exception\\S3Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Exception/S3Exception.php', + 'Aws\\S3\\Exception\\S3MultipartUploadException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Exception/S3MultipartUploadException.php', + 'Aws\\S3\\ExpiresParsingMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/ExpiresParsingMiddleware.php', + 'Aws\\S3\\GetBucketLocationParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/GetBucketLocationParser.php', + 'Aws\\S3\\MultipartCopy' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/MultipartCopy.php', + 'Aws\\S3\\MultipartUploader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/MultipartUploader.php', + 'Aws\\S3\\MultipartUploadingTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/MultipartUploadingTrait.php', + 'Aws\\S3\\ObjectCopier' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/ObjectCopier.php', + 'Aws\\S3\\ObjectUploader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/ObjectUploader.php', + 'Aws\\S3\\Parser\\GetBucketLocationResultMutator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Parser/GetBucketLocationResultMutator.php', + 'Aws\\S3\\Parser\\S3Parser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Parser/S3Parser.php', + 'Aws\\S3\\Parser\\S3ResultMutator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Parser/S3ResultMutator.php', + 'Aws\\S3\\Parser\\ValidateResponseChecksumResultMutator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Parser/ValidateResponseChecksumResultMutator.php', + 'Aws\\S3\\PermanentRedirectMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/PermanentRedirectMiddleware.php', + 'Aws\\S3\\PostObject' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/PostObject.php', + 'Aws\\S3\\PostObjectV4' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/PostObjectV4.php', + 'Aws\\S3\\PutObjectUrlMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/PutObjectUrlMiddleware.php', + 'Aws\\S3\\RegionalEndpoint\\Configuration' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/RegionalEndpoint/Configuration.php', + 'Aws\\S3\\RegionalEndpoint\\ConfigurationInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/RegionalEndpoint/ConfigurationInterface.php', + 'Aws\\S3\\RegionalEndpoint\\ConfigurationProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/RegionalEndpoint/ConfigurationProvider.php', + 'Aws\\S3\\RegionalEndpoint\\Exception\\ConfigurationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/RegionalEndpoint/Exception/ConfigurationException.php', + 'Aws\\S3\\RetryableMalformedResponseParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/RetryableMalformedResponseParser.php', + 'Aws\\S3\\S3Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Client.php', + 'Aws\\S3\\S3ClientInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3ClientInterface.php', + 'Aws\\S3\\S3ClientTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3ClientTrait.php', + 'Aws\\S3\\S3EndpointMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3EndpointMiddleware.php', + 'Aws\\S3\\S3MultiRegionClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3MultiRegionClient.php', + 'Aws\\S3\\S3Transfer\\AbstractMultipartDownloader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/AbstractMultipartDownloader.php', + 'Aws\\S3\\S3Transfer\\AbstractMultipartUploader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/AbstractMultipartUploader.php', + 'Aws\\S3\\S3Transfer\\DirectoryDownloader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/DirectoryDownloader.php', + 'Aws\\S3\\S3Transfer\\DirectoryUploader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/DirectoryUploader.php', + 'Aws\\S3\\S3Transfer\\Exception\\FileDownloadException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Exception/FileDownloadException.php', + 'Aws\\S3\\S3Transfer\\Exception\\ProgressTrackerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Exception/ProgressTrackerException.php', + 'Aws\\S3\\S3Transfer\\Exception\\S3TransferException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Exception/S3TransferException.php', + 'Aws\\S3\\S3Transfer\\Models\\AbstractResumableTransfer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/AbstractResumableTransfer.php', + 'Aws\\S3\\S3Transfer\\Models\\AbstractTransferRequest' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/AbstractTransferRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\DownloadDirectoryRequest' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/DownloadDirectoryRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\DownloadDirectoryResult' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/DownloadDirectoryResult.php', + 'Aws\\S3\\S3Transfer\\Models\\DownloadFileRequest' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/DownloadFileRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\DownloadRequest' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/DownloadRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\DownloadResult' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/DownloadResult.php', + 'Aws\\S3\\S3Transfer\\Models\\ResumableDownload' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/ResumableDownload.php', + 'Aws\\S3\\S3Transfer\\Models\\ResumableUpload' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/ResumableUpload.php', + 'Aws\\S3\\S3Transfer\\Models\\ResumeDownloadRequest' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/ResumeDownloadRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\ResumeUploadRequest' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/ResumeUploadRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\S3TransferManagerConfig' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/S3TransferManagerConfig.php', + 'Aws\\S3\\S3Transfer\\Models\\UploadDirectoryRequest' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/UploadDirectoryRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\UploadDirectoryResult' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/UploadDirectoryResult.php', + 'Aws\\S3\\S3Transfer\\Models\\UploadRequest' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/UploadRequest.php', + 'Aws\\S3\\S3Transfer\\Models\\UploadResult' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Models/UploadResult.php', + 'Aws\\S3\\S3Transfer\\MultipartUploader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/MultipartUploader.php', + 'Aws\\S3\\S3Transfer\\PartGetMultipartDownloader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/PartGetMultipartDownloader.php', + 'Aws\\S3\\S3Transfer\\Progress\\AbstractProgressBarFormat' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/AbstractProgressBarFormat.php', + 'Aws\\S3\\S3Transfer\\Progress\\AbstractTransferListener' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/AbstractTransferListener.php', + 'Aws\\S3\\S3Transfer\\Progress\\ColoredTransferProgressBarFormat' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/ColoredTransferProgressBarFormat.php', + 'Aws\\S3\\S3Transfer\\Progress\\ConsoleProgressBar' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/ConsoleProgressBar.php', + 'Aws\\S3\\S3Transfer\\Progress\\DirectoryProgressTracker' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/DirectoryProgressTracker.php', + 'Aws\\S3\\S3Transfer\\Progress\\DirectoryTransferProgressAggregator' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/DirectoryTransferProgressAggregator.php', + 'Aws\\S3\\S3Transfer\\Progress\\DirectoryTransferProgressSnapshot' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/DirectoryTransferProgressSnapshot.php', + 'Aws\\S3\\S3Transfer\\Progress\\MultiProgressBarFormat' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/MultiProgressBarFormat.php', + 'Aws\\S3\\S3Transfer\\Progress\\MultiProgressTracker' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/MultiProgressTracker.php', + 'Aws\\S3\\S3Transfer\\Progress\\PlainProgressBarFormat' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/PlainProgressBarFormat.php', + 'Aws\\S3\\S3Transfer\\Progress\\ProgressBarFactoryInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/ProgressBarFactoryInterface.php', + 'Aws\\S3\\S3Transfer\\Progress\\ProgressBarInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/ProgressBarInterface.php', + 'Aws\\S3\\S3Transfer\\Progress\\ProgressTrackerInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/ProgressTrackerInterface.php', + 'Aws\\S3\\S3Transfer\\Progress\\SingleProgressTracker' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/SingleProgressTracker.php', + 'Aws\\S3\\S3Transfer\\Progress\\TransferListenerNotifier' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/TransferListenerNotifier.php', + 'Aws\\S3\\S3Transfer\\Progress\\TransferProgressBarFormat' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/TransferProgressBarFormat.php', + 'Aws\\S3\\S3Transfer\\Progress\\TransferProgressSnapshot' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Progress/TransferProgressSnapshot.php', + 'Aws\\S3\\S3Transfer\\RangeGetMultipartDownloader' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/RangeGetMultipartDownloader.php', + 'Aws\\S3\\S3Transfer\\S3TransferManager' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/S3TransferManager.php', + 'Aws\\S3\\S3Transfer\\Utils\\AbstractDownloadHandler' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Utils/AbstractDownloadHandler.php', + 'Aws\\S3\\S3Transfer\\Utils\\FileDownloadHandler' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Utils/FileDownloadHandler.php', + 'Aws\\S3\\S3Transfer\\Utils\\ResumableDownloadHandlerInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Utils/ResumableDownloadHandlerInterface.php', + 'Aws\\S3\\S3Transfer\\Utils\\StreamDownloadHandler' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3Transfer/Utils/StreamDownloadHandler.php', + 'Aws\\S3\\S3UriParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/S3UriParser.php', + 'Aws\\S3\\SSECMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/SSECMiddleware.php', + 'Aws\\S3\\StreamWrapper' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/StreamWrapper.php', + 'Aws\\S3\\Transfer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/Transfer.php', + 'Aws\\S3\\UseArnRegion\\Configuration' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/UseArnRegion/Configuration.php', + 'Aws\\S3\\UseArnRegion\\ConfigurationInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/UseArnRegion/ConfigurationInterface.php', + 'Aws\\S3\\UseArnRegion\\ConfigurationProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/UseArnRegion/ConfigurationProvider.php', + 'Aws\\S3\\UseArnRegion\\Exception\\ConfigurationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/UseArnRegion/Exception/ConfigurationException.php', + 'Aws\\S3\\ValidateResponseChecksumParser' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/S3/ValidateResponseChecksumParser.php', + 'Aws\\SSMContacts\\Exception\\SSMContactsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSMContacts/Exception/SSMContactsException.php', + 'Aws\\SSMContacts\\SSMContactsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSMContacts/SSMContactsClient.php', + 'Aws\\SSMGuiConnect\\Exception\\SSMGuiConnectException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSMGuiConnect/Exception/SSMGuiConnectException.php', + 'Aws\\SSMGuiConnect\\SSMGuiConnectClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSMGuiConnect/SSMGuiConnectClient.php', + 'Aws\\SSMIncidents\\Exception\\SSMIncidentsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSMIncidents/Exception/SSMIncidentsException.php', + 'Aws\\SSMIncidents\\SSMIncidentsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSMIncidents/SSMIncidentsClient.php', + 'Aws\\SSMQuickSetup\\Exception\\SSMQuickSetupException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSMQuickSetup/Exception/SSMQuickSetupException.php', + 'Aws\\SSMQuickSetup\\SSMQuickSetupClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSMQuickSetup/SSMQuickSetupClient.php', + 'Aws\\SSOAdmin\\Exception\\SSOAdminException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSOAdmin/Exception/SSOAdminException.php', + 'Aws\\SSOAdmin\\SSOAdminClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSOAdmin/SSOAdminClient.php', + 'Aws\\SSOOIDC\\Exception\\SSOOIDCException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSOOIDC/Exception/SSOOIDCException.php', + 'Aws\\SSOOIDC\\SSOOIDCClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSOOIDC/SSOOIDCClient.php', + 'Aws\\SSO\\Exception\\SSOException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSO/Exception/SSOException.php', + 'Aws\\SSO\\SSOClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SSO/SSOClient.php', + 'Aws\\SageMakerFeatureStoreRuntime\\Exception\\SageMakerFeatureStoreRuntimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SageMakerFeatureStoreRuntime/Exception/SageMakerFeatureStoreRuntimeException.php', + 'Aws\\SageMakerFeatureStoreRuntime\\SageMakerFeatureStoreRuntimeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SageMakerFeatureStoreRuntime/SageMakerFeatureStoreRuntimeClient.php', + 'Aws\\SageMakerGeospatial\\Exception\\SageMakerGeospatialException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SageMakerGeospatial/Exception/SageMakerGeospatialException.php', + 'Aws\\SageMakerGeospatial\\SageMakerGeospatialClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SageMakerGeospatial/SageMakerGeospatialClient.php', + 'Aws\\SageMakerMetrics\\Exception\\SageMakerMetricsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SageMakerMetrics/Exception/SageMakerMetricsException.php', + 'Aws\\SageMakerMetrics\\SageMakerMetricsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SageMakerMetrics/SageMakerMetricsClient.php', + 'Aws\\SageMakerRuntime\\Exception\\SageMakerRuntimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SageMakerRuntime/Exception/SageMakerRuntimeException.php', + 'Aws\\SageMakerRuntime\\SageMakerRuntimeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SageMakerRuntime/SageMakerRuntimeClient.php', + 'Aws\\SageMaker\\Exception\\SageMakerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SageMaker/Exception/SageMakerException.php', + 'Aws\\SageMaker\\SageMakerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SageMaker/SageMakerClient.php', + 'Aws\\SagemakerEdgeManager\\Exception\\SagemakerEdgeManagerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SagemakerEdgeManager/Exception/SagemakerEdgeManagerException.php', + 'Aws\\SagemakerEdgeManager\\SagemakerEdgeManagerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SagemakerEdgeManager/SagemakerEdgeManagerClient.php', + 'Aws\\SagemakerJobRuntime\\Exception\\SagemakerJobRuntimeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SagemakerJobRuntime/Exception/SagemakerJobRuntimeException.php', + 'Aws\\SagemakerJobRuntime\\SagemakerJobRuntimeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SagemakerJobRuntime/SagemakerJobRuntimeClient.php', + 'Aws\\SavingsPlans\\Exception\\SavingsPlansException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SavingsPlans/Exception/SavingsPlansException.php', + 'Aws\\SavingsPlans\\SavingsPlansClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SavingsPlans/SavingsPlansClient.php', + 'Aws\\Scheduler\\Exception\\SchedulerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Scheduler/Exception/SchedulerException.php', + 'Aws\\Scheduler\\SchedulerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Scheduler/SchedulerClient.php', + 'Aws\\Schemas\\Exception\\SchemasException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Schemas/Exception/SchemasException.php', + 'Aws\\Schemas\\SchemasClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Schemas/SchemasClient.php', + 'Aws\\Script\\Composer\\Composer' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Script/Composer/Composer.php', + 'Aws\\Sdk' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sdk.php', + 'Aws\\SecretsManager\\Exception\\SecretsManagerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SecretsManager/Exception/SecretsManagerException.php', + 'Aws\\SecretsManager\\SecretsManagerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SecretsManager/SecretsManagerClient.php', + 'Aws\\SecurityAgent\\Exception\\SecurityAgentException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SecurityAgent/Exception/SecurityAgentException.php', + 'Aws\\SecurityAgent\\SecurityAgentClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SecurityAgent/SecurityAgentClient.php', + 'Aws\\SecurityHub\\Exception\\SecurityHubException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SecurityHub/Exception/SecurityHubException.php', + 'Aws\\SecurityHub\\SecurityHubClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SecurityHub/SecurityHubClient.php', + 'Aws\\SecurityIR\\Exception\\SecurityIRException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SecurityIR/Exception/SecurityIRException.php', + 'Aws\\SecurityIR\\SecurityIRClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SecurityIR/SecurityIRClient.php', + 'Aws\\SecurityLake\\Exception\\SecurityLakeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SecurityLake/Exception/SecurityLakeException.php', + 'Aws\\SecurityLake\\SecurityLakeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SecurityLake/SecurityLakeClient.php', + 'Aws\\ServerlessApplicationRepository\\Exception\\ServerlessApplicationRepositoryException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ServerlessApplicationRepository/Exception/ServerlessApplicationRepositoryException.php', + 'Aws\\ServerlessApplicationRepository\\ServerlessApplicationRepositoryClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ServerlessApplicationRepository/ServerlessApplicationRepositoryClient.php', + 'Aws\\ServiceCatalog\\Exception\\ServiceCatalogException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ServiceCatalog/Exception/ServiceCatalogException.php', + 'Aws\\ServiceCatalog\\ServiceCatalogClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ServiceCatalog/ServiceCatalogClient.php', + 'Aws\\ServiceDiscovery\\Exception\\ServiceDiscoveryException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ServiceDiscovery/Exception/ServiceDiscoveryException.php', + 'Aws\\ServiceDiscovery\\ServiceDiscoveryClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ServiceDiscovery/ServiceDiscoveryClient.php', + 'Aws\\ServiceQuotas\\Exception\\ServiceQuotasException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ServiceQuotas/Exception/ServiceQuotasException.php', + 'Aws\\ServiceQuotas\\ServiceQuotasClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ServiceQuotas/ServiceQuotasClient.php', + 'Aws\\SesV2\\Exception\\SesV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SesV2/Exception/SesV2Exception.php', + 'Aws\\SesV2\\SesV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SesV2/SesV2Client.php', + 'Aws\\Ses\\Exception\\SesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Ses/Exception/SesException.php', + 'Aws\\Ses\\SesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Ses/SesClient.php', + 'Aws\\Sfn\\Exception\\SfnException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sfn/Exception/SfnException.php', + 'Aws\\Sfn\\SfnClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sfn/SfnClient.php', + 'Aws\\Shield\\Exception\\ShieldException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Shield/Exception/ShieldException.php', + 'Aws\\Shield\\ShieldClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Shield/ShieldClient.php', + 'Aws\\Signature\\AnonymousSignature' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Signature/AnonymousSignature.php', + 'Aws\\Signature\\DpopSignature' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Signature/DpopSignature.php', + 'Aws\\Signature\\S3ExpressSignature' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Signature/S3ExpressSignature.php', + 'Aws\\Signature\\S3SignatureV4' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Signature/S3SignatureV4.php', + 'Aws\\Signature\\SignatureInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Signature/SignatureInterface.php', + 'Aws\\Signature\\SignatureProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Signature/SignatureProvider.php', + 'Aws\\Signature\\SignatureTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Signature/SignatureTrait.php', + 'Aws\\Signature\\SignatureV4' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Signature/SignatureV4.php', + 'Aws\\SignerData\\Exception\\SignerDataException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SignerData/Exception/SignerDataException.php', + 'Aws\\SignerData\\SignerDataClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SignerData/SignerDataClient.php', + 'Aws\\Signin\\Exception\\SigninException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Signin/Exception/SigninException.php', + 'Aws\\Signin\\SigninClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Signin/SigninClient.php', + 'Aws\\SimpleDBv2\\Exception\\SimpleDBv2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SimpleDBv2/Exception/SimpleDBv2Exception.php', + 'Aws\\SimpleDBv2\\SimpleDBv2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SimpleDBv2/SimpleDBv2Client.php', + 'Aws\\SnowBall\\Exception\\SnowBallException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SnowBall/Exception/SnowBallException.php', + 'Aws\\SnowBall\\SnowBallClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SnowBall/SnowBallClient.php', + 'Aws\\SnowDeviceManagement\\Exception\\SnowDeviceManagementException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SnowDeviceManagement/Exception/SnowDeviceManagementException.php', + 'Aws\\SnowDeviceManagement\\SnowDeviceManagementClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SnowDeviceManagement/SnowDeviceManagementClient.php', + 'Aws\\Sns\\Exception\\SnsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sns/Exception/SnsException.php', + 'Aws\\Sns\\SnsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sns/SnsClient.php', + 'Aws\\SocialMessaging\\Exception\\SocialMessagingException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SocialMessaging/Exception/SocialMessagingException.php', + 'Aws\\SocialMessaging\\SocialMessagingClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SocialMessaging/SocialMessagingClient.php', + 'Aws\\Sqs\\Exception\\SqsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sqs/Exception/SqsException.php', + 'Aws\\Sqs\\SqsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sqs/SqsClient.php', + 'Aws\\SsmSap\\Exception\\SsmSapException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SsmSap/Exception/SsmSapException.php', + 'Aws\\SsmSap\\SsmSapClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SsmSap/SsmSapClient.php', + 'Aws\\Ssm\\Exception\\SsmException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Ssm/Exception/SsmException.php', + 'Aws\\Ssm\\SsmClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Ssm/SsmClient.php', + 'Aws\\StorageGateway\\Exception\\StorageGatewayException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/StorageGateway/Exception/StorageGatewayException.php', + 'Aws\\StorageGateway\\StorageGatewayClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/StorageGateway/StorageGatewayClient.php', + 'Aws\\StreamRequestPayloadMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/StreamRequestPayloadMiddleware.php', + 'Aws\\Sts\\Exception\\StsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sts/Exception/StsException.php', + 'Aws\\Sts\\RegionalEndpoints\\Configuration' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sts/RegionalEndpoints/Configuration.php', + 'Aws\\Sts\\RegionalEndpoints\\ConfigurationInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sts/RegionalEndpoints/ConfigurationInterface.php', + 'Aws\\Sts\\RegionalEndpoints\\ConfigurationProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sts/RegionalEndpoints/ConfigurationProvider.php', + 'Aws\\Sts\\RegionalEndpoints\\Exception\\ConfigurationException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sts/RegionalEndpoints/Exception/ConfigurationException.php', + 'Aws\\Sts\\StsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sts/StsClient.php', + 'Aws\\SupplyChain\\Exception\\SupplyChainException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SupplyChain/Exception/SupplyChainException.php', + 'Aws\\SupplyChain\\SupplyChainClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SupplyChain/SupplyChainClient.php', + 'Aws\\SupportApp\\Exception\\SupportAppException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SupportApp/Exception/SupportAppException.php', + 'Aws\\SupportApp\\SupportAppClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SupportApp/SupportAppClient.php', + 'Aws\\SupportAuthZ\\Exception\\SupportAuthZException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SupportAuthZ/Exception/SupportAuthZException.php', + 'Aws\\SupportAuthZ\\SupportAuthZClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/SupportAuthZ/SupportAuthZClient.php', + 'Aws\\Support\\Exception\\SupportException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Support/Exception/SupportException.php', + 'Aws\\Support\\SupportClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Support/SupportClient.php', + 'Aws\\Sustainability\\Exception\\SustainabilityException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sustainability/Exception/SustainabilityException.php', + 'Aws\\Sustainability\\SustainabilityClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Sustainability/SustainabilityClient.php', + 'Aws\\Swf\\Exception\\SwfException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Swf/Exception/SwfException.php', + 'Aws\\Swf\\SwfClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Swf/SwfClient.php', + 'Aws\\Synthetics\\Exception\\SyntheticsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Synthetics/Exception/SyntheticsException.php', + 'Aws\\Synthetics\\SyntheticsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Synthetics/SyntheticsClient.php', + 'Aws\\TaxSettings\\Exception\\TaxSettingsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TaxSettings/Exception/TaxSettingsException.php', + 'Aws\\TaxSettings\\TaxSettingsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TaxSettings/TaxSettingsClient.php', + 'Aws\\Textract\\Exception\\TextractException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Textract/Exception/TextractException.php', + 'Aws\\Textract\\TextractClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Textract/TextractClient.php', + 'Aws\\TimestreamInfluxDB\\Exception\\TimestreamInfluxDBException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TimestreamInfluxDB/Exception/TimestreamInfluxDBException.php', + 'Aws\\TimestreamInfluxDB\\TimestreamInfluxDBClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TimestreamInfluxDB/TimestreamInfluxDBClient.php', + 'Aws\\TimestreamQuery\\Exception\\TimestreamQueryException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TimestreamQuery/Exception/TimestreamQueryException.php', + 'Aws\\TimestreamQuery\\TimestreamQueryClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TimestreamQuery/TimestreamQueryClient.php', + 'Aws\\TimestreamWrite\\Exception\\TimestreamWriteException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TimestreamWrite/Exception/TimestreamWriteException.php', + 'Aws\\TimestreamWrite\\TimestreamWriteClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TimestreamWrite/TimestreamWriteClient.php', + 'Aws\\Tnb\\Exception\\TnbException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Tnb/Exception/TnbException.php', + 'Aws\\Tnb\\TnbClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Tnb/TnbClient.php', + 'Aws\\Token\\BearerTokenAuthorization' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/BearerTokenAuthorization.php', + 'Aws\\Token\\BedrockTokenProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/BedrockTokenProvider.php', + 'Aws\\Token\\ParsesIniTrait' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/ParsesIniTrait.php', + 'Aws\\Token\\RefreshableTokenProviderInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/RefreshableTokenProviderInterface.php', + 'Aws\\Token\\SsoToken' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/SsoToken.php', + 'Aws\\Token\\SsoTokenProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/SsoTokenProvider.php', + 'Aws\\Token\\Token' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/Token.php', + 'Aws\\Token\\TokenAuthorization' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/TokenAuthorization.php', + 'Aws\\Token\\TokenInterface' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/TokenInterface.php', + 'Aws\\Token\\TokenProvider' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/TokenProvider.php', + 'Aws\\Token\\TokenSource' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Token/TokenSource.php', + 'Aws\\TraceMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TraceMiddleware.php', + 'Aws\\TranscribeService\\Exception\\TranscribeServiceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TranscribeService/Exception/TranscribeServiceException.php', + 'Aws\\TranscribeService\\TranscribeServiceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TranscribeService/TranscribeServiceClient.php', + 'Aws\\Transfer\\Exception\\TransferException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Transfer/Exception/TransferException.php', + 'Aws\\Transfer\\TransferClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Transfer/TransferClient.php', + 'Aws\\Translate\\Exception\\TranslateException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Translate/Exception/TranslateException.php', + 'Aws\\Translate\\TranslateClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Translate/TranslateClient.php', + 'Aws\\TrustedAdvisor\\Exception\\TrustedAdvisorException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TrustedAdvisor/Exception/TrustedAdvisorException.php', + 'Aws\\TrustedAdvisor\\TrustedAdvisorClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/TrustedAdvisor/TrustedAdvisorClient.php', + 'Aws\\UserAgentMiddleware' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/UserAgentMiddleware.php', + 'Aws\\Uxc\\Exception\\UxcException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Uxc/Exception/UxcException.php', + 'Aws\\Uxc\\UxcClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Uxc/UxcClient.php', + 'Aws\\VPCLattice\\Exception\\VPCLatticeException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/VPCLattice/Exception/VPCLatticeException.php', + 'Aws\\VPCLattice\\VPCLatticeClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/VPCLattice/VPCLatticeClient.php', + 'Aws\\VerifiedPermissions\\Exception\\VerifiedPermissionsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/VerifiedPermissions/Exception/VerifiedPermissionsException.php', + 'Aws\\VerifiedPermissions\\VerifiedPermissionsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/VerifiedPermissions/VerifiedPermissionsClient.php', + 'Aws\\VoiceID\\Exception\\VoiceIDException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/VoiceID/Exception/VoiceIDException.php', + 'Aws\\VoiceID\\VoiceIDClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/VoiceID/VoiceIDClient.php', + 'Aws\\WAFV2\\Exception\\WAFV2Exception' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WAFV2/Exception/WAFV2Exception.php', + 'Aws\\WAFV2\\WAFV2Client' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WAFV2/WAFV2Client.php', + 'Aws\\WafRegional\\Exception\\WafRegionalException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WafRegional/Exception/WafRegionalException.php', + 'Aws\\WafRegional\\WafRegionalClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WafRegional/WafRegionalClient.php', + 'Aws\\Waf\\Exception\\WafException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Waf/Exception/WafException.php', + 'Aws\\Waf\\WafClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Waf/WafClient.php', + 'Aws\\Waiter' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Waiter.php', + 'Aws\\WellArchitected\\Exception\\WellArchitectedException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WellArchitected/Exception/WellArchitectedException.php', + 'Aws\\WellArchitected\\WellArchitectedClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WellArchitected/WellArchitectedClient.php', + 'Aws\\Wickr\\Exception\\WickrException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Wickr/Exception/WickrException.php', + 'Aws\\Wickr\\WickrClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/Wickr/WickrClient.php', + 'Aws\\WorkDocs\\Exception\\WorkDocsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkDocs/Exception/WorkDocsException.php', + 'Aws\\WorkDocs\\WorkDocsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkDocs/WorkDocsClient.php', + 'Aws\\WorkMailMessageFlow\\Exception\\WorkMailMessageFlowException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkMailMessageFlow/Exception/WorkMailMessageFlowException.php', + 'Aws\\WorkMailMessageFlow\\WorkMailMessageFlowClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkMailMessageFlow/WorkMailMessageFlowClient.php', + 'Aws\\WorkMail\\Exception\\WorkMailException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkMail/Exception/WorkMailException.php', + 'Aws\\WorkMail\\WorkMailClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkMail/WorkMailClient.php', + 'Aws\\WorkSpacesThinClient\\Exception\\WorkSpacesThinClientException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkSpacesThinClient/Exception/WorkSpacesThinClientException.php', + 'Aws\\WorkSpacesThinClient\\WorkSpacesThinClientClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkSpacesThinClient/WorkSpacesThinClientClient.php', + 'Aws\\WorkSpacesWeb\\Exception\\WorkSpacesWebException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkSpacesWeb/Exception/WorkSpacesWebException.php', + 'Aws\\WorkSpacesWeb\\WorkSpacesWebClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkSpacesWeb/WorkSpacesWebClient.php', + 'Aws\\WorkSpaces\\Exception\\WorkSpacesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkSpaces/Exception/WorkSpacesException.php', + 'Aws\\WorkSpaces\\WorkSpacesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkSpaces/WorkSpacesClient.php', + 'Aws\\WorkspacesInstances\\Exception\\WorkspacesInstancesException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkspacesInstances/Exception/WorkspacesInstancesException.php', + 'Aws\\WorkspacesInstances\\WorkspacesInstancesClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WorkspacesInstances/WorkspacesInstancesClient.php', + 'Aws\\WrappedHttpHandler' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/WrappedHttpHandler.php', + 'Aws\\XRay\\Exception\\XRayException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/XRay/Exception/XRayException.php', + 'Aws\\XRay\\XRayClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/XRay/XRayClient.php', + 'Aws\\drs\\Exception\\drsException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/drs/Exception/drsException.php', + 'Aws\\drs\\drsClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/drs/drsClient.php', + 'Aws\\finspace\\Exception\\finspaceException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/finspace/Exception/finspaceException.php', + 'Aws\\finspace\\finspaceClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/finspace/finspaceClient.php', + 'Aws\\imagebuilder\\Exception\\imagebuilderException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/imagebuilder/Exception/imagebuilderException.php', + 'Aws\\imagebuilder\\imagebuilderClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/imagebuilder/imagebuilderClient.php', + 'Aws\\ivschat\\Exception\\ivschatException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ivschat/Exception/ivschatException.php', + 'Aws\\ivschat\\ivschatClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/ivschat/ivschatClient.php', + 'Aws\\kendra\\Exception\\kendraException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/kendra/Exception/kendraException.php', + 'Aws\\kendra\\kendraClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/kendra/kendraClient.php', + 'Aws\\mgn\\Exception\\mgnException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/mgn/Exception/mgnException.php', + 'Aws\\mgn\\mgnClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/mgn/mgnClient.php', + 'Aws\\signer\\Exception\\signerException' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/signer/Exception/signerException.php', + 'Aws\\signer\\signerClient' => __DIR__ . '/..' . '/aws/aws-sdk-php/src/signer/signerClient.php', 'Brick\\Math\\BigDecimal' => __DIR__ . '/..' . '/brick/math/src/BigDecimal.php', 'Brick\\Math\\BigInteger' => __DIR__ . '/..' . '/brick/math/src/BigInteger.php', 'Brick\\Math\\BigNumber' => __DIR__ . '/..' . '/brick/math/src/BigNumber.php', @@ -701,6 +2084,8 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Cron\\MonthField' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/MonthField.php', 'Database\\Factories\\UserFactory' => __DIR__ . '/../..' . '/database/factories/UserFactory.php', 'Database\\Seeders\\DatabaseSeeder' => __DIR__ . '/../..' . '/database/seeders/DatabaseSeeder.php', + 'Database\\Seeders\\MenuListSeeder' => __DIR__ . '/../..' . '/database/seeders/MenuListSeeder.php', + 'Database\\Seeders\\TicketTypeSeeder' => __DIR__ . '/../..' . '/database/seeders/TicketTypeSeeder.php', 'DateError' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateError.php', 'DateException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateException.php', 'DateInvalidOperationException' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/DateInvalidOperationException.php', @@ -1880,14 +3265,18 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'GuzzleHttp\\Handler\\CurlHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlHandler.php', 'GuzzleHttp\\Handler\\CurlMultiHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php', 'GuzzleHttp\\Handler\\CurlShareHandleState' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlShareHandleState.php', + 'GuzzleHttp\\Handler\\CurlVersion' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/CurlVersion.php', 'GuzzleHttp\\Handler\\EasyHandle' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/EasyHandle.php', 'GuzzleHttp\\Handler\\HeaderProcessor' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php', 'GuzzleHttp\\Handler\\MockHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/MockHandler.php', 'GuzzleHttp\\Handler\\Proxy' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/Proxy.php', + 'GuzzleHttp\\Handler\\ProxyEnvironment' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/ProxyEnvironment.php', 'GuzzleHttp\\Handler\\StreamHandler' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/StreamHandler.php', + 'GuzzleHttp\\Handler\\TlsVersion' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Handler/TlsVersion.php', 'GuzzleHttp\\MessageFormatter' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/MessageFormatter.php', 'GuzzleHttp\\MessageFormatterInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/MessageFormatterInterface.php', 'GuzzleHttp\\Middleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Middleware.php', + 'GuzzleHttp\\Multiplexing' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Multiplexing.php', 'GuzzleHttp\\Pool' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Pool.php', 'GuzzleHttp\\PrepareBodyMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/PrepareBodyMiddleware.php', 'GuzzleHttp\\Promise\\AggregateException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/AggregateException.php', @@ -3468,6 +4857,17 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Illuminate\\View\\ViewFinderInterface' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewFinderInterface.php', 'Illuminate\\View\\ViewName' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewName.php', 'Illuminate\\View\\ViewServiceProvider' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/View/ViewServiceProvider.php', + 'JmesPath\\AstRuntime' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/AstRuntime.php', + 'JmesPath\\CompilerRuntime' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/CompilerRuntime.php', + 'JmesPath\\DebugRuntime' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/DebugRuntime.php', + 'JmesPath\\Env' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/Env.php', + 'JmesPath\\FnDispatcher' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/FnDispatcher.php', + 'JmesPath\\Lexer' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/Lexer.php', + 'JmesPath\\Parser' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/Parser.php', + 'JmesPath\\SyntaxErrorException' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/SyntaxErrorException.php', + 'JmesPath\\TreeCompiler' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/TreeCompiler.php', + 'JmesPath\\TreeInterpreter' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/TreeInterpreter.php', + 'JmesPath\\Utils' => __DIR__ . '/..' . '/mtdowling/jmespath.php/src/Utils.php', 'Laravel\\Pail\\Console\\Commands\\PailCommand' => __DIR__ . '/..' . '/laravel/pail/src/Console/Commands/PailCommand.php', 'Laravel\\Pail\\Contracts\\Printer' => __DIR__ . '/..' . '/laravel/pail/src/Contracts/Printer.php', 'Laravel\\Pail\\File' => __DIR__ . '/..' . '/laravel/pail/src/File.php', @@ -3910,6 +5310,9 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'League\\Config\\MutableConfigurationInterface' => __DIR__ . '/..' . '/league/config/src/MutableConfigurationInterface.php', 'League\\Config\\ReadOnlyConfiguration' => __DIR__ . '/..' . '/league/config/src/ReadOnlyConfiguration.php', 'League\\Config\\SchemaBuilderInterface' => __DIR__ . '/..' . '/league/config/src/SchemaBuilderInterface.php', + 'League\\Flysystem\\AwsS3V3\\AwsS3V3Adapter' => __DIR__ . '/..' . '/league/flysystem-aws-s3-v3/AwsS3V3Adapter.php', + 'League\\Flysystem\\AwsS3V3\\PortableVisibilityConverter' => __DIR__ . '/..' . '/league/flysystem-aws-s3-v3/PortableVisibilityConverter.php', + 'League\\Flysystem\\AwsS3V3\\VisibilityConverter' => __DIR__ . '/..' . '/league/flysystem-aws-s3-v3/VisibilityConverter.php', 'League\\Flysystem\\CalculateChecksumFromStream' => __DIR__ . '/..' . '/league/flysystem/src/CalculateChecksumFromStream.php', 'League\\Flysystem\\ChecksumAlgoIsNotSupported' => __DIR__ . '/..' . '/league/flysystem/src/ChecksumAlgoIsNotSupported.php', 'League\\Flysystem\\ChecksumProvider' => __DIR__ . '/..' . '/league/flysystem/src/ChecksumProvider.php', @@ -6740,6 +8143,14 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventSubscriberInterface.php', 'Symfony\\Component\\EventDispatcher\\GenericEvent' => __DIR__ . '/..' . '/symfony/event-dispatcher/GenericEvent.php', 'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ImmutableEventDispatcher.php', + 'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/ExceptionInterface.php', + 'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/FileNotFoundException.php', + 'Symfony\\Component\\Filesystem\\Exception\\IOException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOException.php', + 'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOExceptionInterface.php', + 'Symfony\\Component\\Filesystem\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/InvalidArgumentException.php', + 'Symfony\\Component\\Filesystem\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/RuntimeException.php', + 'Symfony\\Component\\Filesystem\\Filesystem' => __DIR__ . '/..' . '/symfony/filesystem/Filesystem.php', + 'Symfony\\Component\\Filesystem\\Path' => __DIR__ . '/..' . '/symfony/filesystem/Path.php', 'Symfony\\Component\\Finder\\Comparator\\Comparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/Comparator.php', 'Symfony\\Component\\Finder\\Comparator\\DateComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/DateComparator.php', 'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/NumberComparator.php', @@ -7571,6 +8982,7 @@ class ComposerStaticInitd8f67b824a1bf31c8999ead558d9b485 'Termwind\\ValueObjects\\Style' => __DIR__ . '/..' . '/nunomaduro/termwind/src/ValueObjects/Style.php', 'Termwind\\ValueObjects\\Styles' => __DIR__ . '/..' . '/nunomaduro/termwind/src/ValueObjects/Styles.php', 'Tests\\Feature\\ExampleTest' => __DIR__ . '/../..' . '/tests/Feature/ExampleTest.php', + 'Tests\\Feature\\MenuAccessTest' => __DIR__ . '/../..' . '/tests/Feature/MenuAccessTest.php', 'Tests\\TestCase' => __DIR__ . '/../..' . '/tests/TestCase.php', 'Tests\\Unit\\ExampleTest' => __DIR__ . '/../..' . '/tests/Unit/ExampleTest.php', 'TheSeer\\Tokenizer\\Exception' => __DIR__ . '/..' . '/theseer/tokenizer/src/Exception.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 621a62b..e91bdd1 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1,5 +1,162 @@ { "packages": [ + { + "name": "aws/aws-crt-php", + "version": "v1.2.7", + "version_normalized": "1.2.7.0", + "source": { + "type": "git", + "url": "https://github.com/awslabs/aws-crt-php.git", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/awslabs/aws-crt-php/zipball/d71d9906c7bb63a28295447ba12e74723bd3730e", + "reference": "d71d9906c7bb63a28295447ba12e74723bd3730e", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35||^5.6.3||^9.5", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "ext-awscrt": "Make sure you install awscrt native extension to use any of the functionality." + }, + "time": "2024-10-18T22:15:13+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "AWS SDK Common Runtime Team", + "email": "aws-sdk-common-runtime@amazon.com" + } + ], + "description": "AWS Common Runtime for PHP", + "homepage": "https://github.com/awslabs/aws-crt-php", + "keywords": [ + "amazon", + "aws", + "crt", + "sdk" + ], + "support": { + "issues": "https://github.com/awslabs/aws-crt-php/issues", + "source": "https://github.com/awslabs/aws-crt-php/tree/v1.2.7" + }, + "install-path": "../aws/aws-crt-php" + }, + { + "name": "aws/aws-sdk-php", + "version": "3.389.0", + "version_normalized": "3.389.0.0", + "source": { + "type": "git", + "url": "https://github.com/aws/aws-sdk-php.git", + "reference": "e6e6649e58826c7edaa9f546f444461a25a22719" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e6e6649e58826c7edaa9f546f444461a25a22719", + "reference": "e6e6649e58826c7edaa9f546f444461a25a22719", + "shasum": "" + }, + "require": { + "aws/aws-crt-php": "^1.2.3", + "ext-json": "*", + "ext-pcre": "*", + "ext-simplexml": "*", + "guzzlehttp/guzzle": "^7.8.2 || ^8.0", + "guzzlehttp/promises": "^2.0.3 || ^3.0", + "guzzlehttp/psr7": "^2.6.3 || ^3.0", + "mtdowling/jmespath.php": "^2.9.1", + "php": ">=8.1", + "psr/http-message": "^1.0 || ^2.0", + "symfony/filesystem": "^v5.4.45 || ^v6.4.3 || ^v7.1.0 || ^v8.0.0" + }, + "require-dev": { + "andrewsville/php-token-reflection": "^1.4", + "aws/aws-php-sns-message-validator": "~1.0", + "behat/behat": "~3.0", + "composer/composer": "^2.7.8", + "dms/phpunit-arraysubset-asserts": "^v0.5.0", + "doctrine/cache": "~1.4", + "ext-dom": "*", + "ext-openssl": "*", + "ext-sockets": "*", + "phpunit/phpunit": "^10.0", + "psr/cache": "^2.0 || ^3.0", + "psr/simple-cache": "^2.0 || ^3.0", + "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0", + "yoast/phpunit-polyfills": "^2.0" + }, + "suggest": { + "aws/aws-php-sns-message-validator": "To validate incoming SNS notifications", + "doctrine/cache": "To use the DoctrineCacheAdapter", + "ext-curl": "To send requests using cURL", + "ext-openssl": "Allows working with CloudFront private distributions and verifying received SNS messages", + "ext-pcntl": "To use client-side monitoring", + "ext-sockets": "To use client-side monitoring" + }, + "time": "2026-07-24T18:05:53+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Aws\\": "src/" + }, + "exclude-from-classmap": [ + "src/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Amazon Web Services", + "homepage": "https://aws.amazon.com" + } + ], + "description": "AWS SDK for PHP - Use Amazon Web Services in your PHP project", + "homepage": "https://aws.amazon.com/sdk-for-php", + "keywords": [ + "amazon", + "aws", + "cloud", + "dynamodb", + "ec2", + "glacier", + "s3", + "sdk" + ], + "support": { + "forum": "https://github.com/aws/aws-sdk-php/discussions", + "issues": "https://github.com/aws/aws-sdk-php/issues", + "source": "https://github.com/aws/aws-sdk-php/tree/3.389.0" + }, + "install-path": "../aws/aws-sdk-php" + }, { "name": "brick/math", "version": "0.14.8", @@ -887,27 +1044,27 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.11.2", - "version_normalized": "7.11.2.0", + "version": "7.15.1", + "version_normalized": "7.15.1.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "bf5f35ad4b774b9d7c5766c02035e865e7e3fdab" + "reference": "61443dfb33c62f308ee8add20f45b4d6e4bf8d2f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/bf5f35ad4b774b9d7c5766c02035e865e7e3fdab", - "reference": "bf5f35ad4b774b9d7c5766c02035e865e7e3fdab", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/61443dfb33c62f308ee8add20f45b4d6e4bf8d2f", + "reference": "61443dfb33c62f308ee8add20f45b4d6e4bf8d2f", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^2.5", - "guzzlehttp/psr7": "^2.11", + "guzzlehttp/promises": "^2.5.1", + "guzzlehttp/psr7": "^2.13", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.24" + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-client-implementation": "1.0" @@ -915,8 +1072,8 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "guzzle/client-integration-tests": "3.0.2", - "guzzlehttp/test-server": "^0.5", + "guzzle/client-integration-tests": "3.0.3", + "guzzlehttp/test-server": "^0.7", "php-http/message-factory": "^1.1", "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" @@ -926,7 +1083,7 @@ "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, - "time": "2026-06-12T21:49:57+00:00", + "time": "2026-07-18T11:23:11+00:00", "type": "library", "extra": { "bamarni-bin": { @@ -998,7 +1155,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.11.2" + "source": "https://github.com/guzzle/guzzle/tree/7.15.1" }, "funding": [ { @@ -1018,17 +1175,17 @@ }, { "name": "guzzlehttp/promises", - "version": "2.5.0", - "version_normalized": "2.5.0.0", + "version": "2.5.1", + "version_normalized": "2.5.1.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "4360e982f87f5f258bf872d094647791db2f4c8e" + "reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e", - "reference": "4360e982f87f5f258bf872d094647791db2f4c8e", + "url": "https://api.github.com/repos/guzzle/promises/zipball/9ad1e4fc607446a055b95870c7f668e93b5cff29", + "reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29", "shasum": "" }, "require": { @@ -1039,7 +1196,7 @@ "bamarni/composer-bin-plugin": "^1.8.2", "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, - "time": "2026-06-02T12:23:43+00:00", + "time": "2026-07-08T15:48:39+00:00", "type": "library", "extra": { "bamarni-bin": { @@ -1085,7 +1242,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.5.0" + "source": "https://github.com/guzzle/promises/tree/2.5.1" }, "funding": [ { @@ -1105,17 +1262,17 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.11.1", - "version_normalized": "2.11.1.0", + "version": "2.13.0", + "version_normalized": "2.13.0.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "640e2897bbee822dbc8af761d49e1a29b1f2a6b1" + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/640e2897bbee822dbc8af761d49e1a29b1f2a6b1", - "reference": "640e2897bbee822dbc8af761d49e1a29b1f2a6b1", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4", + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4", "shasum": "" }, "require": { @@ -1124,7 +1281,7 @@ "psr/http-message": "^1.1 || ^2.0", "ralouphie/getallheaders": "^3.0", "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.24" + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-factory-implementation": "1.0", @@ -1139,7 +1296,7 @@ "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" }, - "time": "2026-06-12T21:50:12+00:00", + "time": "2026-07-16T22:23:49+00:00", "type": "library", "extra": { "bamarni-bin": { @@ -1207,7 +1364,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.11.1" + "source": "https://github.com/guzzle/psr7/tree/2.13.0" }, "funding": [ { @@ -2271,17 +2428,17 @@ }, { "name": "league/flysystem", - "version": "3.34.0", - "version_normalized": "3.34.0.0", + "version": "3.35.2", + "version_normalized": "3.35.2.0", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e" + "reference": "b277b5dc3d56650b68904117124e79c851e12376" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e", - "reference": "2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/b277b5dc3d56650b68904117124e79c851e12376", + "reference": "b277b5dc3d56650b68904117124e79c851e12376", "shasum": "" }, "require": { @@ -2317,7 +2474,7 @@ "phpunit/phpunit": "^9.5.11|^10.0", "sabre/dav": "^4.6.0" }, - "time": "2026-05-14T10:28:08+00:00", + "time": "2026-07-06T14:42:07+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -2351,10 +2508,68 @@ ], "support": { "issues": "https://github.com/thephpleague/flysystem/issues", - "source": "https://github.com/thephpleague/flysystem/tree/3.34.0" + "source": "https://github.com/thephpleague/flysystem/tree/3.35.2" }, "install-path": "../league/flysystem" }, + { + "name": "league/flysystem-aws-s3-v3", + "version": "3.35.2", + "version_normalized": "3.35.2.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git", + "reference": "8475ef9adfc6498b85469e2abec6fe3118cd08c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/8475ef9adfc6498b85469e2abec6fe3118cd08c4", + "reference": "8475ef9adfc6498b85469e2abec6fe3118cd08c4", + "shasum": "" + }, + "require": { + "aws/aws-sdk-php": "^3.371.5", + "league/flysystem": "^3.10.0", + "league/mime-type-detection": "^1.0.0", + "php": "^8.0.2" + }, + "conflict": { + "guzzlehttp/guzzle": "<7.0", + "guzzlehttp/ringphp": "<1.1.1" + }, + "time": "2026-07-01T23:25:49+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "League\\Flysystem\\AwsS3V3\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Frank de Jonge", + "email": "info@frankdejonge.nl" + } + ], + "description": "AWS S3 filesystem adapter for Flysystem.", + "keywords": [ + "Flysystem", + "aws", + "file", + "files", + "filesystem", + "s3", + "storage" + ], + "support": { + "source": "https://github.com/thephpleague/flysystem-aws-s3-v3/tree/3.35.2" + }, + "install-path": "../league/flysystem-aws-s3-v3" + }, { "name": "league/flysystem-local", "version": "3.31.0", @@ -2409,17 +2624,17 @@ }, { "name": "league/mime-type-detection", - "version": "1.16.0", - "version_normalized": "1.16.0.0", + "version": "1.17.0", + "version_normalized": "1.17.0.0", "source": { "type": "git", "url": "https://github.com/thephpleague/mime-type-detection.git", - "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9" + "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/2d6702ff215bf922936ccc1ad31007edc76451b9", - "reference": "2d6702ff215bf922936ccc1ad31007edc76451b9", + "url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/f5f47eff7c48ed1003069a2ca67f316fb4021c76", + "reference": "f5f47eff7c48ed1003069a2ca67f316fb4021c76", "shasum": "" }, "require": { @@ -2429,9 +2644,9 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^3.2", "phpstan/phpstan": "^0.12.68", - "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0" + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0 || ^11.0 || ^12.0" }, - "time": "2024-09-21T08:32:55+00:00", + "time": "2026-07-09T11:49:27+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -2452,7 +2667,7 @@ "description": "Mime-type detection for Flysystem", "support": { "issues": "https://github.com/thephpleague/mime-type-detection/issues", - "source": "https://github.com/thephpleague/mime-type-detection/tree/1.16.0" + "source": "https://github.com/thephpleague/mime-type-detection/tree/1.17.0" }, "funding": [ { @@ -2846,6 +3061,75 @@ ], "install-path": "../monolog/monolog" }, + { + "name": "mtdowling/jmespath.php", + "version": "2.9.2", + "version_normalized": "2.9.2.0", + "source": { + "type": "git", + "url": "https://github.com/jmespath/jmespath.php.git", + "reference": "2157c5e50e813ec6a96c1eed3be7f64a20fb32a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/2157c5e50e813ec6a96c1eed3be7f64a20fb32a8", + "reference": "2157c5e50e813ec6a96c1eed3be7f64a20fb32a8", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "composer/xdebug-handler": "^3.0.3", + "phpunit/phpunit": "^8.5.52" + }, + "time": "2026-07-06T18:56:19+00:00", + "bin": [ + "bin/jp.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.9-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "src/JmesPath.php" + ], + "psr-4": { + "JmesPath\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + } + ], + "description": "Declaratively specify how to extract elements from a JSON document", + "keywords": [ + "json", + "jsonpath" + ], + "support": { + "issues": "https://github.com/jmespath/jmespath.php/issues", + "source": "https://github.com/jmespath/jmespath.php/tree/2.9.2" + }, + "install-path": "../mtdowling/jmespath.php" + }, { "name": "myclabs/deep-copy", "version": "1.13.4", @@ -6289,23 +6573,23 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.7.0", - "version_normalized": "3.7.0.0", + "version": "v3.7.1", + "version_normalized": "3.7.1.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", - "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/f3202fa1b5097b0af062dc978b32ecf63404e31d", + "reference": "f3202fa1b5097b0af062dc978b32ecf63404e31d", "shasum": "" }, "require": { "php": ">=8.1" }, - "time": "2026-04-13T15:52:40+00:00", + "time": "2026-06-05T06:23:12+00:00", "type": "library", "extra": { "thanks": { @@ -6339,7 +6623,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.1" }, "funding": [ { @@ -6617,6 +6901,79 @@ ], "install-path": "../symfony/event-dispatcher-contracts" }, + { + "name": "symfony/filesystem", + "version": "v7.4.11", + "version_normalized": "7.4.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/d721ea61b4a5fba8c5b6e7c1feda19efea144b50", + "reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^6.4|^7.0|^8.0" + }, + "time": "2026-05-11T16:38:44+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v7.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "install-path": "../symfony/filesystem" + }, { "name": "symfony/finder", "version": "v7.4.8", diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index b231f72..c09f7a8 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,13 +3,31 @@ 'name' => 'laravel/laravel', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '2f805a233dbd854665db766b12582701833e8622', + 'reference' => '2b3592c4c287719f1bacef74685f371b9cdc185f', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), 'dev' => true, ), 'versions' => array( + 'aws/aws-crt-php' => array( + 'pretty_version' => 'v1.2.7', + 'version' => '1.2.7.0', + 'reference' => 'd71d9906c7bb63a28295447ba12e74723bd3730e', + 'type' => 'library', + 'install_path' => __DIR__ . '/../aws/aws-crt-php', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'aws/aws-sdk-php' => array( + 'pretty_version' => '3.389.0', + 'version' => '3.389.0.0', + 'reference' => 'e6e6649e58826c7edaa9f546f444461a25a22719', + 'type' => 'library', + 'install_path' => __DIR__ . '/../aws/aws-sdk-php', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'brick/math' => array( 'pretty_version' => '0.14.8', 'version' => '0.14.8.0', @@ -131,27 +149,27 @@ 'dev_requirement' => false, ), 'guzzlehttp/guzzle' => array( - 'pretty_version' => '7.11.2', - 'version' => '7.11.2.0', - 'reference' => 'bf5f35ad4b774b9d7c5766c02035e865e7e3fdab', + 'pretty_version' => '7.15.1', + 'version' => '7.15.1.0', + 'reference' => '61443dfb33c62f308ee8add20f45b4d6e4bf8d2f', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/guzzle', 'aliases' => array(), 'dev_requirement' => false, ), 'guzzlehttp/promises' => array( - 'pretty_version' => '2.5.0', - 'version' => '2.5.0.0', - 'reference' => '4360e982f87f5f258bf872d094647791db2f4c8e', + 'pretty_version' => '2.5.1', + 'version' => '2.5.1.0', + 'reference' => '9ad1e4fc607446a055b95870c7f668e93b5cff29', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/promises', 'aliases' => array(), 'dev_requirement' => false, ), 'guzzlehttp/psr7' => array( - 'pretty_version' => '2.11.1', - 'version' => '2.11.1.0', - 'reference' => '640e2897bbee822dbc8af761d49e1a29b1f2a6b1', + 'pretty_version' => '2.13.0', + 'version' => '2.13.0.0', + 'reference' => 'dad89620b7a6edb60c15858442eb2e408b45d8f4', 'type' => 'library', 'install_path' => __DIR__ . '/../guzzlehttp/psr7', 'aliases' => array(), @@ -409,7 +427,7 @@ 'laravel/laravel' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '2f805a233dbd854665db766b12582701833e8622', + 'reference' => '2b3592c4c287719f1bacef74685f371b9cdc185f', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -497,14 +515,23 @@ 'dev_requirement' => false, ), 'league/flysystem' => array( - 'pretty_version' => '3.34.0', - 'version' => '3.34.0.0', - 'reference' => '2daaac3b0d4c83ea7ed5d8586e786f5d00f3540e', + 'pretty_version' => '3.35.2', + 'version' => '3.35.2.0', + 'reference' => 'b277b5dc3d56650b68904117124e79c851e12376', 'type' => 'library', 'install_path' => __DIR__ . '/../league/flysystem', 'aliases' => array(), 'dev_requirement' => false, ), + 'league/flysystem-aws-s3-v3' => array( + 'pretty_version' => '3.35.2', + 'version' => '3.35.2.0', + 'reference' => '8475ef9adfc6498b85469e2abec6fe3118cd08c4', + 'type' => 'library', + 'install_path' => __DIR__ . '/../league/flysystem-aws-s3-v3', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'league/flysystem-local' => array( 'pretty_version' => '3.31.0', 'version' => '3.31.0.0', @@ -515,9 +542,9 @@ 'dev_requirement' => false, ), 'league/mime-type-detection' => array( - 'pretty_version' => '1.16.0', - 'version' => '1.16.0.0', - 'reference' => '2d6702ff215bf922936ccc1ad31007edc76451b9', + 'pretty_version' => '1.17.0', + 'version' => '1.17.0.0', + 'reference' => 'f5f47eff7c48ed1003069a2ca67f316fb4021c76', 'type' => 'library', 'install_path' => __DIR__ . '/../league/mime-type-detection', 'aliases' => array(), @@ -565,6 +592,15 @@ 0 => '^1.0', ), ), + 'mtdowling/jmespath.php' => array( + 'pretty_version' => '2.9.2', + 'version' => '2.9.2.0', + 'reference' => '2157c5e50e813ec6a96c1eed3be7f64a20fb32a8', + 'type' => 'library', + 'install_path' => __DIR__ . '/../mtdowling/jmespath.php', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'myclabs/deep-copy' => array( 'pretty_version' => '1.13.4', 'version' => '1.13.4.0', @@ -1068,9 +1104,9 @@ 'dev_requirement' => false, ), 'symfony/deprecation-contracts' => array( - 'pretty_version' => 'v3.7.0', - 'version' => '3.7.0.0', - 'reference' => '50f59d1f3ca46d41ac911f97a78626b6756af35b', + 'pretty_version' => 'v3.7.1', + 'version' => '3.7.1.0', + 'reference' => 'f3202fa1b5097b0af062dc978b32ecf63404e31d', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', 'aliases' => array(), @@ -1109,6 +1145,15 @@ 0 => '2.0|3.0', ), ), + 'symfony/filesystem' => array( + 'pretty_version' => 'v7.4.11', + 'version' => '7.4.11.0', + 'reference' => 'd721ea61b4a5fba8c5b6e7c1feda19efea144b50', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/filesystem', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'symfony/finder' => array( 'pretty_version' => 'v7.4.8', 'version' => '7.4.8.0', diff --git a/vendor/guzzlehttp/guzzle/CHANGELOG.md b/vendor/guzzlehttp/guzzle/CHANGELOG.md index 310fd5c..5cfa467 100644 --- a/vendor/guzzlehttp/guzzle/CHANGELOG.md +++ b/vendor/guzzlehttp/guzzle/CHANGELOG.md @@ -3,6 +3,254 @@ Please refer to [UPGRADING](UPGRADING.md) guide for upgrading to a major version. +## 7.15.1 - 2026-07-18 + +### Security + +- Preserve host-only cookie scope and require explicit persistence markers (GHSA-wm3w-8rrp-j577) +- Bound response cookie admission and generated `Cookie` headers (GHSA-f283-ghqc-fg79) +- Exclude URI fragments from `Referer` headers generated for redirects (GHSA-h95v-h523-3mw8) + + +## 7.15.0 - 2026-07-17 + +### Added + +- Added `Multiplexing::NONE` support as a client, cURL multi handler, and conditional request option + +### Changed + +- Adjusted `guzzlehttp/psr7` version constraint to `^2.13` +- Use locale-independent ASCII folding for all case normalization and comparison +- Bound cURL upload reads to the declared `Content-Length` +- Sanitize the cURL error text exposed through exception handler context +- Fail closed when a named cURL multi connection cap cannot be applied +- Reject the request-level `CURLOPT_SHARE` cURL option when named connection caps are configured +- Strengthen old-libcurl SOCKS isolation for raw `CURLOPT_PRE_PROXY` and opaque share handles +- Isolate HTTP proxy tunnels from opaque shared connection caches +- Trigger runtime deprecations for previously deprecated functionality in 7.1.0 + +### Deprecated + +- Deprecated `Utils::jsonDecode()` and `Utils::jsonEncode()` in favor of native JSON functions +- Deprecated passing `CURLMOPT_PIPELINING` in the cURL multi handler `options` array +- Deprecated passing `CURLOPT_PROXYHEADER` without cURL proxy header separation support + +### Fixed + +- Defer cURL requests created from multi callbacks until native execution unwinds +- Fail synchronous waits from native cURL callbacks promptly instead of self-deadlocking +- Guard cURL multi handle removal against progress callbacks re-entering the handler +- Scope promise waits on the cURL multi handler to the awaited transfer +- Strip `Content-Length` and `Transfer-Encoding` when redirects discard the request body +- Stop re-applying the `delay` request option to followed redirects + + +## 7.14.2 - 2026-07-14 + +### Security + +- Prevent first-class and proxy URL credentials from reaching origins (GHSA-94pj-82f3-465w) + + +## 7.14.1 - 2026-07-13 + +### Changed + +- Adjusted `guzzlehttp/psr7` version constraint to `^2.12.5` + +### Fixed + +- Fail closed when a proxy tunnel isolation cURL option cannot be applied +- Normalize Stringable proxy credential values before computing connection-reuse section signatures +- Restore conservative credential redaction for unparseable proxies with multiple `@` separators +- Redact request URI credentials from the stream handler connection error message +- Reject enabled response streaming (`stream => true`) on cap-configured stream handlers +- Distinguish CurlMultiHandler and StreamHandler outcomes in connection-cap custom-handler guidance +- Reject raw cURL options that conflict with explicit multiplexing guarantees +- Stop explicit multiplexing conflict checks faulting on non-array cURL multi `options` values +- Reject required multiplexing when the final `CURLOPT_HTTPAUTH` mask permits NTLM +- Require an integer `CURLMOPT_PIPELINING` when combined with explicit multiplexing +- Check the required multiplexing cleartext proxy rule against the final cURL configuration +- Bound cURL multi handler blocking selects by the earliest pending request delay +- Stop synchronous cURL multi handler waits blocking on other transfers once the target has settled +- Stop cURL multi completion processing double-settling promises canceled from completion callbacks +- Run ready promise queue tasks before sleeping for delayed cURL multi requests +- Avoid integer overflow in cURL multi delay timing on 32-bit platforms +- Roll back failed cURL multi handle attachment instead of leaving requests pending +- Release the cURL easy handle when the `on_stats` callback throws +- Normalize response trailer field names to lowercase with values in wire order +- Retain response trailers only when an `on_trailers` callback is configured +- Validate the `on_trailers` callback before starting a cURL transfer +- Reject the `on_trailers` request option on the stream handler, which cannot observe trailers +- Match cookies, proxy schemes, auth types, and header names with locale-independent ASCII folding +- Reject proxy option values that Guzzle cannot classify identically to ext-curl + + +## 7.14.0 - 2026-07-08 + +### Added + +- Added the `on_trailers` request option to expose parsed HTTP response trailers +- Added the `multiplex` request option with `Multiplexing::*` modes to control or require HTTP/2 multiplexing +- Added rejection of explicit `multiplex` requests when `CURLMOPT_PIPELINING` disables multiplexing +- Added the `max_host_connections` and `max_total_connections` client and cURL multi handler options + +### Changed + +- Redirects that discard the request body no longer require it to be rewindable +- Synchronous cURL multi handler requests no longer wait for other queued transfers +- Section SOCKS proxy connections by credentials on libcurl before 7.69.0 +- Reject request-level `CURLOPT_SHARE` when combined with authenticated SOCKS proxy configuration +- Redact proxy userinfo containing raw control bytes in cURL errors +- Check linked curl/libcurl NTLM support before applying NTLM auth +- Clarify that NTLM is deprecated by both Guzzle and curl/libcurl +- Remove deprecation for the raw cURL `CURLOPT_CERTINFO` option +- Warn when a cURL multi option cannot be applied + +### Deprecated + +- Deprecate the raw `CURLOPT_PIPEWAIT` cURL option in favour of the `multiplex` request option +- Deprecate unknown handler constructor options +- Deprecate invalid `select_timeout` cURL multi handler option values +- Deprecate raw cURL multi connection cap options in favour of the named options + + +## 7.13.3 - 2026-07-08 + +### Changed + +- Adjusted `guzzlehttp/promises` version constraint to `^2.5.1` +- Adjusted `guzzlehttp/psr7` version constraint to `^2.12.4` +- Pass explicit trim characters ahead of the PHP 8.6 trim default change + +### Fixed + +- Stop matching cookie domains against hosts with a trailing newline +- Reject HTTP status codes and certificate type extensions with a trailing newline +- Treat PCRE engine failures as invalid cookie names during cookie validation +- Report PCRE engine failures when formatting log messages +- Report PCRE engine failures when splitting `no_proxy` values + + +## 7.13.2 - 2026-07-05 + +### Fixed + +- Stop the cURL multi handler busy-waiting on request delays shorter than one second +- Stop cURL HEAD requests with request bodies hanging on responses that declare a content length +- The cURL handler no longer transmits request bodies on HEAD requests +- Preserve response headers when a response includes HTTP trailers +- Harden cURL response header block detection when HTTP trailers are received +- Corrected the PSR-7 class names in the Pool iterator exception +- Redirect body rewind failures no longer leak a bare `RuntimeException` + + +## 7.13.1 - 2026-06-29 + +### Fixed + +- Allow middleware to rewrite partial URIs before transports validate them + + +## 7.13.0 - 2026-06-29 + +### Added + +- Added the `crypto_method_max` request option to cap the maximum TLS protocol version +- Added HTTP QUERY redirect support, preserving method and body on 301 and 302 + +### Changed + +- Section proxy tunnel connection reuse by credential so distinct credentials never share a tunnel +- Isolate concurrent foreign cURL proxy tunnels added while another owner's tunnel is active +- Route credentialed HTTP(S) proxy Proxy-Authorization headers through cURL proxy header handling +- Reject request-level `CURLOPT_SHARE` when combined with authenticated HTTP/HTTPS proxy tunnel configuration +- Remove deprecation for raw cURL `CURLOPT_PREREQFUNCTION` callbacks when defined by PHP cURL +- Route TLS 1.2 `crypto_method` requests to the stream handler when cURL cannot select TLS 1.2 +- Reject final request URIs missing a scheme or host before transfer + +### Deprecated + +- Deprecate invalid protocols, force_ip_resolve, delay, cookies, and allow_redirects values + + +## 7.12.3 - 2026-06-23 + +### Changed + +- Adjusted `guzzlehttp/psr7` version constraint to `^2.12.3` + +### Security + +- Treat IP and numeric cookie domains as exact-match-only (GHSA-g446-98w2-8p5w) + + +## 7.12.2 - 2026-06-23 + +### Fixed + +- Clamp out-of-range `Max-Age` so a very large value no longer overflows to an already-expired timestamp +- Use strict comparison in `CookieJar` conflict resolution so distinct numeric-string names don't overwrite +- Store a cookie whose `Domain` has a trailing dot on the origin host instead of silently discarding it +- Fix `StreamHandler` hard-failing on bracketed IPv6 literal hosts when `force_ip_resolve` is set +- Use strict cookie `Path` comparison so `CookieJar::clear()` with a numeric path keeps a distinct-path cookie +- Fixed cookie handling for falsey `Domain`, `Max-Age`, path, and name values +- Fixed `decode_content` handling for falsey string values +- Fixed deprecated request option values reaching built-in handlers before normalization + + +## 7.12.1 - 2026-06-18 + +### Changed + +- Adjusted `guzzlehttp/psr7` version constraint to `^2.12.1` + +### Fixed + +- Reject proxy URLs with a malformed scheme in the cURL handlers instead of letting libcurl mishandle them + +### Security + +- Reject HTTPS proxies when the installed libcurl lacks HTTPS-proxy support (GHSA-wpwq-4j6v-78m3) +- Reject dot-only cookie `Domain` attributes as match-all (GHSA-cwxw-98qj-8qjx) + + +## 7.12.0 - 2026-06-16 + +### Added + +- Added `RequestOptions` constants for `curl`, `retries`, and `stream_context` + +### Changed + +- Adjusted `guzzlehttp/psr7` version constraint to `^2.12` +- Constrain cURL transport sharing to safe libcurl DNS and SSL session support +- Resolve proxy environment variables in the cURL handlers; libcurl no longer reads the environment itself +- Ignore proxy environment variables when the `proxy` request option makes a decision +- Disable proxy environment variables on Windows SAPIs other than CLI (httpoxy hardening) +- Redact proxy credentials from cURL handler error messages, following `Psr7\Utils::redactUserInfo()` +- Normalize no-proxy domain and IP literal matching across the cURL and stream handlers + +### Deprecated + +- Deprecated the request-level `handler` option, which will be ignored in 8.0 +- Deprecated raw cURL request options outside the built-in cURL handlers' allow-list +- Deprecated the `CURLOPT_PROXYTYPE` cURL request option; set the proxy type via a scheme-prefixed proxy URL +- Deprecated PHP stream context options outside the built-in stream handler allow-list +- Deprecated passing `ntlm` as a built-in `auth` type +- Deprecated `Utils::describeType()` +- Deprecated non-finite floats in the `query` and `form_params` options; 8.0 rejects them +- Deprecated non-string scalar values in the `body` option; 8.0 rejects them + +### Fixed + +- Fix cURL TLS and HTTP/2 capability detection using libcurl feature checks +- Fix proxy `no` list matches being re-proxied through environment-configured proxies by libcurl +- Fix `no` list and `NO_PROXY` matching to support IP CIDR ranges, matching libcurl +- Fix the stream handler not applying scheme-less proxies and their credentials + + ## 7.11.2 - 2026-06-12 ### Fixed diff --git a/vendor/guzzlehttp/guzzle/README.md b/vendor/guzzlehttp/guzzle/README.md index 2615c4e..a7af068 100644 --- a/vendor/guzzlehttp/guzzle/README.md +++ b/vendor/guzzlehttp/guzzle/README.md @@ -72,11 +72,11 @@ composer require guzzlehttp/guzzle [guzzle-4-repo]: https://github.com/guzzle/guzzle/tree/4.x [guzzle-5-repo]: https://github.com/guzzle/guzzle/tree/5.3 [guzzle-6-repo]: https://github.com/guzzle/guzzle/tree/6.5 -[guzzle-7-repo]: https://github.com/guzzle/guzzle/tree/7.11 +[guzzle-7-repo]: https://github.com/guzzle/guzzle/tree/7.15 [guzzle-3-docs]: https://github.com/guzzle/guzzle3/tree/master/docs [guzzle-5-docs]: https://github.com/guzzle/guzzle/tree/5.3/docs [guzzle-6-docs]: https://github.com/guzzle/guzzle/tree/6.5/docs -[guzzle-7-docs]: https://github.com/guzzle/guzzle/blob/7.11/docs/index.md +[guzzle-7-docs]: https://github.com/guzzle/guzzle/blob/7.15/docs/index.md ## Security diff --git a/vendor/guzzlehttp/guzzle/composer.json b/vendor/guzzlehttp/guzzle/composer.json index 35f7d7c..3ae66e6 100644 --- a/vendor/guzzlehttp/guzzle/composer.json +++ b/vendor/guzzlehttp/guzzle/composer.json @@ -53,17 +53,17 @@ "require": { "php": "^7.2.5 || ^8.0", "ext-json": "*", - "guzzlehttp/promises": "^2.5", - "guzzlehttp/psr7": "^2.11", + "guzzlehttp/promises": "^2.5.1", + "guzzlehttp/psr7": "^2.13", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.24" + "symfony/polyfill-php80": "^1.25" }, "require-dev": { "ext-curl": "*", "bamarni/composer-bin-plugin": "^1.8.2", - "guzzle/client-integration-tests": "3.0.2", - "guzzlehttp/test-server": "^0.5", + "guzzle/client-integration-tests": "3.0.3", + "guzzlehttp/test-server": "^0.7", "php-http/message-factory": "^1.1", "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" @@ -81,7 +81,7 @@ "type": "package", "package": { "name": "guzzle/client-integration-tests", - "version": "v3.0.2", + "version": "v3.0.3", "require": { "guzzlehttp/psr7": "^1.7 || ^2.0", "php": "^7.2.5 || ^8.0", @@ -99,7 +99,7 @@ ], "dist": { "type": "zip", - "url": "https://codeload.github.com/guzzle/client-integration-tests/zip/2c025848417c1135031fdf9c728ee53d0a7ceaee" + "url": "https://codeload.github.com/guzzle/client-integration-tests/zip/30edbabe49dedd95e3f21d8a25438f767b653d75" } } } diff --git a/vendor/guzzlehttp/guzzle/package-lock.json b/vendor/guzzlehttp/guzzle/package-lock.json deleted file mode 100644 index 0e14dc1..0000000 --- a/vendor/guzzlehttp/guzzle/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "guzzle", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} diff --git a/vendor/guzzlehttp/guzzle/src/Client.php b/vendor/guzzlehttp/guzzle/src/Client.php index 3429828..28a8e15 100644 --- a/vendor/guzzlehttp/guzzle/src/Client.php +++ b/vendor/guzzlehttp/guzzle/src/Client.php @@ -3,9 +3,11 @@ namespace GuzzleHttp; use GuzzleHttp\Cookie\CookieJar; +use GuzzleHttp\Cookie\CookieJarInterface; use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Exception\InvalidArgumentException; use GuzzleHttp\Handler\CurlShareHandleState; +use GuzzleHttp\Handler\CurlVersion; use GuzzleHttp\Promise as P; use GuzzleHttp\Promise\PromiseInterface; use Psr\Http\Message\RequestInterface; @@ -52,6 +54,20 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface * into relative URIs. Can be a string or instance of UriInterface. * - transport_sharing: (string|null) Transport sharing mode for the * default handler. Accepts TransportSharing::* or null. Defaults to null. + * - max_host_connections: (int|null) Maximum concurrent connections per + * host, applied by the default CurlMultiHandler. The default stream + * fallback receives the cap as a marker only: it rejects enabled + * response streaming ("stream" => true) and does not limit overlapping + * buffered calls. + * - max_total_connections: (int|null) Maximum concurrent connections + * overall, applied by the default CurlMultiHandler. The default stream + * fallback receives the cap as a marker only: it rejects enabled + * response streaming ("stream" => true) and does not limit overlapping + * buffered calls. + * - multiplex: (string|null) Multiplexing::NONE to disable multiplexing on + * the default CurlMultiHandler; the value also becomes the default + * "multiplex" request option. Other Multiplexing::* values act as the + * default request option only. * - **: any request option * * @param array $config Client configuration settings. @@ -60,16 +76,41 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface */ public function __construct(array $config = []) { + $handlerOptions = []; + foreach (['max_host_connections', 'max_total_connections'] as $capOption) { + if (\array_key_exists($capOption, $config)) { + if ($config[$capOption] !== null) { + $handlerOptions[$capOption] = $config[$capOption]; + } + + unset($config[$capOption]); + } + } + + // Deliberately not unset: the value also becomes the default + // "multiplex" request option, which the configured handler accepts. + $handlerMultiplex = ($config['multiplex'] ?? null) === Multiplexing::NONE; + $transportSharing = \array_key_exists('transport_sharing', $config) ? $config['transport_sharing'] : null; $transportSharingMode = CurlShareHandleState::normalizeMode($transportSharing, 'transport_sharing'); unset($config['transport_sharing']); if (!isset($config['handler'])) { - $config['handler'] = $transportSharingMode === TransportSharing::NONE + if ($transportSharingMode !== TransportSharing::NONE) { + $handlerOptions['transport_sharing'] = $transportSharingMode; + } + + if ($handlerMultiplex) { + $handlerOptions['multiplex'] = Multiplexing::NONE; + } + + $config['handler'] = $handlerOptions === [] ? HandlerStack::create() - : HandlerStack::create(Utils::chooseHandler(['transport_sharing' => $transportSharingMode])); + : HandlerStack::create(Utils::chooseHandler($handlerOptions)); } elseif (!\is_callable($config['handler'])) { throw new InvalidArgumentException('handler must be a callable'); + } elseif ($handlerOptions !== []) { + throw new InvalidArgumentException('The "max_host_connections" and "max_total_connections" client options require Guzzle to create the default handler. Configure the options on the CurlMultiHandler constructor to apply numeric connection caps, or on the StreamHandler constructor to reject enabled response streaming, when providing a custom handler.'); } elseif ($transportSharingMode === TransportSharing::HANDLER_REQUIRE) { throw new InvalidArgumentException('The "transport_sharing" client option can only require sharing when Guzzle creates the default handler. Configure the "transport_sharing" option on CurlHandler or CurlMultiHandler when providing a custom cURL handler.'); } @@ -92,6 +133,8 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface */ public function __call($method, $args) { + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s::%s() is deprecated and will be removed in 8.0.', __CLASS__, __FUNCTION__); + if (\count($args) < 1) { throw new InvalidArgumentException('Magic request methods require a URI and optional options array'); } @@ -101,7 +144,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface $isAsync = \substr($method, -5) === 'Async'; $method = $isAsync ? \substr($method, 0, -5) : $method; - $method = \strtoupper($method); + $method = Psr7\Utils::asciiToUpper($method); return $isAsync ? $this->requestAsync($method, $uri, $opts) @@ -168,7 +211,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface */ public function requestAsync(string $method, $uri = '', array $options = []): PromiseInterface { - $normalizedMethod = \strtoupper($method); + $normalizedMethod = Psr7\Utils::asciiToUpper($method); if ($method !== $normalizedMethod) { \trigger_deprecation( 'guzzlehttp/guzzle', @@ -192,6 +235,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface if (\is_array($body)) { throw $this->invalidBody(); } + $body = self::createBodyStream($body); $request = new Psr7\Request($method, $uri, $headers, $body, $version); // Remove the option so that they are not doubly-applied. unset($options['headers'], $options['body'], $options['version']); @@ -214,7 +258,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface */ public function request(string $method, $uri = '', array $options = []): ResponseInterface { - $normalizedMethod = \strtoupper($method); + $normalizedMethod = Psr7\Utils::asciiToUpper($method); if ($method !== $normalizedMethod) { \trigger_deprecation( 'guzzlehttp/guzzle', @@ -258,7 +302,11 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface $uri = Utils::idnUriConvert($uri, $idnOptions); } - return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri; + if ($uri->getScheme() === '' && $uri->getHost() !== '') { + $uri = $uri->withScheme('http'); + } + + return $uri; } /** @@ -307,7 +355,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface // Add the User-Agent header if one was not already set. $hasUserAgent = false; foreach (\array_keys($this->config['headers']) as $name) { - if (\strtolower((string) $name) === 'user-agent') { + if (Psr7\Utils::asciiToLower((string) $name) === 'user-agent') { $hasUserAgent = true; break; } @@ -331,6 +379,8 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface */ private function prepareDefaults(array $options): array { + self::warnAboutRequestLevelHandler($options); + $defaults = $this->config; if (!empty($defaults['headers'])) { @@ -363,7 +413,178 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface self::warnAboutInvalidRequestOptionTypes($result); - return $result; + return self::normalizeDeprecatedRequestOptionValues($result); + } + + /** + * Normalize values that guzzlehttp/guzzle 8.0 rejects only after the + * corresponding 7.x deprecation has already been emitted. + * + * @param array $options + * + * @return array + */ + private static function normalizeDeprecatedRequestOptionValues(array $options): array + { + self::normalizeDeprecatedAuthOptionValues($options); + self::normalizeDeprecatedTlsFileOptionValues($options, 'cert'); + self::normalizeDeprecatedTlsFileOptionValues($options, 'ssl_key'); + self::normalizeDeprecatedStringOptionValues($options); + self::normalizeDeprecatedNumericOptionValues($options); + self::normalizeDeprecatedIntegerOptionValues($options); + + return $options; + } + + /** + * @param mixed $value + */ + private static function canStringifyDeprecatedValue($value): bool + { + return $value === null + || \is_scalar($value) + || (\is_object($value) && \method_exists($value, '__toString')); + } + + /** + * @param mixed $value + */ + private static function stringifyDeprecatedValue($value): string + { + if (\is_float($value) && !\is_finite($value)) { + return \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF'); + } + + if ($value === null) { + return ''; + } + + if (\is_scalar($value)) { + return (string) $value; + } + + if (\is_object($value) && \method_exists($value, '__toString')) { + return $value->__toString(); + } + + throw new \LogicException('Value is not stringable.'); + } + + /** + * @param array $options + */ + private static function normalizeDeprecatedAuthOptionValues(array &$options): void + { + if (!isset($options['auth']) || !\is_array($options['auth']) || $options['auth'] === []) { + return; + } + + foreach ([0, 1] as $index) { + if ( + \array_key_exists($index, $options['auth']) + && !\is_string($options['auth'][$index]) + && self::canStringifyDeprecatedValue($options['auth'][$index]) + ) { + $options['auth'][$index] = self::stringifyDeprecatedValue($options['auth'][$index]); + } + } + + if ( + \array_key_exists(2, $options['auth']) + && $options['auth'][2] !== null + && !\is_string($options['auth'][2]) + && self::canStringifyDeprecatedValue($options['auth'][2]) + ) { + $options['auth'][2] = self::stringifyDeprecatedValue($options['auth'][2]); + } + } + + /** + * @param array $options + */ + private static function normalizeDeprecatedTlsFileOptionValues(array &$options, string $option): void + { + if (!isset($options[$option]) || !\is_array($options[$option])) { + return; + } + + foreach ([0, 1] as $index) { + if ( + \array_key_exists($index, $options[$option]) + && $options[$option][$index] !== null + && !\is_string($options[$option][$index]) + && self::canStringifyDeprecatedValue($options[$option][$index]) + ) { + $options[$option][$index] = self::stringifyDeprecatedValue($options[$option][$index]); + } + } + } + + /** + * @param array $options + */ + private static function normalizeDeprecatedStringOptionValues(array &$options): void + { + foreach (['cert_type', 'force_ip_resolve', 'ssl_key_type'] as $option) { + if ( + \array_key_exists($option, $options) + && !\is_string($options[$option]) + && self::canStringifyDeprecatedValue($options[$option]) + ) { + $options[$option] = self::stringifyDeprecatedValue($options[$option]); + } + } + } + + /** + * @param array $options + */ + private static function normalizeDeprecatedNumericOptionValues(array &$options): void + { + foreach (['connect_timeout', 'delay', 'read_timeout', 'timeout'] as $option) { + if ( + \array_key_exists($option, $options) + && \is_string($options[$option]) + && \is_numeric($options[$option]) + ) { + $options[$option] = $options[$option] + 0; + } + } + } + + /** + * @param array $options + */ + private static function normalizeDeprecatedIntegerOptionValues(array &$options): void + { + foreach (['crypto_method', 'crypto_method_max', 'retries'] as $option) { + if (!\array_key_exists($option, $options)) { + continue; + } + + if (\is_string($options[$option]) && \preg_match('/^-?\d+$/D', $options[$option]) === 1) { + $options[$option] = (int) $options[$option]; + } elseif ( + \is_float($options[$option]) + && \is_finite($options[$option]) + && $options[$option] === (float) (int) $options[$option] + ) { + $options[$option] = (int) $options[$option]; + } + } + } + + private static function warnAboutRequestLevelHandler(array $options): void + { + if (!\array_key_exists('handler', $options)) { + return; + } + + \trigger_deprecation( + 'guzzlehttp/guzzle', + '7.12', + 'Passing the "handler" request option is deprecated; guzzlehttp/guzzle 8.0 will ignore request-level handlers. Configure the handler when creating the Client, or use a separate Client instance for requests that need a different handler.' + ); } private static function warnAboutInvalidRequestOptionTypes(array $options): void @@ -374,6 +595,8 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface if (isset($options['allow_redirects']) && \is_array($options['allow_redirects'])) { self::warnAboutInvalidAllowRedirectsOptionTypes($options['allow_redirects']); + } elseif (isset($options['allow_redirects']) && !\is_bool($options['allow_redirects'])) { + self::warnInvalidRequestOptionType('allow_redirects', 'bool|array', $options['allow_redirects'], '7.13'); } if (isset($options['auth'])) { @@ -388,9 +611,16 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface self::warnIfPresentAndNotString($options, 'cert_type'); self::warnIfPresentAndNotNumber($options, 'connect_timeout'); self::warnIfPresentAndNotInt($options, 'crypto_method'); + self::warnIfPresentAndNotInt($options, 'crypto_method_max', null, '7.13'); self::warnIfPresentAndNotBoolOrResource($options, 'debug'); self::warnIfPresentAndNotBoolOrString($options, 'decode_content'); self::warnIfPresentAndNotNumber($options, 'delay'); + if (isset($options['delay']) && \is_numeric($options['delay'])) { + $delay = (float) $options['delay']; + if (!\is_finite($delay) || $delay < 0.0) { + self::warnInvalidRequestOptionType('delay', 'finite int|float greater than or equal to 0', $options['delay'], '7.13'); + } + } self::warnIfPresentAndNotBoolOrInt($options, 'expect'); if (isset($options['form_params'])) { @@ -401,6 +631,15 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface self::warnInvalidRequestOptionType('force_ip_resolve', 'string', $options['force_ip_resolve']); } + if ( + isset($options['force_ip_resolve']) + && \is_string($options['force_ip_resolve']) + && $options['force_ip_resolve'] !== 'v4' + && $options['force_ip_resolve'] !== 'v6' + ) { + self::warnInvalidRequestOptionType('force_ip_resolve', '"v4"|"v6"', $options['force_ip_resolve'], '7.13'); + } + if (isset($options['headers'])) { self::warnAboutInvalidHeaderOptionTypes($options['headers']); } @@ -413,8 +652,10 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface self::warnIfPresentAndNotCallable($options, 'on_headers'); self::warnIfPresentAndNotCallable($options, 'on_stats'); + self::warnIfPresentAndNotCallable($options, 'on_trailers', null, '7.14'); self::warnIfPresentAndNotCallable($options, 'progress'); self::warnIfPresentAndNotStringArray($options, 'protocols', true); + self::warnAboutInvalidProtocolValues($options, 'protocols'); self::warnAboutInvalidProxyOptionTypes($options); self::warnIfPresentAndNotNumber($options, 'read_timeout'); @@ -437,6 +678,15 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface if (isset($options['cookies']) && $options['cookies'] === true) { self::warnInvalidRequestOptionType('cookies', 'false|CookieJarInterface', $options['cookies']); } + + if ( + isset($options['cookies']) + && $options['cookies'] !== false + && $options['cookies'] !== true + && !($options['cookies'] instanceof CookieJarInterface) + ) { + self::warnInvalidRequestOptionType('cookies', 'false|CookieJarInterface', $options['cookies'], '7.13'); + } } private static function warnAboutInvalidAllowRedirectsOptionTypes(array $allowRedirects): void @@ -445,6 +695,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface self::warnIfPresentAndNotBool($allowRedirects, 'strict', 'allow_redirects.strict'); self::warnIfPresentAndNotBool($allowRedirects, 'referer', 'allow_redirects.referer'); self::warnIfPresentAndNotStringArray($allowRedirects, 'protocols', true, 'allow_redirects.protocols'); + self::warnAboutInvalidProtocolValues($allowRedirects, 'protocols', 'allow_redirects.protocols'); self::warnIfPresentAndNotCallable($allowRedirects, 'on_redirect', 'allow_redirects.on_redirect'); self::warnIfPresentAndNotBool($allowRedirects, 'track_redirects', 'allow_redirects.track_redirects'); } @@ -700,17 +951,25 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface } } - private static function warnIfPresentAndNotCallable(array $options, string $option, ?string $path = null): void - { + private static function warnIfPresentAndNotCallable( + array $options, + string $option, + ?string $path = null, + string $since = '7.11' + ): void { if (\array_key_exists($option, $options) && !\is_callable($options[$option])) { - self::warnInvalidRequestOptionType($path ?? $option, 'callable', $options[$option]); + self::warnInvalidRequestOptionType($path ?? $option, 'callable', $options[$option], $since); } } - private static function warnIfPresentAndNotInt(array $options, string $option, ?string $path = null): void - { + private static function warnIfPresentAndNotInt( + array $options, + string $option, + ?string $path = null, + string $since = '7.11' + ): void { if (\array_key_exists($option, $options) && !\is_int($options[$option])) { - self::warnInvalidRequestOptionType($path ?? $option, 'int', $options[$option]); + self::warnInvalidRequestOptionType($path ?? $option, 'int', $options[$option], $since); } } @@ -752,6 +1011,24 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface } } + /** + * @param array $options + */ + private static function warnAboutInvalidProtocolValues(array $options, string $option, ?string $path = null): void + { + if (!isset($options[$option]) || !\is_array($options[$option])) { + return; + } + + $path = $path ?? $option; + + foreach ($options[$option] as $index => $protocol) { + if (\is_string($protocol) && $protocol !== 'http' && $protocol !== 'https') { + self::warnInvalidRequestOptionType($path.'.'.(string) $index, '"http"|"https"', $protocol, '7.13'); + } + } + } + private static function warnIfPresentAndNotStringOrNumber(array $options, string $option): void { if ( @@ -767,11 +1044,11 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface /** * @param mixed $value */ - private static function warnInvalidRequestOptionType(string $option, string $expected, $value): void + private static function warnInvalidRequestOptionType(string $option, string $expected, $value, string $since = '7.11'): void { \trigger_deprecation( 'guzzlehttp/guzzle', - '7.11', + $since, 'Passing %s to request option "%s" is deprecated; guzzlehttp/guzzle 8.0 requires %s.', \get_debug_type($value), $option, @@ -841,7 +1118,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface .'x-www-form-urlencoded requests, and the multipart ' .'option to send multipart/form-data requests.'); } - $options['body'] = \http_build_query(self::normalizeNonFiniteFloats($options['form_params']), '', '&'); + $options['body'] = \http_build_query(self::normalizeNonFiniteFloats($options['form_params'], 'form_params'), '', '&'); unset($options['form_params']); // Ensure that we don't have the header in different case and set the new value. $options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']); @@ -854,16 +1131,20 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface } if (isset($options['json'])) { - $options['body'] = Utils::jsonEncode($options['json']); + $json = \json_encode($options['json']); + if (\JSON_ERROR_NONE !== \json_last_error()) { + throw new InvalidArgumentException('json_encode error: '.\json_last_error_msg()); + } + + /** @var non-empty-string $json */ + $options['body'] = $json; unset($options['json']); // Ensure that we don't have the header in different case and set the new value. $options['_conditional'] = Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'application/json'; } - if (!empty($options['decode_content']) - && $options['decode_content'] !== true - ) { + if (isset($options['decode_content']) && \is_string($options['decode_content'])) { // Ensure that we don't have the header in different case and set the new value. $options['_conditional'] = Psr7\Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']); $modify['set_headers']['Accept-Encoding'] = (string) $options['decode_content']; @@ -873,13 +1154,13 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface if (\is_array($options['body'])) { throw $this->invalidBody(); } - $modify['body'] = Psr7\Utils::streamFor($options['body']); + $modify['body'] = self::createBodyStream($options['body']); unset($options['body']); } if (!empty($options['auth']) && \is_array($options['auth'])) { $value = $options['auth']; - $type = isset($value[2]) ? \strtolower($value[2]) : 'basic'; + $type = isset($value[2]) ? Psr7\Utils::asciiToLower($value[2]) : 'basic'; switch ($type) { case 'basic': // Ensure that we don't have the header in different case and set the new value. @@ -893,6 +1174,14 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface $options['curl'][\CURLOPT_USERPWD] = "$value[0]:$value[1]"; break; case 'ntlm': + \trigger_deprecation( + 'guzzlehttp/guzzle', + '7.12', + 'Passing "ntlm" as the built-in auth type is deprecated; guzzlehttp/guzzle 8.0 will no longer apply NTLM through the "auth" request option. NTLM is also deprecated by curl/libcurl and may be unavailable in current or future libcurl builds. Avoid NTLM; if you must use it temporarily, configure cURL HTTP authentication options directly with a libcurl build that still supports NTLM.' + ); + if (!CurlVersion::supportsNtlm()) { + throw new InvalidArgumentException('NTLM authentication is not available because the installed curl/libcurl build does not provide NTLM support.'); + } $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_NTLM; $options['curl'][\CURLOPT_USERPWD] = "$value[0]:$value[1]"; break; @@ -902,7 +1191,7 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface if (isset($options['query'])) { $value = $options['query']; if (\is_array($value)) { - $value = \http_build_query(self::normalizeNonFiniteFloats($value), '', '&', \PHP_QUERY_RFC3986); + $value = \http_build_query(self::normalizeNonFiniteFloats($value, 'query'), '', '&', \PHP_QUERY_RFC3986); } if (!\is_string($value)) { throw new InvalidArgumentException('query must be a string or array'); @@ -993,16 +1282,65 @@ class Client implements ClientInterface, \Psr\Http\Client\ClientInterface return $droppedHeaderNames; } + /** + * @param mixed $body + */ + private static function createBodyStream($body): StreamInterface + { + if ($body instanceof StreamInterface) { + return $body; + } + + if (\is_resource($body) || $body === null || \is_string($body) || $body instanceof \Iterator) { + return Psr7\Utils::streamFor($body); + } + + if (\is_scalar($body)) { + \trigger_deprecation('guzzlehttp/guzzle', '7.12', 'Passing a non-string scalar to the "body" request option is deprecated; guzzlehttp/guzzle 8.0 will reject non-string scalar bodies.'); + + return Psr7\Utils::streamFor(self::stringifyScalar($body)); + } + + if (\is_object($body) && \method_exists($body, '__toString')) { + return Psr7\Utils::streamFor((string) $body); + } + + if (\is_callable($body)) { + return Psr7\Utils::streamFor($body); + } + + throw new InvalidArgumentException(\sprintf( + 'Passing %s to request option "body" is invalid; expected resource|string|null|int|float|bool|StreamInterface|callable&object|Iterator|Stringable.', + \get_debug_type($body) + )); + } + + /** + * @param bool|float|int|string $value + */ + private static function stringifyScalar($value): string + { + // Normalize non-finite floats to dodge PHP 8.5's (string) NAN + // coercion warning while the value is still accepted. + if (\is_float($value) && !\is_finite($value)) { + $value = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF'); + } + + return (string) $value; + } + /** * Converts non-finite floats in the array to the strings PHP coerces * them to, as implicit coercion of NAN emits a warning on PHP 8.5. */ - private static function normalizeNonFiniteFloats(array $values): array + private static function normalizeNonFiniteFloats(array $values, string $option): array { foreach ($values as $key => $value) { if (\is_array($value)) { - $values[$key] = self::normalizeNonFiniteFloats($value); + $values[$key] = self::normalizeNonFiniteFloats($value, $option); } elseif (\is_float($value) && !\is_finite($value)) { + \trigger_deprecation('guzzlehttp/guzzle', '7.12', 'Passing a non-finite float in the "%s" request option is deprecated; guzzlehttp/guzzle 8.0 will reject non-finite floats.', $option); + $values[$key] = \is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF'); } } diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php index 289e2e2..ef11cc7 100644 --- a/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php +++ b/vendor/guzzlehttp/guzzle/src/Cookie/CookieJar.php @@ -2,6 +2,7 @@ namespace GuzzleHttp\Cookie; +use GuzzleHttp\Psr7; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; @@ -10,6 +11,11 @@ use Psr\Http\Message\ResponseInterface; */ class CookieJar implements CookieJarInterface { + private const MAX_SET_COOKIE_FIELD_LENGTH = 8190; + private const MAX_SET_COOKIE_FIELDS = 50; + private const MAX_REQUEST_COOKIES = 150; + private const MAX_COOKIE_HEADER_LENGTH = 8190; + /** * @var SetCookie[] Loaded cookie data */ @@ -88,7 +94,7 @@ class CookieJar implements CookieJarInterface public function getCookieByName(string $name): ?SetCookie { foreach ($this->cookies as $cookie) { - if ($cookie->getName() !== null && \strcasecmp($cookie->getName(), $name) === 0) { + if ($cookie->getName() !== null && Psr7\Utils::caselessEquals($cookie->getName(), $name)) { return $cookie; } } @@ -109,14 +115,14 @@ class CookieJar implements CookieJarInterface $this->cookies = []; return; - } elseif (!$path) { + } elseif ($path === null) { $this->cookies = \array_filter( $this->cookies, static function (SetCookie $cookie) use ($domain): bool { return $cookie->getDomain() === null || !$cookie->matchesDomain($domain); } ); - } elseif (!$name) { + } elseif ($name === null) { $this->cookies = \array_filter( $this->cookies, static function (SetCookie $cookie) use ($path, $domain): bool { @@ -130,7 +136,7 @@ class CookieJar implements CookieJarInterface $this->cookies, static function (SetCookie $cookie) use ($path, $domain, $name) { return !($cookie->getDomain() !== null - && $cookie->getName() == $name + && $cookie->getName() === $name && $cookie->matchesPath($path) && $cookie->matchesDomain($domain)); } @@ -168,13 +174,23 @@ class CookieJar implements CookieJarInterface return false; } + $maxAge = $cookie->getMaxAge(); + if ($maxAge !== null && $maxAge <= 0) { + if ($cookie->getDomain() !== null) { + $this->removeCookie($cookie); + } + + return false; + } + // Resolve conflicts with previously set cookies foreach ($this->cookies as $i => $c) { // Two cookies are identical, when their path, and domain are // identical. - if ($c->getPath() != $cookie->getPath() - || $c->getDomain() != $cookie->getDomain() - || $c->getName() != $cookie->getName() + if ($c->getPath() !== $cookie->getPath() + || $c->getDomain() !== $cookie->getDomain() + || $c->getHostOnly() !== $cookie->getHostOnly() + || $c->getName() !== $cookie->getName() ) { continue; } @@ -224,10 +240,23 @@ class CookieJar implements CookieJarInterface public function extractCookies(RequestInterface $request, ResponseInterface $response): void { if ($cookieHeader = $response->getHeader('Set-Cookie')) { + $accepted = 0; foreach ($cookieHeader as $cookie) { + if (\strlen($cookie) > self::MAX_SET_COOKIE_FIELD_LENGTH) { + continue; + } + $sc = SetCookie::fromString($cookie); - if (!$sc->getDomain()) { + $domain = $sc->getDomain(); + if ($domain === null || $domain === '') { $sc->setDomain($request->getUri()->getHost()); + $sc->setHostOnly(true); + } elseif (\substr($domain, -1) === '.' && '' !== \trim($domain, '.')) { + // Keep pure-dot domains rejected by the dot-only fix. + $sc->setDomain($request->getUri()->getHost()); + $sc->setHostOnly(true); + } else { + $sc->setHostOnly(false); } if (0 !== \strpos($sc->getPath(), '/')) { $sc->setPath($this->getCookiePathFromRequest($request)); @@ -237,7 +266,9 @@ class CookieJar implements CookieJarInterface } // Note: At this point `$sc->getDomain()` being a public suffix should // be rejected, but we don't want to pull in the full PSL dependency. - $this->setCookie($sc); + if ($this->setCookie($sc) && ++$accepted === self::MAX_SET_COOKIE_FIELDS) { + break; + } } } } @@ -270,6 +301,7 @@ class CookieJar implements CookieJarInterface public function withCookieHeader(RequestInterface $request): RequestInterface { $values = []; + $headerLength = 8; $uri = $request->getUri(); $scheme = $uri->getScheme(); $host = $uri->getHost(); @@ -282,8 +314,19 @@ class CookieJar implements CookieJarInterface && !$cookie->isExpired() && (!$cookie->getSecure() || $scheme === 'https') ) { - $values[] = $cookie->getName().'=' - .$cookie->getValue(); + $name = (string) $cookie->getName(); + $value = (string) $cookie->getValue(); + $separatorLength = $values === [] ? 0 : 2; + $valueLength = \strlen($name) + 1 + \strlen($value); + if ($headerLength + $separatorLength + $valueLength > self::MAX_COOKIE_HEADER_LENGTH) { + break; + } + + $values[] = $name.'='.$value; + $headerLength += $separatorLength + $valueLength; + if (\count($values) === self::MAX_REQUEST_COOKIES) { + break; + } } } @@ -300,11 +343,36 @@ class CookieJar implements CookieJarInterface { $cookieValue = $cookie->getValue(); if (($cookieValue === null || $cookieValue === '') && $cookie->getDomain() !== null) { - $this->clear( - $cookie->getDomain(), - $cookie->getPath(), - $cookie->getName() - ); + $this->removeCookie($cookie); } } + + private function removeCookie(SetCookie $cookie): void + { + $this->cookies = \array_filter( + $this->cookies, + static function (SetCookie $stored) use ($cookie): bool { + return !($stored->getName() === $cookie->getName() + && $stored->getPath() === $cookie->getPath() + && self::cookieDomainsEqual($stored->getDomain(), $cookie->getDomain()) + && $stored->getHostOnly() === $cookie->getHostOnly()); + } + ); + } + + private static function cookieDomainsEqual(?string $first, ?string $second): bool + { + if ($first === null || $second === null) { + return $first === $second; + } + + if (isset($first[0]) && $first[0] === '.') { + $first = \substr($first, 1); + } + if (isset($second[0]) && $second[0] === '.') { + $second = \substr($second, 1); + } + + return Psr7\Utils::caselessEquals($first, $second); + } } diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php index 290236d..0052b0a 100644 --- a/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php +++ b/vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php @@ -2,7 +2,7 @@ namespace GuzzleHttp\Cookie; -use GuzzleHttp\Utils; +use GuzzleHttp\Exception\InvalidArgumentException; /** * Persists non-session cookies using a JSON formatted file @@ -60,11 +60,18 @@ class FileCookieJar extends CookieJar /** @var SetCookie $cookie */ foreach ($this as $cookie) { if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { - $json[] = $cookie->toArray(); + $data = $cookie->toArray(); + $data['HostOnly'] = $cookie->getHostOnly(); + $json[] = $data; } } - $jsonStr = Utils::jsonEncode($json); + $jsonStr = \json_encode($json); + if (\JSON_ERROR_NONE !== \json_last_error()) { + throw new InvalidArgumentException('json_encode error: '.\json_last_error_msg()); + } + + /** @var non-empty-string $jsonStr */ if (false === \file_put_contents($filename, $jsonStr, \LOCK_EX)) { throw new \RuntimeException("Unable to save file {$filename}"); } @@ -89,10 +96,23 @@ class FileCookieJar extends CookieJar return; } - $data = Utils::jsonDecode($json, true); + $data = \json_decode($json, true); + if (\JSON_ERROR_NONE !== \json_last_error()) { + throw new InvalidArgumentException('json_decode error: '.\json_last_error_msg()); + } + if (\is_array($data)) { + $cookies = []; foreach ($data as $cookie) { - $this->setCookie(new SetCookie($cookie)); + if (!\is_array($cookie) || !\array_key_exists('HostOnly', $cookie) || !\is_bool($cookie['HostOnly'])) { + throw new \RuntimeException("Invalid cookie file: {$filename}"); + } + + $cookies[] = new SetCookie($cookie); + } + + foreach ($cookies as $cookie) { + $this->setCookie($cookie); } } elseif (\is_scalar($data) && !empty($data)) { throw new \RuntimeException("Invalid cookie file: {$filename}"); diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php b/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php index a8207d3..aaaaa79 100644 --- a/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php +++ b/vendor/guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php @@ -50,7 +50,9 @@ class SessionCookieJar extends CookieJar /** @var SetCookie $cookie */ foreach ($this as $cookie) { if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { - $json[] = $cookie->toArray(); + $data = $cookie->toArray(); + $data['HostOnly'] = $cookie->getHostOnly(); + $json[] = $data; } } @@ -78,12 +80,17 @@ class SessionCookieJar extends CookieJar $data = \json_decode($json, true); if (\is_array($data)) { + $cookies = []; foreach ($data as $cookie) { - if (!\is_array($cookie)) { + if (!\is_array($cookie) || !\array_key_exists('HostOnly', $cookie) || !\is_bool($cookie['HostOnly'])) { throw new \RuntimeException('Invalid cookie data'); } - $this->setCookie(new SetCookie($cookie)); + $cookies[] = new SetCookie($cookie); + } + + foreach ($cookies as $cookie) { + $this->setCookie($cookie); } } elseif (\is_scalar($data) && \strlen((string) $data)) { throw new \RuntimeException('Invalid cookie data'); diff --git a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php index 95f170a..29133e7 100644 --- a/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php +++ b/vendor/guzzlehttp/guzzle/src/Cookie/SetCookie.php @@ -2,6 +2,8 @@ namespace GuzzleHttp\Cookie; +use GuzzleHttp\Psr7; + /** * Set-Cookie object */ @@ -27,6 +29,11 @@ class SetCookie */ private $data; + /** + * @var bool Whether this cookie was set without a Domain attribute + */ + private $hostOnly = false; + /** * Create a new SetCookie object from a string. * @@ -37,7 +44,9 @@ class SetCookie // Create the default return array $data = self::$defaults; // Explode the cookie string using a series of semicolons - $pieces = \array_filter(\array_map('trim', \explode(';', $cookie))); + $pieces = \array_filter(\array_map(static function (string $piece): string { + return \trim($piece, " \n\r\t\0\x0B"); + }, \explode(';', $cookie))); // The name of the cookie (first kvp) must exist and include an equal sign. if (!isset($pieces[0]) || \strpos($pieces[0], '=') === false) { return new self($data); @@ -46,7 +55,7 @@ class SetCookie // Add the cookie pieces into the parsed data array foreach ($pieces as $part) { $cookieParts = \explode('=', $part, 2); - $key = \trim($cookieParts[0]); + $key = \trim($cookieParts[0], " \n\r\t\0\x0B"); $value = isset($cookieParts[1]) ? \trim($cookieParts[1], " \n\r\t\0\x0B") : true; @@ -57,7 +66,7 @@ class SetCookie $data['Value'] = $value; } else { foreach (\array_keys(self::$defaults) as $search) { - if (!\strcasecmp($search, $key)) { + if (Psr7\Utils::caselessEquals($search, $key)) { if ($search === 'Max-Age') { if (is_numeric($value)) { $data[$search] = (int) $value; @@ -72,6 +81,9 @@ class SetCookie continue 2; } } + if (Psr7\Utils::caselessEquals('HostOnly', $key)) { + continue; + } $data[$key] = $value; } } @@ -86,6 +98,14 @@ class SetCookie { $this->data = self::$defaults; + if (\array_key_exists('HostOnly', $data)) { + if (!\is_bool($data['HostOnly'])) { + throw new \InvalidArgumentException('Cookie field "HostOnly" must be a boolean'); + } + $this->setHostOnly($data['HostOnly']); + unset($data['HostOnly']); + } + if (isset($data['Name'])) { $this->setName($data['Name']); } @@ -128,18 +148,34 @@ class SetCookie } // Extract the Expires value and turn it into a UNIX timestamp if needed - if (!$this->getExpires() && $this->getMaxAge()) { + $maxAge = $this->getMaxAge(); + if (!$this->getExpires() && $maxAge !== null) { // Calculate the Expires date - $this->setExpires(\time() + $this->getMaxAge()); + $this->setExpires(self::maxAgeToExpires($maxAge, \time())); } elseif (null !== ($expires = $this->getExpires()) && !\is_numeric($expires)) { $this->setExpires($expires); } } + private static function maxAgeToExpires(int $maxAge, int $now): int + { + if ($maxAge <= 0) { + return $now - 1; + } + if ($maxAge > \PHP_INT_MAX - $now) { + return \PHP_INT_MAX; + } + + return $now + $maxAge; + } + public function __toString() { $str = $this->data['Name'].'='.($this->data['Value'] ?? '').'; '; foreach ($this->data as $k => $v) { + if ($k === 'Domain' && $this->getHostOnly()) { + continue; + } if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== false) { if ($k === 'Expires') { $str .= 'Expires='.\gmdate('D, d M Y H:i:s \G\M\T', $v).'; '; @@ -154,7 +190,12 @@ class SetCookie public function toArray(): array { - return $this->data; + $data = $this->data; + if ($this->getHostOnly()) { + $data['HostOnly'] = true; + } + + return $data; } /** @@ -229,6 +270,26 @@ class SetCookie $this->data['Domain'] = null === $domain ? null : (string) $domain; } + /** + * Get whether this cookie is scoped to the origin host only. + * + * @return bool + */ + public function getHostOnly() + { + return $this->hostOnly; + } + + /** + * Set whether this cookie is scoped to the origin host only. + * + * @param bool $hostOnly Set to true for host-only cookies + */ + public function setHostOnly(bool $hostOnly): void + { + $this->hostOnly = $hostOnly; + } + /** * Get the path. * @@ -401,7 +462,7 @@ class SetCookie $cookiePath = $this->getPath(); // Match on exact matches or when path is the default empty "/" - if ($cookiePath === '/' || $cookiePath == $requestPath) { + if ($cookiePath === '/' || $cookiePath === $requestPath) { return true; } @@ -428,27 +489,66 @@ class SetCookie { $cookieDomain = $this->getDomain(); if (null === $cookieDomain) { - return true; + return !$this->getHostOnly(); + } + + if ($this->getHostOnly()) { + return Psr7\Utils::asciiToLower($domain) === Psr7\Utils::asciiToLower($cookieDomain); } // Remove the leading '.' as per spec in RFC 6265. // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3 - $cookieDomain = \ltrim(\strtolower($cookieDomain), '.'); + $cookieDomain = Psr7\Utils::asciiToLower($cookieDomain); + if ($cookieDomain !== '' && $cookieDomain[0] === '.') { + /** @var string */ + $cookieDomain = \substr($cookieDomain, 1); + } + if ('' === $cookieDomain) { + return false; + } - $domain = \strtolower($domain); - - // Domain not set or exact match. - if ('' === $cookieDomain || $domain === $cookieDomain) { + $domain = Psr7\Utils::asciiToLower($domain); + if ($domain === $cookieDomain) { return true; } + // IP literals and numeric hosts are exact-match-only per RFC 6265. + // Only the exact match above may succeed for those cookie domains. + if (self::isIpAddressOrNumericHost($cookieDomain)) { + return false; + } + // Matching the subdomain according to RFC 6265. // https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3 if (\filter_var($domain, \FILTER_VALIDATE_IP)) { return false; } - return (bool) \preg_match('/\.'.\preg_quote($cookieDomain, '/').'$/', $domain); + return (bool) \preg_match('/\.'.\preg_quote($cookieDomain, '/').'$/D', $domain); + } + + private static function isIpAddressOrNumericHost(string $host): bool + { + // Strip one root dot before detection so trailing-dot numeric hosts + // still cannot be matched by subdomains. + if ($host !== '' && \str_ends_with($host, '.')) { + $host = \substr($host, 0, -1); + } + + if (\str_starts_with($host, '[') && \str_ends_with($host, ']')) { + $host = \substr($host, 1, -1); + } + + if (\filter_var($host, \FILTER_VALIDATE_IP) !== false) { + return true; + } + + // Public DNS names do not have an all-numeric rightmost label; treat + // those private/internal hosts as exact-match-only too. + $labels = \explode('.', $host); + $last = (string) \end($labels); + + return $last !== '' && \ctype_digit($last); } /** @@ -472,10 +572,7 @@ class SetCookie } // Check if any of the invalid characters are present in the cookie name - if (\preg_match( - '/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/', - $name - )) { + if (\preg_match('/[\x00-\x20\x22\x28-\x29\x2c\x2f\x3a-\x40\x5c\x7b\x7d\x7f]/', $name) !== 0) { return 'Cookie name must not contain invalid characters: ASCII ' .'Control characters (0-31;127), space, tab and the ' .'following characters: ()<>@,;:\"/?={}'; @@ -491,7 +588,7 @@ class SetCookie // Domains must not be empty, but may be omitted. "0" is not a valid // internet domain, but may be used as server name in a private network. $domain = $this->getDomain(); - if ($domain === '') { + if ($domain === '' || (null !== $domain && '' === \ltrim(\trim($domain, " \n\r\t\0\x0B"), '.'))) { return 'The cookie domain must not be empty'; } diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php index 65f6391..448700c 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlFactory.php @@ -4,10 +4,13 @@ namespace GuzzleHttp\Handler; use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Multiplexing; use GuzzleHttp\Promise as P; use GuzzleHttp\Promise\FulfilledPromise; use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Psr7; use GuzzleHttp\Psr7\LazyOpenStream; +use GuzzleHttp\Psr7\Uri; use GuzzleHttp\TransferStats; use GuzzleHttp\TransportSharing; use GuzzleHttp\Utils; @@ -23,6 +26,29 @@ class CurlFactory implements CurlFactoryInterface { public const CURL_VERSION_STR = 'curl_version'; + private const DELEGATED_PROXY_TUNNEL_OWNER = 'proxy-tunnel:delegated-to-libcurl'; + + /** + * String-valued proxy credential cURL options whose values feed the + * connection-reuse section signatures. Stringable values are cast + * exactly once, before signature computation, so the signature and + * ext-curl observe the same string; a stateful __toString() could + * otherwise produce one value for the signature and a different one on + * the wire, giving two credentials the same section. Numeric options + * (CURLOPT_PROXYTYPE, CURLOPT_PROXY_SSLVERSION) and blob options + * (CURLOPT_PROXY_SSLCERT_BLOB) are deliberately excluded. + */ + private const STRINGABLE_PROXY_CREDENTIAL_OPTIONS = [ + 'CURLOPT_PROXYUSERPWD', + 'CURLOPT_PROXYUSERNAME', + 'CURLOPT_PROXYPASSWORD', + 'CURLOPT_PROXY_SSLCERT', + 'CURLOPT_PROXY_SSLKEY', + 'CURLOPT_PROXY_KEYPASSWD', + 'CURLOPT_PROXY_TLSAUTH_USERNAME', + 'CURLOPT_PROXY_TLSAUTH_PASSWORD', + ]; + /** * @deprecated */ @@ -33,6 +59,17 @@ class CurlFactory implements CurlFactoryInterface */ private $handles = []; + /** + * @var string|null Owner signature of the proxy tunnels that pooled idle + * handles may still hold + */ + private $proxyTunnelOwner; + + /** + * @var bool Whether an in-domain handle has been pooled since the last purge + */ + private $poolMayHoldTunnels = false; + /** * @var int Total number of idle handles to keep in cache */ @@ -49,14 +86,35 @@ class CurlFactory implements CurlFactoryInterface private $shareMode; /** - * @param int $maxHandles Maximum number of idle handles. - * @param resource|\CurlShareHandle|null $shareHandle + * @var bool Whether the configured share handle may own a connection + * cache populated outside this factory + */ + private $opaqueShareConnectionCache = false; + + /** + * @param int $maxHandles Maximum number of idle handles. + * @param resource|\CurlShareHandle|CurlShareHandleState|null $shareHandle */ public function __construct(int $maxHandles, string $shareMode = TransportSharing::NONE, $shareHandle = null) { $this->maxHandles = $maxHandles; $this->shareMode = CurlShareHandleState::normalizeMode($shareMode, 'transport_sharing'); + if ($shareHandle instanceof CurlShareHandleState) { + if ($shareHandle->mode !== $this->shareMode) { + throw new \InvalidArgumentException('The cURL share handle state mode does not match the configured transport sharing mode.'); + } + + // A Guzzle-created handler-lifetime state locks only DNS and TLS + // session data, so its handle can never own a connection cache. + $shareHandle = $shareHandle->handle; + } elseif ($shareHandle !== null) { + // An externally supplied handle's lock set and cached contents + // cannot be inspected from PHP, so it may own a connection cache + // populated outside this factory. + $this->opaqueShareConnectionCache = true; + } + if ($this->shareMode === TransportSharing::NONE && $shareHandle !== null) { throw new \InvalidArgumentException('A cURL share handle cannot be provided when transport sharing is disabled.'); } @@ -86,17 +144,47 @@ class CurlFactory implements CurlFactoryInterface public function create(RequestInterface $request, array $options): EasyHandle { + self::validateRequestUriScheme($request); + + if (isset($options['on_trailers']) && !\is_callable($options['on_trailers'])) { + throw new \InvalidArgumentException('on_trailers must be callable'); + } + $protocolVersion = $request->getProtocolVersion(); if ('' === $protocolVersion) { \trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Sending a request with an empty protocol version is deprecated; guzzlehttp/guzzle 8.0 will reject empty protocol versions.'); $protocolVersion = '1.1'; - $request = \GuzzleHttp\Psr7\Utils::modifyRequest($request, ['version' => $protocolVersion]); + $request = Psr7\Utils::modifyRequest($request, ['version' => $protocolVersion]); + } + + $multiplex = self::normalizeMultiplex($options); + $requiredMultiplex = \in_array($multiplex, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true); + + if ($requiredMultiplex && isset($options['curl']) && \is_array($options['curl'])) { + $requiredModeConflicts = [ + \CURLOPT_HTTP_VERSION => ['CURLOPT_HTTP_VERSION', 'the request protocol version'], + \CURLOPT_URL => ['CURLOPT_URL', 'the request URI'], + \CURLOPT_FOLLOWLOCATION => ['CURLOPT_FOLLOWLOCATION', 'the "allow_redirects" request option'], + ]; + + foreach ($requiredModeConflicts as $option => [$name, $replacement]) { + if (\array_key_exists($option, $options['curl'])) { + // Key presence alone conflicts: whatever the raw value, + // it is a second authority over the protocol or route, + // applied after the required mode's decisions. + throw new \InvalidArgumentException(\sprintf('The "multiplex" request option cannot be required when the raw %s cURL option is set; remove the raw option and use %s instead.', $name, $replacement)); + } + } } if ('2' === $protocolVersion || '2.0' === $protocolVersion) { - if (!self::supportsHttp2()) { + if (!CurlVersion::supportsHttp2()) { + if ($requiredMultiplex) { + throw new ConnectException('Required multiplexing needs libcurl 8.14.0 or newer built with HTTP/2 support.', $request); + } + throw new ConnectException('HTTP/2 is supported by the cURL handler, however libcurl is built without HTTP/2 support.', $request); } } elseif ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) { @@ -109,9 +197,20 @@ class CurlFactory implements CurlFactoryInterface } self::triggerUnsupportedRequestOptionDeprecations($options); - $this->rejectRequestLevelShareConflict($options); + self::triggerUnsupportedCurlOptionDeprecations($options); self::triggerConflictingCurlOptionDeprecations($options); + // Capture the managed Proxy-Authorization values before header + // serialization so they never enter the origin header list, and + // record whether a deprecated raw CURLOPT_HTTPHEADER value replaces + // every generated header, the managed values included. Key presence + // alone replaces: an empty or null raw value still suppresses the + // generated list. + $managedProxyAuthorization = self::managedProxyAuthorizationHeaderLines($request); + $rawHttpHeadersReplaceManaged = isset($options['curl']) + && \is_array($options['curl']) + && \array_key_exists(\CURLOPT_HTTPHEADER, $options['curl']); + $easy = new EasyHandle(); $easy->request = $request; $easy->options = $options; @@ -126,6 +225,47 @@ class CurlFactory implements CurlFactoryInterface $conf = \array_replace($conf, $options['curl']); } + self::assertFinalProxyOptionTypes($conf, $requiredMultiplex && 'https' !== $request->getUri()->getScheme()); + self::isolatePreProxyOnAffectedCurl($conf); + self::normalizeStringableProxyCredentialOptions($conf); + + if ($requiredMultiplex) { + self::assertRequiredMultiplexRouteDirect($easy, $conf); + self::assertRequiredMultiplexAuthSupported($conf); + } + + self::normalizeCurlHeaderOptions($conf); + self::applyProxyAuthorizationHeaderHandling($request, $conf); + self::applyManagedProxyAuthorization($request, $conf, $managedProxyAuthorization, $rawHttpHeadersReplaceManaged); + // Validate the appended managed lines too: a custom RequestInterface + // can bypass a normal PSR-7 implementation's header validation. + self::normalizeCurlHeaderOptions($conf); + $this->rejectRequestLevelShareConflict($options); + self::rejectRequestLevelShareWithProxyAuth($request, $options, $conf); + + if ($this->shareHandle !== null) { + // Conservative blanket mode: a configured share handle hides the + // pooled connections' provenance, so sectioned reuse cannot reason + // about them. + self::forceFreshConnectionForAuthenticatedProxy($request, $conf); + $this->isolateOpaqueShareAnonymousProxyTunnel($request, $conf); + } else { + $signature = self::proxyTunnelSignature($request, $conf); + $easy->proxyTunnelSignature = $signature; + if ($signature !== null && $signature !== $this->proxyTunnelOwner) { + if ($this->poolMayHoldTunnels) { + // Pooled idle handles may hold a different owner's tunnel. + $this->discardIdleHandles(); + $this->poolMayHoldTunnels = false; + } + // The first in-domain owner latches without purging: the pool + // provably holds no in-domain tunnel yet. + $this->proxyTunnelOwner = $signature; + } + } + + $easy->effectiveProxy = self::getEffectiveProxy($conf); + $conf[\CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy); if ($this->shareHandle !== null) { if (!\defined('CURLOPT_SHARE')) { @@ -135,6 +275,10 @@ class CurlFactory implements CurlFactoryInterface $conf[(int) \constant('CURLOPT_SHARE')] = $this->shareHandle; } + if (\defined('CURLOPT_PIPEWAIT')) { + $easy->usesPipewait = !empty($conf[(int) \constant('CURLOPT_PIPEWAIT')]); + } + $handle = $this->handles ? \array_pop($this->handles) : \curl_init(); if (false === $handle) { throw new \RuntimeException('Can not initialize cURL handle.'); @@ -192,6 +336,39 @@ class CurlFactory implements CurlFactoryInterface } } + /** + * @param array $conf + */ + private static function normalizeStringableProxyCredentialOptions(array &$conf): void + { + foreach (self::STRINGABLE_PROXY_CREDENTIAL_OPTIONS as $name) { + if (!\defined($name)) { + continue; + } + + $option = (int) \constant($name); + if (!isset($conf[$option]) || !\is_object($conf[$option]) || !\method_exists($conf[$option], '__toString')) { + continue; + } + + try { + $conf[$option] = (string) $conf[$option]; + } catch (\Throwable $e) { + // Wrap the failure exactly as applyCurlOptions() does for a + // value that cannot be applied. + throw new \InvalidArgumentException( + \sprintf( + 'Unable to set cURL option %s: %s', + self::formatCurlOption($option), + $e->getMessage() + ), + 0, + $e + ); + } + } + } + private function rejectRequestLevelShareConflict(array $options): void { if ($this->shareHandle === null) { @@ -210,6 +387,259 @@ class CurlFactory implements CurlFactoryInterface throw new \InvalidArgumentException('The request-level CURLOPT_SHARE cURL option cannot be combined with configured transport sharing.'); } + private static function normalizeMultiplex(array $options): ?string + { + $multiplex = $options['multiplex'] ?? null; + + if ($multiplex === null) { + // Absent/null leaves multiplexing to libcurl: no CURLOPT_PIPEWAIT + // is written and no guarantees apply. + return null; + } + + if (!\in_array($multiplex, [Multiplexing::NONE, Multiplexing::EAGER, Multiplexing::WAIT, Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) { + throw new \InvalidArgumentException(\sprintf( + 'The "multiplex" option must be null or a GuzzleHttp\\Multiplexing::* constant; received %s.', + \get_debug_type($multiplex) + )); + } + + return $multiplex; + } + + private static function assertRequiredMultiplexSupported(EasyHandle $easy): void + { + if (!CurlVersion::supportsRequiredMultiplex()) { + throw new ConnectException('Required multiplexing needs libcurl 8.14.0 or newer built with HTTP/2 support.', $easy->request); + } + } + + /** + * Required multiplexing sends cleartext requests with HTTP/2 prior + * knowledge, which an HTTP proxy hop silently downgrades, so the request + * must reach the origin directly. The check runs against the final + * merged cURL configuration because deprecated raw proxy options are + * applied after Guzzle's own decisions and may add, replace, or disable + * the selected proxy. Value types that ext-curl would coerce are + * rejected as ambiguous, and only the exact CURLOPT_NOPROXY wildcard '*' + * counts as disabling the primary proxy and pre-proxy: host-specific + * patterns are conservatively treated as leaving them active. + * + * @param array $conf + */ + private static function assertRequiredMultiplexRouteDirect(EasyHandle $easy, array $conf): void + { + if ('https' === $easy->request->getUri()->getScheme()) { + return; + } + + $proxyOptions = [\CURLOPT_PROXY => 'CURLOPT_PROXY']; + if (\defined('CURLOPT_NOPROXY')) { + $proxyOptions[(int) \constant('CURLOPT_NOPROXY')] = 'CURLOPT_NOPROXY'; + } + if (\defined('CURLOPT_PRE_PROXY')) { + $proxyOptions[(int) \constant('CURLOPT_PRE_PROXY')] = 'CURLOPT_PRE_PROXY'; + } + + foreach ($proxyOptions as $option => $name) { + if (\array_key_exists($option, $conf) && !\is_string($conf[$option])) { + throw new \InvalidArgumentException(\sprintf('The "multiplex" request option cannot be required when the final %s cURL option value is not a string.', $name)); + } + } + + if (\defined('CURLOPT_NOPROXY') && ($conf[(int) \constant('CURLOPT_NOPROXY')] ?? null) === '*') { + // libcurl's exact wildcard disables the primary proxy and the + // pre-proxy together, leaving a direct route. + return; + } + + if (self::getEffectiveProxy($conf) !== null + || (\defined('CURLOPT_PRE_PROXY') && ($conf[(int) \constant('CURLOPT_PRE_PROXY')] ?? '') !== '') + ) { + throw new ConnectException('Required multiplexing cannot be guaranteed for cleartext requests sent through a proxy.', $easy->request); + } + } + + /** + * libcurl forces NTLM-authenticated transfers onto HTTP/1.1: when the + * server picks NTLM from the offered mask, the connection is closed and + * the request is retried over HTTP/1.1 whatever HTTP version was asked + * for, silently defeating the required protocol guarantee on both + * cleartext and TLS routes. The final merged mask is checked so the + * deprecated "auth" request option and the raw CURLOPT_HTTPAUTH cURL + * option are both covered, and any mask permitting NTLM, such as + * CURLAUTH_ANY, is rejected because the selection is server-controlled. + * + * @param array $conf + */ + private static function assertRequiredMultiplexAuthSupported(array $conf): void + { + if (!\array_key_exists(\CURLOPT_HTTPAUTH, $conf)) { + return; + } + + $auth = $conf[\CURLOPT_HTTPAUTH]; + if (!\is_scalar($auth)) { + throw new \InvalidArgumentException('The "multiplex" request option cannot be required when the final CURLOPT_HTTPAUTH cURL option value is not an integer.'); + } + + $ntlmBits = \CURLAUTH_NTLM; + if (\defined('CURLAUTH_NTLM_WB')) { + $ntlmBits |= (int) \constant('CURLAUTH_NTLM_WB'); + } + + if (((int) $auth & $ntlmBits) !== 0) { + throw new \InvalidArgumentException('The "multiplex" request option cannot be required when the final CURLOPT_HTTPAUTH cURL option value permits NTLM; libcurl retries NTLM authentication over HTTP/1.1.'); + } + } + + /** + * @param mixed $proxyConf + */ + private static function assertResolvedProxySupported(RequestInterface $request, $proxyConf): void + { + if (!\is_string($proxyConf) || $proxyConf === '') { + return; + } + + $scheme = self::proxyScheme($proxyConf); + if ($scheme !== null && \preg_match('/^[a-z][a-z0-9.+-]*$/D', $scheme) !== 1) { + throw new RequestException('The proxy URL is malformed.', $request); + } + + if ($scheme === 'https' && !CurlVersion::supportsHttpsProxy()) { + throw new RequestException('HTTPS proxies are not supported by the installed libcurl; libcurl 7.52.0 or newer built with HTTPS-proxy support is required.', $request); + } + } + + /** + * @return array{0: mixed, 1: string} + */ + private static function resolveProxy(RequestInterface $request, array $options): array + { + $proxyConf = null; + $noProxyConf = ''; + + if (isset($options['proxy'])) { + if (!\is_array($options['proxy'])) { + $proxyConf = $options['proxy']; + } else { + $scheme = $request->getUri()->getScheme(); + if (isset($options['proxy'][$scheme])) { + if ( + isset($options['proxy']['no']) + && Utils::isUriInNoProxy($request->getUri(), $options['proxy']['no']) + ) { + $proxyConf = ''; + $noProxyConf = '*'; + } else { + $proxyConf = $options['proxy'][$scheme]; + } + } + } + } + + if ($proxyConf === null) { + $proxyConf = ProxyEnvironment::getProxyForScheme($request->getUri()->getScheme()); + if ($proxyConf === null) { + $proxyConf = ''; + } elseif ( + ($noProxy = ProxyEnvironment::getNoProxy()) !== null + && Utils::isUriInNoProxy($request->getUri(), ProxyEnvironment::splitNoProxy($noProxy)) + ) { + $proxyConf = ''; + $noProxyConf = '*'; + } + } + + return [$proxyConf, $noProxyConf]; + } + + /** + * @param array $conf + */ + private static function rejectRequestLevelShareWithProxyAuth(RequestInterface $request, array $options, array $conf): void + { + if (!self::hasRequestLevelCurlShare($options)) { + return; + } + + $proxy = self::getEffectiveProxy($conf); + if ($proxy === null) { + return; + } + + // An external share handle may pool SOCKS connections where no section + // signature can reach them. On affected libcurl, even an anonymous + // request could inherit authenticated state already in that pool. + if (self::isSocksProxy($proxy, $conf)) { + if (!CurlVersion::supportsSocksProxyCredentialAwareConnectionReuse()) { + throw new \InvalidArgumentException('The request-level CURLOPT_SHARE cURL option cannot be combined with SOCKS proxy configuration on libcurl before 7.69.0; use Guzzle-managed "transport_sharing" or a custom handler/factory instead.'); + } + + if (self::hasAuthenticatedSocksProxyState($proxy, $conf)) { + throw new \InvalidArgumentException('The request-level CURLOPT_SHARE cURL option cannot be combined with authenticated SOCKS proxy configuration; use Guzzle-managed "transport_sharing" or a custom handler/factory instead.'); + } + } + + if ( + !self::usesProxyTunnel($request, $conf) + || !self::isHttpProxyForConnectionReuse($proxy, $conf) + ) { + return; + } + + if (self::hasAuthenticatedHttpProxyState($proxy, $conf)) { + throw new \InvalidArgumentException('The request-level CURLOPT_SHARE cURL option cannot be combined with authenticated HTTP/HTTPS proxy tunnel configuration; use Guzzle-managed "transport_sharing" or a custom handler/factory instead.'); + } + + // From libcurl 7.57.0 the external share can also own a connection + // cache seeded outside Guzzle with tunnel identity libcurl cannot + // key, so anonymous tunnels are rejected there too. + if (CurlVersion::supportsShareConnectionCaches()) { + throw new \InvalidArgumentException('The request-level CURLOPT_SHARE cURL option cannot be combined with HTTP/HTTPS proxy tunnel configuration on libcurl 7.57.0 or newer; use Guzzle-managed "transport_sharing" or a custom handler/factory instead.'); + } + } + + private static function hasRequestLevelCurlShare(array $options): bool + { + return \defined('CURLOPT_SHARE') + && isset($options['curl']) + && \is_array($options['curl']) + && \array_key_exists((int) \constant('CURLOPT_SHARE'), $options['curl']); + } + + /** + * @param array $conf + */ + private static function hasAuthenticatedHttpProxyState(string $proxy, array $conf): bool + { + $proxyForParsing = \strpos($proxy, '://') === false ? 'http://'.$proxy : $proxy; + $proxyParts = \parse_url($proxyForParsing); + + if ( + \is_array($proxyParts) + && (\array_key_exists('user', $proxyParts) || \array_key_exists('pass', $proxyParts)) + ) { + return true; + } + + if (self::hasCurlProxyCredentials($conf)) { + return true; + } + + if (self::hasCurlProxyAuthorizationHeader($conf)) { + return true; + } + + $httpHeaders = $conf[\CURLOPT_HTTPHEADER] ?? []; + if (\is_array($httpHeaders) && self::proxyAuthorizationHeaderValuesFromList($httpHeaders) !== []) { + return true; + } + + return self::hasCurlProxyTlsCredentials($conf); + } + /** * @param int|string $option */ @@ -244,6 +674,7 @@ class CurlFactory implements CurlFactoryInterface } $conflictingOptions = self::conflictingCurlOptions(); + $sinceOverrides = self::conflictingCurlOptionSinceOverrides(); foreach ($options['curl'] as $option => $_) { if (!\array_key_exists($option, $conflictingOptions)) { @@ -252,10 +683,11 @@ class CurlFactory implements CurlFactoryInterface $name = self::formatCurlOption($option); $replacement = $conflictingOptions[$option]; + $since = $sinceOverrides[$option] ?? '7.11'; if ($replacement !== null) { \trigger_deprecation( 'guzzlehttp/guzzle', - '7.11', + $since, \sprintf( 'Passing %s in the "curl" request option is deprecated; guzzlehttp/guzzle 8.0 will reject this option because it conflicts with Guzzle-managed request handling. Use %s instead.', $name, @@ -268,7 +700,7 @@ class CurlFactory implements CurlFactoryInterface \trigger_deprecation( 'guzzlehttp/guzzle', - '7.11', + $since, \sprintf( 'Passing %s in the "curl" request option is deprecated; guzzlehttp/guzzle 8.0 will reject this option because it conflicts with Guzzle-managed cURL internals.', $name @@ -277,6 +709,50 @@ class CurlFactory implements CurlFactoryInterface } } + private static function triggerUnsupportedCurlOptionDeprecations(array $options): void + { + if (!isset($options['curl']) || !\is_array($options['curl']) || $options['curl'] === []) { + return; + } + + if ( + \defined('CURLOPT_PROXYHEADER') + && \array_key_exists((int) \constant('CURLOPT_PROXYHEADER'), $options['curl']) + && !CurlVersion::supportsProxyHeaderSeparation() + ) { + \trigger_deprecation( + 'guzzlehttp/guzzle', + '7.15', + \sprintf( + 'Passing %s in the "curl" request option on a build without proxy header separation support is deprecated; guzzlehttp/guzzle 8.0 will reject this configuration because proxy headers require libcurl 7.37.0 or newer built with proxy header separation support.', + self::formatCurlOption((int) \constant('CURLOPT_PROXYHEADER')) + ) + ); + } + + $supportedOptions = self::supportedCurlOptions(); + $conflictingOptions = self::conflictingCurlOptions(); + + foreach ($options['curl'] as $option => $_) { + if ( + !\is_int($option) + || \array_key_exists($option, $supportedOptions) + || \array_key_exists($option, $conflictingOptions) + ) { + continue; + } + + \trigger_deprecation( + 'guzzlehttp/guzzle', + '7.12', + \sprintf( + 'Passing %s in the "curl" request option is deprecated; guzzlehttp/guzzle 8.0 will reject raw cURL options outside the built-in cURL handlers\' allow-list.', + self::formatCurlOption($option) + ) + ); + } + } + private static function triggerUnsupportedRequestOptionDeprecations(array $options): void { if (\array_key_exists('stream_context', $options)) { @@ -318,8 +794,6 @@ class CurlFactory implements CurlFactoryInterface self::addConflictingCurlOption($options, 'CURLOPT_HEADERFUNCTION', 'the "on_headers" request option'); self::addConflictingCurlOption($options, 'CURLOPT_WRITEFUNCTION', 'the "sink" request option'); self::addConflictingCurlOption($options, 'CURLOPT_FILE', 'the "sink" request option'); - self::addConflictingCurlOption($options, 'CURLOPT_RETURNTRANSFER', null); - self::addConflictingCurlOption($options, 'CURLOPT_HEADER', null); self::addConflictingCurlOption($options, 'CURLOPT_TIMEOUT', 'the "timeout" request option'); self::addConflictingCurlOption($options, 'CURLOPT_TIMEOUT_MS', 'the "timeout" request option'); self::addConflictingCurlOption($options, 'CURLOPT_CONNECTTIMEOUT', 'the "connect_timeout" request option'); @@ -332,6 +806,7 @@ class CurlFactory implements CurlFactoryInterface self::addConflictingCurlOption($options, 'CURLOPT_STDERR', 'the "debug" request option'); self::addConflictingCurlOption($options, 'CURLOPT_PROXY', 'the "proxy" request option'); self::addConflictingCurlOption($options, 'CURLOPT_NOPROXY', 'the "proxy" request option'); + self::addConflictingCurlOption($options, 'CURLOPT_PROXYTYPE', 'the "proxy" request option with a scheme-prefixed URL'); self::addConflictingCurlOption($options, 'CURLOPT_FOLLOWLOCATION', 'the "allow_redirects" request option'); self::addConflictingCurlOption($options, 'CURLOPT_MAXREDIRS', 'the "allow_redirects" request option'); self::addConflictingCurlOption($options, 'CURLOPT_POSTREDIR', 'the "allow_redirects" request option'); @@ -339,14 +814,14 @@ class CurlFactory implements CurlFactoryInterface self::addConflictingCurlOption($options, 'CURLOPT_REDIR_PROTOCOLS_STR', 'the "allow_redirects" request option'); self::addConflictingCurlOption($options, 'CURLOPT_PROTOCOLS', 'the "protocols" request option'); self::addConflictingCurlOption($options, 'CURLOPT_PROTOCOLS_STR', 'the "protocols" request option'); - self::addConflictingCurlOption($options, 'CURLOPT_HTTP09_ALLOWED', null); self::addConflictingCurlOption($options, 'CURLOPT_HTTP_VERSION', 'the request protocol version'); + self::addConflictingCurlOption($options, 'CURLOPT_PIPEWAIT', 'the "multiplex" request option'); self::addConflictingCurlOption($options, 'CURLOPT_IPRESOLVE', 'the "force_ip_resolve" request option'); self::addConflictingCurlOption($options, 'CURLOPT_SSL_VERIFYPEER', 'the "verify" request option'); self::addConflictingCurlOption($options, 'CURLOPT_SSL_VERIFYHOST', 'the "verify" request option'); self::addConflictingCurlOption($options, 'CURLOPT_CAINFO', 'the "verify" request option'); self::addConflictingCurlOption($options, 'CURLOPT_CAPATH', 'the "verify" request option'); - self::addConflictingCurlOption($options, 'CURLOPT_SSLVERSION', 'the "crypto_method" request option'); + self::addConflictingCurlOption($options, 'CURLOPT_SSLVERSION', 'the "crypto_method" or "crypto_method_max" request option'); self::addConflictingCurlOption($options, 'CURLOPT_SSLCERT', 'the "cert" request option'); self::addConflictingCurlOption($options, 'CURLOPT_SSLCERTPASSWD', 'the "cert" request option'); self::addConflictingCurlOption($options, 'CURLOPT_SSLCERTTYPE', 'the "cert_type" request option'); @@ -363,6 +838,100 @@ class CurlFactory implements CurlFactoryInterface return $options; } + /** + * @return array + */ + private static function conflictingCurlOptionSinceOverrides(): array + { + static $options = null; + + if ($options !== null) { + return $options; + } + + $options = []; + + if (\defined('CURLOPT_PROXYTYPE')) { + $options[\CURLOPT_PROXYTYPE] = '7.12'; + } + + if (\defined('CURLOPT_PIPEWAIT')) { + $options[\CURLOPT_PIPEWAIT] = '7.14'; + } + + return $options; + } + + /** + * @return array + */ + private static function supportedCurlOptions(): array + { + static $options = null; + + if ($options !== null) { + return $options; + } + + $options = []; + + self::addSupportedCurlOption($options, 'CURLOPT_ADDRESS_SCOPE'); + self::addSupportedCurlOption($options, 'CURLOPT_CERTINFO'); + self::addSupportedCurlOption($options, 'CURLOPT_CONNECT_TO'); + self::addSupportedCurlOption($options, 'CURLOPT_DNS_CACHE_TIMEOUT'); + self::addSupportedCurlOption($options, 'CURLOPT_DNS_INTERFACE'); + self::addSupportedCurlOption($options, 'CURLOPT_DNS_LOCAL_IP4'); + self::addSupportedCurlOption($options, 'CURLOPT_DNS_LOCAL_IP6'); + self::addSupportedCurlOption($options, 'CURLOPT_DNS_SERVERS'); + self::addSupportedCurlOption($options, 'CURLOPT_DNS_SHUFFLE_ADDRESSES'); + self::addSupportedCurlOption($options, 'CURLOPT_ENCODING'); + self::addSupportedCurlOption($options, 'CURLOPT_FORBID_REUSE'); + self::addSupportedCurlOption($options, 'CURLOPT_FRESH_CONNECT'); + self::addSupportedCurlOption($options, 'CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS'); + self::addSupportedCurlOption($options, 'CURLOPT_HTTPAUTH'); + self::addSupportedCurlOption($options, 'CURLOPT_INTERFACE'); + self::addSupportedCurlOption($options, 'CURLOPT_LOCALPORT'); + self::addSupportedCurlOption($options, 'CURLOPT_LOCALPORTRANGE'); + self::addSupportedCurlOption($options, 'CURLOPT_LOW_SPEED_LIMIT'); + self::addSupportedCurlOption($options, 'CURLOPT_LOW_SPEED_TIME'); + self::addSupportedCurlOption($options, 'CURLOPT_MAXAGE_CONN'); + self::addSupportedCurlOption($options, 'CURLOPT_MAXCONNECTS'); + self::addSupportedCurlOption($options, 'CURLOPT_MAXLIFETIME_CONN'); + self::addSupportedCurlOption($options, 'CURLOPT_HTTPPROXYTUNNEL'); + self::addSupportedCurlOption($options, 'CURLOPT_PREREQFUNCTION'); + self::addSupportedCurlOption($options, 'CURLOPT_PROXYHEADER'); + self::addSupportedCurlOption($options, 'CURLOPT_PROXYUSERPWD'); + self::addSupportedCurlOption($options, 'CURLOPT_RESOLVE'); + self::addSupportedCurlOption($options, 'CURLOPT_SSL_CIPHER_LIST'); + self::addSupportedCurlOption($options, 'CURLOPT_SSL_EC_CURVES'); + self::addSupportedCurlOption($options, 'CURLOPT_TCP_FASTOPEN'); + self::addSupportedCurlOption($options, 'CURLOPT_TCP_KEEPALIVE'); + self::addSupportedCurlOption($options, 'CURLOPT_TCP_KEEPIDLE'); + self::addSupportedCurlOption($options, 'CURLOPT_TCP_KEEPINTVL'); + self::addSupportedCurlOption($options, 'CURLOPT_TCP_KEEPCNT'); + self::addSupportedCurlOption($options, 'CURLOPT_TCP_NODELAY'); + self::addSupportedCurlOption($options, 'CURLOPT_TLS13_CIPHERS'); + self::addSupportedCurlOption($options, 'CURLOPT_UNIX_SOCKET_PATH'); + self::addSupportedCurlOption($options, 'CURLOPT_USERPWD'); + + return $options; + } + + /** + * @param array $options + */ + private static function addSupportedCurlOption(array &$options, string $constant): void + { + if (!\defined($constant)) { + return; + } + + $value = \constant($constant); + if (\is_int($value)) { + $options[$value] = true; + } + } + /** * @param array $options */ @@ -378,63 +947,44 @@ class CurlFactory implements CurlFactoryInterface } } - private static function supportsHttp2(): bool - { - static $supportsHttp2 = null; - - if (null === $supportsHttp2) { - $supportsHttp2 = self::supportsTls12() - && defined('CURL_VERSION_HTTP2') - && (\CURL_VERSION_HTTP2 & \curl_version()['features']); - } - - return $supportsHttp2; - } - - private static function supportsTls12(): bool - { - static $supportsTls12 = null; - - if (null === $supportsTls12) { - $supportsTls12 = \CURL_SSLVERSION_TLSv1_2 & \curl_version()['features']; - } - - return $supportsTls12; - } - - private static function supportsTls13(): bool - { - static $supportsTls13 = null; - - if (null === $supportsTls13) { - $supportsTls13 = defined('CURL_SSLVERSION_TLSv1_3') - && (\CURL_SSLVERSION_TLSv1_3 & \curl_version()['features']); - } - - return $supportsTls13; - } - public function release(EasyHandle $easy): void { $resource = $easy->handle; unset($easy->handle); - if (\count($this->handles) >= $this->maxHandles) { + if ( + \count($this->handles) >= $this->maxHandles + || ($easy->proxyTunnelSignature !== null && $easy->proxyTunnelSignature !== $this->proxyTunnelOwner) + ) { + // Pool is full, or this handle belongs to a superseded tunnel + // owner (an async create/release overlap can hand a stale-owner + // handle back after a purge) - drop it instead of pooling it. if (PHP_VERSION_ID < 80000) { \curl_close($resource); } - } else { - // Remove all callback functions as they can hold onto references - // and are not cleaned up by curl_reset. Using curl_setopt_array - // does not work for some reason, so removing each one - // individually. - \curl_setopt($resource, \CURLOPT_HEADERFUNCTION, null); - \curl_setopt($resource, \CURLOPT_READFUNCTION, null); - \curl_setopt($resource, \CURLOPT_WRITEFUNCTION, null); - \curl_setopt($resource, \CURLOPT_PROGRESSFUNCTION, null); - \curl_reset($resource); - $this->handles[] = $resource; + + return; } + + if ($easy->proxyTunnelSignature !== null) { + // A pooled handle now carries the current owner's tunnel. + $this->poolMayHoldTunnels = true; + } + + // Remove all callback functions as they can hold onto references and + // are not cleaned up by curl_reset. Using curl_setopt_array does not + // work for some reason, so removing each one individually. + \curl_setopt($resource, \CURLOPT_HEADERFUNCTION, null); + \curl_setopt($resource, \CURLOPT_READFUNCTION, null); + \curl_setopt($resource, \CURLOPT_WRITEFUNCTION, null); + \curl_setopt($resource, \CURLOPT_PROGRESSFUNCTION, null); + + if (\defined('CURLOPT_PREREQFUNCTION')) { + \curl_setopt($resource, (int) \constant('CURLOPT_PREREQFUNCTION'), null); + } + + \curl_reset($resource); + $this->handles[] = $resource; } /** @@ -447,7 +997,17 @@ class CurlFactory implements CurlFactoryInterface public static function finish(callable $handler, EasyHandle $easy, CurlFactoryInterface $factory): PromiseInterface { if (isset($easy->options['on_stats'])) { - self::invokeStats($easy); + try { + self::invokeStats($easy); + } catch (\Throwable $e) { + try { + $factory->release($easy); + } catch (\Throwable $releaseFailure) { + // Keep the on_stats throwable as the visible failure. + } + + throw $e; + } } if (!$easy->response || $easy->errno) { @@ -463,6 +1023,21 @@ class CurlFactory implements CurlFactoryInterface $body->rewind(); } + if (isset($easy->options['on_trailers'])) { + try { + ($easy->options['on_trailers'])(self::headersFromTrailerLines($easy->trailers), $easy->response); + } catch (\Throwable $e) { + return P\Create::rejectionFor( + new RequestException( + 'An error was encountered during the on_trailers event', + $easy->request, + $easy->response, + $e + ) + ); + } + } + return new FulfilledPromise($easy->response); } @@ -491,7 +1066,7 @@ class CurlFactory implements CurlFactoryInterface 'error' => \curl_error($easy->handle), 'appconnect_time' => \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME), ] + \curl_getinfo($easy->handle); - $ctx[self::CURL_VERSION_STR] = self::getCurlVersion(); + $ctx[self::CURL_VERSION_STR] = CurlVersion::getVersion() ?? ''; $factory->release($easy); // Retry when nothing is present or when curl failed to rewind. @@ -502,17 +1077,6 @@ class CurlFactory implements CurlFactoryInterface return self::createRejection($easy, $ctx); } - private static function getCurlVersion(): string - { - static $curlVersion = null; - - if (null === $curlVersion) { - $curlVersion = \curl_version()['version']; - } - - return $curlVersion; - } - private static function createRejection(EasyHandle $easy, array $ctx): PromiseInterface { static $connectionErrors = [ @@ -523,6 +1087,12 @@ class CurlFactory implements CurlFactoryInterface \CURLE_GOT_NOTHING => true, ]; + $uri = $easy->request->getUri(); + + // Redact the native error before it reaches any exception so the + // handler context matches the sanitized exception message. + $ctx['error'] = self::sanitizeCurlError((string) ($ctx['error'] ?? ''), $uri, $easy->effectiveProxy); + if ($easy->createResponseException) { return P\Create::rejectionFor( new RequestException( @@ -549,19 +1119,17 @@ class CurlFactory implements CurlFactoryInterface ); } - $uri = $easy->request->getUri(); - - $sanitizedError = self::sanitizeCurlError($ctx['error'] ?? '', $uri); + $sanitizedError = $ctx['error']; $message = \sprintf( 'cURL error %s: %s (%s)', $ctx['errno'], $sanitizedError, - 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html' + 'see https://curl.se/libcurl/c/libcurl-errors.html' ); if ('' !== $sanitizedError) { - $redactedUriString = \GuzzleHttp\Psr7\Utils::redactUserInfo($uri)->__toString(); + $redactedUriString = Psr7\Utils::redactUserInfo($uri)->__toString(); if ($redactedUriString !== '' && false === \strpos($sanitizedError, $redactedUriString)) { $message .= \sprintf(' for %s', $redactedUriString); } @@ -575,12 +1143,14 @@ class CurlFactory implements CurlFactoryInterface return P\Create::rejectionFor($error); } - private static function sanitizeCurlError(string $error, UriInterface $uri): string + private static function sanitizeCurlError(string $error, UriInterface $uri, ?string $proxy = null): string { if ('' === $error) { return $error; } + $error = self::redactProxyUserInfo($error, $proxy); + $baseUri = $uri->withQuery('')->withFragment(''); $baseUriString = $baseUri->__toString(); @@ -588,42 +1158,797 @@ class CurlFactory implements CurlFactoryInterface return $error; } - $redactedUriString = \GuzzleHttp\Psr7\Utils::redactUserInfo($baseUri)->__toString(); + $redactedUriString = Psr7\Utils::redactUserInfo($baseUri)->__toString(); return str_replace($baseUriString, $redactedUriString, $error); } + private static function redactProxyUserInfo(string $error, ?string $proxy): string + { + if ($proxy === null || $proxy === '' || \strpos($proxy, '@') === false) { + return $error; + } + + // The error message embeds the proxy string exactly as configured, so + // the userinfo needle is taken verbatim from the raw string: + // parse_url() and Psr7\Uri normalize the components, e.g. by rewriting + // raw control bytes to '_', which could make the replacement miss. + $proxyForParsing = \strpos($proxy, '://') === false ? 'http://'.$proxy : $proxy; + $remainder = \substr($proxyForParsing, \strpos($proxyForParsing, '://') + 3); + + if (\parse_url($proxyForParsing) === false) { + // Raw '/', '?', or '#' separators may sit inside the credentials + // of a proxy that defeats parse_url(), so the redaction cannot + // stop at the apparent authority. + $atPosition = \strrpos($remainder, '@'); + + if ($atPosition === false || $atPosition === 0) { + return $error; + } + + return \str_replace(\substr($remainder, 0, $atPosition).'@', '***@', $error); + } + + $authority = \substr($remainder, 0, \strcspn($remainder, '/?#')); + $atPosition = \strrpos($authority, '@'); + + if ($atPosition === false || $atPosition === 0) { + // A parseable proxy URL with '@' only past its authority, or with + // an empty userinfo, carries no credentials to redact. + return $error; + } + + $rawUserInfo = \substr($authority, 0, $atPosition); + + // Redact with the same policy Psr7\Utils::redactUserInfo() applies to + // request URIs, so the bundled psr7 version governs the redacted form. + $redactedUserInfo = '***'; + + try { + $proxyUri = new Uri($proxyForParsing); + $redactedUserInfo = Psr7\Utils::redactUserInfo($proxyUri)->getUserInfo(); + + if ($redactedUserInfo === $proxyUri->getUserInfo()) { + return $error; + } + } catch (\InvalidArgumentException $e) { + // Unparseable as a URI: fall back to redacting the whole userinfo. + } + + return \str_replace($rawUserInfo.'@', $redactedUserInfo.'@', $error); + } + + /** + * @param array $conf + */ + private static function forceFreshConnectionForAuthenticatedProxy(RequestInterface $request, array &$conf): void + { + $proxy = self::getEffectiveProxy($conf); + + if ($proxy === null || !self::requiresFreshConnectionForAuthenticatedProxy($request, $proxy, $conf)) { + return; + } + + $conf[\CURLOPT_FRESH_CONNECT] = true; + $conf[\CURLOPT_FORBID_REUSE] = true; + } + + /** + * @param array $conf + */ + private function isolateOpaqueShareAnonymousProxyTunnel(RequestInterface $request, array &$conf): void + { + if (!$this->opaqueShareConnectionCache || !CurlVersion::supportsShareConnectionCaches()) { + return; + } + + $proxy = self::getEffectiveProxy($conf); + if ( + $proxy === null + || !self::usesProxyTunnel($request, $conf) + || !self::isHttpProxyForConnectionReuse($proxy, $conf) + || self::hasAuthenticatedHttpProxyState($proxy, $conf) + ) { + return; + } + + // From libcurl 7.57.0 an opaque share handle can own a connection + // cache, and a tunnel seeded there with a literal Proxy-Authorization + // header is never keyed on credentials, so an anonymous request could + // inherit it on every later libcurl version. Requests carrying + // recognized credential state keep the version-gated channel + // safeguards above. + $conf[\CURLOPT_FRESH_CONNECT] = true; + $conf[\CURLOPT_FORBID_REUSE] = true; + } + + /** + * @param array $conf + */ + private static function assertFinalProxyOptionTypes(array $conf, bool $requiredCleartextMultiplex): void + { + if (\array_key_exists(\CURLOPT_PROXYTYPE, $conf) && !\is_int($conf[\CURLOPT_PROXYTYPE])) { + throw new \InvalidArgumentException('CURLOPT_PROXYTYPE must be an integer.'); + } + + foreach (['CURLOPT_PROXY', 'CURLOPT_NOPROXY', 'CURLOPT_PRE_PROXY'] as $name) { + if (!\defined($name)) { + continue; + } + + $option = (int) \constant($name); + if (\array_key_exists($option, $conf) && !\is_string($conf[$option])) { + if ($requiredCleartextMultiplex) { + throw new \InvalidArgumentException(\sprintf('The "multiplex" request option cannot be required when the final %s cURL option value is not a string.', $name)); + } + + throw new \InvalidArgumentException($name.' must be a string.'); + } + } + } + + /** + * @param array $conf + */ + private static function isolatePreProxyOnAffectedCurl(array &$conf): void + { + if (CurlVersion::supportsSocksProxyCredentialAwareConnectionReuse() || !\defined('CURLOPT_PRE_PROXY')) { + return; + } + + $option = (int) \constant('CURLOPT_PRE_PROXY'); + if (!\array_key_exists($option, $conf) || $conf[$option] === '') { + return; + } + + $conf[\CURLOPT_FRESH_CONNECT] = true; + $conf[\CURLOPT_FORBID_REUSE] = true; + } + + /** + * @param array $conf + */ + private static function getEffectiveProxy(array $conf): ?string + { + if (!\array_key_exists(\CURLOPT_PROXY, $conf)) { + return null; + } + + $proxy = $conf[\CURLOPT_PROXY]; + if (!\is_string($proxy) || $proxy === '') { + return null; + } + + // Only the exact raw wildcard is modeled here: libcurl treats '*' as + // bypass-all by whole-string comparison, without trimming or host matching. + if (\defined('CURLOPT_NOPROXY')) { + $noProxy = $conf[(int) \constant('CURLOPT_NOPROXY')] ?? null; + if (\is_string($noProxy) && $noProxy === '*') { + return null; + } + } + + return $proxy; + } + + /** + * @param array $conf + */ + private static function normalizeCurlHeaderOptions(array &$conf): void + { + $options = [\CURLOPT_HTTPHEADER => 'CURLOPT_HTTPHEADER']; + if (\defined('CURLOPT_PROXYHEADER')) { + $options[(int) \constant('CURLOPT_PROXYHEADER')] = 'CURLOPT_PROXYHEADER'; + } + + foreach ($options as $option => $label) { + if (!\array_key_exists($option, $conf) || !\is_array($conf[$option])) { + continue; + } + + $normalized = []; + foreach ($conf[$option] as $key => $entry) { + if (\is_object($entry) && \method_exists($entry, '__toString')) { + $entry = (string) $entry; + } elseif (\is_float($entry) && !\is_finite($entry)) { + $entry = \is_nan($entry) ? 'NAN' : ($entry > 0 ? 'INF' : '-INF'); + } elseif (\is_scalar($entry)) { + $entry = (string) $entry; + } else { + throw new \InvalidArgumentException(\sprintf('%s entries must be strings, stringable objects, or scalar values.', $label)); + } + + if (\strpbrk($entry, "\r\n") !== false) { + throw new \InvalidArgumentException(\sprintf('%s entries must not contain a carriage return or line feed.', $label)); + } + + $normalized[$key] = $entry; + } + + $conf[$option] = $normalized; + } + } + + private static function proxyScheme(string $proxy): ?string + { + $position = \strpos($proxy, '://'); + + return $position === false ? null : Psr7\Utils::asciiToLower(\substr($proxy, 0, $position)); + } + + /** + * @param array $conf + */ + private static function requiresFreshConnectionForAuthenticatedProxy(RequestInterface $request, string $proxy, array $conf): bool + { + // SOCKS authentication binds an identity to the connection itself, and + // below 7.69.0 an opaque configured share may already contain a SOCKS + // connection whose credential state Guzzle cannot inspect. Isolate + // authenticated and anonymous requests so neither can inherit it. + if (self::isSocksProxy($proxy, $conf)) { + return !CurlVersion::supportsSocksProxyCredentialAwareConnectionReuse(); + } + + if (!self::usesProxyTunnel($request, $conf) || !self::isHttpProxyForConnectionReuse($proxy, $conf)) { + return false; + } + + $proxyForParsing = \strpos($proxy, '://') === false ? 'http://'.$proxy : $proxy; + $proxyParts = \parse_url($proxyForParsing); + + if (!\is_array($proxyParts)) { + return false; + } + + if (self::hasCurlProxyAuthorizationHeader($conf)) { + return true; + } + + // A proxy client certificate or TLS-SRP authenticates the client to the + // HTTPS proxy at the TLS layer; libcurl ignored TLS-SRP before 7.83.1 + // (CVE-2022-27782), so an old build can reuse a tunnel across those + // identities. Force a fresh one, as the non-share signature path does. + if ( + !CurlVersion::supportsProxyTlsCredentialAwareConnectionReuse() + && self::hasCurlProxyTlsCredentials($conf) + ) { + return true; + } + + if (CurlVersion::supportsProxyCredentialAwareConnectionReuse()) { + return false; + } + + return \array_key_exists('user', $proxyParts) + || \array_key_exists('pass', $proxyParts) + || self::hasCurlProxyCredentials($conf); + } + + /** + * @param array $conf + */ + private static function hasAuthenticatedSocksProxyState(string $proxy, array $conf): bool + { + $proxyForParsing = \strpos($proxy, '://') === false ? 'http://'.$proxy : $proxy; + $proxyParts = \parse_url($proxyForParsing); + + if ( + \is_array($proxyParts) + && (\array_key_exists('user', $proxyParts) || \array_key_exists('pass', $proxyParts)) + ) { + return true; + } + + return self::hasCurlProxyCredentials($conf); + } + + /** + * @param array $conf + */ + private static function usesProxyTunnel(RequestInterface $request, array $conf): bool + { + $scheme = $request->getUri()->getScheme(); + + if ('https' === $scheme) { + return true; + } + + // An HTTP proxy auto-switches to a CONNECT tunnel when CONNECT_TO + // redirects the origin, so an http:// target with it set tunnels too. + if ('http' === $scheme && self::hasCurlConnectTo($conf)) { + return true; + } + + return \defined('CURLOPT_HTTPPROXYTUNNEL') + && \array_key_exists((int) \constant('CURLOPT_HTTPPROXYTUNNEL'), $conf) + && (bool) $conf[(int) \constant('CURLOPT_HTTPPROXYTUNNEL')]; + } + + /** + * @param array $conf + */ + private static function hasCurlConnectTo(array $conf): bool + { + if (!\defined('CURLOPT_CONNECT_TO')) { + return false; + } + + $option = (int) \constant('CURLOPT_CONNECT_TO'); + if (!\array_key_exists($option, $conf)) { + return false; + } + + $value = $conf[$option]; + + return \is_array($value) + ? $value !== [] + : $value !== null && $value !== false && $value !== ''; + } + + /** + * @param array $conf + */ + private static function isHttpProxyForConnectionReuse(string $proxy, array $conf): bool + { + if (\strpos($proxy, '://') !== false) { + $proxyParts = \parse_url($proxy); + + if (!\is_array($proxyParts) || !isset($proxyParts['scheme'])) { + return false; + } + + $proxyScheme = Psr7\Utils::asciiToLower($proxyParts['scheme']); + + return $proxyScheme === 'http' || $proxyScheme === 'https'; + } + + return !self::isSocksProxyType($conf[\CURLOPT_PROXYTYPE] ?? null); + } + + /** + * @param array $conf + */ + private static function isSocksProxy(string $proxy, array $conf): bool + { + $scheme = self::proxyScheme($proxy); + if ($scheme !== null) { + if (\in_array($scheme, ['socks', 'socks4', 'socks4a', 'socks5', 'socks5h'], true)) { + return true; + } + + // libcurl preserves a raw SOCKS CURLOPT_PROXYTYPE behind an http + // scheme, while every other scheme overrides the proxy type. + if ($scheme !== 'http') { + return false; + } + } + + return self::isSocksProxyType($conf[\CURLOPT_PROXYTYPE] ?? null); + } + + /** + * Computes the connection-reuse section signature for a SOCKS proxy. + * libcurl compares SOCKS credentials on connection reuse from 7.69.0 (curl + * #4835), so no sectioning is needed there. Older libcurl matches a SOCKS + * proxy by type, host, and port only, so every SOCKS request is sectioned + * by its credential state; hashing the credential-less state too keeps an + * unauthenticated request from inheriting an authenticated connection. + * + * @param array $conf + */ + private static function socksProxySignature(string $proxy, array $conf): ?string + { + if (CurlVersion::supportsSocksProxyCredentialAwareConnectionReuse()) { + return null; + } + + $credentialState = []; + foreach (['CURLOPT_PROXYUSERPWD', 'CURLOPT_PROXYUSERNAME', 'CURLOPT_PROXYPASSWORD', 'CURLOPT_PROXYTYPE'] as $name) { + $credentialState[$name] = \defined($name) + ? ($conf[(int) \constant($name)] ?? null) + : null; + } + + return \hash('sha256', \serialize(['socks', $proxy, $credentialState])); + } + + /** + * @param mixed $proxyType + */ + private static function isSocksProxyType($proxyType): bool + { + if (!\is_int($proxyType)) { + return false; + } + + foreach ([ + 'CURLPROXY_SOCKS4' => 4, + 'CURLPROXY_SOCKS5' => 5, + 'CURLPROXY_SOCKS4A' => 6, + 'CURLPROXY_SOCKS5_HOSTNAME' => 7, + ] as $name => $fallback) { + $value = \defined($name) ? (int) \constant($name) : $fallback; + if ($proxyType === $value) { + return true; + } + } + + return false; + } + + /** + * @param array $conf + */ + private static function hasCurlProxyCredentials(array $conf): bool + { + foreach (['CURLOPT_PROXYUSERPWD', 'CURLOPT_PROXYUSERNAME', 'CURLOPT_PROXYPASSWORD'] as $option) { + if (\defined($option) && \array_key_exists((int) \constant($option), $conf)) { + return true; + } + } + + return false; + } + + /** + * @param array $conf + */ + private static function hasCurlProxyTlsCredentials(array $conf): bool + { + foreach ([ + 'CURLOPT_PROXY_SSLCERT', + 'CURLOPT_PROXY_SSLCERT_BLOB', + 'CURLOPT_PROXY_TLSAUTH_USERNAME', + 'CURLOPT_PROXY_TLSAUTH_PASSWORD', + ] as $option) { + if (\defined($option) && \array_key_exists((int) \constant($option), $conf)) { + return true; + } + } + + return false; + } + + /** + * @param array $conf + */ + private static function hasCurlProxyAuthorizationHeader(array $conf): bool + { + return self::curlProxyAuthorizationHeaderValues($conf) !== []; + } + + /** + * @param array $conf + */ + private static function applyProxyAuthorizationHeaderHandling(RequestInterface $request, array &$conf): void + { + $proxy = self::getEffectiveProxy($conf); + if ($proxy === null || !self::isHttpProxyForConnectionReuse($proxy, $conf)) { + return; + } + + $httpHeaders = $conf[\CURLOPT_HTTPHEADER] ?? null; + $movedHeaders = []; + $originHeaders = []; + + if (\is_array($httpHeaders)) { + foreach ($httpHeaders as $header) { + if (\is_string($header) && self::isProxyAuthorizationHeaderLine($header)) { + $movedHeaders[] = $header; + + continue; + } + + $originHeaders[] = $header; + } + } + + if (CurlVersion::supportsProxyHeaderSeparation()) { + if ($movedHeaders !== []) { + $conf[\CURLOPT_HTTPHEADER] = $originHeaders; + self::appendCurlProxyHeaders($conf, $movedHeaders); + } + + // On libcurl 7.37.0-7.42.0 the default is CURLHEADER_UNIFIED. + if ($movedHeaders !== [] || self::hasCurlProxyHeaderOption($conf) || self::usesProxyTunnel($request, $conf)) { + $conf[(int) \constant('CURLOPT_HEADEROPT')] = (int) \constant('CURLHEADER_SEPARATE'); + } + + return; + } + + if (\is_array($httpHeaders) && self::proxyAuthorizationHeaderValuesFromList($httpHeaders) !== []) { + $conf[\CURLOPT_FRESH_CONNECT] = true; + $conf[\CURLOPT_FORBID_REUSE] = true; + } + } + + /** + * Routes the managed first-class Proxy-Authorization values to libcurl's + * proxy-only header channel, independently of Guzzle's proxy prediction: + * libcurl alone decides whether the proxy-only list is used for the + * actual transfer, so the credential can never reach an origin through + * CURLOPT_HTTPHEADER. Without proxy header separation support, values are + * safely omitted on known direct, bypassed, and SOCKS routes; a route that + * may use an HTTP(S) proxy is rejected before cURL initialization and + * network I/O. A deprecated raw CURLOPT_HTTPHEADER replacement suppresses + * every generated header, the managed values included. + * + * @param array $conf + * @param list $headers + */ + private static function applyManagedProxyAuthorization(RequestInterface $request, array &$conf, array $headers, bool $rawHttpHeadersReplaceManaged): void + { + if ($rawHttpHeadersReplaceManaged || $headers === []) { + return; + } + + if (!CurlVersion::supportsProxyHeaderSeparation()) { + $proxy = self::getEffectiveProxy($conf); + if ($proxy !== null && !self::isSocksProxy($proxy, $conf)) { + throw new RequestException('Proxy-Authorization request headers through a possible HTTP or HTTPS proxy require libcurl 7.37.0 or newer built with proxy header separation support.', $request); + } + + return; + } + + self::appendCurlProxyHeaders($conf, $headers); + $conf[(int) \constant('CURLOPT_HEADEROPT')] = (int) \constant('CURLHEADER_SEPARATE'); + } + + /** + * @return list + */ + private static function managedProxyAuthorizationHeaderLines(RequestInterface $request): array + { + $headers = []; + + foreach ($request->getHeader('Proxy-Authorization') as $value) { + $headers[] = $value === '' + ? 'Proxy-Authorization;' + : 'Proxy-Authorization: '.$value; + } + + return $headers; + } + + /** + * @param array $conf + * @param list $headers + */ + private static function appendCurlProxyHeaders(array &$conf, array $headers): void + { + $option = (int) \constant('CURLOPT_PROXYHEADER'); + + if (\array_key_exists($option, $conf)) { + if (!\is_array($conf[$option])) { + throw new \InvalidArgumentException('CURLOPT_PROXYHEADER must be an array when a Proxy-Authorization request header is routed to the proxy header channel.'); + } + + $headers = \array_merge($conf[$option], $headers); + } + + $conf[$option] = $headers; + } + + /** + * @param array $conf + */ + private static function hasCurlProxyHeaderOption(array $conf): bool + { + return \defined('CURLOPT_PROXYHEADER') + && \array_key_exists((int) \constant('CURLOPT_PROXYHEADER'), $conf); + } + + private static function isProxyAuthorizationHeaderLine(string $header): bool + { + $length = \strcspn($header, ':;'); + + if ($length === \strlen($header)) { + return false; + } + + return Psr7\Utils::caselessEquals(\trim(\substr($header, 0, $length), " \n\r\t\0\x0B"), 'Proxy-Authorization'); + } + + private static function proxyAuthorizationHeaderValue(string $header): ?string + { + $position = \strpos($header, ':'); + if ($position === false) { + return null; + } + + if (!Psr7\Utils::caselessEquals(\trim(\substr($header, 0, $position), " \n\r\t\0\x0B"), 'Proxy-Authorization')) { + return null; + } + + $value = \trim(\substr($header, $position + 1), " \n\r\t\0\x0B"); + + return $value === '' ? null : $value; + } + + /** + * @param mixed[] $headers + * + * @return list + */ + private static function proxyAuthorizationHeaderValuesFromList(array $headers): array + { + $values = []; + + foreach ($headers as $header) { + if (!\is_string($header)) { + continue; + } + + $value = self::proxyAuthorizationHeaderValue($header); + if ($value !== null) { + $values[] = $value; + } + } + + return $values; + } + + /** + * Computes the connection-reuse section signature for a proxy tunnel or + * SOCKS proxy, or null when the request does not require sectioning. + * + * @param array $conf + */ + private static function proxyTunnelSignature(RequestInterface $request, array $conf): ?string + { + $proxy = self::getEffectiveProxy($conf); + if ($proxy === null) { + return null; + } + + // SOCKS authentication binds an identity to the connection itself, for + // plain http:// requests as much as https://, so it sections ahead of + // the CONNECT tunnel domain checks. + if (self::isSocksProxy($proxy, $conf)) { + return self::socksProxySignature($proxy, $conf); + } + + if ( + !self::usesProxyTunnel($request, $conf) + || !self::isHttpProxyForConnectionReuse($proxy, $conf) + ) { + return null; + } + + $headerAuth = self::curlProxyAuthorizationHeaderValues($conf); + if ($headerAuth === [] && CurlVersion::supportsProxyCredentialAwareConnectionReuse()) { + // libcurl keys reuse on parsed proxy credentials only from 8.19.0, + // trusted from 8.20.0 (PROXY_CREDENTIAL_REUSE_VERSION); a literal + // Proxy-Authorization header is never keyed and always sections. + return self::DELEGATED_PROXY_TUNNEL_OWNER; + } + + // Hash every proxy channel an old libcurl might not key reuse on. A + // changed signature only forces a fresh connection, never relaxes + // reuse, so over-covering is always safe; under-covering leaks. Proxy + // credentials are the channel CVE-2026-3784 missed; the proxy-TLS + // options are load-bearing on builds before the proxy-TLS reuse fixes + // (the proxy client cert is keyed from 7.52.0, libcurl's first + // HTTPS-proxy release; CVE-2016-5420 (7.50.1) is only the origin-cert + // precedent; TLS-SRP from 7.83.1, CVE-2022-27782) and harmless after. + // The private-key file and passphrase are hashed on this non-delegated + // path too, as fallback hardening: libcurl's mTLS private-key matching + // on reuse was incomplete before 8.21.0 (CVE-2026-8932). This does not + // cover the delegated path (the early return above) or configured share + // handles, so it is not a complete pre-8.21.0 mitigation. The key blob + // and cert/key type encodings (PROXY_SSLKEY_BLOB, PROXY_SSLKEYTYPE, + // PROXY_SSLCERTTYPE) are not hashed and are an accepted residual. + $credentialState = []; + foreach ([ + 'CURLOPT_PROXYUSERPWD', 'CURLOPT_PROXYUSERNAME', 'CURLOPT_PROXYPASSWORD', + 'CURLOPT_PROXYTYPE', + 'CURLOPT_PROXY_SSLCERT', 'CURLOPT_PROXY_SSLCERT_BLOB', 'CURLOPT_PROXY_SSLKEY', + 'CURLOPT_PROXY_KEYPASSWD', 'CURLOPT_PROXY_TLSAUTH_USERNAME', + 'CURLOPT_PROXY_TLSAUTH_PASSWORD', 'CURLOPT_PROXY_SSLVERSION', + ] as $name) { + $credentialState[$name] = \defined($name) + ? ($conf[(int) \constant($name)] ?? null) + : null; + } + + return \hash('sha256', \serialize([$proxy, $credentialState, $headerAuth])); + } + + /** + * @param array $conf + * + * @return list + */ + private static function curlProxyAuthorizationHeaderValues(array $conf): array + { + if (!\defined('CURLOPT_PROXYHEADER')) { + return []; + } + + $option = (int) \constant('CURLOPT_PROXYHEADER'); + if (!\array_key_exists($option, $conf)) { + return []; + } + + $headers = $conf[$option]; + if (!\is_array($headers)) { + return []; + } + + return self::proxyAuthorizationHeaderValuesFromList($headers); + } + + private function discardIdleHandles(): void + { + foreach ($this->handles as $id => $handle) { + if (PHP_VERSION_ID < 80000) { + \curl_close($handle); + } + + unset($this->handles[$id]); + } + } + /** * @return array */ private function getDefaultConf(EasyHandle $easy): array { + $uri = $easy->request->getUri(); + $protocols = Utils::normalizeProtocols($easy->options['protocols'] ?? ['http', 'https']); + $scheme = $uri->getScheme(); + if (!\in_array($scheme, $protocols, true)) { + throw new RequestException(\sprintf('The scheme "%s" is not allowed by the protocols request option.', $scheme), $easy->request); + } + + if ($uri->getHost() === '') { + throw new RequestException('URI must include a scheme and host. Use an absolute URI, a network-path reference starting with //, or configure a base_uri.', $easy->request); + } + $conf = [ '_headers' => $easy->request->getHeaders(), \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), - \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), + \CURLOPT_URL => (string) $uri->withFragment(''), \CURLOPT_RETURNTRANSFER => false, \CURLOPT_HEADER => false, \CURLOPT_CONNECTTIMEOUT => 300, ]; - $protocols = Utils::normalizeProtocols($easy->options['protocols'] ?? ['http', 'https']); - $scheme = $easy->request->getUri()->getScheme(); - if (!\in_array($scheme, $protocols, true)) { - throw new RequestException(\sprintf('The scheme "%s" is not allowed by the protocols request option.', $scheme), $easy->request); - } - if (\defined('CURLOPT_PROTOCOLS')) { $conf[\CURLOPT_PROTOCOLS] = self::curlProtocolMask($protocols); } $version = $easy->request->getProtocolVersion(); + $multiplex = self::normalizeMultiplex($easy->options); if ('2' === $version || '2.0' === $version) { - $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0; + if (\in_array($multiplex, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) { + self::assertRequiredMultiplexSupported($easy); + // New HTTP/2 connections cannot negotiate HTTP/1.x here, and + // the 8.14.0 floor's version-aware reuse matching keeps + // reused connections on HTTP/2 as well. + $conf[\CURLOPT_HTTP_VERSION] = (int) \constant('CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE'); + } else { + $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0; + } + + if (\in_array($multiplex, [Multiplexing::WAIT, Multiplexing::REQUIRE_WAIT], true) && CurlVersion::supportsMultiplex()) { + // Wait for a connection that is still being established to the + // same origin to reveal whether it can be multiplexed instead + // of immediately opening another connection. + $conf[(int) \constant('CURLOPT_PIPEWAIT')] = true; + } } elseif ('1.1' === $version) { + if (\in_array($multiplex, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) { + throw new ConnectException(\sprintf('The "multiplex" request option cannot be required for HTTP/%s requests; use protocol version 2.', $version), $easy->request); + } $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1; } else { + if (\in_array($multiplex, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) { + throw new ConnectException(\sprintf('The "multiplex" request option cannot be required for HTTP/%s requests; use protocol version 2.', $version), $easy->request); + } $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0; } @@ -657,7 +1982,7 @@ class CurlFactory implements CurlFactoryInterface throw new \InvalidArgumentException(\sprintf('%s must be a non-empty string', $option)); } - return \strtoupper($type); + return Psr7\Utils::asciiToUpper($type); } private static function shouldValidateSslKeyFile(?string $type): bool @@ -667,6 +1992,30 @@ class CurlFactory implements CurlFactoryInterface private function applyMethod(EasyHandle $easy, array &$conf): void { + if ($easy->request->getMethod() === 'HEAD') { + // libcurl stops at HEAD response headers only when CURLOPT_NOBODY + // is set; CURLOPT_CUSTOMREQUEST changes only the method string. + // NOBODY also suppresses request upload, so strip non-zero body + // length, transfer coding, and a 100-continue expectation. + $conf[\CURLOPT_CUSTOMREQUEST] = null; + $conf[\CURLOPT_NOBODY] = true; + unset( + $conf[\CURLOPT_WRITEFUNCTION], + $conf[\CURLOPT_READFUNCTION], + $conf[\CURLOPT_FILE], + $conf[\CURLOPT_INFILE] + ); + if (\trim($easy->request->getHeaderLine('Content-Length'), " \n\r\t\0\x0B") !== '0') { + $this->removeHeader('Content-Length', $conf); + } + $this->removeHeader('Transfer-Encoding', $conf); + if (Psr7\Utils::caselessEquals(\trim($easy->request->getHeaderLine('Expect'), " \n\r\t\0\x0B"), '100-continue')) { + $this->removeHeader('Expect', $conf); + } + + return; + } + $body = $easy->request->getBody(); $size = $body->getSize(); @@ -682,14 +2031,6 @@ class CurlFactory implements CurlFactoryInterface if (!$easy->request->hasHeader('Content-Length')) { $conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; } - } elseif ($method === 'HEAD') { - $conf[\CURLOPT_NOBODY] = true; - unset( - $conf[\CURLOPT_WRITEFUNCTION], - $conf[\CURLOPT_READFUNCTION], - $conf[\CURLOPT_FILE], - $conf[\CURLOPT_INFILE] - ); } } @@ -716,8 +2057,19 @@ class CurlFactory implements CurlFactoryInterface if ($body->isSeekable()) { $body->rewind(); } - $conf[\CURLOPT_READFUNCTION] = static function ($ch, $fd, $length) use ($body) { - return $body->read($length); + $remaining = $size; + $conf[\CURLOPT_READFUNCTION] = static function ($ch, $fd, $length) use ($body, &$remaining) { + if ($remaining === 0) { + return ''; + } + + $limit = $remaining === null ? $length : \min($length, $remaining); + $data = $body->read($limit); + if ($remaining !== null) { + $remaining -= \strlen($data); + } + + return $data; }; } @@ -735,6 +2087,17 @@ class CurlFactory implements CurlFactoryInterface private function applyHeaders(EasyHandle $easy, array &$conf): void { foreach ($conf['_headers'] as $name => $values) { + // A first-class Proxy-Authorization header is proxy-scoped and + // must never be generated in the origin header list; managed + // handling routes it to CURLOPT_PROXYHEADER or safely omits it on + // a legacy non-HTTP-proxy route. The + // caselessEquals() helper is locale-independent, unlike + // strcasecmp(), so a locale cannot make this match miss and + // re-leak the credential. + if (Psr7\Utils::caselessEquals((string) $name, 'Proxy-Authorization')) { + continue; + } + foreach ($values as $value) { $value = (string) $value; if ($value === '') { @@ -762,7 +2125,7 @@ class CurlFactory implements CurlFactoryInterface private function removeHeader(string $name, array &$options): void { foreach (\array_keys($options['_headers']) as $key) { - if (!\strcasecmp((string) $key, $name)) { + if (Psr7\Utils::caselessEquals((string) $key, $name)) { unset($options['_headers'][$key]); return; @@ -804,9 +2167,9 @@ class CurlFactory implements CurlFactoryInterface } } - if (!isset($options['curl'][\CURLOPT_ENCODING]) && !empty($options['decode_content'])) { + if (!isset($options['curl'][\CURLOPT_ENCODING]) && isset($options['decode_content']) && $options['decode_content'] !== false) { $accept = $easy->request->getHeaderLine('Accept-Encoding'); - if ($accept) { + if ($accept !== '') { $conf[\CURLOPT_ENCODING] = $accept; } else { // The empty string enables all available decoders and implicitly @@ -822,11 +2185,11 @@ class CurlFactory implements CurlFactoryInterface if (!isset($options['sink'])) { // Use a default temp stream if no sink was set. - $options['sink'] = \GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'w+'); + $options['sink'] = Psr7\Utils::tryFopen('php://temp', 'w+'); } $sink = $options['sink']; if (!\is_string($sink)) { - $sink = \GuzzleHttp\Psr7\Utils::streamFor($sink); + $sink = Psr7\Utils::streamFor($sink); } elseif (!\is_dir(\dirname($sink))) { // Ensure that the directory exists before failing in curl. throw new \RuntimeException(\sprintf('Directory %s does not exist for sink value of %s', \dirname($sink), $sink)); @@ -858,65 +2221,23 @@ class CurlFactory implements CurlFactoryInterface $conf[\CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000; } - if ($timeoutRequiresNoSignal && \strtoupper(\substr(\PHP_OS, 0, 3)) !== 'WIN') { + if ($timeoutRequiresNoSignal && Psr7\Utils::asciiToUpper(\substr(\PHP_OS, 0, 3)) !== 'WIN') { $conf[\CURLOPT_NOSIGNAL] = true; } - if (isset($options['proxy'])) { - if (!\is_array($options['proxy'])) { - $conf[\CURLOPT_PROXY] = $options['proxy']; - } else { - $scheme = $easy->request->getUri()->getScheme(); - if (isset($options['proxy'][$scheme])) { - if ( - isset($options['proxy']['no']) - && Utils::isUriInNoProxy($easy->request->getUri(), $options['proxy']['no']) - ) { - unset($conf[\CURLOPT_PROXY]); - } else { - $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme]; - } - } - } + // Always pin CURLOPT_PROXY (and CURLOPT_NOPROXY when available) so + // that libcurl never falls back to reading proxy environment + // variables itself. When the proxy request option makes no decision, + // the environment is resolved here with libcurl's own semantics. + [$proxyConf, $noProxyConf] = self::resolveProxy($easy->request, $options); + self::assertResolvedProxySupported($easy->request, $proxyConf); + + $conf[\CURLOPT_PROXY] = $proxyConf; + if (\defined('CURLOPT_NOPROXY')) { + $conf[(int) \constant('CURLOPT_NOPROXY')] = $noProxyConf; } - if (isset($options['crypto_method'])) { - $protocolVersion = $easy->request->getProtocolVersion(); - - // If HTTP/2, upgrade TLS 1.0 and 1.1 to 1.2 - if ('2' === $protocolVersion || '2.0' === $protocolVersion) { - if ( - \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method'] - || \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method'] - || \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method'] - ) { - $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2; - } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) { - if (!self::supportsTls13()) { - throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL'); - } - $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3; - } else { - throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); - } - } elseif (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) { - $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_0; - } elseif (\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']) { - $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_1; - } elseif (\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']) { - if (!self::supportsTls12()) { - throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL'); - } - $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2; - } elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) { - if (!self::supportsTls13()) { - throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL'); - } - $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3; - } else { - throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); - } - } + $this->applyTlsVersionRange($easy, $conf); $certType = null; if (isset($options['cert_type'])) { @@ -947,8 +2268,8 @@ class CurlFactory implements CurlFactoryInterface // OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files. // see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html $ext = pathinfo($cert, \PATHINFO_EXTENSION); - if ($certType === null && preg_match('#^(der|p12)$#i', $ext)) { - $conf[\CURLOPT_SSLCERTTYPE] = strtoupper($ext); + if ($certType === null && preg_match('#^(der|p12)$#iD', $ext)) { + $conf[\CURLOPT_SSLCERTTYPE] = Psr7\Utils::asciiToUpper($ext); } $conf[\CURLOPT_SSLCERT] = $cert; } @@ -1002,6 +2323,122 @@ class CurlFactory implements CurlFactoryInterface } } + private function applyTlsVersionRange(EasyHandle $easy, array &$conf): void + { + $options = $easy->options; + $cryptoMethod = $options['crypto_method'] ?? null; + $cryptoMethodMax = $options['crypto_method_max'] ?? null; + + if ($cryptoMethod === null && $cryptoMethodMax === null) { + return; + } + + $protocolVersion = $easy->request->getProtocolVersion(); + $isHttp2 = '2' === $protocolVersion || '2.0' === $protocolVersion; + + if ($isHttp2 && $cryptoMethodMax !== null && TlsVersion::ordinal('crypto_method_max', $cryptoMethodMax) < 12) { + throw new \InvalidArgumentException( + 'Invalid crypto_method_max request option: HTTP/2 requires TLS 1.2 or higher' + ); + } + + if ($isHttp2 && $cryptoMethod !== null && TlsVersion::ordinal('crypto_method', $cryptoMethod) < 12) { + $cryptoMethod = \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; + } + + TlsVersion::assertRange($cryptoMethod, $cryptoMethodMax); + + $sslVersion = $cryptoMethod === null + ? \CURL_SSLVERSION_DEFAULT + : self::curlMinSslVersion($cryptoMethod); + + if ($cryptoMethodMax !== null) { + $sslVersion |= self::curlMaxSslVersion($cryptoMethodMax); + } + + $conf[\CURLOPT_SSLVERSION] = $sslVersion; + } + + /** + * @param mixed $value + */ + private static function curlMinSslVersion($value): int + { + if ($value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT) { + return \CURL_SSLVERSION_TLSv1_0; + } + + if ($value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT) { + return \CURL_SSLVERSION_TLSv1_1; + } + + if ($value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT) { + if (!CurlVersion::supportsTls12()) { + throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL'); + } + + return \CURL_SSLVERSION_TLSv1_2; + } + + if (\defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT) { + if (!CurlVersion::supportsTls13()) { + throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL'); + } + + return \CURL_SSLVERSION_TLSv1_3; + } + + throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); + } + + /** + * @param mixed $value + */ + private static function curlMaxSslVersion($value): int + { + if ($value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT) { + return self::requireCurlMaxSslVersion('CURL_SSLVERSION_MAX_TLSv1_0'); + } + + if ($value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT) { + return self::requireCurlMaxSslVersion('CURL_SSLVERSION_MAX_TLSv1_1'); + } + + if ($value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT) { + return self::requireCurlMaxSslVersion('CURL_SSLVERSION_MAX_TLSv1_2'); + } + + if (\defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT) { + return self::requireCurlMaxSslVersion('CURL_SSLVERSION_MAX_TLSv1_3'); + } + + throw new \InvalidArgumentException('Invalid crypto_method_max request option: unknown version provided'); + } + + private static function requireCurlMaxSslVersion(string $constant): int + { + if (\defined($constant)) { + /** @var int */ + return \constant($constant); + } + + throw new \InvalidArgumentException( + 'Invalid crypto_method_max request option: maximum TLS version control is not supported by your version of cURL' + ); + } + + private static function validateRequestUriScheme(RequestInterface $request): void + { + $scheme = $request->getUri()->getScheme(); + if ($scheme === '') { + throw new RequestException('URI must include a scheme and host. Use an absolute URI, a network-path reference starting with //, or configure a base_uri.', $request); + } + + if (!\in_array($scheme, ['http', 'https'], true)) { + throw new RequestException(\sprintf("The scheme '%s' is not supported.", $scheme), $request); + } + } + /** * This function ensures that a response was set on a transaction. If one * was not set, then the request is retried if possible. This error @@ -1049,6 +2486,24 @@ class CurlFactory implements CurlFactoryInterface return $handler($easy->request, $easy->options); } + /** + * Parses validated trailer field lines into an associative array keyed by + * lowercased field name, preserving first-occurrence key order and wire + * value order. + */ + private static function headersFromTrailerLines(array $lines): array + { + $headers = []; + + foreach ($lines as $line) { + [$name, $value] = \explode(':', $line, 2); + $name = Psr7\Utils::asciiToLower(\trim($name, " \n\r\t\0\x0B")); + $headers[$name][] = \trim($value, " \n\r\t\0\x0B"); + } + + return $headers; + } + private function createHeaderFn(EasyHandle $easy): callable { if (isset($easy->options['on_headers'])) { @@ -1061,13 +2516,24 @@ class CurlFactory implements CurlFactoryInterface $onHeaders = null; } + $startingResponse = false; + $collectingTrailers = false; + $retainTrailers = isset($easy->options['on_trailers']); + return static function ($ch, $h) use ( $onHeaders, $easy, - &$startingResponse + &$startingResponse, + &$collectingTrailers, + $retainTrailers ) { - $value = \trim($h); - if ($value === '') { + $value = \trim($h, " \n\r\t\0\x0B"); + if ($h === "\r\n" || $h === "\n" || $h === "\r" || $h === '') { + if ($collectingTrailers) { + // A blank line ends the trailer section; the response has + // already been created. + return \strlen($h); + } $startingResponse = true; try { $easy->createResponse(); @@ -1088,9 +2554,22 @@ class CurlFactory implements CurlFactoryInterface return -1; } } - } elseif ($startingResponse) { + } elseif ($startingResponse || $collectingTrailers) { + if ($easy->response !== null && !HeaderProcessor::isStatusLineCandidate($h)) { + // Trailer fields arrive through the header callback after + // the body; a new header block always begins with a status + // line. + $collectingTrailers = true; + + if ($retainTrailers && HeaderProcessor::isValidHeaderFieldLine($h)) { + $easy->trailers[] = $value; + } + } else { + $collectingTrailers = false; + $easy->trailers = []; + $easy->headers = [$value]; + } $startingResponse = false; - $easy->headers = [$value]; } else { $easy->headers[] = $value; } @@ -1101,12 +2580,6 @@ class CurlFactory implements CurlFactoryInterface public function __destruct() { - foreach ($this->handles as $id => $handle) { - if (PHP_VERSION_ID < 80000) { - \curl_close($handle); - } - - unset($this->handles[$id]); - } + $this->discardIdleHandles(); } } diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php index fa1ecbe..90be9dc 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlHandler.php @@ -17,6 +17,11 @@ use Psr\Http\Message\RequestInterface; */ class CurlHandler { + private const KNOWN_CONSTRUCTOR_OPTIONS = [ + 'handle_factory' => true, + 'transport_sharing' => true, + ]; + /** * @var CurlFactoryInterface */ @@ -37,6 +42,12 @@ class CurlHandler */ public function __construct(array $options = []) { + foreach ($options as $name => $_) { + if (!isset(self::KNOWN_CONSTRUCTOR_OPTIONS[$name])) { + \trigger_deprecation('guzzlehttp/guzzle', '7.14', \sprintf('The "%s" CurlHandler constructor option is unknown; guzzlehttp/guzzle 8.0 will reject unknown constructor options.', (string) $name)); + } + } + CurlShareHandleState::assertNoRequiredSharingCustomFactoryConflict($options, 'CurlHandler'); $transportSharing = $options['transport_sharing'] ?? null; $sharingMode = CurlShareHandleState::normalizeMode($transportSharing, 'transport_sharing'); @@ -53,7 +64,7 @@ class CurlHandler : null; $this->factory = $this->shareHandleState !== null - ? new CurlFactory(3, $this->shareHandleState->mode, $this->shareHandleState->handle) + ? new CurlFactory(3, $this->shareHandleState->mode, $this->shareHandleState) : new CurlFactory(3); } @@ -63,6 +74,11 @@ class CurlHandler \usleep($options['delay'] * 1000); } + // A Multiplexing::NONE request option holds unconditionally here: + // transport sharing never shares the connection cache on this + // branch, and nothing else executes during the blocking curl_exec(), + // so the transfer cannot share its connection with a concurrent + // transfer. $easy = $this->factory->create($request, $options); \curl_exec($easy->handle); $easy->errno = \curl_errno($easy->handle); diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php index b1f3d74..94cf9b5 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlMultiHandler.php @@ -3,9 +3,12 @@ namespace GuzzleHttp\Handler; use Closure; +use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Multiplexing; use GuzzleHttp\Promise as P; use GuzzleHttp\Promise\Promise; use GuzzleHttp\Promise\PromiseInterface; +use GuzzleHttp\Psr7; use GuzzleHttp\TransportSharing; use GuzzleHttp\Utils; use Psr\Http\Message\RequestInterface; @@ -21,6 +24,31 @@ use Psr\Http\Message\RequestInterface; */ class CurlMultiHandler { + private const KNOWN_CONSTRUCTOR_OPTIONS = [ + 'handle_factory' => true, + 'max_host_connections' => true, + 'max_total_connections' => true, + 'multiplex' => true, + 'options' => true, + 'select_timeout' => true, + 'transport_sharing' => true, + ]; + + private const CONNECTION_CAP_OPTIONS = [ + 'max_host_connections' => 'CURLMOPT_MAX_HOST_CONNECTIONS', + 'max_total_connections' => 'CURLMOPT_MAX_TOTAL_CONNECTIONS', + ]; + + /** + * cURL options that isolate a transfer from foreign proxy tunnel + * connections. Failing to apply either one would fall open into + * credential-bearing connection reuse. + */ + private const PROXY_TUNNEL_ISOLATION_OPTIONS = [ + 'CURLOPT_FRESH_CONNECT', + 'CURLOPT_FORBID_REUSE', + ]; + /** * @var CurlFactoryInterface */ @@ -60,19 +88,81 @@ class CurlMultiHandler */ private $options = []; + /** + * @var array Native options derived from first-class + * constructor options; failing to apply one is an + * error rather than a compatibility warning. + */ + private $requiredOptions = []; + + /** + * @var bool Whether any connection cap constructor option was applied + */ + private $connectionCapsApplied = false; + + /** + * @var bool Whether the "multiplex" constructor option disabled + * multiplexing on this handler's multi handle + */ + private $multiplexDisabled = false; + + /** + * @var bool Whether a custom "handle_factory" constructor option supplies + * the easy handles + */ + private $customHandleFactory = false; + /** @var resource|\CurlMultiHandle */ private $_mh; /** - * @var bool + * @var int Depth of nested guarded native operations (execution and + * handle removal, both of which can run user callbacks). A + * callback can re-enter tick(), and the nested frame must not + * clear the outer frame's guard; deferred work stays parked + * until the outermost frame unwinds. */ - private $executingMulti = false; + private $multiExecDepth = 0; /** - * @var array + * @var bool Guards finishDeferredWork() against re-entry from the + * guarded native removals it performs while flushing. + */ + private $finishingDeferredWork = false; + + /** + * @var array */ private $deferredCancels = []; + /** + * @var array Wait tokens of requests created from inside + * a cURL callback, keyed by handle id; native + * attachment is deferred until the outermost + * native execution unwinds. + */ + private $deferredAdds = []; + + /** + * @var string|null Owner signature of the proxy tunnels the multi handle's + * connection cache may hold + */ + private $proxyTunnelOwner; + + /** @var array Count of attached transfers per proxy tunnel signature. */ + private $activeProxyTunnelSignatures = []; + + /** @var array Maps an attached handle id to its proxy tunnel signature. */ + private $activeProxyTunnelHandles = []; + + /** + * @var int Depth of nested processMessages() calls. Guards against + * multi-handle recreation re-entrancy from processMessages (a + * retried transfer re-invokes the handler); a depth is tracked + * because a completion callback can re-enter tick(). + */ + private $messageProcessingDepth = 0; + /** * This handler accepts the following options: * @@ -80,11 +170,40 @@ class CurlMultiHandler * - transport_sharing: Optional transport sharing mode. * - select_timeout: Optional timeout (in seconds) to block before timing * out while selecting curl handles. Defaults to 1 second. + * - max_host_connections: Optional maximum concurrent connections per host. + * - max_total_connections: Optional maximum concurrent connections overall. + * - multiplex: Optional Multiplexing::NONE to disallow multiplexing on + * this handler's multi handle. The eager, wait, and required modes are + * request options, not handler options; Multiplexing::NONE is also + * conditionally accepted as a request option value. * - options: An associative array of CURLMOPT_* options and * corresponding values for curl_multi_setopt() */ public function __construct(array $options = []) { + foreach ($options as $name => $_) { + if (!isset(self::KNOWN_CONSTRUCTOR_OPTIONS[$name])) { + \trigger_deprecation('guzzlehttp/guzzle', '7.14', \sprintf('The "%s" CurlMultiHandler constructor option is unknown; guzzlehttp/guzzle 8.0 will reject unknown constructor options.', (string) $name)); + } + } + + $handlerMultiplex = $options['multiplex'] ?? null; + if (null !== $handlerMultiplex && Multiplexing::NONE !== $handlerMultiplex) { + if (\in_array($handlerMultiplex, [Multiplexing::EAGER, Multiplexing::WAIT, Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) { + throw new \InvalidArgumentException('The "multiplex" CurlMultiHandler option only accepts Multiplexing::NONE; the eager, wait, and required modes are request options.'); + } + + throw new \InvalidArgumentException(\sprintf('The "multiplex" CurlMultiHandler option must be null or Multiplexing::NONE; received %s.', \get_debug_type($handlerMultiplex))); + } + $this->multiplexDisabled = null !== $handlerMultiplex; + + if ($this->multiplexDisabled && !\defined('CURLMOPT_PIPELINING')) { + // ext-curl only defines the constant when built against libcurl + // 7.16 or newer headers, and such builds compile out the matching + // curl_multi_setopt() case, so the guarantee cannot be applied. + throw new \InvalidArgumentException('The "multiplex" CurlMultiHandler option requires CURLMOPT_PIPELINING, but it is not available in the installed PHP cURL extension.'); + } + CurlShareHandleState::assertNoRequiredSharingCustomFactoryConflict($options, 'CurlMultiHandler'); $transportSharing = $options['transport_sharing'] ?? null; $sharingMode = CurlShareHandleState::normalizeMode($transportSharing, 'transport_sharing'); @@ -92,18 +211,29 @@ class CurlMultiHandler if (\array_key_exists('handle_factory', $options) && $options['handle_factory'] !== null) { $this->shareHandleState = null; $this->factory = $options['handle_factory']; + $this->customHandleFactory = true; } else { $this->shareHandleState = $sharingMode !== TransportSharing::NONE ? CurlShareHandleState::fromOption($transportSharing) : null; $this->factory = $this->shareHandleState !== null - ? new CurlFactory(50, $this->shareHandleState->mode, $this->shareHandleState->handle) + ? new CurlFactory(50, $this->shareHandleState->mode, $this->shareHandleState) : new CurlFactory(50); } if (isset($options['select_timeout'])) { - $this->selectTimeout = $options['select_timeout']; + $selectTimeout = $options['select_timeout']; + if (!\is_int($selectTimeout) && !\is_float($selectTimeout) && (!\is_string($selectTimeout) || !\is_numeric($selectTimeout))) { + \trigger_deprecation('guzzlehttp/guzzle', '7.14', 'Passing a non-numeric "select_timeout" CurlMultiHandler option is deprecated; guzzlehttp/guzzle 8.0 will reject it.'); + } else { + $seconds = (float) $selectTimeout; + if (!\is_finite($seconds) || $seconds < 0 || ($seconds > 0 && (int) ($seconds * 1000) === 0)) { + \trigger_deprecation('guzzlehttp/guzzle', '7.14', 'Passing a "select_timeout" CurlMultiHandler option that is not 0 or greater than or equal to 0.001 seconds is deprecated; guzzlehttp/guzzle 8.0 will reject it.'); + } + } + + $this->selectTimeout = $selectTimeout; } elseif ($selectTimeout = Utils::getenv('GUZZLE_CURL_SELECT_TIMEOUT')) { \trigger_deprecation('guzzlehttp/guzzle', '7.2', 'The GUZZLE_CURL_SELECT_TIMEOUT environment variable is deprecated; use the "select_timeout" option instead.'); $this->selectTimeout = (int) $selectTimeout; @@ -111,7 +241,37 @@ class CurlMultiHandler $this->selectTimeout = 1; } - $this->options = $options['options'] ?? []; + $multiOptions = $options['options'] ?? []; + if (\is_array($multiOptions)) { + self::rejectConnectionCapOptionConflicts($options, $multiOptions); + + if ($this->multiplexDisabled && \array_key_exists(\CURLMOPT_PIPELINING, $multiOptions)) { + // Key presence alone conflicts, even with an agreeing value: + // the named option is the single multiplexing authority. + throw new \InvalidArgumentException('multiplex conflicts with a CURLMOPT_PIPELINING entry in the "options" array.'); + } + + self::triggerConflictingCurlMultiOptionDeprecations($multiOptions); + } elseif (self::hasConnectionCapOption($options)) { + throw new \InvalidArgumentException('options must be an array of cURL multi options when using connection cap options.'); + } elseif ($this->multiplexDisabled) { + throw new \InvalidArgumentException('options must be an array of cURL multi options when using the "multiplex" option.'); + } + + $this->options = $multiOptions; + + if (\is_array($multiOptions)) { + $this->addConnectionCapOptions($options); + + if ($this->multiplexDisabled) { + // CURLPIPE_NOTHING; the constant itself needs libcurl 7.43 + // headers, newer than the oldest supported runtimes. The + // option is required: a handler-wide guarantee must fail + // closed rather than warn like the deprecated raw options. + $this->options[\CURLMOPT_PIPELINING] = 0; + $this->requiredOptions[\CURLMOPT_PIPELINING] = true; + } + } // unsetting the property forces the first access to go through // __get(). @@ -123,8 +283,9 @@ class CurlMultiHandler * * @return resource|\CurlMultiHandle * - * @throws \BadMethodCallException when another field as `_mh` will be gotten - * @throws \RuntimeException when curl can not initialize a multi handle + * @throws \BadMethodCallException when another field as `_mh` will be gotten + * @throws \RuntimeException when curl can not initialize a multi handle + * @throws \InvalidArgumentException when a required cURL multi option cannot be applied */ public function __get($name) { @@ -138,13 +299,34 @@ class CurlMultiHandler throw new \RuntimeException('Can not initialize curl multi handle.'); } - $this->_mh = $multiHandle; + try { + foreach ($this->options as $option => $value) { + if (true === @curl_multi_setopt($multiHandle, $option, $value)) { + continue; + } - foreach ($this->options as $option => $value) { - // A warning is raised in case of a wrong option. - curl_multi_setopt($this->_mh, $option, $value); + if (isset($this->requiredOptions[$option])) { + // A first-class option such as a connection cap must + // never be silently dropped. + throw new \InvalidArgumentException(\sprintf('Unable to apply the cURL multi option %s; it was rejected by the runtime libcurl.', self::formatCurlMultiOption($option))); + } + + \trigger_error(\sprintf('Unable to apply the cURL multi option %s; it was ignored by the runtime libcurl.', self::formatCurlMultiOption($option)), \E_USER_WARNING); + } + } catch (\Throwable $e) { + // Do not publish a partially configured handle; a later access + // retries the initialization from scratch. + try { + \curl_multi_close($multiHandle); + } catch (\Throwable $ignored) { + // Preserve the original failure. + } + + throw $e; } + $this->_mh = $multiHandle; + return $this->_mh; } @@ -163,65 +345,634 @@ class CurlMultiHandler public function __invoke(RequestInterface $request, array $options): PromiseInterface { + if ($this->connectionCapsApplied + && \defined('CURLOPT_SHARE') + && isset($options['curl']) + && \is_array($options['curl']) + && \array_key_exists((int) \constant('CURLOPT_SHARE'), $options['curl']) + ) { + // Key presence alone conflicts: Guzzle cannot verify that a + // caller-managed shared connection pool honors the caps. + throw new \InvalidArgumentException('The request-level CURLOPT_SHARE cURL option cannot be combined with CurlMultiHandler connection cap options because Guzzle cannot verify that an external shared connection pool honors cURL multi connection caps.'); + } + $easy = $this->factory->create($request, $options); + + try { + $this->rejectMultiplexPipeliningConflict($easy, $options); + $this->applyMultiplexNone($easy, $options); + $this->applyProxyTunnelOwnership($easy); + } catch (\Throwable $e) { + try { + $this->factory->release($easy); + } catch (\Throwable $releaseFailure) { + // Preserve the original failure. + } + + throw $e; + } + $id = (int) $easy->handle; + $waitToken = new \stdClass(); + $promise = new Promise( - [$this, 'execute'], - function () use ($id) { - return $this->cancel($id); + function () use ($id, $waitToken): void { + if ($this->multiExecDepth > 0) { + // Waiting cannot drive native cURL while a callback has + // the multi handle busy; fail the wait promptly instead + // of self-deadlocking. + $this->failNestedWait($id, $waitToken); + + return; + } + + $this->executeUntil($id, $waitToken); + }, + function () use ($id, $waitToken) { + return $this->cancel($id, $waitToken); } ); - $this->addRequest(['easy' => $easy, 'deferred' => $promise]); + $entry = ['easy' => $easy, 'deferred' => $promise, 'wait_token' => $waitToken]; + + try { + $this->addRequest($entry); + } catch (\Throwable $e) { + throw $this->discardPendingRequest($id, $entry, $e); + } return $promise; } + /** + * The "multiplex" request option sets CURLOPT_PIPEWAIT, which libcurl + * ignores entirely when the multi handle's CURLMOPT_PIPELINING option + * disables multiplexing, so an explicit request for multiplexing on a + * handler configured against it is a configuration error. The required + * family conflicts marker-independently: a required guarantee on a handler + * that disables multiplexing is contradictory even when the transfer would + * not wait. A raw CURLOPT_PIPEWAIT cURL option conflicts with every + * explicit mode on this handler, where waiting is operationally + * meaningful: whatever its value, it is a second wait/eager authority + * applied after the mode's own decision. + */ + private function rejectMultiplexPipeliningConflict(EasyHandle $easy, array $options): void + { + $multiplex = $options['multiplex'] ?? null; + + if (null === $multiplex) { + return; + } + + if (\defined('CURLOPT_PIPEWAIT') + && isset($options['curl']) + && \is_array($options['curl']) + && \array_key_exists((int) \constant('CURLOPT_PIPEWAIT'), $options['curl']) + ) { + // Key presence alone conflicts, and it must be rejected before + // the marker below is consulted: the marker reflects the final + // merged configuration, which the raw value has falsified. + throw new \InvalidArgumentException('The "multiplex" request option cannot be combined with the raw CURLOPT_PIPEWAIT cURL option on the cURL multi handler; remove the raw option.'); + } + + if (Multiplexing::WAIT === $multiplex && !$easy->usesPipewait) { + // Explicit wait only conflicts when the transfer would actually + // wait; an HTTP/1.1 wait request never sets the marker. + return; + } + + if (!\in_array($multiplex, [Multiplexing::WAIT, Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) { + return; + } + + if ($this->multiplexDisabled) { + // Checked before the raw option: the handler wrote its own + // CURLMOPT_PIPELINING value when "multiplex" disabled it. + throw new \InvalidArgumentException('The "multiplex" request option cannot be combined with a CurlMultiHandler whose "multiplex" option is Multiplexing::NONE; remove the handler option or set the request option to "eager".'); + } + + if (!\is_array($this->options) || !\array_key_exists(\CURLMOPT_PIPELINING, $this->options)) { + // A legacy non-array "options" value is tolerated by the + // constructor and cannot contain the option. + return; + } + + $pipelining = $this->options[\CURLMOPT_PIPELINING]; + if (!\is_scalar($pipelining)) { + // ext-curl derives the integer mask from non-scalar values with + // type-dependent zval semantics, so the effective mask cannot be + // predicted here; require an explicit integer instead. + throw new \InvalidArgumentException('The CurlMultiHandler CURLMOPT_PIPELINING option must be an integer when combined with the "multiplex" request option.'); + } + + $multiplexBit = \defined('CURLPIPE_MULTIPLEX') ? \CURLPIPE_MULTIPLEX : 2; + if (((int) $pipelining & $multiplexBit) !== 0) { + return; + } + + throw new \InvalidArgumentException('The "multiplex" request option cannot be combined with a CurlMultiHandler CURLMOPT_PIPELINING option that disables multiplexing; set CURLMOPT_PIPELINING to CURLPIPE_MULTIPLEX, remove the option, or set the "multiplex" option to "eager".'); + } + + /** + * A Multiplexing::NONE request option is a sole-use guarantee: the + * transfer must not share its connection with any concurrent transfer. + * It holds structurally on a handler whose "multiplex" option is + * Multiplexing::NONE, and for HTTP/1.x transfers, which never join a + * multiplexed connection and open connections nothing can join. An + * HTTP/2 request on a handler that multiplexes is rejected, as is any + * configuration under which the guarantee cannot be verified (custom + * handle factories control the native handle) or cannot be hardened + * (challenge-response authentication retries and Expect 417 retries + * re-enter connection selection as internal follows, which disarm + * CURLOPT_FRESH_CONNECT). A raw CURLMOPT_PIPELINING multi option, and + * deprecated-but-applied raw cURL options that can defeat the declared + * protocol version, retry through internal follows, or replace the + * managed header list, are rejected by key presence. On runtimes whose + * matcher can hand an HTTP/1.x transfer an idle multiplexed connection + * (below libcurl 7.77.0, and 8.11.0-8.12.1), accepted transfers force + * a fresh connection. + */ + private function applyMultiplexNone(EasyHandle $easy, array $options): void + { + if (Multiplexing::NONE !== ($options['multiplex'] ?? null) || $this->multiplexDisabled) { + return; + } + + if (\defined('CURLMOPT_PIPELINING') && \is_array($this->options) && \array_key_exists(\CURLMOPT_PIPELINING, $this->options)) { + // Key presence alone conflicts, matching the constructor's rule + // for the named option: raw multi options that fail to apply only + // warn (they are not in requiredOptions), so even an agreeing + // zero mask cannot prove the guarantee. is_array: legacy non-array + // "options" values are deprecated but still stored. + throw new \InvalidArgumentException('The "multiplex" request option cannot be Multiplexing::NONE alongside a raw CURLMOPT_PIPELINING cURL multi option; replace the raw option with the "multiplex" cURL multi handler option.'); + } + + if ($this->customHandleFactory) { + throw new \InvalidArgumentException('The "multiplex" request option can only be Multiplexing::NONE on a CurlMultiHandler with a custom "handle_factory" when the handler\'s own "multiplex" option is Multiplexing::NONE, because the guarantee is enforced against the native easy handle the factory controls.'); + } + + $version = $easy->request->getProtocolVersion(); + if ('2' === $version || '2.0' === $version) { + throw new \InvalidArgumentException('The "multiplex" request option can only be Multiplexing::NONE for an HTTP/1.x request on a CurlMultiHandler that permits multiplexing; set the "multiplex" client or CurlMultiHandler constructor option to Multiplexing::NONE to disable multiplexing for every transfer, or send the request with its "version" option set to "1.1".'); + } + + if (isset($options['curl']) && \is_array($options['curl'])) { + foreach (['CURLOPT_HTTP_VERSION', 'CURLOPT_HTTPAUTH', 'CURLOPT_PROXYAUTH', 'CURLOPT_FOLLOWLOCATION', 'CURLOPT_HTTPHEADER', 'CURLOPT_ALTSVC', 'CURLOPT_ALTSVC_CTRL', 'CURLOPT_PROXYTYPE'] as $constant) { + if (\defined($constant) && \array_key_exists((int) \constant($constant), $options['curl'])) { + // Key presence alone conflicts. A raw CURLOPT_HTTP_VERSION + // overrides the declared version after the factory + // mapping, and raw alt-svc options or an HTTPS2 proxy + // type can put a declared-HTTP/1.x transfer on a joinable + // HTTP/2 connection; raw challenge-response + // authentication (origin 401 or proxy 407) and native + // redirects re-enter connection selection as internal + // follows, which disarm CURLOPT_FRESH_CONNECT, so the + // hardening below cannot cover them; a raw + // CURLOPT_HTTPHEADER replaces the managed header list, + // including the Expect suppression the check below + // relies on. + throw new \InvalidArgumentException(\sprintf('The "multiplex" request option cannot be Multiplexing::NONE combined with the raw %s cURL option on a CurlMultiHandler that permits multiplexing; remove the raw option, or set the "multiplex" client or CurlMultiHandler constructor option to Multiplexing::NONE.', $constant)); + } + } + } + + if (Psr7\Utils::caselessContains($easy->request->getHeaderLine('Expect'), '100-continue')) { + // libcurl arms its Expect handling by a caseless substring scan + // of the header value (Curl_compareheader), so any value + // containing 100-continue can make a 417 response retry as an + // internal follow, which disarms CURLOPT_FRESH_CONNECT; requests + // without the header are safe because the factory suppresses + // libcurl's automatic Expect. + throw new \InvalidArgumentException('The "multiplex" request option cannot be Multiplexing::NONE for a request carrying an "Expect: 100-continue" header on a CurlMultiHandler that permits multiplexing; remove the explicitly supplied "Expect" header, set the "expect" request option to false to prevent it being added automatically, or set the "multiplex" client or CurlMultiHandler constructor option to Multiplexing::NONE.'); + } + + if (CurlVersion::supportsHttpVersionReuseMatching()) { + return; + } + + // Unqualified curl_setopt so the test bootstrap shadow records it. + if (true !== curl_setopt($easy->handle, \CURLOPT_FRESH_CONNECT, true)) { + // The hardening is the guarantee on these runtimes; failing to + // apply it must fail closed, mirroring applyCurlOptions(). + throw new \InvalidArgumentException('Unable to set cURL option CURLOPT_FRESH_CONNECT.'); + } + } + + /** + * @param array $options + */ + private static function triggerConflictingCurlMultiOptionDeprecations(array $options): void + { + if ($options === []) { + return; + } + + $conflictingOptions = self::conflictingCurlMultiOptions(); + $sinceOverrides = self::conflictingCurlMultiOptionSinceOverrides(); + foreach ($options as $option => $_) { + if (\array_key_exists($option, $conflictingOptions)) { + \trigger_deprecation('guzzlehttp/guzzle', $sinceOverrides[$option] ?? '7.14', \sprintf('Passing %s in the cURL multi handler "options" is deprecated; guzzlehttp/guzzle 8.0 will reject this option. Use %s instead.', self::formatCurlMultiOption($option), $conflictingOptions[$option])); + } + } + } + + /** + * @return array + */ + private static function conflictingCurlMultiOptionSinceOverrides(): array + { + if (!\defined('CURLMOPT_PIPELINING')) { + // Matches conflictingCurlMultiOptions(): ext-curl builds against + // pre-7.16 libcurl headers do not define the constant. + return []; + } + + return [\CURLMOPT_PIPELINING => '7.15']; + } + + /** + * @param array $options + */ + private static function hasConnectionCapOption(array $options): bool + { + foreach (self::CONNECTION_CAP_OPTIONS as $name => $_) { + if (($options[$name] ?? null) !== null) { + return true; + } + } + + return false; + } + + /** + * @param array $constructorOptions + * @param array $multiOptions + */ + private static function rejectConnectionCapOptionConflicts(array $constructorOptions, array $multiOptions): void + { + foreach (self::CONNECTION_CAP_OPTIONS as $name => $constant) { + if (($constructorOptions[$name] ?? null) === null || !\defined($constant)) { + continue; + } + + $option = \constant($constant); + if (\array_key_exists($option, $multiOptions)) { + throw new \InvalidArgumentException(\sprintf('%s conflicts with a %s entry in the "options" array.', $name, $constant)); + } + } + } + + /** + * @param array $options + */ + private function addConnectionCapOptions(array $options): void + { + foreach (self::CONNECTION_CAP_OPTIONS as $name => $constant) { + $value = $options[$name] ?? null; + if ($value === null) { + continue; + } + + if (!\is_int($value) || $value < 1) { + throw new \InvalidArgumentException(\sprintf('%s must be a positive integer.', $name)); + } + + CurlVersion::ensureConnectionCapsSupported($name); + + $option = \constant($constant); + if (\array_key_exists($option, $this->options)) { + throw new \InvalidArgumentException(\sprintf('%s conflicts with a %s entry in the "options" array.', $name, $constant)); + } + + $this->options[$option] = $value; + $this->requiredOptions[$option] = true; + $this->connectionCapsApplied = true; + } + } + + /** + * @param int|string $option + */ + private static function formatCurlMultiOption($option): string + { + if (!\is_int($option)) { + return \sprintf('"%s"', $option); + } + + static $names = null; + + if (null === $names) { + $names = []; + foreach (\get_defined_constants(true)['curl'] ?? [] as $name => $value) { + if (\is_int($value) && \strpos($name, 'CURLMOPT_') === 0 && !isset($names[$value])) { + $names[$value] = $name; + } + } + } + + if (isset($names[$option])) { + return \sprintf('%s (%d)', $names[$option], $option); + } + + return (string) $option; + } + + /** + * @return array + */ + private static function conflictingCurlMultiOptions(): array + { + static $options = null; + + if ($options !== null) { + return $options; + } + + $options = []; + + self::addConflictingCurlMultiOption($options, 'CURLMOPT_MAX_HOST_CONNECTIONS', 'the "max_host_connections" client option or cURL multi handler option'); + self::addConflictingCurlMultiOption($options, 'CURLMOPT_MAX_TOTAL_CONNECTIONS', 'the "max_total_connections" client option or cURL multi handler option'); + self::addConflictingCurlMultiOption($options, 'CURLMOPT_PIPELINING', 'Multiplexing::NONE via the "multiplex" cURL multi handler or client option to disable multiplexing, or remove the raw option for the runtime default (multiplexing defaults on from libcurl 7.62, except 7.65.0 and 7.65.1)'); + + return $options; + } + + /** + * @param array $options + */ + private static function addConflictingCurlMultiOption(array &$options, string $constant, string $replacement): void + { + if (!\defined($constant)) { + return; + } + + $value = \constant($constant); + if (\is_int($value)) { + $options[$value] = $replacement; + } + } + + /** + * Isolates the connection cache when the request's proxy tunnel section + * differs from the one the multi handle's cache may already hold. + */ + private function applyProxyTunnelOwnership(EasyHandle $easy): void + { + $signature = $easy->proxyTunnelSignature; + if ($signature === null || $signature === $this->proxyTunnelOwner) { + return; + } + + if ($this->proxyTunnelOwner === null) { + // No in-domain transfer has ever run on this multi handle: latch + // the owner without destroying pooled direct connections. + $this->proxyTunnelOwner = $signature; + + return; + } + + if ( + $this->handles === [] + && 0 === $this->multiExecDepth + && 0 === $this->messageProcessingDepth + && $this->deferredCancels === [] + ) { + // Idle: hand the connection cache over by recreating the multi + // handle (unsetting re-arms the lazy __get initializer, which + // re-applies the CURLMOPT_* options). + if (isset($this->_mh)) { + \curl_multi_close($this->_mh); + unset($this->_mh); + } + $this->proxyTunnelOwner = $signature; + + return; + } + + // Busy: isolate this transfer from the owner's pooled tunnels. + $this->isolateProxyTunnelTransfer($easy); + } + + private function addCurlHandle(EasyHandle $easy): void + { + $this->isolateFromForeignActiveProxyTunnel($easy); + + // Unqualified curl_multi_add_handle so the test bootstrap shadow can + // override the result. + $result = curl_multi_add_handle($this->_mh, $easy->handle); + + if (\CURLM_OK !== $result) { + if (\PHP_VERSION_ID < 80226 || (\PHP_VERSION_ID >= 80300 && \PHP_VERSION_ID < 80314)) { + // Before PHP 8.2.26 and 8.3.14, ext-curl kept the easy handle + // in its multi bookkeeping even when the native add failed + // (https://github.com/php/php-src/pull/16302); remove it so + // the handle can be pooled or closed safely. + \curl_multi_remove_handle($this->_mh, $easy->handle); + } + + throw new RequestException(\sprintf('Unable to add the cURL handle to the cURL multi handler: %s (%d).', (string) \curl_multi_strerror($result), $result), $easy->request); + } + + $this->markProxyTunnelActive($easy); + + $id = (int) $easy->handle; + if (isset($this->handles[$id])) { + $this->handles[$id]['attached'] = true; + } + } + + /** + * @param resource|\CurlHandle $handle + */ + private function removeCompletedHandleFromMulti(int $id, $handle): void + { + $this->removeHandleFromMulti($handle); + $this->unmarkProxyTunnelActiveById($id); + } + + /** + * Removes a transfer from the multi handle under the native execution + * guard: removing a still-running transfer performs a final progress + * update that can run a user progress callback. + * + * @param resource|\CurlHandle $handle + */ + private function removeHandleFromMulti($handle): void + { + ++$this->multiExecDepth; + + try { + \curl_multi_remove_handle($this->_mh, $handle); + } finally { + --$this->multiExecDepth; + $this->finishDeferredWork(); + } + } + + private function isolateFromForeignActiveProxyTunnel(EasyHandle $easy): void + { + $signature = $easy->proxyTunnelSignature; + + if ($signature === null || $this->activeProxyTunnelSignatures === []) { + return; + } + + if (\count($this->activeProxyTunnelSignatures) === 1 && isset($this->activeProxyTunnelSignatures[$signature])) { + return; + } + + $this->isolateProxyTunnelTransfer($easy); + } + + private function isolateProxyTunnelTransfer(EasyHandle $easy): void + { + foreach (self::PROXY_TUNNEL_ISOLATION_OPTIONS as $name) { + try { + // Unqualified curl_setopt so the test bootstrap shadow records it. + $applied = curl_setopt($easy->handle, (int) \constant($name), true); + } catch (\Throwable $e) { + throw new RequestException(self::proxyTunnelIsolationFailureMessage($name), $easy->request, null, $e); + } + + if (true !== $applied) { + throw new RequestException(self::proxyTunnelIsolationFailureMessage($name), $easy->request); + } + } + } + + private static function proxyTunnelIsolationFailureMessage(string $name): string + { + return \sprintf('Unable to apply the %s cURL option required to isolate the transfer from foreign proxy tunnel connections.', $name); + } + + private function markProxyTunnelActive(EasyHandle $easy): void + { + $signature = $easy->proxyTunnelSignature; + if ($signature === null) { + return; + } + + $id = (int) $easy->handle; + if (isset($this->activeProxyTunnelHandles[$id])) { + if ($this->activeProxyTunnelHandles[$id] === $signature) { + return; + } + + $this->unmarkProxyTunnelActiveById($id); + } + + $this->activeProxyTunnelHandles[$id] = $signature; + $this->activeProxyTunnelSignatures[$signature] = ($this->activeProxyTunnelSignatures[$signature] ?? 0) + 1; + } + + private function unmarkProxyTunnelActive(EasyHandle $easy): void + { + $this->unmarkProxyTunnelActiveById((int) $easy->handle); + } + + private function unmarkProxyTunnelActiveById(int $id): void + { + if (!isset($this->activeProxyTunnelHandles[$id])) { + return; + } + + $signature = $this->activeProxyTunnelHandles[$id]; + unset($this->activeProxyTunnelHandles[$id]); + + if (!isset($this->activeProxyTunnelSignatures[$signature])) { + return; + } + + --$this->activeProxyTunnelSignatures[$signature]; + + if ($this->activeProxyTunnelSignatures[$signature] <= 0) { + unset($this->activeProxyTunnelSignatures[$signature]); + } + } + /** * Ticks the curl event loop. */ public function tick(): void { - // Add any delayed handles if needed. - if ($this->delays) { + $this->tickFor(null, null); + } + + /** + * Ticks the curl event loop, returning before the blocking select if the + * targeted transfer has settled, been canceled, or been replaced by a + * request that reused its native handle ID. + */ + private function tickFor(?int $targetId, ?object $waitToken): void + { + // Add any delayed handles if needed. Attachment is skipped while a + // callback has native execution busy; the outer frame attaches due + // transfers once it unwinds. + if ($this->delays && 0 === $this->multiExecDepth) { $currentTime = Utils::currentTime(); foreach ($this->delays as $id => $delay) { if ($currentTime >= $delay) { + $entry = $this->handles[$id]; unset($this->delays[$id]); - \curl_multi_add_handle( - $this->_mh, - $this->handles[$id]['easy']->handle - ); + + try { + $this->addCurlHandle($entry['easy']); + } catch (\Throwable $e) { + // The promise has already escaped, so reject it + // rather than throw. + $rejection = $this->discardPendingRequest($id, $entry, $e); + if (P\Is::pending($entry['deferred'])) { + $entry['deferred']->reject($rejection); + } + } } } } - // Run curl_multi_exec in the queue to enable other async tasks to run - P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue'])); + // Run curl_multi_exec in the queue to enable other async tasks to + // run, surface completions, and drain any work they queued so a + // ready cancellation or new transfer is not held behind the select. + do { + P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue'])); - // Step through the task queue which may add additional requests. - P\Utils::queue()->run(); + // Step through the task queue which may add additional requests. + P\Utils::queue()->run(); - if ($this->active && \curl_multi_select($this->_mh, $this->selectTimeout) === -1) { + if ($this->multiExecDepth > 0) { + // A cURL callback re-entered the handler while native + // execution is running; the outer frame drives native cURL + // once it unwinds. + return; + } + + if (isset($this->_mh)) { + $this->processMessages(); + } + } while (!P\Utils::queue()->isEmpty()); + + if (!isset($this->_mh)) { + // Nothing is attached natively (or initialization just failed); + // there is nothing to run and nothing to recreate the handle for. + return; + } + + if ($targetId !== null && !$this->hasRequest($targetId, $waitToken)) { + return; + } + + if ($this->active && \curl_multi_select($this->_mh, $this->effectiveSelectTimeout()) === -1) { // Perform a usleep if a select returns -1. // See: https://bugs.php.net/bug.php?id=61141 \usleep(250); } do { - $this->executingMulti = true; - - try { - $exec = \curl_multi_exec($this->_mh, $this->active); - } finally { - $this->executingMulti = false; - $this->cleanupDeferredCancels(); - } + $exec = $this->executeMulti(); // Prevent busy looping for slow HTTP requests. if ($exec === \CURLM_CALL_MULTI_PERFORM) { - \curl_multi_select($this->_mh, $this->selectTimeout); + \curl_multi_select($this->_mh, $this->effectiveSelectTimeout()); } } while ($exec === \CURLM_CALL_MULTI_PERFORM); @@ -233,82 +984,275 @@ class CurlMultiHandler */ private function tickInQueue(): void { - $this->executingMulti = true; - - try { - $exec = \curl_multi_exec($this->_mh, $this->active); - } finally { - $this->executingMulti = false; - $this->cleanupDeferredCancels(); + if ($this->multiExecDepth > 0) { + // A cURL callback re-entered the handler while native execution + // is running; the outer frame drives native cURL once it unwinds. + return; } + if (!isset($this->_mh)) { + // Nothing is attached natively (or initialization just failed); + // there is nothing to run and nothing to recreate the handle for. + return; + } + + $exec = $this->executeMulti(); + if ($exec === \CURLM_CALL_MULTI_PERFORM) { \curl_multi_select($this->_mh, 0); P\Utils::queue()->add(Closure::fromCallable([$this, 'tickInQueue'])); } } + /** + * @phpstan-impure + */ + private function executeMulti(): int + { + ++$this->multiExecDepth; + + try { + return \curl_multi_exec($this->_mh, $this->active); + } finally { + --$this->multiExecDepth; + $this->finishDeferredWork(); + } + } + + /** + * Flushes cancels and attachments deferred while the multi handle was + * busy executing transfers or removing a handle. + */ + private function finishDeferredWork(): void + { + if ($this->multiExecDepth > 0 || $this->finishingDeferredWork) { + // A nested frame (a cURL callback re-entered the handler) must + // not flush while an outer frame is still using the multi + // handle; the outermost frame flushes once it unwinds. + return; + } + + $this->finishingDeferredWork = true; + + try { + $failure = null; + + // Removing a cancelled transfer runs its final progress update, + // whose callback can cancel other transfers or create requests; + // drain until no deferred work remains. + do { + $this->cleanupDeferredCancels($failure); + $this->flushDeferredAdds(); + } while ($this->deferredCancels !== [] || $this->deferredAdds !== []); + + if ($failure !== null) { + throw $failure; + } + } finally { + $this->finishingDeferredWork = false; + } + } + /** * Runs until all outstanding connections have completed. */ public function execute(): void { + if ($this->multiExecDepth > 0) { + // Native cURL cannot be driven while a callback has it busy, so + // the loop would spin without ever progressing. + throw new \LogicException('Cannot run the cURL multi event loop from inside a cURL callback; the callback must return before transfers can progress.'); + } + $queue = P\Utils::queue(); while ($this->handles || !$queue->isEmpty()) { - // If there are no transfers, then sleep for the next delay - if (!$this->active && $this->delays) { + // If there are no transfers, then sleep for the next delay, + // unless ready queue work could change what is pending. + if (!$this->active && $this->delays && $queue->isEmpty()) { \usleep($this->timeToNext()); } $this->tick(); } } + /** + * Runs the event loop until the given transfer has finished, so waiting + * on a promise does not wait for every other transfer on the handler + * like execute() does. + * + * The native cURL handle ID can be reused by a request created from a + * completion callback, so the wait token guards against waiting on an + * unrelated transfer that inherited the ID. + */ + private function executeUntil(int $id, object $waitToken): void + { + $queue = P\Utils::queue(); + + while ($this->hasRequest($id, $waitToken)) { + // If the transfer is delayed, then sleep until it is due, unless + // ready queue work could cancel or replace it first. + if (!$this->active && isset($this->delays[$id]) && $queue->isEmpty()) { + \usleep($this->timeToNext()); + } + $this->tickFor($id, $waitToken); + } + + if (!$queue->isEmpty()) { + $queue->run(); + } + } + + /** + * Checks that the request with the given handle ID is still pending and, + * when a wait token is given, has not been replaced by a request that + * reused the ID. + */ + private function hasRequest(int $id, ?object $waitToken = null): bool + { + if (!isset($this->handles[$id])) { + return false; + } + + return $waitToken === null || ($this->handles[$id]['wait_token'] ?? null) === $waitToken; + } + private function addRequest(array $entry): void { $easy = $entry['easy']; $id = (int) $easy->handle; + $entry['attached'] = false; $this->handles[$id] = $entry; - if (empty($easy->options['delay'])) { - \curl_multi_add_handle($this->_mh, $easy->handle); - } else { + + if (!empty($easy->options['delay'])) { $this->delays[$id] = Utils::currentTime() + ($easy->options['delay'] / 1000); + } elseif ($this->multiExecDepth > 0) { + // A request created from inside a cURL callback cannot be added + // natively while curl_multi_exec() is running; libcurl 7.59+ + // rejects the recursive call. Attach it once the outermost + // native execution unwinds. + $this->deferredAdds[$id] = $entry['wait_token'] ?? null; + } else { + $this->addCurlHandle($easy); + } + } + + /** + * Rolls back a request that can no longer be attached, releasing the + * easy handle exactly once and preserving the original failure. + * + * @param array{easy: EasyHandle, deferred: Promise, wait_token?: object|null, attached?: bool} $entry + */ + private function discardPendingRequest(int $id, array $entry, \Throwable $failure): \Throwable + { + unset($this->handles[$id], $this->delays[$id], $this->deferredAdds[$id]); + + try { + $this->factory->release($entry['easy']); + } catch (\Throwable $e) { + // Preserve the original failure. + } + + return $failure; + } + + /** + * Fails a synchronous wait attempted from inside a cURL callback, where + * native execution cannot progress until the callback returns. + */ + private function failNestedWait(int $id, object $token): void + { + if (!$this->hasRequest($id, $token)) { + return; + } + + $entry = $this->handles[$id]; + $failure = new RequestException('Cannot synchronously wait for a transfer from inside a cURL callback on the same cURL multi handler; the callback must return before the transfer can progress.', $entry['easy']->request, $entry['easy']->response); + + if (!empty($entry['attached'])) { + // Native removal must wait until the outermost execution unwinds. + unset($this->handles[$id], $this->delays[$id], $this->deferredAdds[$id]); + $this->deferredCancels[$id] = ['easy' => $entry['easy'], 'attached' => true]; + } else { + $this->discardPendingRequest($id, $entry, $failure); + } + + $entry['deferred']->reject($failure); + } + + /** + * Attaches requests whose native attachment was deferred because they + * were created from inside a cURL callback. + */ + private function flushDeferredAdds(): void + { + if ($this->deferredAdds === []) { + return; + } + + $adds = $this->deferredAdds; + $this->deferredAdds = []; + + foreach ($adds as $id => $token) { + if (!$this->hasRequest($id, $token)) { + // Cancelled or replaced while the attachment was deferred. + continue; + } + + $entry = $this->handles[$id]; + + try { + $this->addCurlHandle($entry['easy']); + } catch (\Throwable $e) { + // The promise has already escaped, so reject it rather than + // throw. User code may have settled it directly; a settled + // promise must not abort the rest of the snapshot. + $rejection = $this->discardPendingRequest($id, $entry, $e); + if (P\Is::pending($entry['deferred'])) { + $entry['deferred']->reject($rejection); + } + } } } /** * Cancels a handle from sending and removes references to it. * - * @param int $id Handle ID to cancel and remove. + * @param int $id Handle ID to cancel and remove. + * @param object|null $waitToken Identity token that must still match the + * entry when given. * * @return bool True on success, false on failure. */ - private function cancel($id): bool + private function cancel($id, ?object $waitToken = null): bool { if (!is_int($id)) { \trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } - // Cannot cancel if it has been processed. - if (!isset($this->handles[$id])) { + // Cannot cancel if it has been processed or replaced by a request + // that reused the native handle ID. + if (!isset($this->handles[$id]) || ($waitToken !== null && ($this->handles[$id]['wait_token'] ?? null) !== $waitToken)) { return false; } - $easy = $this->handles[$id]['easy']; - unset($this->delays[$id], $this->handles[$id]); + $entry = $this->handles[$id]; + $easy = $entry['easy']; + $attached = !empty($entry['attached']); + unset($this->delays[$id], $this->deferredAdds[$id], $this->handles[$id]); - if ($this->executingMulti) { - $this->deferredCancels[$id] = $easy; + if ($this->multiExecDepth > 0) { + $this->deferredCancels[$id] = ['easy' => $easy, 'attached' => $attached]; return true; } - $this->cleanupCancelledHandle($easy); + $this->cleanupCancelledHandle($easy, $attached); return true; } - private function cleanupDeferredCancels(): void + private function cleanupDeferredCancels(?\Throwable &$failure): void { if ($this->deferredCancels === []) { return; @@ -317,68 +1261,144 @@ class CurlMultiHandler $entries = $this->deferredCancels; $this->deferredCancels = []; - foreach ($entries as $easy) { - $this->cleanupCancelledHandle($easy); + foreach ($entries as $entry) { + try { + $this->cleanupCancelledHandle($entry['easy'], $entry['attached']); + } catch (\Throwable $e) { + // A final progress update can run a throwing user callback; + // clean the remaining entries and surface the first failure + // once the drain completes. + if ($failure === null) { + $failure = $e; + } + } } } - private function cleanupCancelledHandle(EasyHandle $easy): void + private function cleanupCancelledHandle(EasyHandle $easy, bool $attached): void { $handle = $easy->handle; - \curl_multi_remove_handle($this->_mh, $handle); + $failure = null; + + if ($attached) { + try { + $this->removeHandleFromMulti($handle); + } catch (\Throwable $e) { + // The native detach completes even when its final progress + // callback throws; finish this entry before rethrowing. + $failure = $e; + } + } + + $this->unmarkProxyTunnelActive($easy); if (PHP_VERSION_ID < 80000) { - \curl_close($handle); + try { + \curl_close($handle); + } catch (\Throwable $e) { + // An error handler can promote the close warning; keep the + // first failure. + if ($failure === null) { + $failure = $e; + } + } + } + + if ($failure !== null) { + throw $failure; } } private function processMessages(): void { - while ($done = \curl_multi_info_read($this->_mh)) { - if ($done['msg'] !== \CURLMSG_DONE) { - // if it's not done, then it would be premature to remove the handle. ref https://github.com/guzzle/guzzle/pull/2892#issuecomment-945150216 - continue; + // CurlFactory::finish can retry a transfer by re-invoking this handler + // from inside this loop; the guard keeps that re-entry from recreating + // the multi handle mid-iteration (see applyProxyTunnelOwnership). A + // depth is tracked because a completion callback can re-enter tick(), + // and the nested frame must not clear the outer loop's guard. + ++$this->messageProcessingDepth; + + try { + while ($done = \curl_multi_info_read($this->_mh)) { + if ($done['msg'] !== \CURLMSG_DONE) { + // if it's not done, then it would be premature to remove the handle. ref https://github.com/guzzle/guzzle/pull/2892#issuecomment-945150216 + continue; + } + if (!isset($done['handle'])) { + // Work around a PHP issue where cancelled transfers may omit the handle. + // Remove this once we no longer support PHP versions before the fix in + // https://github.com/php/php-src/pull/16302. + continue; + } + $id = (int) $done['handle']; + $this->removeCompletedHandleFromMulti($id, $done['handle']); + + if (!isset($this->handles[$id])) { + // Probably was cancelled. + continue; + } + + $entry = $this->handles[$id]; + unset($this->handles[$id], $this->delays[$id]); + $entry['easy']->errno = $done['result']; + + // finish() can run completion callbacks that cancel this + // promise; a settled promise must not be settled again. + try { + $result = CurlFactory::finish($this, $entry['easy'], $this->factory); + } catch (\Throwable $e) { + if (P\Is::pending($entry['deferred'])) { + $entry['deferred']->reject($e); + } + + continue; + } + + if (P\Is::pending($entry['deferred'])) { + $entry['deferred']->resolve($result); + } } - if (!isset($done['handle'])) { - // Work around a PHP issue where cancelled transfers may omit the handle. - // Remove this once we no longer support PHP versions before the fix in - // https://github.com/php/php-src/pull/16302. - continue; - } - $id = (int) $done['handle']; - \curl_multi_remove_handle($this->_mh, $done['handle']); - - if (!isset($this->handles[$id])) { - // Probably was cancelled. - continue; - } - - $entry = $this->handles[$id]; - unset($this->handles[$id], $this->delays[$id]); - $entry['easy']->errno = $done['result']; - - try { - $result = CurlFactory::finish($this, $entry['easy'], $this->factory); - } catch (\Throwable $e) { - $entry['deferred']->reject($e); - - continue; - } - - $entry['deferred']->resolve($result); + } finally { + --$this->messageProcessingDepth; } } - private function timeToNext(): int + /** + * Bounds a blocking select by the earliest pending request delay so a + * delayed transfer becoming due does not wait out an unrelated + * transfer's full select timeout. + * + * @return float|int + */ + private function effectiveSelectTimeout() + { + if ($this->delays === []) { + return $this->selectTimeout; + } + + return \min($this->selectTimeout, $this->secondsToNext()); + } + + /** + * @return float Seconds until the earliest pending delay is due + */ + private function secondsToNext(): float { $currentTime = Utils::currentTime(); - $nextTime = \PHP_INT_MAX; + $nextTime = \PHP_FLOAT_MAX; foreach ($this->delays as $time) { if ($time < $nextTime) { $nextTime = $time; } } - return ((int) \max(0, $nextTime - $currentTime)) * 1000000; + return \max(0.0, $nextTime - $currentTime); + } + + private function timeToNext(): int + { + // PHP_INT_MAX first: min() then returns the int operand whenever the + // microseconds exceed it, so the cast never sees an oversized float. + return (int) \min(\PHP_INT_MAX, $this->secondsToNext() * 1000000); } } diff --git a/vendor/guzzlehttp/guzzle/src/Handler/CurlShareHandleState.php b/vendor/guzzlehttp/guzzle/src/Handler/CurlShareHandleState.php index ca0c705..75f46c4 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/CurlShareHandleState.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/CurlShareHandleState.php @@ -3,7 +3,6 @@ namespace GuzzleHttp\Handler; use GuzzleHttp\TransportSharing; -use GuzzleHttp\Utils; /** * @internal @@ -70,7 +69,7 @@ final class CurlShareHandleState throw new \InvalidArgumentException(\sprintf( 'The "%s" option must be null or a GuzzleHttp\\TransportSharing::* constant; received %s.', $option, - Utils::describeType($sharing) + \get_debug_type($sharing) )); } @@ -108,7 +107,7 @@ final class CurlShareHandleState self::requireCurlConstant('CURLOPT_SHARE'); $shareOption = self::requireCurlConstant('CURLSHOPT_SHARE'); - $locks = self::handlerLocks(); + $locks = self::handlerLocks($mode); $handle = curl_share_init(); try { @@ -135,12 +134,23 @@ final class CurlShareHandleState /** * @return int[] */ - private static function handlerLocks(): array + private static function handlerLocks(string $mode): array { - return [ + CurlVersion::ensureHandlerSharingSupported(); + + if ($mode === TransportSharing::HANDLER_REQUIRE) { + CurlVersion::ensureSslSessionSharingSupported(); + } + + $locks = [ self::requireCurlConstant('CURL_LOCK_DATA_DNS'), - self::requireCurlConstant('CURL_LOCK_DATA_SSL_SESSION'), ]; + + if (CurlVersion::supportsSslSessionSharing()) { + $locks[] = self::requireCurlConstant('CURL_LOCK_DATA_SSL_SESSION'); + } + + return $locks; } private static function requireCurlConstant(string $constant): int diff --git a/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php b/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php index e43460d..5fa1650 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/EasyHandle.php @@ -30,6 +30,17 @@ final class EasyHandle */ public $headers = []; + /** + * @var array Valid trailer lines, retained only when an on_trailers + * callback is configured + */ + public $trailers = []; + + /** + * @var bool Whether this handle was configured with CURLOPT_PIPEWAIT + */ + public $usesPipewait = false; + /** * @var ResponseInterface|null Received response (if any) */ @@ -50,6 +61,19 @@ final class EasyHandle */ public $errno = 0; + /** + * @var string|null Effective CURLOPT_PROXY value the handle was created with (if any) + */ + public $effectiveProxy; + + /** + * Proxy tunnel or SOCKS proxy section signature for connection-reuse + * isolation, or null when the request does not require sectioning. + * + * @var string|null + */ + public $proxyTunnelSignature; + /** * @var \Throwable|null Exception during on_headers (if any) */ @@ -74,7 +98,7 @@ final class EasyHandle $normalizedKeys = Utils::normalizeHeaderKeys($headers); - if (!empty($this->options['decode_content']) && isset($normalizedKeys['content-encoding'])) { + if (isset($this->options['decode_content']) && $this->options['decode_content'] !== false && isset($normalizedKeys['content-encoding'])) { $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']]; unset($headers[$normalizedKeys['content-encoding']]); if (isset($normalizedKeys['content-length'])) { diff --git a/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php b/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php index f55ac53..ba725cb 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/HeaderProcessor.php @@ -44,7 +44,7 @@ final class HeaderProcessor throw new \RuntimeException('HTTP status code missing from header data'); } - if (!\preg_match('/^\d{3}$/', $status)) { + if (!\preg_match('/^\d{3}$/D', $status)) { throw new \RuntimeException('HTTP status code is invalid'); } @@ -57,6 +57,26 @@ final class HeaderProcessor return [$version, (int) $status, $parts[2] ?? null, Utils::headersFromLines($headers)]; } + public static function isStatusLineCandidate(string $line): bool + { + return \preg_match('/^HTTP\/[0-9]+(?:\.[0-9]+)? [0-9]{3}(?: [^\r\n]*)?(?:\r\n|\r|\n)?$/iD', $line) === 1; + } + + public static function isValidHeaderFieldLine(string $line): bool + { + $parts = \explode(':', $line, 2); + + if (!isset($parts[1])) { + return false; + } + + if (!\preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $parts[0])) { + return false; + } + + return \preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*(?:\r\n|\r|\n)?$/D', \trim($parts[1], " \t")) === 1; + } + /** * @param non-empty-list $headers * @@ -67,7 +87,7 @@ final class HeaderProcessor $lastStatusLine = 0; foreach ($headers as $index => $line) { - if (\preg_match('/^HTTP\/\S+\s+/i', $line)) { + if (self::isStatusLineCandidate($line)) { $lastStatusLine = $index; } } diff --git a/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php index 3ecd596..092642b 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/MockHandler.php @@ -7,7 +7,6 @@ use GuzzleHttp\HandlerStack; use GuzzleHttp\Promise as P; use GuzzleHttp\Promise\PromiseInterface; use GuzzleHttp\TransferStats; -use GuzzleHttp\Utils; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; @@ -160,7 +159,7 @@ class MockHandler implements \Countable ) { $this->queue[] = $value; } else { - throw new \TypeError('Expected a Response, Promise, Throwable or callable. Found '.Utils::describeType($value)); + throw new \TypeError('Expected a Response, Promise, Throwable or callable. Found '.\get_debug_type($value)); } } } diff --git a/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php b/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php index 9df70cf..e0197f6 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/Proxy.php @@ -48,4 +48,34 @@ class Proxy return empty($options['stream']) ? $default($request, $options) : $streaming($request, $options); }; } + + /** + * Sends requests to a fallback handler when the default cURL handler cannot + * honor TLS 1.2 selection. + * + * @param callable(RequestInterface, array): PromiseInterface $default + * @param callable(RequestInterface, array): PromiseInterface $fallback + * + * @return callable(RequestInterface, array): PromiseInterface Returns the composed handler. + */ + public static function wrapTlsFallback(callable $default, callable $fallback): callable + { + return static function (RequestInterface $request, array $options) use ($default, $fallback): PromiseInterface { + if (self::requiresTls12Fallback($options)) { + return $fallback($request, $options); + } + + return $default($request, $options); + }; + } + + /** + * @param array $options + */ + private static function requiresTls12Fallback(array $options): bool + { + return isset($options[RequestOptions::CRYPTO_METHOD]) + && $options[RequestOptions::CRYPTO_METHOD] === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT + && !CurlVersion::supportsTls12(); + } } diff --git a/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php b/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php index 0dd2e64..95e6007 100644 --- a/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php +++ b/vendor/guzzlehttp/guzzle/src/Handler/StreamHandler.php @@ -4,6 +4,7 @@ namespace GuzzleHttp\Handler; use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Exception\RequestException; +use GuzzleHttp\Multiplexing; use GuzzleHttp\Promise as P; use GuzzleHttp\Promise\FulfilledPromise; use GuzzleHttp\Promise\PromiseInterface; @@ -23,6 +24,12 @@ use Psr\Http\Message\UriInterface; */ class StreamHandler { + private const KNOWN_CONSTRUCTOR_OPTIONS = [ + 'max_host_connections' => true, + 'max_total_connections' => true, + 'transport_sharing' => true, + ]; + private const CONNECTION_ERRORS = [ 'php_network_getaddresses:', 'getaddrinfo', @@ -51,19 +58,54 @@ class StreamHandler */ private $transportSharingMode; + /** + * @var bool + */ + private $connectionCapsConfigured = false; + /** * Accepts an associative array of options: * + * - max_host_connections: Optional positive integer or null. A non-null + * value marks the handler as incompatible with enabled response + * streaming; the number is not used for stream-handler admission. + * - max_total_connections: Optional positive integer or null. A non-null + * value marks the handler as incompatible with enabled response + * streaming; the number is not used for stream-handler admission. * - transport_sharing: Optional transport sharing mode. * - * @param array{transport_sharing?: mixed} $options Array of options to use with the handler + * The stream handler cannot cap streamed connections, so a configured cap + * marker rejects enabled response streaming ("stream" => true). Accepted + * transfers are buffered and hold at most one connection per in-flight + * call, but overlapping buffered calls are not collectively limited. + * + * @param array{max_host_connections?: mixed, max_total_connections?: mixed, transport_sharing?: mixed} $options Array of options to use with the handler */ public function __construct(array $options = []) { + foreach ($options as $name => $_) { + if (!isset(self::KNOWN_CONSTRUCTOR_OPTIONS[$name])) { + \trigger_deprecation('guzzlehttp/guzzle', '7.14', \sprintf('The "%s" StreamHandler constructor option is unknown; guzzlehttp/guzzle 8.0 will reject unknown constructor options.', (string) $name)); + } + } + $this->transportSharingMode = CurlShareHandleState::normalizeMode( $options['transport_sharing'] ?? null, 'transport_sharing' ); + + foreach (['max_host_connections', 'max_total_connections'] as $capOption) { + $value = $options[$capOption] ?? null; + if ($value === null) { + continue; + } + + if (!\is_int($value) || $value < 1) { + throw new \InvalidArgumentException(\sprintf('%s must be a positive integer.', $capOption)); + } + + $this->connectionCapsConfigured = true; + } } /** @@ -79,6 +121,29 @@ class StreamHandler \usleep($options['delay'] * 1000); } + $multiplex = $options['multiplex'] ?? null; + + // Multiplexing::NONE is trivially satisfied: the stream handler sends + // one HTTP/1.x request per connection and never multiplexes. + if (null !== $multiplex && !\in_array($multiplex, [Multiplexing::NONE, Multiplexing::EAGER, Multiplexing::WAIT, Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) { + throw new \InvalidArgumentException(\sprintf( + 'The "multiplex" option must be null or a GuzzleHttp\\Multiplexing::* constant; received %s.', + \get_debug_type($multiplex) + )); + } + + if (\in_array($multiplex, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) { + throw new ConnectException('The stream handler cannot guarantee a multiplexed protocol; required multiplexing needs a cURL handler.', $request); + } + + if ($this->connectionCapsConfigured && !empty($options['stream'])) { + throw new \InvalidArgumentException('Enabling the "stream" request option on a stream handler configured with the "max_host_connections" or "max_total_connections" option is not supported because streamed connections cannot be capped.'); + } + + if (isset($options['on_trailers'])) { + throw new \InvalidArgumentException('Passing the "on_trailers" request option to the stream handler is not supported because the stream handler cannot observe trailers.'); + } + $protocolVersion = $request->getProtocolVersion(); if ('' === $protocolVersion) { @@ -105,8 +170,8 @@ class StreamHandler // the behavior of `CurlHandler` if ( ( - 0 === \strcasecmp('PUT', $request->getMethod()) - || 0 === \strcasecmp('POST', $request->getMethod()) + Psr7\Utils::caselessEquals('PUT', $request->getMethod()) + || Psr7\Utils::caselessEquals('POST', $request->getMethod()) ) && 0 === $request->getBody()->getSize() ) { @@ -176,7 +241,7 @@ class StreamHandler $stream = Psr7\Utils::streamFor($stream); $sink = $stream; - if (\strcasecmp('HEAD', $request->getMethod())) { + if (!Psr7\Utils::caselessEquals('HEAD', $request->getMethod())) { $sink = $this->createSink($stream, $options); } @@ -242,7 +307,7 @@ class StreamHandler private function checkDecode(array $options, array $headers, $stream): array { // Automatically decode responses when instructed. - if (!empty($options['decode_content'])) { + if (isset($options['decode_content']) && $options['decode_content'] !== false) { $normalizedKeys = Utils::normalizeHeaderKeys($headers); if (isset($normalizedKeys['content-encoding'])) { $encoding = $headers[$normalizedKeys['content-encoding']]; @@ -328,7 +393,7 @@ class StreamHandler $message .= "[$key] $value".\PHP_EOL; } } - throw new \RuntimeException(\trim($message)); + throw new \RuntimeException(\trim($message, " \n\r\t\0\x0B")); } return $resource; @@ -344,7 +409,12 @@ class StreamHandler $methods = \array_flip(\get_class_methods(__CLASS__)); } - $scheme = $request->getUri()->getScheme(); + $uri = $request->getUri(); + $scheme = $uri->getScheme(); + if ($scheme === '') { + throw new RequestException('URI must include a scheme and host. Use an absolute URI, a network-path reference starting with //, or configure a base_uri.', $request); + } + if (!\in_array($scheme, ['http', 'https'], true)) { throw new RequestException(\sprintf("The scheme '%s' is not supported.", $scheme), $request); } @@ -354,6 +424,10 @@ class StreamHandler throw new RequestException(\sprintf('The scheme "%s" is not allowed by the protocols request option.', $scheme), $request); } + if ($uri->getHost() === '') { + throw new RequestException('URI must include a scheme and host. Use an absolute URI, a network-path reference starting with //, or configure a base_uri.', $request); + } + // HTTP/1.1 streams using the PHP stream wrapper require a // Connection: close header if ($request->getProtocolVersion() === '1.1' @@ -374,10 +448,19 @@ class StreamHandler throw new \InvalidArgumentException('on_headers must be callable'); } + self::assertTlsVersionRangeForOptions($options); + + $proxyAuthorizationAdded = false; if (!empty($options)) { foreach ($options as $key => $value) { $method = "add_{$key}"; if (isset($methods[$method])) { + if ($method === 'add_proxy') { + $proxyAuthorizationAdded = $this->add_proxy($request, $context, $value, $params); + + continue; + } + $this->{$method}($request, $context, $value, $params); } } @@ -387,6 +470,16 @@ class StreamHandler if (!\is_array($options['stream_context'])) { throw new \InvalidArgumentException('stream_context must be an array'); } + if ( + $proxyAuthorizationAdded + && isset($options['stream_context']['http']) + && \is_array($options['stream_context']['http']) + && \array_key_exists('proxy', $options['stream_context']['http']) + ) { + throw new \InvalidArgumentException('stream_context.http.proxy cannot override a proxy after the stream handler has generated a Proxy-Authorization header; configure the final proxy with the "proxy" request option.'); + } + self::triggerConflictingStreamContextOptionDeprecations($options['stream_context']); + self::triggerUnsupportedStreamContextOptionDeprecations($options['stream_context']); $context = \array_replace_recursive($context, $options['stream_context']); } @@ -415,7 +508,7 @@ class StreamHandler $this->lastHeaders = $http_response_header ?? []; if (false === $resource) { - throw new ConnectException(sprintf('Connection refused for URI %s', $uri), $request, null, $context); + throw new ConnectException(sprintf('Connection refused for URI %s', Psr7\Utils::redactUserInfo($uri)), $request, null, $context); } if (isset($options['read_timeout'])) { @@ -434,7 +527,11 @@ class StreamHandler { $uri = $request->getUri(); - if (isset($options['force_ip_resolve']) && !\filter_var($uri->getHost(), \FILTER_VALIDATE_IP)) { + $host = $uri->getHost(); + $hostForIpCheck = $host !== '' && $host[0] === '[' && \substr($host, -1) === ']' + ? \substr($host, 1, -1) + : $host; + if (isset($options['force_ip_resolve']) && !\filter_var($hostForIpCheck, \FILTER_VALIDATE_IP)) { if ('v4' === $options['force_ip_resolve']) { $records = \dns_get_record($uri->getHost(), \DNS_A); if (false === $records || !isset($records[0]['ip'])) { @@ -460,6 +557,17 @@ class StreamHandler { $headers = ''; foreach ($request->getHeaders() as $name => $value) { + // A first-class Proxy-Authorization header is proxy-scoped. Keep + // it out of the origin context; add_proxy() adds one + // validated canonical line only when Guzzle selects a proxy; PHP + // extracts that line for CONNECT and removes it before sending the + // tunneled origin request. The caselessEquals() helper is + // locale-independent, unlike strcasecmp(), so a locale cannot + // make this match miss and re-leak the credential. + if (Psr7\Utils::caselessEquals((string) $name, 'Proxy-Authorization')) { + continue; + } + foreach ($value as $val) { $headers .= "$name: $val\r\n"; } @@ -488,7 +596,7 @@ class StreamHandler } } - $context['http']['header'] = \rtrim($context['http']['header']); + $context['http']['header'] = \rtrim($context['http']['header'], " \n\r\t\0\x0B"); return $context; } @@ -504,15 +612,163 @@ class StreamHandler \trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing the "curl" request option to the stream handler is deprecated; guzzlehttp/guzzle 8.0 will reject this option because the stream handler ignores cURL options.'); } - if (self::usesDigestAuth($options)) { - \trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing digest authentication to the stream handler is deprecated; guzzlehttp/guzzle 8.0 will reject digest authentication with the stream handler because it is only supported by cURL handlers.'); - } - if (\array_key_exists('expect', $options) && $options['expect'] !== false && $request->hasHeader('Expect')) { \trigger_deprecation('guzzlehttp/guzzle', '7.11', 'Passing the "expect" request option to the stream handler is deprecated when it adds an Expect header; guzzlehttp/guzzle 8.0 will reject this option because the stream handler does not support Expect: 100-Continue.'); } } + private static function triggerConflictingStreamContextOptionDeprecations(array $streamContext): void + { + $conflictingOptions = self::conflictingStreamContextOptions(); + + foreach ($streamContext as $wrapper => $contextOptions) { + if (!\is_string($wrapper) || !isset($conflictingOptions[$wrapper]) || !\is_array($contextOptions)) { + continue; + } + + foreach ($contextOptions as $option => $_) { + if (!\is_string($option) || !\array_key_exists($option, $conflictingOptions[$wrapper])) { + continue; + } + + \trigger_deprecation( + 'guzzlehttp/guzzle', + '7.12', + \sprintf( + 'Passing stream_context.%s.%s in the "stream_context" request option is deprecated; guzzlehttp/guzzle 8.0 will reject this option because it conflicts with Guzzle-managed request handling. Use %s instead.', + $wrapper, + $option, + $conflictingOptions[$wrapper][$option] + ) + ); + } + } + } + + private static function triggerUnsupportedStreamContextOptionDeprecations(array $streamContext): void + { + $unsupportedOptions = self::unsupportedStreamContextOptions($streamContext); + if ($unsupportedOptions === []) { + return; + } + + \trigger_deprecation( + 'guzzlehttp/guzzle', + '7.12', + \sprintf( + 'Passing PHP stream context options outside the built-in stream handler allow-list to the "stream_context" request option is deprecated; guzzlehttp/guzzle 8.0 will reject stream context options outside the allow-list. Deprecated option%s: %s.', + \count($unsupportedOptions) === 1 ? '' : 's', + \implode(', ', $unsupportedOptions) + ) + ); + } + + /** + * @return string[] + */ + private static function unsupportedStreamContextOptions(array $streamContext): array + { + $supportedOptions = self::supportedStreamContextOptions(); + $conflictingOptions = self::conflictingStreamContextOptions(); + $unsupportedOptions = []; + + foreach ($streamContext as $wrapper => $contextOptions) { + if (!\is_string($wrapper) || !isset($supportedOptions[$wrapper])) { + if (\is_array($contextOptions)) { + foreach ($contextOptions as $option => $_) { + if (\is_string($wrapper) && \is_string($option) && isset($conflictingOptions[$wrapper]) && \array_key_exists($option, $conflictingOptions[$wrapper])) { + continue; + } + + $unsupportedOptions[] = \sprintf('stream_context.%s.%s', (string) $wrapper, (string) $option); + } + } else { + $unsupportedOptions[] = \sprintf('stream_context.%s', (string) $wrapper); + } + + continue; + } + + if (!\is_array($contextOptions)) { + $unsupportedOptions[] = \sprintf('stream_context.%s', $wrapper); + + continue; + } + + foreach ($contextOptions as $option => $_) { + if (\is_string($option) && isset($conflictingOptions[$wrapper]) && \array_key_exists($option, $conflictingOptions[$wrapper])) { + continue; + } + + if (!\is_string($option) || !\array_key_exists($option, $supportedOptions[$wrapper])) { + $unsupportedOptions[] = \sprintf('stream_context.%s.%s', $wrapper, (string) $option); + } + } + } + + return $unsupportedOptions; + } + + /** + * @return array> + */ + private static function supportedStreamContextOptions(): array + { + return [ + 'http' => [ + 'request_fulluri' => true, + ], + 'socket' => [ + 'bindto' => true, + 'tcp_nodelay' => true, + ], + 'ssl' => [ + 'SNI_enabled' => true, + 'capture_peer_cert' => true, + 'capture_peer_cert_chain' => true, + 'ciphers' => true, + 'disable_compression' => true, + 'no_ticket' => true, + 'peer_fingerprint' => true, + 'security_level' => true, + 'verify_depth' => true, + ], + ]; + } + + /** + * @return array> + */ + private static function conflictingStreamContextOptions(): array + { + return [ + 'http' => [ + 'content' => 'the request body', + 'follow_location' => 'the "allow_redirects" request option', + 'header' => 'the request headers', + 'max_redirects' => 'the "allow_redirects" request option', + 'method' => 'the request method', + 'protocol_version' => 'the request protocol version', + 'proxy' => 'the "proxy" request option', + 'timeout' => 'the "timeout" request option', + ], + 'ssl' => [ + 'allow_self_signed' => 'the "verify" request option', + 'cafile' => 'the "verify" request option', + 'capath' => 'the "verify" request option', + 'crypto_method' => 'the "crypto_method" request option', + 'local_cert' => 'the "cert" request option', + 'local_pk' => 'the "ssl_key" request option', + 'max_proto_version' => 'the "crypto_method_max" request option', + 'min_proto_version' => 'the "crypto_method" request option', + 'passphrase' => 'the "cert" or "ssl_key" request option', + 'peer_name' => 'the request URI', + 'verify_peer' => 'the "verify" request option', + 'verify_peer_name' => 'the "verify" request option', + ], + ]; + } + private function assertTransportSharingSupported(): void { if ($this->transportSharingMode === TransportSharing::HANDLER_REQUIRE) { @@ -530,7 +786,7 @@ class StreamHandler return false; } - $type = \strtolower($options['auth'][2]); + $type = Psr7\Utils::asciiToLower($options['auth'][2]); if ($type === 'digest') { $httpAuth = \defined('CURLAUTH_DIGEST') ? \constant('CURLAUTH_DIGEST') : null; } elseif ($type === 'ntlm') { @@ -545,13 +801,6 @@ class StreamHandler && $options['curl'][\CURLOPT_HTTPAUTH] === $httpAuth; } - private static function usesDigestAuth(array $options): bool - { - return isset($options['auth'][2]) - && \is_string($options['auth'][2]) - && \strtolower($options['auth'][2]) === 'digest'; - } - /** * @param mixed $value as passed via Request transfer options. * @@ -603,7 +852,7 @@ class StreamHandler throw new \InvalidArgumentException(\sprintf('%s must be a non-empty string', $option)); } - if (\strtoupper($value) !== 'PEM') { + if (Psr7\Utils::asciiToUpper($value) !== 'PEM') { throw new \InvalidArgumentException(\sprintf('The stream handler only supports "PEM" for the %s request option.', $option)); } } @@ -611,7 +860,7 @@ class StreamHandler /** * @param mixed $value as passed via Request transfer options. */ - private function add_proxy(RequestInterface $request, array &$options, $value, array &$params): void + private function add_proxy(RequestInterface $request, array &$options, $value, array &$params): bool { $uri = null; @@ -630,18 +879,40 @@ class StreamHandler } if (!$uri) { - return; + return false; } $parsed = $this->parse_proxy($uri); + + // PHP extracts and removes only one Proxy-Authorization line for a + // CONNECT tunnel. Serialize exactly one validated first-class value; + // more than one could leave a credential in the tunneled origin + // request. A first-class value, including an empty one, is + // authoritative over Basic credentials embedded in the proxy URI. + $managed = $request->getHeader('Proxy-Authorization'); + if (\count($managed) > 1) { + throw new \InvalidArgumentException('The stream handler supports exactly one Proxy-Authorization request header value when a proxy is selected.'); + } + if ($managed !== [] && \strpbrk($managed[0], "\r\n") !== false) { + throw new \InvalidArgumentException('Proxy-Authorization request header values must not contain a carriage return or line feed.'); + } + $options['http']['proxy'] = $parsed['proxy']; - if ($parsed['auth']) { - if (!isset($options['http']['header'])) { - $options['http']['header'] = []; - } - $options['http']['header'] .= "\r\nProxy-Authorization: {$parsed['auth']}"; + if (($managed !== [] || $parsed['auth']) && !isset($options['http']['header'])) { + $options['http']['header'] = ''; } + if ($managed !== []) { + $options['http']['header'] .= "\r\nProxy-Authorization: {$managed[0]}"; + + return true; + } elseif ($parsed['auth']) { + $options['http']['header'] .= "\r\nProxy-Authorization: {$parsed['auth']}"; + + return true; + } + + return false; } /** @@ -651,16 +922,29 @@ class StreamHandler { $parsed = \parse_url($url); - if ($parsed !== false && isset($parsed['scheme']) && $parsed['scheme'] === 'http') { - if (isset($parsed['host']) && isset($parsed['port'])) { - $auth = null; - if (isset($parsed['user']) && isset($parsed['pass'])) { - $auth = \base64_encode("{$parsed['user']}:{$parsed['pass']}"); - } + // parse_url() misreads scheme-less proxy authorities like + // "user:pass@host"; re-parse only those forms as HTTP. + $schemeLessAuthority = \strpos($url, '://') === false && \strncmp($url, '//', 2) !== 0; + if ($schemeLessAuthority) { + if (\is_array($parsed) && !isset($parsed['scheme']) && isset($parsed['host'], $parsed['port'])) { + $parsed['scheme'] = 'http'; + } elseif ( + (!\is_array($parsed) || !isset($parsed['host'])) + && (\strpos($url, '@') !== false || \strncmp($url, '[', 1) === 0) + ) { + $parsed = \parse_url('http://'.$url); + } + } + + if (\is_array($parsed) && isset($parsed['scheme']) && Psr7\Utils::caselessEquals($parsed['scheme'], 'http')) { + if (isset($parsed['host'], $parsed['port'])) { + $user = $parsed['user'] ?? ''; + $pass = $parsed['pass'] ?? ''; + $auth = ($user !== '' || $pass !== '') ? 'Basic '.\base64_encode("{$user}:{$pass}") : null; return [ 'proxy' => "tcp://{$parsed['host']}:{$parsed['port']}", - 'auth' => $auth ? "Basic {$auth}" : null, + 'auth' => $auth, ]; } } @@ -701,6 +985,26 @@ class StreamHandler throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); } + /** + * @param mixed $value as passed via Request transfer options. + */ + private function add_crypto_method_max(RequestInterface $request, array &$options, $value, array &$params): void + { + $options['ssl']['max_proto_version'] = TlsVersion::streamProtocolVersion('crypto_method_max', $value); + } + + private static function assertTlsVersionRangeForOptions(array $options): void + { + if (!isset($options['crypto_method_max'])) { + return; + } + + TlsVersion::assertRange( + $options['crypto_method'] ?? null, + $options['crypto_method_max'] + ); + } + /** * @param mixed $value as passed via Request transfer options. */ diff --git a/vendor/guzzlehttp/guzzle/src/MessageFormatter.php b/vendor/guzzlehttp/guzzle/src/MessageFormatter.php index 9b77eee..f59432a 100644 --- a/vendor/guzzlehttp/guzzle/src/MessageFormatter.php +++ b/vendor/guzzlehttp/guzzle/src/MessageFormatter.php @@ -72,8 +72,7 @@ class MessageFormatter implements MessageFormatterInterface { $cache = []; - /** @var string */ - return \preg_replace_callback( + $result = \preg_replace_callback( '/{\s*([A-Za-z_\-\.0-9]+)\s*}/', function (array $matches) use ($request, $response, $error, &$cache) { if (isset($cache[$matches[1]])) { @@ -90,7 +89,7 @@ class MessageFormatter implements MessageFormatterInterface break; case 'req_headers': $result = \trim($request->getMethod() - .' '.$request->getRequestTarget()) + .' '.$request->getRequestTarget(), " \n\r\t\0\x0B") .' HTTP/'.$request->getProtocolVersion()."\r\n" .$this->headers($request); break; @@ -182,6 +181,12 @@ class MessageFormatter implements MessageFormatterInterface }, $this->template ); + + if ($result === null) { + throw new \RuntimeException('Unable to format message: '.\preg_last_error_msg()); + } + + return $result; } /** @@ -194,6 +199,6 @@ class MessageFormatter implements MessageFormatterInterface $result .= $name.': '.\implode(', ', $values)."\r\n"; } - return \trim($result); + return \trim($result, " \n\r\t\0\x0B"); } } diff --git a/vendor/guzzlehttp/guzzle/src/Pool.php b/vendor/guzzlehttp/guzzle/src/Pool.php index 11add53..ef4aba8 100644 --- a/vendor/guzzlehttp/guzzle/src/Pool.php +++ b/vendor/guzzlehttp/guzzle/src/Pool.php @@ -71,7 +71,7 @@ class Pool implements PromisorInterface } elseif (\is_callable($rfn)) { yield $key => $rfn($opts); } else { - throw new \InvalidArgumentException('Each value yielded by the iterator must be a Psr7\Http\Message\RequestInterface or a callable that returns a promise that fulfills with a Psr7\Message\Http\ResponseInterface object.'); + throw new \InvalidArgumentException('Each value yielded by the iterator must be a Psr\Http\Message\RequestInterface or a callable that returns a promise that fulfills with a Psr\Http\Message\ResponseInterface object.'); } } }; diff --git a/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php b/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php index 0fe3e3c..66fb658 100644 --- a/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php +++ b/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php @@ -3,6 +3,7 @@ namespace GuzzleHttp; use GuzzleHttp\Exception\BadResponseException; +use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Exception\TooManyRedirectsException; use GuzzleHttp\Promise\PromiseInterface; use Psr\Http\Message\RequestInterface; @@ -104,6 +105,10 @@ class RedirectMiddleware ); } + // The caller's delay applies once, before the initial request, not + // before each followed redirect. + unset($options['delay']); + $promise = $this($nextRequest, $options); // Add headers to be able to track history of redirects. @@ -169,11 +174,13 @@ class RedirectMiddleware if ($statusCode == 303 || ($statusCode <= 302 && !$options['allow_redirects']['strict']) ) { - $safeMethods = ['GET', 'HEAD', 'OPTIONS']; $requestMethod = $request->getMethod(); - $modify['method'] = in_array($requestMethod, $safeMethods) ? $requestMethod : 'GET'; - $modify['body'] = ''; + if ($requestMethod !== 'QUERY' || !\in_array($statusCode, [301, 302], true)) { + $modify['method'] = \in_array($requestMethod, ['GET', 'HEAD', 'OPTIONS'], true) ? $requestMethod : 'GET'; + $modify['body'] = ''; + $modify['remove_headers'] = ['Content-Length', 'Transfer-Encoding']; + } } $uri = self::redirectUri($request, $response, $protocols); @@ -183,14 +190,27 @@ class RedirectMiddleware } $modify['uri'] = $uri; - Psr7\Message::rewindBody($request); + + // The body only needs to be rewound when the next request reuses it. + if (!isset($modify['body'])) { + try { + Psr7\Message::rewindBody($request); + } catch (\RuntimeException $e) { + throw new RequestException( + 'Redirect failed because the request body could not be rewound: '.$e->getMessage(), + $request, + $response, + $e + ); + } + } // Add the Referer header if it is told to do so and only // add the header if we are not redirecting from https to http. if ($options['allow_redirects']['referer'] && $modify['uri']->getScheme() === $request->getUri()->getScheme() ) { - $uri = $request->getUri()->withUserInfo(''); + $uri = $request->getUri()->withUserInfo('')->withFragment(''); $modify['set_headers']['Referer'] = (string) $uri; } else { $modify['remove_headers'][] = 'Referer'; diff --git a/vendor/guzzlehttp/guzzle/src/RequestOptions.php b/vendor/guzzlehttp/guzzle/src/RequestOptions.php index 5422d95..00b3ba5 100644 --- a/vendor/guzzlehttp/guzzle/src/RequestOptions.php +++ b/vendor/guzzlehttp/guzzle/src/RequestOptions.php @@ -5,7 +5,7 @@ namespace GuzzleHttp; /** * This class contains a list of built-in Guzzle request options. * - * @see https://github.com/guzzle/guzzle/blob/7.11/docs/request-options.md + * @see https://github.com/guzzle/guzzle/blob/7.15/docs/request-options.md */ final class RequestOptions { @@ -20,7 +20,9 @@ final class RequestOptions * - max: (int, default=5) maximum number of allowed redirects. * - strict: (bool, default=false) Set to true to use strict redirects * meaning redirect POST requests with POST requests vs. doing what most - * browsers do which is redirect POST requests with GET requests + * browsers do which is redirect POST requests with GET requests. The + * QUERY method keeps its method and body across non-strict 301 and 302 + * redirects, and a 303 redirect is followed with a body-less GET. * - referer: (bool, default=false) Set to true to enable the Referer * header. * - protocols: (non-empty-array, default=['http', 'https']) @@ -96,6 +98,23 @@ final class RequestOptions */ public const CRYPTO_METHOD = 'crypto_method'; + /** + * crypto_method_max: (int) A value describing the maximum TLS protocol + * version to use. + * + * This setting must be set to one of the + * ``STREAM_CRYPTO_METHOD_TLS*_CLIENT`` constants. On the stream handler, + * PHP 7.3 or higher is required to set a maximum TLS version, and PHP 7.4 + * or higher is required to use TLS 1.3. cURL 7.54.0 or higher is required + * in order to specify a maximum TLS version with the cURL handler. + */ + public const CRYPTO_METHOD_MAX = 'crypto_method_max'; + + /** + * curl: (array) Raw cURL options to apply when using a built-in cURL handler. + */ + public const CURL = 'curl'; + /** * debug: (bool|resource) Set to true or set to a PHP stream returned by * fopen() enable debug output with the HTTP handler used to send a @@ -183,6 +202,93 @@ final class RequestOptions */ public const MULTIPART = 'multipart'; + /** + * multiplex: (string) Controls how a request sent through a built-in + * cURL handler relates to shared, multiplexed connections: how an HTTP/2 + * request pursues one, or, with Multiplexing::NONE, whether the transfer + * may share its connection at all. When the option is not set, + * multiplexing is left to libcurl: nothing waits, and established + * multiplex-capable connections are still shared. Use + * Multiplexing::EAGER to explicitly never wait for pending connections, + * Multiplexing::WAIT to wait on libcurl-eligible pending connections with + * CURLOPT_PIPEWAIT, normally to the same origin, + * Multiplexing::REQUIRE_EAGER to fail unless a multiplexed protocol is + * guaranteed while dialing eagerly, or Multiplexing::REQUIRE_WAIT for the + * same guarantee while also waiting on pending connections. The required + * modes require a handler that permits actual multiplexing, not merely a + * multiplexed protocol, and are rejected on a Multiplexing::NONE handler. + * The stream handler ignores EAGER and WAIT, and rejects the required + * family; CurlHandler has no multi handle to multiplex over. Explicit + * modes reject deprecated raw cURL options they conflict with: the + * required family cannot be combined with a raw CURLOPT_HTTP_VERSION, + * CURLOPT_URL, or CURLOPT_FOLLOWLOCATION; no explicit mode can be + * combined with a raw CURLOPT_PIPEWAIT on the CurlMultiHandler; and + * Multiplexing::NONE on a CurlMultiHandler that permits multiplexing + * cannot be combined with the raw CURLOPT_HTTP_VERSION, CURLOPT_HTTPAUTH + * (including the "auth" request option's "digest" and "ntlm" modes, + * which set it), CURLOPT_PROXYAUTH, CURLOPT_FOLLOWLOCATION, + * CURLOPT_HTTPHEADER, CURLOPT_ALTSVC, CURLOPT_ALTSVC_CTRL, or + * CURLOPT_PROXYTYPE cURL options. The required family also + * rejects final CURLOPT_HTTPAUTH masks that permit NTLM, which libcurl + * retries over HTTP/1.1. The required family validates its cleartext + * proxy rule against the final cURL configuration, after raw options + * such as CURLOPT_PROXY and CURLOPT_PRE_PROXY are applied; only the + * exact raw CURLOPT_NOPROXY wildcard '*' disables the primary proxy and + * pre-proxy there, and raw host-specific patterns are conservatively + * treated as leaving them active. These rejections are + * configuration-conflict checks, not remote security checks. + * + * Multiplexing::NONE disables multiplexing for a whole handler when + * passed as the "multiplex" client configuration option, which + * configures the default handler and also becomes the default request + * option, or, when constructing a handler directly, as the + * CurlMultiHandler "multiplex" constructor option. A handler + * configured with Multiplexing::NONE rejects explicitly requested wait + * modes as a configuration conflict when the transfer would actually + * wait, and always rejects the required modes, because they require a + * handler that permits actual multiplexing, not merely a multiplexed + * protocol. As a request option value, Multiplexing::NONE guarantees the + * transfer does not share its connection with any concurrent transfer. + * Multiplexing::NONE does not force HTTP/1.1: on a Multiplexing::NONE + * handler, HTTP/2 still negotiates and each transfer keeps its + * connection to itself. + * + * The request option value is accepted exactly where the guarantee + * holds and can be verified: on a CurlMultiHandler configured with + * Multiplexing::NONE, for requests whose declared protocol version is + * HTTP/1.x, on CurlHandler, and on the stream handler, which never + * multiplexes. An HTTP/2 request with a Multiplexing::NONE request + * option is rejected on a CurlMultiHandler that permits multiplexing. + * On a CurlMultiHandler that permits multiplexing, Multiplexing::NONE + * is also rejected with a custom "handle_factory", alongside a raw + * CURLMOPT_PIPELINING cURL multi option, and combined with the raw + * CURLOPT_HTTP_VERSION, CURLOPT_HTTPAUTH (including the "auth" request + * option's "digest" and "ntlm" modes, which set it), CURLOPT_PROXYAUTH, + * CURLOPT_FOLLOWLOCATION, CURLOPT_HTTPHEADER, CURLOPT_ALTSVC, + * CURLOPT_ALTSVC_CTRL, or CURLOPT_PROXYTYPE cURL options. It is also + * rejected when the request carries an Expect: 100-continue header (its + * 417 retries select connections outside the safeguards; remove an + * explicitly supplied header, or set the "expect" request option to + * false to prevent it being added automatically). + * + * On a client whose multi handler permits multiplexing, the ordinary + * non-streaming default stack - both cURL handlers available and no + * connection caps forcing multi-only routing - runs synchronous + * requests on the CurlHandler path, which satisfies the guarantee for + * any protocol version, while asynchronous requests run on the + * CurlMultiHandler, so an HTTP/2 request with Multiplexing::NONE + * succeeds synchronously and is rejected asynchronously on the same + * client. Keep-alive reuse between consecutive transfers is + * unaffected, except on libcurl versions below 7.77.0 and from 8.11.0 + * through 8.12.1, where an accepted HTTP/1.x request on a multiplexing + * CurlMultiHandler forces a fresh connection. Custom handlers receive + * the "multiplex" option unchanged: its semantics are handler-defined, + * Guzzle does not guarantee it is honored, and a client-level + * Multiplexing::NONE with a custom handler flows to it as a default + * request option without client-side enforcement. + */ + public const MULTIPLEX = 'multiplex'; + /** * on_headers: (callable) A callable that is invoked when the HTTP headers * of the response have been received but the body has not yet begun to @@ -201,6 +307,17 @@ final class RequestOptions */ public const ON_STATS = 'on_stats'; + /** + * on_trailers: (callable) A callable that is invoked by the built-in cURL + * handlers once per successful transfer, after the response body has been + * received, with an associative array of the parsed HTTP trailers followed + * by the response. Trailer field names are lowercased and grouped + * case-insensitively; values keep their wire order. Malformed trailer + * field lines are discarded before parsing. Trailer fields are reported + * separately from response headers and are never merged into the response. + */ + public const ON_TRAILERS = 'on_trailers'; + /** * progress: (callable) Defines a function to invoke when transfer * progress is made. The function accepts the following positional @@ -270,6 +387,12 @@ final class RequestOptions */ public const STREAM = 'stream'; + /** + * stream_context: (array) PHP stream context options to merge into the + * context used by the built-in stream handler. + */ + public const STREAM_CONTEXT = 'stream_context'; + /** * verify: (bool|string, default=true) Describes the SSL certificate * verification behavior of a request. Set to true to enable SSL @@ -292,6 +415,11 @@ final class RequestOptions */ public const READ_TIMEOUT = 'read_timeout'; + /** + * retries: (int) Current retry count used by the retry middleware. + */ + public const RETRIES = 'retries'; + /** * version: (string|int|float) Specifies the HTTP protocol version to attempt * to use. diff --git a/vendor/guzzlehttp/guzzle/src/Utils.php b/vendor/guzzlehttp/guzzle/src/Utils.php index ce68694..9355c84 100644 --- a/vendor/guzzlehttp/guzzle/src/Utils.php +++ b/vendor/guzzlehttp/guzzle/src/Utils.php @@ -6,6 +6,7 @@ use GuzzleHttp\Exception\InvalidArgumentException; use GuzzleHttp\Handler\CurlHandler; use GuzzleHttp\Handler\CurlMultiHandler; use GuzzleHttp\Handler\CurlShareHandleState; +use GuzzleHttp\Handler\CurlVersion; use GuzzleHttp\Handler\Proxy; use GuzzleHttp\Handler\StreamHandler; use Psr\Http\Message\RequestInterface; @@ -20,9 +21,18 @@ final class Utils * * @return string Returns a string containing the type of the variable and * if a class is provided, the class name. + * + * @deprecated Utils::describeType() will be removed in guzzlehttp/guzzle:8.0. Use get_debug_type() instead. */ public static function describeType($input): string { + \trigger_deprecation( + 'guzzlehttp/guzzle', + '7.12', + '%s() is deprecated and will be removed in 8.0. Use get_debug_type() instead.', + __METHOD__ + ); + switch (\gettype($input)) { case 'object': return 'object('.\get_class($input).')'; @@ -35,7 +45,7 @@ final class Utils /** @var string $varDumpContent */ $varDumpContent = \ob_get_clean(); - return \str_replace('double(', 'float(', \rtrim($varDumpContent)); + return \str_replace('double(', 'float(', \rtrim($varDumpContent, " \n\r\t\0\x0B")); } } @@ -51,7 +61,7 @@ final class Utils foreach ($lines as $line) { $parts = \explode(':', $line, 2); - $headers[\trim($parts[0])][] = isset($parts[1]) ? \trim($parts[1]) : null; + $headers[\trim($parts[0], " \n\r\t\0\x0B")][] = isset($parts[1]) ? \trim($parts[1], " \n\r\t\0\x0B") : null; } return $headers; @@ -81,7 +91,7 @@ final class Utils * * The returned handler is not wrapped by any default middlewares. * - * @param array{transport_sharing?: mixed} $handlerOptions Handler constructor options. + * @param array{transport_sharing?: mixed, max_host_connections?: mixed, max_total_connections?: mixed, multiplex?: mixed} $handlerOptions Handler constructor options. * * @return callable(RequestInterface, array): Promise\PromiseInterface Returns the best handler for the given system. * @@ -89,48 +99,148 @@ final class Utils */ public static function chooseHandler(array $handlerOptions = []): callable { - $handler = null; $sharingMode = CurlShareHandleState::normalizeMode($handlerOptions['transport_sharing'] ?? null, 'transport_sharing'); - $sharingRequested = $sharingMode !== TransportSharing::NONE; - $sharingRequired = $sharingMode === TransportSharing::HANDLER_REQUIRE; - $curlHandlerOptions = []; - $curlSupported = \defined('CURLOPT_CUSTOMREQUEST') - && \function_exists('curl_version') - && version_compare(curl_version()['version'], '7.21.2') >= 0 - && (\function_exists('curl_multi_exec') || \function_exists('curl_exec')); + $sharingRequired = self::isTransportSharingRequired($sharingMode); + $connectionCapsRequired = self::hasConnectionCapOptions($handlerOptions); + $handler = self::createCurlHandler($sharingMode, $handlerOptions); - if ($sharingRequired && !$curlSupported) { + if ($sharingRequired && $handler === null) { throw new \RuntimeException('Required transport sharing requires the PHP cURL extension, curl_exec() or curl_multi_exec(), and libcurl 7.21.2 or higher.'); } - if ($curlSupported) { - if ($sharingRequested) { - $shareState = CurlShareHandleState::fromOption($sharingMode); - if ($shareState !== null) { - $curlHandlerOptions['transport_sharing'] = $shareState; - } - } - - if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) { - $handler = Proxy::wrapSync(new CurlMultiHandler($curlHandlerOptions), new CurlHandler($curlHandlerOptions)); - } elseif (\function_exists('curl_exec')) { - $handler = new CurlHandler($curlHandlerOptions); - } elseif (\function_exists('curl_multi_exec')) { - $handler = new CurlMultiHandler($curlHandlerOptions); - } - } - if (\ini_get('allow_url_fopen')) { - $streamHandler = new StreamHandler(['transport_sharing' => $sharingMode]); - - $handler = $handler - ? Proxy::wrapStreaming($handler, $streamHandler) - : $streamHandler; - } elseif (!$handler) { - throw new \RuntimeException('GuzzleHttp requires cURL, the allow_url_fopen ini setting, or a custom HTTP handler.'); + return self::addStreamHandler($handler, $sharingMode, $sharingRequired, self::connectionCapOptions($handlerOptions)); } - return $handler; + if ($handler !== null) { + return $handler; + } + + if ($connectionCapsRequired) { + throw new \RuntimeException('Connection cap options require a cap-capable cURL multi handler or the allow_url_fopen ini setting for stream fallback.'); + } + + throw new \RuntimeException('GuzzleHttp requires cURL, the allow_url_fopen ini setting, or a custom HTTP handler.'); + } + + private static function isTransportSharingRequired(string $sharingMode): bool + { + return $sharingMode === TransportSharing::HANDLER_REQUIRE; + } + + /** + * @param array{max_host_connections?: mixed, max_total_connections?: mixed} $handlerOptions + */ + private static function hasConnectionCapOptions(array $handlerOptions): bool + { + return self::connectionCapOptions($handlerOptions) !== []; + } + + /** + * @param array{max_host_connections?: mixed, max_total_connections?: mixed, multiplex?: mixed} $handlerOptions + * + * @return (callable(RequestInterface, array): Promise\PromiseInterface)|null + */ + private static function createCurlHandler(string $sharingMode, array $handlerOptions): ?callable + { + if (!\defined('CURLOPT_CUSTOMREQUEST') || !CurlVersion::supportsCurlHandler()) { + return null; + } + + $connectionCapOptions = self::connectionCapOptions($handlerOptions); + if ($connectionCapOptions !== [] && (!CurlVersion::supportsConnectionCaps() || !\function_exists('curl_multi_exec'))) { + return null; + } + + $curlHandlerOptions = self::createCurlHandlerOptions($sharingMode); + $curlMultiHandlerOptions = $curlHandlerOptions + $connectionCapOptions; + if (($handlerOptions['multiplex'] ?? null) === Multiplexing::NONE) { + // Forwarded to the CurlMultiHandler only: CurlHandler and + // StreamHandler validate known options, and both satisfy NONE + // per-request without a handler option. + $curlMultiHandlerOptions['multiplex'] = Multiplexing::NONE; + } + + if (\function_exists('curl_multi_exec') && \function_exists('curl_exec')) { + $multiHandler = new CurlMultiHandler($curlMultiHandlerOptions); + + if ($connectionCapOptions !== []) { + // Connection caps only govern transfers on the multi handle, so + // the synchronous CurlHandler fast path would escape them. + return $multiHandler; + } + + return Proxy::wrapSync($multiHandler, new CurlHandler($curlHandlerOptions)); + } + + if ($connectionCapOptions === [] && \function_exists('curl_exec')) { + return new CurlHandler($curlHandlerOptions); + } + + if (\function_exists('curl_multi_exec')) { + return new CurlMultiHandler($curlMultiHandlerOptions); + } + + return null; + } + + /** + * @return array + */ + private static function createCurlHandlerOptions(string $sharingMode): array + { + if ($sharingMode === TransportSharing::NONE) { + return []; + } + + $shareState = CurlShareHandleState::fromOption($sharingMode); + + return $shareState === null ? [] : ['transport_sharing' => $shareState]; + } + + /** + * @param array{max_host_connections?: mixed, max_total_connections?: mixed} $handlerOptions + * + * @return array{max_host_connections?: int, max_total_connections?: int} + */ + private static function connectionCapOptions(array $handlerOptions): array + { + $options = []; + foreach (['max_host_connections', 'max_total_connections'] as $capOption) { + $value = $handlerOptions[$capOption] ?? null; + if ($value === null) { + continue; + } + + if (!\is_int($value) || $value < 1) { + throw new InvalidArgumentException(\sprintf('%s must be a positive integer.', $capOption)); + } + + $options[$capOption] = $value; + } + + return $options; + } + + /** + * @param (callable(RequestInterface, array): Promise\PromiseInterface)|null $handler + * @param array{max_host_connections?: int, max_total_connections?: int} $connectionCapOptions + * + * @return callable(RequestInterface, array): Promise\PromiseInterface + */ + private static function addStreamHandler(?callable $handler, string $sharingMode, bool $sharingRequired, array $connectionCapOptions): callable + { + $streamHandler = new StreamHandler(['transport_sharing' => $sharingMode] + $connectionCapOptions); + + if ($handler === null) { + return $streamHandler; + } + + if (!$sharingRequired) { + $handler = Proxy::wrapTlsFallback($handler, $streamHandler); + } + + return Proxy::wrapStreaming($handler, $streamHandler); } /** @@ -158,6 +268,8 @@ final class Utils */ public static function defaultCaBundle(): string { + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. This method is not needed in PHP 5.6+.', __METHOD__); + static $cached = null; static $cafiles = [ // Red Hat, CentOS, Fedora (provided by the ca-certificates package) @@ -200,14 +312,14 @@ final class Utils No system CA bundle could be found in any of the the common system locations. PHP versions earlier than 5.6 are not properly configured to use the system's CA bundle by default. In order to verify peer certificates, you will need to -supply the path on disk to a certificate bundle to the 'verify' request -option: https://github.com/guzzle/guzzle/blob/7.11/docs/request-options.md#verify. If +supply the path on disk to a certificate bundle to the 'verify' request option: +https://github.com/guzzle/guzzle/blob/7.15/docs/request-options.md#verify. If you do not need a specific certificate bundle, then Mozilla provides a commonly used CA bundle which can be downloaded here (provided by the maintainer of -cURL): https://curl.haxx.se/ca/cacert.pem. Once you have a CA bundle available -on disk, you can set the 'openssl.cafile' PHP ini setting to point to the path -to the file, allowing you to omit the 'verify' request option. See -https://curl.haxx.se/docs/sslcerts.html for more information. +cURL): https://curl.se/ca/cacert.pem. Once you have a CA bundle available on +disk, you can set the 'openssl.cafile' PHP ini setting to point to the path to +the file, allowing you to omit the 'verify' request option. See +https://curl.se/docs/sslcerts.html for more information. EOT ); } @@ -220,7 +332,7 @@ EOT { $result = []; foreach (\array_keys($headers) as $key) { - $result[\strtolower((string) $key)] = $key; + $result[Psr7\Utils::asciiToLower((string) $key)] = $key; } return $result; @@ -259,19 +371,22 @@ EOT /** * Returns true if the provided host matches any of the no proxy areas. * - * This method will strip a port from the host if it is present. Each pattern - * can be matched with an exact match (e.g., "foo.com" == "foo.com") or a - * partial match: (e.g., "foo.com" == "baz.foo.com" and ".foo.com" == - * "baz.foo.com", but ".foo.com" != "foo.com"). + * This method will strip a port from the host if it is present. Domain + * patterns are matched case-insensitively. Exact IP literal patterns are + * matched by their normalized binary address. * * Areas are matched in the following cases: * 1. "*" (without quotes) always matches any hosts. - * 2. An exact match. - * 3. The area starts with "." and the area is the last part of the host. e.g. + * 2. An exact domain or IP literal match. + * 3. A bare domain matches itself and its subdomains. e.g. 'mit.edu' will + * match 'mit.edu' and 'foo.mit.edu'. + * 4. The area starts with "." and the area is the last part of the host. e.g. * '.mit.edu' will match any host that ends with '.mit.edu'. + * 5. IP CIDR entries match IP literal hosts. e.g. '192.168.0.0/16' will + * match '192.168.1.10' and 'fd00::/8' will match '[fd00::1]'. * * @param string $host Host to check against the patterns. - * @param string[] $noProxyArray An array of host patterns. + * @param string[] $noProxyArray An array of host or CIDR patterns. * * @throws InvalidArgumentException */ @@ -281,43 +396,23 @@ EOT throw new InvalidArgumentException('Empty host provided'); } - $host = self::normalizeNoProxyHost($host, true); - - foreach ($noProxyArray as $area) { - // Always match on wildcards. - if ($area === '*') { - return true; - } - - if ($area === '') { - continue; - } - - $area = self::normalizeNoProxyHost($area, false); - - if ($area === $host) { - // Exact matches. - return true; - } - // Special match if the area when prefixed with ".". Remove any - // existing leading "." and add a new leading ".". - $area = '.'.\ltrim($area, '.'); - if ( - \strpos($host, ':') === false - && \strpos($area, ':') === false - && \substr($host, -\strlen($area)) === $area - ) { - return true; - } + $target = self::parseNoProxyHostString($host); + if ($target === null) { + return false; } - return false; + return self::matchesNoProxyList($target, $noProxyArray); } /** * Returns true if the provided URI matches any of the no proxy areas. * - * @param mixed $noProxy No-proxy host patterns. + * Matching follows the same rules as isHostInNoProxy(), with the + * addition that areas may carry a port (e.g. "example.com:8080" or + * "[::1]:8080") which is compared against the URI port (or the scheme + * default port when the URI has none). + * + * @param mixed $noProxy No-proxy host, host-and-port, or CIDR patterns. * * @internal */ @@ -331,38 +426,34 @@ EOT return false; } - $host = $uri->getHost(); - if ($host === '') { + $target = self::parseNoProxyTarget($uri); + if ($target === null) { return false; } - $port = $uri->getPort(); - if ($port === null) { - $port = self::getDefaultPort($uri->getScheme()); - } + return self::matchesNoProxyList($target, $noProxy); + } + /** + * @param array{type: string, value: string, port: int|null, matchesRoot: bool} $target + * @param array $noProxy + */ + private static function matchesNoProxyList(array $target, array $noProxy): bool + { foreach ($noProxy as $area) { if (!\is_string($area)) { continue; } - $area = \trim($area); + $area = \trim($area, " \n\r\t\0\x0B"); // Always match on wildcards. if ($area === '*') { return true; } - if ($area === '') { - continue; - } - - [$area, $areaPort] = self::splitNoProxyHostAndPort($area); - if ($areaPort !== null && $areaPort !== $port) { - continue; - } - - if (self::isHostInNoProxy($host, [$area])) { + $rule = self::parseNoProxyRule($area); + if ($rule !== null && self::noProxyRuleMatches($target, $rule)) { return true; } } @@ -370,58 +461,157 @@ EOT return false; } - private static function normalizeNoProxyHost(string $host, bool $stripPort): string + /** + * @return array{type: string, value: string, port: int|null, matchesRoot: bool}|null + */ + private static function parseNoProxyTarget(UriInterface $uri): ?array { - if ($host !== '' && $host[0] === '[') { - $closingBracket = \strpos($host, ']'); - - if ($closingBracket !== false) { - $address = \substr($host, 1, $closingBracket - 1); - $tail = \substr($host, $closingBracket + 1); - - if ( - ($tail === '' || ($stripPort && \preg_match('/^:\d+$/', $tail))) - && \filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6) - ) { - return \strtolower($address); - } - } + $host = $uri->getHost(); + if ($host === '') { + return null; } - if (\filter_var($host, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { - return \strtolower($host); - } - - if ($stripPort) { - [$host] = \explode(':', $host, 2); - } - - return $host; + return self::parseNoProxyHost($host, $uri->getPort() ?? self::getDefaultPort($uri->getScheme()), true); } /** - * @return array{0: string, 1: int|null} + * @return array{type: string, value: string, port: int|null, matchesRoot: bool}|null */ - private static function splitNoProxyHostAndPort(string $area): array + private static function parseNoProxyHostString(string $host): ?array + { + $hostAndPort = self::splitNoProxyHostAndPort($host); + if ($hostAndPort === null) { + return null; + } + + [$host] = $hostAndPort; + + return self::parseNoProxyHost($host, null, true); + } + + /** + * @return array{type: string, value: string, port: int|null, matchesRoot: bool}|array{type: string, value: string, prefix: int}|null + */ + private static function parseNoProxyRule(string $area): ?array + { + $area = \trim($area, " \n\r\t\0\x0B"); + if ($area === '' || $area === '*') { + return null; + } + + if (\strpos($area, '/') !== false) { + return self::parseNoProxyCidrRule($area); + } + + $matchesRoot = true; + if ($area[0] === '.') { + $matchesRoot = false; + $area = \substr($area, 1); + } + + $hostAndPort = self::splitNoProxyHostAndPort($area); + if ($hostAndPort === null) { + return null; + } + + [$host, $port] = $hostAndPort; + + if ($host === '*') { + if (!$matchesRoot) { + return null; + } + + return [ + 'type' => 'wildcard', + 'value' => '*', + 'port' => $port, + 'matchesRoot' => true, + ]; + } + + $rule = self::parseNoProxyHost($host, $port, $matchesRoot); + if ($rule !== null && !$matchesRoot && $rule['type'] === 'ip') { + return null; + } + + return $rule; + } + + /** + * @return array{type: string, value: string, port: int|null, matchesRoot: bool}|null + */ + private static function parseNoProxyHost(string $host, ?int $port, bool $matchesRoot): ?array + { + if ($host !== '' && $host[0] === '[') { + if (\substr($host, -1) !== ']') { + return null; + } + + $address = \substr($host, 1, -1); + if (!\filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { + return null; + } + + $host = $address; + } + + $packedIp = self::packIpAddress($host); + if ($packedIp !== false) { + return [ + 'type' => 'ip', + 'value' => $packedIp, + 'port' => $port, + 'matchesRoot' => $matchesRoot, + ]; + } + + if ($host === '' || \strpos($host, ':') !== false) { + return null; + } + + // Normalize a single DNS root dot for no-proxy domain matching. + if (\substr($host, -1) === '.') { + $host = \substr($host, 0, -1); + if ($host === '') { + return null; + } + } + + return [ + 'type' => 'domain', + 'value' => Psr7\Utils::asciiToLower($host), + 'port' => $port, + 'matchesRoot' => $matchesRoot, + ]; + } + + /** + * @return array{0: string, 1: int|null}|null + */ + private static function splitNoProxyHostAndPort(string $area): ?array { if ($area !== '' && $area[0] === '[') { $closingBracket = \strpos($area, ']'); - - if ($closingBracket !== false) { - $tail = \substr($area, $closingBracket + 1); - if ($tail !== '' && $tail[0] === ':') { - $port = self::parseNoProxyPort(\substr($tail, 1)); - - if ($port !== null) { - return [\substr($area, 0, $closingBracket + 1), $port]; - } - } + if ($closingBracket === false) { + return null; } - return [$area, null]; + $host = \substr($area, 0, $closingBracket + 1); + $tail = \substr($area, $closingBracket + 1); + if ($tail === '') { + return [$host, null]; + } + + if ($tail[0] !== ':') { + return null; + } + + $port = self::parseNoProxyPort(\substr($tail, 1)); + + return $port === null ? null : [$host, $port]; } - if (\filter_var($area, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6)) { + if (self::packIpAddress($area) !== false) { return [$area, null]; } @@ -432,7 +622,7 @@ EOT $port = self::parseNoProxyPort(\substr($area, $colon + 1)); if ($port === null) { - return [$area, null]; + return null; } return [\substr($area, 0, $colon), $port]; @@ -440,13 +630,131 @@ EOT private static function parseNoProxyPort(string $port): ?int { - if ($port === '' || !\ctype_digit($port)) { + return self::parseBoundedUnsignedInteger($port, 65535); + } + + /** + * @return array{type: string, value: string, prefix: int}|null + */ + private static function parseNoProxyCidrRule(string $area): ?array + { + $slash = \strpos($area, '/'); + if ($slash === false) { return null; } - $port = (int) $port; + $prefix = \substr($area, $slash + 1); - return $port <= 65535 ? $port : null; + $network = \substr($area, 0, $slash); + if ($network !== '' && $network[0] === '[' && \substr($network, -1) === ']') { + $network = \substr($network, 1, -1); + } + + $network = self::packIpAddress($network); + if ($network === false) { + return null; + } + + $prefix = self::parseBoundedUnsignedInteger($prefix, \strlen($network) * 8); + if ($prefix === null) { + return null; + } + + return [ + 'type' => 'cidr', + 'value' => $network, + 'prefix' => $prefix, + ]; + } + + private static function parseBoundedUnsignedInteger(string $value, int $max): ?int + { + if ($value === '' || !\ctype_digit($value)) { + return null; + } + + $normalized = \ltrim($value, '0'); + $normalized = $normalized === '' ? '0' : $normalized; + $limit = (string) $max; + + if (\strlen($normalized) > \strlen($limit) || (\strlen($normalized) === \strlen($limit) && \strcmp($normalized, $limit) > 0)) { + return null; + } + + return (int) $normalized; + } + + /** + * @param array{type: string, value: string, port: int|null, matchesRoot: bool} $target + * @param array{type: string, value: string, port?: int|null, matchesRoot?: bool, prefix?: int|null} $rule + */ + private static function noProxyRuleMatches(array $target, array $rule): bool + { + if ($rule['type'] === 'wildcard') { + return ($rule['port'] ?? null) === null || $rule['port'] === $target['port']; + } + + if ($rule['type'] === 'cidr') { + if ($target['type'] !== 'ip' || !isset($rule['prefix'])) { + return false; + } + + if (\strlen($target['value']) !== \strlen($rule['value'])) { + return false; + } + + return self::ipMatchesPrefix($target['value'], $rule['value'], $rule['prefix']); + } + + if (($rule['port'] ?? null) !== null && $rule['port'] !== $target['port']) { + return false; + } + + if ($rule['type'] !== $target['type']) { + return false; + } + + if ($rule['type'] === 'ip') { + return $rule['value'] === $target['value']; + } + + if (($rule['matchesRoot'] ?? false) && $target['value'] === $rule['value']) { + return true; + } + + $suffix = '.'.$rule['value']; + + return \substr($target['value'], -\strlen($suffix)) === $suffix; + } + + /** + * @return string|false + */ + private static function packIpAddress(string $ip) + { + if (!\filter_var($ip, \FILTER_VALIDATE_IP)) { + return false; + } + + return \inet_pton($ip); + } + + private static function ipMatchesPrefix(string $address, string $network, int $prefix): bool + { + $fullBytes = \intdiv($prefix, 8); + $remainingBits = $prefix % 8; + + if ($fullBytes > 0 && \substr($address, 0, $fullBytes) !== \substr($network, 0, $fullBytes)) { + return false; + } + + if ($remainingBits === 0) { + return true; + } + + $mask = (0xFF << (8 - $remainingBits)) & 0xFF; + + return (\ord($address[$fullBytes]) & $mask) === (\ord($network[$fullBytes]) & $mask); } private static function getDefaultPort(string $scheme): ?int @@ -476,9 +784,12 @@ EOT * @throws InvalidArgumentException if the JSON cannot be decoded. * * @see https://www.php.net/manual/en/function.json-decode.php + * @deprecated Utils::jsonDecode() will be removed in guzzlehttp/guzzle:8.0. Use PHP's json_decode() instead. */ public static function jsonDecode(string $json, bool $assoc = false, int $depth = 512, int $options = 0) { + \trigger_deprecation('guzzlehttp/guzzle', '7.15', '%s() is deprecated and will be removed in 8.0. Use PHP\'s json_decode() instead.', __METHOD__); + if ($depth < 1) { throw new InvalidArgumentException('json_decode error: Maximum stack depth exceeded'); } @@ -501,9 +812,12 @@ EOT * @throws InvalidArgumentException if the JSON cannot be encoded. * * @see https://www.php.net/manual/en/function.json-encode.php + * @deprecated Utils::jsonEncode() will be removed in guzzlehttp/guzzle:8.0. Use PHP's json_encode() instead. */ public static function jsonEncode($value, int $options = 0, int $depth = 512): string { + \trigger_deprecation('guzzlehttp/guzzle', '7.15', '%s() is deprecated and will be removed in 8.0. Use PHP\'s json_encode() instead.', __METHOD__); + $json = \json_encode($value, $options, $depth); if (\JSON_ERROR_NONE !== \json_last_error()) { throw new InvalidArgumentException('json_encode error: '.\json_last_error_msg()); @@ -550,7 +864,7 @@ EOT 'guzzlehttp/guzzle', '7.11', 'Passing %s as the "idn_conversion" request option is deprecated; guzzlehttp/guzzle 8.0 will reject values that are not true, false, null, or an integer IDNA_* bitmask.', - self::describeType($value) + \get_debug_type($value) ); return (int) $value; diff --git a/vendor/guzzlehttp/guzzle/src/functions.php b/vendor/guzzlehttp/guzzle/src/functions.php index 9ab4b96..7c73949 100644 --- a/vendor/guzzlehttp/guzzle/src/functions.php +++ b/vendor/guzzlehttp/guzzle/src/functions.php @@ -11,11 +11,26 @@ namespace GuzzleHttp; * @return string Returns a string containing the type of the variable and * if a class is provided, the class name. * - * @deprecated describe_type will be removed in guzzlehttp/guzzle:8.0. Use Utils::describeType instead. + * @deprecated describe_type will be removed in guzzlehttp/guzzle:8.0. Use get_debug_type() instead. */ function describe_type($input): string { - return Utils::describeType($input); + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use get_debug_type() instead.', __FUNCTION__); + + switch (\gettype($input)) { + case 'object': + return 'object('.\get_class($input).')'; + case 'array': + return 'array('.\count($input).')'; + default: + \ob_start(); + \var_dump($input); + // normalize float vs double + /** @var string $varDumpContent */ + $varDumpContent = \ob_get_clean(); + + return \str_replace('double(', 'float(', \rtrim($varDumpContent, " \n\r\t\0\x0B")); + } } /** @@ -28,6 +43,8 @@ function describe_type($input): string */ function headers_from_lines(iterable $lines): array { + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::headersFromLines() instead.', __FUNCTION__); + return Utils::headersFromLines($lines); } @@ -42,6 +59,8 @@ function headers_from_lines(iterable $lines): array */ function debug_resource($value = null) { + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::debugResource() instead.', __FUNCTION__); + return Utils::debugResource($value); } @@ -58,6 +77,8 @@ function debug_resource($value = null) */ function choose_handler(): callable { + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::chooseHandler() instead.', __FUNCTION__); + return Utils::chooseHandler(); } @@ -68,6 +89,8 @@ function choose_handler(): callable */ function default_user_agent(): string { + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::defaultUserAgent() instead.', __FUNCTION__); + return Utils::defaultUserAgent(); } @@ -88,7 +111,60 @@ function default_user_agent(): string */ function default_ca_bundle(): string { - return Utils::defaultCaBundle(); + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. This function is not needed in PHP 5.6+.', __FUNCTION__); + + static $cached = null; + static $cafiles = [ + // Red Hat, CentOS, Fedora (provided by the ca-certificates package) + '/etc/pki/tls/certs/ca-bundle.crt', + // Ubuntu, Debian (provided by the ca-certificates package) + '/etc/ssl/certs/ca-certificates.crt', + // FreeBSD (provided by the ca_root_nss package) + '/usr/local/share/certs/ca-root-nss.crt', + // SLES 12 (provided by the ca-certificates package) + '/var/lib/ca-certificates/ca-bundle.pem', + // OS X provided by homebrew (using the default path) + '/usr/local/etc/openssl/cert.pem', + // Google app engine + '/etc/ca-certificates.crt', + // Windows? + 'C:\\windows\\system32\\curl-ca-bundle.crt', + 'C:\\windows\\curl-ca-bundle.crt', + ]; + + if ($cached) { + return $cached; + } + + if ($ca = \ini_get('openssl.cafile')) { + return $cached = $ca; + } + + if ($ca = \ini_get('curl.cainfo')) { + return $cached = $ca; + } + + foreach ($cafiles as $filename) { + if (\file_exists($filename)) { + return $cached = $filename; + } + } + + throw new \RuntimeException( + <<< EOT +No system CA bundle could be found in any of the the common system locations. +PHP versions earlier than 5.6 are not properly configured to use the system's +CA bundle by default. In order to verify peer certificates, you will need to +supply the path on disk to a certificate bundle to the 'verify' request option: +https://github.com/guzzle/guzzle/blob/7.15/docs/request-options.md#verify. If +you do not need a specific certificate bundle, then Mozilla provides a commonly +used CA bundle which can be downloaded here (provided by the maintainer of +cURL): https://curl.se/ca/cacert.pem. Once you have a CA bundle available on +disk, you can set the 'openssl.cafile' PHP ini setting to point to the path to +the file, allowing you to omit the 'verify' request option. See +https://curl.se/docs/sslcerts.html for more information. +EOT + ); } /** @@ -99,6 +175,8 @@ function default_ca_bundle(): string */ function normalize_header_keys(array $headers): array { + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::normalizeHeaderKeys() instead.', __FUNCTION__); + return Utils::normalizeHeaderKeys($headers); } @@ -125,6 +203,8 @@ function normalize_header_keys(array $headers): array */ function is_host_in_noproxy(string $host, array $noProxyArray): bool { + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use Utils::isHostInNoProxy() instead.', __FUNCTION__); + return Utils::isHostInNoProxy($host, $noProxyArray); } @@ -142,11 +222,23 @@ function is_host_in_noproxy(string $host, array $noProxyArray): bool * @throws Exception\InvalidArgumentException if the JSON cannot be decoded. * * @see https://www.php.net/manual/en/function.json-decode.php - * @deprecated json_decode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonDecode instead. + * @deprecated json_decode will be removed in guzzlehttp/guzzle:8.0. Use PHP's json_decode() instead. */ function json_decode(string $json, bool $assoc = false, int $depth = 512, int $options = 0) { - return Utils::jsonDecode($json, $assoc, $depth, $options); + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use PHP\'s json_decode() instead.', __FUNCTION__); + + if ($depth < 1) { + throw new Exception\InvalidArgumentException('json_decode error: Maximum stack depth exceeded'); + } + + $data = \json_decode($json, $assoc, $depth, $options); + if (\JSON_ERROR_NONE !== \json_last_error()) { + throw new Exception\InvalidArgumentException('json_decode error: '.\json_last_error_msg()); + } + + /** @var object|array|string|int|float|bool|null $data */ + return $data; } /** @@ -159,9 +251,18 @@ function json_decode(string $json, bool $assoc = false, int $depth = 512, int $o * @throws Exception\InvalidArgumentException if the JSON cannot be encoded. * * @see https://www.php.net/manual/en/function.json-encode.php - * @deprecated json_encode will be removed in guzzlehttp/guzzle:8.0. Use Utils::jsonEncode instead. + * @deprecated json_encode will be removed in guzzlehttp/guzzle:8.0. Use PHP's json_encode() instead. */ function json_encode($value, int $options = 0, int $depth = 512): string { - return Utils::jsonEncode($value, $options, $depth); + \trigger_deprecation('guzzlehttp/guzzle', '7.1', '%s() is deprecated and will be removed in 8.0. Use PHP\'s json_encode() instead.', __FUNCTION__); + + /** @var positive-int $depth */ + $json = \json_encode($value, $options, $depth); + if (\JSON_ERROR_NONE !== \json_last_error()) { + throw new Exception\InvalidArgumentException('json_encode error: '.\json_last_error_msg()); + } + + /** @var non-empty-string $json */ + return $json; } diff --git a/vendor/guzzlehttp/promises/CHANGELOG.md b/vendor/guzzlehttp/promises/CHANGELOG.md index 72ddd86..e9df8b8 100644 --- a/vendor/guzzlehttp/promises/CHANGELOG.md +++ b/vendor/guzzlehttp/promises/CHANGELOG.md @@ -1,6 +1,13 @@ # CHANGELOG +## 2.5.1 - 2026-07-08 + +### Fixed + +- Fixed recursive `Utils::all()` rejecting generator inputs + + ## 2.5.0 - 2026-06-02 ### Deprecated diff --git a/vendor/guzzlehttp/promises/src/Utils.php b/vendor/guzzlehttp/promises/src/Utils.php index fdbca36..1dcb773 100644 --- a/vendor/guzzlehttp/promises/src/Utils.php +++ b/vendor/guzzlehttp/promises/src/Utils.php @@ -168,6 +168,12 @@ final class Utils if (true === $recursive) { $promise = $promise->then(function ($results) use ($recursive, &$promises) { + // A consumed generator cannot be traversed again, so a + // recursive pass has nothing further to observe. + if ($promises instanceof \Generator) { + return $results; + } + foreach ($promises as $promise) { if (Is::pending($promise)) { return self::all($promises, $recursive); diff --git a/vendor/guzzlehttp/psr7/CHANGELOG.md b/vendor/guzzlehttp/psr7/CHANGELOG.md index 7779034..3d678f4 100644 --- a/vendor/guzzlehttp/psr7/CHANGELOG.md +++ b/vendor/guzzlehttp/psr7/CHANGELOG.md @@ -5,6 +5,66 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 2.13.0 - 2026-07-16 + +### Added + +- Add `Utils::` `asciiToLower`, `asciiToUpper`, `asciiUcFirst`, `caselessEquals`, `caselessContains` + +### Changed + +- Use locale-independent ASCII case folding everywhere case is normalized +- Trigger a runtime deprecation for previously deprecated functionality in 2.3.0 + +## 2.12.5 - 2026-07-13 + +### Fixed + +- Compare header names and hosts with locale-independent ASCII lowercasing +- Compare hosts without locale sensitivity when detecting cross-origin redirects + +## 2.12.4 - 2026-07-08 + +### Changed + +- Pass explicit trim characters ahead of the PHP 8.6 trim default change + +### Fixed + +- Anchor server port and response start-line patterns to the true end of input +- Treat host-less origin-form request targets starting with `//` as paths in `Message::parseRequest()` +- Reject raw DEL bytes in bracketed IP-literal hosts instead of parsing a mutated host +- Reject invalid bytes after a bracketed IP-literal host instead of reparsing a different host + +## 2.12.3 - 2026-06-23 + +### Security + +- Validate the URI host so `getHost()` matches the URI authority (GHSA-c2w2-prh8-qm98) + +## 2.12.2 - 2026-06-23 + +### Fixed + +- Report URI parsing, filtering, and normalization PCRE failures explicitly +- Report HTTP message parser PCRE failures explicitly +- Fail closed when PCRE validation fails for request targets and hosts + +## 2.12.1 - 2026-06-18 + +### Security + +- Reject CR/LF in HTTP method, protocol version, and reason phrase (GHSA-vm85-hxw5-5432) + +## 2.12.0 - 2026-06-16 + +### Deprecated + +- Deprecated non-finite float values in `Query::build()` that guzzlehttp/psr7 3.0 rejects +- Deprecated non-finite float multipart contents that guzzlehttp/psr7 3.0 rejects +- Deprecated non-string scalar bodies in `Utils::streamFor()`; cast them to a string for 3.0 +- Deprecated non-string `Uri::withQueryValues()` values; cast them to a string for 3.0 + ## 2.11.1 - 2026-06-12 ### Fixed diff --git a/vendor/guzzlehttp/psr7/README.md b/vendor/guzzlehttp/psr7/README.md index 0e5ebde..13f2ba5 100644 --- a/vendor/guzzlehttp/psr7/README.md +++ b/vendor/guzzlehttp/psr7/README.md @@ -455,6 +455,56 @@ string. This function does not modify the provided keys when an array is encountered (like `http_build_query()` would). +## `GuzzleHttp\Psr7\Utils::asciiToLower` + +`public static function asciiToLower(string $string): string` + +Converts ASCII uppercase letters in a string to lowercase. + +Unlike strtolower(), which honors LC_CTYPE before PHP 8.2, the conversion is +locale-independent and leaves every non-ASCII byte unchanged, as HTTP protocol +elements require. + + +## `GuzzleHttp\Psr7\Utils::asciiToUpper` + +`public static function asciiToUpper(string $string): string` + +Converts ASCII lowercase letters in a string to uppercase. + +Unlike strtoupper(), which honors LC_CTYPE before PHP 8.2, the conversion is +locale-independent and leaves every non-ASCII byte unchanged, as HTTP protocol +elements require. + + +## `GuzzleHttp\Psr7\Utils::asciiUcFirst` + +`public static function asciiUcFirst(string $string): string` + +Converts the first character of a string to uppercase when it is an ASCII +lowercase letter. + +Unlike ucfirst(), which honors LC_CTYPE before PHP 8.2, the conversion is +locale-independent and leaves every non-ASCII byte unchanged, as HTTP protocol +elements require. + + +## `GuzzleHttp\Psr7\Utils::caselessContains` + +`public static function caselessContains(string $haystack, string $needle): bool` + +Checks whether the haystack contains the needle, comparing ASCII letters +case-insensitively and without locale sensitivity. + + +## `GuzzleHttp\Psr7\Utils::caselessEquals` + +`public static function caselessEquals(string $left, string $right): bool` + +Checks whether two strings are equal, comparing ASCII letters +case-insensitively and without locale sensitivity. + + ## `GuzzleHttp\Psr7\Utils::caselessRemove` `public static function caselessRemove(iterable $keys, $keys, array $data): array` diff --git a/vendor/guzzlehttp/psr7/composer.json b/vendor/guzzlehttp/psr7/composer.json index c51026c..86f2657 100644 --- a/vendor/guzzlehttp/psr7/composer.json +++ b/vendor/guzzlehttp/psr7/composer.json @@ -55,7 +55,7 @@ "psr/http-message": "^1.1 || ^2.0", "ralouphie/getallheaders": "^3.0", "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.24" + "symfony/polyfill-php80": "^1.25" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", diff --git a/vendor/guzzlehttp/psr7/src/Header.php b/vendor/guzzlehttp/psr7/src/Header.php index a53df19..63fc237 100644 --- a/vendor/guzzlehttp/psr7/src/Header.php +++ b/vendor/guzzlehttp/psr7/src/Header.php @@ -95,6 +95,8 @@ final class Header */ public static function normalize($header): array { + \trigger_deprecation('guzzlehttp/psr7', '2.3', 'Header::normalize() is deprecated and will be removed in guzzlehttp/psr7 3.0. Use Header::splitList() instead.'); + $result = []; foreach ((array) $header as $value) { foreach (self::splitList($value) as $parsed) { @@ -142,7 +144,7 @@ final class Header } if (!$isQuoted && $value[$i] === ',') { - $v = \trim($v); + $v = \trim($v, " \n\r\t\0\x0B"); if ($v !== '') { $result[] = $v; } @@ -167,7 +169,7 @@ final class Header $v .= $value[$i]; } - $v = \trim($v); + $v = \trim($v, " \n\r\t\0\x0B"); if ($v !== '') { $result[] = $v; } diff --git a/vendor/guzzlehttp/psr7/src/Message.php b/vendor/guzzlehttp/psr7/src/Message.php index 1e83d44..da3135d 100644 --- a/vendor/guzzlehttp/psr7/src/Message.php +++ b/vendor/guzzlehttp/psr7/src/Message.php @@ -19,7 +19,7 @@ final class Message { if ($message instanceof RequestInterface) { $msg = trim($message->getMethod().' ' - .$message->getRequestTarget()) + .$message->getRequestTarget(), " \n\r\t\0\x0B") .' HTTP/'.$message->getProtocolVersion(); if (!$message->hasHeader('host')) { $msg .= "\r\nHost: ".$message->getUri()->getHost(); @@ -33,7 +33,7 @@ final class Message } foreach ($message->getHeaders() as $name => $values) { - if (is_string($name) && strtolower($name) === 'set-cookie') { + if (is_string($name) && Utils::asciiToLower($name) === 'set-cookie') { foreach ($values as $value) { $msg .= "\r\n{$name}: ".$value; } @@ -181,7 +181,11 @@ final class Message $messageParts = preg_split("/\r?\n\r?\n/", $message, 2); - if ($messageParts === false || count($messageParts) !== 2) { + if ($messageParts === false) { + throw new \RuntimeException('Unable to split HTTP message: '.preg_last_error_msg()); + } + + if (count($messageParts) !== 2) { throw new \InvalidArgumentException('Invalid message: Missing header delimiter'); } @@ -189,24 +193,48 @@ final class Message $rawHeaders .= "\r\n"; // Put back the delimiter we split previously $headerParts = preg_split("/\r?\n/", $rawHeaders, 2); - if ($headerParts === false || count($headerParts) !== 2) { + if ($headerParts === false) { + throw new \RuntimeException('Unable to split HTTP message headers: '.preg_last_error_msg()); + } + + if (count($headerParts) !== 2) { throw new \InvalidArgumentException('Invalid message: Missing status line'); } [$startLine, $rawHeaders] = $headerParts; - if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') { + $versionMatch = preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches); + + if ($versionMatch === false) { + throw new \RuntimeException('Unable to parse HTTP start line: '.preg_last_error_msg()); + } + + if ($versionMatch === 1 && $matches[1] === '1.0') { // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0 $rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders); + + if ($rawHeaders === null) { + throw new \RuntimeException('Unable to unfold HTTP headers: '.preg_last_error_msg()); + } } /** @var array[] $headerLines */ $count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER); + if ($count === false) { + throw new \RuntimeException('Unable to parse HTTP headers: '.preg_last_error_msg()); + } + // If these aren't the same, then one line didn't match and there's an invalid header. if ($count !== substr_count($rawHeaders, "\n")) { // Folding is deprecated, see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4 - if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) { + $hasFoldedHeader = preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders); + + if ($hasFoldedHeader === false) { + throw new \RuntimeException('Unable to inspect HTTP header folding: '.preg_last_error_msg()); + } + + if ($hasFoldedHeader === 1) { throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding'); } @@ -237,8 +265,10 @@ final class Message $host = self::getHostFromHeaders($headers); // If no host is found, then a full URI cannot be constructed. + // Collapse leading slashes so an origin-form target cannot be + // parsed as a network-path reference with its own authority. if ($host === null) { - return $path; + return self::normalizePathForOriginForm($path); } $scheme = substr($host, -4) === ':443' ? 'https' : 'http'; @@ -246,6 +276,15 @@ final class Message return $scheme.'://'.$host.'/'.ltrim($path, '/'); } + private static function normalizePathForOriginForm(string $path): string + { + if (0 === strpos($path, '//')) { + return '/'.ltrim($path, '/'); + } + + return $path; + } + /** * @param array $headers Array of headers (each value an array). */ @@ -255,7 +294,7 @@ final class Message // Numeric array keys are converted to int by PHP. $k = (string) $k; - return strtolower($k) === 'host'; + return Utils::asciiToLower($k) === 'host'; }); if (!$hostKey) { @@ -278,8 +317,18 @@ final class Message public static function parseRequest(string $message): RequestInterface { $data = self::parseMessage($message); + if (strpbrk($data['start-line'], "\r\n") !== false) { + throw new \InvalidArgumentException('Invalid request string'); + } + $matches = []; - if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) { + $requestStartLineMatch = preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches); + + if ($requestStartLineMatch === false) { + throw new \RuntimeException('Unable to parse request start line: '.preg_last_error_msg()); + } + + if ($requestStartLineMatch === 0) { throw new \InvalidArgumentException('Invalid request string'); } $parts = explode(' ', $data['start-line'], 3); @@ -304,10 +353,20 @@ final class Message public static function parseResponse(string $message): ResponseInterface { $data = self::parseMessage($message); + if (strpbrk($data['start-line'], "\r\n") !== false) { + throw new \InvalidArgumentException('Invalid response string'); + } + // According to https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2 // the space between status-code and reason-phrase is required. But // browsers accept responses without space and reason as well. - if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) { + $responseStartLineMatch = preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/D', $data['start-line']); + + if ($responseStartLineMatch === false) { + throw new \RuntimeException('Unable to parse response start line: '.preg_last_error_msg()); + } + + if ($responseStartLineMatch === 0) { throw new \InvalidArgumentException('Invalid response string: '.$data['start-line']); } $parts = explode(' ', $data['start-line'], 3); diff --git a/vendor/guzzlehttp/psr7/src/MessageTrait.php b/vendor/guzzlehttp/psr7/src/MessageTrait.php index 01b0f20..16337a3 100644 --- a/vendor/guzzlehttp/psr7/src/MessageTrait.php +++ b/vendor/guzzlehttp/psr7/src/MessageTrait.php @@ -43,6 +43,8 @@ trait MessageTrait ); } + $this->assertProtocolVersion($version); + if ($this->protocol === $version) { return $this; } @@ -60,12 +62,12 @@ trait MessageTrait public function hasHeader($header): bool { - return isset($this->headerNames[strtolower($header)]); + return isset($this->headerNames[Utils::asciiToLower($header)]); } public function getHeader($header): array { - $header = strtolower($header); + $header = Utils::asciiToLower($header); if (!isset($this->headerNames[$header])) { return []; @@ -101,7 +103,7 @@ trait MessageTrait } } $value = $this->normalizeHeaderValue($value); - $normalized = strtolower($header); + $normalized = Utils::asciiToLower($header); $new = clone $this; if (isset($new->headerNames[$normalized])) { @@ -133,7 +135,7 @@ trait MessageTrait } } $value = $this->normalizeHeaderValue($value); - $normalized = strtolower($header); + $normalized = Utils::asciiToLower($header); $new = clone $this; if (isset($new->headerNames[$normalized])) { @@ -152,7 +154,7 @@ trait MessageTrait */ public function withoutHeader($header): MessageInterface { - $normalized = strtolower($header); + $normalized = Utils::asciiToLower($header); if (!isset($this->headerNames[$normalized])) { return $this; @@ -216,7 +218,7 @@ trait MessageTrait } } $value = $this->normalizeHeaderValue($value); - $normalized = strtolower($header); + $normalized = Utils::asciiToLower($header); if (isset($this->headerNames[$normalized])) { $header = $this->headerNames[$normalized]; $this->headers[$header] = array_merge($this->headers[$header], $value); @@ -307,6 +309,23 @@ trait MessageTrait } } + /** + * @param mixed $version + */ + private function assertProtocolVersion($version): void + { + if (is_string($version)) { + $this->assertNoLineSeparators($version, 'Protocol version'); + } + } + + private function assertNoLineSeparators(string $value, string $field): void + { + if (strpbrk($value, "\r\n") !== false) { + throw new \InvalidArgumentException($field.' must not contain CR or LF characters.'); + } + } + /** * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 * diff --git a/vendor/guzzlehttp/psr7/src/MimeType.php b/vendor/guzzlehttp/psr7/src/MimeType.php index fa493b8..b812864 100644 --- a/vendor/guzzlehttp/psr7/src/MimeType.php +++ b/vendor/guzzlehttp/psr7/src/MimeType.php @@ -1300,6 +1300,6 @@ final class MimeType */ public static function fromExtension(string $extension): ?string { - return self::MIME_TYPES[strtolower($extension)] ?? null; + return self::MIME_TYPES[Utils::asciiToLower($extension)] ?? null; } } diff --git a/vendor/guzzlehttp/psr7/src/MultipartStream.php b/vendor/guzzlehttp/psr7/src/MultipartStream.php index 0031da3..0f454aa 100644 --- a/vendor/guzzlehttp/psr7/src/MultipartStream.php +++ b/vendor/guzzlehttp/psr7/src/MultipartStream.php @@ -26,8 +26,9 @@ final class MultipartStream implements StreamInterface * @param array $elements Array of associative arrays, each containing a * required "name" key mapping to the form field, * name, a required "contents" key mapping to any - * non-array value accepted by Utils::streamFor(), - * or an array for nested expansion. + * non-array value accepted by Utils::streamFor() + * (non-string scalar field values are cast to + * string), or an array for nested expansion. * Optional keys include "headers" (associative * array of custom headers) and "filename" (string * to send as the filename in the part). @@ -77,7 +78,7 @@ final class MultipartStream implements StreamInterface $str .= "{$key}: {$value}\r\n"; } - return "--{$this->boundary}\r\n".trim($str)."\r\n\r\n"; + return "--{$this->boundary}\r\n".trim($str, " \n\r\t\0\x0B")."\r\n\r\n"; } /** @@ -124,7 +125,27 @@ final class MultipartStream implements StreamInterface return; } - $element['contents'] = Utils::streamFor($element['contents']); + $contents = $element['contents']; + if (is_scalar($contents) && !is_string($contents)) { + // Multipart field values are byte strings on the wire, so finite + // numeric and boolean field values are cast to string here rather + // than tripping streamFor()'s non-string-scalar deprecation. Non-finite + // floats are deprecated and normalized here too, so the deprecation is + // reported against MultipartStream instead of transitively through + // streamFor(). + if (is_float($contents) && !is_finite($contents)) { + \trigger_deprecation( + 'guzzlehttp/psr7', + '2.12', + 'Passing a non-finite float as multipart contents is deprecated; guzzlehttp/psr7 3.0 rejects non-finite floats.' + ); + + $contents = is_nan($contents) ? 'NAN' : ($contents > 0 ? 'INF' : '-INF'); + } + + $contents = (string) $contents; + } + $element['contents'] = Utils::streamFor($contents); if (empty($element['filename'])) { $uri = $element['contents']->getMetadata('uri'); @@ -206,9 +227,9 @@ final class MultipartStream implements StreamInterface */ private static function getHeader(array $headers, string $key): ?string { - $lowercaseHeader = strtolower($key); + $lowercaseHeader = Utils::asciiToLower($key); foreach ($headers as $k => $v) { - if (strtolower((string) $k) === $lowercaseHeader) { + if (Utils::asciiToLower((string) $k) === $lowercaseHeader) { return $v; } } diff --git a/vendor/guzzlehttp/psr7/src/Query.php b/vendor/guzzlehttp/psr7/src/Query.php index 0c5f408..a7b8997 100644 --- a/vendor/guzzlehttp/psr7/src/Query.php +++ b/vendor/guzzlehttp/psr7/src/Query.php @@ -127,6 +127,12 @@ final class Query private static function normalizeNonFiniteFloat($value) { if (is_float($value) && !is_finite($value)) { + \trigger_deprecation( + 'guzzlehttp/psr7', + '2.12', + 'Passing a non-finite float to Query::build() is deprecated; guzzlehttp/psr7 3.0 rejects non-finite floats.' + ); + return is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF'); } diff --git a/vendor/guzzlehttp/psr7/src/Request.php b/vendor/guzzlehttp/psr7/src/Request.php index 64b38f3..ae44da4 100644 --- a/vendor/guzzlehttp/psr7/src/Request.php +++ b/vendor/guzzlehttp/psr7/src/Request.php @@ -40,12 +40,14 @@ class Request implements RequestInterface string $version = '1.1' ) { $this->assertMethod($method); + $this->assertProtocolVersion($version); + if (!$uri instanceof UriInterface) { $uri = new Uri($uri); } self::warnOnMethodCasingChange($method); - $this->method = strtoupper($method); + $this->method = Utils::asciiToUpper($method); $this->uri = $uri; $this->setHeaders($headers); $this->protocol = $version; @@ -78,7 +80,13 @@ class Request implements RequestInterface public function withRequestTarget($requestTarget): RequestInterface { - if (preg_match('#\s#', $requestTarget)) { + $hasWhitespace = preg_match('#\s#', $requestTarget); + + if ($hasWhitespace === false) { + throw new \RuntimeException('Unable to validate request target: '.preg_last_error_msg()); + } + + if ($hasWhitespace === 1) { throw new InvalidArgumentException( 'Invalid request target provided; cannot contain whitespace' ); @@ -100,7 +108,7 @@ class Request implements RequestInterface $this->assertMethod($method); self::warnOnMethodCasingChange($method); $new = clone $this; - $new->method = strtoupper($method); + $new->method = Utils::asciiToUpper($method); return $new; } @@ -170,11 +178,13 @@ class Request implements RequestInterface if (!is_string($method) || $method === '') { throw new InvalidArgumentException('Method must be a non-empty string.'); } + + $this->assertNoLineSeparators($method, 'Method'); } private static function warnOnMethodCasingChange(string $method): void { - if ($method !== strtoupper($method)) { + if ($method !== Utils::asciiToUpper($method)) { \trigger_deprecation( 'guzzlehttp/psr7', '2.11', diff --git a/vendor/guzzlehttp/psr7/src/Response.php b/vendor/guzzlehttp/psr7/src/Response.php index 8beec96..4bec816 100644 --- a/vendor/guzzlehttp/psr7/src/Response.php +++ b/vendor/guzzlehttp/psr7/src/Response.php @@ -99,6 +99,7 @@ class Response implements ResponseInterface ?string $reason = null ) { $this->assertStatusCodeRange($status); + $this->assertProtocolVersion($version); $this->statusCode = $status; @@ -108,11 +109,14 @@ class Response implements ResponseInterface $this->setHeaders($headers); if ($reason == '' && isset(self::PHRASES[$this->statusCode])) { - $this->reasonPhrase = self::PHRASES[$this->statusCode]; + $reasonPhrase = self::PHRASES[$this->statusCode]; } else { - $this->reasonPhrase = (string) $reason; + $reasonPhrase = (string) $reason; } + $this->assertNoLineSeparators($reasonPhrase, 'Reason phrase'); + $this->reasonPhrase = $reasonPhrase; + $this->protocol = $version; } @@ -155,7 +159,9 @@ class Response implements ResponseInterface if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) { $reasonPhrase = self::PHRASES[$new->statusCode]; } - $new->reasonPhrase = (string) $reasonPhrase; + $reasonPhrase = (string) $reasonPhrase; + $this->assertNoLineSeparators($reasonPhrase, 'Reason phrase'); + $new->reasonPhrase = $reasonPhrase; return $new; } diff --git a/vendor/guzzlehttp/psr7/src/Rfc7230.php b/vendor/guzzlehttp/psr7/src/Rfc7230.php index 9cd4863..6557f90 100644 --- a/vendor/guzzlehttp/psr7/src/Rfc7230.php +++ b/vendor/guzzlehttp/psr7/src/Rfc7230.php @@ -68,7 +68,13 @@ final class Rfc7230 private static function isValidHostHeaderHost(string $host): bool { - if (preg_match('/[\x00-\x20\x7F\/\?#@\\\\]/', $host)) { + $invalidHost = preg_match('/[\x00-\x20\x7F\/\?#@\\\\]/', $host); + + if ($invalidHost === false) { + return false; + } + + if ($invalidHost === 1) { return false; } diff --git a/vendor/guzzlehttp/psr7/src/ServerRequest.php b/vendor/guzzlehttp/psr7/src/ServerRequest.php index 4271916..30b7a89 100644 --- a/vendor/guzzlehttp/psr7/src/ServerRequest.php +++ b/vendor/guzzlehttp/psr7/src/ServerRequest.php @@ -165,7 +165,7 @@ class ServerRequest extends Request implements ServerRequestInterface */ public static function fromGlobals(): ServerRequestInterface { - $method = strtoupper(self::getServerParam('REQUEST_METHOD') ?? 'GET'); + $method = Utils::asciiToUpper(self::getServerParam('REQUEST_METHOD') ?? 'GET'); $headers = self::removeInvalidHostHeader(self::getAllHeaders()); $uri = self::getUriFromGlobals(); $body = new CachingStream(new LazyOpenStream('php://input', 'r+')); @@ -220,7 +220,7 @@ class ServerRequest extends Request implements ServerRequestInterface private static function removeInvalidHostHeader(array $headers): array { foreach ($headers as $name => $value) { - if (strtolower((string) $name) !== 'host') { + if (Utils::asciiToLower((string) $name) !== 'host') { continue; } @@ -269,7 +269,7 @@ class ServerRequest extends Request implements ServerRequestInterface } $serverPort = self::getServerParam('SERVER_PORT'); - if (!$hasPort && $serverPort !== null && preg_match('/^[+-]?\d+$/', $serverPort) === 1) { + if (!$hasPort && $serverPort !== null && preg_match('/^[+-]?\d+$/D', $serverPort) === 1) { $uri = $uri->withPort((int) $serverPort); } diff --git a/vendor/guzzlehttp/psr7/src/Uri.php b/vendor/guzzlehttp/psr7/src/Uri.php index d1a6e3f..a10f52c 100644 --- a/vendor/guzzlehttp/psr7/src/Uri.php +++ b/vendor/guzzlehttp/psr7/src/Uri.php @@ -99,12 +99,30 @@ class Uri implements UriInterface, \JsonSerializable return self::parsePathNoSchemeReference($url); } - // Preserve bracketed IPv6 literals before encoding, including dotted IPv4 tails. + // Preserve bracketed IPv6 literals before encoding, including dotted IPv4 + // tails. DEL (\x7F) is excluded so a raw-DEL host falls through to the + // general path and is rejected rather than silently mutated by parse_url(). $prefix = ''; - if (preg_match('%^([0-9A-Za-z+.-]+://\[[0-9:.a-fA-F]+\])(.*?)$%', $url, $matches)) { + $ipv6Prefix = preg_match('%\A([0-9A-Za-z+.-]+://\[[^\]\x00-\x20\x7F/?#@]+\])(.*)\z%s', $url, $matches); + + if ($ipv6Prefix === false) { + return false; + } + + if ($ipv6Prefix === 1) { /** @var array{0:string, 1:string, 2:string} $matches */ + $suffix = $matches[2]; + + // After the bracketed host only an optional numeric port and/or a + // path, query, or fragment may follow. Anything else (for example + // `:80@evil` or `:80x`) would let parse_url() reinterpret a + // different host. + if (preg_match('%\A(?::[0-9]*)?(?:[/?#].*)?\z%s', $suffix) !== 1) { + return false; + } + $prefix = $matches[1]; - $url = $matches[2]; + $url = $suffix; } /** @var string|null */ @@ -378,15 +396,26 @@ class Uri implements UriInterface, \JsonSerializable } /** - * Converts non-finite floats to the strings PHP coerces them to, as - * implicit coercion of NAN emits a warning on PHP 8.5. + * Stringifies a non-null query value, deprecating non-string values that + * guzzlehttp/psr7 3.0 will reject. Non-finite floats are normalized to the + * strings PHP coerces them to, as implicit coercion of NAN emits a warning + * on PHP 8.5. * * @param mixed $value */ private static function stringifyQueryValue($value): string { - if (is_float($value) && !is_finite($value)) { - return is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF'); + if (!is_string($value)) { + \trigger_deprecation( + 'guzzlehttp/psr7', + '2.12', + 'Passing %s to Uri::withQueryValues() is deprecated; cast it to a string. guzzlehttp/psr7 3.0 will only accept string or null query values.', + \gettype($value) + ); + + if (is_float($value) && !is_finite($value)) { + return is_nan($value) ? 'NAN' : ($value > 0 ? 'INF' : '-INF'); + } } return (string) $value; @@ -425,7 +454,27 @@ class Uri implements UriInterface, \JsonSerializable return; } - if (preg_match('/[\x00-\x20\x7F]/', $host)) { + // Reject control characters and URI authority delimiters so getHost() + // cannot disagree with the on-wire authority. + $invalidHost = preg_match('/[\x00-\x20\x7F\/\?#@\\\\]/', $host); + + if ($invalidHost === false) { + throw new \RuntimeException('Unable to validate URI host: '.preg_last_error_msg()); + } + + if ($invalidHost === 1) { + throw new \InvalidArgumentException(sprintf('Invalid host: "%s"', $host)); + } + + if (strpos($host, '[') !== false || strpos($host, ']') !== false) { + if ($host[0] !== '[' || substr($host, -1) !== ']') { + throw new \InvalidArgumentException(sprintf('Invalid host: "%s"', $host)); + } + + return; + } + + if (strpos($host, ':') !== false) { throw new \InvalidArgumentException(sprintf('Invalid host: "%s"', $host)); } } @@ -647,7 +696,7 @@ class Uri implements UriInterface, \JsonSerializable throw new \InvalidArgumentException('Scheme must be a string'); } - $scheme = \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); + $scheme = Utils::asciiToLower($scheme); if ($scheme !== '' && !preg_match('/^[a-z][a-z0-9.+-]*$/D', $scheme)) { \trigger_deprecation( @@ -672,10 +721,10 @@ class Uri implements UriInterface, \JsonSerializable throw new \InvalidArgumentException('User info must be a string'); } - return preg_replace_callback( + return $this->filterComponent( '/(?:[^%'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.']+|%(?![A-Fa-f0-9]{2}))/', - [$this, 'rawurlencodeMatchZero'], - $component + $component, + 'Unable to filter URI user info' ); } @@ -690,7 +739,7 @@ class Uri implements UriInterface, \JsonSerializable throw new \InvalidArgumentException('Host must be a string'); } - $host = \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); + $host = Utils::asciiToLower($host); self::assertValidHost($host); return $host; @@ -774,10 +823,10 @@ class Uri implements UriInterface, \JsonSerializable throw new \InvalidArgumentException('Path must be a string'); } - return preg_replace_callback( + return $this->filterComponent( '/(?:[^'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.'%:@\/]++|%(?![A-Fa-f0-9]{2}))/', - [$this, 'rawurlencodeMatchZero'], - $path + $path, + 'Unable to filter URI path' ); } @@ -794,13 +843,24 @@ class Uri implements UriInterface, \JsonSerializable throw new \InvalidArgumentException('Query and fragment must be a string'); } - return preg_replace_callback( + return $this->filterComponent( '/(?:[^'.Rfc3986::CHAR_UNRESERVED.Rfc3986::CHAR_SUB_DELIMS.'%:@\/\?]++|%(?![A-Fa-f0-9]{2}))/', - [$this, 'rawurlencodeMatchZero'], - $str + $str, + 'Unable to filter URI query or fragment' ); } + private function filterComponent(string $pattern, string $component, string $context): string + { + $filtered = preg_replace_callback($pattern, [$this, 'rawurlencodeMatchZero'], $component); + + if ($filtered === null) { + throw new \RuntimeException($context.': '.preg_last_error_msg()); + } + + return $filtered; + } + private function rawurlencodeMatchZero(array $match): string { return rawurlencode($match[0]); diff --git a/vendor/guzzlehttp/psr7/src/UriComparator.php b/vendor/guzzlehttp/psr7/src/UriComparator.php index 0b05c83..ea9a07a 100644 --- a/vendor/guzzlehttp/psr7/src/UriComparator.php +++ b/vendor/guzzlehttp/psr7/src/UriComparator.php @@ -19,7 +19,7 @@ final class UriComparator */ public static function isCrossOrigin(UriInterface $original, UriInterface $modified): bool { - if (\strcasecmp($original->getHost(), $modified->getHost()) !== 0) { + if (!Utils::caselessEquals($original->getHost(), $modified->getHost())) { return true; } diff --git a/vendor/guzzlehttp/psr7/src/UriNormalizer.php b/vendor/guzzlehttp/psr7/src/UriNormalizer.php index 0764d98..e5468b8 100644 --- a/vendor/guzzlehttp/psr7/src/UriNormalizer.php +++ b/vendor/guzzlehttp/psr7/src/UriNormalizer.php @@ -150,7 +150,13 @@ final class UriNormalizer } if ($flags & self::REMOVE_DUPLICATE_SLASHES) { - $uri = $uri->withPath(preg_replace('#//++#', '/', $uri->getPath())); + $path = preg_replace('#//++#', '/', $uri->getPath()); + + if ($path === null) { + throw new \RuntimeException('Unable to remove duplicate slashes from URI path: '.preg_last_error_msg()); + } + + $uri = $uri->withPath($path); } if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') { @@ -186,7 +192,7 @@ final class UriNormalizer $regex = '/(?:%[A-Fa-f0-9]{2})++/'; $callback = function (array $match): string { - return strtoupper($match[0]); + return Utils::asciiToUpper($match[0]); }; return $uri @@ -217,7 +223,7 @@ final class UriNormalizer $normalized = preg_replace_callback($regex, $callback, $component); if ($normalized === null) { - throw new \RuntimeException('Unable to normalize URI component percent-encoding'); + throw new \RuntimeException('Unable to normalize URI component percent-encoding: '.preg_last_error_msg()); } return $normalized; diff --git a/vendor/guzzlehttp/psr7/src/Utils.php b/vendor/guzzlehttp/psr7/src/Utils.php index e8c0df3..c46991e 100644 --- a/vendor/guzzlehttp/psr7/src/Utils.php +++ b/vendor/guzzlehttp/psr7/src/Utils.php @@ -10,6 +10,65 @@ use Psr\Http\Message\UriInterface; final class Utils { + /** + * Converts ASCII uppercase letters in a string to lowercase. + * + * Unlike strtolower(), which honors LC_CTYPE before PHP 8.2, the + * conversion is locale-independent and leaves every non-ASCII byte + * unchanged, as HTTP protocol elements require. + */ + public static function asciiToLower(string $string): string + { + return strtr($string, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); + } + + /** + * Converts ASCII lowercase letters in a string to uppercase. + * + * Unlike strtoupper(), which honors LC_CTYPE before PHP 8.2, the + * conversion is locale-independent and leaves every non-ASCII byte + * unchanged, as HTTP protocol elements require. + */ + public static function asciiToUpper(string $string): string + { + return strtr($string, 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'); + } + + /** + * Converts the first character of a string to uppercase when it is an + * ASCII lowercase letter. + * + * Unlike ucfirst(), which honors LC_CTYPE before PHP 8.2, the conversion + * is locale-independent and leaves every non-ASCII byte unchanged, as + * HTTP protocol elements require. + */ + public static function asciiUcFirst(string $string): string + { + if ($string === '') { + return ''; + } + + return self::asciiToUpper($string[0]).substr($string, 1); + } + + /** + * Checks whether the haystack contains the needle, comparing ASCII + * letters case-insensitively and without locale sensitivity. + */ + public static function caselessContains(string $haystack, string $needle): bool + { + return str_contains(self::asciiToLower($haystack), self::asciiToLower($needle)); + } + + /** + * Checks whether two strings are equal, comparing ASCII letters + * case-insensitively and without locale sensitivity. + */ + public static function caselessEquals(string $left, string $right): bool + { + return self::asciiToLower($left) === self::asciiToLower($right); + } + /** * Remove the items given by the keys, case insensitively from the data. * @@ -20,11 +79,11 @@ final class Utils $result = []; foreach ($keys as &$key) { - $key = strtolower((string) $key); + $key = self::asciiToLower((string) $key); } foreach ($data as $k => $v) { - if (!in_array(strtolower((string) $k), $keys)) { + if (!in_array(self::asciiToLower((string) $k), $keys)) { $result[$k] = $v; } } @@ -212,7 +271,7 @@ final class Utils if ($host !== '') { if (isset($changes['set_headers']) && is_array($changes['set_headers'])) { foreach (array_keys($changes['set_headers']) as $header) { - if (strtolower((string) $header) === 'host') { + if (self::asciiToLower((string) $header) === 'host') { throw new \InvalidArgumentException( 'Cannot modify request with both a URI containing a host and an explicit Host header.' ); @@ -248,7 +307,7 @@ final class Utils $hasHost = false; foreach (array_keys($headers) as $header) { - if (strtolower((string) $header) === 'host') { + if (self::asciiToLower((string) $header) === 'host') { $hasHost = true; break; } @@ -284,7 +343,7 @@ final class Utils $addedHeaders = []; foreach ($headers as $header => $value) { $header = (string) $header; - $normalized = strtolower($header); + $normalized = self::asciiToLower($header); if (isset($addedHeaders[$normalized])) { /** @var RequestInterface */ @@ -462,6 +521,9 @@ final class Utils * in subsequent reads. String inputs are always treated as string bodies, * even when they name callable functions. * + * Passing a non-string scalar (`int`, `float`, or `bool`) is deprecated; cast + * it to a string instead. guzzlehttp/psr7 3.0 will reject non-string scalars. + * * @param resource|string|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data * @param array{size?: int, metadata?: array} $options Additional options * @@ -470,10 +532,20 @@ final class Utils public static function streamFor($resource = '', array $options = []): StreamInterface { if (is_scalar($resource)) { - // Convert non-finite floats explicitly, as implicit coercion of - // NAN emits a warning on PHP 8.5. - if (is_float($resource) && !is_finite($resource)) { - $resource = is_nan($resource) ? 'NAN' : ($resource > 0 ? 'INF' : '-INF'); + if (!is_string($resource)) { + \trigger_deprecation( + 'guzzlehttp/psr7', + '2.12', + 'Passing %s to Utils::streamFor() is deprecated; cast it to a string. guzzlehttp/psr7 3.0 will only accept string, resource, StreamInterface, Stringable, Iterator, callable, or null.', + \gettype($resource) + ); + + if (is_float($resource) && !is_finite($resource)) { + // Normalized only to avoid PHP 8.5's (string) NAN warning + // while deprecated; 3.0 rejects non-finite floats with every + // other non-string scalar. + $resource = is_nan($resource) ? 'NAN' : ($resource > 0 ? 'INF' : '-INF'); + } } $stream = self::tryFopen('php://temp', 'r+'); diff --git a/vendor/league/flysystem/src/Filesystem.php b/vendor/league/flysystem/src/Filesystem.php index 5810bd7..aea3fd0 100644 --- a/vendor/league/flysystem/src/Filesystem.php +++ b/vendor/league/flysystem/src/Filesystem.php @@ -30,7 +30,7 @@ class Filesystem implements FilesystemOperator private ?TemporaryUrlGenerator $temporaryUrlGenerator = null, ) { $this->config = new Config($config); - $this->pathNormalizer = $pathNormalizer ?? new WhitespacePathNormalizer(); + $this->pathNormalizer = $pathNormalizer ?? new WhitespacePathNormalizer($this->config->get('allow_relative_path_traversal', true)); } public function fileExists(string $location): bool @@ -168,9 +168,17 @@ class Filesystem implements FilesystemOperator public function mimeType(string $path): string { - return $this->adapter->mimeType($this->pathNormalizer->normalizePath($path))->mimeType(); + $normalizedPath = $this->pathNormalizer->normalizePath($path); + $attributes = $this->adapter->mimeType($normalizedPath); + + if ($attributes->mimeType() === null) { + throw UnableToRetrieveMetadata::mimeType($path); + } + + return $attributes->mimeType(); } + public function setVisibility(string $path, string $visibility): void { $this->adapter->setVisibility($this->pathNormalizer->normalizePath($path), $visibility); diff --git a/vendor/league/flysystem/src/WhitespacePathNormalizer.php b/vendor/league/flysystem/src/WhitespacePathNormalizer.php index 135b22a..16d78fa 100644 --- a/vendor/league/flysystem/src/WhitespacePathNormalizer.php +++ b/vendor/league/flysystem/src/WhitespacePathNormalizer.php @@ -4,37 +4,42 @@ declare(strict_types=1); namespace League\Flysystem; +use function array_pop; +use function explode; +use function implode; +use function preg_match; +use function str_replace; + class WhitespacePathNormalizer implements PathNormalizer { + private bool $allowRelativePaths; + + public function __construct(bool $allowRelativePathTraversal = true) + { + $this->allowRelativePaths = $allowRelativePathTraversal; + } + public function normalizePath(string $path): string { - $path = str_replace('\\', '/', $path); - $this->rejectFunkyWhiteSpace($path); + $unixPath = str_replace('\\', '/', $path); - return $this->normalizeRelativePath($path); - } - - private function rejectFunkyWhiteSpace(string $path): void - { - if (preg_match('#\p{C}+#u', $path)) { + if (preg_match('#\p{C}+#u', $unixPath)) { throw CorruptedPathDetected::forPath($path); } - } - private function normalizeRelativePath(string $path): string - { $parts = []; - foreach (explode('/', $path) as $part) { + foreach (explode('/', $unixPath) as $part) { switch ($part) { case '': case '.': break; case '..': - if (empty($parts)) { + if ($this->allowRelativePaths === false || empty($parts)) { throw PathTraversalDetected::forPath($path); } + array_pop($parts); break; diff --git a/vendor/league/mime-type-detection/CHANGELOG.md b/vendor/league/mime-type-detection/CHANGELOG.md index eb13863..b19406f 100644 --- a/vendor/league/mime-type-detection/CHANGELOG.md +++ b/vendor/league/mime-type-detection/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 1.17.0 - 2026-07-09 + +- Updated lookup + ## 1.16.0 - 2025-09-21 - Updated lookup diff --git a/vendor/league/mime-type-detection/composer.json b/vendor/league/mime-type-detection/composer.json index cd75bee..1afe644 100644 --- a/vendor/league/mime-type-detection/composer.json +++ b/vendor/league/mime-type-detection/composer.json @@ -17,7 +17,7 @@ "ext-fileinfo": "*" }, "require-dev": { - "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0", + "phpunit/phpunit": "^8.5.8 || ^9.3 || ^10.0 || ^11.0 || ^12.0", "phpstan/phpstan": "^0.12.68", "friendsofphp/php-cs-fixer": "^3.2" }, diff --git a/vendor/league/mime-type-detection/src/GeneratedExtensionToMimeTypeMap.php b/vendor/league/mime-type-detection/src/GeneratedExtensionToMimeTypeMap.php index 65e986a..7700801 100644 --- a/vendor/league/mime-type-detection/src/GeneratedExtensionToMimeTypeMap.php +++ b/vendor/league/mime-type-detection/src/GeneratedExtensionToMimeTypeMap.php @@ -22,6 +22,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi '7z' => 'application/x-7z-compressed', '7zip' => 'application/x-7z-compressed', '123' => 'application/vnd.lotus-1-2-3', + '210' => 'model/step', 'aab' => 'application/x-authorware-bin', 'aac' => 'audio/acc', 'aam' => 'application/x-authorware-map', @@ -40,7 +41,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'afp' => 'application/vnd.ibm.modcap', 'age' => 'application/vnd.age', 'ahead' => 'application/vnd.ahead.space', - 'ai' => 'application/pdf', + 'ai' => 'application/illustrator', 'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 'aiff' => 'audio/x-aiff', @@ -93,6 +94,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'bh2' => 'application/vnd.fujitsu.oasysprs', 'bin' => 'application/octet-stream', 'blb' => 'application/x-blorb', + 'blend' => 'application/x-blender', 'blorb' => 'application/x-blorb', 'bmi' => 'application/vnd.bmi', 'bmml' => 'application/vnd.balsamiq.bmml+xml', @@ -103,6 +105,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'bpk' => 'application/octet-stream', 'bpmn' => 'application/octet-stream', 'brf' => 'application/braille', + 'brush' => 'application/vnd.procreate.brush', + 'brushset' => 'application/vnd.procreate.brushset', 'bsp' => 'model/vnd.valve.source.compiled-map', 'btf' => 'image/prs.btif', 'btif' => 'image/prs.btif', @@ -202,6 +206,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'davmount' => 'application/davmount+xml', 'dbf' => 'application/vnd.dbf', 'dbk' => 'application/docbook+xml', + 'dcm' => 'application/dicom', + 'dcmp' => 'application/vnd.dcmp+xml', 'dcr' => 'application/x-director', 'dcurl' => 'text/vnd.curl.dcurl', 'dd2' => 'application/vnd.oma.dd2+xml', @@ -229,6 +235,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'dmp' => 'application/vnd.tcpdump.pcap', 'dms' => 'application/octet-stream', 'dna' => 'application/vnd.dna', + 'dng' => 'image/x-adobe-dng', 'doc' => 'application/msword', 'docm' => 'application/vnd.ms-word.template.macroEnabled.12', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', @@ -240,6 +247,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'dpx' => 'image/dpx', 'dra' => 'audio/vnd.dra', 'drle' => 'image/dicom-rle', + 'drm' => 'application/vnd.procreate.dream', 'dsc' => 'text/prs.lines.tag', 'dssc' => 'application/dssc+der', 'dst' => 'application/octet-stream', @@ -294,7 +302,9 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'f4v' => 'video/mp4', 'f77' => 'text/x-fortran', 'f90' => 'text/x-fortran', + 'facti' => 'image/vnd.blockfact.facti', 'fbs' => 'image/vnd.fastbidsheet', + 'fbx' => 'application/vnd.autodesk.fbx', 'fcdt' => 'application/vnd.adobe.formscentral.fcdt', 'fcs' => 'application/vnd.isac.fcs', 'fdf' => 'application/vnd.fdf', @@ -339,18 +349,22 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'gca' => 'application/x-gca-compressed', 'gdl' => 'model/vnd.gdl', 'gdoc' => 'application/vnd.google-apps.document', + 'gdraw' => 'application/vnd.google-apps.drawing', 'ged' => 'text/vnd.familysearch.gedcom', 'geo' => 'application/vnd.dynageo', 'geojson' => 'application/geo+json', 'gex' => 'application/vnd.geometry-explorer', + 'gform' => 'application/vnd.google-apps.form', 'ggb' => 'application/vnd.geogebra.file', 'ggs' => 'application/vnd.geogebra.slides', 'ggt' => 'application/vnd.geogebra.tool', 'ghf' => 'application/vnd.groove-help', 'gif' => 'image/gif', 'gim' => 'application/vnd.groove-identity-message', + 'gjam' => 'application/vnd.google-apps.jam', 'glb' => 'model/gltf-binary', 'gltf' => 'model/gltf+json', + 'gmap' => 'application/vnd.google-apps.map', 'gml' => 'application/gml+xml', 'gmx' => 'application/vnd.gmx', 'gnumeric' => 'application/x-gnumeric', @@ -364,8 +378,10 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'gre' => 'application/vnd.geometry-explorer', 'grv' => 'application/vnd.groove-injector', 'grxml' => 'application/srgs+xml', + 'gscript' => 'application/vnd.google-apps.script', 'gsf' => 'application/x-font-ghostscript', 'gsheet' => 'application/vnd.google-apps.spreadsheet', + 'gsite' => 'application/vnd.google-apps.site', 'gslides' => 'application/vnd.google-apps.presentation', 'gtar' => 'application/x-gtar', 'gtm' => 'application/vnd.groove-tool-message', @@ -397,7 +413,6 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'hpid' => 'application/vnd.hp-hpid', 'hps' => 'application/vnd.hp-hps', 'hqx' => 'application/mac-binhex40', - 'hsj2' => 'image/hsj2', 'htc' => 'text/x-component', 'htke' => 'application/vnd.kenameaapp', 'htm' => 'text/html', @@ -424,6 +439,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'imp' => 'application/vnd.accpac.simply.imp', 'ims' => 'application/vnd.ms-ims', 'in' => 'text/plain', + 'indd' => 'application/x-indesign', 'ini' => 'text/plain', 'ink' => 'application/inkml+xml', 'inkml' => 'application/inkml+xml', @@ -431,6 +447,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'iota' => 'application/vnd.astraea-software.iota', 'ipfix' => 'application/ipfix', 'ipk' => 'application/vnd.shana.informed.package', + 'ipynb' => 'application/x-ipynb+json', 'irm' => 'application/vnd.ibm.rights-management', 'irp' => 'application/vnd.irepository.package+xml', 'iso' => 'application/x-iso9660-image', @@ -440,10 +457,13 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'ivu' => 'application/vnd.immervision-ivu', 'jad' => 'text/vnd.sun.j2me.app-descriptor', 'jade' => 'text/jade', + 'jaii' => 'image/jaii', + 'jais' => 'image/jais', 'jam' => 'application/vnd.jam', 'jar' => 'application/java-archive', 'jardiff' => 'application/x-java-archive-diff', 'java' => 'text/x-java-source', + 'jfif' => 'image/jpeg', 'jhc' => 'image/jphc', 'jisp' => 'application/vnd.jisp', 'jls' => 'image/jls', @@ -479,6 +499,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'jxss' => 'image/jxss', 'kar' => 'audio/midi', 'karbon' => 'application/vnd.kde.karbon', + 'kbl' => 'application/kbl+xml', 'kdb' => 'application/octet-stream', 'kdbx' => 'application/x-keepass2', 'key' => 'application/x-iwork-keynote-sffkey', @@ -515,6 +536,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'lnk' => 'application/x-ms-shortcut', 'log' => 'text/plain', 'lostxml' => 'application/lost+xml', + 'lottie' => 'application/zip+dotlottie', 'lrf' => 'application/octet-stream', 'lrm' => 'application/vnd.ms-lrm', 'ltf' => 'application/vnd.frogans.ltf', @@ -532,6 +554,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'm3u' => 'text/plain', 'm3u8' => 'application/vnd.apple.mpegurl', 'm4a' => 'audio/x-m4a', + 'm4b' => 'audio/mp4', 'm4p' => 'application/mp4', 'm4s' => 'video/iso.segment', 'm4u' => 'application/vnd.mpegurl', @@ -569,6 +592,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'mft' => 'application/rpki-manifest', 'mgp' => 'application/vnd.osgeo.mapguide.package', 'mgz' => 'application/vnd.proteus.magazine', + 'mht' => 'message/rfc822', + 'mhtml' => 'message/rfc822', 'mid' => 'audio/midi', 'midi' => 'audio/midi', 'mie' => 'application/x-mie', @@ -692,6 +717,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'ogv' => 'video/ogg', 'ogx' => 'application/ogg', 'omdoc' => 'application/omdoc+xml', + 'one' => 'application/onenote', + 'onea' => 'application/onenote', 'onepkg' => 'application/onenote', 'onetmp' => 'application/onenote', 'onetoc' => 'application/onenote', @@ -727,8 +754,10 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'p8' => 'application/pkcs8', 'p10' => 'application/x-pkcs10', 'p12' => 'application/x-pkcs12', + 'p21' => 'model/step', 'pac' => 'application/x-ns-proxy-autoconfig', 'pages' => 'application/x-iwork-pages-sffpages', + 'parquet' => 'application/vnd.apache.parquet', 'pas' => 'text/x-pascal', 'paw' => 'application/vnd.pawaafile', 'pbd' => 'application/vnd.powerbuilder6', @@ -942,6 +971,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'spq' => 'application/scvp-vp-request', 'spx' => 'audio/ogg', 'sql' => 'application/x-sql', + 'sqlite' => 'application/vnd.sqlite3', + 'sqlite3' => 'application/vnd.sqlite3', 'src' => 'application/x-wais-source', 'srt' => 'application/x-subrip', 'sru' => 'application/sru+xml', @@ -960,6 +991,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'stk' => 'application/hyperstudio', 'stl' => 'model/stl', 'stp' => 'application/STEP', + 'stpnc' => 'model/step', 'stpx' => 'model/step+xml', 'stpxz' => 'model/step-xml+zip', 'stpz' => 'model/step+zip', @@ -986,6 +1018,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'sxi' => 'application/vnd.sun.xml.impress', 'sxm' => 'application/vnd.sun.xml.math', 'sxw' => 'application/vnd.sun.xml.writer', + 'systemverify' => 'application/vnd.pp.systemverify+xml', 't' => 'text/troff', 't3' => 'application/x-t3vm-image', 't38' => 'image/t38', @@ -1094,6 +1127,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'vcx' => 'application/vnd.vcx', 'vdi' => 'application/x-virtualbox-vdi', 'vds' => 'model/vnd.sap.vds', + 'vdx' => 'application/vnd.ms-visio.viewer', + 'vec' => 'application/vec+xml', 'vhd' => 'application/x-virtualbox-vhd', 'vis' => 'application/vnd.visionary', 'viv' => 'video/vnd.vivo', @@ -1104,6 +1139,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'vox' => 'application/x-authorware-bin', 'vrml' => 'model/vrml', 'vsd' => 'application/vnd.visio', + 'vsdx' => 'application/vnd.visio', 'vsf' => 'application/vnd.vsf', 'vss' => 'application/vnd.visio', 'vst' => 'application/vnd.visio', @@ -1111,6 +1147,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'vtf' => 'image/vnd.valve.source.texture', 'vtt' => 'text/vtt', 'vtu' => 'model/vnd.vtu', + 'vtx' => 'application/vnd.visio', 'vxml' => 'application/voicexml+xml', 'w3d' => 'application/x-director', 'wad' => 'application/x-doom', @@ -1289,6 +1326,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/dash+xml' => ['mpd'], 'application/dash-patch+xml' => ['mpp'], 'application/davmount+xml' => ['davmount'], + 'application/dicom' => ['dcm'], 'application/docbook+xml' => ['dbk'], 'application/dssc+der' => ['dssc'], 'application/dssc+xml' => ['xdssc'], @@ -1318,6 +1356,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/json' => ['json', 'map'], 'application/json5' => ['json5'], 'application/jsonml+json' => ['jsonml'], + 'application/kbl+xml' => ['kbl'], 'application/ld+json' => ['jsonld'], 'application/lgr+xml' => ['lgr'], 'application/lost+xml' => ['lostxml'], @@ -1352,11 +1391,11 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/oebps-package+xml' => ['opf'], 'application/ogg' => ['ogx'], 'application/omdoc+xml' => ['omdoc'], - 'application/onenote' => ['onetoc', 'onetoc2', 'onetmp', 'onepkg'], + 'application/onenote' => ['onetoc', 'onetoc2', 'onetmp', 'onepkg', 'one', 'onea'], 'application/oxps' => ['oxps'], 'application/p2p-overlay+xml' => ['relo'], 'application/patch-ops-error+xml' => ['xer'], - 'application/pdf' => ['pdf', 'ai'], + 'application/pdf' => ['pdf'], 'application/pgp-encrypted' => ['pgp'], 'application/pgp-keys' => ['asc'], 'application/pgp-signature' => ['sig', 'asc'], @@ -1423,6 +1462,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/ubjson' => ['ubj'], 'application/urc-ressheet+xml' => ['rsheet'], 'application/urc-targetdesc+xml' => ['td'], + 'application/vec+xml' => ['vec'], 'application/vnd.1000minds.decision-model+xml' => ['1km'], 'application/vnd.3gpp.pic-bw-large' => ['plb'], 'application/vnd.3gpp.pic-bw-small' => ['psb'], @@ -1449,6 +1489,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/vnd.anser-web-certificate-issue-initiation' => ['cii'], 'application/vnd.anser-web-funds-transfer-initiation' => ['fti'], 'application/vnd.antix.game-component' => ['atx'], + 'application/vnd.apache.parquet' => ['parquet'], 'application/vnd.apple.installer+xml' => ['mpkg'], 'application/vnd.apple.keynote' => ['key'], 'application/vnd.apple.mpegurl' => ['m3u8'], @@ -1458,6 +1499,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/vnd.aristanetworks.swi' => ['swi'], 'application/vnd.astraea-software.iota' => ['iota'], 'application/vnd.audiograph' => ['aep'], + 'application/vnd.autodesk.fbx' => ['fbx'], 'application/vnd.balsamiq.bmml+xml' => ['bmml'], 'application/vnd.blueice.multipass' => ['mpm'], 'application/vnd.bmi' => ['bmi'], @@ -1487,6 +1529,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/vnd.dart' => ['dart'], 'application/vnd.data-vision.rdz' => ['rdz'], 'application/vnd.dbf' => ['dbf'], + 'application/vnd.dcmp+xml' => ['dcmp'], 'application/vnd.dece.data' => ['uvf', 'uvvf', 'uvd', 'uvvd'], 'application/vnd.dece.ttml+xml' => ['uvt', 'uvvt'], 'application/vnd.dece.unspecified' => ['uvx', 'uvvx'], @@ -1538,7 +1581,13 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/vnd.geospace' => ['g3w'], 'application/vnd.gmx' => ['gmx'], 'application/vnd.google-apps.document' => ['gdoc'], + 'application/vnd.google-apps.drawing' => ['gdraw'], + 'application/vnd.google-apps.form' => ['gform'], + 'application/vnd.google-apps.jam' => ['gjam'], + 'application/vnd.google-apps.map' => ['gmap'], 'application/vnd.google-apps.presentation' => ['gslides'], + 'application/vnd.google-apps.script' => ['gscript'], + 'application/vnd.google-apps.site' => ['gsite'], 'application/vnd.google-apps.spreadsheet' => ['gsheet'], 'application/vnd.google-earth.kml+xml' => ['kml'], 'application/vnd.google-earth.kmz' => ['kmz'], @@ -1649,6 +1698,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => ['ppsm'], 'application/vnd.ms-powerpoint.template.macroenabled.12' => ['potm'], 'application/vnd.ms-project' => ['mpp', 'mpt'], + 'application/vnd.ms-visio.viewer' => ['vdx'], 'application/vnd.ms-word.document.macroenabled.12' => ['docm'], 'application/vnd.ms-word.template.macroenabled.12' => ['dotm'], 'application/vnd.ms-works' => ['wps', 'wks', 'wcm', 'wdb'], @@ -1713,7 +1763,11 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/vnd.pmi.widget' => ['wg'], 'application/vnd.pocketlearn' => ['plf'], 'application/vnd.powerbuilder6' => ['pbd'], + 'application/vnd.pp.systemverify+xml' => ['systemverify'], 'application/vnd.previewsystems.box' => ['box'], + 'application/vnd.procreate.brush' => ['brush'], + 'application/vnd.procreate.brushset' => ['brushset'], + 'application/vnd.procreate.dream' => ['drm'], 'application/vnd.proteus.magazine' => ['mgz'], 'application/vnd.publishare-delta-tree' => ['qps'], 'application/vnd.pvi.ptid1' => ['ptid'], @@ -1744,6 +1798,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/vnd.solent.sdkm+xml' => ['sdkm', 'sdkd'], 'application/vnd.spotfire.dxp' => ['dxp'], 'application/vnd.spotfire.sfs' => ['sfs'], + 'application/vnd.sqlite3' => ['sqlite', 'sqlite3'], 'application/vnd.stardivision.calc' => ['sdc'], 'application/vnd.stardivision.draw' => ['sda'], 'application/vnd.stardivision.impress' => ['sdd'], @@ -1782,7 +1837,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/vnd.unity' => ['unityweb'], 'application/vnd.uoml+xml' => ['uoml', 'uo'], 'application/vnd.vcx' => ['vcx'], - 'application/vnd.visio' => ['vsd', 'vst', 'vss', 'vsw'], + 'application/vnd.visio' => ['vsd', 'vst', 'vss', 'vsw', 'vsdx', 'vtx'], 'application/vnd.visionary' => ['vis'], 'application/vnd.vsf' => ['vsf'], 'application/vnd.wap.wbxml' => ['wbxml'], @@ -1823,6 +1878,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/x-bcpio' => ['bcpio'], 'application/x-bdoc' => ['bdoc'], 'application/x-bittorrent' => ['torrent'], + 'application/x-blender' => ['blend'], 'application/x-blorb' => ['blb', 'blorb'], 'application/x-bzip' => ['bz'], 'application/x-bzip2' => ['bz2', 'boz'], @@ -1833,6 +1889,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/x-chess-pgn' => ['pgn'], 'application/x-chrome-extension' => ['crx'], 'application/x-cocoa' => ['cco'], + 'application/x-compressed' => ['rar'], 'application/x-conference' => ['nsc'], 'application/x-cpio' => ['cpio'], 'application/x-csh' => ['csh'], @@ -1862,6 +1919,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/x-hdf' => ['hdf'], 'application/x-httpd-php' => ['php', 'php4', 'php3', 'phtml'], 'application/x-install-instructions' => ['install'], + 'application/x-ipynb+json' => ['ipynb'], 'application/x-iso9660-image' => ['iso'], 'application/x-iwork-keynote-sffkey' => ['key'], 'application/x-iwork-numbers-sffnumbers' => ['numbers'], @@ -1939,6 +1997,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/x-xliff+xml' => ['xlf'], 'application/x-xpinstall' => ['xpi'], 'application/x-xz' => ['xz'], + 'application/x-zip-compressed' => ['zip'], 'application/x-zmachine' => ['z1', 'z2', 'z3', 'z4', 'z5', 'z6', 'z7', 'z8'], 'application/xaml+xml' => ['xaml'], 'application/xcap-att+xml' => ['xav'], @@ -1960,15 +2019,17 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'application/yang' => ['yang'], 'application/yin+xml' => ['yin'], 'application/zip' => ['zip'], + 'application/zip+dotlottie' => ['lottie'], 'audio/3gpp' => ['3gpp'], 'audio/aac' => ['adts', 'aac'], 'audio/adpcm' => ['adp'], 'audio/amr' => ['amr'], 'audio/basic' => ['au', 'snd'], + 'audio/matroska' => ['mka'], 'audio/midi' => ['mid', 'midi', 'kar', 'rmi'], 'audio/mobile-xmf' => ['mxmf'], 'audio/mp3' => ['mp3'], - 'audio/mp4' => ['m4a', 'mp4a'], + 'audio/mp4' => ['m4a', 'mp4a', 'm4b'], 'audio/mpeg' => ['mpga', 'mp2', 'mp2a', 'mp3', 'm2a', 'm3a'], 'audio/ogg' => ['oga', 'ogg', 'spx', 'opus'], 'audio/s3m' => ['s3m'], @@ -2030,11 +2091,12 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'image/heif' => ['heif'], 'image/heif-sequence' => ['heifs'], 'image/hej2k' => ['hej2'], - 'image/hsj2' => ['hsj2'], 'image/ief' => ['ief'], + 'image/jaii' => ['jaii'], + 'image/jais' => ['jais'], 'image/jls' => ['jls'], 'image/jp2' => ['jp2', 'jpg2'], - 'image/jpeg' => ['jpeg', 'jpg', 'jpe'], + 'image/jpeg' => ['jpg', 'jpeg', 'jpe', 'jfif'], 'image/jph' => ['jph'], 'image/jphc' => ['jhc'], 'image/jpm' => ['jpm', 'jpgm'], @@ -2049,6 +2111,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'image/jxss' => ['jxss'], 'image/ktx' => ['ktx'], 'image/ktx2' => ['ktx2'], + 'image/pjpeg' => ['jfif'], 'image/png' => ['png'], 'image/prs.btif' => ['btif', 'btf'], 'image/prs.pti' => ['pti'], @@ -2059,6 +2122,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'image/tiff-fx' => ['tfx'], 'image/vnd.adobe.photoshop' => ['psd'], 'image/vnd.airzip.accelerator.azv' => ['azv'], + 'image/vnd.blockfact.facti' => ['facti'], 'image/vnd.dece.graphic' => ['uvi', 'uvvi', 'uvg', 'uvvg'], 'image/vnd.djvu' => ['djvu', 'djv'], 'image/vnd.dvb.subtitle' => ['sub'], @@ -2083,6 +2147,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'image/webp' => ['webp'], 'image/wmf' => ['wmf'], 'image/x-3ds' => ['3ds'], + 'image/x-adobe-dng' => ['dng'], 'image/x-cmu-raster' => ['ras'], 'image/x-cmx' => ['cmx'], 'image/x-freehand' => ['fh', 'fhc', 'fh4', 'fh5', 'fh7'], @@ -2106,7 +2171,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'message/global-delivery-status' => ['u8dsn'], 'message/global-disposition-notification' => ['u8mdn'], 'message/global-headers' => ['u8hdr'], - 'message/rfc822' => ['eml', 'mime'], + 'message/rfc822' => ['eml', 'mime', 'mht', 'mhtml'], 'message/vnd.wfa.wsc' => ['wsc'], 'model/3mf' => ['3mf'], 'model/gltf+json' => ['gltf'], @@ -2117,6 +2182,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'model/mtl' => ['mtl'], 'model/obj' => ['obj'], 'model/prc' => ['prc'], + 'model/step' => ['step', 'stp', 'stpnc', 'p21', '210'], 'model/step+xml' => ['stpx'], 'model/step+zip' => ['stpz'], 'model/step-xml+zip' => ['stpxz'], @@ -2200,6 +2266,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'text/x-opml' => ['opml'], 'text/x-org' => ['org'], 'text/x-pascal' => ['p', 'pas'], + 'text/x-php' => ['php'], 'text/x-processing' => ['pde'], 'text/x-sass' => ['sass'], 'text/x-scss' => ['scss'], @@ -2219,6 +2286,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'video/iso.segment' => ['m4s'], 'video/jpeg' => ['jpgv'], 'video/jpm' => ['jpm', 'jpgm'], + 'video/matroska' => ['mkv'], + 'video/matroska-3d' => ['mk3d'], 'video/mj2' => ['mj2', 'mjp2'], 'video/mp2t' => ['ts', 'm2t', 'm2ts', 'mts'], 'video/mp4' => ['mp4', 'mp4v', 'mpg4', 'f4v'], @@ -2254,6 +2323,8 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap, Extensi 'video/x-smv' => ['smv'], 'x-conference/x-cooltalk' => ['ice'], 'application/x-photoshop' => ['psd'], + 'application/x-indesign' => ['indd'], + 'application/illustrator' => ['ai'], 'application/smil' => ['smi', 'smil'], 'application/powerpoint' => ['ppt'], 'application/vnd.ms-powerpoint.addin.macroEnabled.12' => ['ppam'],