@vyckr/tachyon 0.3.0 → 1.1.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.
Files changed (50) hide show
  1. package/.env.example +3 -17
  2. package/README.md +87 -57
  3. package/bun.lock +127 -0
  4. package/components/counter.html +13 -0
  5. package/deno.lock +19 -0
  6. package/go.mod +3 -0
  7. package/lib/gson-2.3.jar +0 -0
  8. package/main.js +0 -0
  9. package/package.json +19 -20
  10. package/routes/DELETE +18 -0
  11. package/routes/GET +17 -0
  12. package/routes/HTML +28 -0
  13. package/routes/POST +32 -0
  14. package/routes/SOCKET +26 -0
  15. package/routes/api/:version/DELETE +10 -0
  16. package/routes/api/:version/GET +29 -0
  17. package/routes/api/:version/PATCH +24 -0
  18. package/routes/api/GET +29 -0
  19. package/routes/api/POST +16 -0
  20. package/routes/api/PUT +21 -0
  21. package/src/client/404.html +7 -0
  22. package/src/client/dev.html +14 -0
  23. package/src/client/dist.ts +20 -0
  24. package/src/client/hmr.js +12 -0
  25. package/src/client/prod.html +13 -0
  26. package/src/client/render.js +278 -0
  27. package/src/client/routes.json +1 -0
  28. package/src/client/yon.ts +353 -0
  29. package/src/router.ts +186 -0
  30. package/src/serve.ts +144 -0
  31. package/src/server/logger.ts +31 -0
  32. package/src/server/tach.ts +234 -0
  33. package/tests/index.test.ts +110 -0
  34. package/tests/stream.ts +24 -0
  35. package/tests/worker.ts +7 -0
  36. package/tsconfig.json +1 -1
  37. package/Dockerfile +0 -47
  38. package/bun.lockb +0 -0
  39. package/routes/byos/[primary]/doc/index.ts +0 -28
  40. package/routes/byos/[primary]/docs/index.ts +0 -28
  41. package/routes/byos/[primary]/join/[secondary]/docs/index.ts +0 -10
  42. package/routes/byos/[primary]/schema/index.ts +0 -17
  43. package/routes/byos/[primary]/stream/doc/index.ts +0 -28
  44. package/routes/byos/[primary]/stream/docs/index.ts +0 -28
  45. package/routes/proxy.ts +0 -8
  46. package/routes/utils/validation.ts +0 -36
  47. package/src/Tach.ts +0 -543
  48. package/src/Yon.ts +0 -25
  49. package/src/runtime.ts +0 -822
  50. 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
+ }));
@@ -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,7 @@
1
+ <script>
2
+ document.title = 404
3
+ </script>
4
+
5
+ <h1>404</h1>
6
+ <p>Page not found</p>
7
+ <a href="/">Go back to home</a>
@@ -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},"/":{}}