22
33import crypto from "node:crypto" ;
44import os from "node:os" ;
5+ import { pathToFileURL } from "node:url" ;
56
67const cloudUrl = (
78 process . env . SIMDECK_CLOUD_URL || "https://simdeck.djdev.me"
@@ -22,58 +23,62 @@ const providerId =
2223let stopped = false ;
2324let lastRegisterAt = 0 ;
2425let registered = false ;
25- for ( const signal of [ "SIGINT" , "SIGTERM" , "SIGHUP" ] ) {
26- process . once ( signal , ( ) => {
27- stopped = true ;
28- } ) ;
29- }
3026
31- try {
32- if ( ! previewId || ! providerToken ) {
33- const session = await createLocalProviderSession ( ) ;
34- previewId = session . sessionId ;
35- providerToken = session . providerToken ;
36- publicUrl = session . url ;
27+ if ( isMainModule ( ) ) {
28+ for ( const signal of [ "SIGINT" , "SIGTERM" , "SIGHUP" ] ) {
29+ process . once ( signal , ( ) => {
30+ stopped = true ;
31+ } ) ;
3732 }
3833
39- if ( ! publicUrl ) {
40- publicUrl = `${ cloudUrl } /simulator/${ encodeURIComponent ( previewId ) } ` ;
41- }
42- if ( ! localToken ) {
43- localToken = providerToken ;
44- }
34+ try {
35+ if ( ! previewId || ! providerToken ) {
36+ const session = await createLocalProviderSession ( ) ;
37+ previewId = session . sessionId ;
38+ providerToken = session . providerToken ;
39+ publicUrl = session . url ;
40+ }
4541
46- console . log ( `[simdeck-provider-bridge] ${ publicUrl } ` ) ;
42+ if ( ! publicUrl ) {
43+ publicUrl = `${ cloudUrl } /simulator/${ encodeURIComponent ( previewId ) } ` ;
44+ }
45+ publicUrl = normalizeStudioPublicUrl ( publicUrl ) ;
46+ if ( ! localToken ) {
47+ localToken = providerToken ;
48+ }
4749
48- await registerProvider ( ) ;
50+ console . log ( `[simdeck-provider-bridge] ${ publicUrl } ` ) ;
4951
50- while ( ! stopped ) {
51- try {
52- if ( Date . now ( ) - lastRegisterAt > registerIntervalMs ) {
53- await registerProvider ( ) ;
54- }
55- const next = await fetchJson (
56- `${ cloudUrl } /api/actions/providers/rpc/next` ,
57- {
58- previewId,
59- providerToken,
60- } ,
61- ) ;
62- if ( ! next || ! next . request ) {
63- await sleep ( 250 ) ;
64- continue ;
52+ await registerProvider ( ) ;
53+
54+ while ( ! stopped ) {
55+ try {
56+ if ( Date . now ( ) - lastRegisterAt > registerIntervalMs ) {
57+ await registerProvider ( ) ;
58+ }
59+ const next = await fetchJson (
60+ `${ cloudUrl } /api/actions/providers/rpc/next` ,
61+ {
62+ previewId,
63+ providerToken,
64+ } ,
65+ ) ;
66+ if ( ! next || ! next . request ) {
67+ await sleep ( 250 ) ;
68+ continue ;
69+ }
70+ await handleRequest ( next . request ) ;
71+ } catch ( error ) {
72+ console . error (
73+ `[simdeck-provider-bridge] ${ error instanceof Error ? error . message : String ( error ) } ` ,
74+ ) ;
75+ await sleep ( 1000 ) ;
6576 }
66- await handleRequest ( next . request ) ;
67- } catch ( error ) {
68- console . error (
69- `[simdeck-provider-bridge] ${ error instanceof Error ? error . message : String ( error ) } ` ,
70- ) ;
71- await sleep ( 1000 ) ;
7277 }
73- }
74- } finally {
75- if ( registered ) {
76- await markProviderExpired ( ) ;
78+ } finally {
79+ if ( registered ) {
80+ await markProviderExpired ( ) ;
81+ }
7782 }
7883}
7984
@@ -233,6 +238,36 @@ function sleep(ms) {
233238 return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
234239}
235240
241+ function normalizeStudioPublicUrl ( value ) {
242+ return normalizeStudioPublicUrlWithCloud ( value , cloudUrl ) ;
243+ }
244+
245+ export function normalizeStudioPublicUrlWithCloud ( value , baseCloudUrl ) {
246+ const trimmed = String ( value || "" ) . trim ( ) ;
247+ if ( ! trimmed ) {
248+ return "" ;
249+ }
250+
251+ const normalizedCloudUrl = baseCloudUrl . replace ( / \/ $ / , "" ) ;
252+ const cloudOrigin = new URL ( normalizedCloudUrl ) . origin ;
253+ const collapsed = trimmed
254+ . replace ( repeatedPrefixPattern ( normalizedCloudUrl ) , normalizedCloudUrl )
255+ . replace ( repeatedPrefixPattern ( cloudOrigin ) , cloudOrigin ) ;
256+ return new URL ( collapsed , `${ normalizedCloudUrl } /` ) . toString ( ) ;
257+ }
258+
259+ function repeatedPrefixPattern ( prefix ) {
260+ return new RegExp ( `^(?:${ escapeRegExp ( prefix ) } ){2,}` ) ;
261+ }
262+
263+ function escapeRegExp ( value ) {
264+ return value . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, "\\$&" ) ;
265+ }
266+
267+ function isMainModule ( ) {
268+ return import . meta. url === pathToFileURL ( process . argv [ 1 ] || "" ) . href ;
269+ }
270+
236271function stableLocalProviderId ( ) {
237272 const fingerprint = [
238273 os . hostname ( ) ,
0 commit comments