From 61bbfa643160cbf118ef0b029b490eca92d5d8e7 Mon Sep 17 00:00:00 2001 From: joetannenbaum <2702148+joetannenbaum@users.noreply.github.com> Date: Tue, 13 May 2025 22:19:54 +0000 Subject: [PATCH 01/13] Update CHANGELOG --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d46a4aa3..78a2f3f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Release Notes -## [Unreleased](https://github.com/laravel/echo/compare/v2.1.1...2.x) +## [Unreleased](https://github.com/laravel/echo/compare/v2.1.3...2.x) + +## [v2.1.3](https://github.com/laravel/echo/compare/v2.1.1...v2.1.3) - 2025-05-13 + +### What's Changed + +* Fix Reverb app key env name by [@joetannenbaum](https://github.com/joetannenbaum) in https://github.com/laravel/echo/pull/431 + +**Full Changelog**: https://github.com/laravel/echo/compare/v2.1.2...v2.1.3 ## [v2.1.1](https://github.com/laravel/echo/compare/v2.1.0...v2.1.1) - 2025-05-13 From f4f1d76cbc89efc7c802f7b7273a1fdaaea63af9 Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 14 May 2025 10:09:54 -0400 Subject: [PATCH 02/13] fix window types --- packages/laravel-echo/src/connector/connector.ts | 8 +++----- packages/laravel-echo/tsconfig.json | 2 +- packages/laravel-echo/typings/window.d.ts | 4 +--- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/laravel-echo/src/connector/connector.ts b/packages/laravel-echo/src/connector/connector.ts index 3eb2b50d..1741d997 100644 --- a/packages/laravel-echo/src/connector/connector.ts +++ b/packages/laravel-echo/src/connector/connector.ts @@ -1,3 +1,5 @@ +/// + import type { Channel, PresenceChannel } from "../channel"; import type { BroadcastDriver, EchoOptions } from "../echo"; @@ -90,11 +92,7 @@ export abstract class Connector< protected csrfToken(): null | string { let selector; - if ( - typeof window !== "undefined" && - typeof window.Laravel !== "undefined" && - window.Laravel.csrfToken - ) { + if (window?.Laravel?.csrfToken) { return window.Laravel.csrfToken; } diff --git a/packages/laravel-echo/tsconfig.json b/packages/laravel-echo/tsconfig.json index e96a3f49..f53d21f5 100644 --- a/packages/laravel-echo/tsconfig.json +++ b/packages/laravel-echo/tsconfig.json @@ -18,7 +18,7 @@ "emitDecoratorMetadata": true, "esModuleInterop": true, "strict": true, - "typeRoots": ["node_modules/@types"], + "typeRoots": ["node_modules/@types", "./typings"], "lib": ["dom", "es2020"] }, "include": ["./typings/**/*.ts", "./src/**/*.ts"], diff --git a/packages/laravel-echo/typings/window.d.ts b/packages/laravel-echo/typings/window.d.ts index b84c1f77..dcf43166 100644 --- a/packages/laravel-echo/typings/window.d.ts +++ b/packages/laravel-echo/typings/window.d.ts @@ -1,7 +1,5 @@ -import type { io } from "socket.io-client"; import type Pusher from "pusher-js"; - -export {}; +import type { io } from "socket.io-client"; declare global { interface Window { From cdd27d883ad01a7a61e69e1fbf69eae2b765e7aa Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 14 May 2025 10:49:16 -0400 Subject: [PATCH 03/13] Update connector.ts --- .../laravel-echo/src/connector/connector.ts | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/packages/laravel-echo/src/connector/connector.ts b/packages/laravel-echo/src/connector/connector.ts index 1741d997..a4673b7f 100644 --- a/packages/laravel-echo/src/connector/connector.ts +++ b/packages/laravel-echo/src/connector/connector.ts @@ -90,25 +90,14 @@ export abstract class Connector< * Extract the CSRF token from the page. */ protected csrfToken(): null | string { - let selector; - - if (window?.Laravel?.csrfToken) { - return window.Laravel.csrfToken; - } - - if (this.options.csrfToken) { - return this.options.csrfToken; - } - - if ( - typeof document !== "undefined" && - typeof document.querySelector === "function" && - (selector = document.querySelector('meta[name="csrf-token"]')) - ) { - return selector.getAttribute("content"); - } - - return null; + return ( + window?.Laravel?.csrfToken ?? + this.options.csrfToken ?? + document + ?.querySelector('meta[name="csrf-token"]') + ?.getAttribute("content") ?? + null + ); } /** From 485c58e7e64a3f320c13d3f24ae26bbe9652bad8 Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 14 May 2025 10:49:27 -0400 Subject: [PATCH 04/13] Update package.json --- packages/react/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/package.json b/packages/react/package.json index d157b30e..02b861be 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -8,7 +8,7 @@ "ably", "react" ], - "homepage": "https://github.com/laravel/echotree/2.x/packages/react", + "homepage": "https://github.com/laravel/echo/tree/2.x/packages/react", "repository": { "type": "git", "url": "https://github.com/laravel/echo" From 2cdc569fa37af9435ea2e4c8d95e051cdb40cc8b Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 14 May 2025 10:49:38 -0400 Subject: [PATCH 05/13] window and global type fixing --- packages/laravel-echo/package.json | 5 +- .../src/connector/pusher-connector.ts | 2 +- packages/laravel-echo/typings/window.d.ts | 14 ++- pnpm-lock.yaml | 103 ++++++++++++++++++ 4 files changed, 121 insertions(+), 3 deletions(-) diff --git a/packages/laravel-echo/package.json b/packages/laravel-echo/package.json index e20582dc..ceab71f9 100644 --- a/packages/laravel-echo/package.json +++ b/packages/laravel-echo/package.json @@ -37,9 +37,11 @@ "@babel/plugin-transform-numeric-separator": "^7.25.9", "@babel/plugin-transform-object-assign": "^7.25.9", "@babel/preset-env": "^7.26.7", + "@types/jquery": "^3.5.32", "@types/node": "^20.0.0", "@typescript-eslint/eslint-plugin": "^8.21.0", "@typescript-eslint/parser": "^8.21.0", + "axios": "^1.9.0", "eslint": "^9.0.0", "prettier": "^3.5.3", "pusher-js": "^8.0", @@ -48,7 +50,8 @@ "typescript": "^5.7.0", "vite": "^5.0.0", "vite-plugin-dts": "^3.0.0", - "vitest": "^3.1.2" + "vitest": "^3.1.2", + "vue": "^3.5.13" }, "peerDependencies": { "pusher-js": "*", diff --git a/packages/laravel-echo/src/connector/pusher-connector.ts b/packages/laravel-echo/src/connector/pusher-connector.ts index db495de3..6b50b227 100644 --- a/packages/laravel-echo/src/connector/pusher-connector.ts +++ b/packages/laravel-echo/src/connector/pusher-connector.ts @@ -42,7 +42,7 @@ export class PusherConnector< */ channels: Record = {}; - options: PusherOptions; + declare options: PusherOptions; /** * Create a fresh Pusher connection. diff --git a/packages/laravel-echo/typings/window.d.ts b/packages/laravel-echo/typings/window.d.ts index dcf43166..76df6eb1 100644 --- a/packages/laravel-echo/typings/window.d.ts +++ b/packages/laravel-echo/typings/window.d.ts @@ -1,5 +1,8 @@ +import type { JQueryStatic } from "@types/jquery"; +import type { AxiosStatic } from "axios"; import type Pusher from "pusher-js"; import type { io } from "socket.io-client"; +import type { VueElementConstructor } from "vue"; declare global { interface Window { @@ -8,7 +11,16 @@ declare global { }; io?: typeof io; - Pusher?: typeof Pusher; + + Vue?: VueElementConstructor; + axios?: AxiosStatic; + jQuery?: JQueryStatic; + Turbo?: object; } + + const Vue: VueElementConstructor | undefined; + const axios: AxiosStatic | undefined; + const jQuery: JQueryStatic | undefined; + const Turbo: object | undefined; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cdefee7d..5ebcd568 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,9 @@ importers: '@babel/preset-env': specifier: ^7.26.7 version: 7.27.1(@babel/core@7.27.1) + '@types/jquery': + specifier: ^3.5.32 + version: 3.5.32 '@types/node': specifier: ^20.0.0 version: 20.17.32 @@ -43,6 +46,9 @@ importers: '@typescript-eslint/parser': specifier: ^8.21.0 version: 8.31.1(eslint@9.25.1)(typescript@5.8.3) + axios: + specifier: ^1.9.0 + version: 1.9.0 eslint: specifier: ^9.0.0 version: 9.25.1 @@ -70,6 +76,9 @@ importers: vitest: specifier: ^3.1.2 version: 3.1.2(@types/node@20.17.32)(jsdom@26.1.0) + vue: + specifier: ^3.5.13 + version: 3.5.13(typescript@5.8.3) packages/react: devDependencies: @@ -1426,6 +1435,9 @@ packages: '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/jquery@3.5.32': + resolution: {integrity: sha512-b9Xbf4CkMqS02YH8zACqN1xzdxc3cO735Qe5AbSUFmyOiaWAbcpqh9Wna+Uk0vgACvoQHpWDg2rGdHkYPLmCiQ==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1448,6 +1460,9 @@ packages: '@types/react@19.1.2': resolution: {integrity: sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==} + '@types/sizzle@2.3.9': + resolution: {integrity: sha512-xzLEyKB50yqCUPUJkIsrVvoWNfFUbIZI+RspLWt8u+tIW/BetMBZtgV2LY/2o+tYH8dRvQ+eoPf3NdhQCcLE2w==} + '@typescript-eslint/eslint-plugin@8.31.1': resolution: {integrity: sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1679,10 +1694,16 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + axios@1.9.0: + resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} + babel-plugin-polyfill-corejs2@0.4.13: resolution: {integrity: sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==} peerDependencies: @@ -1766,6 +1787,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + commander@10.0.1: resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} engines: {node: '>=14'} @@ -1856,6 +1881,10 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1920,6 +1949,10 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -2036,6 +2069,15 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -2044,6 +2086,10 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + engines: {node: '>= 6'} + fs-extra@11.3.0: resolution: {integrity: sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==} engines: {node: '>=14.14'} @@ -2391,6 +2437,14 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + minimatch@3.0.8: resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} @@ -2544,6 +2598,9 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -4364,6 +4421,10 @@ snapshots: '@types/estree@1.0.7': {} + '@types/jquery@3.5.32': + dependencies: + '@types/sizzle': 2.3.9 + '@types/json-schema@7.0.15': {} '@types/node@20.17.32': @@ -4386,6 +4447,8 @@ snapshots: dependencies: csstype: 3.1.3 + '@types/sizzle@2.3.9': {} + '@typescript-eslint/eslint-plugin@8.31.1(@typescript-eslint/parser@8.31.1(eslint@9.25.1)(typescript@5.8.3))(eslint@9.25.1)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -4701,10 +4764,20 @@ snapshots: assertion-error@2.0.1: {} + asynckit@0.4.0: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 + axios@1.9.0: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.2 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.27.1): dependencies: '@babel/compat-data': 7.27.1 @@ -4804,6 +4877,10 @@ snapshots: color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + commander@10.0.1: {} commander@9.5.0: @@ -4897,6 +4974,8 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + delayed-stream@1.0.0: {} + dequal@2.0.3: {} dom-accessibility-api@0.5.16: {} @@ -4964,6 +5043,13 @@ snapshots: dependencies: es-errors: 1.3.0 + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -5141,6 +5227,8 @@ snapshots: flatted@3.3.3: {} + follow-redirects@1.15.9: {} + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -5150,6 +5238,13 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + form-data@4.0.2: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + mime-types: 2.1.35 + fs-extra@11.3.0: dependencies: graceful-fs: 4.2.11 @@ -5495,6 +5590,12 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + minimatch@3.0.8: dependencies: brace-expansion: 1.1.11 @@ -5638,6 +5739,8 @@ snapshots: proto-list@1.2.4: {} + proxy-from-env@1.1.0: {} + punycode@2.3.1: {} pusher-js@8.4.0: From 8fdbbbe5f7b42c853756a41698c46b2378975043 Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 14 May 2025 11:06:58 -0400 Subject: [PATCH 06/13] type fixes and note to eventually remove vue-resource --- packages/laravel-echo/package.json | 3 +-- packages/laravel-echo/src/echo.ts | 22 +++++++++++++++------- packages/laravel-echo/typings/window.d.ts | 7 +++---- pnpm-lock.yaml | 3 --- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/laravel-echo/package.json b/packages/laravel-echo/package.json index ceab71f9..73dd3ebc 100644 --- a/packages/laravel-echo/package.json +++ b/packages/laravel-echo/package.json @@ -50,8 +50,7 @@ "typescript": "^5.7.0", "vite": "^5.0.0", "vite-plugin-dts": "^3.0.0", - "vitest": "^3.1.2", - "vue": "^3.5.13" + "vitest": "^3.1.2" }, "peerDependencies": { "pusher-js": "*", diff --git a/packages/laravel-echo/src/echo.ts b/packages/laravel-echo/src/echo.ts index 8d6d7f01..43d2ddcd 100644 --- a/packages/laravel-echo/src/echo.ts +++ b/packages/laravel-echo/src/echo.ts @@ -1,3 +1,4 @@ +import type { InternalAxiosRequestConfig } from "axios"; import { Channel, NullChannel, @@ -179,7 +180,9 @@ export default class Echo { * send a connections socket id to a Laravel app with a X-Socket-Id header. */ registerInterceptors(): void { - if (typeof Vue === "function" && Vue.http) { + // TODO: This package is deprecated and we should remove it in a future version. + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (Vue?.http) { this.registerVueRequestInterceptor(); } @@ -200,12 +203,15 @@ export default class Echo { * Register a Vue HTTP interceptor to add the X-Socket-ID header. */ registerVueRequestInterceptor(): void { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call Vue.http.interceptors.push( (request: Record, next: CallableFunction) => { if (this.socketId()) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-call request.headers.set("X-Socket-ID", this.socketId()); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-call next(); }, ); @@ -215,13 +221,15 @@ export default class Echo { * Register an Axios HTTP interceptor to add the X-Socket-ID header. */ registerAxiosRequestInterceptor(): void { - axios.interceptors.request.use((config: Record) => { - if (this.socketId()) { - config.headers["X-Socket-Id"] = this.socketId(); - } + axios!.interceptors.request.use( + (config: InternalAxiosRequestConfig) => { + if (this.socketId()) { + config.headers["X-Socket-Id"] = this.socketId(); + } - return config; - }); + return config; + }, + ); } /** diff --git a/packages/laravel-echo/typings/window.d.ts b/packages/laravel-echo/typings/window.d.ts index 76df6eb1..fdfb41e2 100644 --- a/packages/laravel-echo/typings/window.d.ts +++ b/packages/laravel-echo/typings/window.d.ts @@ -1,8 +1,7 @@ -import type { JQueryStatic } from "@types/jquery"; import type { AxiosStatic } from "axios"; +import type { JQueryStatic } from "jquery"; import type Pusher from "pusher-js"; import type { io } from "socket.io-client"; -import type { VueElementConstructor } from "vue"; declare global { interface Window { @@ -13,13 +12,13 @@ declare global { io?: typeof io; Pusher?: typeof Pusher; - Vue?: VueElementConstructor; + Vue?: any; axios?: AxiosStatic; jQuery?: JQueryStatic; Turbo?: object; } - const Vue: VueElementConstructor | undefined; + const Vue: any | undefined; const axios: AxiosStatic | undefined; const jQuery: JQueryStatic | undefined; const Turbo: object | undefined; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ebcd568..1b08924f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,9 +76,6 @@ importers: vitest: specifier: ^3.1.2 version: 3.1.2(@types/node@20.17.32)(jsdom@26.1.0) - vue: - specifier: ^3.5.13 - version: 3.5.13(typescript@5.8.3) packages/react: devDependencies: From 5cc24d63e83cfbef1b7c58894f716dae1306ac08 Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 14 May 2025 13:07:55 -0400 Subject: [PATCH 07/13] make event and callback optional for Vue useEchoModel --- packages/vue/src/composables/useEcho.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vue/src/composables/useEcho.ts b/packages/vue/src/composables/useEcho.ts index 1768c121..6ac5a9c8 100644 --- a/packages/vue/src/composables/useEcho.ts +++ b/packages/vue/src/composables/useEcho.ts @@ -231,8 +231,8 @@ export const useEchoModel = < >( model: TModel, identifier: string | number, - event: ModelEvents | ModelEvents[], - callback: (payload: ModelPayload) => void, + event: ModelEvents | ModelEvents[] = [], + callback: (payload: ModelPayload) => void = () => {}, dependencies: any[] = [], ) => { return useEcho, TDriver, "private">( From 5750f6632c0fe49625634dd9a22b8fc5f6fd4f57 Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 14 May 2025 13:39:26 -0400 Subject: [PATCH 08/13] fix channel func immediate availabilty --- packages/react/src/hooks/use-echo.ts | 39 ++++++++++--------------- packages/react/src/types.ts | 9 ------ packages/vue/src/composables/useEcho.ts | 27 +++++------------ packages/vue/src/types.ts | 9 ------ 4 files changed, 24 insertions(+), 60 deletions(-) diff --git a/packages/react/src/hooks/use-echo.ts b/packages/react/src/hooks/use-echo.ts index ca0bbd83..a099e7d3 100644 --- a/packages/react/src/hooks/use-echo.ts +++ b/packages/react/src/hooks/use-echo.ts @@ -4,7 +4,6 @@ import { echo } from "../config"; import type { Channel, ChannelData, - ChannelReturnType, Connection, ModelEvents, ModelPayload, @@ -51,7 +50,7 @@ const leaveChannel = (channel: Channel, leaveAll: boolean): void => { const resolveChannelSubscription = ( channel: Channel, -): Connection | void => { +): Connection => { if (channels[channel.id]) { channels[channel.id].count += 1; @@ -60,12 +59,6 @@ const resolveChannelSubscription = ( const channelSubscription = subscribeToChannel(channel); - if (!channelSubscription) { - // eslint-disable-next-line no-console - console.warn(`Failed to subscribe to channel: ${channel.id}`); - return; - } - channels[channel.id] = { count: 1, connection: channelSubscription, @@ -85,11 +78,6 @@ export const useEcho = < dependencies: any[] = [], visibility: TVisibility = "private" as TVisibility, ) => { - const callbackFunc = useCallback(callback, dependencies); - const subscription = useRef | null>(null); - const listening = useRef(false); - - const events = toArray(event); const channel: Channel = { name: channelName, id: ["private", "presence"].includes(visibility) @@ -98,13 +86,22 @@ export const useEcho = < visibility, }; + const callbackFunc = useCallback(callback, dependencies); + const listening = useRef(false); + const initialized = useRef(false); + const subscription = useRef>( + resolveChannelSubscription(channel), + ); + + const events = toArray(event); + const stopListening = useCallback(() => { if (!listening.current) { return; } events.forEach((e) => { - subscription.current!.stopListening(e, callbackFunc); + subscription.current.stopListening(e, callbackFunc); }); listening.current = false; @@ -116,7 +113,7 @@ export const useEcho = < } events.forEach((e) => { - subscription.current!.listen(e, callbackFunc); + subscription.current.listen(e, callbackFunc); }); listening.current = true; @@ -129,14 +126,11 @@ export const useEcho = < }, dependencies); useEffect(() => { - const channelSubscription = - resolveChannelSubscription(channel); - - if (!channelSubscription) { - return; + if (initialized.current) { + subscription.current = resolveChannelSubscription(channel); } - subscription.current = channelSubscription; + initialized.current = true; listen(); @@ -163,8 +157,7 @@ export const useEcho = < /** * Channel instance */ - channel: () => - subscription.current as ChannelReturnType, + channel: () => subscription.current, }; }; diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index 634b6876..a95b8889 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -28,15 +28,6 @@ export type ModelPayload = { afterCommit: boolean; }; -export type ChannelReturnType< - T extends BroadcastDriver, - V extends Channel["visibility"], -> = V extends "presence" - ? Broadcaster[T]["presence"] - : V extends "private" - ? Broadcaster[T]["private"] - : Broadcaster[T]["public"]; - // eslint-disable-next-line @typescript-eslint/no-unused-vars export type ModelName = T extends `${infer _}.${infer U}` ? ModelName diff --git a/packages/vue/src/composables/useEcho.ts b/packages/vue/src/composables/useEcho.ts index 6ac5a9c8..004e1eb6 100644 --- a/packages/vue/src/composables/useEcho.ts +++ b/packages/vue/src/composables/useEcho.ts @@ -4,7 +4,6 @@ import { echo } from "../config"; import type { Channel, ChannelData, - ChannelReturnType, Connection, ModelEvents, ModelPayload, @@ -15,7 +14,7 @@ const channels: Record> = {}; const resolveChannelSubscription = ( channel: Channel, -): Connection | null => { +): Connection => { if (channels[channel.id]) { channels[channel.id].count += 1; @@ -24,12 +23,6 @@ const resolveChannelSubscription = ( const channelSubscription = subscribeToChannel(channel); - if (!channelSubscription) { - // eslint-disable-next-line no-console - console.warn(`Failed to subscribe to channel: ${channel.id}`); - return null; - } - channels[channel.id] = { count: 1, connection: channelSubscription, @@ -95,8 +88,6 @@ export const useEcho = < }, ); - let subscription: Connection | null = null; - const events = Array.isArray(event) ? event : [event]; const channel: Channel = { name: channelName, id: ["private", "presence"].includes(visibility) @@ -105,13 +96,11 @@ export const useEcho = < visibility, }; - const setupSubscription = () => { - subscription = resolveChannelSubscription(channel); - - if (!subscription) { - return; - } + const subscription: Connection = + resolveChannelSubscription(channel); + const events = Array.isArray(event) ? event : [event]; + const setupSubscription = () => { listen(); }; @@ -121,7 +110,7 @@ export const useEcho = < } events.forEach((e) => { - subscription!.listen(e, eventCallback.value); + subscription.listen(e, eventCallback.value); }); listening.value = true; @@ -133,7 +122,7 @@ export const useEcho = < } events.forEach((e) => { - subscription!.stopListening(e, eventCallback.value); + subscription.stopListening(e, eventCallback.value); }); listening.value = false; @@ -184,7 +173,7 @@ export const useEcho = < /** * Channel instance */ - channel: () => subscription! as ChannelReturnType, + channel: () => subscription, }; }; diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index 634b6876..a95b8889 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -28,15 +28,6 @@ export type ModelPayload = { afterCommit: boolean; }; -export type ChannelReturnType< - T extends BroadcastDriver, - V extends Channel["visibility"], -> = V extends "presence" - ? Broadcaster[T]["presence"] - : V extends "private" - ? Broadcaster[T]["private"] - : Broadcaster[T]["public"]; - // eslint-disable-next-line @typescript-eslint/no-unused-vars export type ModelName = T extends `${infer _}.${infer U}` ? ModelName From 9a98c197acb52b93af28e9faeb3b2e52ac472945 Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 14 May 2025 13:53:07 -0400 Subject: [PATCH 09/13] jsdom + withoutInterceptors --- packages/laravel-echo/src/echo.ts | 1 + packages/laravel-echo/tests/echo.test.ts | 61 ++++++++++++++---------- packages/laravel-echo/vite.config.ts | 4 ++ 3 files changed, 41 insertions(+), 25 deletions(-) diff --git a/packages/laravel-echo/src/echo.ts b/packages/laravel-echo/src/echo.ts index 43d2ddcd..9c34830a 100644 --- a/packages/laravel-echo/src/echo.ts +++ b/packages/laravel-echo/src/echo.ts @@ -356,6 +356,7 @@ type GenericOptions = { host?: string | null; key?: string | null; namespace?: string | false; + withoutInterceptors?: boolean; [key: string]: any; }; diff --git a/packages/laravel-echo/tests/echo.test.ts b/packages/laravel-echo/tests/echo.test.ts index b31ffcf0..1dbf1f8b 100644 --- a/packages/laravel-echo/tests/echo.test.ts +++ b/packages/laravel-echo/tests/echo.test.ts @@ -4,36 +4,47 @@ import Echo from "../src/echo"; describe("Echo", () => { test("it will not throw error for supported driver", () => { - expect(() => new Echo({ broadcaster: "reverb" })).not.toThrow( - "Broadcaster string reverb is not supported.", - ); + expect( + () => + new Echo({ broadcaster: "reverb", withoutInterceptors: true }), + ).not.toThrow("Broadcaster string reverb is not supported."); - expect(() => new Echo({ broadcaster: "pusher" })).not.toThrow( - "Broadcaster string pusher is not supported.", - ); + expect( + () => + new Echo({ broadcaster: "pusher", withoutInterceptors: true }), + ).not.toThrow("Broadcaster string pusher is not supported."); - expect(() => new Echo({ broadcaster: "socket.io" })).not.toThrow( - "Broadcaster string socket.io is not supported.", - ); + expect( + () => + new Echo({ + broadcaster: "socket.io", + withoutInterceptors: true, + }), + ).not.toThrow("Broadcaster string socket.io is not supported."); - expect(() => new Echo({ broadcaster: "null" })).not.toThrow( - "Broadcaster string null is not supported.", - ); - expect(() => new Echo({ broadcaster: NullConnector })).not.toThrow(); - - // eslint-disable-next-line - // @ts-ignore - // eslint-disable-next-line @typescript-eslint/no-empty-function - expect(() => new Echo({ broadcaster: () => {} })).not.toThrow( - "Broadcaster function is not supported.", - ); + expect( + () => new Echo({ broadcaster: "null", withoutInterceptors: true }), + ).not.toThrow("Broadcaster string null is not supported."); + expect( + () => + new Echo({ + broadcaster: NullConnector, + withoutInterceptors: true, + }), + ).not.toThrow(); + expect( + () => + // @ts-ignore + // eslint-disable-next-line @typescript-eslint/no-empty-function + new Echo({ broadcaster: () => {}, withoutInterceptors: true }), + ).not.toThrow("Broadcaster function is not supported."); }); test("it will throw error for unsupported driver", () => { - // eslint-disable-next-line - // @ts-ignore - expect(() => new Echo({ broadcaster: "foo" })).toThrow( - "Broadcaster string foo is not supported.", - ); + expect( + // @ts-ignore + // eslint-disable-next-line + () => new Echo({ broadcaster: "foo", withoutInterceptors: true }), + ).toThrow("Broadcaster string foo is not supported."); }); }); diff --git a/packages/laravel-echo/vite.config.ts b/packages/laravel-echo/vite.config.ts index 099ebdc7..f048f2bf 100644 --- a/packages/laravel-echo/vite.config.ts +++ b/packages/laravel-echo/vite.config.ts @@ -52,6 +52,10 @@ const config: UserConfig = (() => { emptyOutDir: true, ...common, }, + test: { + globals: true, + environment: "jsdom", + }, }; })(); From 37a6cd1cb13b03f675d3b358a997949c47a2a2e7 Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 14 May 2025 13:58:19 -0400 Subject: [PATCH 10/13] Update echo.ts --- packages/laravel-echo/src/echo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/laravel-echo/src/echo.ts b/packages/laravel-echo/src/echo.ts index 9c34830a..28503d0e 100644 --- a/packages/laravel-echo/src/echo.ts +++ b/packages/laravel-echo/src/echo.ts @@ -182,7 +182,7 @@ export default class Echo { registerInterceptors(): void { // TODO: This package is deprecated and we should remove it in a future version. // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (Vue?.http) { + if (typeof Vue !== "undefined" && Vue?.http) { this.registerVueRequestInterceptor(); } From 8d93e15f2f00da50e1fe27ee256dec634a8fe368 Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Wed, 14 May 2025 15:16:17 -0400 Subject: [PATCH 11/13] make sure that import.meta.env is defined before using vite var --- packages/react/vite.config.ts | 23 ++++++++++++++++++++++- packages/vue/vite.config.ts | 23 ++++++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/packages/react/vite.config.ts b/packages/react/vite.config.ts index 343dcdbf..bbcffdf6 100644 --- a/packages/react/vite.config.ts +++ b/packages/react/vite.config.ts @@ -1,7 +1,27 @@ import { resolve } from "path"; -import { defineConfig, UserConfig } from "vite"; +import { defineConfig, PluginOption, UserConfig } from "vite"; import dts from "vite-plugin-dts"; +const handleEnvVariablesPlugin = (): PluginOption => { + return { + name: "handle-env-variables-plugin", + generateBundle(options, bundle) { + for (const fileName in bundle) { + const file = bundle[fileName]; + + if (file.type === "chunk" && file.fileName.endsWith(".js")) { + const transformedContent = file.code.replace( + /import\.meta\.env\.VITE_([A-Z0-9_]+)/g, + "(typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_$1 : undefined)", + ); + + file.code = transformedContent; + } + } + }, + }; +}; + const config: UserConfig = (() => { const common: Partial = { rollupOptions: { @@ -40,6 +60,7 @@ const config: UserConfig = (() => { rollupTypes: true, include: ["src/**/*.ts"], }), + handleEnvVariablesPlugin(), ], define: { "import.meta.env.VITE_REVERB_APP_KEY": diff --git a/packages/vue/vite.config.ts b/packages/vue/vite.config.ts index 00f4d5cf..5584f069 100644 --- a/packages/vue/vite.config.ts +++ b/packages/vue/vite.config.ts @@ -1,7 +1,27 @@ import { resolve } from "path"; -import { defineConfig, UserConfig } from "vite"; +import { defineConfig, PluginOption, UserConfig } from "vite"; import dts from "vite-plugin-dts"; +const handleEnvVariablesPlugin = (): PluginOption => { + return { + name: "handle-env-variables-plugin", + generateBundle(options, bundle) { + for (const fileName in bundle) { + const file = bundle[fileName]; + + if (file.type === "chunk" && file.fileName.endsWith(".js")) { + const transformedContent = file.code.replace( + /import\.meta\.env\.VITE_([A-Z0-9_]+)/g, + "(typeof import.meta.env !== 'undefined' ? import.meta.env.VITE_$1 : undefined)", + ); + + file.code = transformedContent; + } + } + }, + }; +}; + const config: UserConfig = (() => { const common: Partial = { rollupOptions: { @@ -40,6 +60,7 @@ const config: UserConfig = (() => { rollupTypes: true, include: ["src/**/*.ts"], }), + handleEnvVariablesPlugin(), ], define: { "import.meta.env.VITE_REVERB_APP_KEY": From 4d4e9ddea368eb2f88b8284123200df6255f0a8e Mon Sep 17 00:00:00 2001 From: Joe Tannenbaum Date: Thu, 15 May 2025 13:42:03 -0400 Subject: [PATCH 12/13] wip --- packages/react/src/hooks/use-echo.ts | 4 +++- packages/react/src/types.ts | 9 +++++++++ packages/vue/src/composables/useEcho.ts | 3 ++- packages/vue/src/types.ts | 9 +++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/react/src/hooks/use-echo.ts b/packages/react/src/hooks/use-echo.ts index a099e7d3..05ce47a0 100644 --- a/packages/react/src/hooks/use-echo.ts +++ b/packages/react/src/hooks/use-echo.ts @@ -4,6 +4,7 @@ import { echo } from "../config"; import type { Channel, ChannelData, + ChannelReturnType, Connection, ModelEvents, ModelPayload, @@ -157,7 +158,8 @@ export const useEcho = < /** * Channel instance */ - channel: () => subscription.current, + channel: () => + subscription.current as ChannelReturnType, }; }; diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index a95b8889..320a7121 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -16,6 +16,15 @@ export type Channel = { visibility: "private" | "public" | "presence"; }; +export type ChannelReturnType< + T extends BroadcastDriver, + V extends Channel["visibility"], +> = V extends "presence" + ? Broadcaster[T]["presence"] + : V extends "private" + ? Broadcaster[T]["private"] + : Broadcaster[T]["public"]; + export type ConfigDefaults = Record< O, Broadcaster[O]["options"] diff --git a/packages/vue/src/composables/useEcho.ts b/packages/vue/src/composables/useEcho.ts index 004e1eb6..db2c9035 100644 --- a/packages/vue/src/composables/useEcho.ts +++ b/packages/vue/src/composables/useEcho.ts @@ -4,6 +4,7 @@ import { echo } from "../config"; import type { Channel, ChannelData, + ChannelReturnType, Connection, ModelEvents, ModelPayload, @@ -173,7 +174,7 @@ export const useEcho = < /** * Channel instance */ - channel: () => subscription, + channel: () => subscription as ChannelReturnType, }; }; diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index a95b8889..320a7121 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -16,6 +16,15 @@ export type Channel = { visibility: "private" | "public" | "presence"; }; +export type ChannelReturnType< + T extends BroadcastDriver, + V extends Channel["visibility"], +> = V extends "presence" + ? Broadcaster[T]["presence"] + : V extends "private" + ? Broadcaster[T]["private"] + : Broadcaster[T]["public"]; + export type ConfigDefaults = Record< O, Broadcaster[O]["options"] From a766620f7266566c168e1c2478f86c1c589f5e50 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 15 May 2025 13:03:12 -0500 Subject: [PATCH 13/13] v2.1.4 --- packages/laravel-echo/package.json | 2 +- packages/react/package.json | 2 +- packages/vue/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/laravel-echo/package.json b/packages/laravel-echo/package.json index 73dd3ebc..bfb1a53f 100644 --- a/packages/laravel-echo/package.json +++ b/packages/laravel-echo/package.json @@ -1,6 +1,6 @@ { "name": "laravel-echo", - "version": "2.1.3", + "version": "2.1.4", "description": "Laravel Echo library for beautiful Pusher and Socket.IO integration", "keywords": [ "laravel", diff --git a/packages/react/package.json b/packages/react/package.json index 02b861be..a80be836 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@laravel/echo-react", - "version": "2.1.3", + "version": "2.1.4", "description": "React hooks for seamless integration with Laravel Echo.", "keywords": [ "laravel", diff --git a/packages/vue/package.json b/packages/vue/package.json index b12b2a31..6176f773 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "@laravel/echo-vue", - "version": "2.1.3", + "version": "2.1.4", "description": "Vue hooks for seamless integration with Laravel Echo.", "keywords": [ "laravel",