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