revojs 0.0.2 → 0.0.3

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,118 +1,249 @@
1
- import defu, { defu as defu$1 } from "defu";
1
+ import { defu } from "defu";
2
2
  import { h } from "revojs/jsx-runtime";
3
3
 
4
4
  //#region src/app/index.ts
5
+ const defineContent = (key) => {
6
+ return Object.assign(String(key), { key });
7
+ };
5
8
  const getContent = async (key) => {
6
- return await import("#virtual/content").then((module) => module.index[key]);
9
+ return await import("#virtual/content").then((module) => module.index[key.toString()]);
7
10
  };
8
11
  const createApp = (config) => {
9
12
  return {
10
- config: defu$1(config, {
13
+ config: defu(config, {
11
14
  client: { entry: "./index.html" },
12
15
  server: { entry: "revojs/presets/node" },
13
16
  content: {
14
- static: {
17
+ [STATIC_CONTENT]: {
15
18
  type: "static",
16
19
  source: "./dist/client",
17
20
  include: ["**/*.*"]
18
21
  },
19
- routes: {
22
+ [PAGES_CONTENT]: {
20
23
  type: "asset",
21
24
  source: "./routes",
22
25
  include: [
23
26
  "**/*.js",
24
27
  "**/*.ts",
25
28
  "**/*.jsx",
26
- "**/*.tsx"
29
+ "**/*.tsx",
30
+ "!**/*.*.js",
31
+ "!**/*.*.ts",
32
+ "!**/*.*.jsx",
33
+ "!**/*.*.tsx"
34
+ ]
35
+ },
36
+ [ROUTES_CONTENT]: {
37
+ type: "asset",
38
+ source: "./routes",
39
+ include: [
40
+ "**/*.*.js",
41
+ "**/*.*.ts",
42
+ "**/*.*.jsx",
43
+ "**/*.*.tsx"
27
44
  ]
28
45
  }
29
46
  },
30
- markdown: {}
47
+ markdown: {},
48
+ dev: { middleware: [] }
31
49
  }),
32
50
  virtuals: {}
33
51
  };
34
52
  };
53
+ const STATIC_CONTENT = defineContent("STATIC");
54
+ const PAGES_CONTENT = defineContent("PAGES");
55
+ const ROUTES_CONTENT = defineContent("ROUTES");
35
56
 
36
57
  //#endregion
37
- //#region src/event/index.ts
38
- const createEvent = (request, context) => {
39
- return {
40
- request,
41
- response: { headers: new Headers() },
42
- context
43
- };
44
- };
45
- const sendText = (event, text) => {
46
- return new Response(text, event.response);
47
- };
48
- const sendHtml = (event, text) => {
49
- setHeader(event, "Content-Type", "text/html");
50
- return new Response(text, event.response);
51
- };
52
- const sendJson = (event, value) => {
53
- return new Response(JSON.stringify(value), event.response);
54
- };
55
- const setHeader = (event, name, value) => {
56
- event.response.headers.set(name, value);
58
+ //#region src/jsx/index.ts
59
+ const svgElements = new Set([
60
+ "altGlyph",
61
+ "altGlyphDef",
62
+ "altGlyphItem",
63
+ "animate",
64
+ "animateColor",
65
+ "animateMotion",
66
+ "animateTransform",
67
+ "circle",
68
+ "clipPath",
69
+ "color-profile",
70
+ "cursor",
71
+ "defs",
72
+ "desc",
73
+ "ellipse",
74
+ "feBlend",
75
+ "feColorMatrix",
76
+ "feComponentTransfer",
77
+ "feComposite",
78
+ "feConvolveMatrix",
79
+ "feDiffuseLighting",
80
+ "feDisplacementMap",
81
+ "feDistantLight",
82
+ "feDropShadow",
83
+ "feFlood",
84
+ "feFuncA",
85
+ "feFuncB",
86
+ "feFuncG",
87
+ "feFuncR",
88
+ "feGaussianBlur",
89
+ "feImage",
90
+ "feMerge",
91
+ "feMergeNode",
92
+ "feMorphology",
93
+ "feOffset",
94
+ "fePointLight",
95
+ "feSpecularLighting",
96
+ "feSpotLight",
97
+ "feTile",
98
+ "feTurbulence",
99
+ "filter",
100
+ "font",
101
+ "font-face",
102
+ "font-face-format",
103
+ "font-face-name",
104
+ "font-face-src",
105
+ "font-face-uri",
106
+ "foreignObject",
107
+ "g",
108
+ "glyph",
109
+ "glyphRef",
110
+ "hkern",
111
+ "image",
112
+ "line",
113
+ "linearGradient",
114
+ "marker",
115
+ "mask",
116
+ "metadata",
117
+ "missing-glyph",
118
+ "mpath",
119
+ "path",
120
+ "pattern",
121
+ "polygon",
122
+ "polyline",
123
+ "radialGradient",
124
+ "rect",
125
+ "set",
126
+ "stop",
127
+ "svg",
128
+ "switch",
129
+ "symbol",
130
+ "text",
131
+ "textPath",
132
+ "tref",
133
+ "tspan",
134
+ "use",
135
+ "view",
136
+ "vkern"
137
+ ]);
138
+ const namespace = (tag) => {
139
+ return svgElements.has(tag) ? "http://www.w3.org/2000/svg" : "http://www.w3.org/1999/xhtml";
57
140
  };
58
141
 
59
142
  //#endregion
60
143
  //#region src/signals/index.ts
144
+ var Scope = class {
145
+ dispose;
146
+ constructor() {
147
+ this.dispose = new Array();
148
+ }
149
+ run(invoke) {
150
+ let previous = activeScope;
151
+ activeScope = this;
152
+ try {
153
+ return invoke();
154
+ } finally {
155
+ activeScope = previous;
156
+ }
157
+ }
158
+ stop() {
159
+ while (this.dispose.length) this.dispose.pop()?.(this);
160
+ }
161
+ };
162
+ var Compute = class {
163
+ invoke;
164
+ scope;
165
+ constructor(invoke) {
166
+ this.invoke = invoke;
167
+ this.scope = new Scope();
168
+ }
169
+ run() {
170
+ this.scope.stop();
171
+ return this.scope.run(this.invoke);
172
+ }
173
+ };
61
174
  var Handler = class Handler {
62
- get = (target, path) => {
63
- if (runningCompute) {
64
- const effects = targets.get(target) ?? new Map();
65
- const set = effects.get(path) ?? new Set();
66
- effects.set(path, set.add(runningCompute));
67
- targets.set(target, effects);
68
- runningCompute.previous?.cleanUps.push((compute) => set.delete(compute));
175
+ get(target, key) {
176
+ const compute = activeCompute;
177
+ if (compute) {
178
+ const computes = targets.get(target) ?? new Map();
179
+ const set = computes.get(key) ?? new Set();
180
+ computes.set(key, set.add(compute));
181
+ targets.set(target, computes);
182
+ onCleanUp(() => {
183
+ compute.scope.stop();
184
+ set.delete(compute);
185
+ });
69
186
  }
70
- const value = Reflect.get(target, path);
71
- if (value instanceof Object) return new Proxy(value, new Handler());
72
- return value;
73
- };
74
- set = (target, path, value) => {
75
- const result = Reflect.set(target, path, value);
76
- for (const effect of targets.get(target)?.get(path) ?? []) {
77
- while (effect.cleanUps.length) effect.cleanUps.pop()?.(effect);
78
- effect.invoke();
187
+ const value = Reflect.get(target, key);
188
+ if (value) {
189
+ if (typeof value === "function") return value.bind(target);
190
+ if (typeof value === "object") return new Proxy(value, new Handler());
79
191
  }
192
+ return value;
193
+ }
194
+ set(target, key, value) {
195
+ const result = Reflect.set(target, key, value);
196
+ for (const compute of targets.get(target)?.get(key) ?? []) compute.run();
80
197
  return result;
81
- };
198
+ }
82
199
  };
83
- const createState = (value) => {
200
+ function createState(value) {
84
201
  return new Proxy({ value }, new Handler());
85
- };
86
- const createCompute = (invoke) => {
87
- return runCompute({
88
- invoke,
89
- cleanUps: []
90
- });
91
- };
92
- const runCompute = (compute) => {
93
- compute.previous = runningCompute;
94
- runningCompute = compute;
202
+ }
203
+ function createCompute(invoke) {
204
+ let previous = activeCompute;
205
+ activeCompute = new Compute(invoke);
95
206
  try {
96
- return compute.invoke();
207
+ return invoke();
97
208
  } finally {
98
- runningCompute = compute.previous;
209
+ activeCompute = previous;
99
210
  }
100
- };
101
- const fromValue = (value) => {
211
+ }
212
+ function createMemo(invoke) {
213
+ let state;
214
+ const compute = createCompute(() => {
215
+ const value = invoke();
216
+ if (typeof state === "object") state.value = value;
217
+ return value;
218
+ });
219
+ state = createState(compute);
220
+ return state;
221
+ }
222
+ function onCleanUp(invoke) {
223
+ activeScope?.dispose.push(invoke);
224
+ }
225
+ function fromValue(value) {
102
226
  if (value) {
103
227
  if (value instanceof Function) return fromValue(value());
104
228
  if (value instanceof Object) return value.value;
105
229
  }
106
230
  return value;
107
- };
108
- const onCleanUp = (cleanUp) => {
109
- runningCompute?.cleanUps.push(cleanUp);
110
- };
231
+ }
232
+ let activeScope;
233
+ let activeCompute;
111
234
  const targets = new WeakMap();
112
- let runningCompute;
113
235
 
114
236
  //#endregion
115
237
  //#region src/html/index.ts
238
+ const isTemplate = (value) => {
239
+ return "tag" in value && "attributes" in value && "children" in value;
240
+ };
241
+ const descriptor = (descriptor$1) => {
242
+ return (typeof descriptor$1 === "object" ? descriptor$1.key : descriptor$1).toString();
243
+ };
244
+ const defineContext = (description) => {
245
+ return { key: Symbol(description) };
246
+ };
116
247
  const createElement = (input, attributes, ...children) => {
117
248
  return {
118
249
  tag: typeof input === "function" ? input.$name : input,
@@ -132,59 +263,85 @@ const toString = (slot) => {
132
263
  default: return "";
133
264
  }
134
265
  };
135
- const slotToString = async (slot) => {
136
- if (Array.isArray(slot)) return await Promise.all(slot.map(slotToString)).then((chunks) => chunks.join(" "));
137
- if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") return toString(slot);
138
- if (typeof slot === "function") return await slotToString(await slot());
266
+ const toFragment = (nodes) => {
267
+ const fragment = document.createDocumentFragment();
268
+ fragment.replaceChildren(...nodes);
269
+ return fragment;
270
+ };
271
+ const renderToString = async (slot, context) => {
272
+ if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") return slot.toString();
273
+ if (typeof slot === "function") return await renderToString(await slot(), context);
139
274
  if (typeof slot === "object") {
140
- if ("tag" in slot && "attributes" in slot && "children" in slot) {
141
- const Constructor = components.get(slot.tag);
142
- const prefix = [slot.tag, ...Object.entries(slot.attributes ?? {}).map(([name, value]) => {
143
- if (!name.startsWith("on")) return `${name}="${toString(value)}"`;
144
- })].filter(Boolean).join(" ");
145
- const children = await slotToString(slot.children);
146
- if (Constructor) {
147
- const component = new Constructor(slot.attributes);
148
- const template = await slotToString(await component.setup());
149
- if (component.shadowRoot) return `<${prefix}> <template shadowRootMode="${component.shadowRoot.mode}"> ${template} </template> ${children} </${slot.tag}>`;
150
- return `<${prefix}> ${template} ${children} </${slot.tag}>`;
275
+ if (Array.isArray(slot)) return await Promise.all(slot.map((slot$1) => renderToString(slot$1, context))).then((chunks) => chunks.join(""));
276
+ if (isTemplate(slot)) {
277
+ const customElement = components.get(slot.tag);
278
+ const prefix = Object.entries(slot.attributes).reduce((chunks, [name, value]) => {
279
+ if (!name.startsWith("on")) chunks.push(`${name}='${toString(value)}'`);
280
+ return chunks;
281
+ }, [slot.tag]).join(" ");
282
+ const children = await renderToString(slot.children, context);
283
+ if (customElement) {
284
+ const element = new customElement(slot.attributes, context);
285
+ const template = await renderToString(await element.setup(), context);
286
+ if (element.shadowRoot) {
287
+ const shadow = await renderToString({
288
+ tag: "template",
289
+ attributes: { shadowRootMode: element.shadowRoot.mode },
290
+ children: [template]
291
+ }, context);
292
+ return `<${prefix}>` + shadow + children + `</${slot.tag}>`;
293
+ }
294
+ return `<${prefix}>` + template + children + `</${slot.tag}>`;
151
295
  }
152
- return `<${prefix}> ${children} </${slot.tag}>`;
296
+ return `<${prefix}>` + children + `</${slot.tag}>`;
153
297
  }
154
- return toString(slot);
298
+ return JSON.stringify(slot);
155
299
  }
156
- return "";
300
+ return "<!---->";
157
301
  };
158
- const slotToNode = async (slot) => {
159
- if (Array.isArray(slot)) {
160
- const fragment = document.createDocumentFragment();
161
- fragment.replaceChildren(...await Promise.all(slot.map(slotToNode)));
162
- return fragment;
163
- }
302
+ const renderToNode = async (slot) => {
164
303
  if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") return document.createTextNode(slot.toString());
165
304
  if (typeof slot === "function") {
166
- let oldNode;
167
- return await createCompute(async () => {
168
- const node = await slotToNode(await slot());
169
- oldNode?.parentNode?.replaceChild(node, oldNode);
170
- oldNode = node;
171
- return node;
305
+ let node;
306
+ await createCompute(async () => {
307
+ let input = slot;
308
+ let parentNode = Array.isArray(node) ? node.at(0)?.parentNode : node?.parentNode;
309
+ while (typeof input === "function") input = await input();
310
+ let next = await (Array.isArray(input) ? Promise.all(input.map(renderToNode)) : renderToNode(input));
311
+ if (Array.isArray(next)) if (Array.isArray(node)) {
312
+ if (parentNode) {
313
+ const range = document.createRange();
314
+ const firstNode = node.at(0);
315
+ if (firstNode) range.setStartBefore(firstNode);
316
+ const lastNode = node.at(-1);
317
+ if (lastNode) range.setEndAfter(lastNode);
318
+ range.deleteContents();
319
+ for (const child of next) parentNode?.appendChild(child);
320
+ }
321
+ } else node?.parentNode?.replaceChild(toFragment(next), node);
322
+ else if (Array.isArray(node)) {} else node?.parentNode?.replaceChild(next, node);
323
+ node = next;
172
324
  });
325
+ if (Array.isArray(node)) return toFragment(node);
326
+ return node ?? document.createComment("");
173
327
  }
174
328
  if (typeof slot === "object") {
175
- if ("tag" in slot && "attributes" in slot && "children" in slot) {
176
- const node = document.createElement(slot.tag);
177
- for (const key in slot.attributes) {
178
- const value = slot.attributes[key];
179
- if (value) if (key.startsWith("on")) {
180
- const event = key.substring(2).toLowerCase();
181
- node.addEventListener(event, value);
182
- onCleanUp(() => node.removeEventListener(event, value));
183
- } else node.setAttribute(key, toString(value));
329
+ if (Array.isArray(slot)) return toFragment(await Promise.all(slot.map(renderToNode)));
330
+ if (isTemplate(slot)) {
331
+ const element = document.createElementNS(namespace(slot.tag), slot.tag);
332
+ for (const name in slot.attributes) {
333
+ const value = slot.attributes[name];
334
+ if (value) if (name.startsWith("on") && typeof value === "function") {
335
+ const event = name.substring(2).toLowerCase();
336
+ const controller = new AbortController();
337
+ element.addEventListener(event, value, { signal: controller.signal });
338
+ onCleanUp(() => controller.abort());
339
+ } else createCompute(() => element.setAttribute(name, toString(value)));
184
340
  }
185
- node.replaceChildren(await slotToNode(slot.children));
186
- return node;
341
+ element.replaceChildren(await renderToNode(slot.children));
342
+ return element;
187
343
  }
344
+ return document.createTextNode(JSON.stringify(slot));
188
345
  }
189
346
  return document.createComment("");
190
347
  };
@@ -196,7 +353,8 @@ const defineComponent = (options) => {
196
353
  events;
197
354
  attributes;
198
355
  shadowRoot;
199
- constructor(input) {
356
+ context;
357
+ constructor(input, context) {
200
358
  this.events = Object.keys(options.events ?? {}).reduce((output, name) => {
201
359
  Reflect.set(output, name, input?.[name], output);
202
360
  return output;
@@ -206,34 +364,48 @@ const defineComponent = (options) => {
206
364
  return output;
207
365
  }, createState({}));
208
366
  this.shadowRoot = options.shadowRoot ?? { mode: "open" };
367
+ this.context = context ?? {};
209
368
  }
369
+ getContext = (input) => {
370
+ return this.context[descriptor(input)];
371
+ };
372
+ setContext = (input, value) => {
373
+ this.context[descriptor(input)] = value;
374
+ };
210
375
  setup = () => options.setup(this);
211
376
  });
212
377
  };
213
378
  const toCustomElement = (component) => {
214
379
  return class extends HTMLElement {
380
+ static formAssociated = true;
381
+ scope;
215
382
  component;
216
383
  constructor() {
217
384
  super();
385
+ this.scope = new Scope();
218
386
  this.component = new component();
219
387
  }
220
388
  async connectedCallback() {
221
389
  let previous = activeElement;
390
+ const parentNode = getCustomElement(this.parentNode);
222
391
  activeElement = this;
223
392
  try {
224
393
  const shadow = this.component.shadowRoot ? this.attachShadow(this.component.shadowRoot) : this;
225
394
  for (const [name, event] of Object.entries(component.$events)) Reflect.set(this.component.events, name, (value) => {
395
+ if (value instanceof Event) return;
226
396
  this.dispatchEvent(new CustomEvent(name.substring(2).toLowerCase(), {
227
397
  ...event,
228
398
  detail: value
229
399
  }));
230
400
  }, this.component.events);
231
- shadow.replaceChildren(await slotToNode(await this.component.setup()));
401
+ if (parentNode) for (const key in parentNode.component.context) this.component.setContext(key, parentNode.component.context[key]);
402
+ await this.scope.run(async () => shadow.replaceChildren(await renderToNode(await this.component.setup())));
232
403
  } finally {
233
404
  activeElement = previous;
234
405
  }
235
406
  }
236
- attributeChangedCallback(name, _, value) {
407
+ attributeChangedCallback(name, oldValue, value) {
408
+ if (value === oldValue) return;
237
409
  const attribute = component.$attributes?.[name];
238
410
  if (attribute) {
239
411
  let convertedValue;
@@ -254,6 +426,9 @@ const toCustomElement = (component) => {
254
426
  Reflect.set(this.component.attributes.value, name, convertedValue ?? attribute.default, this.component.attributes.value);
255
427
  }
256
428
  }
429
+ disconnectedCallback() {
430
+ this.scope.stop();
431
+ }
257
432
  static get observedAttributes() {
258
433
  return Object.keys(component.$attributes ?? {});
259
434
  }
@@ -281,21 +456,121 @@ const getGlobalStyles = () => {
281
456
  return sheet;
282
457
  });
283
458
  };
284
- let globalStyles;
459
+ const getCustomElement = (node) => {
460
+ if (node) {
461
+ if ("component" in node) return node;
462
+ return getCustomElement(node.parentNode);
463
+ }
464
+ };
465
+ const preventDefault = (event) => {
466
+ event.preventDefault();
467
+ };
468
+ const stopPropagation = (event) => {
469
+ event.stopPropagation();
470
+ };
471
+ const stopImmediatePropagation = (event) => {
472
+ event.stopImmediatePropagation();
473
+ };
285
474
  let activeElement;
286
475
  const components = new Map();
287
476
 
477
+ //#endregion
478
+ //#region src/hooks/index.ts
479
+ var Hooks = class {
480
+ hooks;
481
+ constructor() {
482
+ this.hooks = new Map();
483
+ }
484
+ on = (input, invoke) => {
485
+ const key = descriptor(input);
486
+ const invokes = this.hooks.get(key) ?? new Set();
487
+ invokes.add(invoke);
488
+ this.hooks.set(key, invokes);
489
+ return onCleanUp(() => invokes.delete(invoke));
490
+ };
491
+ dispatch = (input, value) => {
492
+ const invokes = this.hooks.get(descriptor(input));
493
+ if (invokes) for (const invoke of invokes) invoke(value);
494
+ };
495
+ };
496
+ const defineHook = (description) => {
497
+ return { key: Symbol(description) };
498
+ };
499
+
288
500
  //#endregion
289
501
  //#region src/http/index.ts
290
- const mimeTypes = {
291
- css: "text/css",
292
- js: "text/javascript",
293
- txt: "text/plain"
502
+ const createEvent = (request, context) => {
503
+ return {
504
+ request,
505
+ response: { headers: new Headers() },
506
+ context
507
+ };
508
+ };
509
+ const sendText = (event, text) => {
510
+ event.response.headers.set("Content-Type", "text/plain");
511
+ return new Response(text, event.response);
512
+ };
513
+ const sendHtml = (event, text) => {
514
+ event.response.headers.set("Content-Type", "text/html");
515
+ return new Response(text, event.response);
516
+ };
517
+ const sendJson = (event, value) => {
518
+ event.response.headers.set("Content-Type", "application/json");
519
+ return new Response(JSON.stringify(value), event.response);
520
+ };
521
+ const sendRedirect = (event, path) => {
522
+ event.response.status = 302;
523
+ event.response.headers.set("Location", path);
524
+ return new Response(null, event.response);
525
+ };
526
+ const sendBadRequest = (event, text) => {
527
+ event.response.status = 400;
528
+ return new Response(text, event.response);
529
+ };
530
+ const sendUnauthorized = (event) => {
531
+ event.response.status = 401;
532
+ return new Response(null, event.response);
533
+ };
534
+ const getRequestUrl = (event) => {
535
+ return new URL(event instanceof Request ? event.url : event.request.url);
536
+ };
537
+ const getCookies = (event) => {
538
+ const cookies = event.request.headers.get("Cookie")?.split("; ") ?? [];
539
+ return cookies.reduce((result, cookie) => {
540
+ const [name, value] = cookie.split("=");
541
+ if (name && value) result[name] = decodeURIComponent(value);
542
+ return result;
543
+ }, {});
544
+ };
545
+ const getSetCookies = (event) => {
546
+ const cookies = event.request.headers.getSetCookie();
547
+ return cookies.reduce((result, cookie) => {
548
+ const [name, value] = cookie.split("=");
549
+ if (name && value) result[name] = decodeURIComponent(value);
550
+ return result;
551
+ }, {});
552
+ };
553
+ const setCookie = (event, name, value, options) => {
554
+ let cookie = name + "=" + encodeURIComponent(value);
555
+ if (options?.domain) cookie += `; Domain=${options.domain}`;
556
+ if (options?.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
557
+ if (options?.httpOnly) cookie += `; HttpOnly`;
558
+ if (options?.maxAge) cookie += `; Max-Age=${options.maxAge}`;
559
+ if (options?.path) cookie += `; Path=${options.path}`;
560
+ if (options?.priority) cookie += `; Priority=${options.priority}`;
561
+ if (options?.sameSite) cookie += `; SameSite=${options.sameSite}`;
562
+ if (options?.secure) cookie += `; Secure`;
563
+ event.response.headers.append("Set-Cookie", cookie);
294
564
  };
295
565
  const getMimeType = (file) => {
296
566
  const extension = /\.([a-zA-Z0-9]+?)$/.exec(file)?.at(1);
297
567
  return mimeTypes[extension ?? ""] ?? "text/plain";
298
568
  };
569
+ const mimeTypes = {
570
+ css: "text/css",
571
+ js: "text/javascript",
572
+ txt: "text/plain"
573
+ };
299
574
 
300
575
  //#endregion
301
576
  //#region src/markdown/index.ts
@@ -435,47 +710,79 @@ var Radix = class Radix {
435
710
  const defineRoute = (route) => {
436
711
  return route;
437
712
  };
438
- const defineSocket = (socket) => {
439
- return socket;
440
- };
441
713
  const fileName = (path) => {
442
714
  return path.split("/").pop()?.split(".").slice(0, -1).join(".");
443
715
  };
444
716
  const toPath = (value) => {
445
- return value.replace(/\/index/, "").replace(/index/, "").replace(/\.(js|ts|jsx|tsx)$/, "").replaceAll(/\[(.*)\]/g, (_, name) => ":" + name);
717
+ const path = (value.startsWith("/") ? value : "/" + value).replaceAll(/\/index/g, "").replaceAll(/\[(.*)\]/g, (_, name) => ":" + name);
718
+ return (path.startsWith("/") ? path : "/" + path).split(".");
719
+ };
720
+ const $fetch = async (input, init) => {
721
+ let response;
722
+ if (activeEvent) {
723
+ const url = new URL(input.toString(), activeEvent.request.url);
724
+ response = await (await import("#virtual/runtime")).runtime.fetch(new Request(url, init), activeEvent.context);
725
+ } else response = await fetch(input, init);
726
+ if (response.ok === false) throw response;
727
+ switch (response.headers.get("Content-Type")) {
728
+ case "application/json": return response.json();
729
+ default: return response;
730
+ }
731
+ };
732
+ const getVariables = (event) => {
733
+ return event ? event.context.variables : import.meta.env;
446
734
  };
447
735
  const createRuntime = async () => {
448
736
  const radix = new Radix();
449
- const assets = await getContent("static");
737
+ const middlewares = new Array();
738
+ const assets = await getContent(STATIC_CONTENT);
450
739
  for (const path in assets) radix.insert("GET/" + path, defineRoute({ fetch: async (event) => {
451
- setHeader(event, "Content-Type", getMimeType(path));
452
- return sendText(event, await assets[path]());
740
+ event.response.headers.set("Content-Type", getMimeType(path));
741
+ return new Response(await assets[path]?.(), event.response);
453
742
  } }));
454
- const routes = await getContent("routes");
455
- for (const path in routes) radix.insert("GET/" + toPath(path), defineRoute({ fetch: async (event) => {
456
- const route = await routes[path]();
457
- if (route) {
458
- if ("message" in route) return sendText(event, "WebSocket");
459
- if ("$name" in route) {
460
- const slot = await import("#virtual/client").then((module) => module.index);
461
- return sendHtml(event, await slotToString(slot));
743
+ const pages = await getContent(PAGES_CONTENT);
744
+ for (const path in pages) {
745
+ const [name] = toPath(path);
746
+ radix.insert("GET" + name, defineRoute({ fetch: async (event) => {
747
+ const slot = await import("#virtual/client").then((module) => module.index);
748
+ return sendHtml(event, await renderToString(slot, {}));
749
+ } }));
750
+ }
751
+ const routes = await getContent(ROUTES_CONTENT);
752
+ for (const path in routes) {
753
+ const [name, method] = toPath(path);
754
+ radix.insert((method ?? "GET").toUpperCase() + name, defineRoute({ fetch: async (event) => {
755
+ const route = await routes[path]?.();
756
+ if (route) return route.fetch(event);
757
+ } }));
758
+ }
759
+ const invoke = (event, next, index) => {
760
+ return middlewares.at(index)?.(event, () => invoke(event, next, index + 1)) ?? next(event);
761
+ };
762
+ return {
763
+ radix,
764
+ middlewares,
765
+ fetch: async (request, context) => {
766
+ const url = getRequestUrl(request);
767
+ const { value: route, inputs } = radix.match(request.method + url.pathname);
768
+ activeEvent = createEvent(request, {
769
+ ...context,
770
+ inputs
771
+ });
772
+ try {
773
+ if (route) {
774
+ const response = await invoke(activeEvent, route.fetch, 0);
775
+ if (response) return response;
776
+ }
777
+ return sendText(activeEvent, "Not found");
778
+ } catch (response) {
779
+ if (response instanceof Response) return response;
780
+ throw response;
781
+ } finally {
782
+ activeEvent = undefined;
462
783
  }
463
- return route.fetch(event);
464
784
  }
465
- } }));
466
- return { fetch: async (request, context) => {
467
- const url = new URL(request.url);
468
- const { value: route, inputs } = radix.match(request.method + url.pathname);
469
- activeEvent = createEvent(request, {
470
- ...context,
471
- inputs
472
- });
473
- try {
474
- return await route?.fetch(activeEvent) ?? sendText(activeEvent, "Not found");
475
- } finally {
476
- activeEvent = undefined;
477
- }
478
- } };
785
+ };
479
786
  };
480
787
  let activeEvent;
481
788
 
@@ -483,10 +790,14 @@ let activeEvent;
483
790
  //#region src/router/index.tsx
484
791
  const Outlet = defineComponent({
485
792
  name: "x-outlet",
793
+ shadowRoot: false,
486
794
  setup: async () => {
487
795
  const radix = new Radix();
488
- const routes = await getContent("routes");
489
- for (const path in routes) radix.insert("/" + toPath(path), routes[path]);
796
+ const routes = await getContent(PAGES_CONTENT);
797
+ for (const path in routes) {
798
+ const [name] = toPath(path);
799
+ radix.insert(name, routes[path]);
800
+ }
490
801
  const url = createState(new URL(activeEvent ? activeEvent.request.url : window.location.href));
491
802
  if (isClient()) {
492
803
  const controller = new AbortController();
@@ -513,4 +824,4 @@ const anchorNavigate = (event) => {
513
824
  };
514
825
 
515
826
  //#endregion
516
- export { Handler, Outlet, Radix, activeElement, activeEvent, addStyles, anchorNavigate, components, createApp, createCompute, createElement, createEvent, createRuntime, createState, defineComponent, defineRoute, defineSocket, fileName, fromValue, getContent, getGlobalStyles, getMimeType, globalStyles, isClient, isServer, markdownToSlot, navigate, onCleanUp, registerComponent, runCompute, runningCompute, sendHtml, sendJson, sendText, setHeader, slotToNode, slotToString, targets, toCustomElement, toPath, toString };
827
+ export { $fetch, Compute, Handler, Hooks, Outlet, PAGES_CONTENT, ROUTES_CONTENT, Radix, STATIC_CONTENT, Scope, activeCompute, activeElement, activeEvent, activeScope, addStyles, anchorNavigate, components, createApp, createCompute, createElement, createEvent, createMemo, createRuntime, createState, defineComponent, defineContent, defineContext, defineHook, defineRoute, descriptor, fileName, fromValue, getContent, getCookies, getCustomElement, getGlobalStyles, getMimeType, getRequestUrl, getSetCookies, getVariables, isClient, isServer, isTemplate, markdownToSlot, navigate, onCleanUp, preventDefault, registerComponent, renderToNode, renderToString, sendBadRequest, sendHtml, sendJson, sendRedirect, sendText, sendUnauthorized, setCookie, stopImmediatePropagation, stopPropagation, targets, toCustomElement, toFragment, toPath, toString };