Socket
Socket
Sign inDemoInstall

@stackblitz/sdk

Package Overview
Dependencies
Maintainers
4
Versions
44
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@stackblitz/sdk - npm Package Compare versions

Comparing version 1.9.0-alpha.0 to 1.9.0-alpha.1

502

bundles/sdk.js

@@ -1,9 +0,503 @@

"use strict";const N="https://stackblitz.com",E=["angular-cli","create-react-app","html","javascript","node","polymer","typescript","vue"],w={clickToLoad:e=>d("ctl",e),devToolsHeight:e=>b("devtoolsheight",e),forceEmbedLayout:e=>d("embed",e),hideDevTools:e=>d("hidedevtools",e),hideExplorer:e=>d("hideExplorer",e),hideNavigation:e=>d("hideNavigation",e),showSidebar:e=>j("showSidebar",e),openFile:e=>C("file",e).join("&"),terminalHeight:e=>b("terminalHeight",e),theme:e=>T("theme",["light","dark"],e),view:e=>T("view",["preview","editor"],e)};function S(e={}){const t=Object.entries(e).map(([n,r])=>r!=null&&w.hasOwnProperty(n)?w[n](r):"").filter(Boolean);return t.length?`?${t.join("&")}`:""}function d(e,t){return t===!0?`${e}=1`:""}function j(e,t){return typeof t=="boolean"?`${e}=${t?"1":"0"}`:""}function b(e,t){if(typeof t=="number"&&!Number.isNaN(t)){const n=Math.min(100,Math.max(0,t));return`${e}=${Math.round(n)}`}return""}function T(e,t,n){return typeof n=="string"&&t.includes(n)?`${e}=${n}`:""}function C(e,t){return(Array.isArray(t)?t:[t]).filter(r=>typeof r=="string"&&r.trim()!=="").map(r=>`${e}=${encodeURIComponent(r.trim())}`)}function P(){return Math.random().toString(36).slice(2,6)+Math.random().toString(36).slice(2,6)}function h(e,t){return`${$(t)}${e}${S(t)}`}function m(e,t){const n={forceEmbedLayout:!0};return t&&typeof t=="object"&&Object.assign(n,t),`${$(n)}${e}${S(n)}`}function $(e={}){return(typeof e.origin=="string"?e.origin:N).replace(/\/$/,"")}function f(e,t,n){if(!t||!e||!e.parentNode)throw new Error("Invalid Element");e.id&&(t.id=e.id),e.className&&(t.className=e.className),D(t,n),e.replaceWith(t)}function _(e){if(typeof e=="string"){const t=document.getElementById(e);if(!t)throw new Error(`Could not find element with id '${e}'`);return t}else if(e instanceof HTMLElement)return e;throw new Error(`Invalid element: ${e}`)}function g(e){return e&&e.newWindow===!1?"_self":"_blank"}function D(e,t={}){const n=Object.hasOwnProperty.call(t,"height")?`${t.height}`:`${300}`,r=Object.hasOwnProperty.call(t,"width")?`${t.width}`:void 0;e.setAttribute("height",n),r?e.setAttribute("width",r):e.setAttribute("style","width:100%;")}class A{constructor(t){this.pending={},this.port=t,this.port.onmessage=this.messageListener.bind(this)}request({type:t,payload:n}){return new Promise((r,i)=>{const o=P();this.pending[o]={resolve:r,reject:i},this.port.postMessage({type:t,payload:{...n,__reqid:o}})})}messageListener(t){if(typeof t.data.payload?.__reqid!="string")return;const{type:n,payload:r}=t.data,{__reqid:i,__success:o,__error:s}=r;this.pending[i]&&(o?this.pending[i].resolve(this.cleanResult(r)):this.pending[i].reject(s?`${n}: ${s}`:n),delete this.pending[i])}cleanResult(t){const n={...t};return delete n.__reqid,delete n.__success,delete n.__error,Object.keys(n).length?n:null}}class F{constructor(t,n){this.editor={openFile:r=>this._rdc.request({type:"SDK_OPEN_FILE",payload:{path:r}}),setCurrentFile:r=>this._rdc.request({type:"SDK_SET_CURRENT_FILE",payload:{path:r}}),setTheme:r=>this._rdc.request({type:"SDK_SET_UI_THEME",payload:{theme:r}}),setView:r=>this._rdc.request({type:"SDK_SET_UI_VIEW",payload:{view:r}}),showSidebar:(r=!0)=>this._rdc.request({type:"SDK_TOGGLE_SIDEBAR",payload:{visible:r}})},this.preview={origin:"",getUrl:()=>this._rdc.request({type:"SDK_GET_PREVIEW_URL",payload:{}}).then(r=>r?.url??null),setUrl:(r="/")=>{if(typeof r!="string"||!r.startsWith("/"))throw new Error(`Invalid argument: expected a path starting with '/', got '${r}'`);return this._rdc.request({type:"SDK_SET_PREVIEW_URL",payload:{path:r}})}},this._rdc=new A(t),Object.defineProperty(this.preview,"origin",{value:typeof n.previewOrigin=="string"?n.previewOrigin:null,writable:!1})}applyFsDiff(t){const n=r=>r!==null&&typeof r=="object";if(!n(t)||!n(t.create))throw new Error("Invalid diff object: expected diff.create to be an object.");if(!Array.isArray(t.destroy))throw new Error("Invalid diff object: expected diff.destroy to be an array.");return this._rdc.request({type:"SDK_APPLY_FS_DIFF",payload:t})}getDependencies(){return this._rdc.request({type:"SDK_GET_DEPS_SNAPSHOT",payload:{}})}getFsSnapshot(){return this._rdc.request({type:"SDK_GET_FS_SNAPSHOT",payload:{}})}}const l=[];class L{constructor(t){this.id=P(),this.element=t,this.pending=new Promise((n,r)=>{const i=({data:a,ports:p})=>{a?.action==="SDK_INIT_SUCCESS"&&a.id===this.id&&(this.vm=new F(p[0],a.payload),n(this.vm),s())},o=()=>{this.element.contentWindow?.postMessage({action:"SDK_INIT",id:this.id},"*")};function s(){window.clearInterval(I),window.removeEventListener("message",i)}window.addEventListener("message",i),o();let y=0;const I=window.setInterval(()=>{if(this.vm){s();return}if(y>=20){s(),r("Timeout: Unable to establish a connection with the StackBlitz VM"),l.forEach((a,p)=>{a.id===this.id&&l.splice(p,1)});return}y++,o()},500)}),l.push(this)}}const O=e=>{const t=e instanceof Element?"element":"id";return l.find(n=>n[t]===e)??null};function c(e,t){const n=document.createElement("input");return n.type="hidden",n.name=e,n.value=t,n}function M(e){return`[${e.replace(/\[/g,"%5B").replace(/\]/g,"%5D")}]`}function v(e){if(!E.includes(e.template)){const r=E.map(i=>`'${i}'`).join(", ");console.warn(`Unsupported project.template: must be one of ${r}`)}const t=e.template==="node",n=document.createElement("form");return n.method="POST",n.setAttribute("style","display:none!important;"),n.appendChild(c("project[title]",e.title)),n.appendChild(c("project[description]",e.description)),n.appendChild(c("project[template]",e.template)),e.dependencies&&(t?console.warn("Invalid project.dependencies: dependencies must be provided as a 'package.json' file when using the 'node' template."):n.appendChild(c("project[dependencies]",JSON.stringify(e.dependencies)))),e.settings&&n.appendChild(c("project[settings]",JSON.stringify(e.settings))),Object.keys(e.files).forEach(r=>{const i="project[files]"+M(r),o=e.files[r];typeof o=="string"&&n.appendChild(c(i,o))}),n}function R(e,t){const n=v(e);return n.action=m("/run",t),n.id="sb_run",`<!doctype html>
"use strict";
const CONNECT_INTERVAL = 500;
const CONNECT_MAX_ATTEMPTS = 20;
const DEFAULT_FRAME_HEIGHT = 300;
const DEFAULT_ORIGIN = "https://stackblitz.com";
const PROJECT_TEMPLATES = [
"angular-cli",
"create-react-app",
"html",
"javascript",
"node",
"polymer",
"typescript",
"vue"
];
const generators = {
clickToLoad: (value) => trueParam("ctl", value),
devToolsHeight: (value) => percentParam("devtoolsheight", value),
forceEmbedLayout: (value) => trueParam("embed", value),
hideDevTools: (value) => trueParam("hidedevtools", value),
hideExplorer: (value) => trueParam("hideExplorer", value),
hideNavigation: (value) => trueParam("hideNavigation", value),
showSidebar: (value) => booleanParam("showSidebar", value),
openFile: (value) => stringParams("file", value).join("&"),
terminalHeight: (value) => percentParam("terminalHeight", value),
theme: (value) => enumParam("theme", ["light", "dark"], value),
view: (value) => enumParam("view", ["preview", "editor"], value)
};
function buildParams(options = {}) {
const params = Object.entries(options).map(([key, value]) => {
if (value != null && generators.hasOwnProperty(key)) {
return generators[key](value);
}
return "";
}).filter(Boolean);
return params.length ? `?${params.join("&")}` : "";
}
function trueParam(name, value) {
if (value === true) {
return `${name}=1`;
}
return "";
}
function booleanParam(name, value) {
if (typeof value === "boolean") {
return `${name}=${value ? "1" : "0"}`;
}
return "";
}
function percentParam(name, value) {
if (typeof value === "number" && !Number.isNaN(value)) {
const clamped = Math.min(100, Math.max(0, value));
return `${name}=${Math.round(clamped)}`;
}
return "";
}
function enumParam(name, oneOf, value) {
if (typeof value === "string" && oneOf.includes(value)) {
return `${name}=${value}`;
}
return "";
}
function stringParams(name, value) {
const values = Array.isArray(value) ? value : [value];
return values.filter((val) => typeof val === "string" && val.trim() !== "").map((val) => `${name}=${encodeURIComponent(val.trim())}`);
}
function genID() {
return Math.random().toString(36).slice(2, 6) + Math.random().toString(36).slice(2, 6);
}
function openUrl(route, options) {
return `${getOrigin(options)}${route}${buildParams(options)}`;
}
function embedUrl(route, options) {
const config = {
forceEmbedLayout: true
};
if (options && typeof options === "object") {
Object.assign(config, options);
}
return `${getOrigin(config)}${route}${buildParams(config)}`;
}
function getOrigin(options = {}) {
const origin = typeof options.origin === "string" ? options.origin : DEFAULT_ORIGIN;
return origin.replace(/\/$/, "");
}
function replaceAndEmbed(target, frame, options) {
if (!frame || !target || !target.parentNode) {
throw new Error("Invalid Element");
}
if (target.id) {
frame.id = target.id;
}
if (target.className) {
frame.className = target.className;
}
setFrameDimensions(frame, options);
target.replaceWith(frame);
}
function findElement(elementOrId) {
if (typeof elementOrId === "string") {
const element = document.getElementById(elementOrId);
if (!element) {
throw new Error(`Could not find element with id '${elementOrId}'`);
}
return element;
} else if (elementOrId instanceof HTMLElement) {
return elementOrId;
}
throw new Error(`Invalid element: ${elementOrId}`);
}
function openTarget(options) {
return options && options.newWindow === false ? "_self" : "_blank";
}
function setFrameDimensions(frame, options = {}) {
const height = Object.hasOwnProperty.call(options, "height") ? `${options.height}` : `${DEFAULT_FRAME_HEIGHT}`;
const width = Object.hasOwnProperty.call(options, "width") ? `${options.width}` : void 0;
frame.setAttribute("height", height);
if (width) {
frame.setAttribute("width", width);
} else {
frame.setAttribute("style", "width:100%;");
}
}
class RDC {
constructor(port) {
this.pending = {};
this.port = port;
this.port.onmessage = this.messageListener.bind(this);
}
request({ type, payload }) {
return new Promise((resolve, reject) => {
const id = genID();
this.pending[id] = { resolve, reject };
this.port.postMessage({
type,
payload: {
...payload,
// Ensure the payload object includes the request ID
__reqid: id
}
});
});
}
messageListener(event) {
if (typeof event.data.payload?.__reqid !== "string") {
return;
}
const { type, payload } = event.data;
const { __reqid: id, __success: success, __error: error } = payload;
if (this.pending[id]) {
if (success) {
this.pending[id].resolve(this.cleanResult(payload));
} else {
this.pending[id].reject(error ? `${type}: ${error}` : type);
}
delete this.pending[id];
}
}
cleanResult(payload) {
const result = { ...payload };
delete result.__reqid;
delete result.__success;
delete result.__error;
return Object.keys(result).length ? result : null;
}
}
class VM {
constructor(port, config) {
this.editor = {
/**
* Open one of several files in tabs and/or split panes.
*
* @since 1.7.0 Added support for opening multiple files
*/
openFile: (path) => {
return this._rdc.request({
type: "SDK_OPEN_FILE",
payload: { path }
});
},
/**
* Set a project file as the currently selected file.
*
* - This may update the highlighted file in the file explorer,
* and the currently open and/or focused editor tab.
* - It will _not_ open a new editor tab if the provided path does not
* match a currently open tab. See `vm.editor.openFile` to open files.
*
* @since 1.7.0
* @experimental
*/
setCurrentFile: (path) => {
return this._rdc.request({
type: "SDK_SET_CURRENT_FILE",
payload: { path }
});
},
/**
* Change the color theme
*
* @since 1.7.0
*/
setTheme: (theme) => {
return this._rdc.request({
type: "SDK_SET_UI_THEME",
payload: { theme }
});
},
/**
* Change the display mode of the project:
*
* - `default`: show the editor and preview pane
* - `editor`: show the editor pane only
* - `preview`: show the preview pane only
*
* @since 1.7.0
*/
setView: (view) => {
return this._rdc.request({
type: "SDK_SET_UI_VIEW",
payload: { view }
});
},
/**
* Change the display mode of the sidebar:
*
* - `true`: show the sidebar
* - `false`: hide the sidebar
*
* @since 1.7.0
*/
showSidebar: (visible = true) => {
return this._rdc.request({
type: "SDK_TOGGLE_SIDEBAR",
payload: { visible }
});
}
};
this.preview = {
/**
* The origin (protocol and domain) of the preview iframe.
*
* In WebContainers-based projects, the origin will always be `null`;
* try using `vm.preview.getUrl` instead.
*
* @see https://developer.stackblitz.com/guides/user-guide/available-environments
*/
origin: "",
/**
* Get the current preview URL.
*
* In both and EngineBlock and WebContainers-based projects, the preview URL
* may not reflect the exact path of the current page, after user navigation.
*
* In WebContainers-based projects, the preview URL will be `null` initially,
* and until the project starts a web server.
*
* @since 1.7.0
* @experimental
*/
getUrl: () => {
return this._rdc.request({
type: "SDK_GET_PREVIEW_URL",
payload: {}
}).then((data) => data?.url ?? null);
},
/**
* Change the path of the preview URL.
*
* In WebContainers-based projects, this will be ignored if there is no
* currently running web server.
*
* @since 1.7.0
* @experimental
*/
setUrl: (path = "/") => {
if (typeof path !== "string" || !path.startsWith("/")) {
throw new Error(`Invalid argument: expected a path starting with '/', got '${path}'`);
}
return this._rdc.request({
type: "SDK_SET_PREVIEW_URL",
payload: { path }
});
}
};
this._rdc = new RDC(port);
Object.defineProperty(this.preview, "origin", {
value: typeof config.previewOrigin === "string" ? config.previewOrigin : null,
writable: false
});
}
/**
* Apply batch updates to the project files in one call.
*/
applyFsDiff(diff) {
const isObject = (val) => val !== null && typeof val === "object";
if (!isObject(diff) || !isObject(diff.create)) {
throw new Error("Invalid diff object: expected diff.create to be an object.");
} else if (!Array.isArray(diff.destroy)) {
throw new Error("Invalid diff object: expected diff.destroy to be an array.");
}
return this._rdc.request({
type: "SDK_APPLY_FS_DIFF",
payload: diff
});
}
/**
* Get the project’s defined dependencies.
*
* In EngineBlock projects, version numbers represent the resolved dependency versions.
* In WebContainers-based projects, returns data from the project’s `package.json` without resolving installed version numbers.
*/
getDependencies() {
return this._rdc.request({
type: "SDK_GET_DEPS_SNAPSHOT",
payload: {}
});
}
/**
* Get a snapshot of the project files and their content.
*/
getFsSnapshot() {
return this._rdc.request({
type: "SDK_GET_FS_SNAPSHOT",
payload: {}
});
}
}
const connections = [];
class Connection {
constructor(element) {
this.id = genID();
this.element = element;
this.pending = new Promise((resolve, reject) => {
const listenForSuccess = ({ data, ports }) => {
if (data?.action === "SDK_INIT_SUCCESS" && data.id === this.id) {
this.vm = new VM(ports[0], data.payload);
resolve(this.vm);
cleanup();
}
};
const pingFrame = () => {
this.element.contentWindow?.postMessage(
{
action: "SDK_INIT",
id: this.id
},
"*"
);
};
function cleanup() {
window.clearInterval(interval);
window.removeEventListener("message", listenForSuccess);
}
window.addEventListener("message", listenForSuccess);
pingFrame();
let attempts = 0;
const interval = window.setInterval(() => {
if (this.vm) {
cleanup();
return;
}
if (attempts >= CONNECT_MAX_ATTEMPTS) {
cleanup();
reject("Timeout: Unable to establish a connection with the StackBlitz VM");
connections.forEach((connection, index) => {
if (connection.id === this.id) {
connections.splice(index, 1);
}
});
return;
}
attempts++;
pingFrame();
}, CONNECT_INTERVAL);
});
connections.push(this);
}
}
const getConnection = (identifier) => {
const key = identifier instanceof Element ? "element" : "id";
return connections.find((c) => c[key] === identifier) ?? null;
};
function createHiddenInput(name, value) {
const input = document.createElement("input");
input.type = "hidden";
input.name = name;
input.value = value;
return input;
}
function bracketedFilePath(path) {
return `[${path.replace(/\[/g, "%5B").replace(/\]/g, "%5D")}]`;
}
function createProjectForm(project) {
if (!PROJECT_TEMPLATES.includes(project.template)) {
const names = PROJECT_TEMPLATES.map((t) => `'${t}'`).join(", ");
console.warn(`Unsupported project.template: must be one of ${names}`);
}
const isWebContainers = project.template === "node";
const form = document.createElement("form");
form.method = "POST";
form.setAttribute("style", "display:none!important;");
form.appendChild(createHiddenInput("project[title]", project.title));
form.appendChild(createHiddenInput("project[description]", project.description));
form.appendChild(createHiddenInput("project[template]", project.template));
if (project.dependencies) {
if (isWebContainers) {
console.warn(
`Invalid project.dependencies: dependencies must be provided as a 'package.json' file when using the 'node' template.`
);
} else {
form.appendChild(
createHiddenInput("project[dependencies]", JSON.stringify(project.dependencies))
);
}
}
if (project.settings) {
form.appendChild(createHiddenInput("project[settings]", JSON.stringify(project.settings)));
}
Object.keys(project.files).forEach((path) => {
const name = "project[files]" + bracketedFilePath(path);
const value = project.files[path];
if (typeof value === "string") {
form.appendChild(createHiddenInput(name, value));
}
});
return form;
}
function createProjectFrameHTML(project, options) {
const form = createProjectForm(project);
form.action = embedUrl("/run", options);
form.id = "sb_run";
const html = `<!doctype html>
<html>
<head><title></title></head>
<body>
${n.outerHTML}
<script>document.getElementById('${n.id}').submit();<\/script>
${form.outerHTML}
<script>document.getElementById('${form.id}').submit();<\/script>
</body>
</html>`}function U(e,t){const n=v(e);n.action=h("/run",t),n.target=g(t),document.body.appendChild(n),n.submit(),document.body.removeChild(n)}function u(e){return e?.contentWindow?(O(e)??new L(e)).pending:Promise.reject("Provided element is not an iframe.")}function q(e,t){U(e,t)}function H(e,t){const n=h(`/edit/${e}`,t),r=g(t);window.open(n,r)}function K(e,t){const n=h(`/github/${e}`,t),r=g(t);window.open(n,r)}function k(e,t,n){const r=_(e),i=R(t,n),o=document.createElement("iframe");return f(r,o,n),o.contentDocument?.write(i),u(o)}function G(e,t,n){const r=_(e),i=document.createElement("iframe");return i.src=m(`/edit/${t}`,n),f(r,i,n),u(i)}function W(e,t,n){const r=_(e),i=document.createElement("iframe");return i.src=m(`/github/${t}`,n),f(r,i,n),u(i)}const V={connect:u,embedGithubProject:W,embedProject:k,embedProjectId:G,openGithubProject:K,openProject:q,openProjectId:H};module.exports=V;
</html>`;
return html;
}
function openNewProject(project, options) {
const form = createProjectForm(project);
form.action = openUrl("/run", options);
form.target = openTarget(options);
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
}
function connect(frameEl) {
if (!frameEl?.contentWindow) {
return Promise.reject("Provided element is not an iframe.");
}
const connection = getConnection(frameEl) ?? new Connection(frameEl);
return connection.pending;
}
function openProject(project, options) {
openNewProject(project, options);
}
function openProjectId(projectId, options) {
const url = openUrl(`/edit/${projectId}`, options);
const target = openTarget(options);
window.open(url, target);
}
function openGithubProject(repoSlug, options) {
const url = openUrl(`/github/${repoSlug}`, options);
const target = openTarget(options);
window.open(url, target);
}
function embedProject(elementOrId, project, options) {
const element = findElement(elementOrId);
const html = createProjectFrameHTML(project, options);
const frame = document.createElement("iframe");
replaceAndEmbed(element, frame, options);
frame.contentDocument?.write(html);
return connect(frame);
}
function embedProjectId(elementOrId, projectId, options) {
const element = findElement(elementOrId);
const frame = document.createElement("iframe");
frame.src = embedUrl(`/edit/${projectId}`, options);
replaceAndEmbed(element, frame, options);
return connect(frame);
}
function embedGithubProject(elementOrId, repoSlug, options) {
const element = findElement(elementOrId);
const frame = document.createElement("iframe");
frame.src = embedUrl(`/github/${repoSlug}`, options);
replaceAndEmbed(element, frame, options);
return connect(frame);
}
const StackBlitzSDK = {
connect,
embedGithubProject,
embedProject,
embedProjectId,
openGithubProject,
openProject,
openProjectId
};
module.exports = StackBlitzSDK;
//# sourceMappingURL=sdk.js.map

505

bundles/sdk.m.js

@@ -1,2 +0,6 @@

const N = "https://stackblitz.com", E = [
const CONNECT_INTERVAL = 500;
const CONNECT_MAX_ATTEMPTS = 20;
const DEFAULT_FRAME_HEIGHT = 300;
const DEFAULT_ORIGIN = "https://stackblitz.com";
const PROJECT_TEMPLATES = [
"angular-cli",

@@ -10,88 +14,127 @@ "create-react-app",

"vue"
], w = {
clickToLoad: (e) => d("ctl", e),
devToolsHeight: (e) => b("devtoolsheight", e),
forceEmbedLayout: (e) => d("embed", e),
hideDevTools: (e) => d("hidedevtools", e),
hideExplorer: (e) => d("hideExplorer", e),
hideNavigation: (e) => d("hideNavigation", e),
showSidebar: (e) => j("showSidebar", e),
openFile: (e) => C("file", e).join("&"),
terminalHeight: (e) => b("terminalHeight", e),
theme: (e) => T("theme", ["light", "dark"], e),
view: (e) => T("view", ["preview", "editor"], e)
];
const generators = {
clickToLoad: (value) => trueParam("ctl", value),
devToolsHeight: (value) => percentParam("devtoolsheight", value),
forceEmbedLayout: (value) => trueParam("embed", value),
hideDevTools: (value) => trueParam("hidedevtools", value),
hideExplorer: (value) => trueParam("hideExplorer", value),
hideNavigation: (value) => trueParam("hideNavigation", value),
showSidebar: (value) => booleanParam("showSidebar", value),
openFile: (value) => stringParams("file", value).join("&"),
terminalHeight: (value) => percentParam("terminalHeight", value),
theme: (value) => enumParam("theme", ["light", "dark"], value),
view: (value) => enumParam("view", ["preview", "editor"], value)
};
function S(e = {}) {
const t = Object.entries(e).map(([n, r]) => r != null && w.hasOwnProperty(n) ? w[n](r) : "").filter(Boolean);
return t.length ? `?${t.join("&")}` : "";
function buildParams(options = {}) {
const params = Object.entries(options).map(([key, value]) => {
if (value != null && generators.hasOwnProperty(key)) {
return generators[key](value);
}
return "";
}).filter(Boolean);
return params.length ? `?${params.join("&")}` : "";
}
function d(e, t) {
return t === !0 ? `${e}=1` : "";
function trueParam(name, value) {
if (value === true) {
return `${name}=1`;
}
return "";
}
function j(e, t) {
return typeof t == "boolean" ? `${e}=${t ? "1" : "0"}` : "";
function booleanParam(name, value) {
if (typeof value === "boolean") {
return `${name}=${value ? "1" : "0"}`;
}
return "";
}
function b(e, t) {
if (typeof t == "number" && !Number.isNaN(t)) {
const n = Math.min(100, Math.max(0, t));
return `${e}=${Math.round(n)}`;
function percentParam(name, value) {
if (typeof value === "number" && !Number.isNaN(value)) {
const clamped = Math.min(100, Math.max(0, value));
return `${name}=${Math.round(clamped)}`;
}
return "";
}
function T(e, t, n) {
return typeof n == "string" && t.includes(n) ? `${e}=${n}` : "";
function enumParam(name, oneOf, value) {
if (typeof value === "string" && oneOf.includes(value)) {
return `${name}=${value}`;
}
return "";
}
function C(e, t) {
return (Array.isArray(t) ? t : [t]).filter((r) => typeof r == "string" && r.trim() !== "").map((r) => `${e}=${encodeURIComponent(r.trim())}`);
function stringParams(name, value) {
const values = Array.isArray(value) ? value : [value];
return values.filter((val) => typeof val === "string" && val.trim() !== "").map((val) => `${name}=${encodeURIComponent(val.trim())}`);
}
function P() {
function genID() {
return Math.random().toString(36).slice(2, 6) + Math.random().toString(36).slice(2, 6);
}
function h(e, t) {
return `${$(t)}${e}${S(t)}`;
function openUrl(route, options) {
return `${getOrigin(options)}${route}${buildParams(options)}`;
}
function m(e, t) {
const n = {
forceEmbedLayout: !0
function embedUrl(route, options) {
const config = {
forceEmbedLayout: true
};
return t && typeof t == "object" && Object.assign(n, t), `${$(n)}${e}${S(n)}`;
if (options && typeof options === "object") {
Object.assign(config, options);
}
return `${getOrigin(config)}${route}${buildParams(config)}`;
}
function $(e = {}) {
return (typeof e.origin == "string" ? e.origin : N).replace(/\/$/, "");
function getOrigin(options = {}) {
const origin = typeof options.origin === "string" ? options.origin : DEFAULT_ORIGIN;
return origin.replace(/\/$/, "");
}
function f(e, t, n) {
if (!t || !e || !e.parentNode)
function replaceAndEmbed(target, frame, options) {
if (!frame || !target || !target.parentNode) {
throw new Error("Invalid Element");
e.id && (t.id = e.id), e.className && (t.className = e.className), D(t, n), e.replaceWith(t);
}
if (target.id) {
frame.id = target.id;
}
if (target.className) {
frame.className = target.className;
}
setFrameDimensions(frame, options);
target.replaceWith(frame);
}
function _(e) {
if (typeof e == "string") {
const t = document.getElementById(e);
if (!t)
throw new Error(`Could not find element with id '${e}'`);
return t;
} else if (e instanceof HTMLElement)
return e;
throw new Error(`Invalid element: ${e}`);
function findElement(elementOrId) {
if (typeof elementOrId === "string") {
const element = document.getElementById(elementOrId);
if (!element) {
throw new Error(`Could not find element with id '${elementOrId}'`);
}
return element;
} else if (elementOrId instanceof HTMLElement) {
return elementOrId;
}
throw new Error(`Invalid element: ${elementOrId}`);
}
function g(e) {
return e && e.newWindow === !1 ? "_self" : "_blank";
function openTarget(options) {
return options && options.newWindow === false ? "_self" : "_blank";
}
function D(e, t = {}) {
const n = Object.hasOwnProperty.call(t, "height") ? `${t.height}` : `${300}`, r = Object.hasOwnProperty.call(t, "width") ? `${t.width}` : void 0;
e.setAttribute("height", n), r ? e.setAttribute("width", r) : e.setAttribute("style", "width:100%;");
function setFrameDimensions(frame, options = {}) {
const height = Object.hasOwnProperty.call(options, "height") ? `${options.height}` : `${DEFAULT_FRAME_HEIGHT}`;
const width = Object.hasOwnProperty.call(options, "width") ? `${options.width}` : void 0;
frame.setAttribute("height", height);
if (width) {
frame.setAttribute("width", width);
} else {
frame.setAttribute("style", "width:100%;");
}
}
class A {
constructor(t) {
this.pending = {}, this.port = t, this.port.onmessage = this.messageListener.bind(this);
class RDC {
constructor(port) {
this.pending = {};
this.port = port;
this.port.onmessage = this.messageListener.bind(this);
}
request({ type: t, payload: n }) {
return new Promise((r, i) => {
const o = P();
this.pending[o] = { resolve: r, reject: i }, this.port.postMessage({
type: t,
request({ type, payload }) {
return new Promise((resolve, reject) => {
const id = genID();
this.pending[id] = { resolve, reject };
this.port.postMessage({
type,
payload: {
...n,
...payload,
// Ensure the payload object includes the request ID
__reqid: o
__reqid: id
}

@@ -101,15 +144,27 @@ });

}
messageListener(t) {
if (typeof t.data.payload?.__reqid != "string")
messageListener(event) {
if (typeof event.data.payload?.__reqid !== "string") {
return;
const { type: n, payload: r } = t.data, { __reqid: i, __success: o, __error: s } = r;
this.pending[i] && (o ? this.pending[i].resolve(this.cleanResult(r)) : this.pending[i].reject(s ? `${n}: ${s}` : n), delete this.pending[i]);
}
const { type, payload } = event.data;
const { __reqid: id, __success: success, __error: error } = payload;
if (this.pending[id]) {
if (success) {
this.pending[id].resolve(this.cleanResult(payload));
} else {
this.pending[id].reject(error ? `${type}: ${error}` : type);
}
delete this.pending[id];
}
}
cleanResult(t) {
const n = { ...t };
return delete n.__reqid, delete n.__success, delete n.__error, Object.keys(n).length ? n : null;
cleanResult(payload) {
const result = { ...payload };
delete result.__reqid;
delete result.__success;
delete result.__error;
return Object.keys(result).length ? result : null;
}
}
class F {
constructor(t, n) {
class VM {
constructor(port, config) {
this.editor = {

@@ -121,6 +176,8 @@ /**

*/
openFile: (r) => this._rdc.request({
type: "SDK_OPEN_FILE",
payload: { path: r }
}),
openFile: (path) => {
return this._rdc.request({
type: "SDK_OPEN_FILE",
payload: { path }
});
},
/**

@@ -137,6 +194,8 @@ * Set a project file as the currently selected file.

*/
setCurrentFile: (r) => this._rdc.request({
type: "SDK_SET_CURRENT_FILE",
payload: { path: r }
}),
setCurrentFile: (path) => {
return this._rdc.request({
type: "SDK_SET_CURRENT_FILE",
payload: { path }
});
},
/**

@@ -147,6 +206,8 @@ * Change the color theme

*/
setTheme: (r) => this._rdc.request({
type: "SDK_SET_UI_THEME",
payload: { theme: r }
}),
setTheme: (theme) => {
return this._rdc.request({
type: "SDK_SET_UI_THEME",
payload: { theme }
});
},
/**

@@ -161,6 +222,8 @@ * Change the display mode of the project:

*/
setView: (r) => this._rdc.request({
type: "SDK_SET_UI_VIEW",
payload: { view: r }
}),
setView: (view) => {
return this._rdc.request({
type: "SDK_SET_UI_VIEW",
payload: { view }
});
},
/**

@@ -174,7 +237,10 @@ * Change the display mode of the sidebar:

*/
showSidebar: (r = !0) => this._rdc.request({
type: "SDK_TOGGLE_SIDEBAR",
payload: { visible: r }
})
}, this.preview = {
showSidebar: (visible = true) => {
return this._rdc.request({
type: "SDK_TOGGLE_SIDEBAR",
payload: { visible }
});
}
};
this.preview = {
/**

@@ -201,6 +267,8 @@ * The origin (protocol and domain) of the preview iframe.

*/
getUrl: () => this._rdc.request({
type: "SDK_GET_PREVIEW_URL",
payload: {}
}).then((r) => r?.url ?? null),
getUrl: () => {
return this._rdc.request({
type: "SDK_GET_PREVIEW_URL",
payload: {}
}).then((data) => data?.url ?? null);
},
/**

@@ -215,13 +283,16 @@ * Change the path of the preview URL.

*/
setUrl: (r = "/") => {
if (typeof r != "string" || !r.startsWith("/"))
throw new Error(`Invalid argument: expected a path starting with '/', got '${r}'`);
setUrl: (path = "/") => {
if (typeof path !== "string" || !path.startsWith("/")) {
throw new Error(`Invalid argument: expected a path starting with '/', got '${path}'`);
}
return this._rdc.request({
type: "SDK_SET_PREVIEW_URL",
payload: { path: r }
payload: { path }
});
}
}, this._rdc = new A(t), Object.defineProperty(this.preview, "origin", {
value: typeof n.previewOrigin == "string" ? n.previewOrigin : null,
writable: !1
};
this._rdc = new RDC(port);
Object.defineProperty(this.preview, "origin", {
value: typeof config.previewOrigin === "string" ? config.previewOrigin : null,
writable: false
});

@@ -232,11 +303,12 @@ }

*/
applyFsDiff(t) {
const n = (r) => r !== null && typeof r == "object";
if (!n(t) || !n(t.create))
applyFsDiff(diff) {
const isObject = (val) => val !== null && typeof val === "object";
if (!isObject(diff) || !isObject(diff.create)) {
throw new Error("Invalid diff object: expected diff.create to be an object.");
if (!Array.isArray(t.destroy))
} else if (!Array.isArray(diff.destroy)) {
throw new Error("Invalid diff object: expected diff.destroy to be an array.");
}
return this._rdc.request({
type: "SDK_APPLY_FS_DIFF",
payload: t
payload: diff
});

@@ -266,9 +338,16 @@ }

}
const l = [];
class L {
constructor(t) {
this.id = P(), this.element = t, this.pending = new Promise((n, r) => {
const i = ({ data: a, ports: p }) => {
a?.action === "SDK_INIT_SUCCESS" && a.id === this.id && (this.vm = new F(p[0], a.payload), n(this.vm), s());
}, o = () => {
const connections = [];
class Connection {
constructor(element) {
this.id = genID();
this.element = element;
this.pending = new Promise((resolve, reject) => {
const listenForSuccess = ({ data, ports }) => {
if (data?.action === "SDK_INIT_SUCCESS" && data.id === this.id) {
this.vm = new VM(ports[0], data.payload);
resolve(this.vm);
cleanup();
}
};
const pingFrame = () => {
this.element.contentWindow?.postMessage(

@@ -282,102 +361,156 @@ {

};
function s() {
window.clearInterval(I), window.removeEventListener("message", i);
function cleanup() {
window.clearInterval(interval);
window.removeEventListener("message", listenForSuccess);
}
window.addEventListener("message", i), o();
let y = 0;
const I = window.setInterval(() => {
window.addEventListener("message", listenForSuccess);
pingFrame();
let attempts = 0;
const interval = window.setInterval(() => {
if (this.vm) {
s();
cleanup();
return;
}
if (y >= 20) {
s(), r("Timeout: Unable to establish a connection with the StackBlitz VM"), l.forEach((a, p) => {
a.id === this.id && l.splice(p, 1);
if (attempts >= CONNECT_MAX_ATTEMPTS) {
cleanup();
reject("Timeout: Unable to establish a connection with the StackBlitz VM");
connections.forEach((connection, index) => {
if (connection.id === this.id) {
connections.splice(index, 1);
}
});
return;
}
y++, o();
}, 500);
}), l.push(this);
attempts++;
pingFrame();
}, CONNECT_INTERVAL);
});
connections.push(this);
}
}
const O = (e) => {
const t = e instanceof Element ? "element" : "id";
return l.find((n) => n[t] === e) ?? null;
const getConnection = (identifier) => {
const key = identifier instanceof Element ? "element" : "id";
return connections.find((c) => c[key] === identifier) ?? null;
};
function c(e, t) {
const n = document.createElement("input");
return n.type = "hidden", n.name = e, n.value = t, n;
function createHiddenInput(name, value) {
const input = document.createElement("input");
input.type = "hidden";
input.name = name;
input.value = value;
return input;
}
function M(e) {
return `[${e.replace(/\[/g, "%5B").replace(/\]/g, "%5D")}]`;
function bracketedFilePath(path) {
return `[${path.replace(/\[/g, "%5B").replace(/\]/g, "%5D")}]`;
}
function v(e) {
if (!E.includes(e.template)) {
const r = E.map((i) => `'${i}'`).join(", ");
console.warn(`Unsupported project.template: must be one of ${r}`);
function createProjectForm(project) {
if (!PROJECT_TEMPLATES.includes(project.template)) {
const names = PROJECT_TEMPLATES.map((t) => `'${t}'`).join(", ");
console.warn(`Unsupported project.template: must be one of ${names}`);
}
const t = e.template === "node", n = document.createElement("form");
return n.method = "POST", n.setAttribute("style", "display:none!important;"), n.appendChild(c("project[title]", e.title)), n.appendChild(c("project[description]", e.description)), n.appendChild(c("project[template]", e.template)), e.dependencies && (t ? console.warn(
"Invalid project.dependencies: dependencies must be provided as a 'package.json' file when using the 'node' template."
) : n.appendChild(
c("project[dependencies]", JSON.stringify(e.dependencies))
)), e.settings && n.appendChild(c("project[settings]", JSON.stringify(e.settings))), Object.keys(e.files).forEach((r) => {
const i = "project[files]" + M(r), o = e.files[r];
typeof o == "string" && n.appendChild(c(i, o));
}), n;
const isWebContainers = project.template === "node";
const form = document.createElement("form");
form.method = "POST";
form.setAttribute("style", "display:none!important;");
form.appendChild(createHiddenInput("project[title]", project.title));
form.appendChild(createHiddenInput("project[description]", project.description));
form.appendChild(createHiddenInput("project[template]", project.template));
if (project.dependencies) {
if (isWebContainers) {
console.warn(
`Invalid project.dependencies: dependencies must be provided as a 'package.json' file when using the 'node' template.`
);
} else {
form.appendChild(
createHiddenInput("project[dependencies]", JSON.stringify(project.dependencies))
);
}
}
if (project.settings) {
form.appendChild(createHiddenInput("project[settings]", JSON.stringify(project.settings)));
}
Object.keys(project.files).forEach((path) => {
const name = "project[files]" + bracketedFilePath(path);
const value = project.files[path];
if (typeof value === "string") {
form.appendChild(createHiddenInput(name, value));
}
});
return form;
}
function R(e, t) {
const n = v(e);
return n.action = m("/run", t), n.id = "sb_run", `<!doctype html>
function createProjectFrameHTML(project, options) {
const form = createProjectForm(project);
form.action = embedUrl("/run", options);
form.id = "sb_run";
const html = `<!doctype html>
<html>
<head><title></title></head>
<body>
${n.outerHTML}
<script>document.getElementById('${n.id}').submit();<\/script>
${form.outerHTML}
<script>document.getElementById('${form.id}').submit();<\/script>
</body>
</html>`;
return html;
}
function U(e, t) {
const n = v(e);
n.action = h("/run", t), n.target = g(t), document.body.appendChild(n), n.submit(), document.body.removeChild(n);
function openNewProject(project, options) {
const form = createProjectForm(project);
form.action = openUrl("/run", options);
form.target = openTarget(options);
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
}
function u(e) {
return e?.contentWindow ? (O(e) ?? new L(e)).pending : Promise.reject("Provided element is not an iframe.");
function connect(frameEl) {
if (!frameEl?.contentWindow) {
return Promise.reject("Provided element is not an iframe.");
}
const connection = getConnection(frameEl) ?? new Connection(frameEl);
return connection.pending;
}
function q(e, t) {
U(e, t);
function openProject(project, options) {
openNewProject(project, options);
}
function H(e, t) {
const n = h(`/edit/${e}`, t), r = g(t);
window.open(n, r);
function openProjectId(projectId, options) {
const url = openUrl(`/edit/${projectId}`, options);
const target = openTarget(options);
window.open(url, target);
}
function K(e, t) {
const n = h(`/github/${e}`, t), r = g(t);
window.open(n, r);
function openGithubProject(repoSlug, options) {
const url = openUrl(`/github/${repoSlug}`, options);
const target = openTarget(options);
window.open(url, target);
}
function k(e, t, n) {
const r = _(e), i = R(t, n), o = document.createElement("iframe");
return f(r, o, n), o.contentDocument?.write(i), u(o);
function embedProject(elementOrId, project, options) {
const element = findElement(elementOrId);
const html = createProjectFrameHTML(project, options);
const frame = document.createElement("iframe");
replaceAndEmbed(element, frame, options);
frame.contentDocument?.write(html);
return connect(frame);
}
function G(e, t, n) {
const r = _(e), i = document.createElement("iframe");
return i.src = m(`/edit/${t}`, n), f(r, i, n), u(i);
function embedProjectId(elementOrId, projectId, options) {
const element = findElement(elementOrId);
const frame = document.createElement("iframe");
frame.src = embedUrl(`/edit/${projectId}`, options);
replaceAndEmbed(element, frame, options);
return connect(frame);
}
function W(e, t, n) {
const r = _(e), i = document.createElement("iframe");
return i.src = m(`/github/${t}`, n), f(r, i, n), u(i);
function embedGithubProject(elementOrId, repoSlug, options) {
const element = findElement(elementOrId);
const frame = document.createElement("iframe");
frame.src = embedUrl(`/github/${repoSlug}`, options);
replaceAndEmbed(element, frame, options);
return connect(frame);
}
const V = {
connect: u,
embedGithubProject: W,
embedProject: k,
embedProjectId: G,
openGithubProject: K,
openProject: q,
openProjectId: H
const StackBlitzSDK = {
connect,
embedGithubProject,
embedProject,
embedProjectId,
openGithubProject,
openProject,
openProjectId
};
export {
V as default
StackBlitzSDK as default
};
//# sourceMappingURL=sdk.m.js.map

@@ -1,9 +0,507 @@

(function(l,u){typeof exports=="object"&&typeof module<"u"?module.exports=u():typeof define=="function"&&define.amd?define(u):(l=typeof globalThis<"u"?globalThis:l||self,l.StackBlitzSDK=u())})(this,function(){"use strict";const N="https://stackblitz.com",w=["angular-cli","create-react-app","html","javascript","node","polymer","typescript","vue"],b={clickToLoad:e=>d("ctl",e),devToolsHeight:e=>T("devtoolsheight",e),forceEmbedLayout:e=>d("embed",e),hideDevTools:e=>d("hidedevtools",e),hideExplorer:e=>d("hideExplorer",e),hideNavigation:e=>d("hideNavigation",e),showSidebar:e=>j("showSidebar",e),openFile:e=>C("file",e).join("&"),terminalHeight:e=>T("terminalHeight",e),theme:e=>P("theme",["light","dark"],e),view:e=>P("view",["preview","editor"],e)};function S(e={}){const t=Object.entries(e).map(([n,i])=>i!=null&&b.hasOwnProperty(n)?b[n](i):"").filter(Boolean);return t.length?`?${t.join("&")}`:""}function d(e,t){return t===!0?`${e}=1`:""}function j(e,t){return typeof t=="boolean"?`${e}=${t?"1":"0"}`:""}function T(e,t){if(typeof t=="number"&&!Number.isNaN(t)){const n=Math.min(100,Math.max(0,t));return`${e}=${Math.round(n)}`}return""}function P(e,t,n){return typeof n=="string"&&t.includes(n)?`${e}=${n}`:""}function C(e,t){return(Array.isArray(t)?t:[t]).filter(i=>typeof i=="string"&&i.trim()!=="").map(i=>`${e}=${encodeURIComponent(i.trim())}`)}function $(){return Math.random().toString(36).slice(2,6)+Math.random().toString(36).slice(2,6)}function m(e,t){return`${v(t)}${e}${S(t)}`}function f(e,t){const n={forceEmbedLayout:!0};return t&&typeof t=="object"&&Object.assign(n,t),`${v(n)}${e}${S(n)}`}function v(e={}){return(typeof e.origin=="string"?e.origin:N).replace(/\/$/,"")}function _(e,t,n){if(!t||!e||!e.parentNode)throw new Error("Invalid Element");e.id&&(t.id=e.id),e.className&&(t.className=e.className),A(t,n),e.replaceWith(t)}function g(e){if(typeof e=="string"){const t=document.getElementById(e);if(!t)throw new Error(`Could not find element with id '${e}'`);return t}else if(e instanceof HTMLElement)return e;throw new Error(`Invalid element: ${e}`)}function y(e){return e&&e.newWindow===!1?"_self":"_blank"}function A(e,t={}){const n=Object.hasOwnProperty.call(t,"height")?`${t.height}`:`${300}`,i=Object.hasOwnProperty.call(t,"width")?`${t.width}`:void 0;e.setAttribute("height",n),i?e.setAttribute("width",i):e.setAttribute("style","width:100%;")}class F{constructor(t){this.pending={},this.port=t,this.port.onmessage=this.messageListener.bind(this)}request({type:t,payload:n}){return new Promise((i,r)=>{const o=$();this.pending[o]={resolve:i,reject:r},this.port.postMessage({type:t,payload:{...n,__reqid:o}})})}messageListener(t){if(typeof t.data.payload?.__reqid!="string")return;const{type:n,payload:i}=t.data,{__reqid:r,__success:o,__error:c}=i;this.pending[r]&&(o?this.pending[r].resolve(this.cleanResult(i)):this.pending[r].reject(c?`${n}: ${c}`:n),delete this.pending[r])}cleanResult(t){const n={...t};return delete n.__reqid,delete n.__success,delete n.__error,Object.keys(n).length?n:null}}class L{constructor(t,n){this.editor={openFile:i=>this._rdc.request({type:"SDK_OPEN_FILE",payload:{path:i}}),setCurrentFile:i=>this._rdc.request({type:"SDK_SET_CURRENT_FILE",payload:{path:i}}),setTheme:i=>this._rdc.request({type:"SDK_SET_UI_THEME",payload:{theme:i}}),setView:i=>this._rdc.request({type:"SDK_SET_UI_VIEW",payload:{view:i}}),showSidebar:(i=!0)=>this._rdc.request({type:"SDK_TOGGLE_SIDEBAR",payload:{visible:i}})},this.preview={origin:"",getUrl:()=>this._rdc.request({type:"SDK_GET_PREVIEW_URL",payload:{}}).then(i=>i?.url??null),setUrl:(i="/")=>{if(typeof i!="string"||!i.startsWith("/"))throw new Error(`Invalid argument: expected a path starting with '/', got '${i}'`);return this._rdc.request({type:"SDK_SET_PREVIEW_URL",payload:{path:i}})}},this._rdc=new F(t),Object.defineProperty(this.preview,"origin",{value:typeof n.previewOrigin=="string"?n.previewOrigin:null,writable:!1})}applyFsDiff(t){const n=i=>i!==null&&typeof i=="object";if(!n(t)||!n(t.create))throw new Error("Invalid diff object: expected diff.create to be an object.");if(!Array.isArray(t.destroy))throw new Error("Invalid diff object: expected diff.destroy to be an array.");return this._rdc.request({type:"SDK_APPLY_FS_DIFF",payload:t})}getDependencies(){return this._rdc.request({type:"SDK_GET_DEPS_SNAPSHOT",payload:{}})}getFsSnapshot(){return this._rdc.request({type:"SDK_GET_FS_SNAPSHOT",payload:{}})}}const p=[];class O{constructor(t){this.id=$(),this.element=t,this.pending=new Promise((n,i)=>{const r=({data:a,ports:E})=>{a?.action==="SDK_INIT_SUCCESS"&&a.id===this.id&&(this.vm=new L(E[0],a.payload),n(this.vm),c())},o=()=>{this.element.contentWindow?.postMessage({action:"SDK_INIT",id:this.id},"*")};function c(){window.clearInterval(x),window.removeEventListener("message",r)}window.addEventListener("message",r),o();let I=0;const x=window.setInterval(()=>{if(this.vm){c();return}if(I>=20){c(),i("Timeout: Unable to establish a connection with the StackBlitz VM"),p.forEach((a,E)=>{a.id===this.id&&p.splice(E,1)});return}I++,o()},500)}),p.push(this)}}const M=e=>{const t=e instanceof Element?"element":"id";return p.find(n=>n[t]===e)??null};function s(e,t){const n=document.createElement("input");return n.type="hidden",n.name=e,n.value=t,n}function R(e){return`[${e.replace(/\[/g,"%5B").replace(/\]/g,"%5D")}]`}function D(e){if(!w.includes(e.template)){const i=w.map(r=>`'${r}'`).join(", ");console.warn(`Unsupported project.template: must be one of ${i}`)}const t=e.template==="node",n=document.createElement("form");return n.method="POST",n.setAttribute("style","display:none!important;"),n.appendChild(s("project[title]",e.title)),n.appendChild(s("project[description]",e.description)),n.appendChild(s("project[template]",e.template)),e.dependencies&&(t?console.warn("Invalid project.dependencies: dependencies must be provided as a 'package.json' file when using the 'node' template."):n.appendChild(s("project[dependencies]",JSON.stringify(e.dependencies)))),e.settings&&n.appendChild(s("project[settings]",JSON.stringify(e.settings))),Object.keys(e.files).forEach(i=>{const r="project[files]"+R(i),o=e.files[i];typeof o=="string"&&n.appendChild(s(r,o))}),n}function U(e,t){const n=D(e);return n.action=f("/run",t),n.id="sb_run",`<!doctype html>
(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() : typeof define === "function" && define.amd ? define(factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, global.StackBlitzSDK = factory());
})(this, function() {
"use strict";
const CONNECT_INTERVAL = 500;
const CONNECT_MAX_ATTEMPTS = 20;
const DEFAULT_FRAME_HEIGHT = 300;
const DEFAULT_ORIGIN = "https://stackblitz.com";
const PROJECT_TEMPLATES = [
"angular-cli",
"create-react-app",
"html",
"javascript",
"node",
"polymer",
"typescript",
"vue"
];
const generators = {
clickToLoad: (value) => trueParam("ctl", value),
devToolsHeight: (value) => percentParam("devtoolsheight", value),
forceEmbedLayout: (value) => trueParam("embed", value),
hideDevTools: (value) => trueParam("hidedevtools", value),
hideExplorer: (value) => trueParam("hideExplorer", value),
hideNavigation: (value) => trueParam("hideNavigation", value),
showSidebar: (value) => booleanParam("showSidebar", value),
openFile: (value) => stringParams("file", value).join("&"),
terminalHeight: (value) => percentParam("terminalHeight", value),
theme: (value) => enumParam("theme", ["light", "dark"], value),
view: (value) => enumParam("view", ["preview", "editor"], value)
};
function buildParams(options = {}) {
const params = Object.entries(options).map(([key, value]) => {
if (value != null && generators.hasOwnProperty(key)) {
return generators[key](value);
}
return "";
}).filter(Boolean);
return params.length ? `?${params.join("&")}` : "";
}
function trueParam(name, value) {
if (value === true) {
return `${name}=1`;
}
return "";
}
function booleanParam(name, value) {
if (typeof value === "boolean") {
return `${name}=${value ? "1" : "0"}`;
}
return "";
}
function percentParam(name, value) {
if (typeof value === "number" && !Number.isNaN(value)) {
const clamped = Math.min(100, Math.max(0, value));
return `${name}=${Math.round(clamped)}`;
}
return "";
}
function enumParam(name, oneOf, value) {
if (typeof value === "string" && oneOf.includes(value)) {
return `${name}=${value}`;
}
return "";
}
function stringParams(name, value) {
const values = Array.isArray(value) ? value : [value];
return values.filter((val) => typeof val === "string" && val.trim() !== "").map((val) => `${name}=${encodeURIComponent(val.trim())}`);
}
function genID() {
return Math.random().toString(36).slice(2, 6) + Math.random().toString(36).slice(2, 6);
}
function openUrl(route, options) {
return `${getOrigin(options)}${route}${buildParams(options)}`;
}
function embedUrl(route, options) {
const config = {
forceEmbedLayout: true
};
if (options && typeof options === "object") {
Object.assign(config, options);
}
return `${getOrigin(config)}${route}${buildParams(config)}`;
}
function getOrigin(options = {}) {
const origin = typeof options.origin === "string" ? options.origin : DEFAULT_ORIGIN;
return origin.replace(/\/$/, "");
}
function replaceAndEmbed(target, frame, options) {
if (!frame || !target || !target.parentNode) {
throw new Error("Invalid Element");
}
if (target.id) {
frame.id = target.id;
}
if (target.className) {
frame.className = target.className;
}
setFrameDimensions(frame, options);
target.replaceWith(frame);
}
function findElement(elementOrId) {
if (typeof elementOrId === "string") {
const element = document.getElementById(elementOrId);
if (!element) {
throw new Error(`Could not find element with id '${elementOrId}'`);
}
return element;
} else if (elementOrId instanceof HTMLElement) {
return elementOrId;
}
throw new Error(`Invalid element: ${elementOrId}`);
}
function openTarget(options) {
return options && options.newWindow === false ? "_self" : "_blank";
}
function setFrameDimensions(frame, options = {}) {
const height = Object.hasOwnProperty.call(options, "height") ? `${options.height}` : `${DEFAULT_FRAME_HEIGHT}`;
const width = Object.hasOwnProperty.call(options, "width") ? `${options.width}` : void 0;
frame.setAttribute("height", height);
if (width) {
frame.setAttribute("width", width);
} else {
frame.setAttribute("style", "width:100%;");
}
}
class RDC {
constructor(port) {
this.pending = {};
this.port = port;
this.port.onmessage = this.messageListener.bind(this);
}
request({ type, payload }) {
return new Promise((resolve, reject) => {
const id = genID();
this.pending[id] = { resolve, reject };
this.port.postMessage({
type,
payload: {
...payload,
// Ensure the payload object includes the request ID
__reqid: id
}
});
});
}
messageListener(event) {
if (typeof event.data.payload?.__reqid !== "string") {
return;
}
const { type, payload } = event.data;
const { __reqid: id, __success: success, __error: error } = payload;
if (this.pending[id]) {
if (success) {
this.pending[id].resolve(this.cleanResult(payload));
} else {
this.pending[id].reject(error ? `${type}: ${error}` : type);
}
delete this.pending[id];
}
}
cleanResult(payload) {
const result = { ...payload };
delete result.__reqid;
delete result.__success;
delete result.__error;
return Object.keys(result).length ? result : null;
}
}
class VM {
constructor(port, config) {
this.editor = {
/**
* Open one of several files in tabs and/or split panes.
*
* @since 1.7.0 Added support for opening multiple files
*/
openFile: (path) => {
return this._rdc.request({
type: "SDK_OPEN_FILE",
payload: { path }
});
},
/**
* Set a project file as the currently selected file.
*
* - This may update the highlighted file in the file explorer,
* and the currently open and/or focused editor tab.
* - It will _not_ open a new editor tab if the provided path does not
* match a currently open tab. See `vm.editor.openFile` to open files.
*
* @since 1.7.0
* @experimental
*/
setCurrentFile: (path) => {
return this._rdc.request({
type: "SDK_SET_CURRENT_FILE",
payload: { path }
});
},
/**
* Change the color theme
*
* @since 1.7.0
*/
setTheme: (theme) => {
return this._rdc.request({
type: "SDK_SET_UI_THEME",
payload: { theme }
});
},
/**
* Change the display mode of the project:
*
* - `default`: show the editor and preview pane
* - `editor`: show the editor pane only
* - `preview`: show the preview pane only
*
* @since 1.7.0
*/
setView: (view) => {
return this._rdc.request({
type: "SDK_SET_UI_VIEW",
payload: { view }
});
},
/**
* Change the display mode of the sidebar:
*
* - `true`: show the sidebar
* - `false`: hide the sidebar
*
* @since 1.7.0
*/
showSidebar: (visible = true) => {
return this._rdc.request({
type: "SDK_TOGGLE_SIDEBAR",
payload: { visible }
});
}
};
this.preview = {
/**
* The origin (protocol and domain) of the preview iframe.
*
* In WebContainers-based projects, the origin will always be `null`;
* try using `vm.preview.getUrl` instead.
*
* @see https://developer.stackblitz.com/guides/user-guide/available-environments
*/
origin: "",
/**
* Get the current preview URL.
*
* In both and EngineBlock and WebContainers-based projects, the preview URL
* may not reflect the exact path of the current page, after user navigation.
*
* In WebContainers-based projects, the preview URL will be `null` initially,
* and until the project starts a web server.
*
* @since 1.7.0
* @experimental
*/
getUrl: () => {
return this._rdc.request({
type: "SDK_GET_PREVIEW_URL",
payload: {}
}).then((data) => data?.url ?? null);
},
/**
* Change the path of the preview URL.
*
* In WebContainers-based projects, this will be ignored if there is no
* currently running web server.
*
* @since 1.7.0
* @experimental
*/
setUrl: (path = "/") => {
if (typeof path !== "string" || !path.startsWith("/")) {
throw new Error(`Invalid argument: expected a path starting with '/', got '${path}'`);
}
return this._rdc.request({
type: "SDK_SET_PREVIEW_URL",
payload: { path }
});
}
};
this._rdc = new RDC(port);
Object.defineProperty(this.preview, "origin", {
value: typeof config.previewOrigin === "string" ? config.previewOrigin : null,
writable: false
});
}
/**
* Apply batch updates to the project files in one call.
*/
applyFsDiff(diff) {
const isObject = (val) => val !== null && typeof val === "object";
if (!isObject(diff) || !isObject(diff.create)) {
throw new Error("Invalid diff object: expected diff.create to be an object.");
} else if (!Array.isArray(diff.destroy)) {
throw new Error("Invalid diff object: expected diff.destroy to be an array.");
}
return this._rdc.request({
type: "SDK_APPLY_FS_DIFF",
payload: diff
});
}
/**
* Get the project’s defined dependencies.
*
* In EngineBlock projects, version numbers represent the resolved dependency versions.
* In WebContainers-based projects, returns data from the project’s `package.json` without resolving installed version numbers.
*/
getDependencies() {
return this._rdc.request({
type: "SDK_GET_DEPS_SNAPSHOT",
payload: {}
});
}
/**
* Get a snapshot of the project files and their content.
*/
getFsSnapshot() {
return this._rdc.request({
type: "SDK_GET_FS_SNAPSHOT",
payload: {}
});
}
}
const connections = [];
class Connection {
constructor(element) {
this.id = genID();
this.element = element;
this.pending = new Promise((resolve, reject) => {
const listenForSuccess = ({ data, ports }) => {
if (data?.action === "SDK_INIT_SUCCESS" && data.id === this.id) {
this.vm = new VM(ports[0], data.payload);
resolve(this.vm);
cleanup();
}
};
const pingFrame = () => {
this.element.contentWindow?.postMessage(
{
action: "SDK_INIT",
id: this.id
},
"*"
);
};
function cleanup() {
window.clearInterval(interval);
window.removeEventListener("message", listenForSuccess);
}
window.addEventListener("message", listenForSuccess);
pingFrame();
let attempts = 0;
const interval = window.setInterval(() => {
if (this.vm) {
cleanup();
return;
}
if (attempts >= CONNECT_MAX_ATTEMPTS) {
cleanup();
reject("Timeout: Unable to establish a connection with the StackBlitz VM");
connections.forEach((connection, index) => {
if (connection.id === this.id) {
connections.splice(index, 1);
}
});
return;
}
attempts++;
pingFrame();
}, CONNECT_INTERVAL);
});
connections.push(this);
}
}
const getConnection = (identifier) => {
const key = identifier instanceof Element ? "element" : "id";
return connections.find((c) => c[key] === identifier) ?? null;
};
function createHiddenInput(name, value) {
const input = document.createElement("input");
input.type = "hidden";
input.name = name;
input.value = value;
return input;
}
function bracketedFilePath(path) {
return `[${path.replace(/\[/g, "%5B").replace(/\]/g, "%5D")}]`;
}
function createProjectForm(project) {
if (!PROJECT_TEMPLATES.includes(project.template)) {
const names = PROJECT_TEMPLATES.map((t) => `'${t}'`).join(", ");
console.warn(`Unsupported project.template: must be one of ${names}`);
}
const isWebContainers = project.template === "node";
const form = document.createElement("form");
form.method = "POST";
form.setAttribute("style", "display:none!important;");
form.appendChild(createHiddenInput("project[title]", project.title));
form.appendChild(createHiddenInput("project[description]", project.description));
form.appendChild(createHiddenInput("project[template]", project.template));
if (project.dependencies) {
if (isWebContainers) {
console.warn(
`Invalid project.dependencies: dependencies must be provided as a 'package.json' file when using the 'node' template.`
);
} else {
form.appendChild(
createHiddenInput("project[dependencies]", JSON.stringify(project.dependencies))
);
}
}
if (project.settings) {
form.appendChild(createHiddenInput("project[settings]", JSON.stringify(project.settings)));
}
Object.keys(project.files).forEach((path) => {
const name = "project[files]" + bracketedFilePath(path);
const value = project.files[path];
if (typeof value === "string") {
form.appendChild(createHiddenInput(name, value));
}
});
return form;
}
function createProjectFrameHTML(project, options) {
const form = createProjectForm(project);
form.action = embedUrl("/run", options);
form.id = "sb_run";
const html = `<!doctype html>
<html>
<head><title></title></head>
<body>
${n.outerHTML}
<script>document.getElementById('${n.id}').submit();<\/script>
${form.outerHTML}
<script>document.getElementById('${form.id}').submit();<\/script>
</body>
</html>`}function q(e,t){const n=D(e);n.action=m("/run",t),n.target=y(t),document.body.appendChild(n),n.submit(),document.body.removeChild(n)}function h(e){return e?.contentWindow?(M(e)??new O(e)).pending:Promise.reject("Provided element is not an iframe.")}function K(e,t){q(e,t)}function H(e,t){const n=m(`/edit/${e}`,t),i=y(t);window.open(n,i)}function k(e,t){const n=m(`/github/${e}`,t),i=y(t);window.open(n,i)}function G(e,t,n){const i=g(e),r=U(t,n),o=document.createElement("iframe");return _(i,o,n),o.contentDocument?.write(r),h(o)}function B(e,t,n){const i=g(e),r=document.createElement("iframe");return r.src=f(`/edit/${t}`,n),_(i,r,n),h(r)}function W(e,t,n){const i=g(e),r=document.createElement("iframe");return r.src=f(`/github/${t}`,n),_(i,r,n),h(r)}return{connect:h,embedGithubProject:W,embedProject:G,embedProjectId:B,openGithubProject:k,openProject:K,openProjectId:H}});
</html>`;
return html;
}
function openNewProject(project, options) {
const form = createProjectForm(project);
form.action = openUrl("/run", options);
form.target = openTarget(options);
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
}
function connect(frameEl) {
if (!frameEl?.contentWindow) {
return Promise.reject("Provided element is not an iframe.");
}
const connection = getConnection(frameEl) ?? new Connection(frameEl);
return connection.pending;
}
function openProject(project, options) {
openNewProject(project, options);
}
function openProjectId(projectId, options) {
const url = openUrl(`/edit/${projectId}`, options);
const target = openTarget(options);
window.open(url, target);
}
function openGithubProject(repoSlug, options) {
const url = openUrl(`/github/${repoSlug}`, options);
const target = openTarget(options);
window.open(url, target);
}
function embedProject(elementOrId, project, options) {
const element = findElement(elementOrId);
const html = createProjectFrameHTML(project, options);
const frame = document.createElement("iframe");
replaceAndEmbed(element, frame, options);
frame.contentDocument?.write(html);
return connect(frame);
}
function embedProjectId(elementOrId, projectId, options) {
const element = findElement(elementOrId);
const frame = document.createElement("iframe");
frame.src = embedUrl(`/edit/${projectId}`, options);
replaceAndEmbed(element, frame, options);
return connect(frame);
}
function embedGithubProject(elementOrId, repoSlug, options) {
const element = findElement(elementOrId);
const frame = document.createElement("iframe");
frame.src = embedUrl(`/github/${repoSlug}`, options);
replaceAndEmbed(element, frame, options);
return connect(frame);
}
const StackBlitzSDK = {
connect,
embedGithubProject,
embedProject,
embedProjectId,
openGithubProject,
openProject,
openProjectId
};
return StackBlitzSDK;
});
//# sourceMappingURL=sdk.umd.js.map
{
"name": "@stackblitz/sdk",
"version": "1.9.0-alpha.0",
"version": "1.9.0-alpha.1",
"description": "SDK for generating and embedding StackBlitz projects.",

@@ -35,18 +35,18 @@ "license": "MIT",

"devDependencies": {
"@playwright/test": "^1.30.0",
"@playwright/test": "^1.31.2",
"@rollup/plugin-replace": "^5.0.2",
"@types/body-parser": "^1.19.2",
"@types/lodash": "^4.14.191",
"@vitest/coverage-c8": "^0.28.4",
"@vitest/ui": "^0.28.4",
"body-parser": "^1.20.1",
"happy-dom": "^8.2.6",
"@vitest/coverage-c8": "^0.29.2",
"@vitest/ui": "^0.29.1",
"body-parser": "^1.20.2",
"happy-dom": "^8.9.0",
"lodash": "^4.17.21",
"prettier": "^2.8.3",
"rimraf": "^4.1.2",
"prettier": "^2.8.4",
"rimraf": "^4.2.0",
"typescript": "~4.4.4",
"vite": "^4.1.1",
"vite": "^4.1.4",
"vite-tsconfig-paths": "^4.0.5",
"vitest": "^0.28.4"
"vitest": "^0.29.2"
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc