@doenet/doenetml-iframe 0.7.20-dev.320 → 0.7.20-dev.323
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/index.d.ts +12 -1
- package/index.js +151 -4
- package/index.js.map +1 -1
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ declare type DoenetEditorProps = Omit<React.ComponentProps<typeof DoenetEditor_2
|
|
|
75
75
|
* DoenetML component. Instead you must use the message passing interface of `DoenetViewer` to communicate
|
|
76
76
|
* with the underlying DoenetML component.
|
|
77
77
|
*/
|
|
78
|
-
export declare function DoenetViewer({ doenetML, standaloneUrl: specifiedStandaloneUrl, cssUrl: specifiedCssUrl, doenetmlVersion: specifiedDoenetmlVersion, autodetectVersion, ...doenetViewerProps }: DoenetViewerIframeProps): default_2.JSX.Element | null;
|
|
78
|
+
export declare function DoenetViewer({ doenetML, standaloneUrl: specifiedStandaloneUrl, cssUrl: specifiedCssUrl, doenetmlVersion: specifiedDoenetmlVersion, autodetectVersion, useSharedCoreWorker, ...doenetViewerProps }: DoenetViewerIframeProps): default_2.JSX.Element | null;
|
|
79
79
|
|
|
80
80
|
export declare type DoenetViewerIframeProps = DoenetViewerProps & {
|
|
81
81
|
doenetML: string;
|
|
@@ -100,6 +100,17 @@ export declare type DoenetViewerIframeProps = DoenetViewerProps & {
|
|
|
100
100
|
* overwriting any doenetmlVersion or urls provided
|
|
101
101
|
*/
|
|
102
102
|
autodetectVersion?: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Opt-in (#1466): multiplex this viewer's core onto a shared core worker
|
|
105
|
+
* owned by this (parent) page, instead of the iframe booting its own
|
|
106
|
+
* ~100 MB dedicated worker. Viewers on the same page with the same
|
|
107
|
+
* standalone version share workers (up to a pool cap per worker), which
|
|
108
|
+
* is the dominant memory saving on pages embedding many documents.
|
|
109
|
+
* Trade-off: a worker-level hang or crash affects every document on that
|
|
110
|
+
* worker (per-core teardown stays individual; suspect workers are
|
|
111
|
+
* quarantined so retries boot fresh ones). Default off.
|
|
112
|
+
*/
|
|
113
|
+
useSharedCoreWorker?: boolean;
|
|
103
114
|
};
|
|
104
115
|
|
|
105
116
|
declare type DoenetViewerProps = Omit<React.ComponentProps<typeof DoenetViewer_2>, "doenetML" | "externalVirtualKeyboardProvided">;
|
package/index.js
CHANGED
|
@@ -25618,7 +25618,7 @@ function requireCssesc$1() {
|
|
|
25618
25618
|
return cssesc_1$1;
|
|
25619
25619
|
}
|
|
25620
25620
|
requireCssesc$1();
|
|
25621
|
-
const viewerIframeJsSource = '(function() {\n "use strict";\n async function waitForStandaloneBundle(timeoutMs) {\n if (typeof window.renderDoenetViewerToContainer === "function") {\n return true;\n }\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n if (typeof window.renderDoenetViewerToContainer === "function") {\n return true;\n }\n }\n return false;\n }\n ComlinkViewer.expose(\n { renderViewerWithFunctionProps },\n ComlinkViewer.windowEndpoint(globalThis.parent)\n );\n function renderViewerWithFunctionProps(...args) {\n const augmentedDoenetViewerProps = { ...doenetViewerProps };\n augmentedDoenetViewerProps.externalVirtualKeyboardProvided = true;\n for (const propName of doenetViewerPropsSpecified) {\n if (!(propName in doenetViewerProps)) {\n const idx = args.indexOf(propName);\n if (idx !== -1) {\n augmentedDoenetViewerProps[propName] = args[idx + 1];\n }\n }\n }\n if (!doenetViewerPropsSpecified.includes("requestScrollTo")) {\n augmentedDoenetViewerProps.requestScrollTo = (offset) => {\n messageParentFromViewer({ type: "scrollTo", offset });\n };\n }\n window.renderDoenetViewerToContainer(\n document.getElementById("root"),\n void 0,\n augmentedDoenetViewerProps\n );\n }\n (async () => {\n if (await waitForStandaloneBundle(6e4)) {\n messageParentFromViewer({ iframeReady: true });\n } else {\n messageParentFromViewer({\n error: "Invalid DoenetML version or DoenetML package not found"\n });\n }\n })().catch((err) => {\n console.error(\n "iframe DoenetViewer: unexpected failure while signalling iframeReady",\n err\n );\n try {\n messageParentFromViewer({\n error: "iframe viewer failed to initialize"\n });\n } catch {\n }\n });\n function messageParentFromViewer(data) {\n window.parent.postMessage(\n {\n origin: viewerId,\n data\n },\n window.parent.origin\n );\n }\n})();\n';
|
|
25621
|
+
const viewerIframeJsSource = '(function() {\n "use strict";\n async function waitForStandaloneBundle(timeoutMs) {\n if (typeof window.renderDoenetViewerToContainer === "function") {\n return true;\n }\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n if (typeof window.renderDoenetViewerToContainer === "function") {\n return true;\n }\n }\n return false;\n }\n ComlinkViewer.expose(\n { renderViewerWithFunctionProps },\n ComlinkViewer.windowEndpoint(globalThis.parent)\n );\n function renderViewerWithFunctionProps(...args) {\n const augmentedDoenetViewerProps = { ...doenetViewerProps };\n augmentedDoenetViewerProps.externalVirtualKeyboardProvided = true;\n for (const propName of doenetViewerPropsSpecified) {\n if (!(propName in doenetViewerProps)) {\n const idx = args.indexOf(propName);\n if (idx !== -1) {\n augmentedDoenetViewerProps[propName] = args[idx + 1];\n }\n }\n }\n if (!doenetViewerPropsSpecified.includes("requestScrollTo")) {\n augmentedDoenetViewerProps.requestScrollTo = (offset) => {\n messageParentFromViewer({ type: "scrollTo", offset });\n };\n }\n window.renderDoenetViewerToContainer(\n document.getElementById("root"),\n void 0,\n augmentedDoenetViewerProps\n );\n }\n function installSharedCorePortProvider() {\n let coreCounter = 0;\n window.doenetGlobalConfig.createExternalCoreWorkerPort = () => {\n const coreId = `${viewerId}-core-${++coreCounter}`;\n const channel = new MessageChannel();\n window.parent.postMessage(\n { origin: viewerId, data: { type: "createSharedCore", coreId } },\n window.parent.origin,\n [channel.port2]\n );\n return {\n port: channel.port1,\n destroy: (suspectWedge) => {\n messageParentFromViewer({\n type: "destroySharedCore",\n coreId,\n suspectWedge: Boolean(suspectWedge)\n });\n }\n };\n };\n }\n (async () => {\n if (await waitForStandaloneBundle(6e4)) {\n if (typeof doenetSharedCoreWorker !== "undefined" && doenetSharedCoreWorker) {\n installSharedCorePortProvider();\n }\n messageParentFromViewer({ iframeReady: true });\n } else {\n messageParentFromViewer({\n error: "Invalid DoenetML version or DoenetML package not found"\n });\n }\n })().catch((err) => {\n console.error(\n "iframe DoenetViewer: unexpected failure while signalling iframeReady",\n err\n );\n try {\n messageParentFromViewer({\n error: "iframe viewer failed to initialize"\n });\n } catch {\n }\n });\n function messageParentFromViewer(data) {\n window.parent.postMessage(\n {\n origin: viewerId,\n data\n },\n window.parent.origin\n );\n }\n})();\n';
|
|
25622
25622
|
const editorIframeJsSource = `(function() {
|
|
25623
25623
|
"use strict";
|
|
25624
25624
|
let editorControlHandle = null;
|
|
@@ -25856,7 +25856,7 @@ function createIframeHead(standaloneUrl, cssUrl) {
|
|
|
25856
25856
|
function createIframeBodyOpenTag(darkMode) {
|
|
25857
25857
|
return `<body style="margin:0" ${BODY_BACKGROUND_ATTRIBUTE}="${bodyBackgroundMode(darkMode)}">`;
|
|
25858
25858
|
}
|
|
25859
|
-
function createHtmlForDoenetViewer(id2, doenetML, doenetViewerProps, standaloneUrl, cssUrl) {
|
|
25859
|
+
function createHtmlForDoenetViewer(id2, doenetML, doenetViewerProps, standaloneUrl, cssUrl, useSharedCoreWorker = false) {
|
|
25860
25860
|
const doenetViewerPropsSpecified = Object.keys(doenetViewerProps);
|
|
25861
25861
|
return `
|
|
25862
25862
|
<html style="overflow:hidden">
|
|
@@ -25866,6 +25866,7 @@ function createHtmlForDoenetViewer(id2, doenetML, doenetViewerProps, standaloneU
|
|
|
25866
25866
|
const viewerId = "${id2}";
|
|
25867
25867
|
const doenetViewerProps = ${JSON.stringify(doenetViewerProps)};
|
|
25868
25868
|
const doenetViewerPropsSpecified = ${JSON.stringify(doenetViewerPropsSpecified)};
|
|
25869
|
+
const doenetSharedCoreWorker = ${JSON.stringify(!!useSharedCoreWorker)};
|
|
25869
25870
|
import * as ComlinkViewer from "https://unpkg.com/comlink/dist/esm/comlink.mjs";
|
|
25870
25871
|
|
|
25871
25872
|
// This source code has been compiled by vite and should be directly included.
|
|
@@ -25907,6 +25908,124 @@ function createHtmlForDoenetEditor(id2, doenetML, width, doenetEditorProps, stan
|
|
|
25907
25908
|
</html>
|
|
25908
25909
|
`;
|
|
25909
25910
|
}
|
|
25911
|
+
const MAX_CORES_PER_WORKER = 12;
|
|
25912
|
+
const hostsByWorkerUrl = /* @__PURE__ */ new Map();
|
|
25913
|
+
const coreRecords = /* @__PURE__ */ new Map();
|
|
25914
|
+
function resolveWorkerUrl(standaloneUrl) {
|
|
25915
|
+
try {
|
|
25916
|
+
return new URL("./doenetml-worker/index.js", standaloneUrl).href;
|
|
25917
|
+
} catch {
|
|
25918
|
+
}
|
|
25919
|
+
try {
|
|
25920
|
+
return new URL("/doenetml-worker/index.js", document.baseURI).href;
|
|
25921
|
+
} catch {
|
|
25922
|
+
return null;
|
|
25923
|
+
}
|
|
25924
|
+
}
|
|
25925
|
+
function workerCreationUrl(workerUrl) {
|
|
25926
|
+
try {
|
|
25927
|
+
if (new URL(workerUrl).origin === window.location.origin) {
|
|
25928
|
+
return workerUrl;
|
|
25929
|
+
}
|
|
25930
|
+
} catch {
|
|
25931
|
+
}
|
|
25932
|
+
const bootstrap = `importScripts(${JSON.stringify(workerUrl)});`;
|
|
25933
|
+
return URL.createObjectURL(
|
|
25934
|
+
new Blob([bootstrap], { type: "text/javascript" })
|
|
25935
|
+
);
|
|
25936
|
+
}
|
|
25937
|
+
function quarantineHost(workerUrl, host) {
|
|
25938
|
+
host.quarantined = true;
|
|
25939
|
+
if (host.liveCores <= 0) {
|
|
25940
|
+
try {
|
|
25941
|
+
host.worker.terminate();
|
|
25942
|
+
} catch {
|
|
25943
|
+
}
|
|
25944
|
+
const pool = hostsByWorkerUrl.get(workerUrl);
|
|
25945
|
+
const index2 = pool?.indexOf(host) ?? -1;
|
|
25946
|
+
if (pool && index2 !== -1) {
|
|
25947
|
+
pool.splice(index2, 1);
|
|
25948
|
+
}
|
|
25949
|
+
}
|
|
25950
|
+
}
|
|
25951
|
+
function getHost(workerUrl) {
|
|
25952
|
+
let pool = hostsByWorkerUrl.get(workerUrl);
|
|
25953
|
+
if (!pool) {
|
|
25954
|
+
pool = [];
|
|
25955
|
+
hostsByWorkerUrl.set(workerUrl, pool);
|
|
25956
|
+
}
|
|
25957
|
+
let host = pool.find(
|
|
25958
|
+
(h2) => !h2.quarantined && h2.liveCores < MAX_CORES_PER_WORKER
|
|
25959
|
+
);
|
|
25960
|
+
if (!host) {
|
|
25961
|
+
const worker = new Worker(workerCreationUrl(workerUrl), {
|
|
25962
|
+
type: "classic"
|
|
25963
|
+
});
|
|
25964
|
+
const newHost = {
|
|
25965
|
+
worker,
|
|
25966
|
+
remote: wrap$3(worker),
|
|
25967
|
+
liveCores: 0,
|
|
25968
|
+
quarantined: false
|
|
25969
|
+
};
|
|
25970
|
+
worker.addEventListener("error", () => {
|
|
25971
|
+
quarantineHost(workerUrl, newHost);
|
|
25972
|
+
});
|
|
25973
|
+
pool.push(newHost);
|
|
25974
|
+
host = newHost;
|
|
25975
|
+
}
|
|
25976
|
+
return host;
|
|
25977
|
+
}
|
|
25978
|
+
function handleCreateSharedCore({
|
|
25979
|
+
viewerId,
|
|
25980
|
+
coreId,
|
|
25981
|
+
standaloneUrl,
|
|
25982
|
+
port
|
|
25983
|
+
}) {
|
|
25984
|
+
const workerUrl = resolveWorkerUrl(standaloneUrl);
|
|
25985
|
+
if (workerUrl === null) {
|
|
25986
|
+
console.warn(
|
|
25987
|
+
"doenetml-iframe: could not resolve a shared core worker URL for",
|
|
25988
|
+
standaloneUrl
|
|
25989
|
+
);
|
|
25990
|
+
return;
|
|
25991
|
+
}
|
|
25992
|
+
const host = getHost(workerUrl);
|
|
25993
|
+
host.liveCores++;
|
|
25994
|
+
const idPromise = host.remote.createCore(transfer(port, [port]));
|
|
25995
|
+
idPromise.catch(() => {
|
|
25996
|
+
});
|
|
25997
|
+
coreRecords.set(coreId, {
|
|
25998
|
+
viewerId,
|
|
25999
|
+
host,
|
|
26000
|
+
workerUrl,
|
|
26001
|
+
idPromise,
|
|
26002
|
+
destroyed: false
|
|
26003
|
+
});
|
|
26004
|
+
}
|
|
26005
|
+
function handleDestroySharedCore({
|
|
26006
|
+
coreId,
|
|
26007
|
+
suspectWedge
|
|
26008
|
+
}) {
|
|
26009
|
+
const record = coreRecords.get(coreId);
|
|
26010
|
+
if (!record || record.destroyed) {
|
|
26011
|
+
return;
|
|
26012
|
+
}
|
|
26013
|
+
record.destroyed = true;
|
|
26014
|
+
coreRecords.delete(coreId);
|
|
26015
|
+
record.host.liveCores--;
|
|
26016
|
+
record.idPromise.then((id2) => record.host.remote.destroyCore(id2)).catch(() => {
|
|
26017
|
+
});
|
|
26018
|
+
if (suspectWedge || record.host.quarantined) {
|
|
26019
|
+
quarantineHost(record.workerUrl, record.host);
|
|
26020
|
+
}
|
|
26021
|
+
}
|
|
26022
|
+
function destroySharedCoresForViewer(viewerId) {
|
|
26023
|
+
for (const [coreId, record] of [...coreRecords]) {
|
|
26024
|
+
if (record.viewerId === viewerId) {
|
|
26025
|
+
handleDestroySharedCore({ coreId, suspectWedge: false });
|
|
26026
|
+
}
|
|
26027
|
+
}
|
|
26028
|
+
}
|
|
25910
26029
|
function findNodesWithPositionInfo(nodes) {
|
|
25911
26030
|
if (typeof nodes !== "object" || nodes === null) {
|
|
25912
26031
|
return [];
|
|
@@ -63053,7 +63172,7 @@ function ExternalVirtualKeyboard({
|
|
|
63053
63172
|
}
|
|
63054
63173
|
);
|
|
63055
63174
|
}
|
|
63056
|
-
const version = "0.7.20-dev.
|
|
63175
|
+
const version = "0.7.20-dev.323";
|
|
63057
63176
|
const latestDoenetmlVersion = version;
|
|
63058
63177
|
function subscribeToPinnedTheme() {
|
|
63059
63178
|
return () => {
|
|
@@ -63074,6 +63193,7 @@ function DoenetViewer({
|
|
|
63074
63193
|
cssUrl: specifiedCssUrl,
|
|
63075
63194
|
doenetmlVersion: specifiedDoenetmlVersion,
|
|
63076
63195
|
autodetectVersion = true,
|
|
63196
|
+
useSharedCoreWorker = false,
|
|
63077
63197
|
...doenetViewerProps
|
|
63078
63198
|
}) {
|
|
63079
63199
|
const [id2, _2] = React__default__default.useState(() => Math.random().toString(36).slice(2));
|
|
@@ -63106,6 +63226,16 @@ function DoenetViewer({
|
|
|
63106
63226
|
} else {
|
|
63107
63227
|
cssUrl = `https://cdn.jsdelivr.net/npm/@doenet/standalone@${selectedDoenetmlVersion}/style.css`;
|
|
63108
63228
|
}
|
|
63229
|
+
const standaloneUrlRef = React__default__default.useRef(standaloneUrl);
|
|
63230
|
+
standaloneUrlRef.current = standaloneUrl;
|
|
63231
|
+
React__default__default.useEffect(() => {
|
|
63232
|
+
if (!useSharedCoreWorker) {
|
|
63233
|
+
return;
|
|
63234
|
+
}
|
|
63235
|
+
return () => {
|
|
63236
|
+
destroySharedCoresForViewer(id2);
|
|
63237
|
+
};
|
|
63238
|
+
}, []);
|
|
63109
63239
|
React__default__default.useEffect(() => {
|
|
63110
63240
|
const listener = (event) => {
|
|
63111
63241
|
if (event.origin !== window.location.origin) {
|
|
@@ -63127,6 +63257,22 @@ function DoenetViewer({
|
|
|
63127
63257
|
return;
|
|
63128
63258
|
}
|
|
63129
63259
|
const data = event.data.data;
|
|
63260
|
+
if (data.type === "createSharedCore" && event.ports[0]) {
|
|
63261
|
+
handleCreateSharedCore({
|
|
63262
|
+
viewerId: id2,
|
|
63263
|
+
coreId: String(data.coreId),
|
|
63264
|
+
standaloneUrl: standaloneUrlRef.current,
|
|
63265
|
+
port: event.ports[0]
|
|
63266
|
+
});
|
|
63267
|
+
return;
|
|
63268
|
+
}
|
|
63269
|
+
if (data.type === "destroySharedCore") {
|
|
63270
|
+
handleDestroySharedCore({
|
|
63271
|
+
coreId: String(data.coreId),
|
|
63272
|
+
suspectWedge: Boolean(data.suspectWedge)
|
|
63273
|
+
});
|
|
63274
|
+
return;
|
|
63275
|
+
}
|
|
63130
63276
|
if (ref.current && data.type === "scrollTo" && typeof data.offset === "number") {
|
|
63131
63277
|
const iframeTop = ref.current.getBoundingClientRect().top + window.scrollY;
|
|
63132
63278
|
const targetAbsoluteTop = iframeTop + data.offset;
|
|
@@ -63208,7 +63354,8 @@ function DoenetViewer({
|
|
|
63208
63354
|
doenetML,
|
|
63209
63355
|
doenetViewerProps,
|
|
63210
63356
|
standaloneUrl,
|
|
63211
|
-
cssUrl
|
|
63357
|
+
cssUrl,
|
|
63358
|
+
useSharedCoreWorker
|
|
63212
63359
|
),
|
|
63213
63360
|
style: {
|
|
63214
63361
|
width: "100%",
|