sibujs 4.11.0 → 4.12.0
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/README.md +2 -2
- package/dist/browser.cjs +1 -1
- package/dist/browser.js +4 -4
- package/dist/build.cjs +19 -1
- package/dist/build.js +13 -12
- package/dist/cdn.dev.global.js +12 -12
- package/dist/cdn.full.dev.global.js +13 -13
- package/dist/cdn.full.global.js +12 -12
- package/dist/cdn.global.js +12 -12
- package/dist/{chunk-FC6KCJSA.js → chunk-2F4PSJ5Z.js} +162 -67
- package/dist/{chunk-R244COCR.js → chunk-653KGQS2.js} +3 -3
- package/dist/chunk-AM6DZLBM.js +181 -0
- package/dist/{chunk-ADWDDJ5M.js → chunk-B2Y5BJSP.js} +1 -1
- package/dist/{chunk-KREPZR2U.js → chunk-CI4W6XAU.js} +4 -4
- package/dist/{chunk-NBBFF5VA.js → chunk-DFZL3AX7.js} +6 -6
- package/dist/{chunk-OJV2RW4D.js → chunk-EMB5PG7V.js} +1 -1
- package/dist/{chunk-3ACAAQIN.js → chunk-F2UWOZ6J.js} +4 -0
- package/dist/{chunk-REOAVT2M.js → chunk-HXT2RNH5.js} +1 -1
- package/dist/{chunk-F6B5CKF7.js → chunk-IHA62NGJ.js} +1 -1
- package/dist/{chunk-VZDMHDVA.js → chunk-LEUEKK4G.js} +2 -2
- package/dist/{chunk-WQ7M57JJ.js → chunk-LXBMVGVB.js} +3 -3
- package/dist/{chunk-RRKGQKPU.js → chunk-LYTUTXQP.js} +4 -4
- package/dist/{chunk-5IODQRR3.js → chunk-NAVLCP4S.js} +4 -4
- package/dist/{chunk-YXJHLMRH.js → chunk-NB7TFSJE.js} +5 -5
- package/dist/{chunk-5G53CC6L.js → chunk-QLG5IK7F.js} +4 -4
- package/dist/{chunk-KFJUMC3Q.js → chunk-RK7EPQPG.js} +1 -1
- package/dist/{chunk-LQ2C44YH.js → chunk-T5O2CUHV.js} +1 -1
- package/dist/{chunk-CHQPJY47.js → chunk-WFRQ6D7H.js} +1 -1
- package/dist/{chunk-3Z7AEJMQ.js → chunk-WH4U2VUE.js} +5 -3
- package/dist/{chunk-HMOPAC2Z.js → chunk-XX4BRMWF.js} +15 -3
- package/dist/{chunk-7V7VFT7E.js → chunk-ZJCHY2IB.js} +1 -1
- package/dist/{chunk-MCPL5WKQ.js → chunk-ZPY2UAKM.js} +3 -3
- package/dist/{chunk-DEUONJYR.js → chunk-ZTYCCBU3.js} +19 -11
- package/dist/data.cjs +1 -1
- package/dist/data.js +5 -5
- package/dist/devtools.cjs +1 -1
- package/dist/devtools.js +4 -4
- package/dist/ecosystem.cjs +9 -1
- package/dist/ecosystem.d.cts +1 -1
- package/dist/ecosystem.d.ts +1 -1
- package/dist/ecosystem.js +8 -8
- package/dist/extras.cjs +368 -154
- package/dist/extras.d.cts +2 -2
- package/dist/extras.d.ts +2 -2
- package/dist/extras.js +23 -22
- package/dist/index.cjs +19 -1
- package/dist/index.d.cts +91 -90
- package/dist/index.d.ts +91 -90
- package/dist/index.js +13 -12
- package/dist/motion.cjs +1 -1
- package/dist/motion.js +3 -3
- package/dist/patterns.cjs +1 -1
- package/dist/patterns.js +5 -5
- package/dist/performance.cjs +1 -1
- package/dist/performance.d.cts +2 -2
- package/dist/performance.d.ts +2 -2
- package/dist/performance.js +7 -6
- package/dist/plugins.cjs +344 -109
- package/dist/plugins.d.cts +1 -1
- package/dist/plugins.d.ts +1 -1
- package/dist/plugins.js +15 -12
- package/dist/{ssr-C8hgIAw7.d.cts → ssr-Db0Sgdu3.d.cts} +7 -4
- package/dist/{ssr-C8hgIAw7.d.ts → ssr-Db0Sgdu3.d.ts} +7 -4
- package/dist/{ssr-5OWAH3U2.js → ssr-VBM6J5SJ.js} +4 -2
- package/dist/ssr.cjs +346 -128
- package/dist/ssr.d.cts +1 -1
- package/dist/ssr.d.ts +1 -1
- package/dist/ssr.js +10 -9
- package/dist/{tagFactory-BL2fymez.d.ts → tagFactory-Bnd_Nph9.d.ts} +1 -1
- package/dist/{tagFactory-8qL9LCIx.d.cts → tagFactory-DkytnJws.d.cts} +1 -1
- package/dist/testing.cjs +1 -1
- package/dist/testing.js +2 -2
- package/dist/ui.cjs +251 -24
- package/dist/ui.d.cts +111 -1
- package/dist/ui.d.ts +111 -1
- package/dist/ui.js +130 -8
- package/dist/widgets.cjs +1 -1
- package/dist/widgets.js +6 -6
- package/package.json +1 -1
package/dist/ssr.js
CHANGED
|
@@ -23,9 +23,9 @@ import {
|
|
|
23
23
|
wasm,
|
|
24
24
|
worker,
|
|
25
25
|
workerFn
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-NB7TFSJE.js";
|
|
27
27
|
import "./chunk-52XFPGSN.js";
|
|
28
|
-
import "./chunk-
|
|
28
|
+
import "./chunk-T5O2CUHV.js";
|
|
29
29
|
import {
|
|
30
30
|
collectStream,
|
|
31
31
|
deserializeState,
|
|
@@ -45,17 +45,18 @@ import {
|
|
|
45
45
|
ssrSuspense,
|
|
46
46
|
suspenseSwapScript,
|
|
47
47
|
trustHTML
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-2F4PSJ5Z.js";
|
|
49
|
+
import "./chunk-AM6DZLBM.js";
|
|
49
50
|
import "./chunk-62BDYGAG.js";
|
|
50
|
-
import "./chunk-
|
|
51
|
-
import "./chunk-
|
|
52
|
-
import "./chunk-
|
|
51
|
+
import "./chunk-WH4U2VUE.js";
|
|
52
|
+
import "./chunk-XX4BRMWF.js";
|
|
53
|
+
import "./chunk-F2UWOZ6J.js";
|
|
53
54
|
import "./chunk-7ZHH77QA.js";
|
|
54
|
-
import "./chunk-
|
|
55
|
+
import "./chunk-EMB5PG7V.js";
|
|
55
56
|
import "./chunk-EXXSN3BW.js";
|
|
56
57
|
import "./chunk-7XHATCIH.js";
|
|
57
|
-
import "./chunk-
|
|
58
|
-
import "./chunk-
|
|
58
|
+
import "./chunk-IHA62NGJ.js";
|
|
59
|
+
import "./chunk-B2Y5BJSP.js";
|
|
59
60
|
import "./chunk-NYNYSPK7.js";
|
|
60
61
|
export {
|
|
61
62
|
Head,
|
|
@@ -93,7 +93,7 @@ type TagEventHandlers = {
|
|
|
93
93
|
* it types `onElement`.
|
|
94
94
|
*/
|
|
95
95
|
interface TagProps<El extends Element = HTMLElement> {
|
|
96
|
-
id?: string | (() => string);
|
|
96
|
+
id?: string | (() => string | null | undefined);
|
|
97
97
|
class?: string | (() => string | null | undefined | false) | Record<string, boolean | (() => boolean)>;
|
|
98
98
|
style?: StyleMap | string | (() => string | StyleObject);
|
|
99
99
|
/**
|
|
@@ -93,7 +93,7 @@ type TagEventHandlers = {
|
|
|
93
93
|
* it types `onElement`.
|
|
94
94
|
*/
|
|
95
95
|
interface TagProps<El extends Element = HTMLElement> {
|
|
96
|
-
id?: string | (() => string);
|
|
96
|
+
id?: string | (() => string | null | undefined);
|
|
97
97
|
class?: string | (() => string | null | undefined | false) | Record<string, boolean | (() => boolean)>;
|
|
98
98
|
style?: StyleMap | string | (() => string | StyleObject);
|
|
99
99
|
/**
|
package/dist/testing.cjs
CHANGED
|
@@ -780,7 +780,7 @@ function forEachSubscriber(signal, visit) {
|
|
|
780
780
|
}
|
|
781
781
|
|
|
782
782
|
// src/reactivity/track.ts
|
|
783
|
-
var _runtimeVersion = true ? "4.
|
|
783
|
+
var _runtimeVersion = true ? "4.12.0" : "dev";
|
|
784
784
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
785
785
|
function resolveReactiveApi() {
|
|
786
786
|
const g = globalThis;
|
package/dist/testing.js
CHANGED
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
} from "./chunk-7ZHH77QA.js";
|
|
4
4
|
import {
|
|
5
5
|
effect
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-EMB5PG7V.js";
|
|
7
7
|
import "./chunk-7XHATCIH.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-B2Y5BJSP.js";
|
|
9
9
|
import {
|
|
10
10
|
dispose,
|
|
11
11
|
replaceChildrenSafely,
|
package/dist/ui.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// ui.ts
|
|
21
21
|
var ui_exports = {};
|
|
22
22
|
__export(ui_exports, {
|
|
23
|
+
DEFAULT_EMBED_SANDBOX: () => DEFAULT_EMBED_SANDBOX,
|
|
23
24
|
FocusTrap: () => FocusTrap,
|
|
24
25
|
RenderProp: () => RenderProp,
|
|
25
26
|
VirtualList: () => VirtualList,
|
|
@@ -46,6 +47,7 @@ __export(ui_exports, {
|
|
|
46
47
|
defineStrictComponent: () => defineStrictComponent,
|
|
47
48
|
dialog: () => dialog,
|
|
48
49
|
email: () => email,
|
|
50
|
+
embed: () => embed,
|
|
49
51
|
eventBus: () => eventBus,
|
|
50
52
|
focus: () => focus,
|
|
51
53
|
form: () => form,
|
|
@@ -69,6 +71,7 @@ __export(ui_exports, {
|
|
|
69
71
|
required: () => required,
|
|
70
72
|
scopedStyle: () => scopedStyle,
|
|
71
73
|
scrollLock: () => scrollLock,
|
|
74
|
+
setEmbedPolicy: () => setEmbedPolicy,
|
|
72
75
|
ssnMask: () => ssnMask,
|
|
73
76
|
svgElement: () => svgElement,
|
|
74
77
|
timeMask: () => timeMask,
|
|
@@ -860,7 +863,7 @@ function forEachSubscriber(signal2, visit) {
|
|
|
860
863
|
}
|
|
861
864
|
|
|
862
865
|
// src/reactivity/track.ts
|
|
863
|
-
var _runtimeVersion = true ? "4.
|
|
866
|
+
var _runtimeVersion = true ? "4.12.0" : "dev";
|
|
864
867
|
var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
|
|
865
868
|
function resolveReactiveApi() {
|
|
866
869
|
const g = globalThis;
|
|
@@ -1093,7 +1096,7 @@ var isBatching = API2.isBatching;
|
|
|
1093
1096
|
// src/core/signals/signal.ts
|
|
1094
1097
|
var _g = globalThis;
|
|
1095
1098
|
function signal(initial, options) {
|
|
1096
|
-
const
|
|
1099
|
+
const state3 = {
|
|
1097
1100
|
value: initial,
|
|
1098
1101
|
__v: 0,
|
|
1099
1102
|
__sc: 0,
|
|
@@ -1106,57 +1109,57 @@ function signal(initial, options) {
|
|
|
1106
1109
|
};
|
|
1107
1110
|
const debugName = DEV ? options?.name : void 0;
|
|
1108
1111
|
const equalsFn = options?.equals;
|
|
1109
|
-
if (debugName)
|
|
1112
|
+
if (debugName) state3.__name = debugName;
|
|
1110
1113
|
function get() {
|
|
1111
|
-
recordDependency2(
|
|
1112
|
-
return
|
|
1114
|
+
recordDependency2(state3);
|
|
1115
|
+
return state3.value;
|
|
1113
1116
|
}
|
|
1114
|
-
get.__signal =
|
|
1117
|
+
get.__signal = state3;
|
|
1115
1118
|
if (debugName) get.__name = debugName;
|
|
1116
1119
|
let set;
|
|
1117
1120
|
if (equalsFn) {
|
|
1118
1121
|
set = (next) => {
|
|
1119
|
-
const prev =
|
|
1122
|
+
const prev = state3.value;
|
|
1120
1123
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
1121
1124
|
if (equalsFn(prev, newValue)) return;
|
|
1122
|
-
|
|
1123
|
-
|
|
1125
|
+
state3.value = newValue;
|
|
1126
|
+
state3.__v++;
|
|
1124
1127
|
if (DEV) {
|
|
1125
1128
|
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1126
|
-
if (hook) emitDevtools(hook, "signal:update", { signal:
|
|
1129
|
+
if (hook) emitDevtools(hook, "signal:update", { signal: state3, name: debugName, oldValue: prev, newValue });
|
|
1127
1130
|
}
|
|
1128
|
-
if (!enqueueBatchedSignal(
|
|
1129
|
-
notifySubscribers2(
|
|
1131
|
+
if (!enqueueBatchedSignal(state3)) {
|
|
1132
|
+
notifySubscribers2(state3);
|
|
1130
1133
|
}
|
|
1131
1134
|
};
|
|
1132
1135
|
} else if (DEV) {
|
|
1133
1136
|
set = (next) => {
|
|
1134
|
-
const prev =
|
|
1137
|
+
const prev = state3.value;
|
|
1135
1138
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
1136
1139
|
if (Object.is(newValue, prev)) return;
|
|
1137
|
-
|
|
1138
|
-
|
|
1140
|
+
state3.value = newValue;
|
|
1141
|
+
state3.__v++;
|
|
1139
1142
|
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1140
|
-
if (hook) emitDevtools(hook, "signal:update", { signal:
|
|
1141
|
-
if (!enqueueBatchedSignal(
|
|
1142
|
-
notifySubscribers2(
|
|
1143
|
+
if (hook) emitDevtools(hook, "signal:update", { signal: state3, name: debugName, oldValue: prev, newValue });
|
|
1144
|
+
if (!enqueueBatchedSignal(state3)) {
|
|
1145
|
+
notifySubscribers2(state3);
|
|
1143
1146
|
}
|
|
1144
1147
|
};
|
|
1145
1148
|
} else {
|
|
1146
1149
|
set = (next) => {
|
|
1147
|
-
const prev =
|
|
1150
|
+
const prev = state3.value;
|
|
1148
1151
|
const newValue = typeof next === "function" ? next(prev) : next;
|
|
1149
1152
|
if (Object.is(newValue, prev)) return;
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
if (!enqueueBatchedSignal(
|
|
1153
|
-
notifySubscribers2(
|
|
1153
|
+
state3.value = newValue;
|
|
1154
|
+
state3.__v++;
|
|
1155
|
+
if (!enqueueBatchedSignal(state3)) {
|
|
1156
|
+
notifySubscribers2(state3);
|
|
1154
1157
|
}
|
|
1155
1158
|
};
|
|
1156
1159
|
}
|
|
1157
1160
|
if (DEV) {
|
|
1158
1161
|
const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
|
|
1159
|
-
if (hook) emitDevtools(hook, "signal:create", { signal:
|
|
1162
|
+
if (hook) emitDevtools(hook, "signal:create", { signal: state3, name: debugName, getter: get, initial });
|
|
1160
1163
|
}
|
|
1161
1164
|
return [get, set];
|
|
1162
1165
|
}
|
|
@@ -3226,6 +3229,227 @@ function dialog() {
|
|
|
3226
3229
|
return { open, close, isOpen, toggle, dispose: dispose2 };
|
|
3227
3230
|
}
|
|
3228
3231
|
|
|
3232
|
+
// src/core/rendering/requireDocument.ts
|
|
3233
|
+
function noDocument() {
|
|
3234
|
+
throw new Error(
|
|
3235
|
+
"[SibuJS] renderToString needs a DOM: register happy-dom or jsdom globals first." + (DEV ? ' There is no global `document`, and SibuJS builds live DOM nodes, on the server too. For example: import { GlobalRegistrator } from "@happy-dom/global-registrator"; GlobalRegistrator.register(); See SSR in AGENTS.md \xA710.' : "")
|
|
3236
|
+
);
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
// src/utils/embedPolicy.ts
|
|
3240
|
+
var state2 = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.embed.v2"), () => ({
|
|
3241
|
+
allowed: [],
|
|
3242
|
+
frames: /* @__PURE__ */ new WeakMap()
|
|
3243
|
+
}));
|
|
3244
|
+
var REQUEST_CACHE = "embed";
|
|
3245
|
+
function currentEmbedAllowlist() {
|
|
3246
|
+
const scoped = getRequestStore()?.caches?.get(REQUEST_CACHE)?.get("allowed");
|
|
3247
|
+
return scoped ?? state2.allowed;
|
|
3248
|
+
}
|
|
3249
|
+
function setEmbedPolicy(policy) {
|
|
3250
|
+
const allowed = [];
|
|
3251
|
+
for (const entry of policy.allowedOrigins) {
|
|
3252
|
+
let url = null;
|
|
3253
|
+
try {
|
|
3254
|
+
url = new URL(entry);
|
|
3255
|
+
} catch {
|
|
3256
|
+
url = null;
|
|
3257
|
+
}
|
|
3258
|
+
if (!url || url.protocol !== "https:" || url.username || url.password || url.search || url.hash) {
|
|
3259
|
+
throw new TypeError(
|
|
3260
|
+
`setEmbedPolicy: "${entry}" is not an allowed embed origin. Use an absolute https: URL, optionally with a path prefix, and no credentials, query or fragment.`
|
|
3261
|
+
);
|
|
3262
|
+
}
|
|
3263
|
+
allowed.push({ origin: url.origin, path: url.pathname === "/" ? "" : url.pathname });
|
|
3264
|
+
}
|
|
3265
|
+
const store = getRequestStore();
|
|
3266
|
+
if (store) {
|
|
3267
|
+
const caches = store.caches ?? (store.caches = /* @__PURE__ */ new Map());
|
|
3268
|
+
let scoped = caches.get(REQUEST_CACHE);
|
|
3269
|
+
if (!scoped) {
|
|
3270
|
+
scoped = /* @__PURE__ */ new Map();
|
|
3271
|
+
caches.set(REQUEST_CACHE, scoped);
|
|
3272
|
+
}
|
|
3273
|
+
scoped.set("allowed", allowed);
|
|
3274
|
+
} else {
|
|
3275
|
+
state2.allowed = allowed;
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
function resolveEmbedSrc(value, allowed = currentEmbedAllowlist()) {
|
|
3279
|
+
if (typeof value !== "string" || sanitizeUrl(value) === "") return null;
|
|
3280
|
+
let url;
|
|
3281
|
+
try {
|
|
3282
|
+
url = new URL(value);
|
|
3283
|
+
} catch {
|
|
3284
|
+
return null;
|
|
3285
|
+
}
|
|
3286
|
+
if (url.protocol !== "https:" || url.username || url.password) return null;
|
|
3287
|
+
if (typeof location !== "undefined" && url.origin === location.origin) return null;
|
|
3288
|
+
for (const entry of allowed) {
|
|
3289
|
+
if (url.origin !== entry.origin) continue;
|
|
3290
|
+
const path = entry.path;
|
|
3291
|
+
if (path === "" || url.pathname === path || url.pathname.startsWith(path.endsWith("/") ? path : `${path}/`)) {
|
|
3292
|
+
return url.href;
|
|
3293
|
+
}
|
|
3294
|
+
}
|
|
3295
|
+
return null;
|
|
3296
|
+
}
|
|
3297
|
+
var DEFAULT_EMBED_SANDBOX = [
|
|
3298
|
+
"allow-scripts",
|
|
3299
|
+
"allow-same-origin",
|
|
3300
|
+
"allow-presentation",
|
|
3301
|
+
"allow-popups",
|
|
3302
|
+
"allow-popups-to-escape-sandbox"
|
|
3303
|
+
];
|
|
3304
|
+
var EMBED_FEATURES = /* @__PURE__ */ new Set([
|
|
3305
|
+
"accelerometer",
|
|
3306
|
+
"autoplay",
|
|
3307
|
+
"clipboard-write",
|
|
3308
|
+
"encrypted-media",
|
|
3309
|
+
"fullscreen",
|
|
3310
|
+
"gyroscope",
|
|
3311
|
+
"picture-in-picture",
|
|
3312
|
+
"web-share"
|
|
3313
|
+
]);
|
|
3314
|
+
var EMBED_REFERRER_POLICIES = /* @__PURE__ */ new Set([
|
|
3315
|
+
"no-referrer",
|
|
3316
|
+
"same-origin",
|
|
3317
|
+
"strict-origin",
|
|
3318
|
+
"strict-origin-when-cross-origin"
|
|
3319
|
+
]);
|
|
3320
|
+
var DEFAULT_EMBED_REFERRER_POLICY = "strict-origin-when-cross-origin";
|
|
3321
|
+
function narrowEmbedSandbox(value) {
|
|
3322
|
+
const out = [];
|
|
3323
|
+
for (const token of value.toLowerCase().split(/[\t\n\f\r ]+/)) {
|
|
3324
|
+
if (token && DEFAULT_EMBED_SANDBOX.includes(token) && !out.includes(token)) out.push(token);
|
|
3325
|
+
}
|
|
3326
|
+
return out.join(" ");
|
|
3327
|
+
}
|
|
3328
|
+
function narrowEmbedAllow(value) {
|
|
3329
|
+
const granted = [];
|
|
3330
|
+
const denied = [];
|
|
3331
|
+
for (const directive of value.split(";")) {
|
|
3332
|
+
const tokens = directive.trim().split(/[\t\n\f\r ]+/);
|
|
3333
|
+
const feature = tokens[0];
|
|
3334
|
+
if (!feature) continue;
|
|
3335
|
+
const list = tokens.slice(1).map((token) => token.toLowerCase());
|
|
3336
|
+
const grantsSrc = (list.length === 0 || list.includes("'src'") || list.includes("*")) && !list.includes("'none'");
|
|
3337
|
+
if (!grantsSrc) denied.push(feature);
|
|
3338
|
+
else if (EMBED_FEATURES.has(feature) && !granted.includes(feature)) granted.push(feature);
|
|
3339
|
+
}
|
|
3340
|
+
return granted.filter((feature) => !denied.includes(feature)).join("; ");
|
|
3341
|
+
}
|
|
3342
|
+
function narrowEmbedReferrerPolicy(value) {
|
|
3343
|
+
const lower = value.trim().toLowerCase();
|
|
3344
|
+
return EMBED_REFERRER_POLICIES.has(lower) ? lower : DEFAULT_EMBED_REFERRER_POLICY;
|
|
3345
|
+
}
|
|
3346
|
+
function recordEmbedFrame(frame, record) {
|
|
3347
|
+
state2.frames.set(frame, record);
|
|
3348
|
+
}
|
|
3349
|
+
|
|
3350
|
+
// src/ui/embed.ts
|
|
3351
|
+
var DEFAULT_ALLOW = "encrypted-media; picture-in-picture; fullscreen";
|
|
3352
|
+
var RATIO = /^\d+(\.\d+)?(\s*\/\s*\d+(\.\d+)?)?$/;
|
|
3353
|
+
var warnedEmbeds = /* @__PURE__ */ new Set();
|
|
3354
|
+
function warnEmbed(key, message) {
|
|
3355
|
+
devWarnLazy(() => {
|
|
3356
|
+
if (warnedEmbeds.has(key) || warnedEmbeds.size >= 100) return "";
|
|
3357
|
+
warnedEmbeds.add(key);
|
|
3358
|
+
return message;
|
|
3359
|
+
});
|
|
3360
|
+
}
|
|
3361
|
+
function embed(props) {
|
|
3362
|
+
const { src, title } = props;
|
|
3363
|
+
if (typeof title !== "function" && (typeof title !== "string" || !title.trim())) {
|
|
3364
|
+
throw new TypeError("embed: `title` is required \u2014 it is the frame's accessible name.");
|
|
3365
|
+
}
|
|
3366
|
+
if (typeof document === "undefined") noDocument();
|
|
3367
|
+
if ("srcdoc" in props) {
|
|
3368
|
+
warnEmbed("srcdoc", "embed: `srcdoc` is never written \u2014 a frame's document must come from an allowlisted src.");
|
|
3369
|
+
}
|
|
3370
|
+
const el = document.createElement("iframe");
|
|
3371
|
+
const allowed = currentEmbedAllowlist();
|
|
3372
|
+
const sandbox = props.sandbox === void 0 ? DEFAULT_EMBED_SANDBOX.join(" ") : narrowEmbedSandbox(props.sandbox);
|
|
3373
|
+
if (props.sandbox !== void 0 && sandbox !== props.sandbox.trim().toLowerCase().split(/\s+/).join(" ")) {
|
|
3374
|
+
warnEmbed(
|
|
3375
|
+
`sandbox|${props.sandbox}`,
|
|
3376
|
+
`embed: sandbox "${props.sandbox}" was narrowed to "${sandbox}". A sandbox may only remove tokens from DEFAULT_EMBED_SANDBOX (${DEFAULT_EMBED_SANDBOX.join(" ")}).`
|
|
3377
|
+
);
|
|
3378
|
+
}
|
|
3379
|
+
el.setAttribute("sandbox", sandbox);
|
|
3380
|
+
const allowIn = props.allow ?? DEFAULT_ALLOW;
|
|
3381
|
+
const allow = narrowEmbedAllow(allowIn);
|
|
3382
|
+
const requested = allowIn.split(";").map((d) => d.trim().toLowerCase()).filter(Boolean).join("; ");
|
|
3383
|
+
if (allow !== requested) {
|
|
3384
|
+
warnEmbed(
|
|
3385
|
+
`allow|${allowIn}`,
|
|
3386
|
+
`embed: allow "${allowIn}" was narrowed to "${allow}" \u2014 only player features are delegated.`
|
|
3387
|
+
);
|
|
3388
|
+
}
|
|
3389
|
+
if (allow) el.setAttribute("allow", allow);
|
|
3390
|
+
if (allow.split("; ").includes("fullscreen")) el.setAttribute("allowfullscreen", "");
|
|
3391
|
+
const referrer = narrowEmbedReferrerPolicy(props.referrerpolicy ?? DEFAULT_EMBED_REFERRER_POLICY);
|
|
3392
|
+
if (props.referrerpolicy !== void 0 && referrer !== props.referrerpolicy) {
|
|
3393
|
+
warnEmbed(
|
|
3394
|
+
`referrer|${props.referrerpolicy}`,
|
|
3395
|
+
`embed: referrerpolicy "${props.referrerpolicy}" is less strict than the default; using "${referrer}".`
|
|
3396
|
+
);
|
|
3397
|
+
}
|
|
3398
|
+
el.setAttribute("referrerpolicy", referrer);
|
|
3399
|
+
recordEmbedFrame(el, { allowed, sandbox, allow, referrer });
|
|
3400
|
+
el.setAttribute("loading", props.loading === "eager" ? "eager" : "lazy");
|
|
3401
|
+
if (props.aspectRatio !== void 0) {
|
|
3402
|
+
if (RATIO.test(props.aspectRatio.trim())) {
|
|
3403
|
+
el.style.setProperty("aspect-ratio", props.aspectRatio.trim());
|
|
3404
|
+
el.style.setProperty("width", "100%");
|
|
3405
|
+
el.style.setProperty("height", "auto");
|
|
3406
|
+
el.style.setProperty("border", "0");
|
|
3407
|
+
} else {
|
|
3408
|
+
warnEmbed(
|
|
3409
|
+
`ratio|${props.aspectRatio}`,
|
|
3410
|
+
`embed: aspectRatio "${props.aspectRatio}" is not a ratio such as "16 / 9".`
|
|
3411
|
+
);
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
if (typeof props.class === "string") el.setAttribute("class", props.class);
|
|
3415
|
+
if (typeof props.id === "string") el.id = props.id;
|
|
3416
|
+
if (typeof title === "function") {
|
|
3417
|
+
registerDisposer(
|
|
3418
|
+
el,
|
|
3419
|
+
reactiveBinding2(() => {
|
|
3420
|
+
const value = String(title() ?? "");
|
|
3421
|
+
if (value.trim()) el.setAttribute("title", value);
|
|
3422
|
+
else warnEmbed("title", "embed: a `title` getter returned a blank value; the previous title is kept.");
|
|
3423
|
+
}, el)
|
|
3424
|
+
);
|
|
3425
|
+
} else {
|
|
3426
|
+
el.setAttribute("title", title);
|
|
3427
|
+
}
|
|
3428
|
+
const commitSrc = (value) => {
|
|
3429
|
+
const safe = resolveEmbedSrc(value, allowed);
|
|
3430
|
+
if (safe === null) {
|
|
3431
|
+
el.removeAttribute("src");
|
|
3432
|
+
if (value != null) {
|
|
3433
|
+
warnEmbed(
|
|
3434
|
+
`src|${String(value)}`,
|
|
3435
|
+
`embed: refusing src "${String(value)}" \u2014 it is not an https: URL on an origin allowed by setEmbedPolicy().`
|
|
3436
|
+
);
|
|
3437
|
+
}
|
|
3438
|
+
return;
|
|
3439
|
+
}
|
|
3440
|
+
if (el.getAttribute("src") !== safe) el.setAttribute("src", safe);
|
|
3441
|
+
};
|
|
3442
|
+
if (typeof src === "function") {
|
|
3443
|
+
registerDisposer(
|
|
3444
|
+
el,
|
|
3445
|
+
reactiveBinding2(() => commitSrc(src()), el)
|
|
3446
|
+
);
|
|
3447
|
+
} else {
|
|
3448
|
+
commitSrc(src);
|
|
3449
|
+
}
|
|
3450
|
+
return el;
|
|
3451
|
+
}
|
|
3452
|
+
|
|
3229
3453
|
// src/ui/toast.ts
|
|
3230
3454
|
function toast(options) {
|
|
3231
3455
|
const duration = options?.duration ?? 3e3;
|
|
@@ -4133,6 +4357,7 @@ function createGuard(validator) {
|
|
|
4133
4357
|
}
|
|
4134
4358
|
// Annotate the CommonJS export names for ESM import in node:
|
|
4135
4359
|
0 && (module.exports = {
|
|
4360
|
+
DEFAULT_EMBED_SANDBOX,
|
|
4136
4361
|
FocusTrap,
|
|
4137
4362
|
RenderProp,
|
|
4138
4363
|
VirtualList,
|
|
@@ -4159,6 +4384,7 @@ function createGuard(validator) {
|
|
|
4159
4384
|
defineStrictComponent,
|
|
4160
4385
|
dialog,
|
|
4161
4386
|
email,
|
|
4387
|
+
embed,
|
|
4162
4388
|
eventBus,
|
|
4163
4389
|
focus,
|
|
4164
4390
|
form,
|
|
@@ -4182,6 +4408,7 @@ function createGuard(validator) {
|
|
|
4182
4408
|
required,
|
|
4183
4409
|
scopedStyle,
|
|
4184
4410
|
scrollLock,
|
|
4411
|
+
setEmbedPolicy,
|
|
4185
4412
|
ssnMask,
|
|
4186
4413
|
svgElement,
|
|
4187
4414
|
timeMask,
|
package/dist/ui.d.cts
CHANGED
|
@@ -180,6 +180,116 @@ interface DialogAriaHandle {
|
|
|
180
180
|
*/
|
|
181
181
|
declare function createDialogAria(element: HTMLElement, options?: DialogAriaOptions): DialogAriaHandle;
|
|
182
182
|
|
|
183
|
+
/**
|
|
184
|
+
* The policy `embed()` enforces.
|
|
185
|
+
*
|
|
186
|
+
* Each entry of `allowedOrigins` is an absolute `https:` URL: an origin
|
|
187
|
+
* (`"https://www.youtube-nocookie.com"`) allows every path on it; an origin
|
|
188
|
+
* with a path (`"https://player.vimeo.com/video/"`) allows that path and what
|
|
189
|
+
* lies beneath it. Credentials, a query or a fragment in an entry are refused.
|
|
190
|
+
*/
|
|
191
|
+
interface EmbedPolicy {
|
|
192
|
+
allowedOrigins: readonly string[];
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Set the origins `embed()` may frame. Replaces the previous policy; the
|
|
196
|
+
* default allows nothing. Call it once at startup — on the server too, before
|
|
197
|
+
* rendering.
|
|
198
|
+
*
|
|
199
|
+
* Inside `runInSSRContext()` it sets the policy of THAT request only, so a
|
|
200
|
+
* multi-tenant server can configure each render without one request's
|
|
201
|
+
* allowlist reaching another's. Outside a request it sets the process-wide
|
|
202
|
+
* policy every request without its own falls back to.
|
|
203
|
+
*
|
|
204
|
+
* A frame is judged by the policy in force when `embed()` built it: a later
|
|
205
|
+
* `setEmbedPolicy()` changes neither its getter `src` checks nor how SSR
|
|
206
|
+
* serializes it.
|
|
207
|
+
*
|
|
208
|
+
* @throws TypeError for an entry that is not an absolute `https:` URL, or
|
|
209
|
+
* that carries credentials, a query or a fragment.
|
|
210
|
+
*/
|
|
211
|
+
declare function setEmbedPolicy(policy: EmbedPolicy): void;
|
|
212
|
+
/**
|
|
213
|
+
* The sandbox `embed()` applies by default — the tokens a hosted video player
|
|
214
|
+
* needs and nothing more:
|
|
215
|
+
*
|
|
216
|
+
* - `allow-scripts`, `allow-same-origin`: the player runs and keeps its own
|
|
217
|
+
* storage. Safe together only because the frame is never same-origin with
|
|
218
|
+
* the page (see `resolveEmbedSrc`).
|
|
219
|
+
* - `allow-presentation`: casting to a second screen.
|
|
220
|
+
* - `allow-popups`, `allow-popups-to-escape-sandbox`: "watch on …" links
|
|
221
|
+
* open a normal tab instead of a crippled sandboxed one.
|
|
222
|
+
*
|
|
223
|
+
* Absent on purpose: `allow-top-navigation*` (the player cannot navigate the
|
|
224
|
+
* page), `allow-forms`, `allow-modals`, `allow-downloads`,
|
|
225
|
+
* `allow-pointer-lock`, `allow-storage-access-by-user-activation`.
|
|
226
|
+
*/
|
|
227
|
+
declare const DEFAULT_EMBED_SANDBOX: readonly string[];
|
|
228
|
+
|
|
229
|
+
/** Props for {@link embed}. */
|
|
230
|
+
interface EmbedProps {
|
|
231
|
+
/**
|
|
232
|
+
* The player URL. Must be absolute `https:` and match the allowlist set with
|
|
233
|
+
* `setEmbedPolicy()`. A getter is re-checked on every change; a refused or
|
|
234
|
+
* `null` value leaves the frame without a `src`.
|
|
235
|
+
*/
|
|
236
|
+
src: string | (() => string | null | undefined);
|
|
237
|
+
/** Accessible name of the frame. Required. */
|
|
238
|
+
title: string | (() => string);
|
|
239
|
+
/**
|
|
240
|
+
* Permissions-policy features, as in the `allow` attribute. Narrowed to the
|
|
241
|
+
* player features (`accelerometer`, `autoplay`, `clipboard-write`,
|
|
242
|
+
* `encrypted-media`, `fullscreen`, `gyroscope`, `picture-in-picture`,
|
|
243
|
+
* `web-share`). Default: `"encrypted-media; picture-in-picture; fullscreen"`.
|
|
244
|
+
* A feature is kept only when every declaration of it grants the frame's own
|
|
245
|
+
* origin (no allowlist, `'src'` or `*`); `fullscreen 'none'`, `'self'` or a
|
|
246
|
+
* list of other origins drops it instead of granting it.
|
|
247
|
+
*/
|
|
248
|
+
allow?: string;
|
|
249
|
+
/**
|
|
250
|
+
* Sandbox tokens. May only REMOVE tokens from `DEFAULT_EMBED_SANDBOX`; other
|
|
251
|
+
* tokens are dropped. `""` applies every restriction.
|
|
252
|
+
*/
|
|
253
|
+
sandbox?: string;
|
|
254
|
+
/** Default `"strict-origin-when-cross-origin"`; only stricter values are accepted. */
|
|
255
|
+
referrerpolicy?: "no-referrer" | "same-origin" | "strict-origin" | "strict-origin-when-cross-origin";
|
|
256
|
+
/** Default `"lazy"`. */
|
|
257
|
+
loading?: "lazy" | "eager";
|
|
258
|
+
/** A CSS ratio such as `"16 / 9"`. Sizes the frame to the container's width. */
|
|
259
|
+
aspectRatio?: string;
|
|
260
|
+
class?: string;
|
|
261
|
+
id?: string;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Embed a third-party player (YouTube, Vimeo, …) in an `<iframe>`.
|
|
265
|
+
*
|
|
266
|
+
* The tag factories, `svgElement()` and `DOMPool` refuse `iframe`; this is the
|
|
267
|
+
* one runtime door, and it only opens onto origins the application allowlisted
|
|
268
|
+
* with `setEmbedPolicy()`:
|
|
269
|
+
*
|
|
270
|
+
* - `src` must be absolute `https:` and match an allowlisted origin and path
|
|
271
|
+
* prefix. A value outside the allowlist — a getter's included — is omitted
|
|
272
|
+
* and reported with a development warning. `javascript:`, `data:` and
|
|
273
|
+
* `blob:` can never match; neither can the page's own origin.
|
|
274
|
+
* - `title` is required. `srcdoc` is never written.
|
|
275
|
+
* - Safe defaults: `loading="lazy"`, `referrerpolicy=
|
|
276
|
+
* "strict-origin-when-cross-origin"`, `sandbox` set to
|
|
277
|
+
* `DEFAULT_EMBED_SANDBOX` (written before `src`, so it applies to the first
|
|
278
|
+
* document), and `allow` limited to player features. `sandbox`,
|
|
279
|
+
* `referrerpolicy` and `allow` can only be narrowed.
|
|
280
|
+
* - `renderToString` serializes the frame under the same policy.
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```ts
|
|
284
|
+
* import { embed, setEmbedPolicy } from "sibujs/ui";
|
|
285
|
+
* setEmbedPolicy({ allowedOrigins: ["https://www.youtube-nocookie.com/embed/"] });
|
|
286
|
+
* embed({ src: `https://www.youtube-nocookie.com/embed/${id}`, title: "Product tour", aspectRatio: "16 / 9" });
|
|
287
|
+
* ```
|
|
288
|
+
*
|
|
289
|
+
* @throws TypeError when `title` is missing or blank.
|
|
290
|
+
*/
|
|
291
|
+
declare function embed(props: EmbedProps): HTMLIFrameElement;
|
|
292
|
+
|
|
183
293
|
/**
|
|
184
294
|
* lazyEffect creates an effect that only activates when the target element
|
|
185
295
|
* becomes visible in the viewport (via IntersectionObserver). When the element
|
|
@@ -292,4 +402,4 @@ interface ScrollLockHandle {
|
|
|
292
402
|
*/
|
|
293
403
|
declare function scrollLock(): ScrollLockHandle;
|
|
294
404
|
|
|
295
|
-
export { type DialogAriaHandle, type DialogAriaOptions, type FocusManagerHandle, type FocusManagerOptions, type FormActionHandle, type FormActionState, type FormActionSubmit, type IntervalHandle, type ListboxHandle, type ListboxOptions, type ScrollLockHandle, type TimeoutHandle, createDialogAria, createFocusManager, createListbox, formAction, hover, interval, lazyEffect, scrollLock, timeout };
|
|
405
|
+
export { DEFAULT_EMBED_SANDBOX, type DialogAriaHandle, type DialogAriaOptions, type EmbedPolicy, type EmbedProps, type FocusManagerHandle, type FocusManagerOptions, type FormActionHandle, type FormActionState, type FormActionSubmit, type IntervalHandle, type ListboxHandle, type ListboxOptions, type ScrollLockHandle, type TimeoutHandle, createDialogAria, createFocusManager, createListbox, embed, formAction, hover, interval, lazyEffect, scrollLock, setEmbedPolicy, timeout };
|