@@ -3,13 +3,15 @@ import { parse, mustEnd } from 'yieldparser';
3
3
import { toCode } from 'scalemodel' ;
4
4
import { IconElementHandler } from './view/icons' ;
5
5
import { sha , yieldmachineSha } from './sha' ;
6
+ import * as PageContent from "./pages" ;
6
7
7
8
let devSHAs = { } ;
8
9
if ( PRODUCTION_LIKE !== '1' ) {
9
10
devSHAs = require ( './sha.dev' ) . devSHAs ;
10
11
}
11
12
12
13
const config = Object . freeze ( {
14
+ // TODO: for development, we could start a local server `npx collected-press --port 8989 ./`
13
15
pressGitHubURL : new URL ( `https://collected.press/1/github/RoyalIcing/regenerated.dev@${ sha } /` ) ,
14
16
pressS3URL : new URL ( `https://staging.collected.press/1/s3/object/us-west-2/collected-workspaces/` ) ,
15
17
jsdelivrURL : new URL ( `https://cdn.jsdelivr.net/gh/RoyalIcing/regenerated.dev@${ sha } /` ) ,
@@ -47,7 +49,7 @@ const ExternalScripts = {
47
49
} ,
48
50
}
49
51
50
- function * PrismScript ( ) {
52
+ function * PrismScript ( ) {
51
53
return ;
52
54
yield html `<!-- Prism syntax highlighting -->
53
55
< script src ="https://cdnjs.cloudflare.com/ajax/libs/prism/1.21.0/components/prism-core.min.js " integrity ="sha512-hqRrGU7ys5tkcqxx5FIZTBb7PkO2o3mU6U5+qB9b55kgMlBUT4J2wPwQfMCxeJW1fC8pBxuatxoH//z0FInhrA== " crossorigin ="anonymous "> </ script >
@@ -124,7 +126,7 @@ function* PathParser() {
124
126
yield mustEnd ;
125
127
return { type : 'press' , url : new URL ( "readme.md" , config . pressYieldmachineURL ) , title : 'Yield Machine' } ;
126
128
}
127
-
129
+
128
130
return yield [ Home , ArticleModule , ArticlePage , YieldmachinePage ] ;
129
131
}
130
132
@@ -298,6 +300,124 @@ async function renderPage(event, requestURL, contentURL, clientURL, title) {
298
300
) ;
299
301
}
300
302
303
+ // From: https://developers.cloudflare.com/workers/learning/using-websockets/#writing-a-websocket-client
304
+ async function websocket ( url ) {
305
+ // Make a fetch request including `Upgrade: websocket` header.
306
+ // The Workers Runtime will automatically handle other requirements
307
+ // of the WebSocket protocol, like the Sec-WebSocket-Key header.
308
+ const response = await fetch ( url , {
309
+ headers : {
310
+ Upgrade : 'websocket' ,
311
+ } ,
312
+ } ) ;
313
+
314
+ // If the WebSocket handshake completed successfully, then the
315
+ // response has a `webSocket` property.
316
+ let ws = response . webSocket ;
317
+ if ( ! ws ) {
318
+ throw new Error ( "server didn't accept WebSocket" ) ;
319
+ }
320
+
321
+ // Call accept() to indicate that you'll be handling the socket here
322
+ // in JavaScript, as opposed to returning it on to a client.
323
+ await ws . accept ( ) ;
324
+
325
+ // Now you can send and receive messages like before.
326
+ return ws ;
327
+ }
328
+
329
+ class CollectedPressRenderer {
330
+ constructor ( ) {
331
+ this . requestedIDs = new Set ( ) ;
332
+ this . replies = new Map ( ) ;
333
+ }
334
+
335
+ async start ( ) {
336
+ if ( this . ws ) {
337
+ console . log ( "READY STATE" , this . ws . readyState )
338
+ if ( this . ws . readyState <= 1 ) {
339
+ return this . ws
340
+ }
341
+ }
342
+
343
+ const ws = await websocket ( 'https://collected.press/1/ws' )
344
+ // const ws = await websocket('http://localhost:4321/1/ws')
345
+ ws . addEventListener ( 'message' , event => {
346
+ console . log ( "RECEIVED" , event )
347
+ try {
348
+ const data = ( typeof event . data === "string" ) ? event . data : ( new TextDecoder ( ) ) . decode ( event . data )
349
+ const reply = JSON . parse ( data ) ;
350
+ const id = reply . id ;
351
+ if ( typeof id === 'string' && this . requestedIDs . has ( id ) ) {
352
+ this . replies . set ( id , reply ) ;
353
+ }
354
+ }
355
+ finally { }
356
+ // console.log(event.data);
357
+ } ) ;
358
+ this . ws = ws ;
359
+ return ws ;
360
+ }
361
+
362
+ async send ( id , method , params ) {
363
+ const ws = await this . start ( ) ;
364
+
365
+ const message = {
366
+ jsonrpc : "2.0" ,
367
+ id,
368
+ method,
369
+ params
370
+ } ;
371
+ this . requestedIDs . add ( id ) ;
372
+ ws . send ( JSON . stringify ( message ) ) ;
373
+ }
374
+
375
+ async renderMarkdown ( source ) {
376
+ const id = crypto . randomUUID ( )
377
+ await this . send ( id , "renderMarkdown" , {
378
+ type : "text/markdown" ,
379
+ source
380
+ } ) ;
381
+
382
+ const aborter = new AbortController ( ) ;
383
+ setTimeout ( ( ) => {
384
+ aborter . abort ( )
385
+ } , 5000 ) ;
386
+
387
+ return new Promise ( ( resolve , reject ) => {
388
+ let succeeded = false ;
389
+ // aborter.signal.addEventListener('close', () => {
390
+ // console.log("CLOSE!")
391
+ // aborter.abort()
392
+ // }, { signal: aborter.signal });
393
+ // aborter.signal.addEventListener('error', () => {
394
+ // console.log("CLOSE ERROR!")
395
+ // aborter.abort()
396
+ // }, { signal: aborter.signal });
397
+ aborter . signal . addEventListener ( 'abort' , ( ) => {
398
+ // We get an error unless we close, so we always restart the WebSocket!
399
+ // It’s a bit unfortunate as the whole point is that the connection is left open.
400
+ this . ws ?. close ( )
401
+
402
+ if ( ! succeeded ) {
403
+ reject ( Error ( "Timed out" ) ) ;
404
+ }
405
+ } )
406
+
407
+ this . ws . addEventListener ( 'message' , ( ) => {
408
+ if ( this . replies . has ( id ) ) {
409
+ resolve ( this . replies . get ( id ) ) ;
410
+ succeeded = true ;
411
+ console . log ( 'ID' , id , aborter . signal . aborted )
412
+ aborter . abort ( ) ;
413
+ }
414
+ } , { signal : aborter . signal } ) ;
415
+ } ) ;
416
+ }
417
+ }
418
+
419
+ let collectedPressRenderer = null ;
420
+
301
421
/**
302
422
* Respond with results
303
423
* @param {Request } request
@@ -308,13 +428,29 @@ async function handleRequest(request, event) {
308
428
const { pathname } = url ;
309
429
const { success, result } = parsePath ( pathname ) ;
310
430
431
+ if ( ! success ) {
432
+ return notFoundResponse ( url ) ;
433
+ }
434
+
435
+ function pressURL ( path ) {
436
+ if ( PRODUCTION_LIKE === '1' ) {
437
+ pressGitHubURL ( path )
438
+ } else {
439
+ pressS3URL ( `text/markdown/${ devSHAs [ path ] } ` )
440
+ }
441
+ }
442
+
311
443
console . log ( 'Go!' )
312
444
const render = renderPage . bind ( null , event , url )
313
445
console . log ( result , devSHAs )
314
446
315
- if ( ! success ) {
316
- return notFoundResponse ( url ) ;
317
- } else if ( result . type === 'home' ) {
447
+ if ( collectedPressRenderer === null ) {
448
+ collectedPressRenderer = new CollectedPressRenderer ( )
449
+ await collectedPressRenderer . start ( )
450
+ }
451
+
452
+
453
+ if ( result . type === 'home' ) {
318
454
if ( url . searchParams . has ( 'icons' ) ) {
319
455
const res = await render ( pressGitHubURL ( "pages/home.md" ) , undefined , 'JavaScript Regenerated' ) ;
320
456
const rewriter = new HTMLRewriter ( ) ;
@@ -327,7 +463,24 @@ async function handleRequest(request, event) {
327
463
/* return new Response('<!doctype html><html lang=en><meta charset=utf-8><meta name=viewport content="width=device-width"><p>Hello!</p>', { headers: { 'content-type': contentTypes.html } }); */
328
464
} else if ( result . type === 'article' ) {
329
465
if ( result . slug === 'parsing' ) {
330
- return render ( pressGitHubURL ( "pages/parsing.md" ) , jsdelivrURL ( "pages/parsing.client.js" ) , 'JavaScript Regenerated: Parsing' )
466
+ if ( PRODUCTION_LIKE === '1' ) {
467
+ return render ( pressGitHubURL ( "pages/parsing.md" ) , jsdelivrURL ( "pages/parsing.client.js" ) , 'JavaScript Regenerated: Parsing' )
468
+ } else {
469
+ const pageContent = PageContent . Parsing ;
470
+ // const resultHTML = await collectedPressRenderer.renderMarkdown(pageContent).then(result => result?.result?.html)
471
+ const [ stream , promise ] = streamStyledHTML ( ( ) => [
472
+ // renderHTML([html`<title>`, title, html`</title>`]),
473
+ `<body>` ,
474
+ renderBanner ( ) ,
475
+ `<main>` ,
476
+ collectedPressRenderer . renderMarkdown ( pageContent ) . then ( result => result ?. result ?. html ) . catch ( error => "Error loading from collected.press: " + error . message ) ,
477
+ `</main>` ,
478
+ // fetchContentHTML(pressGitHubURL("pages/_footer.md")),
479
+ ] )
480
+ event . waitUntil ( promise ) ;
481
+ return new Response ( stream , { headers : { ...secureHTMLHeaders , 'content-type' : contentTypes . html } } ) ;
482
+ // return new Response(resultHTML ?? "Error!", { headers: { ...secureHTMLHeaders, 'content-type': contentTypes.html } });
483
+ }
331
484
} else if ( result . slug === 'routing' ) {
332
485
return render ( pressGitHubURL ( "pages/routing.md" ) , undefined , 'JavaScript Regenerated: Routing' )
333
486
} else if ( result . slug === 'pattern-matching' ) {
@@ -372,5 +525,5 @@ async function handleRequest(request, event) {
372
525
} else {
373
526
return notFoundResponse ( url ) ;
374
527
}
375
- /* return new Response(result.slug, { headers: { 'content-type': contentTypes.html } }); */
528
+ /* return new Response(result.slug, { headers: { 'content-type': contentTypes.html } }); */
376
529
}
0 commit comments