@manyducks.co/dolla 0.68.1 → 0.69.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,35 +9,54 @@ Dolla is a frontend framework that covers the common needs of complex apps, such
9
9
 
10
10
  Dolla gives you a set of composable state container primitives. Everything that happens in your app is a direct result of a value changing inside one of these containers. There is no VDOM. There is no other way to make the app function than to use these containers correctly. However, the advantage is that state, transformations and their side effects are expressed right in front of your eyes rather than being hidden deep in the framework. It's a bit more work to understand up front, but when you do the whole app becomes easier to understand and maintain.
11
11
 
12
+ A Dolla app is like a house. In this house, State is to data as pipes are to water. Views are appliances that receive that data and make it do something for the user. ShowerView sprays liquid data over their head. FreezerView makes data ice cubes for later. Stores are parts of this system the user doesn't interact with directly, but that hold or process data. WaterHeaterStore keeps data nice and hot for ShowerView and SinkView. SewerStore drains used data away from the app for further processing.
13
+
14
+ But if you're reading this, you're probably a programmer and not a plumber. All of this is to say, State is a static structure that moves a constantly changing stream of data between Views where data informs the state of DOM nodes the user sees and interacts with. Stores are a type of stateful data component that can be used to share data between Views or provide methods to abstract away interacting with an API.
15
+
12
16
  Let's first get into some examples.
13
17
 
14
18
  ## State
15
19
 
16
- ### Writables
20
+ States come in two varieties, each with a constructor function and a TypeScript type to match. These are:
21
+
22
+ - `Readable<T>`, which has only a `.get()` method that returns the current value.
23
+ - `Writable<T>`, which extends `Readable<T>` and adds a couple methods:
24
+ - `.set(value: T)` to replace the stored value.
25
+ - `.update(callback: (current: T) => T)` which takes a function that receives the current value and returns a new one.
17
26
 
18
- Writables have a few methods:
27
+ The constructor functions are `$` for `Readable`s and `$$` for `Writable`s. By convention, the names for each are prefixed with the same number of `$`s to indicate its type. This makes the data flow in code a lot easier to understand at a glance.
19
28
 
20
29
  ```js
21
- const $$number = writable(5);
30
+ import { $, $$ } from "@manyducks.co/dolla";
31
+
32
+ // By convention, Writable names are prefixed with two dollar signs and Readable with one.
33
+ const $$number = $$(5);
22
34
 
23
35
  // Returns the current value held by the Writable.
24
36
  $$number.get();
25
-
26
37
  // Stores a new value to the Writable.
27
38
  $$number.set(12);
28
-
29
39
  // Uses a callback to update the value. Takes the current value and returns the next.
30
40
  $$number.update((current) => current + 1);
41
+
42
+ // Convert to a read-only Readable with the same live value.
43
+ const $readOnlyNumber = $($$number);
44
+
45
+ // Derive a new state from an existing one.
46
+ const $doubled = $($$number, (value) => value * 2);
47
+ $doubled.get(); // 26 ($$number is 13)
48
+
49
+ // Derive one new state from the latest values of many other states.
50
+ const $many = $($$number, $doubled, (num, doubled) => num + doubled);
31
51
  ```
32
52
 
33
- For the first example, a simple greeter app. The user types their name into a text input and that value is reflected in a heading above the input. For this we will use the `writable` function to create a state container. That container can be slotted into our JSX as a text node or DOM property. Any changes to the value will now be reflected in the DOM.
53
+ Now how do we use it? For a real example, a simple greeter app. The user types their name into a text input and that value is reflected in a heading above the input. For this we will use the `writable` function to create a state container. That container can be slotted into our JSX as a text node or DOM property. Any changes to the value will now be reflected in the DOM.
34
54
 
35
55
  ```jsx
36
- import { writable } from "@manyducks.co/dolla";
56
+ import { $$ } from "@manyducks.co/dolla";
37
57
 
38
58
  function UserView() {
39
- // By convention writables start with '$$'
40
- const $$name = writable("Valued Customer");
59
+ const $$name = $$("Valued Customer");
41
60
 
42
61
  return (
43
62
  <section>
@@ -56,31 +75,16 @@ function UserView() {
56
75
  }
57
76
  ```
58
77
 
59
- ### Readables
60
-
61
- Readables are like Writables with only a `get` function. Typically, readables are derived from a Writable or derived from other states with `computed`.
62
-
63
- ```js
64
- import { writable, readable } from "@manyducks.co/dolla";
65
-
66
- const $$value = writable("This is the value.");
67
-
68
- // By convention Readable names start with '$'.
69
- const $value = readable($$value);
70
- ```
71
-
72
- You can now safely pass `$value` around without worrying about that code changing it. `$value` will always reflect the value of `$$value`.
73
-
74
78
  ### Computed
75
79
 
76
80
  Computed states take one or more Readables or Writables and produce a new value _computed_ from those.
77
81
 
78
82
  ```js
79
- import { writable, computed } from "@manyducks.co/dolla";
83
+ import { $, $$ } from "@manyducks.co/dolla";
80
84
 
81
- const $$count = writable(100);
85
+ const $$count = $$(100);
82
86
 
83
- const $double = computed($$count, (value) => value * 2);
87
+ const $double = $($$count, (value) => value * 2);
84
88
  ```
85
89
 
86
90
  In that example, `$$double` will always have a value derived from that of `$$count`.
@@ -88,13 +92,13 @@ In that example, `$$double` will always have a value derived from that of `$$cou
88
92
  Let's look at a more typical example where we're basically joining two pieces of data; a list of users and the ID of the selected user.
89
93
 
90
94
  ```js
91
- import { writable, computed } from "@manyducks.co/dolla";
95
+ import { $, $$ } from "@manyducks.co/dolla";
92
96
 
93
97
  // Let's assume this list of users was fetched from an API somewhere.
94
- const $$people = writable([
98
+ const $$people = $$([
95
99
  {
96
100
  id: 1,
97
- name: "Bob",
101
+ name: "Borb",
98
102
  },
99
103
  {
100
104
  id: 2,
@@ -107,15 +111,15 @@ const $$people = writable([
107
111
  ]);
108
112
 
109
113
  // Let's assume this ID was chosen from an input where the above users were displayed.
110
- const $$selectedId = writable(2);
114
+ const $$selectedId = $$(2);
111
115
 
112
116
  // Now we get the object of the person who is selected.
113
- const $selectedPerson = computed([$$people, $$selectedId], ([people, selectedId]) => {
117
+ const $selectedPerson = $($$people, $$selectedId, (people, selectedId) => {
114
118
  return people.find((person) => person.id === selectedId);
115
119
  });
116
120
 
117
121
  // Now we get a Readable of just that person's name. Say we're going to display it on the page somewhere.
118
- const $personName = computed($selectedPerson, (person) => person.name);
122
+ const $personName = $($selectedPerson, (person) => person.name);
119
123
 
120
124
  console.log($personName.get()); // "Bex"
121
125
  ```
@@ -127,12 +131,12 @@ Notice that the structure above composes a data pipeline; if any of the data cha
127
131
  The `unwrap` function returns the current value of a Readable or Writable, or if passed a non-Readable value returns that exact value. This function is used to guarantee you have a plain value when you may be dealing with either a container or a plain value.
128
132
 
129
133
  ```js
130
- import { readable, writable, unwrap } from "@manyducks.co/dolla";
134
+ import { unwrap } from "@manyducks.co/dolla";
131
135
 
132
- const $$number = writable(5);
136
+ const $$number = $$(5);
133
137
 
134
138
  unwrap($$number); // 5
135
- unwrap(readable(5)); // 5
139
+ unwrap(State.readable(5)); // 5
136
140
  unwrap(5); // 5
137
141
  ```
138
142
 
@@ -177,7 +181,7 @@ function ListItemView(props) {
177
181
  }
178
182
  ```
179
183
 
180
- As you may have guessed, you can pass Readables and Writables as props and slot them in in exactly the same way. This is important because Views do not re-render the way you might expect from other frameworks. Whatever you pass as props is what the View gets for its entire lifecycle.
184
+ As you may have guessed, you can pass States as props and slot them in in exactly the same way. This is important because Views do not re-render the way you might expect from other frameworks. Whatever you pass as props is what the View gets for its entire lifecycle.
181
185
 
182
186
  ### View Helpers
183
187
 
@@ -213,7 +217,7 @@ The `repeat` helper repeats a render function for each item in a list. The `keyF
213
217
 
214
218
  ```jsx
215
219
  function RepeatedListView() {
216
- const $items = readable(["Squirrel", "Chipmunk", "Groundhog"]);
220
+ const $items = $(["Squirrel", "Chipmunk", "Groundhog"]);
217
221
 
218
222
  return (
219
223
  <ul>
@@ -332,7 +336,7 @@ function ExampleView(props, ctx) {
332
336
  }
333
337
  ```
334
338
 
335
- #### Observing Readables
339
+ #### Observing States
336
340
 
337
341
  The `observe` function starts observing when the view is connected and stops when disconnected. This takes care of cleaning up observers so you don't have to worry about memory leaks.
338
342
 
@@ -353,10 +357,10 @@ function ExampleView(props, ctx) {
353
357
  Putting it all together, we have a view that maintains a counter. The user sees the current count displayed, and below it three buttons; one to increment by 1, one to decrement by 1, and one to reset the value to 0.
354
358
 
355
359
  ```jsx
356
- import { writable } from "@manyducks.co/dolla";
360
+ import { $$ } from "@manyducks.co/dolla";
357
361
 
358
362
  function CounterView(props, ctx) {
359
- const $$count = writable(0);
363
+ const $$count = $$(0);
360
364
 
361
365
  function increment() {
362
366
  $$count.update((n) => n + 1);
@@ -392,9 +396,9 @@ Stores are accessed with the `getStore` function available on the context object
392
396
  Stores are helpful for managing persistent state that needs to be accessed in many places.
393
397
 
394
398
  ```js
395
- import { makeApp } from "@manyducks.co/dolla";
399
+ import { App } from "@manyducks.co/dolla";
396
400
 
397
- const app = makeApp();
401
+ const app = App();
398
402
 
399
403
  // We define a store that just exports a message.
400
404
  function MessageStore() {
@@ -476,9 +480,9 @@ function LayoutView() {
476
480
  ## Apps and Routing
477
481
 
478
482
  ```jsx
479
- import { makeApp } from "@manyducks.co/dolla";
483
+ import { App } from "@manyducks.co/dolla";
480
484
 
481
- const app = makeApp({
485
+ const app = App({
482
486
  // Debug options control what gets printed from messages logged through view and store contexts.
483
487
  debug: {
484
488
  // A comma-separated list of filters. '*' means allow everything and '-dolla/*' means suppress messages with labels beginning with 'dolla/'.
@@ -495,63 +499,80 @@ const app = makeApp({
495
499
  error: true,
496
500
  },
497
501
 
498
- // Router options control how routes are matched
499
- router: {
500
- hash: true, // Use hash-based routing
501
- },
502
-
503
502
  mode: "development", // or "production" (enables additional debug features and logging in "development")
503
+
504
+ view: (_, ctx) => {
505
+ // Define a custom root view. By default this just renders any routes like so:
506
+ return ctx.outlet();
507
+ },
504
508
  });
505
509
  ```
506
510
 
507
- #### Main View, Routes and Outlets
511
+ #### Routes and Outlets
508
512
 
509
513
  The main view (defined with the app's `main` method) is the top-level view that will always be displayed while the app is connected.
510
514
 
511
515
  ```jsx
512
- // Here is a hypothetical main view with a layout and navigation:
513
- app.main((props, ctx) => {
514
- return (
515
- <div class="todo-layout">
516
- <nav>
517
- <ul>
518
- <li>
519
- <a href="/tasks">Tasks</a>
520
- </li>
521
- <li>
522
- <a href="/completed">Completed</a>
523
- </li>
524
- </ul>
525
- </nav>
526
- {/*
527
- * An outlet is where children of a view are shown.
528
- * Because this is a main view, children in this case
529
- * are the views that correspond to matched routes.
530
- */}
531
- {ctx.outlet()}
532
- </div>
533
- );
516
+ // Here is an app with a hypothetical main view with a layout and navigation:
517
+ const app = App({
518
+ view: (_, ctx) => {
519
+ return (
520
+ <div class="todo-layout">
521
+ <nav>
522
+ <ul>
523
+ <li>
524
+ <a href="/tasks">Tasks</a>
525
+ </li>
526
+ <li>
527
+ <a href="/completed">Completed</a>
528
+ </li>
529
+ </ul>
530
+ </nav>
531
+ {/*
532
+ * An outlet is where children of a view are shown.
533
+ * Because this is a main view, children in this case
534
+ * are the views that correspond to matched routes.
535
+ */}
536
+ {ctx.outlet()}
537
+ </div>
538
+ );
539
+ },
534
540
  });
535
541
 
536
- // Here are a couple of routes to be rendered into our layout:
537
- app.route("/tasks", TasksView);
538
- app.route("/completed", CompletedView);
542
+ app.addStore(RouterStore, {
543
+ hash: true, // Use hash-based routing (default false)
544
+
545
+ // Here are a couple of routes to be rendered into our layout:
546
+ routes: [
547
+ { path: "/tasks", view: TasksView },
548
+ { path: "/completed", view: CompletedView },
549
+ ],
550
+ });
539
551
  ```
540
552
 
541
553
  Routes can also be nested. Just like the main view and its routes, subroutes will be displayed in the outlet of their parent view.
542
554
 
543
555
  ```jsx
544
- app.route("/tasks", TasksView, (sub) => {
545
- sub.route("/", TaskListView);
546
-
547
- // In routes, `{value}` is a dynamic value that matches anything,
548
- // and `{#value}` is a dynamic value that matches a number.
549
- sub.route("/{#id}", TaskDetailsView);
550
- sub.route("/{#id}/edit", TaskEditView);
551
-
552
- // If the route is any other than the ones defined above, redirect to the list.
553
- // Redirects support './' and '../' style relative paths.
554
- sub.redirect("*", "./");
556
+ app.addStore(RouterStore, {
557
+ routes: [
558
+ {
559
+ path: "/tasks",
560
+ view: TasksView,
561
+ routes: [
562
+ { path: "/", view: TaskListView },
563
+
564
+ // In routes, `{value}` is a dynamic value that matches anything,
565
+ // and `{#value}` is a dynamic value that matches a number.
566
+ { path: "/{#id}", view: TaskDetailsView },
567
+ { path: "/{#id}/edit", view: TaskEditView },
568
+
569
+ // If the route is any other than the ones defined above, redirect to the list.
570
+ // Redirects support './' and '../' style relative paths.
571
+ { path: "*", redirect: "./" },
572
+ ],
573
+ },
574
+ { path: "/completed", view: CompletedView },
575
+ ],
555
576
  });
556
577
  ```
557
578
 
@@ -561,11 +582,9 @@ Dolla makes heavy use of client-side routing. You can define as many routes as y
561
582
  will determine which one the app shows at any given time. By building an app around routes, lots of things one expects
562
583
  from a web app will just work; back and forward buttons, sharable URLs, bookmarks, etc.
563
584
 
564
- Routing in Dolla is aesthetically inspired by [choo.js](https://www.choo.io/docs/routing)
565
- with technical inspiration from [@reach/router](https://reach.tech/router/), as routes are matched by highest
566
- specificity regardless of the order they were registered. This avoids some confusing situations that come up with
567
- order-based routers like that of `express`. On the other hand, order-based routers can support regular expressions as
568
- patterns which Dolla's router cannot.
585
+ Routes are matched by highest specificity regardless of the order they were registered.
586
+ This avoids some confusing situations that come up with order-based routers like that of `express`.
587
+ On the other hand, order-based routers can support regular expressions as patterns which Dolla's router cannot.
569
588
 
570
589
  #### Route Patterns
571
590
 
@@ -584,31 +603,38 @@ to your code (`router` store, `$params` readable). Below are some examples of pa
584
603
  Now, here are some route examples in the context of an app:
585
604
 
586
605
  ```js
606
+ import { App, RouterStore } from "@manyducks.co/dolla";
587
607
  import { PersonDetails, ThingIndex, ThingDetails, ThingEdit, ThingDelete } from "./components.js";
588
608
 
589
- const app = createApp();
590
-
591
- app
592
- .route("/people/{name}", PersonDetails)
593
-
594
- // Routes can be nested. Also, a `null` component with subroutes acts as a namespace for those subroutes.
595
- // Passing a view instead of `null` results in subroutes being rendered inside that view wherever `ctx.outlet()` is called.
596
- .route("/things", null, (sub) => {
597
- sub.route("/", ThingIndex); // matches `/things`
598
- sub.route("/{#id}", ThingDetails); // matches `/things/{#id}`
599
- sub.route("/{#id}/edit", ThingEdit); // matches `/things/{#id}/edit`
600
- sub.route("/{#id}/delete", ThingDelete); // matches `/things/{#id}/delete`
601
- });
609
+ const app = App();
610
+
611
+ app.addStore(RouterStore, {
612
+ routes: [
613
+ { path: "/people/{name}", view: PersonDetails },
614
+ {
615
+ // A `null` component with subroutes acts as a namespace for those subroutes.
616
+ // Passing a view instead of `null` results in subroutes being rendered inside that view wherever `ctx.outlet()` is called.
617
+ path: "/things",
618
+ view: null,
619
+ routes: [
620
+ { path: "/", view: ThingIndex }, // matches `/things`
621
+ { path: "/{#id}", view: ThingDetails }, // matches `/things/{#id}`
622
+ { path: "/{#id}/edit", view: ThingEdit }, // matches `/things/{#id}/edit`
623
+ { path: "/{#id}/delete", view: ThingDelete }, // matches `/things/{#id}/delete`
624
+ ],
625
+ },
626
+ ],
627
+ });
602
628
  ```
603
629
 
604
630
  As you may have inferred from the code above, when the URL matches a pattern the corresponding view is displayed. If we
605
631
  visit `/people/john`, we will see the `PersonDetails` view and the params will be `{ name: "john" }`. Params can be
606
- accessed inside those views through the built-in `router` store.
632
+ accessed inside those views through `RouterStore`.
607
633
 
608
634
  ```js
609
635
  function PersonDetails(props, ctx) {
610
636
  // `router` store allows you to work with the router from inside the app.
611
- const router = ctx.getStore("router");
637
+ const router = ctx.getStore(RouterStore);
612
638
 
613
639
  // Info about the current route is exported as a set of Readables. Query params are also Writable through $$query:
614
640
  const { $path, $pattern, $params, $$query } = router;
package/lib/app.d.ts CHANGED
@@ -2,19 +2,25 @@ import { CrashCollector } from "./classes/CrashCollector.js";
2
2
  import { DebugHub, type DebugOptions } from "./classes/DebugHub.js";
3
3
  import { DOMHandle } from "./markup.js";
4
4
  import { initStore, type Store } from "./store.js";
5
- import { type LanguageConfig } from "./stores/language.js";
6
- import { type RedirectContext, type RouterOptions } from "./stores/router.js";
7
5
  import { type BuiltInStores } from "./types.js";
8
6
  import { type View } from "./view.js";
9
- interface AppOptions {
7
+ interface StoreConfig<O, E> {
8
+ store: Store<O, E>;
9
+ options?: O;
10
+ }
11
+ interface IAppOptions {
10
12
  /**
11
13
  * Options for the debug system.
12
14
  */
13
15
  debug?: DebugOptions;
14
16
  /**
15
- * Options to configure how routing works.
17
+ * The view to be rendered by the app.
18
+ */
19
+ view?: View<{}>;
20
+ /**
21
+ * App-level stores.
16
22
  */
17
- router?: RouterOptions;
23
+ stores?: StoreConfig<any, any>[];
18
24
  /**
19
25
  * Configures the app based on the environment it's running in.
20
26
  */
@@ -42,80 +48,14 @@ export interface StoreRegistration<O = any> {
42
48
  options?: O;
43
49
  instance?: ReturnType<typeof initStore>;
44
50
  }
45
- interface AppRouter {
46
- /**
47
- * Adds a new pattern, a view to display while that pattern matches the current URL, and an optional function to configure route chaining.
48
- * Route chaining allows you to add nested routes and redirects that are displayed within the `view`'s outlet while `pattern` matches the current URL.
49
- *
50
- * @param pattern - A URL pattern to match against the current URL.
51
- * @param view - The view to display while `pattern` matches the current URL.
52
- * @param subroutes - A callback that takes a router object. Use this to append nested routes and redirects.
53
- */
54
- route<I>(pattern: string, view: View<I>, subroutes?: (router: AppRouter) => void): this;
55
- /**
56
- * Adds a new pattern and chains a set of nested routes that are displayed without a layout `view`.
57
- *
58
- * @param pattern - A URL pattern to match against the current URL.
59
- * @param view - Pass null to render subroutes without a parent view.
60
- * @param subroutes - A callback that takes a router object. Use this to append nested routes and redirects.
61
- */
62
- route(pattern: string, view: null, subroutes: (router: AppRouter) => void): this;
63
- /**
64
- * Adds a new pattern that will redirect to a different route when matched.
65
- *
66
- * @param pattern - A URL pattern to match against the current URL.
67
- * @param redirectPath - A path to redirect to when `pattern` matches the current URL.
68
- */
69
- redirect(pattern: string, redirectPath: string): this;
70
- /**
71
- * Adds a new pattern that will redirect to a different route when matched, as calculated by a callback function.
72
- * Useful when you require more insight into the path that matched the pattern before deciding where to send the user.
73
- *
74
- * @param pattern - A URL pattern to match against the current URL.
75
- * @param createPath - A function that generates a redirect path from the current URL match.
76
- */
77
- redirect(pattern: string, createPath: (ctx: RedirectContext) => string): this;
78
- }
79
51
  interface ConfigureContext {
80
52
  }
81
53
  type ConfigureCallback = (ctx: ConfigureContext) => void | Promise<void>;
82
- export interface App extends AppRouter {
54
+ export interface IApp {
83
55
  readonly isConnected: boolean;
84
- /**
85
- * Displays view at the root of the app. All other routes render inside this view's outlet.
86
- */
87
- main<A extends Record<string, any>>(view: View<A>, attributes?: A): this;
88
56
  /**
89
57
  * Makes this store accessible from any other component in the app, except for stores registered before this one.
90
58
  */
91
- store<O>(store: Store<O, any>, options?: O): this;
92
- /**
93
- * Returns the shared instance of `store`.
94
- */
95
- getStore<T extends Store<any, any>>(store: T): ReturnType<T>;
96
- /**
97
- * Returns the shared instance of a built-in store.
98
- */
99
- getStore<N extends keyof BuiltInStores>(name: N): BuiltInStores[N];
100
- /**
101
- * Adds a new language translation to the app.
102
- *
103
- * @param tag - A valid BCP47 language tag, like `en-US`, `en-GB`, `ja`, etc.
104
- * @param config - Language configuration.
105
- */
106
- language(tag: string, config: LanguageConfig): this;
107
- /**
108
- * Sets the initial language. The app will default to the first language added if this is not called.
109
- */
110
- setLanguage(tag: string): this;
111
- /**
112
- * Sets the initial language based on the user's locale.
113
- * Falls back to `fallback` language if provided, otherwise falls back to the first language added.
114
- *
115
- * @param tag - Set to "auto" to autodetect the user's language.
116
- * @param fallback - The language tag to default to if the app fails to detect an appropriate language.
117
- */
118
- setLanguage(tag: "auto", fallback?: string): this;
119
59
  /**
120
60
  * Runs `callback` after app-level stores are connected to the app, but before views are connected to the DOM.
121
61
  * Use this function to run async configuration code before displaying content to the user.
@@ -134,5 +74,5 @@ export interface App extends AppRouter {
134
74
  */
135
75
  disconnect(): Promise<void>;
136
76
  }
137
- export declare function makeApp(options?: AppOptions): App;
77
+ export declare function App(options?: IAppOptions): IApp;
138
78
  export {};
package/lib/index.d.ts CHANGED
@@ -1,17 +1,15 @@
1
- export { makeApp } from "./app.js";
2
- export { spring } from "./spring.js";
3
- export { readable, writable, computed, proxy, observe, unwrap, isReadable, isWritable } from "./state.js";
1
+ export { App } from "./app.js";
2
+ export { $, $$, observe, unwrap, isReadable, isWritable, type Readable, type Writable } from "./state.js";
4
3
  export { m, cond, repeat, portal } from "./markup.js";
5
4
  export { Fragment } from "./views/fragment.js";
6
- export { StoreScope } from "./views/store-scope.js";
7
- export type { DialogProps } from "./stores/dialog.js";
8
- export type { StoreScopeProps } from "./views/store-scope.js";
9
- export type { Spring } from "./spring.js";
10
- export type { Readable, Writable } from "./state.js";
5
+ export { StoreScope, type StoreScopeProps } from "./views/store-scope.js";
6
+ export { RouterStore } from "./stores/router.js";
7
+ export { LanguageStore } from "./stores/language.js";
8
+ export { HTTPStore, type HTTPMiddleware } from "./stores/http.js";
9
+ export { DialogStore, type DialogProps } from "./stores/dialog.js";
11
10
  export type { ViewContext } from "./view.js";
12
11
  export type { StoreContext } from "./store.js";
13
12
  export type { Markup } from "./markup.js";
14
- export type { HTTPMiddleware } from "./stores/http.js";
15
13
  export type { InputType, Renderable } from "./types.js";
16
14
  import type { IntrinsicElements as Elements } from "./types";
17
15
  declare global {