@tinyfx/runtime 0.1.4 → 0.1.8

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
@@ -1,16 +1,15 @@
1
1
  # @tinyfx/runtime
2
2
 
3
- > The minimal, browser-side runtime for tinyfx.
4
-
5
- A lightweight collection of helpers for reactivity, DOM manipulation, and typed HTTP communication. Designed to be used standalone or as part of the `tinyfx` build pipeline.
3
+ The TinyFX browser runtime: signals, DOM helpers, typed HTTP, and router lifecycle utilities.
6
4
 
7
5
  ## Features
8
6
 
9
- - **Signals** Explicit reactivity without Proxies.
10
- - **DOM Helpers** One-way bindings for text, attributes, and classes.
11
- - **Typed HTTP** Minimal wrapper over `fetch` with response typing.
12
- - **DI Container** — Simple `provide()`/`inject()` registry for app services.
13
- - **Tiny** No dependencies, tree-shakeable, and extremely fast.
7
+ - Signals (`signal`, `effect`)
8
+ - DOM binding helpers (`bindText`, `bindAttr`, `bindClass`)
9
+ - Typed HTTP client (`createHttp`)
10
+ - Router helpers (`initRouter`, `navigate`, `goBack`, `getParam`)
11
+ - Lifecycle hooks (`onMount`, `onDestroy`)
12
+ - Lightweight `TinyFxContext` (`params`, `navigate`)
14
13
 
15
14
  ## Installation
16
15
 
@@ -20,27 +19,21 @@ npm install @tinyfx/runtime
20
19
 
21
20
  ## Usage
22
21
 
23
- ### Signals (Reactivity)
24
-
25
- Signals provide a simple way to manage state that triggers effects when changed.
22
+ ### Signals
26
23
 
27
24
  ```ts
28
25
  import { signal, effect } from "@tinyfx/runtime";
29
26
 
30
27
  const count = signal(0);
31
28
 
32
- // Runs immediately, and re-runs whenever count() changes
33
29
  effect(() => {
34
- console.log("The count is:", count());
30
+ console.log("count:", count());
35
31
  });
36
32
 
37
- count.set(1); // logs: "The count is: 1"
38
- count.set(count() + 1); // logs: "The count is: 2"
33
+ count.set(count() + 1);
39
34
  ```
40
35
 
41
- ### DOM Bindings
42
-
43
- Keep your JS decoupled from the DOM while maintaining reactive updates.
36
+ ### DOM bindings
44
37
 
45
38
  ```ts
46
39
  import { signal, bindText, bindClass, bindAttr } from "@tinyfx/runtime";
@@ -51,132 +44,66 @@ const isActive = signal(false);
51
44
  const el = document.getElementById("counter");
52
45
  const btn = document.querySelector("button");
53
46
 
54
- bindText(el, () => `Count: ${count()}`);
55
- bindClass(btn, "active", isActive);
56
- bindAttr(btn, "disabled", () => count() > 10);
57
- ```
58
-
59
- ### Typed HTTP Client
60
-
61
- A thin wrapper over `fetch` that returns typed responses.
62
-
63
- ```ts
64
- import { createHttp } from "@tinyfx/runtime";
65
-
66
- interface User {
67
- id: number;
68
- name: string;
47
+ if (el && btn) {
48
+ bindText(el, () => `Count: ${count()}`);
49
+ bindClass(btn, "active", isActive);
50
+ bindAttr(btn, "disabled", () => count() > 10);
69
51
  }
70
-
71
- const api = createHttp({ baseUrl: "/api" });
72
-
73
- // Fully typed response
74
- const users = await api.get<User[]>("/users");
75
-
76
- // POST with data
77
- await api.post("/users", { name: "Alice" });
78
52
  ```
79
53
 
80
- ### DI Container (provide / inject)
81
-
82
- TinyFX includes a small dependency injection container to avoid importing singleton instances everywhere.
83
-
84
- Import it from `@tinyfx/runtime/di`:
54
+ ### HTTP client
85
55
 
86
56
  ```ts
87
- import { Container } from "@tinyfx/runtime/di";
88
-
89
- class Config {
90
- constructor(public baseUrl: string) {}
91
- }
92
-
93
- const container = new Container();
94
- container.provide(Config, new Config("/api"));
95
-
96
- const cfg = container.inject(Config);
97
- console.log(cfg.baseUrl);
98
- ```
99
-
100
- #### Using DI with tinyfx components
101
-
102
- When you use the tinyfx compiler, the generated glue calls your component behavior as:
57
+ import { createHttp } from "@tinyfx/runtime";
103
58
 
104
- ```ts
105
- init(el, { container })
59
+ const http = createHttp({ baseUrl: "/api" });
60
+ const users = await http.get<{ id: number; name: string }[]>("/users");
106
61
  ```
107
62
 
108
- So your component can inject services without importing global singletons:
63
+ ### TinyFxContext
109
64
 
110
65
  ```ts
111
- import type { TinyFxContext } from "@tinyfx/runtime/di";
112
- import { HttpService } from "../lib/http.service";
66
+ import type { TinyFxContext } from "@tinyfx/runtime";
113
67
 
114
68
  export function init(el: HTMLElement, ctx: TinyFxContext) {
115
- const http = ctx.container.inject(HttpService);
116
- http.getWeather().then((text) => {
117
- el.textContent = text;
118
- });
69
+ console.log(ctx.params);
70
+ ctx.navigate("/about");
119
71
  }
120
72
  ```
121
73
 
122
- #### Example: service that uses the HTTP helper
74
+ ### Router + lifecycle hooks
123
75
 
124
76
  ```ts
125
- // src/lib/http.service.ts
126
- import { createHttp } from "@tinyfx/runtime";
127
-
128
- export class HttpService {
129
- private readonly http;
77
+ import { initRouter, onMount, onDestroy } from "@tinyfx/runtime";
130
78
 
131
- constructor(baseUrl: string) {
132
- this.http = createHttp({ baseUrl });
133
- }
134
-
135
- getWeather() {
136
- // wttr.in can return plain text; your wrapper returns text when not JSON.
137
- return this.http.get<string>("");
138
- }
139
- }
140
- ```
141
-
142
- ```ts
143
- // src/main.ts
144
- import { Container } from "@tinyfx/runtime/di";
145
- import { setupComponents } from "./tinyfx.gen";
146
- import { HttpService } from "./lib/http.service";
79
+ initRouter({
80
+ "/": { page: "index", path: "/" },
81
+ });
147
82
 
148
- const container = new Container();
149
- container.provide(HttpService, new HttpService("https://wttr.in/tunisa"));
83
+ onMount(() => {
84
+ console.log("mounted");
85
+ });
150
86
 
151
- document.addEventListener("DOMContentLoaded", () => {
152
- setupComponents(container);
87
+ onDestroy(() => {
88
+ console.log("destroyed");
153
89
  });
154
90
  ```
155
91
 
156
- ## API Overview
157
-
158
- ### Signals
159
- - `signal<T>(value: T): Signal<T>`
160
- - `effect(fn: () => void): void`
161
-
162
- ### DOM
163
- - `bindText(el: HTMLElement, source: Signal<any> | (() => any)): void`
164
- - `bindAttr(el: HTMLElement, attr: string, source: Signal<any> | (() => any)): void`
165
- - `bindClass(el: HTMLElement, className: string, source: Signal<boolean> | (() => boolean)): void`
166
-
167
- ### HTTP
168
- - `createHttp(config: HttpConfig): HttpClient`
169
- - `client.get<T>(url: string): Promise<T>`
170
- - `client.post<T>(url: string, data?: any): Promise<T>`
171
- - `client.put<T>(url: string, data?: any): Promise<T>`
172
- - `client.del<T>(url: string): Promise<T>`
173
-
174
- ### DI
175
- - `new Container()`
176
- - `container.provide(token, instance)`
177
- - `container.inject(token)`
178
- - `createToken<T>(description: string): symbol`
179
- - `TinyFxContext` (passed to component init via tinyfx glue)
92
+ ## API overview
93
+
94
+ - `signal<T>(value: T)`
95
+ - `effect(fn)`
96
+ - `bindText(el, source)`
97
+ - `bindAttr(el, attr, source)`
98
+ - `bindClass(el, className, source)`
99
+ - `createHttp(config?)`
100
+ - `initRouter(routes)`
101
+ - `getParam(name)`
102
+ - `navigate(path)`
103
+ - `goBack()`
104
+ - `onMount(fn)`
105
+ - `onDestroy(fn)`
106
+ - `TinyFxContext`
180
107
 
181
108
  ## License
182
109
 
@@ -0,0 +1,4 @@
1
+ export interface TinyFxContext {
2
+ params: Record<string, string>;
3
+ navigate: (path: string) => void;
4
+ }
@@ -0,0 +1,3 @@
1
+ // Lightweight page context passed to components by the runtime.
2
+ // Not a DI container — services are created explicitly via factory functions.
3
+ export {};
package/dist/di.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  export type Token<T> = (new (...args: any[]) => T) | symbol;
3
3
  /** Create a unique symbol token with a debug description. */
4
4
  export declare function createToken<T>(description: string): Token<T>;
5
- /** Context passed to every component's init() function. */
5
+ /** Context passed to every component's mount() factory function. */
6
6
  export interface TinyFxContext {
7
7
  container: Container;
8
8
  }
package/dist/http.d.ts CHANGED
@@ -1,10 +1,2 @@
1
- export interface HttpConfig {
2
- baseUrl?: string;
3
- headers?: Record<string, string>;
4
- }
5
- export declare function createHttp(config?: HttpConfig): {
6
- get: <T>(url: string) => Promise<T>;
7
- post: <T>(url: string, body?: unknown) => Promise<T>;
8
- put: <T>(url: string, body?: unknown) => Promise<T>;
9
- del: <T>(url: string) => Promise<T>;
10
- };
1
+ export { createHttp } from "./http/http";
2
+ export type { HttpConfig } from "./http/data";
package/dist/http.js CHANGED
@@ -1,38 +1 @@
1
- // @tinyfx/runtime HTTP client
2
- // Thin, typed wrapper over fetch.
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
11
- };
12
- export function createHttp(config = {}) {
13
- var _a, _b;
14
- const base = (_a = config.baseUrl) !== null && _a !== void 0 ? _a : "";
15
- const defaultHeaders = (_b = config.headers) !== null && _b !== void 0 ? _b : {};
16
- function request(method, url, body) {
17
- return __awaiter(this, void 0, void 0, function* () {
18
- const headers = Object.assign({}, defaultHeaders);
19
- if (body !== undefined) {
20
- headers["Content-Type"] = "application/json";
21
- }
22
- const res = yield fetch(base + url, {
23
- method,
24
- headers,
25
- body: body !== undefined ? JSON.stringify(body) : undefined,
26
- });
27
- if (!res.ok)
28
- throw new Error(`${method} ${url}: ${res.status} ${res.statusText}`);
29
- return res.json();
30
- });
31
- }
32
- return {
33
- get: (url) => request("GET", url),
34
- post: (url, body) => request("POST", url, body),
35
- put: (url, body) => request("PUT", url, body),
36
- del: (url) => request("DELETE", url),
37
- };
38
- }
1
+ export { createHttp } from "./http/http";
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- export { signal, effect, Signal } from "./signals";
1
+ export { signal, effect } from "./signals";
2
2
  export { bindText, bindAttr, bindClass } from "./dom";
3
- export { createHttp } from "./http/http";
4
- export type { HttpConfig } from "./http/data";
5
- export { Container, createToken } from "./di";
6
- export type { Token, TinyFxContext } from "./di";
3
+ export { createHttp } from "./http";
4
+ export { initRouter, getParam, navigate, goBack } from "./router/index";
5
+ export { onMount, onDestroy } from "./router/lifecycle";
6
+ export type { TinyFxContext } from "./context";
7
+ export type { RouteMap, RouteMeta } from "./router/types";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // @tinyfx/runtime — barrel export
2
- export { signal, effect, Signal } from "./signals";
2
+ export { signal, effect } from "./signals";
3
3
  export { bindText, bindAttr, bindClass } from "./dom";
4
- export { createHttp } from "./http/http";
5
- export { Container, createToken } from "./di";
4
+ export { createHttp } from "./http";
5
+ export { initRouter, getParam, navigate, goBack } from "./router/index";
6
+ export { onMount, onDestroy } from "./router/lifecycle";
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Mark anchor tags whose `href` pathname matches `pathname` with
3
+ * `data-active="true"`, removing the attribute from all others.
4
+ *
5
+ * Only the pathname portion of each href is compared (query strings and
6
+ * fragments are ignored).
7
+ *
8
+ * @example
9
+ * // In a nav: <a href="/blog">Blog</a>
10
+ * // On the /blog page this element will get data-active="true"
11
+ * highlightActiveLinks("/blog");
12
+ */
13
+ export declare function highlightActiveLinks(pathname: string): void;
@@ -0,0 +1,31 @@
1
+ // @tinyfx/runtime — active link highlighting
2
+ /**
3
+ * Mark anchor tags whose `href` pathname matches `pathname` with
4
+ * `data-active="true"`, removing the attribute from all others.
5
+ *
6
+ * Only the pathname portion of each href is compared (query strings and
7
+ * fragments are ignored).
8
+ *
9
+ * @example
10
+ * // In a nav: <a href="/blog">Blog</a>
11
+ * // On the /blog page this element will get data-active="true"
12
+ * highlightActiveLinks("/blog");
13
+ */
14
+ export function highlightActiveLinks(pathname) {
15
+ const links = document.querySelectorAll("a[href]");
16
+ links.forEach((link) => {
17
+ try {
18
+ // Resolve href against the current origin to normalise relative URLs
19
+ const url = new URL(link.getAttribute("href"), window.location.origin);
20
+ if (url.pathname === pathname) {
21
+ link.setAttribute("data-active", "true");
22
+ }
23
+ else {
24
+ link.removeAttribute("data-active");
25
+ }
26
+ }
27
+ catch (_a) {
28
+ // Ignore malformed / external hrefs
29
+ }
30
+ });
31
+ }
@@ -0,0 +1,24 @@
1
+ import type { RouteMap } from "./types";
2
+ import { navigate, goBack } from "./navigate";
3
+ import { getParam } from "./params";
4
+ export type { RouteMap };
5
+ export { navigate, goBack, getParam };
6
+ /**
7
+ * Initialise the TinyFX router for the current page.
8
+ *
9
+ * Called automatically from the compiler-generated `tinyfx.gen.ts` on every
10
+ * page load. Matches `window.location.pathname` against the compiled route
11
+ * map, resolves URL params, starts lifecycle hooks, and highlights active
12
+ * navigation links.
13
+ *
14
+ * @param routes - The route map emitted by `tinyfx build`.
15
+ *
16
+ * @example
17
+ * // tinyfx.gen.ts (auto-generated — do not edit)
18
+ * initRouter({
19
+ * "/": { page: "home", path: "/" },
20
+ * "/blog": { page: "blog", path: "/blog" },
21
+ * "/blog/:slug":{ page: "blog-post", path: "/blog/:slug" },
22
+ * });
23
+ */
24
+ export declare function initRouter(routes: RouteMap): void;
@@ -0,0 +1,38 @@
1
+ // @tinyfx/runtime — router entry point
2
+ import { navigate, goBack } from "./navigate";
3
+ import { getParam, resolveParams } from "./params";
4
+ import { initLifecycle } from "./lifecycle";
5
+ import { highlightActiveLinks } from "./active-links";
6
+ export { navigate, goBack, getParam };
7
+ /**
8
+ * Initialise the TinyFX router for the current page.
9
+ *
10
+ * Called automatically from the compiler-generated `tinyfx.gen.ts` on every
11
+ * page load. Matches `window.location.pathname` against the compiled route
12
+ * map, resolves URL params, starts lifecycle hooks, and highlights active
13
+ * navigation links.
14
+ *
15
+ * @param routes - The route map emitted by `tinyfx build`.
16
+ *
17
+ * @example
18
+ * // tinyfx.gen.ts (auto-generated — do not edit)
19
+ * initRouter({
20
+ * "/": { page: "home", path: "/" },
21
+ * "/blog": { page: "blog", path: "/blog" },
22
+ * "/blog/:slug":{ page: "blog-post", path: "/blog/:slug" },
23
+ * });
24
+ */
25
+ export function initRouter(routes) {
26
+ const pathname = window.location.pathname;
27
+ for (const pattern of Object.keys(routes)) {
28
+ if (resolveParams(pattern, pathname)) {
29
+ // Match found
30
+ initLifecycle();
31
+ highlightActiveLinks(pathname);
32
+ return;
33
+ }
34
+ }
35
+ // No route matched — log a warning and do nothing else.
36
+ console.warn(`[tinyfx] No route matched for pathname: "${pathname}". ` +
37
+ "Check that a page file exists for this URL in src/pages/.");
38
+ }
@@ -0,0 +1,24 @@
1
+ type LifecycleCallback = () => void;
2
+ /**
3
+ * Register a callback to run when the page has mounted (DOM ready).
4
+ *
5
+ * - If the document is still loading, the callback runs after DOMContentLoaded.
6
+ * - If the document is already loaded, the callback runs immediately.
7
+ */
8
+ export declare function onMount(fn: LifecycleCallback): void;
9
+ /**
10
+ * Register a callback to run when the user navigates away from this page.
11
+ * Fires on the browser's `pagehide` event.
12
+ */
13
+ export declare function onDestroy(fn: LifecycleCallback): void;
14
+ /**
15
+ * Trigger all registered `onMount` callbacks.
16
+ * Called by `initRouter` after matching the current route.
17
+ */
18
+ export declare function flushMount(): void;
19
+ /**
20
+ * Wire up registered `onDestroy` callbacks to the `pagehide` event.
21
+ * Called by `initRouter` after matching the current route.
22
+ */
23
+ export declare function initLifecycle(): void;
24
+ export {};
@@ -0,0 +1,44 @@
1
+ // @tinyfx/runtime — page lifecycle hooks
2
+ const mountCallbacks = [];
3
+ const destroyCallbacks = [];
4
+ /**
5
+ * Register a callback to run when the page has mounted (DOM ready).
6
+ *
7
+ * - If the document is still loading, the callback runs after DOMContentLoaded.
8
+ * - If the document is already loaded, the callback runs immediately.
9
+ */
10
+ export function onMount(fn) {
11
+ if (document.readyState === "loading") {
12
+ mountCallbacks.push(fn);
13
+ return;
14
+ }
15
+ fn();
16
+ }
17
+ /**
18
+ * Register a callback to run when the user navigates away from this page.
19
+ * Fires on the browser's `pagehide` event.
20
+ */
21
+ export function onDestroy(fn) {
22
+ destroyCallbacks.push(fn);
23
+ }
24
+ /**
25
+ * Trigger all registered `onMount` callbacks.
26
+ * Called by `initRouter` after matching the current route.
27
+ */
28
+ export function flushMount() {
29
+ const run = () => mountCallbacks.forEach((fn) => fn());
30
+ if (document.readyState === "loading") {
31
+ document.addEventListener("DOMContentLoaded", run, { once: true });
32
+ }
33
+ else {
34
+ run();
35
+ }
36
+ }
37
+ /**
38
+ * Wire up registered `onDestroy` callbacks to the `pagehide` event.
39
+ * Called by `initRouter` after matching the current route.
40
+ */
41
+ export function initLifecycle() {
42
+ flushMount();
43
+ window.addEventListener("pagehide", () => destroyCallbacks.forEach((fn) => fn()), { once: true });
44
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Navigate to the given path.
3
+ * This is a full browser navigation — not a DOM swap.
4
+ *
5
+ * @example
6
+ * navigate("/blog/hello-world");
7
+ */
8
+ export declare function navigate(path: string): void;
9
+ /**
10
+ * Go back one step in the browser history.
11
+ */
12
+ export declare function goBack(): void;
@@ -0,0 +1,18 @@
1
+ // @tinyfx/runtime — navigation helpers
2
+ // Hard router: navigation is always a real browser page load.
3
+ /**
4
+ * Navigate to the given path.
5
+ * This is a full browser navigation — not a DOM swap.
6
+ *
7
+ * @example
8
+ * navigate("/blog/hello-world");
9
+ */
10
+ export function navigate(path) {
11
+ window.location.href = path;
12
+ }
13
+ /**
14
+ * Go back one step in the browser history.
15
+ */
16
+ export function goBack() {
17
+ window.history.go(-1);
18
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Match `pattern` (e.g. "/blog/:slug") against `pathname` (e.g. "/blog/hello").
3
+ *
4
+ * Returns true if they match, and stores any `:param` captures in the
5
+ * module-level store so `getParam()` can retrieve them.
6
+ *
7
+ * Static segments must match exactly; `:param` segments match any non-empty
8
+ * path segment.
9
+ */
10
+ export declare function resolveParams(pattern: string, pathname: string): boolean;
11
+ /**
12
+ * Retrieve a URL parameter resolved for the current page.
13
+ *
14
+ * @example
15
+ * // On the page with route "/blog/:slug" and URL "/blog/hello-world"
16
+ * getParam("slug"); // → "hello-world"
17
+ */
18
+ export declare function getParam(key: string): string | undefined;
19
+ export declare function getParams(): Record<string, string>;
@@ -0,0 +1,45 @@
1
+ // @tinyfx/runtime — route param resolution
2
+ /** Module-level store for the current page's resolved params. */
3
+ let currentParams = {};
4
+ /**
5
+ * Match `pattern` (e.g. "/blog/:slug") against `pathname` (e.g. "/blog/hello").
6
+ *
7
+ * Returns true if they match, and stores any `:param` captures in the
8
+ * module-level store so `getParam()` can retrieve them.
9
+ *
10
+ * Static segments must match exactly; `:param` segments match any non-empty
11
+ * path segment.
12
+ */
13
+ export function resolveParams(pattern, pathname) {
14
+ const patternParts = pattern.split("/").filter(Boolean);
15
+ const pathParts = pathname.split("/").filter(Boolean);
16
+ if (patternParts.length !== pathParts.length)
17
+ return false;
18
+ const captured = {};
19
+ for (let i = 0; i < patternParts.length; i++) {
20
+ const seg = patternParts[i];
21
+ if (seg.startsWith(":")) {
22
+ // Dynamic segment — capture the value
23
+ captured[seg.slice(1)] = decodeURIComponent(pathParts[i]);
24
+ }
25
+ else if (seg !== pathParts[i]) {
26
+ // Static segment mismatch
27
+ return false;
28
+ }
29
+ }
30
+ currentParams = captured;
31
+ return true;
32
+ }
33
+ /**
34
+ * Retrieve a URL parameter resolved for the current page.
35
+ *
36
+ * @example
37
+ * // On the page with route "/blog/:slug" and URL "/blog/hello-world"
38
+ * getParam("slug"); // → "hello-world"
39
+ */
40
+ export function getParam(key) {
41
+ return currentParams[key];
42
+ }
43
+ export function getParams() {
44
+ return Object.assign({}, currentParams);
45
+ }
@@ -0,0 +1,9 @@
1
+ /** Metadata for a single route, produced by the compiler. */
2
+ export interface RouteMeta {
3
+ /** The page name, derived from the source filename. */
4
+ page: string;
5
+ /** The declared URL pattern, e.g. "/blog/:slug". */
6
+ path: string;
7
+ }
8
+ /** The full route map emitted by `tinyfx build` into tinyfx.gen.ts. */
9
+ export type RouteMap = Record<string, RouteMeta>;
@@ -0,0 +1,2 @@
1
+ // @tinyfx/runtime — router types
2
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinyfx/runtime",
3
- "version": "0.1.4",
3
+ "version": "0.1.8",
4
4
  "description": "Minimal frontend runtime — signals, DOM helpers, typed HTTP, DTO mapping",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -10,7 +10,7 @@
10
10
  "./signals": "./dist/signals.js",
11
11
  "./dom": "./dist/dom.js",
12
12
  "./http": "./dist/http.js",
13
- "./di": "./dist/di.js"
13
+ "./router": "./dist/router/index.js"
14
14
  },
15
15
  "files": [
16
16
  "dist"