revojs 0.0.62 → 0.0.64

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.
@@ -47,14 +47,14 @@ export interface ComponentOptions<TEvents extends Events, TAttributes extends At
47
47
  attributes?: TAttributes;
48
48
  shadowRoot?: ShadowRootInit;
49
49
  styles?: Array<string>;
50
- setup: (component: Component<TEvents, TAttributes>) => Slot | Promise<Slot>;
50
+ setup: (component: Component<TEvents, TAttributes>) => Slot;
51
51
  }
52
52
  export interface Component<TEvents extends Events, TAttributes extends Attributes> {
53
53
  readonly scope: Scope;
54
54
  readonly events: EventOutput<TEvents>;
55
55
  readonly attributes: State<AttributeOutput<TAttributes>>;
56
56
  readonly shadowRoot?: ShadowRootInit;
57
- setup: () => Slot | Promise<Slot>;
57
+ setup: () => Slot;
58
58
  }
59
59
  export interface ComponentConstructor<TEvents extends Events, TAttributes extends Attributes> {
60
60
  $name: string;
@@ -81,7 +81,7 @@ export declare const toString: (slot: Slot) => string;
81
81
  export declare const toArray: (hydration: Hydration) => Array<Node>;
82
82
  export declare const toRange: (hydration: Hydration) => Range;
83
83
  export declare const toFragment: (hydration: Hydration) => DocumentFragment;
84
- export declare const hydrate: (scope: Scope, parentNode: Node, slot: Slot, index: number, previous?: Hydration) => Promise<Hydration>;
84
+ export declare const hydrate: (scope: Scope, parentNode: Node, slot: Slot, index: number, previous?: Hydration) => Hydration;
85
85
  export declare const renderToString: (scope: Scope, slot: Slot) => Promise<string>;
86
86
  export declare const defineComponent: <TEvents extends Events, TAttributes extends Attributes>(options: ComponentOptions<TEvents, TAttributes>) => ComponentConstructor<TEvents, TAttributes>;
87
87
  export declare const toCustomElement: <TEvents extends Events, TAttributes extends Attributes>(Component: ComponentConstructor<TEvents, TAttributes>) => CustomElementConstructor<TEvents, TAttributes>;
package/dist/index.js CHANGED
@@ -99,6 +99,163 @@ const namespace = (tag) => {
99
99
  return svgElements.has(tag) ? "http://www.w3.org/2000/svg" : "http://www.w3.org/1999/xhtml";
100
100
  };
101
101
 
102
+ //#endregion
103
+ //#region src/http/index.ts
104
+ const sendText = (scope, text) => {
105
+ const { response } = useRuntime(scope);
106
+ response.headers.set("Content-Type", "text/plain");
107
+ return new Response(text, response);
108
+ };
109
+ const sendHtml = (scope, text) => {
110
+ const { response } = useRuntime(scope);
111
+ response.headers.set("Content-Type", "text/html");
112
+ return new Response(text, response);
113
+ };
114
+ const sendJson = (scope, value) => {
115
+ const { response } = useRuntime(scope);
116
+ response.headers.set("Content-Type", "application/json");
117
+ return new Response(JSON.stringify(value), response);
118
+ };
119
+ const sendRedirect = (scope, path) => {
120
+ const { response } = useRuntime(scope);
121
+ response.status = 302;
122
+ response.headers.set("Location", path);
123
+ return new Response(null, response);
124
+ };
125
+ const sendBadRequest = (scope, text) => {
126
+ const { response } = useRuntime(scope);
127
+ response.status = 400;
128
+ return new Response(text, response);
129
+ };
130
+ const sendUnauthorized = (scope) => {
131
+ const { response } = useRuntime(scope);
132
+ response.status = 401;
133
+ return new Response(null, response);
134
+ };
135
+ const useRequestUrl = (scope, base) => {
136
+ const { request } = useRuntime(scope);
137
+ return new URL(request.url, base);
138
+ };
139
+ const useCookies = (scope) => {
140
+ const { request } = useRuntime(scope);
141
+ return (isClient() ? document.cookie : request.headers.get("Cookie") ?? "").split("; ").reduce((result, cookie) => {
142
+ const [name, value] = cookie.split("=");
143
+ if (name && value) result[name] = decodeURIComponent(value);
144
+ return result;
145
+ }, {});
146
+ };
147
+ const useSetCookies = (scope) => {
148
+ const { request } = useRuntime(scope);
149
+ return request.headers.getSetCookie().reduce((result, cookie) => {
150
+ const [name, value] = cookie.split("=");
151
+ if (name && value) result[name] = decodeURIComponent(value);
152
+ return result;
153
+ }, {});
154
+ };
155
+ const setCookie = (scope, name, value, options) => {
156
+ const { response } = useRuntime(scope);
157
+ let cookie = name + "=" + encodeURIComponent(value);
158
+ if (options?.domain) cookie += `; Domain=${options.domain}`;
159
+ if (options?.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
160
+ if (options?.httpOnly) cookie += `; HttpOnly`;
161
+ if (options?.maxAge) cookie += `; Max-Age=${options.maxAge}`;
162
+ if (options?.path) cookie += `; Path=${options.path}`;
163
+ if (options?.priority) cookie += `; Priority=${options.priority}`;
164
+ if (options?.sameSite) cookie += `; SameSite=${options.sameSite}`;
165
+ if (options?.secure) cookie += `; Secure`;
166
+ if (isClient()) document.cookie = cookie;
167
+ else response.headers.append("Set-Cookie", cookie);
168
+ };
169
+ const mimeType = (file) => {
170
+ const extension = /\.([a-zA-Z0-9]+?)$/.exec(file)?.at(1);
171
+ return mimeTypes[extension ?? ""] ?? "text/plain";
172
+ };
173
+ const mimeTypes = {
174
+ txt: "text/plain",
175
+ css: "text/css",
176
+ html: "text/html",
177
+ htm: "text/html",
178
+ js: "text/javascript",
179
+ json: "application/json",
180
+ xml: "application/xml",
181
+ csv: "text/csv",
182
+ jpg: "image/jpeg",
183
+ jpeg: "image/jpeg",
184
+ png: "image/png",
185
+ gif: "image/gif",
186
+ webp: "image/webp",
187
+ svg: "image/svg+xml",
188
+ bmp: "image/bmp",
189
+ ico: "image/x-icon",
190
+ ttf: "font/ttf",
191
+ otf: "font/otf",
192
+ woff: "font/woff",
193
+ woff2: "font/woff2",
194
+ mp3: "audio/mpeg",
195
+ wav: "audio/wav",
196
+ ogg: "audio/ogg",
197
+ m4a: "audio/mp4",
198
+ mp4: "video/mp4",
199
+ webm: "video/webm",
200
+ ogv: "video/ogg",
201
+ mov: "video/quicktime",
202
+ avi: "video/x-msvideo",
203
+ zip: "application/zip",
204
+ rar: "application/vnd.rar",
205
+ tar: "application/x-tar",
206
+ gz: "application/gzip",
207
+ "7z": "application/x-7z-compressed",
208
+ pdf: "application/pdf",
209
+ doc: "application/msword",
210
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
211
+ xls: "application/vnd.ms-excel",
212
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
213
+ ppt: "application/vnd.ms-powerpoint",
214
+ pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
215
+ exe: "application/vnd.microsoft.portable-executable",
216
+ apk: "application/vnd.android.package-archive"
217
+ };
218
+
219
+ //#endregion
220
+ //#region src/radix/index.ts
221
+ var Radix = class Radix {
222
+ value;
223
+ input;
224
+ children;
225
+ constructor(input) {
226
+ this.input = input;
227
+ this.children = {};
228
+ }
229
+ insert = (path, value) => {
230
+ let node = this;
231
+ for (let segment of path.split("/")) {
232
+ let input;
233
+ if (segment.startsWith(":")) {
234
+ input = segment.substring(1);
235
+ segment = ":";
236
+ }
237
+ let childNode = node.children[segment];
238
+ if (childNode === void 0) {
239
+ childNode = new Radix(input);
240
+ node.children[segment] = childNode;
241
+ }
242
+ node = childNode;
243
+ }
244
+ node.value = value;
245
+ return this;
246
+ };
247
+ match = (path) => {
248
+ let node = this;
249
+ const match = { inputs: {} };
250
+ for (const segment of path.split("/")) {
251
+ node = node?.children[segment] ?? node?.children[":"];
252
+ if (node?.input) match.inputs[node.input] = segment;
253
+ }
254
+ match.value = node?.value;
255
+ return match;
256
+ };
257
+ };
258
+
102
259
  //#endregion
103
260
  //#region src/signals/index.ts
104
261
  var StopEvent = class extends Event {
@@ -117,9 +274,7 @@ var Scope = class extends EventTarget {
117
274
  }
118
275
  getContext(input) {
119
276
  let scope = this;
120
- const seen = /* @__PURE__ */ new Set();
121
- while (scope && !seen.has(scope)) {
122
- seen.add(scope);
277
+ while (scope) {
123
278
  if (scope.context.has(input)) return scope.context.get(input);
124
279
  scope = scope.parentScope;
125
280
  }
@@ -184,11 +339,10 @@ function createState(value) {
184
339
  function createCompute(scope, invoke) {
185
340
  let previous = activeCompute;
186
341
  activeCompute = new Compute(scope, invoke);
187
- try {
188
- return invoke(activeCompute);
189
- } finally {
190
- activeCompute = previous;
191
- }
342
+ const result = invoke(activeCompute);
343
+ if (result instanceof Promise) return result.finally(() => activeCompute = previous);
344
+ activeCompute = previous;
345
+ return result;
192
346
  }
193
347
  function createMemo(scope, invoke) {
194
348
  let state;
@@ -211,71 +365,186 @@ let activeCompute;
211
365
  const targets = /* @__PURE__ */ new WeakMap();
212
366
 
213
367
  //#endregion
214
- //#region src/html/index.ts
215
- var MountedEvent = class extends Event {
216
- constructor() {
217
- super("mounted");
218
- }
368
+ //#region src/runtime/index.ts
369
+ const isRoute = (value) => {
370
+ return !!value && typeof value === "object" && "fetch" in value;
219
371
  };
220
- const isTemplate = (value) => {
221
- return !!value && typeof value === "object" && "tag" in value && "attributes" in value && "children" in value;
372
+ const useRuntime = (scope) => {
373
+ return scope.getContext(RUNTIME_CONTEXT);
222
374
  };
223
- const isCustomElement = (value) => {
224
- return !!value && typeof value === "object" && "component" in value;
375
+ const useRoute = (scope) => {
376
+ return scope.getContext(ROUTE_CONTEXT);
225
377
  };
226
- const isComponent = (value) => {
227
- return !!value && typeof value === "function" && "$name" in value;
378
+ const defineRoute = (route) => {
379
+ return route;
228
380
  };
229
- const useHost = (scope) => {
230
- return scope.getContext(HOST_CONTEXT);
381
+ const defineMiddleware = (middleware) => {
382
+ return middleware;
231
383
  };
232
- const createElement = (input, attributes, ...children) => {
233
- if (isComponent(input)) {
234
- const template = {
235
- tag: input.$name,
236
- attributes: attributes ?? {},
237
- children
238
- };
239
- if (input.$styles.length) {
240
- const classes = template.attributes["class"];
241
- template.attributes["class"] = (classes ? [classes, ...input.$styles] : input.$styles).join(" ");
242
- }
243
- return template;
244
- }
245
- if (typeof input === "string") return {
246
- tag: input,
247
- attributes: attributes ?? {},
248
- children
249
- };
250
- return input?.({
251
- ...attributes,
252
- children
253
- });
384
+ const fileName = (path) => {
385
+ return path.split("/").pop()?.split(".").slice(0, -1).join(".");
254
386
  };
255
- const toString = (slot) => {
256
- switch (typeof slot) {
257
- case "string":
258
- case "number":
259
- case "bigint":
260
- case "boolean":
261
- case "symbol": return slot.toString();
262
- case "object": return JSON.stringify(slot);
263
- case "function": return toString(slot());
264
- default: return "";
265
- }
387
+ const toPath = (value) => {
388
+ const path = (value.startsWith("/") ? value : "/" + value).replaceAll(/\/index/g, "").replaceAll(/\[(.*?)\]/g, (_, name) => ":" + name);
389
+ const route = path.startsWith("/") ? path : "/" + path;
390
+ const split = route.split(".");
391
+ return split.length === 3 ? [split.at(0), split.at(1)] : [split.at(0)];
266
392
  };
267
- const toArray = (hydration) => {
268
- if (Array.isArray(hydration)) return hydration.reduce((items, child) => items.concat(toArray(child)), new Array());
269
- else return [hydration];
393
+ const $fetch = async (scope, input, options) => {
394
+ const { request, variables } = useRuntime(scope);
395
+ let response;
396
+ if (request) {
397
+ const next = new Scope();
398
+ const url = new URL(input.toString(), request.url);
399
+ next.setContext(RUNTIME_CONTEXT, {
400
+ tasks: new Array(),
401
+ request: new Request(url, options),
402
+ response: { headers: new Headers() },
403
+ variables
404
+ });
405
+ const previous = new URL(request.url);
406
+ if (url.origin === previous.origin) response = await (await import("#virtual/runtime")).runtime.fetch(next);
407
+ }
408
+ response ??= await fetch(input, options);
409
+ if (response.ok === false) throw response;
410
+ const contentType = response.headers.get("Content-Type")?.split(";").shift() ?? "";
411
+ switch (contentType) {
412
+ case "application/json": return response.json();
413
+ default: return response;
414
+ }
270
415
  };
271
- const toRange = (hydration) => {
272
- const items = toArray(hydration);
273
- const range = document.createRange();
274
- const firstNode = items.at(0);
275
- if (firstNode) range.setStartBefore(firstNode);
276
- const lastNode = items.at(-1);
277
- if (lastNode) range.setEndAfter(lastNode);
278
- return range;
416
+ const useAsync = (scope, invoke) => {
417
+ const { tasks } = useRuntime(scope);
418
+ const state = createState();
419
+ const isLoading = createState(true);
420
+ const task = invoke().then((value) => state.value = value).finally(() => isLoading.value = false);
421
+ if (isServer()) tasks.push(task);
422
+ return {
423
+ state,
424
+ isLoading
425
+ };
426
+ };
427
+ const createRuntime = async () => {
428
+ const radix = new Radix();
429
+ const middlewares = new Array();
430
+ const routes = await import("#virtual/routes").then((module) => module.default);
431
+ for (const path in routes) {
432
+ const [name, method] = toPath(path);
433
+ radix.insert((method ?? "GET").toUpperCase() + name, defineRoute({ fetch: async (event) => {
434
+ const route = routes[path];
435
+ if (isRoute(route)) return route.fetch(event);
436
+ return sendHtml(event, await renderToString(event, await import("#virtual/client").then((module) => module.client)));
437
+ } }));
438
+ }
439
+ const assets = await import("#virtual/assets").then((module) => module.default);
440
+ for (const path in assets) radix.insert("GET/" + path, defineRoute({ fetch: async (scope) => {
441
+ const { response } = useRuntime(scope);
442
+ let content = assets[path];
443
+ if (content) {
444
+ if (path.endsWith(".png")) {
445
+ const [_, base64] = content.split(",");
446
+ const binary = atob(base64.replace(/-/g, "+").replace(/_/g, "/"));
447
+ const bytes = new Uint8Array(binary.length);
448
+ for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
449
+ content = bytes;
450
+ }
451
+ }
452
+ response.headers.set("Content-Type", mimeType(path));
453
+ return new Response(content, response);
454
+ } }));
455
+ const invoke = (scope, route, index) => {
456
+ return middlewares.at(index)?.fetch(scope, () => invoke(scope, route, index + 1)) ?? route.fetch(scope);
457
+ };
458
+ return {
459
+ radix,
460
+ middlewares,
461
+ fetch: async (scope) => {
462
+ const { request } = useRuntime(scope);
463
+ const { pathname } = useRequestUrl(scope);
464
+ const { value: route, inputs } = radix.match(request.method + pathname);
465
+ try {
466
+ scope.setContext(ROUTE_CONTEXT, { inputs: createState(inputs) });
467
+ if (route) {
468
+ const response = await invoke(scope, route, 0);
469
+ if (response) return response;
470
+ }
471
+ return sendText(scope, "NOT_FOUND");
472
+ } catch (exception) {
473
+ if (exception instanceof Response) return exception;
474
+ throw exception;
475
+ }
476
+ }
477
+ };
478
+ };
479
+ const RUNTIME_CONTEXT = defineContext("RUNTIME_CONTEXT");
480
+ const ROUTE_CONTEXT = defineContext("ROUTE_CONTEXT");
481
+
482
+ //#endregion
483
+ //#region src/html/index.ts
484
+ var MountedEvent = class extends Event {
485
+ constructor() {
486
+ super("mounted");
487
+ }
488
+ };
489
+ const isTemplate = (value) => {
490
+ return !!value && typeof value === "object" && "tag" in value && "attributes" in value && "children" in value;
491
+ };
492
+ const isCustomElement = (value) => {
493
+ return !!value && typeof value === "object" && "component" in value;
494
+ };
495
+ const isComponent = (value) => {
496
+ return !!value && typeof value === "function" && "$name" in value;
497
+ };
498
+ const useHost = (scope) => {
499
+ return scope.getContext(HOST_CONTEXT);
500
+ };
501
+ const createElement = (input, attributes, ...children) => {
502
+ if (isComponent(input)) {
503
+ const template = {
504
+ tag: input.$name,
505
+ attributes: attributes ?? {},
506
+ children
507
+ };
508
+ if (input.$styles.length) {
509
+ const classes = template.attributes["class"];
510
+ template.attributes["class"] = (classes ? [classes, ...input.$styles] : input.$styles).join(" ");
511
+ }
512
+ return template;
513
+ }
514
+ if (typeof input === "string") return {
515
+ tag: input,
516
+ attributes: attributes ?? {},
517
+ children
518
+ };
519
+ return input?.({
520
+ ...attributes,
521
+ children
522
+ });
523
+ };
524
+ const toString = (slot) => {
525
+ switch (typeof slot) {
526
+ case "string":
527
+ case "number":
528
+ case "bigint":
529
+ case "boolean":
530
+ case "symbol": return slot.toString();
531
+ case "object": return JSON.stringify(slot);
532
+ case "function": return toString(slot());
533
+ default: return "";
534
+ }
535
+ };
536
+ const toArray = (hydration) => {
537
+ if (Array.isArray(hydration)) return hydration.reduce((items, child) => items.concat(toArray(child)), new Array());
538
+ else return [hydration];
539
+ };
540
+ const toRange = (hydration) => {
541
+ const items = toArray(hydration);
542
+ const range = document.createRange();
543
+ const firstNode = items.at(0);
544
+ if (firstNode) range.setStartBefore(firstNode);
545
+ const lastNode = items.at(-1);
546
+ if (lastNode) range.setEndAfter(lastNode);
547
+ return range;
279
548
  };
280
549
  const toFragment = (hydration) => {
281
550
  return toArray(hydration).reduce((fragment, node) => {
@@ -283,20 +552,20 @@ const toFragment = (hydration) => {
283
552
  return fragment;
284
553
  }, document.createDocumentFragment());
285
554
  };
286
- const hydrate = async (scope, parentNode, slot, index, previous) => {
555
+ const hydrate = (scope, parentNode, slot, index, previous) => {
287
556
  let hydration = parentNode.childNodes.item(index);
288
557
  if (Array.isArray(slot)) {
289
558
  const items = new Array();
290
- for (const [index$1, childSlot] of slot.entries()) items.push(await hydrate(scope, parentNode, childSlot, index$1, previous));
559
+ for (const [index$1, childSlot] of slot.entries()) items.push(hydrate(scope, parentNode, childSlot, index$1, previous));
291
560
  if (items.length) hydration = items;
292
561
  else if (previous || !(hydration instanceof Comment)) hydration = document.createComment("");
293
562
  }
294
563
  if (typeof slot === "function") {
295
564
  let previous$1;
296
- await createCompute(scope, async (scope$1) => {
565
+ createCompute(scope, (scope$1) => {
297
566
  let input = slot;
298
- while (typeof input === "function") input = await input();
299
- hydration = await hydrate(scope$1, parentNode, input, index, previous$1);
567
+ while (typeof input === "function") input = input();
568
+ hydration = hydrate(scope$1, parentNode, input, index, previous$1);
300
569
  if (previous$1 && hydration !== previous$1) if (Array.isArray(hydration)) if (Array.isArray(previous$1)) {
301
570
  const range = toRange(previous$1);
302
571
  range.deleteContents();
@@ -331,13 +600,14 @@ const hydrate = async (scope, parentNode, slot, index, previous) => {
331
600
  return target.setAttribute(name, set);
332
601
  }
333
602
  });
334
- for (const [index$1, childSlot] of slot.children.entries()) await hydrate(scope, hydration, childSlot, index$1, previous);
603
+ for (const [index$1, childSlot] of slot.children.entries()) hydrate(scope, hydration, childSlot, index$1, previous);
335
604
  }
336
605
  hydration ??= document.createComment("");
337
606
  if (parentNode.childNodes.item(index) === null) parentNode.appendChild(toFragment(hydration));
338
607
  return hydration;
339
608
  };
340
609
  const renderToString = async (scope, slot) => {
610
+ const { tasks } = useRuntime(scope);
341
611
  if (typeof slot === "number" || typeof slot === "bigint" || typeof slot === "boolean" || typeof slot === "string" || typeof slot === "symbol") return slot.toString();
342
612
  if (typeof slot === "function") {
343
613
  let input = slot;
@@ -359,7 +629,9 @@ const renderToString = async (scope, slot) => {
359
629
  let content = `<${prefix}>`;
360
630
  if (CustomElement) {
361
631
  const element = new CustomElement(slot.attributes, scope);
362
- const template = await renderToString(element.scope, await element.setup());
632
+ const result = element.setup();
633
+ while (tasks.length) await tasks.shift();
634
+ const template = await renderToString(element.scope, result);
363
635
  if (element.shadowRoot) {
364
636
  const shadow = {
365
637
  tag: "template",
@@ -405,9 +677,16 @@ const defineComponent = (options) => {
405
677
  const toCustomElement = (Component) => {
406
678
  return class extends HTMLElement {
407
679
  static formAssociated = true;
408
- component = new Component();
409
- internals = this.attachInternals();
410
- async connectedCallback() {
680
+ component;
681
+ constructor() {
682
+ super();
683
+ this.component = new Component();
684
+ this.component.scope.setContext(HOST_CONTEXT, {
685
+ host: this,
686
+ internals: this.attachInternals()
687
+ });
688
+ }
689
+ connectedCallback() {
411
690
  let rootNode = this;
412
691
  const findParentScope = (node) => {
413
692
  if (node) {
@@ -425,11 +704,7 @@ const toCustomElement = (Component) => {
425
704
  detail: value
426
705
  }));
427
706
  });
428
- this.component.scope.setContext(HOST_CONTEXT, {
429
- host: this,
430
- internals: this.internals
431
- });
432
- await hydrate(this.component.scope, rootNode, await this.component.setup(), 0);
707
+ hydrate(this.component.scope, rootNode, this.component.setup(), 0);
433
708
  requestAnimationFrame(() => this.dispatchEvent(new MountedEvent()));
434
709
  }
435
710
  attributeChangedCallback(name, oldValue, value) {
@@ -498,266 +773,6 @@ const stopImmediatePropagation = (event) => event.stopImmediatePropagation();
498
773
  const components = /* @__PURE__ */ new Map();
499
774
  const HOST_CONTEXT = defineContext("HOST_CONTEXT");
500
775
 
501
- //#endregion
502
- //#region src/radix/index.ts
503
- var Radix = class Radix {
504
- value;
505
- input;
506
- children;
507
- constructor(input) {
508
- this.input = input;
509
- this.children = {};
510
- }
511
- insert = (path, value) => {
512
- let node = this;
513
- for (let segment of path.split("/")) {
514
- let input;
515
- if (segment.startsWith(":")) {
516
- input = segment.substring(1);
517
- segment = ":";
518
- }
519
- let childNode = node.children[segment];
520
- if (childNode === void 0) {
521
- childNode = new Radix(input);
522
- node.children[segment] = childNode;
523
- }
524
- node = childNode;
525
- }
526
- node.value = value;
527
- return this;
528
- };
529
- match = (path) => {
530
- let node = this;
531
- const match = { inputs: {} };
532
- for (const segment of path.split("/")) {
533
- node = node?.children[segment] ?? node?.children[":"];
534
- if (node?.input) match.inputs[node.input] = segment;
535
- }
536
- match.value = node?.value;
537
- return match;
538
- };
539
- };
540
-
541
- //#endregion
542
- //#region src/runtime/index.ts
543
- const isRoute = (value) => {
544
- return !!value && typeof value === "object" && "fetch" in value;
545
- };
546
- const useRuntime = (scope) => {
547
- return scope.getContext(RUNTIME_CONTEXT);
548
- };
549
- const useRoute = (scope) => {
550
- return scope.getContext(ROUTE_CONTEXT);
551
- };
552
- const defineRoute = (route) => {
553
- return route;
554
- };
555
- const defineMiddleware = (middleware) => {
556
- return middleware;
557
- };
558
- const fileName = (path) => {
559
- return path.split("/").pop()?.split(".").slice(0, -1).join(".");
560
- };
561
- const toPath = (value) => {
562
- const path = (value.startsWith("/") ? value : "/" + value).replaceAll(/\/index/g, "").replaceAll(/\[(.*?)\]/g, (_, name) => ":" + name);
563
- const route = path.startsWith("/") ? path : "/" + path;
564
- const split = route.split(".");
565
- return split.length === 3 ? [split.at(0), split.at(1)] : [split.at(0)];
566
- };
567
- const $fetch = async (scope, input, options) => {
568
- const { request, variables } = useRuntime(scope);
569
- let response;
570
- if (request) {
571
- const next = new Scope();
572
- const url = new URL(input.toString(), request.url);
573
- next.setContext(RUNTIME_CONTEXT, {
574
- request: new Request(url, options),
575
- response: { headers: new Headers() },
576
- variables
577
- });
578
- const previous = new URL(request.url);
579
- if (url.origin === previous.origin) response = await (await import("#virtual/runtime")).runtime.fetch(next);
580
- }
581
- response ??= await fetch(input, options);
582
- if (response.ok === false) throw response;
583
- const contentType = response.headers.get("Content-Type")?.split(";").shift() ?? "";
584
- switch (contentType) {
585
- case "application/json": return response.json();
586
- default: return response;
587
- }
588
- };
589
- const createRuntime = async () => {
590
- const radix = new Radix();
591
- const middlewares = new Array();
592
- const routes = await import("#virtual/routes").then((module) => module.routes);
593
- for (const path in routes) {
594
- const [name, method] = toPath(path);
595
- radix.insert((method ?? "GET").toUpperCase() + name, defineRoute({ fetch: async (event) => {
596
- const route = await routes[path]?.();
597
- if (isRoute(route)) return route.fetch(event);
598
- return sendHtml(event, await renderToString(event, await import("#virtual/client").then((module) => module.client)));
599
- } }));
600
- }
601
- const assets = await import("#virtual/assets").then((module) => module.assets);
602
- for (const path in assets) radix.insert("GET/" + path, defineRoute({ fetch: async (scope) => {
603
- const { response } = useRuntime(scope);
604
- let content = await assets[path]?.();
605
- if (content) {
606
- if (path.endsWith(".png")) {
607
- const [_, base64] = content.split(",");
608
- const binary = atob(base64.replace(/-/g, "+").replace(/_/g, "/"));
609
- const bytes = new Uint8Array(binary.length);
610
- for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
611
- content = bytes;
612
- }
613
- }
614
- response.headers.set("Content-Type", mimeType(path));
615
- return new Response(content, response);
616
- } }));
617
- const invoke = (scope, route, index) => {
618
- return middlewares.at(index)?.fetch(scope, () => invoke(scope, route, index + 1)) ?? route.fetch(scope);
619
- };
620
- return {
621
- radix,
622
- middlewares,
623
- fetch: async (scope) => {
624
- const { request } = useRuntime(scope);
625
- const { pathname } = useRequestUrl(scope);
626
- const { value: route, inputs } = radix.match(request.method + pathname);
627
- try {
628
- scope.setContext(ROUTE_CONTEXT, { inputs: createState(inputs) });
629
- if (route) {
630
- const response = await invoke(scope, route, 0);
631
- if (response) return response;
632
- }
633
- return sendText(scope, "NOT_FOUND");
634
- } catch (exception) {
635
- if (exception instanceof Response) return exception;
636
- throw exception;
637
- }
638
- }
639
- };
640
- };
641
- const RUNTIME_CONTEXT = defineContext("RUNTIME_CONTEXT");
642
- const ROUTE_CONTEXT = defineContext("ROUTE_CONTEXT");
643
-
644
- //#endregion
645
- //#region src/http/index.ts
646
- const sendText = (scope, text) => {
647
- const { response } = useRuntime(scope);
648
- response.headers.set("Content-Type", "text/plain");
649
- return new Response(text, response);
650
- };
651
- const sendHtml = (scope, text) => {
652
- const { response } = useRuntime(scope);
653
- response.headers.set("Content-Type", "text/html");
654
- return new Response(text, response);
655
- };
656
- const sendJson = (scope, value) => {
657
- const { response } = useRuntime(scope);
658
- response.headers.set("Content-Type", "application/json");
659
- return new Response(JSON.stringify(value), response);
660
- };
661
- const sendRedirect = (scope, path) => {
662
- const { response } = useRuntime(scope);
663
- response.status = 302;
664
- response.headers.set("Location", path);
665
- return new Response(null, response);
666
- };
667
- const sendBadRequest = (scope, text) => {
668
- const { response } = useRuntime(scope);
669
- response.status = 400;
670
- return new Response(text, response);
671
- };
672
- const sendUnauthorized = (scope) => {
673
- const { response } = useRuntime(scope);
674
- response.status = 401;
675
- return new Response(null, response);
676
- };
677
- const useRequestUrl = (scope, base) => {
678
- const { request } = useRuntime(scope);
679
- return new URL(request.url, base);
680
- };
681
- const useCookies = (scope) => {
682
- const { request } = useRuntime(scope);
683
- return (isClient() ? document.cookie : request.headers.get("Cookie") ?? "").split("; ").reduce((result, cookie) => {
684
- const [name, value] = cookie.split("=");
685
- if (name && value) result[name] = decodeURIComponent(value);
686
- return result;
687
- }, {});
688
- };
689
- const useSetCookies = (scope) => {
690
- const { request } = useRuntime(scope);
691
- return request.headers.getSetCookie().reduce((result, cookie) => {
692
- const [name, value] = cookie.split("=");
693
- if (name && value) result[name] = decodeURIComponent(value);
694
- return result;
695
- }, {});
696
- };
697
- const setCookie = (scope, name, value, options) => {
698
- const { response } = useRuntime(scope);
699
- let cookie = name + "=" + encodeURIComponent(value);
700
- if (options?.domain) cookie += `; Domain=${options.domain}`;
701
- if (options?.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
702
- if (options?.httpOnly) cookie += `; HttpOnly`;
703
- if (options?.maxAge) cookie += `; Max-Age=${options.maxAge}`;
704
- if (options?.path) cookie += `; Path=${options.path}`;
705
- if (options?.priority) cookie += `; Priority=${options.priority}`;
706
- if (options?.sameSite) cookie += `; SameSite=${options.sameSite}`;
707
- if (options?.secure) cookie += `; Secure`;
708
- if (isClient()) document.cookie = cookie;
709
- else response.headers.append("Set-Cookie", cookie);
710
- };
711
- const mimeType = (file) => {
712
- const extension = /\.([a-zA-Z0-9]+?)$/.exec(file)?.at(1);
713
- return mimeTypes[extension ?? ""] ?? "text/plain";
714
- };
715
- const mimeTypes = {
716
- txt: "text/plain",
717
- css: "text/css",
718
- html: "text/html",
719
- htm: "text/html",
720
- js: "text/javascript",
721
- json: "application/json",
722
- xml: "application/xml",
723
- csv: "text/csv",
724
- jpg: "image/jpeg",
725
- jpeg: "image/jpeg",
726
- png: "image/png",
727
- gif: "image/gif",
728
- webp: "image/webp",
729
- svg: "image/svg+xml",
730
- bmp: "image/bmp",
731
- ico: "image/x-icon",
732
- ttf: "font/ttf",
733
- otf: "font/otf",
734
- woff: "font/woff",
735
- woff2: "font/woff2",
736
- mp3: "audio/mpeg",
737
- wav: "audio/wav",
738
- ogg: "audio/ogg",
739
- m4a: "audio/mp4",
740
- mp4: "video/mp4",
741
- webm: "video/webm",
742
- ogv: "video/ogg",
743
- mov: "video/quicktime",
744
- avi: "video/x-msvideo",
745
- zip: "application/zip",
746
- rar: "application/vnd.rar",
747
- tar: "application/x-tar",
748
- gz: "application/gzip",
749
- "7z": "application/x-7z-compressed",
750
- pdf: "application/pdf",
751
- doc: "application/msword",
752
- docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
753
- xls: "application/vnd.ms-excel",
754
- xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
755
- ppt: "application/vnd.ms-powerpoint",
756
- pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
757
- exe: "application/vnd.microsoft.portable-executable",
758
- apk: "application/vnd.android.package-archive"
759
- };
760
-
761
776
  //#endregion
762
777
  //#region src/router/index.tsx
763
778
  var NavigateEvent = class extends Event {
@@ -765,12 +780,11 @@ var NavigateEvent = class extends Event {
765
780
  super("navigate");
766
781
  }
767
782
  };
768
- const ROUTER_CONTEXT = defineContext("ROUTER_CONTEXT");
769
- const createRouter = (options) => {
770
- const navigator = new EventTarget();
771
- const radix = new Radix();
783
+ const provideRouterContext = (scope, options) => {
772
784
  const url = createState();
773
785
  const route = createState();
786
+ const radix = new Radix();
787
+ const navigator = new EventTarget();
774
788
  for (const path in options.routes) {
775
789
  const [name] = toPath(path);
776
790
  if (name) {
@@ -778,31 +792,26 @@ const createRouter = (options) => {
778
792
  if (value) radix.insert(name, value);
779
793
  }
780
794
  }
781
- const registerRouterContext = async (scope) => {
782
- const fetch$1 = async () => {
783
- const { inputs } = useRoute(scope);
784
- const { request } = useRuntime(scope);
785
- url.value = new URL(request?.url ?? window?.location.href);
786
- const match = radix.match(url.value.pathname);
787
- inputs.value = match.inputs;
788
- await startViewTransition(async () => route.value = await match.value?.());
789
- };
790
- if (isClient()) useEvent(scope, window, "popstate", () => navigator.dispatchEvent(new NavigateEvent()));
791
- scope.setContext(ROUTE_CONTEXT, { inputs: createState() });
792
- scope.setContext(ROUTER_CONTEXT, {
793
- options,
794
- navigator,
795
- url,
796
- radix,
797
- route
798
- });
799
- await fetch$1().then(() => useEvent(scope, navigator, "navigate", fetch$1));
800
- return useRouter(scope);
801
- };
802
- return {
803
- ROUTER_CONTEXT,
804
- registerRouterContext
795
+ const fetch$1 = () => {
796
+ const { inputs } = useRoute(scope);
797
+ const { request } = useRuntime(scope);
798
+ url.value = new URL(request?.url ?? window?.location.href);
799
+ const match = radix.match(url.value.pathname);
800
+ inputs.value = match.inputs;
801
+ route.value = match.value;
805
802
  };
803
+ if (isClient()) useEvent(scope, window, "popstate", () => navigator.dispatchEvent(new NavigateEvent()));
804
+ scope.setContext(ROUTE_CONTEXT, { inputs: createState() });
805
+ scope.setContext(ROUTER_CONTEXT, {
806
+ options,
807
+ navigator,
808
+ url,
809
+ radix,
810
+ route
811
+ });
812
+ fetch$1();
813
+ useEvent(scope, navigator, "navigate", async () => await startViewTransition(fetch$1));
814
+ return useRouter(scope);
806
815
  };
807
816
  const useRouter = (scope, context) => {
808
817
  const { url, route, navigator } = scope.getContext(context ?? ROUTER_CONTEXT);
@@ -831,38 +840,33 @@ const Page = defineComponent({
831
840
  return () => route.value;
832
841
  }
833
842
  });
843
+ const ROUTER_CONTEXT = defineContext("ROUTER_CONTEXT");
834
844
 
835
845
  //#endregion
836
846
  //#region src/locale/index.ts
837
- const LOCALE_CONTEXT = defineContext("LOCALE_CONTEXT");
838
- const createLocale = (options) => {
847
+ const provideLocaleContext = (scope, options) => {
848
+ const { inputs } = useRoute(scope);
849
+ const { navigator } = useRouter(scope);
839
850
  const locale = createState(options.defaultLocale);
840
851
  const messages = createState();
841
- const registerLocaleContext = async (scope) => {
842
- const { inputs } = useRoute(scope);
843
- const { navigator } = useRouter(scope);
844
- const fetch$1 = async () => {
845
- if (options.input) {
846
- const input = inputs.value[options.input];
847
- if (input && input in options.locales) locale.value = input;
848
- }
849
- if (locale.value) {
850
- const target = options.locales[locale.value];
851
- messages.value = typeof target === "function" ? await target() : target;
852
- }
853
- };
854
- await fetch$1().then(() => useEvent(scope, navigator, "navigate", fetch$1));
855
- scope.setContext(LOCALE_CONTEXT, {
856
- locale,
857
- messages,
858
- options
859
- });
860
- return useLocale(scope);
861
- };
862
- return {
863
- LOCALE_CONTEXT,
864
- registerLocaleContext
852
+ const fetch$1 = () => {
853
+ if (options.input) {
854
+ const input = inputs.value[options.input];
855
+ if (input && input in options.locales) locale.value = input;
856
+ }
857
+ if (locale.value) {
858
+ const target = options.locales[locale.value];
859
+ messages.value = target;
860
+ }
865
861
  };
862
+ fetch$1();
863
+ useEvent(scope, navigator, "navigate", fetch$1);
864
+ scope.setContext(LOCALE_CONTEXT, {
865
+ locale,
866
+ messages,
867
+ options
868
+ });
869
+ return useLocale(scope);
866
870
  };
867
871
  const useLocale = (scope, context) => {
868
872
  const { locale, messages } = scope.getContext(context ?? LOCALE_CONTEXT);
@@ -880,6 +884,7 @@ const useLocale = (scope, context) => {
880
884
  date
881
885
  };
882
886
  };
887
+ const LOCALE_CONTEXT = defineContext("LOCALE_CONTEXT");
883
888
 
884
889
  //#endregion
885
- export { $fetch, CLIENT, Compute, HOST_CONTEXT, Handler, LOCALE_CONTEXT, MountedEvent, NavigateEvent, Page, ROUTER_CONTEXT, ROUTE_CONTEXT, RUNTIME_CONTEXT, Radix, SERVER, Scope, StopEvent, activeCompute, components, createApp, createCompute, createElement, createLocale, createMemo, createRouter, createRuntime, createState, defineComponent, defineContext, defineMiddleware, defineRoute, fileName, fromValue, hydrate, isClient, isComponent, isCustomElement, isRoute, isServer, isTemplate, mimeType, onMounted, preventDefault, registerComponent, renderToString, sendBadRequest, sendHtml, sendJson, sendRedirect, sendText, sendUnauthorized, setCookie, startViewTransition, stopImmediatePropagation, stopPropagation, targets, toArray, toCustomElement, toFragment, toPath, toRange, toString, useCookies, useEvent, useHost, useLocale, useRequestUrl, useRoute, useRouter, useRuntime, useSetCookies };
890
+ export { $fetch, CLIENT, Compute, HOST_CONTEXT, Handler, LOCALE_CONTEXT, MountedEvent, NavigateEvent, Page, ROUTER_CONTEXT, ROUTE_CONTEXT, RUNTIME_CONTEXT, Radix, SERVER, Scope, StopEvent, activeCompute, components, createApp, createCompute, createElement, createMemo, createRuntime, createState, defineComponent, defineContext, defineMiddleware, defineRoute, fileName, fromValue, hydrate, isClient, isComponent, isCustomElement, isRoute, isServer, isTemplate, mimeType, onMounted, preventDefault, provideLocaleContext, provideRouterContext, registerComponent, renderToString, sendBadRequest, sendHtml, sendJson, sendRedirect, sendText, sendUnauthorized, setCookie, startViewTransition, stopImmediatePropagation, stopPropagation, targets, toArray, toCustomElement, toFragment, toPath, toRange, toString, useAsync, useCookies, useEvent, useHost, useLocale, useRequestUrl, useRoute, useRouter, useRuntime, useSetCookies };
package/dist/jsx/index.js CHANGED
@@ -3,6 +3,11 @@ function defineContext(key) {
3
3
  return key;
4
4
  }
5
5
 
6
+ //#endregion
7
+ //#region src/runtime/index.ts
8
+ const RUNTIME_CONTEXT = defineContext("RUNTIME_CONTEXT");
9
+ const ROUTE_CONTEXT = defineContext("ROUTE_CONTEXT");
10
+
6
11
  //#endregion
7
12
  //#region src/html/index.ts
8
13
  const isComponent = (value) => {
@@ -1,24 +1,19 @@
1
1
  import { type Descriptor, Scope, type State } from "../signals";
2
- export type Locales = Record<string, Record<string, string> | (() => Promise<Record<string, string>>)>;
3
- export type LocaleOptions<T extends Locales = Locales> = {
4
- locales: T;
5
- defaultLocale?: keyof T;
2
+ export type LocaleOptions = {
3
+ locales: Record<string, Record<string, string>>;
4
+ defaultLocale?: string;
6
5
  input?: string;
7
6
  };
8
- export type LocaleContext<T extends LocaleOptions = LocaleOptions> = {
7
+ export type LocaleContext = {
9
8
  locale: State<string | undefined>;
10
9
  messages: State<Record<string, string> | undefined>;
11
- options: T;
10
+ options: LocaleOptions;
12
11
  };
13
- export declare const LOCALE_CONTEXT: Descriptor<LocaleContext<LocaleOptions<Locales>>>;
14
- export declare const createLocale: <T extends LocaleOptions>(options: T) => {
15
- LOCALE_CONTEXT: Descriptor<LocaleContext<T>>;
16
- registerLocaleContext: (scope: Scope) => Promise<{
17
- locale: State<string | undefined>;
18
- messages: State<Record<string, string> | undefined>;
19
- $: (key: never) => () => string | number | symbol;
20
- date: (date?: Date, options?: Intl.DateTimeFormatOptions) => string | undefined;
21
- }>;
12
+ export declare const provideLocaleContext: (scope: Scope, options: LocaleOptions) => {
13
+ locale: State<string | undefined>;
14
+ messages: State<Record<string, string> | undefined>;
15
+ $: (key: string) => () => string | number | symbol;
16
+ date: (date?: Date, options?: Intl.DateTimeFormatOptions) => string | undefined;
22
17
  };
23
18
  export declare const useLocale: <T extends LocaleContext>(scope: Scope, context?: Descriptor<T>) => {
24
19
  locale: State<string | undefined>;
@@ -26,3 +21,4 @@ export declare const useLocale: <T extends LocaleContext>(scope: Scope, context?
26
21
  $: (key: keyof T["options"]["locales"][keyof T["options"]["locales"]]) => () => string | number | symbol;
27
22
  date: (date?: Date, options?: Intl.DateTimeFormatOptions) => string | undefined;
28
23
  };
24
+ export declare const LOCALE_CONTEXT: Descriptor<LocaleContext>;
@@ -1,20 +1,17 @@
1
- import { RUNTIME_CONTEXT, Scope } from "./runtime-plDYfDhw.js";
1
+ import { RUNTIME_CONTEXT, Scope } from "./runtime-Daz6s680.js";
2
2
  import { serve } from "bun";
3
3
  import { runtime } from "#virtual/runtime";
4
4
 
5
5
  //#region src/presets/bun.ts
6
- serve({ fetch: (request) => {
6
+ serve({ fetch: async (request) => {
7
7
  const scope = new Scope();
8
8
  scope.setContext(RUNTIME_CONTEXT, {
9
+ tasks: new Array(),
9
10
  request,
10
11
  response: { headers: new Headers() },
11
12
  variables: process.env
12
13
  });
13
- try {
14
- return runtime.fetch(scope);
15
- } finally {
16
- scope.stop();
17
- }
14
+ return await runtime.fetch(scope).finally(() => scope.stop());
18
15
  } });
19
16
 
20
17
  //#endregion
@@ -1,19 +1,16 @@
1
- import { RUNTIME_CONTEXT, Scope } from "./runtime-plDYfDhw.js";
1
+ import { RUNTIME_CONTEXT, Scope } from "./runtime-Daz6s680.js";
2
2
  import { runtime } from "#virtual/runtime";
3
3
 
4
4
  //#region src/presets/cloudflare.ts
5
- var cloudflare_default = { fetch: (request, variables) => {
5
+ var cloudflare_default = { fetch: async (request, variables) => {
6
6
  const scope = new Scope();
7
7
  scope.setContext(RUNTIME_CONTEXT, {
8
+ tasks: new Array(),
8
9
  request,
9
10
  response: { headers: new Headers() },
10
11
  variables
11
12
  });
12
- try {
13
- return runtime.fetch(scope);
14
- } finally {
15
- scope.stop();
16
- }
13
+ return await runtime.fetch(scope).finally(() => scope.stop());
17
14
  } };
18
15
 
19
16
  //#endregion
@@ -15,9 +15,7 @@ var Scope = class extends EventTarget {
15
15
  }
16
16
  getContext(input) {
17
17
  let scope = this;
18
- const seen = /* @__PURE__ */ new Set();
19
- while (scope && !seen.has(scope)) {
20
- seen.add(scope);
18
+ while (scope) {
21
19
  if (scope.context.has(input)) return scope.context.get(input);
22
20
  scope = scope.parentScope;
23
21
  }
@@ -1,30 +1,25 @@
1
1
  import { type Slot } from "../html";
2
2
  import { Radix } from "../radix";
3
3
  import { type Descriptor, Scope, type State } from "../signals";
4
- export type Routes = Record<string, () => Promise<Slot>>;
5
- export type RouterOptions<T extends Routes = Routes> = {
6
- routes: T;
4
+ export type RouterOptions = {
5
+ routes: Record<string, Slot>;
7
6
  };
8
- export type RouterContext<T extends RouterOptions = RouterOptions> = {
9
- options: T;
10
- navigator: EventTarget;
7
+ export type RouterContext = {
11
8
  url: State<URL | undefined>;
12
- radix: Radix<() => Promise<Slot>>;
13
9
  route: State<Slot | undefined>;
10
+ radix: Radix<Slot>;
11
+ navigator: EventTarget;
12
+ options: RouterOptions;
14
13
  };
15
14
  export declare class NavigateEvent extends Event {
16
15
  constructor();
17
16
  }
18
- export declare const ROUTER_CONTEXT: Descriptor<RouterContext<RouterOptions<Routes>>>;
19
- export declare const createRouter: <T extends RouterOptions>(options: T) => {
20
- ROUTER_CONTEXT: Descriptor<RouterContext<T>>;
21
- registerRouterContext: (scope: Scope) => Promise<{
22
- url: State<URL | undefined>;
23
- route: State<unknown>;
24
- navigator: EventTarget;
25
- navigate: (path: string) => void;
26
- anchorNavigate: (event: Event) => void;
27
- }>;
17
+ export declare const provideRouterContext: (scope: Scope, options: RouterOptions) => {
18
+ url: State<URL | undefined>;
19
+ route: State<unknown>;
20
+ navigator: EventTarget;
21
+ navigate: (path: string) => void;
22
+ anchorNavigate: (event: Event) => void;
28
23
  };
29
24
  export declare const useRouter: <T extends RouterContext>(scope: Scope, context?: Descriptor<T>) => {
30
25
  url: State<URL | undefined>;
@@ -34,6 +29,7 @@ export declare const useRouter: <T extends RouterContext>(scope: Scope, context?
34
29
  anchorNavigate: (event: Event) => void;
35
30
  };
36
31
  export declare const Page: import("..").ComponentConstructor<import("..").Events, import("..").Attributes>;
32
+ export declare const ROUTER_CONTEXT: Descriptor<RouterContext>;
37
33
  declare global {
38
34
  interface ElementEventMap {
39
35
  navigate: NavigateEvent;
@@ -13,6 +13,7 @@ export type Runtime = {
13
13
  fetch: (scope: Scope) => Promise<Response>;
14
14
  };
15
15
  export type RuntimeContext<T = Record<string, unknown>> = {
16
+ tasks: Array<Promise<unknown>>;
16
17
  request: Request;
17
18
  response: ResponseOptions;
18
19
  variables: T;
@@ -28,6 +29,10 @@ export declare const defineMiddleware: (middleware: Middleware) => Middleware;
28
29
  export declare const fileName: (path: string) => string | undefined;
29
30
  export declare const toPath: (value: string) => (string | undefined)[];
30
31
  export declare const $fetch: <T>(scope: Scope, input: string | URL, options?: RequestInit) => Promise<T>;
32
+ export declare const useAsync: <T>(scope: Scope, invoke: () => Promise<T>) => {
33
+ state: State<T | undefined>;
34
+ isLoading: State<boolean>;
35
+ };
31
36
  export declare const createRuntime: () => Promise<Runtime>;
32
37
  export declare const RUNTIME_CONTEXT: import("..").Descriptor<RuntimeContext<Record<string, unknown>>>;
33
38
  export declare const ROUTE_CONTEXT: import("..").Descriptor<RouteContext>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "revojs",
3
- "version": "0.0.62",
3
+ "version": "0.0.64",
4
4
  "type": "module",
5
5
  "repository": "coverbase/revojs",
6
6
  "license": "MIT",
@@ -1,9 +1,7 @@
1
- declare module "#virtual/locales" {
2
- export const locales: Record<string, () => Promise<Record<string, string>>>;
3
- }
1
+ declare module "#virtual/runtime" {
2
+ import type { Runtime } from "revojs";
4
3
 
5
- declare module "#virtual/assets" {
6
- export const assets: Record<string, () => Promise<string>>;
4
+ export const runtime: Runtime;
7
5
  }
8
6
 
9
7
  declare module "#virtual/client" {
@@ -15,11 +13,19 @@ declare module "#virtual/client" {
15
13
  declare module "#virtual/routes" {
16
14
  import type { Route, Slot } from "revojs";
17
15
 
18
- export const routes: Record<string, () => Promise<Route | Slot>>;
16
+ const routes: Record<string, Route | Slot>;
17
+
18
+ export default routes;
19
19
  }
20
20
 
21
- declare module "#virtual/runtime" {
22
- import type { Runtime } from "revojs";
21
+ declare module "#virtual/locales" {
22
+ const locales: Record<string, Record<string, string>>;
23
23
 
24
- export const runtime: Runtime;
24
+ export default locales;
25
+ }
26
+
27
+ declare module "#virtual/assets" {
28
+ const assets: Record<string, string>;
29
+
30
+ export default assets;
25
31
  }