lucent-pack 0.1.0__py3-none-any.whl

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,794 @@
1
+ // src/httpuv/constants.ts
2
+ var WS_FRAME = {
3
+ SEND: "websocket.send",
4
+ CLOSE: "websocket.close"
5
+ };
6
+ var WASM_R_HOME = "/lib/R";
7
+ var REQUEST_TIMEOUT_MS = 18e4;
8
+ var MSG = {
9
+ REGISTER_HOST: "httpuv_register_host",
10
+ HTTP_REQUEST: "httpuv_http_request",
11
+ HTTP_RESPONSE: "httpuv_http_response",
12
+ WS_PUSH: "httpuv_ws_push",
13
+ /** Iframe → SW (on session port): frame delivered to Shiny.createSocket. */
14
+ WS_PUSH_ACK: "httpuv_ws_push_ack",
15
+ STOP: "httpuv_stop",
16
+ /** Drop cached GET /shiny/ without tearing down the R worker (app restart). */
17
+ CLEAR_APP_CACHE: "httpuv_clear_app_cache",
18
+ /** Host -> SW: full shiny::resourcePaths() map (warmup). */
19
+ REGISTER_RESOURCE_PATHS: "httpuv_register_resource_paths",
20
+ /** Host -> SW: upsert or remove one addResourcePath prefix (empty dir removes). */
21
+ UPDATE_RESOURCE_PATH: "httpuv_update_resource_path",
22
+ /** SW -> host: Comlink to the R worker was lost (e.g. after SW update). */
23
+ REQUEST_COMLINK: "httpuv_request_comlink",
24
+ /** Iframe → SW: transfer a MessagePort for session WS push delivery. */
25
+ REGISTER_SESSION: "httpuv_register_session",
26
+ /** SW → iframe (on session port): registration accepted. */
27
+ SESSION_ACK: "httpuv_session_ack",
28
+ /** SW → iframe client: session MessagePort was lost (e.g. SW idle restart); re-REGISTER_SESSION. */
29
+ REQUEST_SESSION_PORT: "httpuv_request_session_port"
30
+ };
31
+ var WARMUP_REQUEST_HEADER = "X-Shiny-Forge-Warmup";
32
+ var CHANNEL = {
33
+ HTTP_REQUEST: "httpuv_http_request",
34
+ TCP_RESPONSE: "httpuv_tcp_response",
35
+ WS_OPEN: "httpuv_ws_open",
36
+ WS_MESSAGE: "httpuv_ws_message",
37
+ WS_CLOSE: "httpuv_ws_close",
38
+ WS_RESPONSE: "httpuv_ws_response",
39
+ STDIN: "stdin"
40
+ };
41
+ var HTTPUV_OPTIONS = {
42
+ ON_REQUEST: "httpuv_onRequest",
43
+ ON_WS_OPEN: "httpuv_onWSOpen",
44
+ ON_WS_MESSAGE: "httpuv_onWSMessage",
45
+ ON_WS_CLOSE: "httpuv_onWSClose"
46
+ };
47
+ var COMLINK = {
48
+ PORT_HANDOFF: "httpuv_comlink_port",
49
+ /**
50
+ * SW -> host: the handoff port arrived. Sent before the Comlink round-trip so
51
+ * the host can distinguish a dropped handoff (worker evicted mid-boot) from an
52
+ * R worker that never finished setup.
53
+ */
54
+ PORT_HANDOFF_ACK: "httpuv_comlink_port_ack"
55
+ };
56
+
57
+ // src/httpuv/debug.ts
58
+ function isHttpuvDebug() {
59
+ if (globalThis.__HTTPUV_DEBUG__) {
60
+ return true;
61
+ }
62
+ try {
63
+ if (typeof location !== "undefined") {
64
+ const params = new URLSearchParams(location.search);
65
+ if (params.has("httpuvDebug") || params.get("debug") === "httpuv") {
66
+ return true;
67
+ }
68
+ }
69
+ if (typeof localStorage !== "undefined" && localStorage.getItem("shinyForgeDebug") === "1") {
70
+ return true;
71
+ }
72
+ if (typeof self !== "undefined" && self.location?.href) {
73
+ const params = new URL(self.location.href).searchParams;
74
+ if (params.has("httpuvDebug") || params.get("debug") === "httpuv") {
75
+ return true;
76
+ }
77
+ }
78
+ } catch {
79
+ }
80
+ return false;
81
+ }
82
+ function httpuvDebugLog(stage, ...args) {
83
+ if (!isHttpuvDebug()) {
84
+ return;
85
+ }
86
+ console.info(`[httpuv-debug:${stage}]`, ...args);
87
+ }
88
+ function enableHttpuvDebug() {
89
+ globalThis.__HTTPUV_DEBUG__ = true;
90
+ try {
91
+ localStorage.setItem("shinyForgeDebug", "1");
92
+ } catch {
93
+ }
94
+ console.info(
95
+ "[httpuv-debug] enabled - reload or run shinyForge.enableHttpuvDebug() then re-run app"
96
+ );
97
+ }
98
+
99
+ // src/httpuv/prefix.ts
100
+ var shinyPrefix = null;
101
+ function resolveShinyPrefix(fromUrl) {
102
+ const prefix = new URL("shiny/", fromUrl).pathname;
103
+ return prefix.endsWith("/") ? prefix : `${prefix}/`;
104
+ }
105
+ function setShinyPrefix(prefix) {
106
+ shinyPrefix = prefix.endsWith("/") ? prefix : `${prefix}/`;
107
+ }
108
+ function getShinyPrefix() {
109
+ if (!shinyPrefix) {
110
+ throw new Error("Shiny prefix not initialized");
111
+ }
112
+ return shinyPrefix;
113
+ }
114
+ var SESSION_DIR = "__session__";
115
+ var HOST_DIR = "__host__";
116
+ function normalizeSessionHandle(handle) {
117
+ let s = String(handle ?? "").trim();
118
+ while (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) {
119
+ s = s.slice(1, -1);
120
+ }
121
+ return s;
122
+ }
123
+ function resolveSessionPrefix(fromUrl) {
124
+ return `${resolveShinyPrefix(fromUrl)}${SESSION_DIR}/`;
125
+ }
126
+ function getSessionPrefix() {
127
+ return `${getShinyPrefix()}${SESSION_DIR}/`;
128
+ }
129
+ function parseSessionAction(urlString, prefix) {
130
+ const url = new URL(urlString);
131
+ const sessionPrefix = `${prefix}${SESSION_DIR}/`;
132
+ if (!url.pathname.startsWith(sessionPrefix)) {
133
+ return null;
134
+ }
135
+ const action = url.pathname.slice(sessionPrefix.length).replace(/\/$/, "");
136
+ if (!["open", "send", "recv", "close"].includes(action)) {
137
+ return null;
138
+ }
139
+ const rawHandle = url.searchParams.get("handle");
140
+ return {
141
+ action,
142
+ handle: rawHandle ? normalizeSessionHandle(rawHandle) : null
143
+ };
144
+ }
145
+ function isSessionHttpRequest(urlString, prefix) {
146
+ if (prefix) {
147
+ const session = parseSessionAction(urlString, prefix);
148
+ return session !== null && session.action !== "recv";
149
+ }
150
+ return /\/__session__\/(open|send|close)(?:\?|$|\/)/.test(urlString);
151
+ }
152
+ function isHostPushUrl(urlString, prefix) {
153
+ const url = new URL(urlString);
154
+ return url.pathname === `${prefix}${HOST_DIR}/push`;
155
+ }
156
+ function shinyAppUrl(subpath = "", fromUrl = import.meta.url) {
157
+ const base = new URL("shiny/", fromUrl);
158
+ return new URL(subpath.replace(/^\//, ""), base).href;
159
+ }
160
+
161
+ // src/httpuv/bridge.ts
162
+ var invokeROption = null;
163
+ var pushToR = null;
164
+ function decodeBase64Body(data) {
165
+ const text = String(data ?? "");
166
+ const binary = atob(text);
167
+ const bytes = new Uint8Array(binary.length);
168
+ for (let i = 0; i < binary.length; i++) {
169
+ bytes[i] = binary.charCodeAt(i);
170
+ }
171
+ return bytes.buffer;
172
+ }
173
+ function encodeResponseBody(body) {
174
+ if (body == null) {
175
+ return null;
176
+ }
177
+ if (typeof body === "object" && !Array.isArray(body) && body.httpuvRaw === "base64") {
178
+ return decodeBase64Body(body.data);
179
+ }
180
+ if (typeof body === "string") {
181
+ return body;
182
+ }
183
+ if (body instanceof ArrayBuffer) {
184
+ return body;
185
+ }
186
+ if (ArrayBuffer.isView(body)) {
187
+ return body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength);
188
+ }
189
+ if (Array.isArray(body)) {
190
+ return new Uint8Array(body).buffer;
191
+ }
192
+ return String(body);
193
+ }
194
+ function normalizeHeaders(headers) {
195
+ if (!headers || typeof headers !== "object") {
196
+ return {};
197
+ }
198
+ const out = {};
199
+ for (const [key, value] of Object.entries(headers)) {
200
+ if (value != null) {
201
+ out[key] = String(value);
202
+ }
203
+ }
204
+ return out;
205
+ }
206
+ function shinyPathInfo(pathname) {
207
+ const shinyPrefix2 = getShinyPrefix();
208
+ if (!pathname.startsWith(shinyPrefix2)) {
209
+ return pathname || "/";
210
+ }
211
+ const rest = pathname.slice(shinyPrefix2.length).replace(/^\/+/, "");
212
+ return rest ? `/${rest}` : "/";
213
+ }
214
+ function buildReq(msg) {
215
+ const url = new URL(msg.url);
216
+ const pathInfo = shinyPathInfo(url.pathname);
217
+ const queryString = url.search.length > 1 ? url.search.slice(1) : "";
218
+ const shinyPrefix2 = getShinyPrefix();
219
+ const req = {
220
+ UUID: msg.uuid,
221
+ REQUEST_METHOD: msg.method,
222
+ SCRIPT_NAME: shinyPrefix2.replace(/\/$/, ""),
223
+ PATH_INFO: pathInfo,
224
+ QUERY_STRING: queryString,
225
+ "rook.version": "1.1-0",
226
+ "rook.url_scheme": url.protocol === "https:" ? "https" : "http",
227
+ SERVER_NAME: url.hostname,
228
+ SERVER_PORT: url.port || (url.protocol === "https:" ? "443" : "80"),
229
+ REMOTE_ADDR: "127.0.0.1",
230
+ REMOTE_PORT: "0",
231
+ HEADERS: { ...msg.headers ?? {} }
232
+ };
233
+ for (const [key, value] of Object.entries(msg.headers ?? {})) {
234
+ req[`HTTP_${key.toUpperCase().replace(/-/g, "_")}`] = value;
235
+ }
236
+ if (msg.body) {
237
+ req.body = msg.body;
238
+ req.CONTENT_LENGTH = String(msg.body.byteLength);
239
+ }
240
+ return req;
241
+ }
242
+ function shinySocketScriptUrl() {
243
+ return new URL("./shiny-socket.js", import.meta.url).pathname;
244
+ }
245
+ function injectShinySocketBootstrap(html) {
246
+ const prefix = getShinyPrefix();
247
+ const baseTag = html.includes("<base") ? "" : `<base href="${prefix}">`;
248
+ const tag = `<script type="module" src="${shinySocketScriptUrl()}"><\/script>`;
249
+ const injection = [baseTag, tag].filter(Boolean).join("\n ");
250
+ if (html.includes("<head")) {
251
+ return html.replace(/<head([^>]*)>/, `<head$1>
252
+ ${injection}`);
253
+ }
254
+ if (html.includes("<body")) {
255
+ return html.replace(/<body([^>]*)>/, `<body$1>
256
+ ${tag}`);
257
+ }
258
+ return `${tag}
259
+ ${html}`;
260
+ }
261
+ function bodyToText(body) {
262
+ if (body == null) {
263
+ return null;
264
+ }
265
+ if (typeof body === "string") {
266
+ return body;
267
+ }
268
+ if (body instanceof ArrayBuffer) {
269
+ return new TextDecoder().decode(body);
270
+ }
271
+ if (ArrayBuffer.isView(body)) {
272
+ return new TextDecoder().decode(body);
273
+ }
274
+ if (Array.isArray(body)) {
275
+ return new TextDecoder().decode(new Uint8Array(body));
276
+ }
277
+ return null;
278
+ }
279
+ function maybeInjectShinySocketBootstrap(body, headers) {
280
+ const contentType = Object.entries(headers).find(([key]) => key.toLowerCase() === "content-type")?.[1] ?? "";
281
+ const normalized = encodeResponseBody(body);
282
+ if (!contentType.includes("text/html")) {
283
+ return normalized;
284
+ }
285
+ const html = bodyToText(normalized);
286
+ if (!html) {
287
+ return normalized;
288
+ }
289
+ return injectShinySocketBootstrap(html);
290
+ }
291
+ function sessionMessageFromBody(body) {
292
+ if (!body || body.byteLength === 0) {
293
+ return { binary: false, message: "" };
294
+ }
295
+ const bytes = new Uint8Array(body);
296
+ const isText = bytes.every((b) => b === 9 || b === 10 || b === 13 || b >= 32 && b <= 126);
297
+ if (isText) {
298
+ return { binary: false, message: new TextDecoder().decode(body) };
299
+ }
300
+ return { binary: true, message: body };
301
+ }
302
+ function handleSessionHttp(msg, session) {
303
+ switch (session.action) {
304
+ case "open": {
305
+ const handle = normalizeSessionHandle(crypto.randomUUID());
306
+ const req = buildReq(msg);
307
+ pushInboundChannelMessage({ type: CHANNEL.WS_OPEN, handle, req });
308
+ sendTcpResponse(
309
+ msg.uuid ?? "",
310
+ 200,
311
+ { "Content-Type": "application/json" },
312
+ JSON.stringify({ handle })
313
+ );
314
+ break;
315
+ }
316
+ case "send": {
317
+ if (!session.handle) {
318
+ sendTcpResponse(msg.uuid ?? "", 400, { "Content-Type": "text/plain" }, "missing handle");
319
+ return;
320
+ }
321
+ const { binary, message } = sessionMessageFromBody(msg.body);
322
+ pushInboundChannelMessage({
323
+ type: CHANNEL.WS_MESSAGE,
324
+ handle: normalizeSessionHandle(session.handle),
325
+ binary,
326
+ message
327
+ });
328
+ sendTcpResponse(msg.uuid ?? "", 204, {}, null);
329
+ break;
330
+ }
331
+ case "close": {
332
+ if (!session.handle) {
333
+ sendTcpResponse(msg.uuid ?? "", 400, { "Content-Type": "text/plain" }, "missing handle");
334
+ return;
335
+ }
336
+ pushInboundChannelMessage({
337
+ type: CHANNEL.WS_CLOSE,
338
+ handle: normalizeSessionHandle(session.handle)
339
+ });
340
+ sendTcpResponse(msg.uuid ?? "", 204, {}, null);
341
+ break;
342
+ }
343
+ default:
344
+ sendTcpResponse(
345
+ msg.uuid ?? "",
346
+ 404,
347
+ { "Content-Type": "text/plain" },
348
+ "unknown session action"
349
+ );
350
+ }
351
+ }
352
+ function sendTcpResponse(uuid, status, headers, body) {
353
+ getChannel().write({
354
+ type: CHANNEL.TCP_RESPONSE,
355
+ uuid,
356
+ data: {
357
+ status,
358
+ headers,
359
+ body
360
+ }
361
+ });
362
+ }
363
+ function getServiceWorkerController() {
364
+ return navigator.serviceWorker.controller;
365
+ }
366
+ function serializeBodyForChannel(body) {
367
+ if (!body) {
368
+ return null;
369
+ }
370
+ return Array.from(new Uint8Array(body));
371
+ }
372
+ function formatOutboundForHost(msg, deliver) {
373
+ if (msg.type === CHANNEL.TCP_RESPONSE) {
374
+ const headers = normalizeHeaders(msg.data?.headers);
375
+ let body2;
376
+ try {
377
+ body2 = maybeInjectShinySocketBootstrap(msg.data?.body, headers);
378
+ } catch (err) {
379
+ console.warn("[httpuv-bridge] shiny socket bootstrap injection failed", err);
380
+ body2 = encodeResponseBody(msg.data?.body);
381
+ }
382
+ const transfer2 = body2 instanceof ArrayBuffer ? [body2] : [];
383
+ deliver(
384
+ {
385
+ type: MSG.HTTP_RESPONSE,
386
+ uuid: msg.uuid,
387
+ status: msg.data?.status ?? 500,
388
+ headers,
389
+ body: body2
390
+ },
391
+ transfer2
392
+ );
393
+ return;
394
+ }
395
+ const body = encodeResponseBody(msg.data?.message);
396
+ const transfer = body instanceof ArrayBuffer ? [body] : [];
397
+ if (msg.type === CHANNEL.WS_RESPONSE) {
398
+ deliver(
399
+ {
400
+ type: MSG.WS_PUSH,
401
+ handle: normalizeSessionHandle(msg.data?.handle),
402
+ binary: msg.data?.binary ?? false,
403
+ wsType: msg.data?.type ?? WS_FRAME.SEND,
404
+ message: body
405
+ },
406
+ transfer
407
+ );
408
+ }
409
+ }
410
+ async function postToServiceWorker(msg) {
411
+ const controller = getServiceWorkerController();
412
+ formatOutboundForHost(msg, (outbound, transfer = []) => {
413
+ if (msg.type === CHANNEL.TCP_RESPONSE) {
414
+ if (controller) {
415
+ controller.postMessage(outbound, transfer);
416
+ return;
417
+ }
418
+ void fetch(shinyAppUrl("__host__/push"), {
419
+ method: "POST",
420
+ headers: { "Content-Type": "application/json" },
421
+ body: JSON.stringify(outbound)
422
+ });
423
+ return;
424
+ }
425
+ if (msg.type === CHANNEL.WS_RESPONSE) {
426
+ if (controller) {
427
+ controller.postMessage(outbound, transfer);
428
+ return;
429
+ }
430
+ const outboundMsg = outbound;
431
+ void fetch(shinyAppUrl("__host__/push"), {
432
+ method: "POST",
433
+ headers: { "Content-Type": "application/json" },
434
+ body: JSON.stringify({
435
+ ...outbound,
436
+ message: typeof outboundMsg.message === "string" ? outboundMsg.message : void 0
437
+ })
438
+ });
439
+ }
440
+ });
441
+ }
442
+ var postOutbound = null;
443
+ var deferredOutbound = [];
444
+ function flushDeferredOutbound() {
445
+ if (deferredOutbound.length === 0) {
446
+ return;
447
+ }
448
+ const batch = deferredOutbound.splice(0, deferredOutbound.length);
449
+ for (const msg of batch) {
450
+ postOutboundSync(msg);
451
+ }
452
+ }
453
+ function postOutboundMaybeDefer(msg) {
454
+ const depth = globalThis.Module?._rWasmEvalDepth ?? 0;
455
+ if (depth > 0) {
456
+ deferredOutbound.push(msg);
457
+ if (msg.type === CHANNEL.TCP_RESPONSE) {
458
+ httpuvDebugLog("channel-tcp-response-deferred", {
459
+ uuid: msg.uuid,
460
+ status: msg.data?.status,
461
+ depth
462
+ });
463
+ }
464
+ return;
465
+ }
466
+ if (msg.type === CHANNEL.TCP_RESPONSE) {
467
+ httpuvDebugLog("channel-tcp-response", {
468
+ uuid: msg.uuid,
469
+ status: msg.data?.status
470
+ });
471
+ }
472
+ postOutboundSync(msg);
473
+ }
474
+ function postOutboundSync(msg) {
475
+ if (postOutbound) {
476
+ formatOutboundForHost(msg, (outbound, transfer = []) => {
477
+ postOutbound?.(outbound, transfer);
478
+ });
479
+ return;
480
+ }
481
+ void postToServiceWorker(msg);
482
+ }
483
+ function createChannel() {
484
+ const inbox = [];
485
+ return {
486
+ inbox,
487
+ hasMessage() {
488
+ return inbox.length > 0;
489
+ },
490
+ read() {
491
+ return inbox.shift() ?? { type: CHANNEL.STDIN };
492
+ },
493
+ write(msg) {
494
+ if (msg.type === CHANNEL.TCP_RESPONSE || msg.type === CHANNEL.WS_RESPONSE) {
495
+ postOutboundMaybeDefer(msg);
496
+ return;
497
+ }
498
+ console.warn(
499
+ "[httpuv-bridge] inbound channel.write is deprecated; use pushInboundChannelMessage",
500
+ msg.type
501
+ );
502
+ pushInboundChannelMessage(msg);
503
+ }
504
+ };
505
+ }
506
+ function dispatch(msg) {
507
+ if (!msg || typeof msg !== "object" || typeof msg.type !== "string") {
508
+ return;
509
+ }
510
+ switch (msg.type) {
511
+ case CHANNEL.HTTP_REQUEST: {
512
+ const req = buildReq(msg);
513
+ const handled = invokeROption?.(HTTPUV_OPTIONS.ON_REQUEST, req) ?? false;
514
+ if (!handled) {
515
+ sendTcpResponse(
516
+ msg.uuid ?? "",
517
+ 503,
518
+ { "Content-Type": "text/plain" },
519
+ "httpuv: no R handler registered"
520
+ );
521
+ }
522
+ break;
523
+ }
524
+ case CHANNEL.WS_OPEN: {
525
+ const handled = invokeROption?.(HTTPUV_OPTIONS.ON_WS_OPEN, msg.handle, msg.req) ?? false;
526
+ if (!handled) {
527
+ httpuvDebugLog("bridge-ws-open-no-handler", msg.handle);
528
+ }
529
+ break;
530
+ }
531
+ case CHANNEL.WS_MESSAGE: {
532
+ const handled = invokeROption?.(
533
+ HTTPUV_OPTIONS.ON_WS_MESSAGE,
534
+ msg.handle,
535
+ msg.binary,
536
+ msg.message
537
+ ) ?? false;
538
+ if (!handled) {
539
+ httpuvDebugLog("bridge-ws-message-no-handler", msg.handle);
540
+ getChannel().write({
541
+ type: CHANNEL.WS_RESPONSE,
542
+ data: {
543
+ handle: msg.handle,
544
+ binary: msg.binary,
545
+ type: WS_FRAME.SEND,
546
+ message: msg.message
547
+ }
548
+ });
549
+ }
550
+ break;
551
+ }
552
+ case CHANNEL.WS_CLOSE: {
553
+ invokeROption?.(HTTPUV_OPTIONS.ON_WS_CLOSE, msg.handle);
554
+ break;
555
+ }
556
+ default:
557
+ console.warn("[httpuv-bridge] unhandled channel message", msg.type);
558
+ }
559
+ }
560
+ function drainInboundChannel() {
561
+ const channel = getChannel();
562
+ while (channel.hasMessage()) {
563
+ const msg = channel.read();
564
+ if (msg.type === CHANNEL.STDIN) {
565
+ continue;
566
+ }
567
+ dispatch(msg);
568
+ }
569
+ }
570
+ function getChannel() {
571
+ const httpuv = ensureModuleHttpuv();
572
+ if (!httpuv.channel) {
573
+ httpuv.channel = createChannel();
574
+ }
575
+ return httpuv.channel;
576
+ }
577
+ function ensureModuleHttpuv() {
578
+ globalThis.Module = globalThis.Module ?? {};
579
+ globalThis.Module.httpuv = globalThis.Module.httpuv ?? {};
580
+ return globalThis.Module.httpuv;
581
+ }
582
+ function pushInboundChannelMessage(msg) {
583
+ if (!pushToR) {
584
+ console.warn("[httpuv-bridge] pushToR not configured");
585
+ return false;
586
+ }
587
+ pushToR(msg);
588
+ return true;
589
+ }
590
+ function pushInboundHostMessage(msg) {
591
+ if (!msg || typeof msg !== "object") {
592
+ return;
593
+ }
594
+ switch (msg.type) {
595
+ case MSG.HTTP_REQUEST: {
596
+ const session = parseSessionAction(msg.url ?? "", getShinyPrefix());
597
+ if (session && session.action !== "recv") {
598
+ httpuvDebugLog("bridge-inbound-session", {
599
+ uuid: msg.uuid,
600
+ action: session.action,
601
+ handle: session.handle
602
+ });
603
+ handleSessionHttp(msg, session);
604
+ break;
605
+ }
606
+ httpuvDebugLog("bridge-push-http", {
607
+ uuid: msg.uuid,
608
+ method: msg.method,
609
+ url: msg.url
610
+ });
611
+ pushInboundChannelMessage({
612
+ type: CHANNEL.HTTP_REQUEST,
613
+ uuid: msg.uuid,
614
+ method: msg.method,
615
+ url: msg.url,
616
+ headers: msg.headers ?? {},
617
+ body: serializeBodyForChannel(msg.body),
618
+ clientId: msg.clientId
619
+ });
620
+ break;
621
+ }
622
+ case MSG.STOP:
623
+ break;
624
+ }
625
+ }
626
+ function handleInboundHostMessage(msg) {
627
+ pushInboundHostMessage(msg);
628
+ }
629
+ function installServiceWorkerListener() {
630
+ navigator.serviceWorker.addEventListener("message", (event) => {
631
+ handleInboundHostMessage(event.data);
632
+ });
633
+ }
634
+ function installHttpuvBridge(options = {}) {
635
+ if (options.postOutbound) {
636
+ postOutbound = options.postOutbound;
637
+ }
638
+ if (options.pushToR) {
639
+ pushToR = options.pushToR;
640
+ }
641
+ const httpuv = ensureModuleHttpuv();
642
+ if (!httpuv.channel) {
643
+ httpuv.channel = createChannel();
644
+ }
645
+ httpuv.dispatch = dispatch;
646
+ httpuv.drainInboundChannel = drainInboundChannel;
647
+ httpuv.pushInboundChannelMessage = pushInboundChannelMessage;
648
+ httpuv.pushInboundHostMessage = pushInboundHostMessage;
649
+ httpuv.buildReq = buildReq;
650
+ httpuv.injectShinySocketBootstrap = injectShinySocketBootstrap;
651
+ httpuv.shinySocketScriptUrl = shinySocketScriptUrl;
652
+ httpuv.shinyPrefix = getShinyPrefix();
653
+ if (options.requestHostService) {
654
+ httpuv.requestHostService = options.requestHostService;
655
+ }
656
+ if (options.scheduleHostDelay) {
657
+ httpuv.scheduleHostDelay = options.scheduleHostDelay;
658
+ }
659
+ httpuv.pushWsMessage = (handle, message, opts = {}) => {
660
+ getChannel().write({
661
+ type: CHANNEL.WS_RESPONSE,
662
+ data: {
663
+ handle,
664
+ binary: opts.binary ?? false,
665
+ type: opts.wsType ?? WS_FRAME.SEND,
666
+ message
667
+ }
668
+ });
669
+ };
670
+ httpuv.bindInvokeROption = (fn) => {
671
+ invokeROption = fn;
672
+ };
673
+ const installSwListener = options.installSwListener ?? true;
674
+ if (installSwListener && !httpuv._swListenerInstalled) {
675
+ installServiceWorkerListener();
676
+ httpuv._swListenerInstalled = true;
677
+ }
678
+ httpuvDebugLog("bridge-installed");
679
+ return httpuv;
680
+ }
681
+ function setInvokeROption(fn) {
682
+ invokeROption = fn;
683
+ }
684
+ function setPushToR(fn) {
685
+ pushToR = fn;
686
+ }
687
+
688
+ // src/httpuv/r-push.ts
689
+ function jsonForR(value) {
690
+ return JSON.stringify(JSON.stringify(value));
691
+ }
692
+ function serializeReqForR(req) {
693
+ const out = { ...req };
694
+ const body = out.body;
695
+ if (body instanceof ArrayBuffer) {
696
+ out.body = Array.from(new Uint8Array(body));
697
+ } else if (ArrayBuffer.isView(body)) {
698
+ out.body = Array.from(
699
+ new Uint8Array(body.buffer, body.byteOffset, body.byteLength)
700
+ );
701
+ }
702
+ return out;
703
+ }
704
+ function encodeWsPayloadBytes(message, binary) {
705
+ if (binary) {
706
+ if (message instanceof ArrayBuffer) {
707
+ return Array.from(new Uint8Array(message));
708
+ }
709
+ if (ArrayBuffer.isView(message)) {
710
+ return Array.from(new Uint8Array(message.buffer, message.byteOffset, message.byteLength));
711
+ }
712
+ if (Array.isArray(message)) {
713
+ return message;
714
+ }
715
+ }
716
+ return Array.from(new TextEncoder().encode(String(message ?? "")));
717
+ }
718
+ function isLikelyStaticAsset(url) {
719
+ return /\.(js|css|png|jpe?g|gif|svg|woff2?|ico|map)(\?|$)/i.test(url) || /\/shiny\/(shared|jquery|bootstrap|htmltools|shiny-)/i.test(url);
720
+ }
721
+ function channelMessageToRExpr(msg) {
722
+ switch (msg.type) {
723
+ case CHANNEL.HTTP_REQUEST: {
724
+ const payload = {
725
+ uuid: msg.uuid,
726
+ method: msg.method,
727
+ url: msg.url,
728
+ headers: msg.headers ?? {},
729
+ body: msg.body ?? null
730
+ };
731
+ const msgJson = jsonForR(payload);
732
+ return `local({
733
+ msg <- jsonlite::fromJSON(${msgJson}, simplifyVector=FALSE)
734
+ wrapper <- get("active_app_wrapper", envir=httpuv:::.globals)
735
+ if (is.null(wrapper)) {
736
+ if (!is.null(msg$uuid)) {
737
+ httpuv:::httpuv_write_tcp_response(
738
+ msg$uuid,
739
+ list(
740
+ status = 503L,
741
+ headers = list(\`Content-Type\` = "text/plain"),
742
+ body = "httpuv: no server running"
743
+ )
744
+ )
745
+ }
746
+ } else {
747
+ httpuv:::httpuv_handle_http_request(wrapper, msg)
748
+ }
749
+ invisible(TRUE)
750
+ })`;
751
+ }
752
+ case CHANNEL.WS_OPEN: {
753
+ const reqPart = msg.req ? `jsonlite::fromJSON(${jsonForR(serializeReqForR(msg.req))}, simplifyVector=FALSE)` : "NULL";
754
+ return `local({
755
+ wrapper <- get("active_app_wrapper", envir=httpuv:::.globals)
756
+ if (!is.null(wrapper)) {
757
+ req <- ${reqPart}
758
+ wrapper$onWSOpen(${jsonForR(msg.handle)}, httpuv:::httpuv_js_req_to_rook(req))
759
+ }
760
+ invisible(TRUE)
761
+ })`;
762
+ }
763
+ case CHANNEL.WS_MESSAGE: {
764
+ const bytesJson = jsonForR(encodeWsPayloadBytes(msg.message, Boolean(msg.binary)));
765
+ return `local({
766
+ wrapper <- get("active_app_wrapper", envir=httpuv:::.globals)
767
+ if (!is.null(wrapper)) {
768
+ msg_raw <- httpuv:::httpuv_bytes_to_raw(
769
+ jsonlite::fromJSON(${bytesJson}, simplifyVector=FALSE)
770
+ )
771
+ wrapper$onWSMessage(${jsonForR(msg.handle)}, TRUE, msg_raw)
772
+ }
773
+ invisible(TRUE)
774
+ })`;
775
+ }
776
+ case CHANNEL.WS_CLOSE:
777
+ return `local({
778
+ wrapper <- get("active_app_wrapper", envir=httpuv:::.globals)
779
+ if (!is.null(wrapper)) {
780
+ wrapper$onWSClose(${jsonForR(msg.handle)})
781
+ }
782
+ invisible(TRUE)
783
+ })`;
784
+ default:
785
+ throw new Error(`unsupported inbound channel message type: ${msg.type}`);
786
+ }
787
+ }
788
+
789
+ // src/httpuv/index.ts
790
+ var HTTPUV_TRANSPORT_VERSION = "0.0.0";
791
+
792
+ export { CHANNEL, COMLINK, HOST_DIR, HTTPUV_OPTIONS, HTTPUV_TRANSPORT_VERSION, MSG, REQUEST_TIMEOUT_MS, SESSION_DIR, WARMUP_REQUEST_HEADER, WASM_R_HOME, WS_FRAME, buildReq, channelMessageToRExpr, dispatch, drainInboundChannel, enableHttpuvDebug, flushDeferredOutbound, getSessionPrefix, getShinyPrefix, handleInboundHostMessage, httpuvDebugLog, injectShinySocketBootstrap, installHttpuvBridge, isHostPushUrl, isHttpuvDebug, isLikelyStaticAsset, isSessionHttpRequest, jsonForR, normalizeSessionHandle, parseSessionAction, pushInboundChannelMessage, pushInboundHostMessage, resolveSessionPrefix, resolveShinyPrefix, serializeReqForR, setInvokeROption, setPushToR, setShinyPrefix, shinyAppUrl, shinySocketScriptUrl };
793
+ //# sourceMappingURL=httpuv-web.js.map
794
+ //# sourceMappingURL=httpuv-web.js.map