@vyckr/tachyon 0.2.0 → 1.0.0
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.
- package/.env.example +3 -17
- package/README.md +87 -57
- package/bun.lock +127 -0
- package/components/counter.html +13 -0
- package/deno.lock +19 -0
- package/go.mod +3 -0
- package/lib/gson-2.3.jar +0 -0
- package/main.js +0 -0
- package/package.json +19 -20
- package/routes/DELETE +18 -0
- package/routes/GET +17 -0
- package/routes/HTML +28 -0
- package/routes/POST +32 -0
- package/routes/SOCKET +26 -0
- package/routes/api/:version/DELETE +10 -0
- package/routes/api/:version/GET +29 -0
- package/routes/api/:version/PATCH +24 -0
- package/routes/api/GET +29 -0
- package/routes/api/POST +16 -0
- package/routes/api/PUT +21 -0
- package/src/client/404.html +7 -0
- package/src/client/dev.html +14 -0
- package/src/client/dist.ts +20 -0
- package/src/client/hmr.js +12 -0
- package/src/client/prod.html +13 -0
- package/src/client/render.js +278 -0
- package/src/client/routes.json +1 -0
- package/src/client/yon.ts +341 -0
- package/src/router.ts +185 -0
- package/src/serve.ts +144 -0
- package/src/server/logger.ts +31 -0
- package/src/server/tach.ts +234 -0
- package/tests/index.test.ts +110 -0
- package/tests/stream.ts +24 -0
- package/tests/worker.ts +7 -0
- package/tsconfig.json +1 -1
- package/Dockerfile +0 -47
- package/bun.lockb +0 -0
- package/routes/byos/[primary]/doc/index.ts +0 -28
- package/routes/byos/[primary]/docs/index.ts +0 -28
- package/routes/byos/[primary]/join/[secondary]/docs/index.ts +0 -10
- package/routes/byos/[primary]/schema/index.ts +0 -17
- package/routes/byos/[primary]/stream/doc/index.ts +0 -28
- package/routes/byos/[primary]/stream/docs/index.ts +0 -28
- package/routes/utils/validation.ts +0 -36
- package/src/Tach.ts +0 -602
- package/src/Yon.ts +0 -25
- package/src/runtime.ts +0 -822
- package/types/index.d.ts +0 -13
package/routes/api/GET
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env dotnet-script
|
|
2
|
+
#r "nuget: System.Text.Json, 9.0.0"
|
|
3
|
+
using System;
|
|
4
|
+
using System.Text.Json;
|
|
5
|
+
using System.Text.Json.Nodes;
|
|
6
|
+
using System.IO;
|
|
7
|
+
|
|
8
|
+
Console.WriteLine("Executing .NET Script....");
|
|
9
|
+
|
|
10
|
+
// Parse the JSON context
|
|
11
|
+
var ctx = JsonNode.Parse(Console.ReadLine())?.AsObject();
|
|
12
|
+
|
|
13
|
+
// Create a new empty JsonObject
|
|
14
|
+
var result = new JsonObject();
|
|
15
|
+
|
|
16
|
+
// Copy each property from ctx to result
|
|
17
|
+
foreach (var prop in ctx)
|
|
18
|
+
{
|
|
19
|
+
result[prop.Key] = JsonNode.Parse(prop.Value.ToJsonString());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Add the message
|
|
23
|
+
result["message"] = "Hello from .NET Script!";
|
|
24
|
+
|
|
25
|
+
// Write to file
|
|
26
|
+
File.WriteAllText("/tmp/" + Environment.ProcessId, result.ToJsonString(new JsonSerializerOptions
|
|
27
|
+
{
|
|
28
|
+
WriteIndented = false
|
|
29
|
+
}));
|
package/routes/api/POST
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import json
|
|
3
|
+
import sys
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
print("Executing Python....")
|
|
7
|
+
|
|
8
|
+
ctx = json.loads(sys.stdin.read())
|
|
9
|
+
|
|
10
|
+
ctx["message"] = "Hello from Python!"
|
|
11
|
+
|
|
12
|
+
file = open(f"/tmp/{os.getpid()}", "w")
|
|
13
|
+
|
|
14
|
+
file.write(json.dumps(ctx))
|
|
15
|
+
|
|
16
|
+
file.close()
|
package/routes/api/PUT
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env php
|
|
2
|
+
<?php
|
|
3
|
+
|
|
4
|
+
while(FALSE !== ($line = fgets(STDIN))) {
|
|
5
|
+
|
|
6
|
+
$ctx = json_decode($line, true);
|
|
7
|
+
|
|
8
|
+
echo "Executing PHP...\n";
|
|
9
|
+
|
|
10
|
+
$ctx["message"] = "Hello from PHP!";
|
|
11
|
+
|
|
12
|
+
$pid = getmypid();
|
|
13
|
+
|
|
14
|
+
$file = fopen("/tmp/$pid", "w");
|
|
15
|
+
|
|
16
|
+
fwrite($file, json_encode($ctx));
|
|
17
|
+
|
|
18
|
+
fclose($file);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
?>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title></title>
|
|
8
|
+
<script src="/main.js"></script>
|
|
9
|
+
<script src="/render.js"></script>
|
|
10
|
+
<script src="/hmr.js"></script>
|
|
11
|
+
<link rel="stylesheet" href="/styles.css">
|
|
12
|
+
</head>
|
|
13
|
+
<body></body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import Router from "../router.js";
|
|
3
|
+
import Yon from "./yon.js";
|
|
4
|
+
|
|
5
|
+
const start = Date.now()
|
|
6
|
+
|
|
7
|
+
await Yon.createStaticRoutes()
|
|
8
|
+
|
|
9
|
+
for(const route in Router.reqRoutes) {
|
|
10
|
+
|
|
11
|
+
if(route.includes('hmr')) continue
|
|
12
|
+
|
|
13
|
+
const res = await Router.reqRoutes[route][`GET`]()
|
|
14
|
+
|
|
15
|
+
await Bun.write(Bun.file(`${process.cwd()}/dist/${route}`), await res.text())
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
await Bun.write(Bun.file(`${process.cwd()}/dist/index.html`), await Bun.file(`${import.meta.dir}/prod.html`).text())
|
|
19
|
+
|
|
20
|
+
console.log(`Built in ${Date.now() - start}ms`)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const url = new URL(window.location.href);
|
|
2
|
+
|
|
3
|
+
const ws = new WebSocket(`ws://localhost:9876${url.pathname}`);
|
|
4
|
+
|
|
5
|
+
ws.onopen = () => {
|
|
6
|
+
console.log('HMR Enabled');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
ws.onmessage = (event) => {
|
|
10
|
+
console.log('HMR Update');
|
|
11
|
+
window.location.reload()
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title></title>
|
|
8
|
+
<script src="/main.js"></script>
|
|
9
|
+
<script src="/render.js"></script>
|
|
10
|
+
<link rel="stylesheet" href="/styles.css">
|
|
11
|
+
</head>
|
|
12
|
+
<body></body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/** @type {Function} */
|
|
2
|
+
let render
|
|
3
|
+
/** @type {Timer} */
|
|
4
|
+
let interval
|
|
5
|
+
const elementsVar = 'elements';
|
|
6
|
+
/** @type {Map<string, Record<string, number>>} */
|
|
7
|
+
const routes = new Map()
|
|
8
|
+
let params = []
|
|
9
|
+
/** @type {Record<string, any>} */
|
|
10
|
+
const slugs = {}
|
|
11
|
+
let currentTemplate = ''
|
|
12
|
+
let previousRender = ''
|
|
13
|
+
let currentScript = ''
|
|
14
|
+
|
|
15
|
+
let counter = 0
|
|
16
|
+
let elementId;
|
|
17
|
+
let selectionStart;
|
|
18
|
+
|
|
19
|
+
if(routes.size === 0) {
|
|
20
|
+
|
|
21
|
+
fetch('/routes.json')
|
|
22
|
+
.then(res => res.json())
|
|
23
|
+
.then(data => {
|
|
24
|
+
for (const [path, slugs] of Object.entries(data)) {
|
|
25
|
+
routes.set(path, slugs)
|
|
26
|
+
}
|
|
27
|
+
setPageTemplate(window.location.pathname)
|
|
28
|
+
})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function loopRender() {
|
|
32
|
+
|
|
33
|
+
const loop = () => {
|
|
34
|
+
if(render) mergeBodyHTML(render())
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return setInterval(() => {
|
|
38
|
+
queueMicrotask(loop)
|
|
39
|
+
}, 200)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {string} html
|
|
44
|
+
* @param {HTMLScriptElement} script
|
|
45
|
+
* @param {HTMLStyleElement | undefined} style
|
|
46
|
+
*/
|
|
47
|
+
function mergeBodyHTML(html, style) {
|
|
48
|
+
|
|
49
|
+
if(html === previousRender) return
|
|
50
|
+
|
|
51
|
+
const styles = document.head.getElementsByTagName('style')
|
|
52
|
+
if(styles.length > 0) styles[0].remove()
|
|
53
|
+
if(style) document.head.appendChild(style)
|
|
54
|
+
|
|
55
|
+
removeEvents()
|
|
56
|
+
|
|
57
|
+
previousRender = html
|
|
58
|
+
document.body.innerHTML = html
|
|
59
|
+
|
|
60
|
+
counter = 0
|
|
61
|
+
|
|
62
|
+
addEvents()
|
|
63
|
+
|
|
64
|
+
if(elementId) {
|
|
65
|
+
|
|
66
|
+
const element = document.getElementById(elementId)
|
|
67
|
+
|
|
68
|
+
if(element) {
|
|
69
|
+
|
|
70
|
+
element.focus()
|
|
71
|
+
|
|
72
|
+
if(selectionStart && 'setSelectionRange' in element) {
|
|
73
|
+
element.setSelectionRange(selectionStart, selectionStart)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
elementId = null;
|
|
78
|
+
selectionStart = null
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {HTMLCollection | undefined} elements
|
|
84
|
+
*/
|
|
85
|
+
function removeEvents(elements) {
|
|
86
|
+
|
|
87
|
+
if(!elements) elements = document.body.children
|
|
88
|
+
|
|
89
|
+
for (const element of elements) {
|
|
90
|
+
|
|
91
|
+
for (const attribute of element.attributes) {
|
|
92
|
+
|
|
93
|
+
if (attribute.name.startsWith('@')) {
|
|
94
|
+
|
|
95
|
+
const event = attribute.name.substring(1)
|
|
96
|
+
|
|
97
|
+
element.removeEventListener(event, (e) => {})
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
removeEvents(element.children)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {HTMLCollection | undefined} elements
|
|
107
|
+
*/
|
|
108
|
+
function addEvents(elements) {
|
|
109
|
+
|
|
110
|
+
if(!elements) elements = document.body.children
|
|
111
|
+
|
|
112
|
+
for (const element of elements) {
|
|
113
|
+
|
|
114
|
+
for (const attribute of element.attributes) {
|
|
115
|
+
|
|
116
|
+
if (attribute.name.startsWith('@')) {
|
|
117
|
+
|
|
118
|
+
element.id ||= ++counter
|
|
119
|
+
|
|
120
|
+
const event = attribute.name.substring(1)
|
|
121
|
+
|
|
122
|
+
element.addEventListener(event, (e) => {
|
|
123
|
+
|
|
124
|
+
if(interval) clearInterval(interval)
|
|
125
|
+
|
|
126
|
+
const [ compId, classId ] = element.classList.values().filter(val => val.startsWith('ty-')).toArray()
|
|
127
|
+
|
|
128
|
+
elementId = element.id
|
|
129
|
+
selectionStart = e.target.selectionStart
|
|
130
|
+
|
|
131
|
+
const func = attribute.value.includes('=>') ? `(${attribute.value})("${e.target.value}", "${e.target.defaultValue}")` : attribute.value
|
|
132
|
+
|
|
133
|
+
const execute = { compId, classId, func }
|
|
134
|
+
|
|
135
|
+
mergeBodyHTML(render(execute))
|
|
136
|
+
|
|
137
|
+
interval = loopRender()
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
addEvents(element.children)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
document.addEventListener('click', (e) => {
|
|
147
|
+
if(e.target.href) {
|
|
148
|
+
const url = new URL(e.target.href)
|
|
149
|
+
if(url.origin !== location.origin) return
|
|
150
|
+
e.preventDefault()
|
|
151
|
+
setPageTemplate(url.pathname)
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
window.onpopstate = function (e) {
|
|
156
|
+
setPageTemplate(window.location.pathname)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {string} pathname
|
|
161
|
+
*/
|
|
162
|
+
function setPageTemplate(pathname) {
|
|
163
|
+
|
|
164
|
+
if(interval) clearInterval(interval)
|
|
165
|
+
|
|
166
|
+
let url;
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
|
|
170
|
+
let handler = getHandler(pathname)
|
|
171
|
+
|
|
172
|
+
if(handler === '/') handler = ''
|
|
173
|
+
|
|
174
|
+
url = `${handler}/HTML.js`
|
|
175
|
+
|
|
176
|
+
} catch(err) {
|
|
177
|
+
url = `/404.js`
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
import(`/pages${url}`).then(async module => {
|
|
181
|
+
window.history.replaceState({}, '', pathname)
|
|
182
|
+
render = await module.default()
|
|
183
|
+
interval = loopRender()
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param {string} pathname
|
|
189
|
+
*/
|
|
190
|
+
function getHandler(pathname) {
|
|
191
|
+
|
|
192
|
+
let handler;
|
|
193
|
+
|
|
194
|
+
if (pathname === '/') return pathname
|
|
195
|
+
|
|
196
|
+
const paths = pathname.split('/').slice(1);
|
|
197
|
+
|
|
198
|
+
let bestMatchKey = '';
|
|
199
|
+
let bestMatchLength = -1;
|
|
200
|
+
|
|
201
|
+
for (const [routeKey] of routes) {
|
|
202
|
+
|
|
203
|
+
const routeSegs = routeKey.split('/')
|
|
204
|
+
|
|
205
|
+
const isMatch = pathsMatch(routeSegs, paths.slice(0, routeSegs.length));
|
|
206
|
+
|
|
207
|
+
if (isMatch && routeSegs.length > bestMatchLength) {
|
|
208
|
+
bestMatchKey = routeKey;
|
|
209
|
+
bestMatchLength = routeSegs.length;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (bestMatchKey) {
|
|
214
|
+
|
|
215
|
+
handler = bestMatchKey
|
|
216
|
+
|
|
217
|
+
params = parseParams(paths.slice(bestMatchLength))
|
|
218
|
+
|
|
219
|
+
const slugMap = routes.get(bestMatchKey) ?? {}
|
|
220
|
+
|
|
221
|
+
Object.entries(slugMap).forEach(([key, idx]) => {
|
|
222
|
+
key = key.replace(':', '')
|
|
223
|
+
slugs[key] = paths[idx]
|
|
224
|
+
})
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!handler) throw new Error(`Route ${pathname} not found`, { cause: 404 });
|
|
228
|
+
|
|
229
|
+
return handler
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @param {string[]} routeSegs
|
|
234
|
+
* @param {string[]} pathSegs
|
|
235
|
+
*/
|
|
236
|
+
function pathsMatch(routeSegs, pathSegs) {
|
|
237
|
+
|
|
238
|
+
if (routeSegs.length !== pathSegs.length) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const slugs = routes.get(routeSegs.join('/')) || new Map()
|
|
243
|
+
|
|
244
|
+
for (let i = 0; i < routeSegs.length; i++) {
|
|
245
|
+
if (!slugs.has(routeSegs[i]) && routeSegs[i] !== pathSegs[i]) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @param {string[]} input
|
|
255
|
+
*/
|
|
256
|
+
function parseParams(input) {
|
|
257
|
+
|
|
258
|
+
const params = []
|
|
259
|
+
|
|
260
|
+
for(const param of input) {
|
|
261
|
+
|
|
262
|
+
const num = Number(param)
|
|
263
|
+
|
|
264
|
+
if(!Number.isNaN(num)) params.push(num)
|
|
265
|
+
|
|
266
|
+
else if(param === 'true') params.push(true)
|
|
267
|
+
|
|
268
|
+
else if(param === 'false') params.push(false)
|
|
269
|
+
|
|
270
|
+
else if(param === 'null') params.push(null)
|
|
271
|
+
|
|
272
|
+
else if(param === 'undefined') params.push(undefined)
|
|
273
|
+
|
|
274
|
+
else params.push(param)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return params
|
|
278
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"/api/:version":{":version":1},"/":{}}
|