@yh-ui/yh-ui 1.0.60 → 1.0.62

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.
@@ -0,0 +1,1514 @@
1
+ const DEFAULT_EDITOR_ORIGIN = 'https://stackblitz.com';
2
+ const SEARCH_PARAM_AUTH_CODE = 'code';
3
+ const SEARCH_PARAM_ERROR = 'error';
4
+ const SEARCH_PARAM_ERROR_DESCRIPTION = 'error_description';
5
+ const BROADCAST_CHANNEL_NAME = '__wc_api_bc__';
6
+ const STORAGE_TOKENS_NAME = '__wc_api_tokens__';
7
+ const STORAGE_CODE_VERIFIER_NAME = '__wc_api_verifier__';
8
+ const STORAGE_POPUP_NAME = '__wc_api_popup__';
9
+
10
+ class TypedEventTarget {
11
+ _bus = new EventTarget();
12
+ listen(listener) {
13
+ function wrappedListener(event) {
14
+ listener(event.data);
15
+ }
16
+ this._bus.addEventListener('message', wrappedListener);
17
+ return () => this._bus.removeEventListener('message', wrappedListener);
18
+ }
19
+ fireEvent(data) {
20
+ this._bus.dispatchEvent(new MessageEvent('message', { data }));
21
+ }
22
+ }
23
+
24
+ const IGNORED_ERROR = new Error();
25
+ IGNORED_ERROR.stack = '';
26
+ const accessTokenChangedListeners = new TypedEventTarget();
27
+ /**
28
+ * @internal
29
+ */
30
+ class Tokens {
31
+ origin;
32
+ refresh;
33
+ access;
34
+ expires;
35
+ _revoked = new AbortController();
36
+ constructor(
37
+ // editor origin that those tokens are bound to, mostly used for development
38
+ origin,
39
+ // token to use to get a new access token
40
+ refresh,
41
+ // token to provide to webcontainer
42
+ access,
43
+ // time in UTC when the token expires
44
+ expires) {
45
+ this.origin = origin;
46
+ this.refresh = refresh;
47
+ this.access = access;
48
+ this.expires = expires;
49
+ }
50
+ async activate(onFailedRefresh) {
51
+ if (this._revoked.signal.aborted) {
52
+ throw new Error('Token revoked');
53
+ }
54
+ // if the access token expired we fetch a new one
55
+ if (this.expires < Date.now()) {
56
+ if (!(await this._fetchNewAccessToken())) {
57
+ return false;
58
+ }
59
+ }
60
+ this._sync();
61
+ this._startRefreshTokensLoop(onFailedRefresh);
62
+ return true;
63
+ }
64
+ async revoke(clientId, ignoreRevokeError) {
65
+ this._revoked.abort();
66
+ try {
67
+ const response = await fetch(`${this.origin}/oauth/revoke`, {
68
+ method: 'POST',
69
+ headers: {
70
+ 'Content-Type': 'application/x-www-form-urlencoded',
71
+ },
72
+ body: new URLSearchParams({ token: this.refresh, token_type_hint: 'refresh_token', client_id: clientId }),
73
+ mode: 'cors',
74
+ });
75
+ if (!response.ok) {
76
+ throw new Error(`Failed to logout`);
77
+ }
78
+ }
79
+ catch (error) {
80
+ if (!ignoreRevokeError) {
81
+ throw error;
82
+ }
83
+ }
84
+ clearTokensInStorage();
85
+ }
86
+ static fromStorage() {
87
+ const savedTokens = readTokensFromStorage();
88
+ if (!savedTokens) {
89
+ return null;
90
+ }
91
+ return new Tokens(savedTokens.origin, savedTokens.refresh, savedTokens.access, savedTokens.expires);
92
+ }
93
+ static async fromAuthCode({ editorOrigin, clientId, codeVerifier, authCode, redirectUri, }) {
94
+ const response = await fetch(`${editorOrigin}/oauth/token`, {
95
+ method: 'POST',
96
+ headers: {
97
+ 'Content-Type': 'application/x-www-form-urlencoded',
98
+ },
99
+ body: new URLSearchParams({
100
+ client_id: clientId,
101
+ code: authCode,
102
+ code_verifier: codeVerifier,
103
+ grant_type: 'authorization_code',
104
+ redirect_uri: redirectUri,
105
+ }),
106
+ mode: 'cors',
107
+ });
108
+ if (!response.ok) {
109
+ throw new Error(`Failed to fetch token: ${response.status}`);
110
+ }
111
+ const tokenResponse = await response.json();
112
+ assertTokenResponse(tokenResponse);
113
+ const { access_token: access, refresh_token: refresh } = tokenResponse;
114
+ const expires = getExpiresFromTokenResponse(tokenResponse);
115
+ return new Tokens(editorOrigin, refresh, access, expires);
116
+ }
117
+ async _fetchNewAccessToken() {
118
+ try {
119
+ const response = await fetch(`${this.origin}/oauth/token`, {
120
+ method: 'POST',
121
+ headers: {
122
+ 'Content-Type': 'application/x-www-form-urlencoded',
123
+ },
124
+ body: new URLSearchParams({
125
+ grant_type: 'refresh_token',
126
+ refresh_token: this.refresh,
127
+ }),
128
+ mode: 'cors',
129
+ signal: this._revoked.signal,
130
+ });
131
+ if (!response.ok) {
132
+ throw IGNORED_ERROR;
133
+ }
134
+ const tokenResponse = await response.json();
135
+ assertTokenResponse(tokenResponse);
136
+ const { access_token: access, refresh_token: refresh } = tokenResponse;
137
+ const expires = getExpiresFromTokenResponse(tokenResponse);
138
+ this.access = access;
139
+ this.expires = expires;
140
+ this.refresh = refresh;
141
+ return true;
142
+ }
143
+ catch {
144
+ clearTokensInStorage();
145
+ return false;
146
+ }
147
+ }
148
+ _sync() {
149
+ persistTokensInStorage(this);
150
+ fireAccessTokenChanged(this.access);
151
+ }
152
+ async _startRefreshTokensLoop(onFailedRefresh) {
153
+ while (true) {
154
+ const expiresIn = this.expires - Date.now() - 1000;
155
+ await wait(Math.max(expiresIn, 1000));
156
+ if (this._revoked.signal.aborted) {
157
+ return;
158
+ }
159
+ if (!this._fetchNewAccessToken()) {
160
+ onFailedRefresh();
161
+ return;
162
+ }
163
+ this._sync();
164
+ }
165
+ }
166
+ }
167
+ /**
168
+ * @internal
169
+ */
170
+ function clearTokensInStorage() {
171
+ localStorage.removeItem(STORAGE_TOKENS_NAME);
172
+ }
173
+ /**
174
+ * @internal
175
+ */
176
+ function addAccessTokenChangedListener(listener) {
177
+ return accessTokenChangedListeners.listen(listener);
178
+ }
179
+ function readTokensFromStorage() {
180
+ const serializedTokens = localStorage.getItem(STORAGE_TOKENS_NAME);
181
+ if (!serializedTokens) {
182
+ return null;
183
+ }
184
+ try {
185
+ return JSON.parse(serializedTokens);
186
+ }
187
+ catch {
188
+ return null;
189
+ }
190
+ }
191
+ function persistTokensInStorage(tokens) {
192
+ localStorage.setItem(STORAGE_TOKENS_NAME, JSON.stringify(tokens));
193
+ }
194
+ function getExpiresFromTokenResponse({ created_at, expires_in }) {
195
+ return (created_at + expires_in) * 1000;
196
+ }
197
+ function assertTokenResponse(token) {
198
+ if (typeof token !== 'object' || !token) {
199
+ throw new Error('Invalid Token Response');
200
+ }
201
+ if (typeof token.access_token !== 'string' ||
202
+ typeof token.refresh_token !== 'string' ||
203
+ typeof token.created_at !== 'number' ||
204
+ typeof token.expires_in !== 'number') {
205
+ throw new Error('Invalid Token Response');
206
+ }
207
+ }
208
+ function wait(ms) {
209
+ return new Promise((resolve) => setTimeout(resolve, ms));
210
+ }
211
+ function fireAccessTokenChanged(accessToken) {
212
+ accessTokenChangedListeners.fireEvent(accessToken);
213
+ }
214
+
215
+ const params = {};
216
+ let editorOrigin = null;
217
+ const iframeSettings = {
218
+ get editorOrigin() {
219
+ if (editorOrigin == null) {
220
+ editorOrigin = new URL(globalThis.WEBCONTAINER_API_IFRAME_URL ?? DEFAULT_EDITOR_ORIGIN).origin;
221
+ }
222
+ return editorOrigin;
223
+ },
224
+ set editorOrigin(newOrigin) {
225
+ editorOrigin = new URL(newOrigin).origin;
226
+ },
227
+ setQueryParam(key, value) {
228
+ params[key] = value;
229
+ },
230
+ get url() {
231
+ const url = new URL(this.editorOrigin);
232
+ url.pathname = '/headless';
233
+ for (const param in params) {
234
+ url.searchParams.set(param, params[param]);
235
+ }
236
+ url.searchParams.set('version', "1.6.4");
237
+ return url;
238
+ },
239
+ };
240
+
241
+ /**
242
+ * Implementation of https://www.rfc-editor.org/rfc/rfc7636#section-4.2 that can
243
+ * run in the browser.
244
+ *
245
+ * @internal
246
+ *
247
+ * @param input Code verifier.
248
+ */
249
+ async function S256(input) {
250
+ // input here is assumed to match https://www.rfc-editor.org/rfc/rfc3986#section-2.3
251
+ const ascii = new TextEncoder().encode(input);
252
+ const sha256 = new Uint8Array(await crypto.subtle.digest('SHA-256', ascii));
253
+ // base64url encode, based on https://developer.mozilla.org/en-US/docs/Glossary/Base64#the_unicode_problem
254
+ return btoa(sha256.reduce((binary, byte) => binary + String.fromCodePoint(byte), ''))
255
+ .replace(/\+/g, '-')
256
+ .replace(/\//g, '_')
257
+ .replace(/=+$/, '');
258
+ }
259
+ /**
260
+ * Implementation of https://www.rfc-editor.org/rfc/rfc7636#section-4.1 with
261
+ * a slight deviation:
262
+ *
263
+ * - We use 128 characters (it's expected to be between 43 and 128)
264
+ * - We use 64 characters instead of 66
265
+ *
266
+ * So the entropy is lower given the space size is 64^128 instead of 66^128.
267
+ * It still satisfies the entropy constraint given that 64^128 > 66^43.
268
+ *
269
+ * @internal
270
+ */
271
+ function newCodeVerifier() {
272
+ const random = new Uint8Array(96);
273
+ crypto.getRandomValues(random);
274
+ let codeVerifier = '';
275
+ for (let i = 0; i < 32; ++i) {
276
+ codeVerifier += nextFourChars(random[3 * i + 0], random[3 * i + 1], random[3 * i + 2]);
277
+ }
278
+ return codeVerifier;
279
+ }
280
+ function nextFourChars(byte1, byte2, byte3) {
281
+ const char1 = byte1 >> 2;
282
+ const char2 = ((byte1 & 3) << 4) | (byte2 >> 4);
283
+ const char3 = (byte2 & 15) | ((byte3 & 192) >> 2);
284
+ const char4 = byte3 & 63;
285
+ return [char1, char2, char3, char4].map(unreservedCharacters).join('');
286
+ }
287
+ function unreservedCharacters(code) {
288
+ let offset;
289
+ if (code < 26) {
290
+ offset = code + 65; // [A-Z]
291
+ }
292
+ else if (code < 52) {
293
+ offset = code - 26 + 97; // [a-z]
294
+ }
295
+ else if (code < 62) {
296
+ offset = code - 52 + 48; // [0-9]
297
+ }
298
+ else {
299
+ offset = code === 62 ? 30 /* _ */ : 45 /* - */;
300
+ }
301
+ return String.fromCharCode(offset);
302
+ }
303
+
304
+ /**
305
+ * @internal
306
+ */
307
+ function resettablePromise() {
308
+ let resolve;
309
+ let promise;
310
+ function reset() {
311
+ promise = new Promise((_resolve) => (resolve = _resolve));
312
+ }
313
+ reset();
314
+ return {
315
+ get promise() {
316
+ return promise;
317
+ },
318
+ resolve(value) {
319
+ return resolve(value);
320
+ },
321
+ reset,
322
+ };
323
+ }
324
+
325
+ /**
326
+ * @internal
327
+ */
328
+ const authState = {
329
+ initialized: false,
330
+ bootCalled: false,
331
+ authComplete: resettablePromise(),
332
+ clientId: '',
333
+ oauthScope: '',
334
+ broadcastChannel: null,
335
+ get editorOrigin() {
336
+ return iframeSettings.editorOrigin;
337
+ },
338
+ tokens: null,
339
+ };
340
+ const authFailedListeners = new TypedEventTarget();
341
+ const loggedOutListeners = new TypedEventTarget();
342
+ function broadcastMessage(message) {
343
+ if (!authState.broadcastChannel) {
344
+ return;
345
+ }
346
+ authState.broadcastChannel.postMessage(message);
347
+ // check if we are in a popup mode
348
+ if (localStorage.getItem(STORAGE_POPUP_NAME) === 'true' && message.type !== 'auth-logout') {
349
+ localStorage.removeItem(STORAGE_POPUP_NAME);
350
+ // wait a tick to make sure the posted message has been sent
351
+ setTimeout(() => {
352
+ window.close();
353
+ });
354
+ }
355
+ }
356
+ const auth$1 = {
357
+ init({ editorOrigin, clientId, scope }) {
358
+ if (authState.initialized) {
359
+ throw new Error('Init should only be called once');
360
+ }
361
+ if (authState.bootCalled) {
362
+ throw new Error('`auth.init` should always be called before `WebContainer.boot`');
363
+ }
364
+ authState.initialized = true;
365
+ authState.tokens = Tokens.fromStorage();
366
+ authState.clientId = clientId;
367
+ authState.oauthScope = scope;
368
+ authState.broadcastChannel = new BroadcastChannel(BROADCAST_CHANNEL_NAME);
369
+ // configure iframe url
370
+ iframeSettings.setQueryParam('client_id', clientId);
371
+ if (editorOrigin) {
372
+ iframeSettings.editorOrigin = new URL(editorOrigin).origin;
373
+ }
374
+ loggedOutListeners.listen(() => authState.authComplete.reset());
375
+ // if authentication or logout are done in another page, we want to reflect the state on this page as well
376
+ authState.broadcastChannel.addEventListener('message', onChannelMessage);
377
+ async function onChannelMessage(event) {
378
+ const typedEvent = event.data;
379
+ if (typedEvent.type === 'auth-complete') {
380
+ authState.tokens = Tokens.fromStorage();
381
+ // we ignore the possible error here because they can't have expired just yet
382
+ await authState.tokens.activate(onFailedTokenRefresh);
383
+ authState.authComplete.resolve();
384
+ return;
385
+ }
386
+ if (typedEvent.type === 'auth-failed') {
387
+ authFailedListeners.fireEvent(typedEvent);
388
+ return;
389
+ }
390
+ if (typedEvent.type === 'auth-logout') {
391
+ loggedOutListeners.fireEvent();
392
+ return;
393
+ }
394
+ }
395
+ if (authState.tokens) {
396
+ const tokens = authState.tokens;
397
+ if (tokens.origin === authState.editorOrigin) {
398
+ /**
399
+ * Here we assume that the refresh token never expires which
400
+ * might not be correct. If that is the case though, we will
401
+ * emit a 'logged-out' event to signal that the user has been
402
+ * logged out, which could also happen at a later time anyway.
403
+ *
404
+ * Because this flow is done entirely locally, we do not broadcast
405
+ * anything to the other tabs. They should be performing a similar
406
+ * check.
407
+ */
408
+ (async () => {
409
+ const success = await tokens.activate(onFailedTokenRefresh);
410
+ if (!success) {
411
+ // if we got new token in the meantime we discard this error
412
+ if (authState.tokens !== tokens) {
413
+ return;
414
+ }
415
+ loggedOutListeners.fireEvent();
416
+ return;
417
+ }
418
+ authState.authComplete.resolve();
419
+ })();
420
+ return { status: 'authorized' };
421
+ }
422
+ clearTokensInStorage();
423
+ authState.tokens = null;
424
+ }
425
+ const locationURL = new URL(window.location.href);
426
+ const { searchParams } = locationURL;
427
+ const updateURL = () => window.history.replaceState({}, document.title, locationURL);
428
+ // check for errors first, aka the user declined the authorisation or stackblitz did
429
+ if (searchParams.has(SEARCH_PARAM_ERROR)) {
430
+ const error = searchParams.get(SEARCH_PARAM_ERROR);
431
+ const description = searchParams.get(SEARCH_PARAM_ERROR_DESCRIPTION);
432
+ searchParams.delete(SEARCH_PARAM_ERROR);
433
+ searchParams.delete(SEARCH_PARAM_ERROR_DESCRIPTION);
434
+ updateURL();
435
+ broadcastMessage({ type: 'auth-failed', error, description });
436
+ return { status: 'auth-failed', error, description };
437
+ }
438
+ // if there's an auth code
439
+ if (searchParams.has(SEARCH_PARAM_AUTH_CODE)) {
440
+ const authCode = searchParams.get(SEARCH_PARAM_AUTH_CODE);
441
+ const editorOrigin = authState.editorOrigin;
442
+ searchParams.delete(SEARCH_PARAM_AUTH_CODE);
443
+ updateURL();
444
+ const codeVerifier = localStorage.getItem(STORAGE_CODE_VERIFIER_NAME);
445
+ if (!codeVerifier) {
446
+ return { status: 'need-auth' };
447
+ }
448
+ localStorage.removeItem(STORAGE_CODE_VERIFIER_NAME);
449
+ Tokens.fromAuthCode({
450
+ editorOrigin,
451
+ clientId: authState.clientId,
452
+ authCode,
453
+ codeVerifier,
454
+ redirectUri: defaultRedirectUri(),
455
+ })
456
+ .then(async (tokens) => {
457
+ authState.tokens = tokens;
458
+ assertAuthTokens(authState.tokens);
459
+ const success = await authState.tokens.activate(onFailedTokenRefresh);
460
+ // if authentication failed we throw, and we'll mark auth as failed
461
+ if (!success) {
462
+ throw new Error();
463
+ }
464
+ authState.authComplete.resolve();
465
+ broadcastMessage({ type: 'auth-complete' });
466
+ })
467
+ .catch((error) => {
468
+ // this should never happen unless the rails app is now down for some reason?
469
+ console.error(error);
470
+ // treat it as a logged out event so that the user can retry to login
471
+ loggedOutListeners.fireEvent();
472
+ broadcastMessage({ type: 'auth-logout' });
473
+ });
474
+ return { status: 'authorized' };
475
+ }
476
+ return { status: 'need-auth' };
477
+ },
478
+ async startAuthFlow({ popup } = {}) {
479
+ if (!authState.initialized) {
480
+ throw new Error('auth.init must be called first');
481
+ }
482
+ if (popup) {
483
+ localStorage.setItem(STORAGE_POPUP_NAME, 'true');
484
+ const height = 500;
485
+ const width = 620;
486
+ const left = window.screenLeft + (window.outerWidth - width) / 2;
487
+ const top = window.screenTop + (window.outerHeight - height) / 2;
488
+ window.open(await generateOAuthRequest(), '_blank', `popup,width=${width},height=${height},left=${left},top=${top}`);
489
+ }
490
+ else {
491
+ window.location.href = await generateOAuthRequest();
492
+ }
493
+ },
494
+ async logout({ ignoreRevokeError } = {}) {
495
+ await authState.tokens?.revoke(authState.clientId, ignoreRevokeError ?? false);
496
+ loggedOutListeners.fireEvent();
497
+ broadcastMessage({ type: 'auth-logout' });
498
+ },
499
+ loggedIn() {
500
+ return authState.authComplete.promise;
501
+ },
502
+ on(event, listener) {
503
+ switch (event) {
504
+ case 'auth-failed': {
505
+ return authFailedListeners.listen(listener);
506
+ }
507
+ case 'logged-out': {
508
+ return loggedOutListeners.listen(listener);
509
+ }
510
+ default: {
511
+ throw new Error(`Unsupported event type '${event}'.`);
512
+ }
513
+ }
514
+ },
515
+ };
516
+ function onFailedTokenRefresh() {
517
+ loggedOutListeners.fireEvent();
518
+ broadcastMessage({ type: 'auth-logout' });
519
+ }
520
+ function defaultRedirectUri() {
521
+ return window.location.href;
522
+ }
523
+ async function generateOAuthRequest() {
524
+ const codeVerifier = newCodeVerifier();
525
+ localStorage.setItem(STORAGE_CODE_VERIFIER_NAME, codeVerifier);
526
+ const codeChallenge = await S256(codeVerifier);
527
+ const url = new URL('/oauth/authorize', authState.editorOrigin);
528
+ const { searchParams } = url;
529
+ searchParams.append('response_type', 'code');
530
+ searchParams.append('client_id', authState.clientId);
531
+ searchParams.append('redirect_uri', defaultRedirectUri());
532
+ searchParams.append('scope', authState.oauthScope);
533
+ searchParams.append('code_challenge', codeChallenge);
534
+ searchParams.append('code_challenge_method', 'S256');
535
+ return url.toString();
536
+ }
537
+ /**
538
+ * @internal
539
+ */
540
+ function assertAuthTokens(tokens) {
541
+ if (!tokens) {
542
+ throw new Error('Oops! Tokens is not defined when it always should be.');
543
+ }
544
+ }
545
+
546
+ /**
547
+ * This type is in a separate module so that localservice can import it
548
+ * without bundling all the other webcontainer specific stuff.
549
+ */
550
+ var PreviewMessageType;
551
+ (function (PreviewMessageType) {
552
+ PreviewMessageType["UncaughtException"] = "PREVIEW_UNCAUGHT_EXCEPTION";
553
+ PreviewMessageType["UnhandledRejection"] = "PREVIEW_UNHANDLED_REJECTION";
554
+ PreviewMessageType["ConsoleError"] = "PREVIEW_CONSOLE_ERROR";
555
+ })(PreviewMessageType || (PreviewMessageType = {}));
556
+
557
+ var __defProp = Object.defineProperty;
558
+ var __export = (target, all) => {
559
+ for (var name in all)
560
+ __defProp(target, name, { get: all[name], enumerable: true });
561
+ };
562
+
563
+ // dist/vendor/comlink.js
564
+ var comlink_exports = {};
565
+ __export(comlink_exports, {
566
+ createEndpoint: () => createEndpoint,
567
+ expose: () => expose,
568
+ proxy: () => proxy,
569
+ proxyMarker: () => proxyMarker,
570
+ releaseProxy: () => releaseProxy,
571
+ transfer: () => transfer,
572
+ transferHandlers: () => transferHandlers,
573
+ windowEndpoint: () => windowEndpoint,
574
+ wrap: () => wrap
575
+ });
576
+
577
+ // ../../node_modules/comlink/dist/esm/comlink.mjs
578
+ var proxyMarker = Symbol("Comlink.proxy");
579
+ var createEndpoint = Symbol("Comlink.endpoint");
580
+ var releaseProxy = Symbol("Comlink.releaseProxy");
581
+ var throwMarker = Symbol("Comlink.thrown");
582
+ var isObject = (val) => typeof val === "object" && val !== null || typeof val === "function";
583
+ var proxyTransferHandler = {
584
+ canHandle: (val) => isObject(val) && val[proxyMarker],
585
+ serialize(obj) {
586
+ const { port1, port2 } = new MessageChannel();
587
+ expose(obj, port1);
588
+ return [port2, [port2]];
589
+ },
590
+ deserialize(port) {
591
+ port.start();
592
+ return wrap(port);
593
+ }
594
+ };
595
+ var throwTransferHandler = {
596
+ canHandle: (value) => isObject(value) && throwMarker in value,
597
+ serialize({ value }) {
598
+ let serialized;
599
+ if (value instanceof Error) {
600
+ serialized = {
601
+ isError: true,
602
+ value: {
603
+ message: value.message,
604
+ name: value.name,
605
+ stack: value.stack
606
+ }
607
+ };
608
+ } else {
609
+ serialized = { isError: false, value };
610
+ }
611
+ return [serialized, []];
612
+ },
613
+ deserialize(serialized) {
614
+ if (serialized.isError) {
615
+ throw Object.assign(new Error(serialized.value.message), serialized.value);
616
+ }
617
+ throw serialized.value;
618
+ }
619
+ };
620
+ var transferHandlers = /* @__PURE__ */ new Map([
621
+ ["proxy", proxyTransferHandler],
622
+ ["throw", throwTransferHandler]
623
+ ]);
624
+ function expose(obj, ep = self) {
625
+ ep.addEventListener("message", function callback(ev) {
626
+ if (!ev || !ev.data) {
627
+ return;
628
+ }
629
+ const { id, type, path } = Object.assign({ path: [] }, ev.data);
630
+ const argumentList = (ev.data.argumentList || []).map(fromWireValue);
631
+ let returnValue;
632
+ try {
633
+ const parent = path.slice(0, -1).reduce((obj2, prop) => obj2[prop], obj);
634
+ const rawValue = path.reduce((obj2, prop) => obj2[prop], obj);
635
+ switch (type) {
636
+ case 0:
637
+ {
638
+ returnValue = rawValue;
639
+ }
640
+ break;
641
+ case 1:
642
+ {
643
+ parent[path.slice(-1)[0]] = fromWireValue(ev.data.value);
644
+ returnValue = true;
645
+ }
646
+ break;
647
+ case 2:
648
+ {
649
+ returnValue = rawValue.apply(parent, argumentList);
650
+ }
651
+ break;
652
+ case 3:
653
+ {
654
+ const value = new rawValue(...argumentList);
655
+ returnValue = proxy(value);
656
+ }
657
+ break;
658
+ case 4:
659
+ {
660
+ const { port1, port2 } = new MessageChannel();
661
+ expose(obj, port2);
662
+ returnValue = transfer(port1, [port1]);
663
+ }
664
+ break;
665
+ case 5:
666
+ {
667
+ returnValue = void 0;
668
+ }
669
+ break;
670
+ }
671
+ } catch (value) {
672
+ returnValue = { value, [throwMarker]: 0 };
673
+ }
674
+ Promise.resolve(returnValue).catch((value) => {
675
+ return { value, [throwMarker]: 0 };
676
+ }).then((returnValue2) => {
677
+ const [wireValue, transferables] = toWireValue(returnValue2);
678
+ ep.postMessage(Object.assign(Object.assign({}, wireValue), { id }), transferables);
679
+ if (type === 5) {
680
+ ep.removeEventListener("message", callback);
681
+ closeEndPoint(ep);
682
+ }
683
+ });
684
+ });
685
+ if (ep.start) {
686
+ ep.start();
687
+ }
688
+ }
689
+ function isMessagePort(endpoint) {
690
+ return endpoint.constructor.name === "MessagePort";
691
+ }
692
+ function closeEndPoint(endpoint) {
693
+ if (isMessagePort(endpoint))
694
+ endpoint.close();
695
+ }
696
+ function wrap(ep, target) {
697
+ return createProxy(ep, [], target);
698
+ }
699
+ function throwIfProxyReleased(isReleased) {
700
+ if (isReleased) {
701
+ throw new Error("Proxy has been released and is not useable");
702
+ }
703
+ }
704
+ function createProxy(ep, path = [], target = function() {
705
+ }) {
706
+ let isProxyReleased = false;
707
+ const proxy2 = new Proxy(target, {
708
+ get(_target, prop) {
709
+ throwIfProxyReleased(isProxyReleased);
710
+ if (prop === releaseProxy) {
711
+ return () => {
712
+ return requestResponseMessage(ep, {
713
+ type: 5,
714
+ path: path.map((p) => p.toString())
715
+ }).then(() => {
716
+ closeEndPoint(ep);
717
+ isProxyReleased = true;
718
+ });
719
+ };
720
+ }
721
+ if (prop === "then") {
722
+ if (path.length === 0) {
723
+ return { then: () => proxy2 };
724
+ }
725
+ const r = requestResponseMessage(ep, {
726
+ type: 0,
727
+ path: path.map((p) => p.toString())
728
+ }).then(fromWireValue);
729
+ return r.then.bind(r);
730
+ }
731
+ return createProxy(ep, [...path, prop]);
732
+ },
733
+ set(_target, prop, rawValue) {
734
+ throwIfProxyReleased(isProxyReleased);
735
+ const [value, transferables] = toWireValue(rawValue);
736
+ return requestResponseMessage(ep, {
737
+ type: 1,
738
+ path: [...path, prop].map((p) => p.toString()),
739
+ value
740
+ }, transferables).then(fromWireValue);
741
+ },
742
+ apply(_target, _thisArg, rawArgumentList) {
743
+ throwIfProxyReleased(isProxyReleased);
744
+ const last = path[path.length - 1];
745
+ if (last === createEndpoint) {
746
+ return requestResponseMessage(ep, {
747
+ type: 4
748
+ }).then(fromWireValue);
749
+ }
750
+ if (last === "bind") {
751
+ return createProxy(ep, path.slice(0, -1));
752
+ }
753
+ const [argumentList, transferables] = processArguments(rawArgumentList);
754
+ return requestResponseMessage(ep, {
755
+ type: 2,
756
+ path: path.map((p) => p.toString()),
757
+ argumentList
758
+ }, transferables).then(fromWireValue);
759
+ },
760
+ construct(_target, rawArgumentList) {
761
+ throwIfProxyReleased(isProxyReleased);
762
+ const [argumentList, transferables] = processArguments(rawArgumentList);
763
+ return requestResponseMessage(ep, {
764
+ type: 3,
765
+ path: path.map((p) => p.toString()),
766
+ argumentList
767
+ }, transferables).then(fromWireValue);
768
+ }
769
+ });
770
+ return proxy2;
771
+ }
772
+ function myFlat(arr) {
773
+ return Array.prototype.concat.apply([], arr);
774
+ }
775
+ function processArguments(argumentList) {
776
+ const processed = argumentList.map(toWireValue);
777
+ return [processed.map((v) => v[0]), myFlat(processed.map((v) => v[1]))];
778
+ }
779
+ var transferCache = /* @__PURE__ */ new WeakMap();
780
+ function transfer(obj, transfers) {
781
+ transferCache.set(obj, transfers);
782
+ return obj;
783
+ }
784
+ function proxy(obj) {
785
+ return Object.assign(obj, { [proxyMarker]: true });
786
+ }
787
+ function windowEndpoint(w, context = self, targetOrigin = "*") {
788
+ return {
789
+ postMessage: (msg, transferables) => w.postMessage(msg, targetOrigin, transferables),
790
+ addEventListener: context.addEventListener.bind(context),
791
+ removeEventListener: context.removeEventListener.bind(context)
792
+ };
793
+ }
794
+ function toWireValue(value) {
795
+ for (const [name, handler] of transferHandlers) {
796
+ if (handler.canHandle(value)) {
797
+ const [serializedValue, transferables] = handler.serialize(value);
798
+ return [
799
+ {
800
+ type: 3,
801
+ name,
802
+ value: serializedValue
803
+ },
804
+ transferables
805
+ ];
806
+ }
807
+ }
808
+ return [
809
+ {
810
+ type: 0,
811
+ value
812
+ },
813
+ transferCache.get(value) || []
814
+ ];
815
+ }
816
+ function fromWireValue(value) {
817
+ switch (value.type) {
818
+ case 3:
819
+ return transferHandlers.get(value.name).deserialize(value.value);
820
+ case 0:
821
+ return value.value;
822
+ }
823
+ }
824
+ function requestResponseMessage(ep, msg, transfers) {
825
+ return new Promise((resolve) => {
826
+ const id = generateUUID();
827
+ ep.addEventListener("message", function l(ev) {
828
+ if (!ev.data || !ev.data.id || ev.data.id !== id) {
829
+ return;
830
+ }
831
+ ep.removeEventListener("message", l);
832
+ resolve(ev.data);
833
+ });
834
+ if (ep.start) {
835
+ ep.start();
836
+ }
837
+ ep.postMessage(Object.assign({ id }, msg), transfers);
838
+ });
839
+ }
840
+ function generateUUID() {
841
+ return new Array(4).fill(0).map(() => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(16)).join("-");
842
+ }
843
+
844
+ /**
845
+ * This function reloads the provided iframe.
846
+ *
847
+ * @param preview The iframe page to reload.
848
+ * @param hardRefreshTimeout The timeout after which the preview is reset if it hasn't responded to the reload event.
849
+ */
850
+ function reloadPreview(preview, hardRefreshTimeout = 200) {
851
+ const { port1, port2 } = new MessageChannel();
852
+ let resolve;
853
+ const promise = new Promise((_resolve) => {
854
+ resolve = _resolve;
855
+ });
856
+ const done = () => {
857
+ resolve();
858
+ port2.close();
859
+ };
860
+ const timeout = setTimeout(() => {
861
+ const iframeSrc = preview.src;
862
+ preview.src = iframeSrc;
863
+ done();
864
+ }, hardRefreshTimeout);
865
+ port2.addEventListener('message', (event) => {
866
+ const data = event.data;
867
+ if (data == null || typeof data !== 'object') {
868
+ return;
869
+ }
870
+ if (data.type === 'LOCALSERVICE_WINDOW_RELOADED') {
871
+ clearTimeout(timeout);
872
+ done();
873
+ }
874
+ });
875
+ preview.contentWindow?.postMessage({
876
+ type: 'LOCALSERVICE_RELOAD_WINDOW',
877
+ callback: port1,
878
+ }, '*', [port1]);
879
+ return promise;
880
+ }
881
+
882
+ const PREVIEW_MESSAGE_TYPES = [
883
+ PreviewMessageType.ConsoleError,
884
+ PreviewMessageType.UncaughtException,
885
+ PreviewMessageType.UnhandledRejection,
886
+ ];
887
+ function isPreviewMessage(data) {
888
+ if (data == null || typeof data !== 'object') {
889
+ return false;
890
+ }
891
+ if (!('type' in data) || !PREVIEW_MESSAGE_TYPES.includes(data.type)) {
892
+ return false;
893
+ }
894
+ return true;
895
+ }
896
+
897
+ /**
898
+ * @internal
899
+ */
900
+ function nullPrototype(source) {
901
+ const prototype = Object.create(null);
902
+ if (!source) {
903
+ return prototype;
904
+ }
905
+ return Object.assign(prototype, source);
906
+ }
907
+
908
+ /**
909
+ * @internal
910
+ */
911
+ function toInternalFileSystemTree(tree) {
912
+ const newTree = { d: {} };
913
+ for (const name of Object.keys(tree)) {
914
+ const entry = tree[name];
915
+ if ('file' in entry) {
916
+ if ('symlink' in entry.file) {
917
+ newTree.d[name] = { f: { l: entry.file.symlink } };
918
+ continue;
919
+ }
920
+ const contents = entry.file.contents;
921
+ const stringContents = typeof contents === 'string' ? contents : toLatin1(contents);
922
+ const binary = typeof contents === 'string' ? {} : { b: true };
923
+ newTree.d[name] = { f: { c: stringContents, ...binary } };
924
+ continue;
925
+ }
926
+ const newEntry = toInternalFileSystemTree(entry.directory);
927
+ newTree.d[name] = newEntry;
928
+ }
929
+ return newTree;
930
+ }
931
+ /**
932
+ * @internal
933
+ */
934
+ function toExternalFileSystemTree(tree) {
935
+ const newTree = nullPrototype();
936
+ if ('f' in tree) {
937
+ throw new Error('It is not possible to export a single file in the JSON format.');
938
+ }
939
+ if ('d' in tree) {
940
+ for (const name of Object.keys(tree.d)) {
941
+ const entry = tree.d[name];
942
+ if ('d' in entry) {
943
+ newTree[name] = nullPrototype({
944
+ directory: toExternalFileSystemTree(entry),
945
+ });
946
+ }
947
+ else if ('f' in entry) {
948
+ if ('c' in entry.f) {
949
+ newTree[name] = nullPrototype({
950
+ file: nullPrototype({
951
+ contents: entry.f.b ? fromBinaryString(entry.f.c) : entry.f.c,
952
+ }),
953
+ });
954
+ }
955
+ else if ('l' in entry.f) {
956
+ newTree[name] = nullPrototype({
957
+ file: nullPrototype({
958
+ symlink: entry.f.l,
959
+ }),
960
+ });
961
+ }
962
+ }
963
+ }
964
+ }
965
+ return newTree;
966
+ }
967
+ function fromBinaryString(s) {
968
+ const encoded = new Uint8Array(s.length);
969
+ for (let i = 0; i < s.length; i++) {
970
+ encoded[i] = s[i].charCodeAt(0);
971
+ }
972
+ return encoded;
973
+ }
974
+ function toLatin1(bytes) {
975
+ let decoded = '';
976
+ for (let i = 0; i < bytes.length; i++) {
977
+ decoded += String.fromCharCode(bytes[i]);
978
+ }
979
+ return decoded;
980
+ }
981
+
982
+ /**
983
+ * The WebContainer Public API allows you build custom applications on top of an in-browser Node.js runtime.
984
+ *
985
+ * Its main entrypoint is the {@link WebContainer} class.
986
+ *
987
+ * @packageDocumentation
988
+ */
989
+ const auth = auth$1;
990
+ let bootPromise = null;
991
+ let cachedServerPromise = null;
992
+ let cachedBootOptions = {};
993
+ const decoder = new TextDecoder();
994
+ const encoder = new TextEncoder();
995
+ /**
996
+ * The main export of this library. An instance of `WebContainer` represents a runtime
997
+ * ready to be used.
998
+ */
999
+ class WebContainer {
1000
+ _instance;
1001
+ _runtimeInfo;
1002
+ /**
1003
+ * Gives access to the underlying file system.
1004
+ */
1005
+ fs;
1006
+ /** @internal */
1007
+ static _instance = null;
1008
+ /** @internal */
1009
+ static _teardownPromise = null;
1010
+ _tornDown = false;
1011
+ _unsubscribeFromTokenChangedListener = () => { };
1012
+ /** @internal */
1013
+ constructor(
1014
+ /** @internal */
1015
+ _instance, fs, previewScript,
1016
+ /** @internal */
1017
+ _runtimeInfo) {
1018
+ this._instance = _instance;
1019
+ this._runtimeInfo = _runtimeInfo;
1020
+ this.fs = new FileSystemAPIClient(fs);
1021
+ // forward the credentials to webcontainer if needed
1022
+ if (authState.initialized) {
1023
+ this._unsubscribeFromTokenChangedListener = addAccessTokenChangedListener((accessToken) => {
1024
+ this._instance.setCredentials({ accessToken, editorOrigin: authState.editorOrigin });
1025
+ });
1026
+ (async () => {
1027
+ await authState.authComplete.promise;
1028
+ if (this._tornDown) {
1029
+ return;
1030
+ }
1031
+ assertAuthTokens(authState.tokens);
1032
+ await this._instance.setCredentials({
1033
+ accessToken: authState.tokens.access,
1034
+ editorOrigin: authState.editorOrigin,
1035
+ });
1036
+ })().catch((error) => {
1037
+ // print the error as this is likely a bug in webcontainer
1038
+ console.error(error);
1039
+ });
1040
+ }
1041
+ }
1042
+ async spawn(command, optionsOrArgs, options) {
1043
+ let args = [];
1044
+ if (Array.isArray(optionsOrArgs)) {
1045
+ args = optionsOrArgs;
1046
+ }
1047
+ else {
1048
+ options = optionsOrArgs;
1049
+ }
1050
+ let output = undefined;
1051
+ let outputStream = new ReadableStream();
1052
+ if (options?.output !== false) {
1053
+ const result = streamWithPush();
1054
+ output = result.push;
1055
+ outputStream = result.stream;
1056
+ }
1057
+ let stdout = undefined;
1058
+ let stdoutStream;
1059
+ let stderr = undefined;
1060
+ let stderrStream;
1061
+ const wrappedOutput = proxyListener(binaryListener(output));
1062
+ const wrappedStdout = proxyListener(binaryListener(stdout));
1063
+ const wrappedStderr = proxyListener(binaryListener(stderr));
1064
+ const process = await this._instance.run({
1065
+ command,
1066
+ args,
1067
+ cwd: options?.cwd,
1068
+ env: options?.env,
1069
+ terminal: options?.terminal,
1070
+ }, wrappedStdout, wrappedStderr, wrappedOutput);
1071
+ return new WebContainerProcessImpl(process, outputStream, stdoutStream, stderrStream);
1072
+ }
1073
+ async export(path, options) {
1074
+ const serializeOptions = {
1075
+ format: options?.format ?? 'json',
1076
+ includes: options?.includes,
1077
+ excludes: options?.excludes,
1078
+ external: true,
1079
+ };
1080
+ const result = await this._instance.serialize(path, serializeOptions);
1081
+ if (serializeOptions.format === 'json') {
1082
+ const data = JSON.parse(decoder.decode(result));
1083
+ return toExternalFileSystemTree(data);
1084
+ }
1085
+ return result;
1086
+ }
1087
+ on(event, listener) {
1088
+ if (event === 'preview-message') {
1089
+ const originalListener = listener;
1090
+ listener = ((message) => {
1091
+ if (isPreviewMessage(message)) {
1092
+ originalListener(message);
1093
+ }
1094
+ });
1095
+ }
1096
+ const { listener: wrapped, subscribe } = syncSubscription(listener);
1097
+ return subscribe(this._instance.on(event, comlink_exports.proxy(wrapped)));
1098
+ }
1099
+ /**
1100
+ * Mounts a tree of files into the filesystem. This can be specified as a tree object ({@link FileSystemTree})
1101
+ * or as a binary snapshot generated by [`@webcontainer/snapshot`](https://www.npmjs.com/package/@webcontainer/snapshot).
1102
+ *
1103
+ * @param snapshotOrTree - A tree of files, or a binary snapshot. Note that binary payloads will be transferred.
1104
+ * @param options - Additional options.
1105
+ */
1106
+ mount(snapshotOrTree, options) {
1107
+ const payload = snapshotOrTree instanceof Uint8Array
1108
+ ? snapshotOrTree
1109
+ : snapshotOrTree instanceof ArrayBuffer
1110
+ ? new Uint8Array(snapshotOrTree)
1111
+ : encoder.encode(JSON.stringify(toInternalFileSystemTree(snapshotOrTree)));
1112
+ return this._instance.loadFiles(comlink_exports.transfer(payload, [payload.buffer]), {
1113
+ mountPoints: options?.mountPoint,
1114
+ });
1115
+ }
1116
+ /**
1117
+ * Set a custom script to be injected into all previews. When this function is called, every
1118
+ * future page reload will contain the provided script tag on all HTML responses.
1119
+ *
1120
+ * Note:
1121
+ *
1122
+ * When this function resolves, every preview reloaded _after_ will have the new script.
1123
+ * Existing preview have to be explicitely reloaded.
1124
+ *
1125
+ * To reload a preview you can use `reloadPreview`.
1126
+ *
1127
+ * @param scriptSrc Source for the script tag.
1128
+ * @param options Options to define which type of script this is.
1129
+ */
1130
+ setPreviewScript(scriptSrc, options) {
1131
+ return this._instance.setPreviewScript(scriptSrc, options);
1132
+ }
1133
+ /**
1134
+ * The default value of the `PATH` environment variable for processes started through {@link spawn}.
1135
+ */
1136
+ get path() {
1137
+ return this._runtimeInfo.path;
1138
+ }
1139
+ /**
1140
+ * The full path to the working directory (see {@link FileSystemAPI}).
1141
+ */
1142
+ get workdir() {
1143
+ return this._runtimeInfo.cwd;
1144
+ }
1145
+ /**
1146
+ * Destroys the WebContainer instance, turning it unusable, and releases its resources. After this,
1147
+ * a new WebContainer instance can be obtained by calling {@link WebContainer.boot | `boot`}.
1148
+ *
1149
+ * All entities derived from this instance (e.g. processes, the file system, etc.) also become unusable
1150
+ * after calling this method.
1151
+ */
1152
+ teardown() {
1153
+ if (this._tornDown) {
1154
+ throw new Error('WebContainer already torn down');
1155
+ }
1156
+ this._tornDown = true;
1157
+ this._unsubscribeFromTokenChangedListener();
1158
+ const teardownFn = async () => {
1159
+ try {
1160
+ await this.fs._teardown();
1161
+ await this._instance.teardown();
1162
+ }
1163
+ finally {
1164
+ this._instance[comlink_exports.releaseProxy]();
1165
+ if (WebContainer._instance === this) {
1166
+ WebContainer._instance = null;
1167
+ }
1168
+ }
1169
+ };
1170
+ WebContainer._teardownPromise = teardownFn();
1171
+ }
1172
+ /**
1173
+ * Boots a WebContainer. Only a single instance of WebContainer can be booted concurrently
1174
+ * (see {@link WebContainer.teardown | `teardown`}).
1175
+ *
1176
+ * Booting WebContainer is an expensive operation.
1177
+ */
1178
+ static async boot(options = {}) {
1179
+ await this._teardownPromise;
1180
+ WebContainer._teardownPromise = null;
1181
+ const { workdirName } = options;
1182
+ if (window.crossOriginIsolated && options.coep === 'none') {
1183
+ console.warn(`A Cross-Origin-Embedder-Policy header is required in cross origin isolated environments.\nSet the 'coep' option to 'require-corp'.`);
1184
+ }
1185
+ if (workdirName?.includes('/') || workdirName === '..' || workdirName === '.') {
1186
+ throw new Error('workdirName should be a valid folder name');
1187
+ }
1188
+ // signal that boot was called to auth module as calling auth.init after boot is likely incorrect
1189
+ authState.bootCalled = true;
1190
+ // try to "acquire the lock", i.e. wait for any ongoing boot request to finish
1191
+ while (bootPromise) {
1192
+ await bootPromise;
1193
+ }
1194
+ if (WebContainer._instance) {
1195
+ throw new Error('Only a single WebContainer instance can be booted');
1196
+ }
1197
+ const instancePromise = unsynchronizedBoot(options);
1198
+ // the "lock" is a promise for the ongoing boot that never fails
1199
+ bootPromise = instancePromise.catch(() => { });
1200
+ try {
1201
+ const instance = await instancePromise;
1202
+ WebContainer._instance = instance;
1203
+ return instance;
1204
+ }
1205
+ finally {
1206
+ // release the "lock"
1207
+ bootPromise = null;
1208
+ }
1209
+ }
1210
+ }
1211
+ /**
1212
+ * Configure an API key to be used for this instance of WebContainer.
1213
+ *
1214
+ * @param key WebContainer API key.
1215
+ */
1216
+ function configureAPIKey(key) {
1217
+ if (authState.bootCalled) {
1218
+ throw new Error('`configureAPIKey` should always be called before `WebContainer.boot`');
1219
+ }
1220
+ iframeSettings.setQueryParam('client_id', key);
1221
+ }
1222
+ const DIR_ENTRY_TYPE_FILE = 1;
1223
+ const DIR_ENTRY_TYPE_DIR = 2;
1224
+ /**
1225
+ * @internal
1226
+ */
1227
+ class DirEntImpl {
1228
+ name;
1229
+ _type;
1230
+ constructor(name, _type) {
1231
+ this.name = name;
1232
+ this._type = _type;
1233
+ }
1234
+ isFile() {
1235
+ return this._type === DIR_ENTRY_TYPE_FILE;
1236
+ }
1237
+ isDirectory() {
1238
+ return this._type === DIR_ENTRY_TYPE_DIR;
1239
+ }
1240
+ }
1241
+ class FSWatcher {
1242
+ _apiClient;
1243
+ _path;
1244
+ _options;
1245
+ _listener;
1246
+ _wrappedListener;
1247
+ _watcher;
1248
+ _closed = false;
1249
+ constructor(_apiClient, _path, _options, _listener) {
1250
+ this._apiClient = _apiClient;
1251
+ this._path = _path;
1252
+ this._options = _options;
1253
+ this._listener = _listener;
1254
+ this._apiClient._watchers.add(this);
1255
+ this._wrappedListener = (event, filename) => {
1256
+ if (this._listener && !this._closed) {
1257
+ this._listener(event, filename);
1258
+ }
1259
+ };
1260
+ this._apiClient._fs
1261
+ .watch(this._path, this._options, proxyListener(this._wrappedListener))
1262
+ .then((_watcher) => {
1263
+ this._watcher = _watcher;
1264
+ if (this._closed) {
1265
+ return this._teardown();
1266
+ }
1267
+ return undefined;
1268
+ })
1269
+ .catch(console.error);
1270
+ }
1271
+ async close() {
1272
+ if (!this._closed) {
1273
+ this._closed = true;
1274
+ this._apiClient._watchers.delete(this);
1275
+ await this._teardown();
1276
+ }
1277
+ }
1278
+ /**
1279
+ * @internal
1280
+ */
1281
+ async _teardown() {
1282
+ await this._watcher?.close().finally(() => {
1283
+ this._watcher?.[comlink_exports.releaseProxy]();
1284
+ });
1285
+ }
1286
+ }
1287
+ /**
1288
+ * @internal
1289
+ */
1290
+ class WebContainerProcessImpl {
1291
+ output;
1292
+ input;
1293
+ exit;
1294
+ _process;
1295
+ stdout;
1296
+ stderr;
1297
+ constructor(process, output, stdout, stderr) {
1298
+ this.output = output;
1299
+ this._process = process;
1300
+ this.input = new WritableStream({
1301
+ write: (data) => {
1302
+ // this promise is not supposed to fail anyway
1303
+ this._getProcess()
1304
+ ?.write(data)
1305
+ .catch(() => { });
1306
+ },
1307
+ });
1308
+ this.exit = this._onExit();
1309
+ this.stdout = stdout;
1310
+ this.stderr = stderr;
1311
+ }
1312
+ kill() {
1313
+ this._process?.kill();
1314
+ }
1315
+ resize(dimensions) {
1316
+ this._getProcess()?.resize(dimensions);
1317
+ }
1318
+ async _onExit() {
1319
+ try {
1320
+ return await this._process.onExit;
1321
+ }
1322
+ finally {
1323
+ this._process?.[comlink_exports.releaseProxy]();
1324
+ this._process = null;
1325
+ }
1326
+ }
1327
+ _getProcess() {
1328
+ if (this._process == null) {
1329
+ console.warn('This process already exited');
1330
+ }
1331
+ return this._process;
1332
+ }
1333
+ }
1334
+ /**
1335
+ * @internal
1336
+ */
1337
+ class FileSystemAPIClient {
1338
+ _fs;
1339
+ _watchers = new Set([]);
1340
+ constructor(fs) {
1341
+ this._fs = fs;
1342
+ }
1343
+ rm(...args) {
1344
+ return this._fs.rm(...args);
1345
+ }
1346
+ async readFile(path, encoding) {
1347
+ return await this._fs.readFile(path, encoding);
1348
+ }
1349
+ async rename(oldPath, newPath) {
1350
+ return await this._fs.rename(oldPath, newPath);
1351
+ }
1352
+ async writeFile(path, data, options) {
1353
+ if (data instanceof Uint8Array) {
1354
+ const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
1355
+ data = comlink_exports.transfer(new Uint8Array(buffer), [buffer]);
1356
+ }
1357
+ await this._fs.writeFile(path, data, options);
1358
+ }
1359
+ async readdir(path, options) {
1360
+ const result = await this._fs.readdir(path, options);
1361
+ if (isStringArray(result)) {
1362
+ return result;
1363
+ }
1364
+ if (isTypedArrayCollection(result)) {
1365
+ return result;
1366
+ }
1367
+ const entries = result.map((entry) => new DirEntImpl(entry.name, entry['Symbol(type)']));
1368
+ return entries;
1369
+ }
1370
+ async mkdir(path, options) {
1371
+ return await this._fs.mkdir(path, options);
1372
+ }
1373
+ watch(path, options, listener) {
1374
+ if (typeof options === 'function') {
1375
+ listener = options;
1376
+ options = null;
1377
+ }
1378
+ return new FSWatcher(this, path, options, listener);
1379
+ }
1380
+ /**
1381
+ * @internal
1382
+ */
1383
+ async _teardown() {
1384
+ this._fs[comlink_exports.releaseProxy]();
1385
+ await Promise.all([...this._watchers].map((watcher) => watcher.close()));
1386
+ }
1387
+ }
1388
+ async function unsynchronizedBoot(options) {
1389
+ const { serverPromise } = serverFactory(options);
1390
+ const server = await serverPromise;
1391
+ const instance = await server.build({
1392
+ host: window.location.host,
1393
+ version: "1.6.4",
1394
+ workdirName: options.workdirName,
1395
+ forwardPreviewErrors: options.forwardPreviewErrors,
1396
+ });
1397
+ const [fs, previewScript, runtimeInfo] = await Promise.all([
1398
+ instance.fs(),
1399
+ instance.previewScript(),
1400
+ instance.runtimeInfo(),
1401
+ ]);
1402
+ return new WebContainer(instance, fs, previewScript, runtimeInfo);
1403
+ }
1404
+ function binaryListener(listener) {
1405
+ if (listener == null) {
1406
+ return undefined;
1407
+ }
1408
+ return (data) => {
1409
+ if (data instanceof Uint8Array) {
1410
+ listener(decoder.decode(data));
1411
+ }
1412
+ else if (data == null) {
1413
+ listener(null);
1414
+ }
1415
+ };
1416
+ }
1417
+ function proxyListener(listener) {
1418
+ if (listener == null) {
1419
+ return undefined;
1420
+ }
1421
+ return comlink_exports.proxy(listener);
1422
+ }
1423
+ function serverFactory(options) {
1424
+ if (cachedServerPromise != null) {
1425
+ if (options.coep !== cachedBootOptions.coep) {
1426
+ console.warn(`Attempting to boot WebContainer with 'coep: ${options.coep}'`);
1427
+ console.warn(`First boot had 'coep: ${cachedBootOptions.coep}', new settings will not take effect!`);
1428
+ }
1429
+ return { serverPromise: cachedServerPromise };
1430
+ }
1431
+ if (options.coep) {
1432
+ iframeSettings.setQueryParam('coep', options.coep);
1433
+ }
1434
+ if (options.experimentalNode) {
1435
+ iframeSettings.setQueryParam('experimental_node', '1');
1436
+ }
1437
+ const iframe = document.createElement('iframe');
1438
+ iframe.style.display = 'none';
1439
+ iframe.setAttribute('allow', 'cross-origin-isolated');
1440
+ const url = iframeSettings.url;
1441
+ iframe.src = url.toString();
1442
+ const { origin } = url;
1443
+ cachedBootOptions = { ...options };
1444
+ cachedServerPromise = new Promise((resolve) => {
1445
+ const onMessage = (event) => {
1446
+ if (event.origin !== origin) {
1447
+ return;
1448
+ }
1449
+ const { data } = event;
1450
+ if (data.type === 'init') {
1451
+ resolve(comlink_exports.wrap(event.ports[0]));
1452
+ return;
1453
+ }
1454
+ if (data.type === 'warning') {
1455
+ console[data.level].call(console, data.message);
1456
+ return;
1457
+ }
1458
+ };
1459
+ window.addEventListener('message', onMessage);
1460
+ });
1461
+ document.body.insertBefore(iframe, null);
1462
+ return { serverPromise: cachedServerPromise };
1463
+ }
1464
+ function isStringArray(list) {
1465
+ return typeof list[0] === 'string';
1466
+ }
1467
+ function isTypedArrayCollection(list) {
1468
+ return list[0] instanceof Uint8Array;
1469
+ }
1470
+ function streamWithPush() {
1471
+ let controller = null;
1472
+ const stream = new ReadableStream({
1473
+ start(controller_) {
1474
+ controller = controller_;
1475
+ },
1476
+ });
1477
+ const push = (item) => {
1478
+ if (item != null) {
1479
+ controller?.enqueue(item);
1480
+ }
1481
+ else {
1482
+ controller?.close();
1483
+ controller = null;
1484
+ }
1485
+ };
1486
+ return { stream, push };
1487
+ }
1488
+ function syncSubscription(listener) {
1489
+ let stopped = false;
1490
+ let unsubscribe = () => { };
1491
+ const wrapped = ((...args) => {
1492
+ if (stopped) {
1493
+ return;
1494
+ }
1495
+ listener(...args);
1496
+ });
1497
+ return {
1498
+ subscribe(promise) {
1499
+ promise.then((unsubscribe_) => {
1500
+ unsubscribe = unsubscribe_;
1501
+ if (stopped) {
1502
+ unsubscribe();
1503
+ }
1504
+ });
1505
+ return () => {
1506
+ stopped = true;
1507
+ unsubscribe();
1508
+ };
1509
+ },
1510
+ listener: wrapped,
1511
+ };
1512
+ }
1513
+
1514
+ export { PreviewMessageType, WebContainer, auth, configureAPIKey, isPreviewMessage, reloadPreview };