laravel-echo-api-gateway 0.1.2 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,12 +7,16 @@ on:
7
7
  jobs:
8
8
  pest:
9
9
  runs-on: ubuntu-latest
10
- name: PestPHP
10
+ strategy:
11
+ fail-fast: true
12
+ matrix:
13
+ php: ['8.1']
14
+ name: Pest - PHP ${{ matrix.php }}
11
15
  steps:
12
- - uses: actions/checkout@v2
16
+ - uses: actions/checkout@v4
13
17
  - uses: shivammathur/setup-php@v2
14
18
  with:
15
- php-version: '8.0'
19
+ php-version: ${{ matrix.php }}
16
20
  extensions: dom, mbstring, soap, bcmath, pdo_sqlite, intl
17
21
  coverage: pcov
18
22
  - uses: ramsey/composer-install@v1
@@ -26,10 +30,10 @@ jobs:
26
30
  AWS_SESSION_TOKEN: fake
27
31
  - name: Upload coverage to Codecov
28
32
  if: always()
29
- uses: codecov/codecov-action@v1
33
+ uses: codecov/codecov-action@v2
30
34
  with:
31
35
  token: ${{ secrets.CODECOV_TOKEN }}
32
- flags: pest
36
+ flags: pest-${{ matrix.php }}
33
37
  file: test-results/pest.xml
34
38
 
35
39
  larastan:
@@ -39,7 +43,7 @@ jobs:
39
43
  - uses: actions/checkout@v2
40
44
  - uses: shivammathur/setup-php@v2
41
45
  with:
42
- php-version: '8.0'
46
+ php-version: ${{ matrix.php }}
43
47
  extensions: soap, bcmath, pdo_sqlite, intl
44
48
  - uses: ramsey/composer-install@v1
45
49
  with:
@@ -76,7 +80,6 @@ jobs:
76
80
  token: ${{ secrets.CODECOV_TOKEN }}
77
81
  flags: jest
78
82
  file: test-results/jest/clover.xml
79
-
80
83
  # phpcs:
81
84
  # runs-on: ubuntu-latest
82
85
  # name: PHP Style Fixer
@@ -10,7 +10,7 @@ jobs:
10
10
  runs-on: ubuntu-latest
11
11
 
12
12
  steps:
13
- - uses: actions/checkout@v2
13
+ - uses: actions/checkout@v4
14
14
  with:
15
15
  ref: ${{ github.event.release.target_commitish }}
16
16
  - name: Use Node.js 12
@@ -24,7 +24,7 @@ jobs:
24
24
  - run: yarn version --new-version ${{ github.event.release.tag_name }} --no-git-tag-version
25
25
  # - run: yarn run build
26
26
  # - run: yarn test
27
- - run: yarn publish --access public --tag latest
27
+ - run: yarn publish --verbose --access public --tag latest
28
28
  env:
29
29
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
30
30
  - run: git add package.json
package/README.md CHANGED
@@ -21,7 +21,7 @@ advised to only use this package for non-critical / non-production projects.
21
21
  In order to use this package, your project needs to meet the following criteria:
22
22
 
23
23
  - PHP 7.4 or 8.x
24
- - Laravel 6, 7 or 8
24
+ - Laravel 6 to 11
25
25
  - Uses either [bref](https://bref.sh) or [Laravel Vapor](https://vapor.laravel.com) to deploy to AWS
26
26
  - Has a working queue
27
27
  - Uses Laravel Mix or any other tool to bundle your assets
@@ -120,6 +120,7 @@ provider:
120
120
 
121
121
  environment:
122
122
  # Add these variables
123
+ # Please note : in Laravel 11, this setting is now BROADCAST_CONNECTION
123
124
  BROADCAST_DRIVER: laravel-echo-api-gateway
124
125
  LARAVEL_ECHO_API_GATEWAY_DYNAMODB_TABLE: !Ref ConnectionsTable
125
126
  LARAVEL_ECHO_API_GATEWAY_API_ID: !Ref WebsocketsApi
@@ -196,7 +197,24 @@ window.Echo = new Echo({
196
197
  broadcaster,
197
198
  // replace the placeholders
198
199
  host: 'wss://{api-ip}.execute-api.{region}.amazonaws.com/{stage}',
200
+ authEndpoint: '{auth-url}/broadcasting/auth', // Optional: Use if you have a separate authentication endpoint
201
+ bearerToken: '{token}', // Optional: Use if you need a Bearer Token for authentication
199
202
  });
200
203
  ```
201
204
 
205
+ You can also enable console output by passing a `debug: true` otpion to your window.Echo intializer :
206
+ ```js
207
+ import Echo from 'laravel-echo';
208
+ import {broadcaster} from 'laravel-echo-api-gateway';
209
+
210
+ window.Echo = new Echo({
211
+ broadcaster,
212
+ // replace the placeholders
213
+ host: 'wss://{api-ip}.execute-api.{region}.amazonaws.com/{stage}',
214
+ debug: true
215
+ });
216
+ ```
217
+
218
+
219
+
202
220
  Lastly, you have to generate your assets by running Laravel Mix. After this step, you should be up and running.
@@ -2,6 +2,9 @@ import { Channel } from "./Channel";
2
2
  export declare type Options = {
3
3
  authEndpoint: string;
4
4
  host: string;
5
+ bearerToken: string;
6
+ auth: any;
7
+ debug: boolean;
5
8
  };
6
9
  export declare type MessageBody = {
7
10
  event: string;
@@ -16,7 +19,10 @@ export declare class Websocket {
16
19
  private internalListeners;
17
20
  private channelBacklog;
18
21
  private socketId;
22
+ private closing;
23
+ private hasConnected;
19
24
  private pingInterval;
25
+ private connect;
20
26
  constructor(options: Options);
21
27
  protected parseMessage(body: string): MessageBody;
22
28
  getSocketId(): string;
@@ -21,20 +21,44 @@ function _defineProperties(target, props) {
21
21
  function _createClass(Constructor, protoProps, staticProps) {
22
22
  if (protoProps) _defineProperties(Constructor.prototype, protoProps);
23
23
  if (staticProps) _defineProperties(Constructor, staticProps);
24
+ Object.defineProperty(Constructor, "prototype", {
25
+ writable: false
26
+ });
24
27
  return Constructor;
25
28
  }
26
29
 
30
+ function _extends() {
31
+ _extends = Object.assign || function (target) {
32
+ for (var i = 1; i < arguments.length; i++) {
33
+ var source = arguments[i];
34
+
35
+ for (var key in source) {
36
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
37
+ target[key] = source[key];
38
+ }
39
+ }
40
+ }
41
+
42
+ return target;
43
+ };
44
+
45
+ return _extends.apply(this, arguments);
46
+ }
47
+
27
48
  function _inherits(subClass, superClass) {
28
49
  if (typeof superClass !== "function" && superClass !== null) {
29
50
  throw new TypeError("Super expression must either be null or a function");
30
51
  }
31
52
 
32
- subClass.prototype = Object.create(superClass && superClass.prototype, {
33
- constructor: {
34
- value: subClass,
35
- writable: true,
36
- configurable: true
37
- }
53
+ Object.defineProperty(subClass, "prototype", {
54
+ value: Object.create(superClass && superClass.prototype, {
55
+ constructor: {
56
+ value: subClass,
57
+ writable: true,
58
+ configurable: true
59
+ }
60
+ }),
61
+ writable: false
38
62
  });
39
63
  if (superClass) _setPrototypeOf(subClass, superClass);
40
64
  }
@@ -61,7 +85,7 @@ function _isNativeReflectConstruct() {
61
85
  if (typeof Proxy === "function") return true;
62
86
 
63
87
  try {
64
- Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
88
+ Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
65
89
  return true;
66
90
  } catch (e) {
67
91
  return false;
@@ -79,6 +103,8 @@ function _assertThisInitialized(self) {
79
103
  function _possibleConstructorReturn(self, call) {
80
104
  if (call && (typeof call === "object" || typeof call === "function")) {
81
105
  return call;
106
+ } else if (call !== void 0) {
107
+ throw new TypeError("Derived constructors may only return object or undefined");
82
108
  }
83
109
 
84
110
  return _assertThisInitialized(self);
@@ -103,6 +129,80 @@ function _createSuper(Derived) {
103
129
  };
104
130
  }
105
131
 
132
+ function _unsupportedIterableToArray(o, minLen) {
133
+ if (!o) return;
134
+ if (typeof o === "string") return _arrayLikeToArray(o, minLen);
135
+ var n = Object.prototype.toString.call(o).slice(8, -1);
136
+ if (n === "Object" && o.constructor) n = o.constructor.name;
137
+ if (n === "Map" || n === "Set") return Array.from(o);
138
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
139
+ }
140
+
141
+ function _arrayLikeToArray(arr, len) {
142
+ if (len == null || len > arr.length) len = arr.length;
143
+
144
+ for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
145
+
146
+ return arr2;
147
+ }
148
+
149
+ function _createForOfIteratorHelper(o, allowArrayLike) {
150
+ var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
151
+
152
+ if (!it) {
153
+ if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
154
+ if (it) o = it;
155
+ var i = 0;
156
+
157
+ var F = function () {};
158
+
159
+ return {
160
+ s: F,
161
+ n: function () {
162
+ if (i >= o.length) return {
163
+ done: true
164
+ };
165
+ return {
166
+ done: false,
167
+ value: o[i++]
168
+ };
169
+ },
170
+ e: function (e) {
171
+ throw e;
172
+ },
173
+ f: F
174
+ };
175
+ }
176
+
177
+ throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
178
+ }
179
+
180
+ var normalCompletion = true,
181
+ didErr = false,
182
+ err;
183
+ return {
184
+ s: function () {
185
+ it = it.call(o);
186
+ },
187
+ n: function () {
188
+ var step = it.next();
189
+ normalCompletion = step.done;
190
+ return step;
191
+ },
192
+ e: function (e) {
193
+ didErr = true;
194
+ err = e;
195
+ },
196
+ f: function () {
197
+ try {
198
+ if (!normalCompletion && it.return != null) it.return();
199
+ } finally {
200
+ if (didErr) throw err;
201
+ }
202
+ }
203
+ };
204
+ }
205
+
106
206
  class Connector {
107
207
  /**
108
208
  * Create a new class instance.
@@ -157,83 +257,136 @@ class Connector {
157
257
 
158
258
  var Websocket = /*#__PURE__*/function () {
159
259
  function Websocket(options) {
160
- var _this = this;
161
-
162
260
  _classCallCheck(this, Websocket);
163
261
 
164
262
  this.buffer = [];
165
263
  this.listeners = {};
166
264
  this.internalListeners = {};
167
265
  this.channelBacklog = [];
266
+ this.closing = false;
267
+ this.hasConnected = false;
168
268
  this.options = options;
169
- this.websocket = new WebSocket(options.host);
269
+ this.connect(this.options.host);
270
+ return this;
271
+ }
170
272
 
171
- this.websocket.onopen = function () {
172
- while (_this.buffer.length) {
173
- var message = _this.buffer[0];
273
+ _createClass(Websocket, [{
274
+ key: "connect",
275
+ value: function connect(host) {
276
+ var _this = this;
174
277
 
175
- _this.send(message);
278
+ this.options.debug && console.log('Trying to connect...');
279
+ this.websocket = new WebSocket(host);
176
280
 
177
- _this.buffer.splice(0, 1);
178
- }
179
- };
281
+ this.websocket.onerror = function () {
282
+ if (!_this.hasConnected) {
283
+ setTimeout(function () {
284
+ _this.socketId = undefined;
180
285
 
181
- this.websocket.onmessage = function (messageEvent) {
182
- var message = _this.parseMessage(messageEvent.data);
286
+ _this.connect(host);
287
+ }, 3000);
288
+ }
289
+ };
183
290
 
184
- if (!message) {
185
- return;
186
- }
291
+ this.websocket.onopen = function () {
292
+ _this.options.debug && console.log('Connected !');
293
+ _this.hasConnected = true;
187
294
 
188
- if (message.channel) {
189
- console.log("Received event ".concat(message.event, " on channel ").concat(message.channel));
295
+ _this.send({
296
+ event: 'whoami'
297
+ });
190
298
 
191
- if (_this.listeners[message.channel] && _this.listeners[message.channel][message.event]) {
192
- _this.listeners[message.channel][message.event](message.data);
193
- }
299
+ while (_this.buffer.length) {
300
+ var message = _this.buffer[0];
194
301
 
195
- return;
196
- }
302
+ _this.send(message);
197
303
 
198
- if (_this.internalListeners[message.event]) {
199
- _this.internalListeners[message.event](message.data);
200
- }
201
- };
304
+ _this.buffer.splice(0, 1);
305
+ } // Register events only once connected, or they won't be registered if connection failed/lost
202
306
 
203
- this.on('whoami', function (_ref) {
204
- var socketId = _ref.socket_id;
205
- _this.socketId = socketId;
206
- console.log("just set socketId to ".concat(socketId));
207
307
 
208
- while (_this.channelBacklog.length) {
209
- var channel = _this.channelBacklog[0];
308
+ _this.websocket.onmessage = function (messageEvent) {
309
+ var message = _this.parseMessage(messageEvent.data);
210
310
 
211
- _this.actuallySubscribe(channel);
311
+ _this.options.debug && console.log('onmessage', messageEvent.data);
212
312
 
213
- _this.channelBacklog.splice(0, 1);
214
- }
215
- });
216
- this.send({
217
- event: 'whoami'
218
- }); // send ping every 60 seconds to keep connection alive
313
+ if (!message) {
314
+ return;
315
+ }
219
316
 
220
- this.pingInterval = setInterval(function () {
221
- console.log('Sending ping');
317
+ if (message.channel) {
318
+ _this.options.debug && console.log("Received event ".concat(message.event, " on channel ").concat(message.channel));
222
319
 
223
- _this.send({
224
- event: 'ping'
225
- });
226
- }, 60 * 1000);
227
- return this;
228
- }
320
+ if (_this.listeners[message.channel] && _this.listeners[message.channel][message.event]) {
321
+ _this.listeners[message.channel][message.event](message.data);
322
+ }
229
323
 
230
- _createClass(Websocket, [{
324
+ return;
325
+ }
326
+
327
+ if (_this.internalListeners[message.event]) {
328
+ _this.internalListeners[message.event](message.data);
329
+ }
330
+ }; // send ping every 60 seconds to keep connection alive
331
+
332
+
333
+ _this.pingInterval = setInterval(function () {
334
+ if (_this.websocket.readyState === _this.websocket.OPEN) {
335
+ _this.options.debug && console.log('Sending ping');
336
+
337
+ _this.send({
338
+ event: 'ping'
339
+ });
340
+ }
341
+ }, 60 * 1000);
342
+ };
343
+
344
+ this.websocket.onclose = function () {
345
+ _this.options.debug && console.info('Connection closed.');
346
+
347
+ if (_this.closing) {
348
+ return;
349
+ }
350
+
351
+ _this.hasConnected = false;
352
+ _this.options.debug && console.info('Connection lost, reconnecting...');
353
+ setTimeout(function () {
354
+ _this.socketId = undefined;
355
+
356
+ _this.connect(host);
357
+ }, 1000);
358
+ };
359
+
360
+ this.on('whoami', function (_ref) {
361
+ var socketId = _ref.socket_id;
362
+ _this.socketId = socketId;
363
+ _this.options.debug && console.log("just set socketId to ".concat(socketId)); // Handle the backlog and don't empty it, we'll need it if we lose connection
364
+
365
+ var channel;
366
+
367
+ var _iterator = _createForOfIteratorHelper(_this.channelBacklog),
368
+ _step;
369
+
370
+ try {
371
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
372
+ channel = _step.value;
373
+
374
+ _this.actuallySubscribe(channel);
375
+ }
376
+ } catch (err) {
377
+ _iterator.e(err);
378
+ } finally {
379
+ _iterator.f();
380
+ }
381
+ });
382
+ }
383
+ }, {
231
384
  key: "parseMessage",
232
385
  value: function parseMessage(body) {
233
386
  try {
234
387
  return JSON.parse(body);
235
388
  } catch (error) {
236
- console.error(error);
389
+ this.options.debug && console.error(error);
237
390
  return undefined;
238
391
  }
239
392
  }
@@ -260,6 +413,7 @@ var Websocket = /*#__PURE__*/function () {
260
413
  }, {
261
414
  key: "close",
262
415
  value: function close() {
416
+ this.closing = true;
263
417
  this.internalListeners = {};
264
418
  clearInterval(this.pingInterval);
265
419
  this.pingInterval = undefined;
@@ -280,26 +434,32 @@ var Websocket = /*#__PURE__*/function () {
280
434
  var _this2 = this;
281
435
 
282
436
  if (channel.name.startsWith('private-') || channel.name.startsWith('presence-')) {
283
- console.log("Sending auth request for channel ".concat(channel.name));
437
+ this.options.debug && console.log("Sending auth request for channel ".concat(channel.name));
438
+
439
+ if (this.options.bearerToken) {
440
+ this.options.auth.headers['Authorization'] = 'Bearer ' + this.options.bearerToken;
441
+ }
442
+
284
443
  axios.post(this.options.authEndpoint, {
285
444
  socket_id: this.getSocketId(),
286
445
  channel_name: channel.name
446
+ }, {
447
+ headers: this.options.auth.headers || {}
287
448
  }).then(function (response) {
288
- console.log("Subscribing to channels ".concat(channel.name));
449
+ _this2.options.debug && console.log("Subscribing to channels ".concat(channel.name));
289
450
 
290
451
  _this2.send({
291
452
  event: 'subscribe',
292
- data: {
293
- channel: channel.name,
294
- auth: response.data.auth
295
- }
453
+ data: _extends({
454
+ channel: channel.name
455
+ }, response.data)
296
456
  });
297
457
  })["catch"](function (error) {
298
- console.log("Auth request for channel ".concat(channel.name, " failed"));
299
- console.error(error);
458
+ _this2.options.debug && console.log("Auth request for channel ".concat(channel.name, " failed"));
459
+ _this2.options.debug && console.error(error);
300
460
  });
301
461
  } else {
302
- console.log("Subscribing to channels ".concat(channel.name));
462
+ this.options.debug && console.log("Subscribing to channels ".concat(channel.name));
303
463
  this.send({
304
464
  event: 'subscribe',
305
465
  data: {
@@ -508,9 +668,12 @@ var Channel$1 = /*#__PURE__*/function (_BaseChannel) {
508
668
  }, {
509
669
  key: "whisper",
510
670
  value: function whisper(event, data) {
671
+ var channel = this.name;
672
+ var formattedEvent = "client-" + event;
511
673
  this.socket.send({
512
- event: event,
513
- data: data
674
+ "event": formattedEvent,
675
+ data: data,
676
+ channel: channel
514
677
  });
515
678
  return this;
516
679
  }