almostnode 0.1.0 → 0.2.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 +163 -1
- package/dist/index.cjs +70 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +52 -9
- package/dist/index.mjs.map +1 -1
- package/dist/next-plugin.cjs +56 -0
- package/dist/next-plugin.cjs.map +1 -0
- package/dist/next-plugin.d.ts +62 -0
- package/dist/next-plugin.d.ts.map +1 -0
- package/dist/next-plugin.mjs +30 -0
- package/dist/next-plugin.mjs.map +1 -0
- package/dist/sandbox-helpers.d.ts +29 -6
- package/dist/sandbox-helpers.d.ts.map +1 -1
- package/dist/server-bridge.d.ts +10 -1
- package/dist/server-bridge.d.ts.map +1 -1
- package/dist/vite-plugin.cjs +56 -0
- package/dist/vite-plugin.cjs.map +1 -0
- package/dist/vite-plugin.d.ts +36 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.mjs +30 -0
- package/dist/vite-plugin.mjs.map +1 -0
- package/package.json +21 -1
- package/src/frameworks/next-dev-server.ts +2 -2
- package/src/index.ts +1 -0
- package/src/next-plugin.ts +96 -0
- package/src/sandbox-helpers.ts +101 -9
- package/src/server-bridge.ts +14 -2
- package/src/vite-plugin.ts +76 -0
package/src/sandbox-helpers.ts
CHANGED
|
@@ -5,13 +5,74 @@
|
|
|
5
5
|
* to provide browser-enforced isolation from the main application.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
13
|
+
// @ts-ignore - import.meta.url is available in ESM
|
|
14
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get the contents of the service worker file.
|
|
18
|
+
* Returns null if the file is not found (e.g., running in browser).
|
|
19
|
+
*/
|
|
20
|
+
function getServiceWorkerContent(): string | null {
|
|
21
|
+
try {
|
|
22
|
+
// Try dist directory first (when running from built package)
|
|
23
|
+
let swPath = path.join(__dirname, '__sw__.js');
|
|
24
|
+
if (fs.existsSync(swPath)) {
|
|
25
|
+
return fs.readFileSync(swPath, 'utf-8');
|
|
26
|
+
}
|
|
27
|
+
// Try relative to src (when running from source)
|
|
28
|
+
swPath = path.join(__dirname, '../dist/__sw__.js');
|
|
29
|
+
if (fs.existsSync(swPath)) {
|
|
30
|
+
return fs.readFileSync(swPath, 'utf-8');
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SandboxHtmlOptions {
|
|
39
|
+
/**
|
|
40
|
+
* URL to load almostnode from (e.g., unpkg, jsdelivr, or your CDN)
|
|
41
|
+
* @default 'https://unpkg.com/almostnode/dist/index.js'
|
|
42
|
+
*/
|
|
43
|
+
almostnodeUrl?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Whether to include service worker registration for dev server support.
|
|
46
|
+
* When true, the sandbox can run ViteDevServer/NextDevServer with URL access.
|
|
47
|
+
* @default true
|
|
48
|
+
*/
|
|
49
|
+
includeServiceWorker?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
8
52
|
/**
|
|
9
53
|
* HTML template for the sandbox page.
|
|
10
54
|
* This loads almostnode and handles postMessage communication with the parent.
|
|
11
55
|
*
|
|
12
|
-
* @param
|
|
56
|
+
* @param options - Configuration options or legacy URL string
|
|
13
57
|
*/
|
|
14
|
-
export function getSandboxHtml(
|
|
58
|
+
export function getSandboxHtml(options: SandboxHtmlOptions | string = {}): string {
|
|
59
|
+
// Support legacy string argument
|
|
60
|
+
const opts: SandboxHtmlOptions = typeof options === 'string'
|
|
61
|
+
? { almostnodeUrl: options }
|
|
62
|
+
: options;
|
|
63
|
+
|
|
64
|
+
const almostnodeUrl = opts.almostnodeUrl ?? 'https://unpkg.com/almostnode/dist/index.js';
|
|
65
|
+
const includeServiceWorker = opts.includeServiceWorker ?? true;
|
|
66
|
+
|
|
67
|
+
const serviceWorkerScript = includeServiceWorker ? `
|
|
68
|
+
// Register service worker for dev server support
|
|
69
|
+
if ('serviceWorker' in navigator) {
|
|
70
|
+
navigator.serviceWorker.register('/__sw__.js', { scope: '/' })
|
|
71
|
+
.then(reg => console.log('[Sandbox] Service worker registered'))
|
|
72
|
+
.catch(err => console.warn('[Sandbox] Service worker registration failed:', err));
|
|
73
|
+
}
|
|
74
|
+
` : '';
|
|
75
|
+
|
|
15
76
|
return `<!DOCTYPE html>
|
|
16
77
|
<html>
|
|
17
78
|
<head>
|
|
@@ -20,7 +81,8 @@ export function getSandboxHtml(justNodeUrl = 'https://unpkg.com/almostnode/dist/
|
|
|
20
81
|
</head>
|
|
21
82
|
<body>
|
|
22
83
|
<script type="module">
|
|
23
|
-
import { VirtualFS, Runtime } from '${
|
|
84
|
+
import { VirtualFS, Runtime } from '${almostnodeUrl}';
|
|
85
|
+
${serviceWorkerScript}
|
|
24
86
|
|
|
25
87
|
let vfs = null;
|
|
26
88
|
let runtime = null;
|
|
@@ -122,29 +184,59 @@ export function getSandboxVercelConfig(): object {
|
|
|
122
184
|
};
|
|
123
185
|
}
|
|
124
186
|
|
|
187
|
+
export interface GenerateSandboxFilesOptions extends SandboxHtmlOptions {
|
|
188
|
+
// Inherits almostnodeUrl and includeServiceWorker from SandboxHtmlOptions
|
|
189
|
+
}
|
|
190
|
+
|
|
125
191
|
/**
|
|
126
|
-
* Generate all files needed for deploying a sandbox to Vercel.
|
|
192
|
+
* Generate all files needed for deploying a sandbox to Vercel or other platforms.
|
|
127
193
|
*
|
|
128
|
-
* @param
|
|
194
|
+
* @param options - Configuration options or legacy URL string
|
|
129
195
|
* @returns Object with file names as keys and content as values
|
|
130
196
|
*
|
|
131
197
|
* @example
|
|
132
198
|
* ```typescript
|
|
133
|
-
* import { generateSandboxFiles } from 'almostnode
|
|
199
|
+
* import { generateSandboxFiles } from 'almostnode';
|
|
200
|
+
* import fs from 'fs';
|
|
134
201
|
*
|
|
135
202
|
* const files = generateSandboxFiles();
|
|
203
|
+
*
|
|
136
204
|
* // Write files to sandbox/ directory
|
|
205
|
+
* fs.mkdirSync('sandbox', { recursive: true });
|
|
206
|
+
* for (const [filename, content] of Object.entries(files)) {
|
|
207
|
+
* fs.writeFileSync(`sandbox/${filename}`, content);
|
|
208
|
+
* }
|
|
209
|
+
*
|
|
137
210
|
* // Deploy to Vercel: cd sandbox && vercel --prod
|
|
138
211
|
* ```
|
|
139
212
|
*/
|
|
140
|
-
export function generateSandboxFiles(
|
|
213
|
+
export function generateSandboxFiles(options: GenerateSandboxFilesOptions | string = {}): {
|
|
141
214
|
'index.html': string;
|
|
142
215
|
'vercel.json': string;
|
|
216
|
+
'__sw__.js'?: string;
|
|
143
217
|
} {
|
|
144
|
-
|
|
145
|
-
|
|
218
|
+
// Support legacy string argument
|
|
219
|
+
const opts: GenerateSandboxFilesOptions = typeof options === 'string'
|
|
220
|
+
? { almostnodeUrl: options }
|
|
221
|
+
: options;
|
|
222
|
+
|
|
223
|
+
const includeServiceWorker = opts.includeServiceWorker ?? true;
|
|
224
|
+
const swContent = includeServiceWorker ? getServiceWorkerContent() : null;
|
|
225
|
+
|
|
226
|
+
const files: {
|
|
227
|
+
'index.html': string;
|
|
228
|
+
'vercel.json': string;
|
|
229
|
+
'__sw__.js'?: string;
|
|
230
|
+
} = {
|
|
231
|
+
'index.html': getSandboxHtml(opts),
|
|
146
232
|
'vercel.json': JSON.stringify(getSandboxVercelConfig(), null, 2),
|
|
147
233
|
};
|
|
234
|
+
|
|
235
|
+
if (swContent) {
|
|
236
|
+
files['__sw__.js'] = swContent;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return files;
|
|
148
240
|
}
|
|
149
241
|
|
|
150
242
|
/**
|
package/src/server-bridge.ts
CHANGED
|
@@ -38,6 +38,14 @@ export interface BridgeOptions {
|
|
|
38
38
|
onServerReady?: (port: number, url: string) => void;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export interface InitServiceWorkerOptions {
|
|
42
|
+
/**
|
|
43
|
+
* The URL path to the service worker file
|
|
44
|
+
* @default '/__sw__.js'
|
|
45
|
+
*/
|
|
46
|
+
swUrl?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
/**
|
|
42
50
|
* Server Bridge manages virtual HTTP servers and routes requests
|
|
43
51
|
*/
|
|
@@ -146,14 +154,18 @@ export class ServerBridge extends EventEmitter {
|
|
|
146
154
|
|
|
147
155
|
/**
|
|
148
156
|
* Initialize Service Worker communication
|
|
157
|
+
* @param options - Configuration options for the service worker
|
|
158
|
+
* @param options.swUrl - Custom URL path to the service worker file (default: '/__sw__.js')
|
|
149
159
|
*/
|
|
150
|
-
async initServiceWorker(): Promise<void> {
|
|
160
|
+
async initServiceWorker(options?: InitServiceWorkerOptions): Promise<void> {
|
|
151
161
|
if (!('serviceWorker' in navigator)) {
|
|
152
162
|
throw new Error('Service Workers not supported');
|
|
153
163
|
}
|
|
154
164
|
|
|
165
|
+
const swUrl = options?.swUrl ?? '/__sw__.js';
|
|
166
|
+
|
|
155
167
|
// Register service worker
|
|
156
|
-
const registration = await navigator.serviceWorker.register(
|
|
168
|
+
const registration = await navigator.serviceWorker.register(swUrl, {
|
|
157
169
|
scope: '/',
|
|
158
170
|
});
|
|
159
171
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vite Plugin for almostnode
|
|
3
|
+
*
|
|
4
|
+
* Serves the service worker file from the package's dist directory,
|
|
5
|
+
* enabling seamless integration when almostnode is installed as an npm package.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Plugin, ViteDevServer } from 'vite';
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
14
|
+
// @ts-ignore - import.meta.url is available in ESM
|
|
15
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
|
|
17
|
+
export interface AlmostnodePluginOptions {
|
|
18
|
+
/**
|
|
19
|
+
* The URL path where the service worker will be served
|
|
20
|
+
* @default '/__sw__.js'
|
|
21
|
+
*/
|
|
22
|
+
swPath?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Vite plugin that serves the almostnode service worker file.
|
|
27
|
+
*
|
|
28
|
+
* When almostnode is installed as an npm package, the service worker file
|
|
29
|
+
* is located at node_modules/almostnode/dist/__sw__.js but the browser
|
|
30
|
+
* tries to load it from the root URL (/__sw__.js). This plugin intercepts
|
|
31
|
+
* requests to the service worker path and serves the file from the correct location.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* // vite.config.ts
|
|
36
|
+
* import { defineConfig } from 'vite';
|
|
37
|
+
* import { almostnodePlugin } from 'almostnode/vite';
|
|
38
|
+
*
|
|
39
|
+
* export default defineConfig({
|
|
40
|
+
* plugins: [almostnodePlugin()]
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function almostnodePlugin(options: AlmostnodePluginOptions = {}): Plugin {
|
|
45
|
+
const swPath = options.swPath || '/__sw__.js';
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
name: 'almostnode',
|
|
49
|
+
|
|
50
|
+
configureServer(server: ViteDevServer) {
|
|
51
|
+
server.middlewares.use(swPath, (_req, res) => {
|
|
52
|
+
// The service worker file is in the dist directory relative to this file
|
|
53
|
+
// In src: ../dist/__sw__.js
|
|
54
|
+
// In dist: ./__sw__.js
|
|
55
|
+
let swFilePath = path.join(__dirname, '__sw__.js');
|
|
56
|
+
|
|
57
|
+
// If running from src directory during development, look in dist
|
|
58
|
+
if (!fs.existsSync(swFilePath)) {
|
|
59
|
+
swFilePath = path.join(__dirname, '../dist/__sw__.js');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!fs.existsSync(swFilePath)) {
|
|
63
|
+
res.statusCode = 404;
|
|
64
|
+
res.end('Service worker file not found. Make sure almostnode is built.');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
res.setHeader('Content-Type', 'application/javascript');
|
|
69
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
70
|
+
res.end(fs.readFileSync(swFilePath));
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default almostnodePlugin;
|