@b9g/platform-node 0.1.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/package.json +50 -0
- package/src/index.d.ts +7 -0
- package/src/index.js +10 -0
- package/src/platform.d.ts +68 -0
- package/src/platform.js +350 -0
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@b9g/platform-node",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Node.js platform adapter for Shovel with hot reloading and ESBuild integration",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"shovel",
|
|
7
|
+
"platform",
|
|
8
|
+
"nodejs",
|
|
9
|
+
"adapter",
|
|
10
|
+
"hot-reload",
|
|
11
|
+
"esbuild"
|
|
12
|
+
],
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@b9g/platform": "workspace:*",
|
|
15
|
+
"@b9g/cache": "workspace:*",
|
|
16
|
+
"@remix-run/node-fetch-server": "^0.11.0",
|
|
17
|
+
"@aws-sdk/client-s3": "^3.0.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@b9g/libuild": "^0.1.10",
|
|
21
|
+
"bun-types": "latest",
|
|
22
|
+
"@types/node": "^18.0.0"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"types": "src/index.d.ts",
|
|
26
|
+
"module": "src/index.js",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./src/index.d.ts",
|
|
30
|
+
"import": "./src/index.js"
|
|
31
|
+
},
|
|
32
|
+
"./platform": {
|
|
33
|
+
"types": "./src/platform.d.ts",
|
|
34
|
+
"import": "./src/platform.js"
|
|
35
|
+
},
|
|
36
|
+
"./platform.js": {
|
|
37
|
+
"types": "./src/platform.d.ts",
|
|
38
|
+
"import": "./src/platform.js"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json",
|
|
41
|
+
"./index": {
|
|
42
|
+
"types": "./src/index.d.ts",
|
|
43
|
+
"import": "./src/index.js"
|
|
44
|
+
},
|
|
45
|
+
"./index.js": {
|
|
46
|
+
"types": "./src/index.d.ts",
|
|
47
|
+
"import": "./src/index.js"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @b9g/platform-node - Node.js platform adapter for Shovel
|
|
3
|
+
*
|
|
4
|
+
* Provides hot reloading, ESBuild integration, and optimized caching for Node.js environments.
|
|
5
|
+
*/
|
|
6
|
+
export { NodePlatform, createNodePlatform, type NodePlatformOptions, } from "./platform.js";
|
|
7
|
+
export type { Platform, CacheConfig, StaticConfig, Handler, Server, ServerOptions, } from "@b9g/platform";
|
package/src/index.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js platform implementation - ServiceWorker entrypoint loader for Node.js
|
|
3
|
+
*
|
|
4
|
+
* Handles the complex ESBuild VM system, hot reloading, and module linking
|
|
5
|
+
* to make ServiceWorker-style apps run in Node.js environments.
|
|
6
|
+
*/
|
|
7
|
+
import { BasePlatform, PlatformConfig, CacheConfig, Handler, Server, ServerOptions, ServiceWorkerOptions, ServiceWorkerInstance } from "@b9g/platform";
|
|
8
|
+
import { CustomCacheStorage } from "@b9g/cache";
|
|
9
|
+
export interface NodePlatformOptions extends PlatformConfig {
|
|
10
|
+
/** Enable hot reloading (default: true in development) */
|
|
11
|
+
hotReload?: boolean;
|
|
12
|
+
/** Port for development server (default: 3000) */
|
|
13
|
+
port?: number;
|
|
14
|
+
/** Host for development server (default: localhost) */
|
|
15
|
+
host?: string;
|
|
16
|
+
/** Working directory for file resolution */
|
|
17
|
+
cwd?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Node.js platform implementation
|
|
21
|
+
* ServiceWorker entrypoint loader for Node.js with ESBuild VM system
|
|
22
|
+
*/
|
|
23
|
+
export declare class NodePlatform extends BasePlatform {
|
|
24
|
+
readonly name = "node";
|
|
25
|
+
private options;
|
|
26
|
+
private workerManager?;
|
|
27
|
+
private cacheStorage?;
|
|
28
|
+
private _dist?;
|
|
29
|
+
constructor(options?: NodePlatformOptions);
|
|
30
|
+
/**
|
|
31
|
+
* Build artifacts filesystem (install-time only)
|
|
32
|
+
*/
|
|
33
|
+
get distDir(): FileSystemDirectoryHandle;
|
|
34
|
+
/**
|
|
35
|
+
* THE MAIN JOB - Load and run a ServiceWorker-style entrypoint in Node.js
|
|
36
|
+
* Uses Worker threads with coordinated cache storage for isolation and standards compliance
|
|
37
|
+
*/
|
|
38
|
+
loadServiceWorker(entrypoint: string, options?: ServiceWorkerOptions): Promise<ServiceWorkerInstance>;
|
|
39
|
+
/**
|
|
40
|
+
* Get platform-specific default cache configuration for Node.js
|
|
41
|
+
*/
|
|
42
|
+
protected getDefaultCacheConfig(): CacheConfig;
|
|
43
|
+
/**
|
|
44
|
+
* SUPPORTING UTILITY - Create cache storage optimized for Node.js
|
|
45
|
+
* Now uses the base class implementation with dynamic loading
|
|
46
|
+
*/
|
|
47
|
+
createCaches(config?: CacheConfig): Promise<CustomCacheStorage>;
|
|
48
|
+
/**
|
|
49
|
+
* SUPPORTING UTILITY - Create HTTP server for Node.js
|
|
50
|
+
*/
|
|
51
|
+
createServer(handler: Handler, options?: ServerOptions): Server;
|
|
52
|
+
/**
|
|
53
|
+
* Get filesystem root for File System Access API
|
|
54
|
+
*/
|
|
55
|
+
getFileSystemRoot(name?: string): Promise<FileSystemDirectoryHandle>;
|
|
56
|
+
/**
|
|
57
|
+
* Dispose of platform resources
|
|
58
|
+
*/
|
|
59
|
+
dispose(): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Create a Node.js platform instance
|
|
63
|
+
*/
|
|
64
|
+
export declare function createNodePlatform(options?: NodePlatformOptions): NodePlatform;
|
|
65
|
+
/**
|
|
66
|
+
* Default export for easy importing
|
|
67
|
+
*/
|
|
68
|
+
export default createNodePlatform;
|
package/src/platform.js
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/// <reference types="./platform.d.ts" />
|
|
2
|
+
// src/platform.ts
|
|
3
|
+
import {
|
|
4
|
+
BasePlatform
|
|
5
|
+
} from "@b9g/platform";
|
|
6
|
+
import { CustomCacheStorage, MemoryCacheManager, PostMessageCache } from "@b9g/cache";
|
|
7
|
+
import { FileSystemRegistry, getFileSystemRoot, NodeFileSystemAdapter, NodeFileSystemDirectoryHandle } from "@b9g/filesystem";
|
|
8
|
+
import * as Http from "http";
|
|
9
|
+
import * as Path from "path";
|
|
10
|
+
import { Worker } from "worker_threads";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
var __dirname = Path.dirname(__filename);
|
|
14
|
+
var WorkerManager = class {
|
|
15
|
+
constructor(cacheStorage, options, workerCount = 1, entrypoint) {
|
|
16
|
+
this.entrypoint = entrypoint;
|
|
17
|
+
this.memoryCacheManager = new MemoryCacheManager();
|
|
18
|
+
this.options = options;
|
|
19
|
+
console.info(
|
|
20
|
+
"[WorkerManager] Constructor called with entrypoint:",
|
|
21
|
+
entrypoint
|
|
22
|
+
);
|
|
23
|
+
this.initWorkers(workerCount);
|
|
24
|
+
}
|
|
25
|
+
workers = [];
|
|
26
|
+
currentWorker = 0;
|
|
27
|
+
requestId = 0;
|
|
28
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
29
|
+
memoryCacheManager;
|
|
30
|
+
options;
|
|
31
|
+
initWorkers(count) {
|
|
32
|
+
for (let i = 0; i < count; i++) {
|
|
33
|
+
this.createWorker();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
createWorker() {
|
|
37
|
+
let workerScript;
|
|
38
|
+
try {
|
|
39
|
+
const workerUrl = import.meta.resolve("@b9g/shovel/worker.js");
|
|
40
|
+
workerScript = fileURLToPath(workerUrl);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`Could not resolve @b9g/shovel/worker.js: ${error.message}`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
const worker = new Worker(workerScript);
|
|
47
|
+
worker.on("message", (message) => {
|
|
48
|
+
if (message.type?.startsWith("cache:")) {
|
|
49
|
+
this.memoryCacheManager.handleMessage(worker, message);
|
|
50
|
+
} else {
|
|
51
|
+
this.handleWorkerMessage(message);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
worker.on("error", (error) => {
|
|
55
|
+
console.error("[Platform-Node] Worker error:", error);
|
|
56
|
+
});
|
|
57
|
+
this.workers.push(worker);
|
|
58
|
+
return worker;
|
|
59
|
+
}
|
|
60
|
+
handleWorkerMessage(message) {
|
|
61
|
+
if (message.type === "response" && message.requestId) {
|
|
62
|
+
const pending = this.pendingRequests.get(message.requestId);
|
|
63
|
+
if (pending) {
|
|
64
|
+
const response = new Response(message.response.body, {
|
|
65
|
+
status: message.response.status,
|
|
66
|
+
statusText: message.response.statusText,
|
|
67
|
+
headers: message.response.headers
|
|
68
|
+
});
|
|
69
|
+
pending.resolve(response);
|
|
70
|
+
this.pendingRequests.delete(message.requestId);
|
|
71
|
+
}
|
|
72
|
+
} else if (message.type === "error" && message.requestId) {
|
|
73
|
+
const pending = this.pendingRequests.get(message.requestId);
|
|
74
|
+
if (pending) {
|
|
75
|
+
pending.reject(new Error(message.error));
|
|
76
|
+
this.pendingRequests.delete(message.requestId);
|
|
77
|
+
}
|
|
78
|
+
} else if (message.type === "ready") {
|
|
79
|
+
console.info(`[Platform-Node] ServiceWorker ready (v${message.version})`);
|
|
80
|
+
} else if (message.type === "worker-ready") {
|
|
81
|
+
console.info("[Platform-Node] Worker initialized");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Handle HTTP request using round-robin Worker selection
|
|
86
|
+
*/
|
|
87
|
+
async handleRequest(request) {
|
|
88
|
+
const worker = this.workers[this.currentWorker];
|
|
89
|
+
console.info(
|
|
90
|
+
`[WorkerManager] Dispatching to worker ${this.currentWorker} of ${this.workers.length}`
|
|
91
|
+
);
|
|
92
|
+
this.currentWorker = (this.currentWorker + 1) % this.workers.length;
|
|
93
|
+
const requestId = ++this.requestId;
|
|
94
|
+
return new Promise((resolve2, reject) => {
|
|
95
|
+
this.pendingRequests.set(requestId, { resolve: resolve2, reject });
|
|
96
|
+
worker.postMessage({
|
|
97
|
+
type: "request",
|
|
98
|
+
request: {
|
|
99
|
+
url: request.url,
|
|
100
|
+
method: request.method,
|
|
101
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
102
|
+
body: request.body
|
|
103
|
+
},
|
|
104
|
+
requestId
|
|
105
|
+
});
|
|
106
|
+
setTimeout(() => {
|
|
107
|
+
if (this.pendingRequests.has(requestId)) {
|
|
108
|
+
this.pendingRequests.delete(requestId);
|
|
109
|
+
reject(new Error("Request timeout"));
|
|
110
|
+
}
|
|
111
|
+
}, 3e4);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Reload ServiceWorker with new version (hot reload simulation)
|
|
116
|
+
*/
|
|
117
|
+
async reloadWorkers(version = Date.now()) {
|
|
118
|
+
console.info(`[Platform-Node] Reloading ServiceWorker (v${version})`);
|
|
119
|
+
const loadPromises = this.workers.map((worker) => {
|
|
120
|
+
return new Promise((resolve2) => {
|
|
121
|
+
const handleReady = (message) => {
|
|
122
|
+
if (message.type === "ready" && message.version === version) {
|
|
123
|
+
worker.off("message", handleReady);
|
|
124
|
+
resolve2();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
console.info("[Platform-Node] Sending load message:", {
|
|
128
|
+
version,
|
|
129
|
+
entrypoint: this.entrypoint
|
|
130
|
+
});
|
|
131
|
+
worker.on("message", handleReady);
|
|
132
|
+
worker.postMessage({
|
|
133
|
+
type: "load",
|
|
134
|
+
version,
|
|
135
|
+
entrypoint: this.entrypoint
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
await Promise.all(loadPromises);
|
|
140
|
+
console.info(`[Platform-Node] All Workers reloaded (v${version})`);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Graceful shutdown
|
|
144
|
+
*/
|
|
145
|
+
async terminate() {
|
|
146
|
+
const terminatePromises = this.workers.map((worker) => worker.terminate());
|
|
147
|
+
await Promise.allSettled(terminatePromises);
|
|
148
|
+
await this.memoryCacheManager.dispose();
|
|
149
|
+
this.workers = [];
|
|
150
|
+
this.pendingRequests.clear();
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
var NodePlatform = class extends BasePlatform {
|
|
154
|
+
name = "node";
|
|
155
|
+
options;
|
|
156
|
+
workerManager;
|
|
157
|
+
cacheStorage;
|
|
158
|
+
_dist;
|
|
159
|
+
constructor(options = {}) {
|
|
160
|
+
super(options);
|
|
161
|
+
this.options = {
|
|
162
|
+
hotReload: process.env.NODE_ENV !== "production",
|
|
163
|
+
port: 3e3,
|
|
164
|
+
host: "localhost",
|
|
165
|
+
cwd: process.cwd(),
|
|
166
|
+
...options
|
|
167
|
+
};
|
|
168
|
+
FileSystemRegistry.register("node", new NodeFileSystemAdapter({
|
|
169
|
+
rootPath: this.options.cwd
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Build artifacts filesystem (install-time only)
|
|
174
|
+
*/
|
|
175
|
+
get distDir() {
|
|
176
|
+
if (!this._dist) {
|
|
177
|
+
const distPath = Path.resolve(this.options.cwd, "dist");
|
|
178
|
+
this._dist = new NodeFileSystemDirectoryHandle(distPath);
|
|
179
|
+
}
|
|
180
|
+
return this._dist;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* THE MAIN JOB - Load and run a ServiceWorker-style entrypoint in Node.js
|
|
184
|
+
* Uses Worker threads with coordinated cache storage for isolation and standards compliance
|
|
185
|
+
*/
|
|
186
|
+
async loadServiceWorker(entrypoint, options = {}) {
|
|
187
|
+
const entryPath = Path.resolve(this.options.cwd, entrypoint);
|
|
188
|
+
if (!this.cacheStorage) {
|
|
189
|
+
this.cacheStorage = await this.createCaches(options.caches);
|
|
190
|
+
}
|
|
191
|
+
if (this.workerManager) {
|
|
192
|
+
await this.workerManager.terminate();
|
|
193
|
+
}
|
|
194
|
+
const workerCount = options.workerCount || 1;
|
|
195
|
+
console.info(
|
|
196
|
+
"[Platform-Node] Creating WorkerManager with entryPath:",
|
|
197
|
+
entryPath
|
|
198
|
+
);
|
|
199
|
+
this.workerManager = new WorkerManager(
|
|
200
|
+
this.cacheStorage,
|
|
201
|
+
this.options,
|
|
202
|
+
workerCount,
|
|
203
|
+
entryPath
|
|
204
|
+
);
|
|
205
|
+
const version = Date.now();
|
|
206
|
+
await this.workerManager.reloadWorkers(version);
|
|
207
|
+
const instance = {
|
|
208
|
+
runtime: this.workerManager,
|
|
209
|
+
handleRequest: async (request) => {
|
|
210
|
+
if (!this.workerManager) {
|
|
211
|
+
throw new Error("WorkerManager not initialized");
|
|
212
|
+
}
|
|
213
|
+
return this.workerManager.handleRequest(request);
|
|
214
|
+
},
|
|
215
|
+
install: async () => {
|
|
216
|
+
console.info(
|
|
217
|
+
"[Platform-Node] ServiceWorker installed via Worker threads"
|
|
218
|
+
);
|
|
219
|
+
},
|
|
220
|
+
activate: async () => {
|
|
221
|
+
console.info(
|
|
222
|
+
"[Platform-Node] ServiceWorker activated via Worker threads"
|
|
223
|
+
);
|
|
224
|
+
},
|
|
225
|
+
collectStaticRoutes: async () => {
|
|
226
|
+
return [];
|
|
227
|
+
},
|
|
228
|
+
get ready() {
|
|
229
|
+
return this.workerManager !== void 0;
|
|
230
|
+
},
|
|
231
|
+
dispose: async () => {
|
|
232
|
+
if (this.workerManager) {
|
|
233
|
+
await this.workerManager.terminate();
|
|
234
|
+
this.workerManager = void 0;
|
|
235
|
+
}
|
|
236
|
+
console.info("[Platform-Node] ServiceWorker disposed");
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
console.info(
|
|
240
|
+
"[Platform-Node] ServiceWorker loaded with Worker threads and coordinated caches"
|
|
241
|
+
);
|
|
242
|
+
return instance;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Get platform-specific default cache configuration for Node.js
|
|
246
|
+
*/
|
|
247
|
+
getDefaultCacheConfig() {
|
|
248
|
+
return {
|
|
249
|
+
pages: { type: "memory" },
|
|
250
|
+
// PostMessage cache for worker coordination
|
|
251
|
+
api: { type: "memory" },
|
|
252
|
+
static: { type: "memory" }
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* SUPPORTING UTILITY - Create cache storage optimized for Node.js
|
|
257
|
+
* Now uses the base class implementation with dynamic loading
|
|
258
|
+
*/
|
|
259
|
+
async createCaches(config) {
|
|
260
|
+
const cacheStorage = await super.createCaches(config);
|
|
261
|
+
return new CustomCacheStorage((name) => {
|
|
262
|
+
return new PostMessageCache(name, {
|
|
263
|
+
maxEntries: 1e3,
|
|
264
|
+
maxSize: 50 * 1024 * 1024
|
|
265
|
+
// 50MB
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* SUPPORTING UTILITY - Create HTTP server for Node.js
|
|
271
|
+
*/
|
|
272
|
+
createServer(handler, options = {}) {
|
|
273
|
+
const port = options.port ?? this.options.port;
|
|
274
|
+
const host = options.host ?? this.options.host;
|
|
275
|
+
const httpServer = Http.createServer(async (req, res) => {
|
|
276
|
+
try {
|
|
277
|
+
const url = `http://${req.headers.host}${req.url}`;
|
|
278
|
+
const request = new Request(url, {
|
|
279
|
+
method: req.method,
|
|
280
|
+
headers: req.headers,
|
|
281
|
+
body: req.method !== "GET" && req.method !== "HEAD" ? req : void 0
|
|
282
|
+
});
|
|
283
|
+
const response = await handler(request);
|
|
284
|
+
res.statusCode = response.status;
|
|
285
|
+
res.statusMessage = response.statusText;
|
|
286
|
+
response.headers.forEach((value, key) => {
|
|
287
|
+
res.setHeader(key, value);
|
|
288
|
+
});
|
|
289
|
+
if (response.body) {
|
|
290
|
+
const reader = response.body.getReader();
|
|
291
|
+
const pump = async () => {
|
|
292
|
+
const { done, value } = await reader.read();
|
|
293
|
+
if (done) {
|
|
294
|
+
res.end();
|
|
295
|
+
} else {
|
|
296
|
+
res.write(value);
|
|
297
|
+
await pump();
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
await pump();
|
|
301
|
+
} else {
|
|
302
|
+
res.end();
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.error("[Platform-Node] Request error:", error);
|
|
306
|
+
res.statusCode = 500;
|
|
307
|
+
res.setHeader("Content-Type", "text/plain");
|
|
308
|
+
res.end("Internal Server Error");
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
return {
|
|
312
|
+
listen: () => {
|
|
313
|
+
return new Promise((resolve2) => {
|
|
314
|
+
httpServer.listen(port, host, () => {
|
|
315
|
+
console.info(`\u{1F680} Server running at http://${host}:${port}`);
|
|
316
|
+
resolve2();
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
},
|
|
320
|
+
close: () => new Promise((resolve2) => {
|
|
321
|
+
httpServer.close(() => resolve2());
|
|
322
|
+
}),
|
|
323
|
+
address: () => ({ port, host })
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Get filesystem root for File System Access API
|
|
328
|
+
*/
|
|
329
|
+
async getFileSystemRoot(name = "default") {
|
|
330
|
+
return await getFileSystemRoot(name);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Dispose of platform resources
|
|
334
|
+
*/
|
|
335
|
+
async dispose() {
|
|
336
|
+
if (this.workerManager) {
|
|
337
|
+
await this.workerManager.terminate();
|
|
338
|
+
this.workerManager = void 0;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
function createNodePlatform(options) {
|
|
343
|
+
return new NodePlatform(options);
|
|
344
|
+
}
|
|
345
|
+
var platform_default = createNodePlatform;
|
|
346
|
+
export {
|
|
347
|
+
NodePlatform,
|
|
348
|
+
createNodePlatform,
|
|
349
|
+
platform_default as default
|
|
350
|
+
};
|