Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions examples/embed-project-node/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>Embed a dynamically generated Node project with the StackBlitz SDK</title>
<script type="module" src="./index.ts"></script>
</head>
<body>
<header>
<h1>Embed a dynamically generated Node project with the StackBlitz SDK</h1>
<div>
<label> <input type="checkbox" name="corp" /> Cross-Origin Isolation </label>
</div>
<nav>
<button type="button" name="embed-project">Embed project</button>
</nav>
</header>
<div id="embed">
<p>Embed will go here</p>
</div>
</body>
</html>
83 changes: 83 additions & 0 deletions examples/embed-project-node/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import sdk, { Project } from '@stackblitz/sdk';

import './styles.css';

// A dynamically generated WebContainers (`node`) project.
// For `node` projects, npm dependencies must be declared in the
// `package.json` file (the `dependencies` field of the Project is ignored).
const project: Project = {
title: 'Dynamically Generated Node Project',
description: 'A Node.js Express server created with the StackBlitz SDK!',
template: 'node',
files: {
'package.json': JSON.stringify(
{
name: 'sdk-node-project',
version: '0.0.0',
private: true,
type: 'module',
scripts: {
start: 'node index.js',
},
dependencies: {
express: '^4.18.2',
},
},
null,
2
),
'index.js': `import express from 'express';

const app = express();
const port = 3000;

app.get('/', (_req, res) => {
res.send('<h1>Hello from a dynamically generated Node project!</h1>');
});

app.listen(port, () => {
console.log(\`Server listening on http://localhost:\${port}\`);
});
`,
},
};

function embedProject() {
const queryParams = new URLSearchParams(window.location.search);

sdk.embedProject('embed', project, {
openFile: 'index.js',
startScript: 'start',
crossOriginIsolated: queryParams.get('corp') === '1',
});
}

function toggleCorp(event: Event) {
const queryParams = new URLSearchParams(window.location.search);
const isChecked = (event.target as any)?.checked;

if (isChecked) {
if (!queryParams.has('corp') || queryParams.get('corp') !== '1') {
queryParams.set('corp', '1');
}
} else {
queryParams.delete('corp');
}

window.location.search = queryParams.toString();
}

function setup() {
const embedButton = document.querySelector('[name=embed-project]') as HTMLButtonElement;
const corpCheckbox = document.querySelector('[name=corp]') as HTMLInputElement;

embedButton.addEventListener('click', embedProject);
corpCheckbox.addEventListener('change', toggleCorp);

// mark the checkbox checked if the corp param is already set
const queryParams = new URLSearchParams(window.location.search);

corpCheckbox.checked = queryParams.get('corp') === '1';
}

setup();
9 changes: 9 additions & 0 deletions examples/embed-project-node/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "sdk-example-embed-project-node",
"description": "Demo of the StackBlitz SDK's embedProject method for creating a dynamic WebContainers (node) project",
"version": "0.0.0",
"private": true,
"dependencies": {
"@stackblitz/sdk": "^1.8.1"
}
}
54 changes: 54 additions & 0 deletions examples/embed-project-node/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
html {
height: 100%;
text-align: center;
font-family: system-ui, sans-serif;
color: black;
background-color: white;
}

body {
height: 100%;
margin: 0;
display: flex;
flex-direction: column;
}

h1 {
margin: 1rem;
font-size: 1.25rem;
}

nav {
margin: 1rem;
font-size: 0.9rem;
}

select,
button {
margin: 0.2em;
padding: 0.2em 0.5em;
font-size: inherit;
font-family: inherit;
}

#embed {
display: flex;
flex: 1 1 60%;
flex-direction: column;
justify-content: center;
overflow: hidden;
width: 100%;
height: auto;
margin: 0;
border: 0;
}

#embed > p {
width: min(300px, 100%);
margin: 2rem auto;
padding: 4rem 1rem;
border: dashed 2px #ccc;
border-radius: 0.5em;
font-size: 85%;
color: #777;
}
5 changes: 5 additions & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ <h1>StackBlitz SDK Examples</h1>
</li>
<li><a href="/examples/open-embed-github-project/">Open and embed a GitHub repo</a></li>
<li><a href="/examples/embed-project-vm/">Control an embedded project with the SDK</a></li>
<li>
<a href="/examples/embed-project-node/"
>Embed a dynamically generated Node (WebContainers) project</a
>
</li>
</ul>
</body>
</html>
73 changes: 49 additions & 24 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
"test:format": "npx prettier --check ."
},
"devDependencies": {
"@playwright/test": "^1.32.2",
"@playwright/test": "^1.61.1",
"@rollup/plugin-replace": "^5.0.2",
"@types/body-parser": "^1.19.2",
"@types/lodash": "^4.14.192",
Expand Down
45 changes: 45 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,48 @@ export const UI_THEMES = ['light', 'dark'] as const;
* Supported editor view modes
*/
export const UI_VIEWS = ['editor', 'preview'] as const;

/**
* Permissions Policy features delegated to the embed iframe.
*
* Each feature is delegated to any origin (`feature *`) so that the embedded
* StackBlitz document — and any (potentially cross-origin) iframes it nests,
* such as project previews — can actually use them. A feature can only be used
* in a nested frame if every ancestor frame was granted it, so the outer embed
* frame must delegate the feature for it to reach a preview iframe deeper down.
*
* `cross-origin-isolated` is intentionally omitted: it does not accept the `*`
* allowlist value and is delegated separately to the StackBlitz origin.
*/
export const EMBED_ALLOW_FEATURES = [
'accelerometer',
'ambient-light-sensor',
'autoplay',
'battery',
'bluetooth',
'camera',
'clipboard-read',
'clipboard-write',
'display-capture',
'encrypted-media',
'fullscreen',
'gamepad',
'geolocation',
'gyroscope',
'hid',
'idle-detection',
'local-network',
'local-network-access',
'loopback-network',
'magnetometer',
'microphone',
'midi',
'payment',
'picture-in-picture',
'publickey-credentials-get',
'screen-wake-lock',
'serial',
'usb',
'web-share',
'xr-spatial-tracking',
] as const;
Loading
Loading