phoenix_live_view 1.1.29 → 1.1.31
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.
- package/assets/js/phoenix_live_view/entry_uploader.js +1 -1
- package/assets/js/phoenix_live_view/hooks.js +1 -1
- package/assets/js/phoenix_live_view/js_commands.ts +3 -0
- package/assets/js/phoenix_live_view/utils.js +24 -0
- package/assets/js/phoenix_live_view/view_hook.ts +45 -17
- package/assets/js/types/utils.d.ts +1 -0
- package/assets/js/types/view_hook.d.ts +43 -15
- package/package.json +1 -1
- package/priv/static/phoenix_live_view.cjs.js +20 -3
- package/priv/static/phoenix_live_view.cjs.js.map +2 -2
- package/priv/static/phoenix_live_view.esm.js +20 -3
- package/priv/static/phoenix_live_view.esm.js.map +2 -2
- package/priv/static/phoenix_live_view.js +20 -3
- package/priv/static/phoenix_live_view.min.js +6 -6
|
@@ -264,7 +264,7 @@ Hooks.InfiniteScroll = {
|
|
|
264
264
|
updated() {
|
|
265
265
|
// Check if the scroll container still exists
|
|
266
266
|
// https://github.com/phoenixframework/phoenix_live_view/issues/4169.
|
|
267
|
-
if (!this.scrollContainer.isConnected) {
|
|
267
|
+
if (this.scrollContainer && !this.scrollContainer.isConnected) {
|
|
268
268
|
this.destroyed();
|
|
269
269
|
this.mounted();
|
|
270
270
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import JS from "./js";
|
|
2
2
|
import LiveSocket from "./live_socket";
|
|
3
|
+
import { ensureSameOrigin } from "./utils";
|
|
3
4
|
|
|
4
5
|
type Transition = string | string[];
|
|
5
6
|
|
|
@@ -346,6 +347,7 @@ export default (
|
|
|
346
347
|
});
|
|
347
348
|
},
|
|
348
349
|
navigate(href, opts = {}) {
|
|
350
|
+
ensureSameOrigin(href, "navigate");
|
|
349
351
|
const customEvent = new CustomEvent("phx:exec");
|
|
350
352
|
liveSocket.historyRedirect(
|
|
351
353
|
customEvent,
|
|
@@ -356,6 +358,7 @@ export default (
|
|
|
356
358
|
);
|
|
357
359
|
},
|
|
358
360
|
patch(href, opts = {}) {
|
|
361
|
+
ensureSameOrigin(href, "patch");
|
|
359
362
|
const customEvent = new CustomEvent("phx:exec");
|
|
360
363
|
liveSocket.pushHistoryPatch(
|
|
361
364
|
customEvent,
|
|
@@ -4,6 +4,30 @@ import EntryUploader from "./entry_uploader";
|
|
|
4
4
|
|
|
5
5
|
export const logError = (msg, obj) => console.error && console.error(msg, obj);
|
|
6
6
|
|
|
7
|
+
// Live navigation can only stay within the current origin, as it joins the
|
|
8
|
+
// target over the existing socket. A full URL to a different origin (or a
|
|
9
|
+
// non-http(s) scheme, which resolves to an opaque "null" origin) is a
|
|
10
|
+
// programming error, so we fail loudly instead of attempting a broken join.
|
|
11
|
+
export const ensureSameOrigin = (
|
|
12
|
+
href,
|
|
13
|
+
kind,
|
|
14
|
+
) => {
|
|
15
|
+
let url;
|
|
16
|
+
try {
|
|
17
|
+
url = new URL(href, window.location.href);
|
|
18
|
+
} catch {
|
|
19
|
+
throw new Error(
|
|
20
|
+
`expected ${kind} destination to be a valid URL, got: ${href}`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
if (url.origin !== window.location.origin) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`cannot ${kind} to "${href}" because its origin does not match the ` +
|
|
26
|
+
`current origin "${window.location.origin}". Use window.location directly for cross-origin navigation.`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
7
31
|
export const isCid = (cid) => {
|
|
8
32
|
const type = typeof cid;
|
|
9
33
|
return type === "number" || (type === "string" && /^(0|[1-9]\d*)$/.test(cid));
|
|
@@ -73,38 +73,45 @@ export interface HookInterface<E extends HTMLElement = HTMLElement> {
|
|
|
73
73
|
js(): HookJSCommands;
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* Pushes an event to the server.
|
|
76
|
+
* Pushes an event to the server and invokes a callback with the server's reply.
|
|
77
77
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
* @param [onReply] - A callback to handle the server's reply.
|
|
78
|
+
* **Note:** this version silently ignores push errors.
|
|
79
|
+
* Use the {@link pushEvent | promise-returning version} to handle errors.
|
|
81
80
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
81
|
+
* @param event - The event name.
|
|
82
|
+
* @param payload - The payload to send to the server. Defaults to an empty object.
|
|
83
|
+
* @param onReply - A callback to handle the server's reply.
|
|
84
84
|
*/
|
|
85
85
|
pushEvent(event: string, payload: any, onReply: OnReply): void;
|
|
86
|
+
/**
|
|
87
|
+
* Pushes an event to the server and returns a Promise that resolves with the server's reply.
|
|
88
|
+
*
|
|
89
|
+
* The promise will be rejected in case of errors
|
|
90
|
+
* such as a disconnected state, timeout, or the server rejecting the event.
|
|
91
|
+
*
|
|
92
|
+
* @param event - The event name.
|
|
93
|
+
* @param [payload] - The payload to send to the server. Defaults to an empty object.
|
|
94
|
+
* @returns A promise that fulfills or rejects with the server's reply.
|
|
95
|
+
*/
|
|
86
96
|
pushEvent(event: string, payload?: any): Promise<any>;
|
|
87
97
|
|
|
88
98
|
/**
|
|
89
|
-
*
|
|
99
|
+
* Pushes a targeted event to the server and invokes a callback with the server's reply.
|
|
90
100
|
*
|
|
91
101
|
* It sends the event to the LiveComponent or LiveView the `selectorOrTarget` is defined in,
|
|
92
102
|
* where its value can be either a query selector, an actual DOM element, or a CID (component id)
|
|
93
103
|
* returned by the `@myself` assign.
|
|
94
104
|
*
|
|
95
|
-
* If the
|
|
96
|
-
* even if
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
105
|
+
* If the selector matches multiple elements, the event is sent to all of them,
|
|
106
|
+
* even if they belong to the same LiveComponent or LiveView.
|
|
107
|
+
*
|
|
108
|
+
* **Note:** this version silently ignores push errors.
|
|
109
|
+
* Use the {@link pushEventTo | promise-returning version} to handle errors.
|
|
100
110
|
*
|
|
101
111
|
* @param selectorOrTarget - The selector, element, or CID to target.
|
|
102
112
|
* @param event - The event name.
|
|
103
|
-
* @param
|
|
104
|
-
* @param
|
|
105
|
-
*
|
|
106
|
-
* When onReply is not provided, the method returns a Promise.
|
|
107
|
-
* When onReply is provided, the method returns void.
|
|
113
|
+
* @param payload - The payload to send to the server. Defaults to an empty object.
|
|
114
|
+
* @param onReply - A callback to handle the server's reply.
|
|
108
115
|
*/
|
|
109
116
|
pushEventTo(
|
|
110
117
|
selectorOrTarget: PhxTarget,
|
|
@@ -112,6 +119,27 @@ export interface HookInterface<E extends HTMLElement = HTMLElement> {
|
|
|
112
119
|
payload: object,
|
|
113
120
|
onReply: OnReply,
|
|
114
121
|
): void;
|
|
122
|
+
/**
|
|
123
|
+
* Pushes a targeted event to the server and returns a Promise that resolves with the server's reply.
|
|
124
|
+
*
|
|
125
|
+
* It sends the event to the LiveComponent or LiveView the `selectorOrTarget` is defined in,
|
|
126
|
+
* where its value can be either a query selector, an actual DOM element, or a CID (component id)
|
|
127
|
+
* returned by the `@myself` assign.
|
|
128
|
+
*
|
|
129
|
+
* If the selector matches multiple elements, the event is sent to all of them,
|
|
130
|
+
* even if they belong to the same LiveComponent or LiveView.
|
|
131
|
+
* Because of this, it returns a single promise that matches the return value of
|
|
132
|
+
* [`Promise.allSettled()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled#return_value).
|
|
133
|
+
* Individual fulfilled values are of the format `{ reply, ref }`, where `reply` is the server's reply.
|
|
134
|
+
*
|
|
135
|
+
* The individual promises will be rejected in case of errors
|
|
136
|
+
* such as a disconnected state, timeout, or the server rejecting the event.
|
|
137
|
+
*
|
|
138
|
+
* @param selectorOrTarget - The selector, element, or CID to target.
|
|
139
|
+
* @param event - The event name.
|
|
140
|
+
* @param [payload] - The payload to send to the server. Defaults to an empty object.
|
|
141
|
+
* @returns A promise that resolves when the event has been handled by all targets.
|
|
142
|
+
*/
|
|
115
143
|
pushEventTo(
|
|
116
144
|
selectorOrTarget: PhxTarget,
|
|
117
145
|
event: string,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export function detectDuplicateIds(): void;
|
|
2
2
|
export function detectInvalidStreamInserts(inserts: any): void;
|
|
3
3
|
export function logError(msg: any, obj: any): void;
|
|
4
|
+
export function ensureSameOrigin(href: any, kind: any): void;
|
|
4
5
|
export function isCid(cid: any): boolean;
|
|
5
6
|
export function debug(view: any, kind: any, msg: any, obj: any): void;
|
|
6
7
|
export function closure(val: any): any;
|
|
@@ -59,39 +59,67 @@ export interface HookInterface<E extends HTMLElement = HTMLElement> {
|
|
|
59
59
|
*/
|
|
60
60
|
js(): HookJSCommands;
|
|
61
61
|
/**
|
|
62
|
-
* Pushes an event to the server.
|
|
62
|
+
* Pushes an event to the server and invokes a callback with the server's reply.
|
|
63
63
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* @param [onReply] - A callback to handle the server's reply.
|
|
64
|
+
* **Note:** this version silently ignores push errors.
|
|
65
|
+
* Use the {@link pushEvent | promise-returning version} to handle errors.
|
|
67
66
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
67
|
+
* @param event - The event name.
|
|
68
|
+
* @param payload - The payload to send to the server. Defaults to an empty object.
|
|
69
|
+
* @param onReply - A callback to handle the server's reply.
|
|
70
70
|
*/
|
|
71
71
|
pushEvent(event: string, payload: any, onReply: OnReply): void;
|
|
72
|
+
/**
|
|
73
|
+
* Pushes an event to the server and returns a Promise that resolves with the server's reply.
|
|
74
|
+
*
|
|
75
|
+
* The promise will be rejected in case of errors
|
|
76
|
+
* such as a disconnected state, timeout, or the server rejecting the event.
|
|
77
|
+
*
|
|
78
|
+
* @param event - The event name.
|
|
79
|
+
* @param [payload] - The payload to send to the server. Defaults to an empty object.
|
|
80
|
+
* @returns A promise that fulfills or rejects with the server's reply.
|
|
81
|
+
*/
|
|
72
82
|
pushEvent(event: string, payload?: any): Promise<any>;
|
|
73
83
|
/**
|
|
74
|
-
*
|
|
84
|
+
* Pushes a targeted event to the server and invokes a callback with the server's reply.
|
|
85
|
+
*
|
|
86
|
+
* It sends the event to the LiveComponent or LiveView the `selectorOrTarget` is defined in,
|
|
87
|
+
* where its value can be either a query selector, an actual DOM element, or a CID (component id)
|
|
88
|
+
* returned by the `@myself` assign.
|
|
89
|
+
*
|
|
90
|
+
* If the selector matches multiple elements, the event is sent to all of them,
|
|
91
|
+
* even if they belong to the same LiveComponent or LiveView.
|
|
92
|
+
*
|
|
93
|
+
* **Note:** this version silently ignores push errors.
|
|
94
|
+
* Use the {@link pushEventTo | promise-returning version} to handle errors.
|
|
95
|
+
*
|
|
96
|
+
* @param selectorOrTarget - The selector, element, or CID to target.
|
|
97
|
+
* @param event - The event name.
|
|
98
|
+
* @param payload - The payload to send to the server. Defaults to an empty object.
|
|
99
|
+
* @param onReply - A callback to handle the server's reply.
|
|
100
|
+
*/
|
|
101
|
+
pushEventTo(selectorOrTarget: PhxTarget, event: string, payload: object, onReply: OnReply): void;
|
|
102
|
+
/**
|
|
103
|
+
* Pushes a targeted event to the server and returns a Promise that resolves with the server's reply.
|
|
75
104
|
*
|
|
76
105
|
* It sends the event to the LiveComponent or LiveView the `selectorOrTarget` is defined in,
|
|
77
106
|
* where its value can be either a query selector, an actual DOM element, or a CID (component id)
|
|
78
107
|
* returned by the `@myself` assign.
|
|
79
108
|
*
|
|
80
|
-
* If the
|
|
81
|
-
* even if
|
|
82
|
-
*
|
|
109
|
+
* If the selector matches multiple elements, the event is sent to all of them,
|
|
110
|
+
* even if they belong to the same LiveComponent or LiveView.
|
|
111
|
+
* Because of this, it returns a single promise that matches the return value of
|
|
83
112
|
* [`Promise.allSettled()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled#return_value).
|
|
84
113
|
* Individual fulfilled values are of the format `{ reply, ref }`, where `reply` is the server's reply.
|
|
85
114
|
*
|
|
115
|
+
* The individual promises will be rejected in case of errors
|
|
116
|
+
* such as a disconnected state, timeout, or the server rejecting the event.
|
|
117
|
+
*
|
|
86
118
|
* @param selectorOrTarget - The selector, element, or CID to target.
|
|
87
119
|
* @param event - The event name.
|
|
88
120
|
* @param [payload] - The payload to send to the server. Defaults to an empty object.
|
|
89
|
-
* @
|
|
90
|
-
*
|
|
91
|
-
* When onReply is not provided, the method returns a Promise.
|
|
92
|
-
* When onReply is provided, the method returns void.
|
|
121
|
+
* @returns A promise that resolves when the event has been handled by all targets.
|
|
93
122
|
*/
|
|
94
|
-
pushEventTo(selectorOrTarget: PhxTarget, event: string, payload: object, onReply: OnReply): void;
|
|
95
123
|
pushEventTo(selectorOrTarget: PhxTarget, event: string, payload?: object): Promise<PromiseSettledResult<{
|
|
96
124
|
reply: any;
|
|
97
125
|
ref: number;
|
package/package.json
CHANGED
|
@@ -172,7 +172,7 @@ var EntryUploader = class {
|
|
|
172
172
|
}
|
|
173
173
|
upload() {
|
|
174
174
|
this.uploadChannel.onError((reason) => this.error(reason));
|
|
175
|
-
this.uploadChannel.join().receive("ok", (_data) => this.readNextChunk()).receive("error", (reason) => this.error(reason));
|
|
175
|
+
this.uploadChannel.join().receive("ok", (_data) => this.readNextChunk()).receive("error", ({ reason }) => this.error(reason));
|
|
176
176
|
}
|
|
177
177
|
isDone() {
|
|
178
178
|
return this.offset >= this.entry.file.size;
|
|
@@ -215,6 +215,21 @@ var EntryUploader = class {
|
|
|
215
215
|
|
|
216
216
|
// js/phoenix_live_view/utils.js
|
|
217
217
|
var logError = (msg, obj) => console.error && console.error(msg, obj);
|
|
218
|
+
var ensureSameOrigin = (href, kind) => {
|
|
219
|
+
let url;
|
|
220
|
+
try {
|
|
221
|
+
url = new URL(href, window.location.href);
|
|
222
|
+
} catch {
|
|
223
|
+
throw new Error(
|
|
224
|
+
`expected ${kind} destination to be a valid URL, got: ${href}`
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
if (url.origin !== window.location.origin) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`cannot ${kind} to "${href}" because its origin does not match the current origin "${window.location.origin}". Use window.location directly for cross-origin navigation.`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
218
233
|
var isCid = (cid) => {
|
|
219
234
|
const type = typeof cid;
|
|
220
235
|
return type === "number" || type === "string" && /^(0|[1-9]\d*)$/.test(cid);
|
|
@@ -1459,7 +1474,7 @@ Hooks.InfiniteScroll = {
|
|
|
1459
1474
|
}
|
|
1460
1475
|
},
|
|
1461
1476
|
updated() {
|
|
1462
|
-
if (!this.scrollContainer.isConnected) {
|
|
1477
|
+
if (this.scrollContainer && !this.scrollContainer.isConnected) {
|
|
1463
1478
|
this.destroyed();
|
|
1464
1479
|
this.mounted();
|
|
1465
1480
|
}
|
|
@@ -3882,6 +3897,7 @@ var js_commands_default = (liveSocket, eventType) => {
|
|
|
3882
3897
|
});
|
|
3883
3898
|
},
|
|
3884
3899
|
navigate(href, opts = {}) {
|
|
3900
|
+
ensureSameOrigin(href, "navigate");
|
|
3885
3901
|
const customEvent = new CustomEvent("phx:exec");
|
|
3886
3902
|
liveSocket.historyRedirect(
|
|
3887
3903
|
customEvent,
|
|
@@ -3892,6 +3908,7 @@ var js_commands_default = (liveSocket, eventType) => {
|
|
|
3892
3908
|
);
|
|
3893
3909
|
},
|
|
3894
3910
|
patch(href, opts = {}) {
|
|
3911
|
+
ensureSameOrigin(href, "patch");
|
|
3895
3912
|
const customEvent = new CustomEvent("phx:exec");
|
|
3896
3913
|
liveSocket.pushHistoryPatch(
|
|
3897
3914
|
customEvent,
|
|
@@ -5976,7 +5993,7 @@ var LiveSocket = class {
|
|
|
5976
5993
|
}
|
|
5977
5994
|
// public
|
|
5978
5995
|
version() {
|
|
5979
|
-
return "1.1.
|
|
5996
|
+
return "1.1.31";
|
|
5980
5997
|
}
|
|
5981
5998
|
isProfileEnabled() {
|
|
5982
5999
|
return this.sessionStorage.getItem(PHX_LV_PROFILE) === "true";
|