Skip to content

Commit ec8678e

Browse files
committedMay 21, 2022
Use collected.press’s new websocket API to render local content
1 parent 2227138 commit ec8678e

File tree

11 files changed

+1469
-6112
lines changed

11 files changed

+1469
-6112
lines changed
 

‎Makefile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ latest:
1010

1111
dev: install
1212
@CF_ACCOUNT_ID=$(CF_ACCOUNT_ID) CF_ZONE_ID=$(CF_ZONE_ID) npm start
13-
14-
miniflare: install
15-
npx miniflare@latest --live-reload
1613

1714
production: sha.js
1815
@CF_ACCOUNT_ID=$(CF_ACCOUNT_ID) CF_ZONE_ID=$(CF_ZONE_ID) wrangler publish
@@ -49,7 +46,7 @@ define sha256_file
4946
endef
5047

5148
AWS_ENV := AWS_ACCESS_KEY_ID=$(AWS_ACCESS_KEY_ID) AWS_SECRET_ACCESS_KEY=$(AWS_SECRET_ACCESS_KEY)
52-
FILES := pages/machines.client.js pages/machines.md
49+
FILES := pages/machines.client.js pages/machines.md pages/parsing.md
5350
FILE := pages/machines.client.js
5451
DIGEST_HEX := $(call sha256_file,$(FILE))
5552
MIME_TYPE := application/javascript
@@ -79,3 +76,7 @@ tmp/sha/$(REGENERATED_DEV_SHA) tmp/sha/$(YIELDMACHINE_SHA):
7976

8077
sha.js: tmp/sha/$(REGENERATED_DEV_SHA) tmp/sha/$(YIELDMACHINE_SHA)
8178
@echo "export const sha = '$(REGENERATED_DEV_SHA)'; export const yieldmachineSha = '$(YIELDMACHINE_SHA)';" > sha.js
79+
80+
tmp/pages-txt/parsing.txt:
81+
@mkdir -p tmp/pages-txt
82+
@cp pages/parsing.md tmp/pages-txt/parsing.txt

‎collected-press-local.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
const http = require("http");
2+
const host = 'localhost';
3+
const port = 8000;
4+
5+
function handler(req, res) {
6+
res.writeHead(200);
7+
res.end("My first server!");
8+
};

‎index.js

Lines changed: 160 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import { parse, mustEnd } from 'yieldparser';
33
import { toCode } from 'scalemodel';
44
import { IconElementHandler } from './view/icons';
55
import { sha, yieldmachineSha } from './sha';
6+
import * as PageContent from "./pages";
67

78
let devSHAs = {};
89
if (PRODUCTION_LIKE !== '1') {
910
devSHAs = require('./sha.dev').devSHAs;
1011
}
1112

1213
const config = Object.freeze({
14+
// TODO: for development, we could start a local server `npx collected-press --port 8989 ./`
1315
pressGitHubURL: new URL(`https://collected.press/1/github/RoyalIcing/regenerated.dev@${sha}/`),
1416
pressS3URL: new URL(`https://staging.collected.press/1/s3/object/us-west-2/collected-workspaces/`),
1517
jsdelivrURL: new URL(`https://cdn.jsdelivr.net/gh/RoyalIcing/regenerated.dev@${sha}/`),
@@ -47,7 +49,7 @@ const ExternalScripts = {
4749
},
4850
}
4951

50-
function *PrismScript() {
52+
function* PrismScript() {
5153
return;
5254
yield html`<!-- Prism syntax highlighting -->
5355
<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() {
124126
yield mustEnd;
125127
return { type: 'press', url: new URL("readme.md", config.pressYieldmachineURL), title: 'Yield Machine' };
126128
}
127-
129+
128130
return yield [Home, ArticleModule, ArticlePage, YieldmachinePage];
129131
}
130132

@@ -298,6 +300,124 @@ async function renderPage(event, requestURL, contentURL, clientURL, title) {
298300
);
299301
}
300302

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+
301421
/**
302422
* Respond with results
303423
* @param {Request} request
@@ -308,13 +428,29 @@ async function handleRequest(request, event) {
308428
const { pathname } = url;
309429
const { success, result } = parsePath(pathname);
310430

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+
311443
console.log('Go!')
312444
const render = renderPage.bind(null, event, url)
313445
console.log(result, devSHAs)
314446

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') {
318454
if (url.searchParams.has('icons')) {
319455
const res = await render(pressGitHubURL("pages/home.md"), undefined, 'JavaScript Regenerated');
320456
const rewriter = new HTMLRewriter();
@@ -327,7 +463,24 @@ async function handleRequest(request, event) {
327463
/* 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 } }); */
328464
} else if (result.type === 'article') {
329465
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+
}
331484
} else if (result.slug === 'routing') {
332485
return render(pressGitHubURL("pages/routing.md"), undefined, 'JavaScript Regenerated: Routing')
333486
} else if (result.slug === 'pattern-matching') {
@@ -372,5 +525,5 @@ async function handleRequest(request, event) {
372525
} else {
373526
return notFoundResponse(url);
374527
}
375-
/* return new Response(result.slug, { headers: { 'content-type': contentTypes.html } }); */
528+
/* return new Response(result.slug, { headers: { 'content-type': contentTypes.html } }); */
376529
}

‎package-lock.json

Lines changed: 1257 additions & 6090 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"private": true,
3-
"name": "worker",
3+
"name": "regenerated.dev",
44
"version": "1.0.0",
55
"description": "A template for kick starting a Cloudflare Workers project",
66
"main": "index.js",
@@ -12,14 +12,12 @@
1212
"author": "Patrick Smith <pgwsmith@gmail.com>",
1313
"license": "MIT",
1414
"devDependencies": {
15-
"@cloudflare/wrangler": "^1.19.3",
16-
"@playwright/test": "^1.15.0",
15+
"@playwright/test": "^1.22.2",
1716
"aws4fetch": "^1.0.13",
18-
"miniflare": "^1.4.1",
19-
"prettier": "^1.19.1"
17+
"prettier": "^1.19.1",
18+
"wrangler": "^2.0.6"
2019
},
2120
"dependencies": {
22-
"parcook": "^0.2.0",
2321
"scalemodel": "^0.1.0",
2422
"yieldcss": "^0.1.0",
2523
"yieldmarkup": "^0.2.0",

‎pages/apply.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Apply
2+
3+
```js
4+
function* Numbers() {
5+
yield 1;
6+
yield 2;
7+
yield 3;
8+
}
9+
10+
apply(Numbers, []); // [1, 2, 3]
11+
12+
function* Concat(head, tail) {
13+
yield* head;
14+
yield* tail;
15+
}
16+
17+
apply(Concat, [[1, 2], [3, 4]]); // [1, 2, 3, 4]
18+
```
19+
20+
```js
21+
function apply(f, a) {
22+
return Array.from(f.apply(null, a));
23+
}
24+
```

‎pages/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// export * as ParsingDebug from "./parsing.txt"
2+
// console.log("ParsingDebug", ParsingDebug)
3+
export { default as Parsing } from "../tmp/pages-txt/parsing.txt"

‎pages/machines.client.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
cond,
88
always,
99
accumulate,
10-
} from 'https://cdn.jsdelivr.net/npm/yieldmachine@0.4.10/dist/yieldmachine.mjs';
10+
} from 'https://cdn.jsdelivr.net/npm/yieldmachine@0.4.12/dist/yieldmachine.module.js';
1111

1212
function ClickedState(button) {
1313
function* Initial() {

‎pages/parsing.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
Library used: [yieldparser](https://github.com/JavaScriptRegenerated/yieldparser)
44

5-
## IP Address
5+
## Examples
6+
7+
### IP Address
68

79
<form class="Y">
810
<label for=parsing-ip-address-input>Enter text to parse:</label> <input id=parsing-ip-address-input type=text value="1.2.3.4">
911
<output id=parsing-ip-address-output><pre><code class="target language-json"></code></pre></output>
1012
</form>
1113

14+
----
15+
1216
```js
1317
import { parse } from 'yieldparser';
1418

‎sha.dev.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const devSHAs = { 'pages/machines.client.js': 'fd6e5535c0006de4f0d02f9828acc2606f1b911cad3daf0bc3e42f12b4cc2f02', 'pages/machines.md': '06cb35a52fd2f62f177b1ad956d5b931dc46f45d54bdca7a6122daaa22865c7b' }
1+
export const devSHAs = { 'pages/machines.client.js': 'a9a2aeea6a689f636148919e1dc9c0406d45f4645d43aff8141c076350170294', 'pages/machines.md': '06cb35a52fd2f62f177b1ad956d5b931dc46f45d54bdca7a6122daaa22865c7b' }

‎wrangler.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
name = "regenerated-dev"
2+
main = "index.js"
23
compatibility_date = "2021-09-26"
3-
type = "webpack"
4-
zone_id = ""
54
kv_namespaces = []
65
workers_dev = true
76

0 commit comments

Comments
 (0)
Please sign in to comment.