revojs 0.0.88 → 0.1.1

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/dist/index.js CHANGED
@@ -1,284 +1,98 @@
1
- //#region src/app/index.ts
2
- function mergeObjects(base, input) {
3
- if (input === null || input === void 0) return mergeObjects(base, {});
4
- const object = structuredClone(input);
5
- for (const key in base) {
6
- if (key === "__proto__" || key === "constructor") continue;
7
- const value = base[key];
8
- if (value === null || value === void 0) continue;
9
- if (Array.isArray(value) && Array.isArray(object[key])) object[key] = [...value, ...object[key]];
10
- else if (typeof value === "object" && typeof object[key] === "object") object[key] = mergeObjects(value, object[key]);
11
- else object[key] = value;
12
- }
13
- return object;
14
- }
15
- function createApp(config) {
16
- return {
17
- config: mergeObjects(config, {
18
- modules: [],
19
- client: {
20
- entry: "index.html",
21
- externals: []
22
- },
23
- server: {
24
- entry: "@revojs/bun/runtime",
25
- externals: []
26
- },
27
- dev: { middleware: [] }
28
- }),
29
- virtuals: {}
30
- };
31
- }
32
- const SERVER = "ssr";
33
- const CLIENT = "client";
34
-
35
- //#endregion
36
- //#region src/jsx/index.ts
37
- const svgElements = new Set([
38
- "altGlyph",
39
- "altGlyphDef",
40
- "altGlyphItem",
41
- "animate",
42
- "animateColor",
43
- "animateMotion",
44
- "animateTransform",
45
- "circle",
46
- "clipPath",
47
- "color-profile",
48
- "cursor",
49
- "defs",
50
- "desc",
51
- "ellipse",
52
- "feBlend",
53
- "feColorMatrix",
54
- "feComponentTransfer",
55
- "feComposite",
56
- "feConvolveMatrix",
57
- "feDiffuseLighting",
58
- "feDisplacementMap",
59
- "feDistantLight",
60
- "feDropShadow",
61
- "feFlood",
62
- "feFuncA",
63
- "feFuncB",
64
- "feFuncG",
65
- "feFuncR",
66
- "feGaussianBlur",
67
- "feImage",
68
- "feMerge",
69
- "feMergeNode",
70
- "feMorphology",
71
- "feOffset",
72
- "fePointLight",
73
- "feSpecularLighting",
74
- "feSpotLight",
75
- "feTile",
76
- "feTurbulence",
77
- "filter",
78
- "font",
79
- "font-face",
80
- "font-face-format",
81
- "font-face-name",
82
- "font-face-src",
83
- "font-face-uri",
84
- "foreignObject",
85
- "g",
86
- "glyph",
87
- "glyphRef",
88
- "hkern",
89
- "image",
90
- "line",
91
- "linearGradient",
92
- "marker",
93
- "mask",
94
- "metadata",
95
- "missing-glyph",
96
- "mpath",
97
- "path",
98
- "pattern",
99
- "polygon",
100
- "polyline",
101
- "radialGradient",
102
- "rect",
103
- "set",
104
- "stop",
105
- "svg",
106
- "switch",
107
- "symbol",
108
- "text",
109
- "textPath",
110
- "tref",
111
- "tspan",
112
- "use",
113
- "view",
114
- "vkern"
115
- ]);
116
- function namespace(tag) {
117
- return svgElements.has(tag) ? "http://www.w3.org/2000/svg" : "http://www.w3.org/1999/xhtml";
118
- }
119
-
120
- //#endregion
121
- //#region src/schema/index.ts
122
- function isFailure(result) {
123
- return "issues" in result;
124
- }
125
- function parseSchema(scope, schema, value) {
126
- const result = schema["~standard"].validate(value);
127
- if (isFailure(result)) throw sendBadRequest(scope, result.issues.map((issue) => issue.message).join(", "));
128
- return result.value;
129
- }
130
-
131
- //#endregion
132
- //#region src/signals/index.ts
133
- var StopEvent = class extends Event {
1
+ //#region src/server/index.ts
2
+ var Radix = class {
3
+ rootNode;
134
4
  constructor() {
135
- super("stop");
136
- }
137
- };
138
- var Scope = class extends EventTarget {
139
- parentScope;
140
- context;
141
- constructor(parentScope) {
142
- super();
143
- this.parentScope = parentScope;
144
- this.parentScope?.onStop(() => this.stop());
145
- this.context = /* @__PURE__ */ new Map();
5
+ this.rootNode = {
6
+ type: "PATH",
7
+ children: {}
8
+ };
146
9
  }
147
- getContext(input) {
148
- let scope = this;
149
- while (scope) {
150
- if (scope.context.has(input)) return scope.context.get(input);
151
- scope = scope.parentScope;
10
+ use(path, value) {
11
+ let node = this.rootNode;
12
+ for (const segment of path.split("/")) {
13
+ if (segment.startsWith(WILDCARD)) {
14
+ let childNode$1 = node.children[WILDCARD];
15
+ childNode$1 ??= {
16
+ type: "WILDCARD",
17
+ parameter: segment.substring(WILDCARD.length),
18
+ children: {}
19
+ };
20
+ node.children[WILDCARD] ??= childNode$1;
21
+ node = childNode$1;
22
+ continue;
23
+ }
24
+ if (segment.startsWith(PARAMETER)) {
25
+ let childNode$1 = node.children[PARAMETER];
26
+ childNode$1 ??= {
27
+ type: "PARAMETER",
28
+ parameter: segment.substring(PARAMETER.length),
29
+ children: {}
30
+ };
31
+ node.children[PARAMETER] ??= childNode$1;
32
+ node = childNode$1;
33
+ continue;
34
+ }
35
+ let childNode = node.children[segment];
36
+ childNode ??= {
37
+ type: "PATH",
38
+ children: {}
39
+ };
40
+ node.children[segment] ??= childNode;
41
+ node = childNode;
152
42
  }
153
- return {};
154
- }
155
- setContext(input, value) {
156
- this.context.set(input, value);
157
- }
158
- onStop(input) {
159
- this.addEventListener("stop", input, { once: true });
160
- }
161
- stop() {
162
- return this.dispatchEvent(new StopEvent());
163
- }
164
- };
165
- var Compute = class extends Scope {
166
- invoke;
167
- constructor(parentScope, invoke) {
168
- super(parentScope);
169
- this.invoke = invoke;
170
- }
171
- run() {
172
- this.stop();
173
- return this.invoke(this);
43
+ node.value = value;
44
+ return node;
174
45
  }
175
46
  };
176
- var Handler = class Handler {
177
- get(target, key) {
178
- const compute = activeCompute;
179
- if (compute) {
180
- const computes = targets.get(target) ?? /* @__PURE__ */ new Map();
181
- const set = computes.get(key) ?? /* @__PURE__ */ new Set();
182
- computes.set(key, set.add(compute));
183
- targets.set(target, computes);
184
- compute.parentScope?.onStop(() => {
185
- set.delete(compute);
186
- if (set.size === 0) {
187
- computes.delete(key);
188
- if (computes.size === 0) targets.delete(target);
47
+ var Router = class extends Radix {
48
+ fetch(scope, next) {
49
+ const { request } = useServer(scope);
50
+ const { pathname } = useUrl(scope);
51
+ const context = {
52
+ route: this.rootNode,
53
+ segments: (request.method.toUpperCase() + pathname).split("/"),
54
+ parameters: {}
55
+ };
56
+ const invoke = (node, index) => {
57
+ if (index === context.segments.length) return node.value;
58
+ const segment = context.segments[index];
59
+ if (node.children[segment]) {
60
+ const route$1 = invoke(node.children[segment], index + 1);
61
+ if (route$1) return route$1;
62
+ }
63
+ if (node.children[PARAMETER]) {
64
+ const parameterNode = node.children[PARAMETER];
65
+ const route$1 = invoke(parameterNode, index + 1);
66
+ if (route$1) {
67
+ context.parameters[parameterNode.parameter] = segment;
68
+ return route$1;
189
69
  }
190
- });
191
- }
192
- const value = Reflect.get(target, key);
193
- if (value) {
194
- if (typeof value === "function" && !value.prototype) return value.bind(target);
195
- if (typeof value === "object") {
196
- const tag = Object.prototype.toString.call(value);
197
- if (tag === "[object Object]" || tag === "[object Array]" || tag === "[object Map]" || tag === "[object Set]" || tag === "[object WeakMap]" || tag === "[object WeakSet]") return new Proxy(value, new Handler());
198
70
  }
71
+ if (node.children[WILDCARD]) {
72
+ const wildcardNode = node.children[WILDCARD];
73
+ context.parameters[wildcardNode.parameter] = segment;
74
+ return wildcardNode.value ?? invoke(wildcardNode, segment.length);
75
+ }
76
+ };
77
+ const route = invoke(this.rootNode, 0);
78
+ if (route) {
79
+ scope.setContext(ROUTER_CONTEXT, context);
80
+ return route.fetch(scope);
199
81
  }
200
- return value;
201
- }
202
- set(target, key, value) {
203
- const result = Reflect.set(target, key, value);
204
- for (const compute of targets.get(target)?.get(key) ?? []) compute.run();
205
- return result;
82
+ return next?.();
206
83
  }
207
84
  };
208
- function createState(value) {
209
- return new Proxy({ value }, new Handler());
210
- }
211
- function createCompute(scope, invoke) {
212
- let previous = activeCompute;
213
- activeCompute = new Compute(scope, invoke);
214
- const result = invoke(activeCompute);
215
- if (result instanceof Promise) return result.finally(() => activeCompute = previous);
216
- activeCompute = previous;
217
- return result;
218
- }
219
- function createMemo(scope, invoke) {
220
- let state;
221
- const compute = createCompute(scope, (scope$1) => {
222
- const value = invoke(scope$1);
223
- if (typeof state === "object") state.value = value;
224
- return value;
225
- });
226
- state = createState(compute);
227
- return state;
228
- }
229
- function fromValue(value) {
230
- if (value instanceof Function) return fromValue(value());
231
- return value;
232
- }
233
- function untrack(invoke) {
234
- let previous = activeCompute;
235
- activeCompute = void 0;
236
- const result = invoke();
237
- if (result instanceof Promise) return result.finally(() => activeCompute = previous);
238
- activeCompute = previous;
239
- return result;
240
- }
241
- function defineContext(key) {
242
- return key;
243
- }
244
- let activeCompute;
245
- const targets = /* @__PURE__ */ new WeakMap();
246
-
247
- //#endregion
248
- //#region src/http/index.ts
249
- function sendText(scope, text) {
250
- const { response } = useRuntime(scope);
251
- response.headers.set("Content-Type", "text/plain");
252
- return new Response(text, response);
253
- }
254
- function sendHtml(scope, text) {
255
- const { response } = useRuntime(scope);
256
- response.headers.set("Content-Type", "text/html");
257
- return new Response(text, response);
258
- }
259
- function sendJson(scope, value) {
260
- const { response } = useRuntime(scope);
261
- response.headers.set("Content-Type", "application/json");
262
- return new Response(JSON.stringify(value), response);
263
- }
264
- function sendRedirect(scope, path) {
265
- const { response } = useRuntime(scope);
266
- response.status = 302;
267
- response.headers.set("Location", path);
268
- return new Response(null, response);
85
+ function defineRoute(route) {
86
+ return route;
269
87
  }
270
- function sendBadRequest(scope, text) {
271
- const { response } = useRuntime(scope);
272
- response.status = 400;
273
- return new Response(text, response);
88
+ function defineMiddleware(middleware) {
89
+ return middleware;
274
90
  }
275
- function sendUnauthorized(scope) {
276
- const { response } = useRuntime(scope);
277
- response.status = 401;
278
- return new Response(null, response);
91
+ function useServer(scope) {
92
+ return scope.getContext(SERVER_CONTEXT);
279
93
  }
280
94
  function useUrl(scope, base) {
281
- const { request } = useRuntime(scope);
95
+ const { request } = useServer(scope);
282
96
  return new URL(request?.url ?? window?.location.href, base);
283
97
  }
284
98
  function useQuery(scope, schema) {
@@ -286,23 +100,8 @@ function useQuery(scope, schema) {
286
100
  const entries = Object.fromEntries(searchParams);
287
101
  return schema ? parseSchema(scope, schema, entries) : entries;
288
102
  }
289
- function useCookie(scope, name, schema, options) {
290
- const cookies = useCookies(scope);
291
- const state = createState(parseSchema(scope, schema, cookies[name]));
292
- createCompute(scope, () => {
293
- switch (typeof state.value) {
294
- case "string":
295
- case "number":
296
- case "bigint":
297
- case "boolean":
298
- case "symbol": return setCookie(scope, name, state.value.toString(), options);
299
- case "object": return setCookie(scope, name, JSON.stringify(state.value), options);
300
- }
301
- });
302
- return state;
303
- }
304
103
  function useCookies(scope, schema) {
305
- const { request } = useRuntime(scope);
104
+ const { request } = useServer(scope);
306
105
  const entries = (isClient() ? document.cookie : request.headers.get("Cookie") ?? "").split("; ").reduce((result, cookie) => {
307
106
  const [name, value] = cookie.split("=");
308
107
  if (name && value) result[name] = decodeURIComponent(value);
@@ -311,7 +110,7 @@ function useCookies(scope, schema) {
311
110
  return schema ? parseSchema(scope, schema, entries) : entries;
312
111
  }
313
112
  function useSetCookies(scope, schema) {
314
- const { request } = useRuntime(scope);
113
+ const { request } = useServer(scope);
315
114
  const entries = request.headers.getSetCookie().reduce((result, cookie) => {
316
115
  const [name, value] = cookie.split("=");
317
116
  if (name && value) result[name] = decodeURIComponent(value);
@@ -320,7 +119,7 @@ function useSetCookies(scope, schema) {
320
119
  return schema ? parseSchema(scope, schema, entries) : entries;
321
120
  }
322
121
  function setCookie(scope, name, value, options) {
323
- const { response } = useRuntime(scope);
122
+ const { response } = useServer(scope);
324
123
  let cookie = name + "=" + encodeURIComponent(value);
325
124
  if (options?.domain) cookie += `; Domain=${options.domain}`;
326
125
  if (options?.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
@@ -333,10 +132,77 @@ function setCookie(scope, name, value, options) {
333
132
  if (isClient()) document.cookie = cookie;
334
133
  else response.headers.append("Set-Cookie", cookie);
335
134
  }
135
+ function sendText(scope, text, config) {
136
+ const { response } = useServer(scope);
137
+ response.headers.set("Content-Type", "text/plain");
138
+ return new Response(text, mergeObjects(response, config));
139
+ }
140
+ function sendHtml(scope, text, config) {
141
+ const { response } = useServer(scope);
142
+ response.headers.set("Content-Type", "text/html");
143
+ return new Response(text, mergeObjects(response, config));
144
+ }
145
+ function sendJson(scope, value, config) {
146
+ const { response } = useServer(scope);
147
+ response.headers.set("Content-Type", "application/json");
148
+ return new Response(JSON.stringify(value), mergeObjects(response, config));
149
+ }
150
+ function sendRedirect(scope, path, config) {
151
+ const { response } = useServer(scope);
152
+ response.status = 302;
153
+ response.headers.set("Location", path);
154
+ return new Response(null, mergeObjects(response, config));
155
+ }
156
+ function sendBadRequest(scope, text, config) {
157
+ const { response } = useServer(scope);
158
+ response.status = 400;
159
+ return new Response(text, mergeObjects(response, config));
160
+ }
161
+ function sendUnauthorized(scope, config) {
162
+ const { response } = useServer(scope);
163
+ response.status = 401;
164
+ return new Response(null, mergeObjects(response, config));
165
+ }
336
166
  function mimeType(file) {
337
167
  const extension = /\.([a-zA-Z0-9]+?)$/.exec(file)?.at(1);
338
168
  return mimeTypes[extension ?? ""] ?? "text/plain";
339
169
  }
170
+ function toRoutePath(path) {
171
+ const result = ("/" + path).replaceAll(/\/index/g, "").replaceAll(/\[\.\.\.(.*?)\]/g, (_, value) => WILDCARD + value).replaceAll(/\[(.*?)\]/g, (_, value) => PARAMETER + value);
172
+ return (result.startsWith("/") ? result : "/" + result).split(".");
173
+ }
174
+ async function createServer() {
175
+ const router = new Router();
176
+ const pipeline = new Array();
177
+ const middlewares = await import("#virtual/middlewares").then((module) => Object.entries(module.default));
178
+ for (const [_, middleware] of middlewares) pipeline.push(middleware);
179
+ const assets = await import("#virtual/assets").then((module) => Object.entries(module.default));
180
+ for (const [path, asset] of assets) router.use(`GET/${path}`, defineRoute({ async fetch(scope) {
181
+ const { response } = useServer(scope);
182
+ response.headers.set("Content-Type", mimeType(path));
183
+ return new Response(asset, response);
184
+ } }));
185
+ const routes = await import("#virtual/routes").then((module) => Object.entries(module.default));
186
+ for (const [path, route] of routes) {
187
+ const [name, method] = toRoutePath(path);
188
+ router.use(method?.toUpperCase() + name, route);
189
+ }
190
+ pipeline.push(router);
191
+ const invoke = (scope, index) => {
192
+ return pipeline.at(index)?.fetch(scope, () => invoke(scope, index + 1));
193
+ };
194
+ return {
195
+ router,
196
+ pipeline,
197
+ async fetch(scope) {
198
+ return await invoke(scope, 0) ?? sendText(scope, "NOT_FOUND", { status: 404 });
199
+ }
200
+ };
201
+ }
202
+ const ROUTER_CONTEXT = defineContext("ROUTER_CONTEXT");
203
+ const SERVER_CONTEXT = defineContext("SERVER_CONTEXT");
204
+ const WILDCARD = "$";
205
+ const PARAMETER = ":";
340
206
  const mimeTypes = {
341
207
  txt: "text/plain",
342
208
  css: "text/css",
@@ -384,520 +250,41 @@ const mimeTypes = {
384
250
  };
385
251
 
386
252
  //#endregion
387
- //#region src/radix/index.ts
388
- var Radix = class Radix {
389
- value;
390
- input;
391
- children;
392
- constructor(input) {
393
- this.input = input;
394
- this.children = {};
395
- }
396
- insert(path, value) {
397
- let node = this;
398
- for (let segment of path.split("/")) {
399
- let input;
400
- if (segment.startsWith(":")) {
401
- input = segment.substring(1);
402
- segment = ":";
403
- }
404
- let childNode = node.children[segment];
405
- if (childNode === void 0) {
406
- childNode = new Radix(input);
407
- node.children[segment] = childNode;
408
- }
409
- node = childNode;
410
- }
411
- node.value = value;
412
- return this;
413
- }
414
- match(path) {
415
- let node = this;
416
- const match = { inputs: {} };
417
- for (const segment of path.split("/")) {
418
- node = node?.children[segment] ?? node?.children[":"];
419
- if (node?.input) match.inputs[node.input] = segment;
420
- }
421
- match.value = node?.value;
422
- return match;
423
- }
424
- };
425
-
426
- //#endregion
427
- //#region src/runtime/index.ts
428
- function isRoute(value) {
429
- return !!value && typeof value === "object" && "fetch" in value;
430
- }
431
- function useRuntime(scope) {
432
- return scope.getContext(RUNTIME_CONTEXT);
433
- }
434
- function useRoute(scope) {
435
- return scope.getContext(ROUTE_CONTEXT);
436
- }
437
- async function useRoutes() {
438
- return await import("#virtual/routes").then((module) => module.default);
439
- }
440
- async function useAssets() {
441
- return await import("#virtual/assets").then((module) => module.default);
442
- }
443
- async function useLocales() {
444
- return await import("#virtual/locales").then((module) => module.default);
445
- }
446
- function defineRoute(route) {
447
- return route;
448
- }
449
- function defineMiddleware(middleware) {
450
- return middleware;
451
- }
452
- function fileName(path) {
453
- return path.split("/").pop()?.split(".").slice(0, -1).join(".");
454
- }
455
- function toPath(value) {
456
- const path = (value.startsWith("/") ? value : "/" + value).replaceAll(/\/index/g, "").replaceAll(/\[(.*?)\]/g, (_, name) => ":" + name);
457
- const route = path.startsWith("/") ? path : "/" + path;
458
- const split = route.split(".");
459
- return split.length === 3 ? [split.at(0), split.at(1)] : [split.at(0)];
460
- }
461
- async function $fetch(scope, input, options) {
462
- const { request, variables } = useRuntime(scope);
463
- let response;
464
- if (request) {
465
- const next = new Scope();
466
- const url = new URL(input.toString(), request.url);
467
- next.setContext(RUNTIME_CONTEXT, {
468
- tasks: [],
469
- states: {},
470
- request: new Request(url, options),
471
- response: { headers: new Headers() },
472
- variables
473
- });
474
- const previous = new URL(request.url);
475
- if (url.origin === previous.origin) response = await (await import("#virtual/runtime")).runtime.fetch(next);
476
- }
477
- response ??= await fetch(input, options);
478
- if (response.ok === false) throw response;
479
- const contentType = response.headers.get("Content-Type")?.split(";").shift() ?? "";
480
- switch (contentType) {
481
- case "application/json": return response.json();
482
- default: return response;
483
- }
484
- }
485
- function useState(scope, name, value) {
486
- const state = createState(value);
487
- if (isClient()) {
488
- if (STATES === void 0) {
489
- const element = document.getElementById("STATES");
490
- STATES = element?.textContent ? JSON.parse(element.textContent) : {};
491
- }
492
- state.value = STATES[name];
493
- createCompute(scope, () => STATES && (STATES[name] = state.value));
494
- }
495
- if (isServer()) {
496
- const { states } = useRuntime(scope);
497
- states[name] = state;
498
- }
499
- return state;
500
- }
501
- function useAsync(scope, name, invoke, defaultOptions) {
502
- const { tasks } = useRuntime(scope);
503
- const state = useState(scope, name);
504
- const isLoading = createState(false);
505
- const execute = async (options) => {
506
- const onCatch = options?.catch ?? defaultOptions?.catch;
507
- const viewTransition = options?.viewTransition ?? defaultOptions?.viewTransition;
508
- isLoading.value = true;
509
- try {
510
- const result = await invoke();
511
- if (JSON.stringify(state.value ?? {}) !== JSON.stringify(result)) if (viewTransition) await startViewTransition(viewTransition, () => state.value = result);
512
- else state.value = result;
513
- } catch (error) {
514
- onCatch?.(error);
515
- } finally {
516
- isLoading.value = false;
517
- }
518
- return state.value;
519
- };
520
- const task = execute(defaultOptions);
521
- if (isServer()) tasks.push(task);
522
- return {
523
- state,
524
- isLoading,
525
- execute
526
- };
527
- }
528
- function useFetch(scope, input, options) {
529
- return useAsync(scope, input.toString(), () => $fetch(scope, input, options), options);
530
- }
531
- async function createRuntime() {
532
- const radix = new Radix();
533
- const middlewares = new Array();
534
- const routes = await useRoutes();
535
- for (const path in routes) {
536
- const [name, method] = toPath(path);
537
- radix.insert((method ?? "GET").toUpperCase() + name, defineRoute({ fetch: async (scope) => {
538
- const route = routes[path];
539
- if (isRoute(route)) return route.fetch(scope);
540
- const { states } = useRuntime(scope);
541
- const content = await renderToString(scope, await import("#virtual/client").then((module) => module.client), { head: { children: ["<!--STATES-->"] } });
542
- const entries = Object.entries(states).reduce((states$1, [name$1, state]) => {
543
- return {
544
- ...states$1,
545
- [name$1]: state.value
546
- };
547
- }, {});
548
- return sendHtml(scope, content.replace("<!--STATES-->", await renderToString(scope, {
549
- tag: "script",
550
- attributes: {
551
- id: "STATES",
552
- type: "application/json"
553
- },
554
- children: [JSON.stringify(entries)]
555
- })));
556
- } }));
557
- }
558
- const assets = await useAssets();
559
- for (const path in assets) radix.insert("GET/" + path, defineRoute({ fetch: async (scope) => {
560
- const { response } = useRuntime(scope);
561
- let content = assets[path];
562
- if (content) {
563
- if (path.endsWith(".png")) {
564
- const [_, base64] = content.split(",");
565
- const binary = atob(base64.replace(/-/g, "+").replace(/_/g, "/"));
566
- const bytes = new Uint8Array(binary.length);
567
- for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
568
- content = bytes;
569
- }
570
- }
571
- response.headers.set("Content-Type", mimeType(path));
572
- return new Response(content, response);
573
- } }));
574
- const invoke = (scope, route, index) => {
575
- return middlewares.at(index)?.fetch(scope, () => invoke(scope, route, index + 1)) ?? route.fetch(scope);
576
- };
577
- return {
578
- radix,
579
- middlewares,
580
- fetch: async (scope) => {
581
- const { request } = useRuntime(scope);
582
- const { pathname } = useUrl(scope);
583
- const { value: route, inputs } = radix.match(request.method + pathname);
584
- try {
585
- scope.setContext(ROUTE_CONTEXT, { inputs: createState(inputs) });
586
- if (route) {
587
- const response = await invoke(scope, route, 0);
588
- if (response) return response;
589
- }
590
- return sendText(scope, "NOT_FOUND");
591
- } catch (exception) {
592
- if (exception instanceof Response) return exception;
593
- throw exception;
594
- }
595
- }
596
- };
597
- }
598
- let STATES;
599
- const RUNTIME_CONTEXT = defineContext("RUNTIME_CONTEXT");
600
- const ROUTE_CONTEXT = defineContext("ROUTE_CONTEXT");
601
-
602
- //#endregion
603
- //#region src/html/index.ts
604
- var MountedEvent = class extends Event {
253
+ //#region src/shared/index.ts
254
+ var StopEvent = class extends Event {
605
255
  constructor() {
606
- super("mounted");
256
+ super("stop");
607
257
  }
608
258
  };
609
- function isTemplate(value) {
610
- return !!value && typeof value === "object" && "tag" in value && "attributes" in value && "children" in value;
611
- }
612
- function isCustomElement(value) {
613
- return !!value && typeof value === "object" && "component" in value;
614
- }
615
- function isComponent(value) {
616
- return !!value && typeof value === "function" && "$name" in value;
617
- }
618
- function useHost(scope) {
619
- return scope.getContext(HOST_CONTEXT);
620
- }
621
- function createElement(input, attributes, ...children) {
622
- if (isComponent(input)) return {
623
- tag: input.$name,
624
- attributes: attributes ?? {},
625
- children
626
- };
627
- if (typeof input === "string") return {
628
- tag: input,
629
- attributes: attributes ?? {},
630
- children
631
- };
632
- return input?.({
633
- ...attributes,
634
- children
635
- });
636
- }
637
- function toString(slot) {
638
- switch (typeof slot) {
639
- case "string":
640
- case "number":
641
- case "bigint":
642
- case "boolean":
643
- case "symbol": return slot.toString();
644
- case "object": return JSON.stringify(slot);
645
- case "function": return toString(slot());
646
- default: return "";
647
- }
648
- }
649
- function toArray(hydration) {
650
- if (Array.isArray(hydration)) return hydration.reduce((items, child) => items.concat(toArray(child)), []);
651
- return [hydration];
652
- }
653
- function toRange(hydration) {
654
- const items = toArray(hydration);
655
- const range = document.createRange();
656
- const firstNode = items.at(0);
657
- if (firstNode) range.setStartBefore(firstNode);
658
- const lastNode = items.at(-1);
659
- if (lastNode) range.setEndAfter(lastNode);
660
- return range;
661
- }
662
- function toFragment(hydration) {
663
- return toArray(hydration).reduce((fragment, node) => {
664
- fragment.appendChild(node);
665
- return fragment;
666
- }, document.createDocumentFragment());
667
- }
668
- function hydrate(scope, parentNode, slot, index, previous) {
669
- let hydration = parentNode.childNodes.item(index);
670
- if (Array.isArray(slot)) {
671
- const items = new Array();
672
- for (const [index$1, childSlot] of slot.entries()) items.push(hydrate(scope, parentNode, childSlot, index$1, previous));
673
- if (items.length) hydration = items;
674
- else if (previous || !(hydration instanceof Comment)) hydration = document.createComment("");
675
- }
676
- if (typeof slot === "function") {
677
- let previous$1;
678
- createCompute(scope, (scope$1) => {
679
- let input = slot;
680
- while (typeof input === "function") input = input();
681
- hydration = hydrate(scope$1, parentNode, input, index, previous$1);
682
- if (previous$1 && hydration !== previous$1) if (Array.isArray(hydration)) if (Array.isArray(previous$1)) {
683
- const range = toRange(previous$1);
684
- range.deleteContents();
685
- range.collapse(true);
686
- range.insertNode(toFragment(hydration));
687
- } else if (parentNode.contains(previous$1)) parentNode.replaceChild(toFragment(hydration), previous$1);
688
- else parentNode.replaceChild(toFragment(hydration), parentNode.childNodes.item(index));
689
- else if (Array.isArray(previous$1)) {
690
- const range = toRange(previous$1);
691
- range.deleteContents();
692
- range.collapse(true);
693
- range.insertNode(hydration);
694
- } else if (parentNode.contains(previous$1)) parentNode.replaceChild(hydration, previous$1);
695
- else parentNode.replaceChild(hydration, parentNode.childNodes.item(index));
696
- previous$1 = hydration;
697
- });
698
- }
699
- if ((slot === null || slot === void 0) && (previous || !(hydration instanceof Comment))) hydration = document.createComment("");
700
- if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") {
701
- const textContent = slot.toString();
702
- if (previous || !(hydration instanceof Text)) hydration = document.createTextNode(textContent);
703
- else if (textContent !== hydration.textContent) hydration = parentNode.replaceChild(document.createTextNode(textContent), hydration);
704
- }
705
- if (isTemplate(slot)) {
706
- const Component = components.get(slot.tag);
707
- if (Component) registerComponent(Component);
708
- if (previous || !(hydration instanceof Element && hydration.tagName.toUpperCase() === slot.tag.toUpperCase())) hydration = document.createElementNS(namespace(slot.tag), slot.tag);
709
- for (const [name, value] of Object.entries(slot.attributes)) createCompute(scope, (scope$1) => {
710
- const target = hydration;
711
- if (name.startsWith("on")) {
712
- const event = name.substring(2).toLowerCase();
713
- useEvent(scope$1, target, event, value);
714
- } else {
715
- const set = toString(value);
716
- if (set === "" || set === "false") return target.removeAttribute(name);
717
- return target.setAttribute(name, set);
718
- }
719
- });
720
- let index$1 = 0;
721
- for (const childSlot of slot.children) {
722
- const nodes = toArray(hydrate(scope, hydration, childSlot, index$1));
723
- index$1 += nodes.length || 1;
724
- }
725
- }
726
- hydration ??= document.createComment("");
727
- if (parentNode.childNodes.item(index) === null) parentNode.appendChild(toFragment(hydration));
728
- return hydration;
729
- }
730
- async function renderToString(scope, slot, defaults) {
731
- const { tasks } = useRuntime(scope);
732
- if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") return slot.toString();
733
- if (typeof slot === "function") {
734
- let input = slot;
735
- while (typeof input === "function") input = await input();
736
- return await renderToString(scope, input, defaults);
737
- }
738
- if (Array.isArray(slot)) {
739
- let items = "";
740
- for (const childSlot of slot) items += await renderToString(scope, childSlot, defaults);
741
- if (items === "") return "<!---->";
742
- return items;
743
- }
744
- if (isTemplate(slot)) {
745
- const merge = mergeObjects(slot, defaults?.[slot.tag]);
746
- const CustomElement = components.get(merge.tag);
747
- const prefix = Object.entries(merge.attributes).reduce((chunks, [name, value]) => {
748
- if (value && !name.startsWith("on")) chunks.push(`${name}='${toString(value)}'`);
749
- return chunks;
750
- }, [merge.tag]).join(" ");
751
- let content = `<${prefix}>`;
752
- if (CustomElement) {
753
- const element = new CustomElement(merge.attributes, scope);
754
- const result = element.setup();
755
- while (tasks.length) await tasks.shift();
756
- const template = await renderToString(element.scope, result, defaults);
757
- if (element.shadowRoot) {
758
- const shadow = {
759
- tag: "template",
760
- attributes: { shadowRootMode: element.shadowRoot.mode },
761
- children: [template]
762
- };
763
- content += await renderToString(element.scope, shadow, defaults);
764
- } else content += template;
765
- }
766
- for (const childSlot of merge.children) content += await renderToString(scope, childSlot, defaults);
767
- content += `</${merge.tag}>`;
768
- return content;
259
+ var Scope = class extends EventTarget {
260
+ parentScope;
261
+ context;
262
+ constructor(parentScope) {
263
+ super();
264
+ this.parentScope = parentScope;
265
+ this.parentScope?.onStop(() => this.stop());
266
+ this.context = {};
769
267
  }
770
- return "<!---->";
771
- }
772
- function defineComponent(options) {
773
- class Instance {
774
- static $name = options.name;
775
- static $events = options.events ?? {};
776
- static $attributes = options.attributes ?? {};
777
- scope;
778
- events;
779
- attributes;
780
- shadowRoot;
781
- constructor(input, parentScope) {
782
- this.scope = new Scope(parentScope);
783
- this.events = Object.keys(options.events ?? {}).reduce((output, name) => {
784
- Reflect.set(output, name, input?.[name], output);
785
- return output;
786
- }, {});
787
- this.attributes = Object.entries(options.attributes ?? {}).reduce((attributes, [name, attribute]) => {
788
- Reflect.set(attributes.value, name, fromValue(input?.[name]) ?? attribute.default, attributes.value);
789
- return attributes;
790
- }, createState({}));
791
- this.shadowRoot = options.shadowRoot;
268
+ getContext(input) {
269
+ let scope = this;
270
+ while (scope) {
271
+ if (scope.context[input]) return scope.context[input];
272
+ scope = scope.parentScope;
792
273
  }
793
- setup = () => options.setup(this);
274
+ return {};
794
275
  }
795
- components.set(options.name, Instance);
796
- return Instance;
797
- }
798
- function toCustomElement(Component) {
799
- return class extends HTMLElement {
800
- static formAssociated = true;
801
- component;
802
- constructor() {
803
- super();
804
- this.component = new Component();
805
- this.component.scope.setContext(HOST_CONTEXT, {
806
- host: this,
807
- internals: this.attachInternals()
808
- });
809
- }
810
- connectedCallback() {
811
- let rootNode = this;
812
- const findParentScope = (node) => {
813
- if (node) {
814
- if (isCustomElement(node)) return node.component.scope;
815
- if (node instanceof ShadowRoot) return findParentScope(node.host);
816
- return findParentScope(node.parentNode);
817
- }
818
- };
819
- this.component.scope.parentScope = findParentScope(this.parentNode);
820
- if (this.component.shadowRoot) rootNode = this.shadowRoot ?? this.attachShadow(this.component.shadowRoot);
821
- for (const [name, event] of Object.entries(Component.$events)) Reflect.set(this.component.events, name, (value) => {
822
- if (value instanceof Event) return;
823
- this.dispatchEvent(new CustomEvent(name.substring(2).toLowerCase(), {
824
- ...event,
825
- detail: value
826
- }));
827
- });
828
- hydrate(this.component.scope, rootNode, untrack(this.component.setup), 0);
829
- requestAnimationFrame(() => this.dispatchEvent(new MountedEvent()));
830
- }
831
- attributeChangedCallback(name, oldValue, value) {
832
- if (value === oldValue) return;
833
- const attribute = Component.$attributes?.[name];
834
- if (attribute) {
835
- let convertedValue;
836
- if (value) switch (attribute.type) {
837
- case String:
838
- convertedValue = value;
839
- break;
840
- case Number:
841
- convertedValue = Number(value);
842
- break;
843
- case Boolean:
844
- convertedValue = value.toLowerCase() === "true";
845
- break;
846
- case Object:
847
- convertedValue = JSON.parse(value);
848
- break;
849
- }
850
- Reflect.set(this.component.attributes.value, name, convertedValue ?? attribute.default, this.component.attributes.value);
851
- }
852
- }
853
- disconnectedCallback() {
854
- this.component.scope.stop();
855
- }
856
- static get observedAttributes() {
857
- return Object.keys(Component.$attributes ?? {});
858
- }
859
- };
860
- }
861
- function registerComponent(component) {
862
- if (isClient()) {
863
- const previous = customElements.get(component.$name);
864
- if (previous === void 0) customElements.define(component.$name, toCustomElement(component));
276
+ setContext(input, value) {
277
+ this.context[input] = value;
865
278
  }
866
- return component;
867
- }
868
- function useEvent(scope, target, event, input, options) {
869
- const controller = new AbortController();
870
- target?.addEventListener(event, (event$1) => {
871
- if (Array.isArray(input)) for (const target$1 of input) target$1?.(event$1);
872
- else input?.(event$1);
873
- }, {
874
- signal: controller.signal,
875
- ...options
876
- });
877
- scope.onStop(() => controller.abort());
878
- }
879
- function onMounted(scope, event) {
880
- if (isClient()) {
881
- const { host } = useHost(scope);
882
- useEvent(scope, host, "mounted", event);
279
+ onStop(input) {
280
+ this.addEventListener("stop", input, { once: true });
883
281
  }
884
- }
885
- async function startViewTransition(name, invoke) {
886
- if (isClient() && document.startViewTransition !== void 0) {
887
- await activeViewTransition.value?.update;
888
- const update = document.startViewTransition(invoke).finished.finally(() => activeViewTransition.value = void 0);
889
- activeViewTransition.value = {
890
- name,
891
- update
892
- };
893
- return update;
282
+ stop() {
283
+ return this.dispatchEvent(new StopEvent());
894
284
  }
895
- return invoke();
896
- }
897
- function onViewTransition(name, value) {
898
- return () => {
899
- if (activeViewTransition.value?.name === name && isClient()) return value instanceof Function ? value(name) : value;
900
- };
285
+ };
286
+ function defineContext(name) {
287
+ return name;
901
288
  }
902
289
  function isClient() {
903
290
  return typeof window !== "undefined";
@@ -905,140 +292,58 @@ function isClient() {
905
292
  function isServer() {
906
293
  return typeof window === "undefined";
907
294
  }
908
- function preventDefault(event) {
909
- event.preventDefault();
910
- }
911
- function stopPropagation(event) {
912
- event.stopPropagation();
295
+ function isFailure(result) {
296
+ return "issues" in result;
913
297
  }
914
- function stopImmediatePropagation(event) {
915
- event.stopImmediatePropagation();
298
+ function parseSchema(scope, schema, value) {
299
+ const result = schema["~standard"].validate(value);
300
+ if (isFailure(result)) throw sendBadRequest(scope, result.issues.map((issue) => issue.message).join(", "));
301
+ return result.value;
916
302
  }
917
- const activeViewTransition = createState();
918
- const components = /* @__PURE__ */ new Map();
919
- const HOST_CONTEXT = defineContext("HOST_CONTEXT");
920
-
921
- //#endregion
922
- //#region src/router/index.tsx
923
- var NavigateEvent = class extends Event {
924
- constructor() {
925
- super("navigate");
926
- }
927
- };
928
- var AfterNavigateEvent = class extends Event {
929
- constructor() {
930
- super("afterNavigate");
931
- }
932
- };
933
- function provideRouterContext(scope, options) {
934
- const url = createState();
935
- const route = createState();
936
- const radix = new Radix();
937
- const navigator = new EventTarget();
938
- for (const path in options.routes) {
939
- const [name] = toPath(path);
940
- if (name) {
941
- const value = options.routes[path];
942
- if (value) radix.insert(name, value);
943
- }
303
+ function mergeObjects(base, input) {
304
+ if (input === null || input === void 0) return mergeObjects(base, {});
305
+ const object = structuredClone(input);
306
+ for (const key in base) {
307
+ if (key === "__proto__" || key === "constructor") continue;
308
+ const value = base[key];
309
+ if (value === null || value === void 0) continue;
310
+ if (Array.isArray(value) && Array.isArray(object[key])) object[key] = [...value, ...object[key]];
311
+ else if (typeof value === "object" && typeof object[key] === "object") object[key] = mergeObjects(value, object[key]);
312
+ else object[key] = value;
944
313
  }
945
- const fetch$1 = () => {
946
- const { request } = useRuntime(scope);
947
- const { inputs } = useRoute(scope);
948
- var next = new URL(request?.url ?? window?.location.href);
949
- if (next.toString() !== url.value?.toString()) {
950
- const match = radix.match(next.pathname);
951
- url.value = next;
952
- inputs.value = match.inputs;
953
- route.value = match.value;
954
- navigator.dispatchEvent(new AfterNavigateEvent());
955
- }
956
- };
957
- scope.setContext(ROUTE_CONTEXT, { inputs: createState() });
958
- scope.setContext(ROUTER_CONTEXT, {
959
- options,
960
- navigator,
961
- url,
962
- radix,
963
- route
964
- });
965
- fetch$1();
966
- useEvent(scope, navigator, "navigate", () => startViewTransition("navigate", fetch$1));
967
- if (isClient()) useEvent(scope, window, "popstate", fetch$1);
968
- return useRouter(scope);
969
- }
970
- function useRouter(scope, context) {
971
- const { url, route, navigator, options, radix } = scope.getContext(context ?? ROUTER_CONTEXT);
972
- const navigate = (path) => {
973
- if (isClient()) {
974
- if (window.location.pathname != path) window.history.pushState(window.history.state, "", path);
975
- navigator.dispatchEvent(new NavigateEvent());
976
- }
977
- if (isServer()) throw sendRedirect(scope, path);
978
- };
979
- const anchorNavigate = (event) => {
980
- event.preventDefault();
981
- navigate(event.currentTarget?.getAttribute("href") ?? "/");
982
- };
983
- return {
984
- url,
985
- route,
986
- navigator,
987
- options,
988
- radix,
989
- navigate,
990
- anchorNavigate
991
- };
314
+ return object;
992
315
  }
993
- const Page = defineComponent({
994
- name: "x-page",
995
- setup: ({ scope }) => {
996
- const { route } = useRouter(scope);
997
- return () => route.value;
998
- }
999
- });
1000
- const ROUTER_CONTEXT = defineContext("ROUTER_CONTEXT");
1001
316
 
1002
317
  //#endregion
1003
- //#region src/locale/index.ts
1004
- function provideLocaleContext(scope, options) {
1005
- const { inputs } = useRoute(scope);
1006
- const { navigator, navigate } = useRouter(scope);
1007
- const locale = createState(options.defaultLocale);
1008
- const messages = createState();
1009
- const fetch$1 = () => {
1010
- const input = inputs.value[options.input];
1011
- if (input && input in options.locales) locale.value = input;
1012
- else navigate(`/${options.defaultLocale}`);
1013
- if (locale.value) messages.value = options.locales[locale.value];
1014
- };
1015
- fetch$1();
1016
- useEvent(scope, navigator, "afterNavigate", fetch$1);
1017
- scope.setContext(LOCALE_CONTEXT, {
1018
- locale,
1019
- messages,
1020
- options
318
+ //#region src/app/index.ts
319
+ function createApp(inputConfig) {
320
+ let config = mergeObjects(inputConfig, {
321
+ modules: [],
322
+ sources: {
323
+ assets: {
324
+ match: "**/*",
325
+ entries: ["./public"],
326
+ suffix: "?raw"
327
+ },
328
+ routes: {
329
+ match: "**/*.{js,ts}",
330
+ entries: ["./routes"]
331
+ },
332
+ middlewares: {
333
+ match: "**/*.{js,ts}",
334
+ entries: ["./middlewares"]
335
+ }
336
+ }
1021
337
  });
1022
- return useLocale(scope);
1023
- }
1024
- function useLocale(scope, context) {
1025
- const { locale, messages, options } = scope.getContext(context ?? LOCALE_CONTEXT);
1026
- const $ = (key) => {
1027
- return () => messages.value?.[key] ?? key;
1028
- };
1029
- const $date = (date, options$1) => {
1030
- const format = new Intl.DateTimeFormat(locale.value, options$1);
1031
- if (date) return format.format(date);
1032
- };
338
+ for (const module of config.modules) config = mergeObjects(config, module.config);
1033
339
  return {
1034
- locale,
1035
- messages,
1036
- options,
1037
- $,
1038
- $date
340
+ config,
341
+ virtuals: {},
342
+ alias: {}
1039
343
  };
1040
344
  }
1041
- const LOCALE_CONTEXT = defineContext("LOCALE_CONTEXT");
345
+ const SERVER = "ssr";
346
+ const CLIENT = "client";
1042
347
 
1043
348
  //#endregion
1044
- export { $fetch, AfterNavigateEvent, CLIENT, Compute, HOST_CONTEXT, Handler, LOCALE_CONTEXT, MountedEvent, NavigateEvent, Page, ROUTER_CONTEXT, ROUTE_CONTEXT, RUNTIME_CONTEXT, Radix, SERVER, STATES, Scope, StopEvent, activeCompute, activeViewTransition, components, createApp, createCompute, createElement, createMemo, createRuntime, createState, defineComponent, defineContext, defineMiddleware, defineRoute, fileName, fromValue, hydrate, isClient, isComponent, isCustomElement, isFailure, isRoute, isServer, isTemplate, mergeObjects, mimeType, onMounted, onViewTransition, parseSchema, preventDefault, provideLocaleContext, provideRouterContext, registerComponent, renderToString, sendBadRequest, sendHtml, sendJson, sendRedirect, sendText, sendUnauthorized, setCookie, startViewTransition, stopImmediatePropagation, stopPropagation, targets, toArray, toCustomElement, toFragment, toPath, toRange, toString, untrack, useAssets, useAsync, useCookie, useCookies, useEvent, useFetch, useHost, useLocale, useLocales, useQuery, useRoute, useRouter, useRoutes, useRuntime, useSetCookies, useState, useUrl };
349
+ export { CLIENT, PARAMETER, ROUTER_CONTEXT, Radix, Router, SERVER, SERVER_CONTEXT, Scope, StopEvent, WILDCARD, createApp, createServer, defineContext, defineMiddleware, defineRoute, isClient, isFailure, isServer, mergeObjects, mimeType, mimeTypes, parseSchema, sendBadRequest, sendHtml, sendJson, sendRedirect, sendText, sendUnauthorized, setCookie, toRoutePath, useCookies, useQuery, useServer, useSetCookies, useUrl };