@tinyfx/runtime 0.2.0 → 0.2.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
@@ -1,15 +1,20 @@
1
1
  # @tinyfx/runtime
2
2
 
3
- The TinyFX browser runtime: signals, DOM helpers, typed HTTP, and router lifecycle utilities.
3
+ The TinyFX browser runtime: signals, DOM helpers, typed HTTP, router lifecycle utilities, and dynamic registration APIs.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - Signals (`signal`, `effect`)
8
8
  - DOM binding helpers (`bindText`, `bindAttr`, `bindClass`)
9
- - Typed HTTP client (`createHttp`)
10
- - Router helpers (`initRouter`, `navigate`, `goBack`, `getParam`)
9
+ - Typed HTTP client (`createHttp`) with discriminated error types
10
+ - Router helpers (`getParam`, `getParams`, `navigate`, `goBack`)
11
11
  - Lifecycle hooks (`onMount`, `onDestroy`)
12
12
  - Lightweight `TinyFxContext` (`params`, `navigate`)
13
+ - Dynamic component registration (`registerComponent`, `getComponentFactory`, `mountComponents`)
14
+ - Dynamic page registration (`registerPage`, `getPageModule`, `runPageInit`)
15
+ - Mount state tracking (`markMounted`, `isMounted`)
16
+ - Path matching (`matchPath`, `splitPath`, `RouteDef`)
17
+ - Unified initialization (`init`)
13
18
 
14
19
  ## Installation
15
20
 
@@ -33,7 +38,7 @@ effect(() => {
33
38
  count.set(count() + 1);
34
39
  ```
35
40
 
36
- ### DOM bindings
41
+ ### DOM Bindings
37
42
 
38
43
  ```ts
39
44
  import { signal, bindText, bindClass, bindAttr } from "@tinyfx/runtime";
@@ -46,12 +51,12 @@ const btn = document.querySelector("button");
46
51
 
47
52
  if (el && btn) {
48
53
  bindText(el, () => `Count: ${count()}`);
49
- bindClass(btn, "active", isActive);
54
+ bindClass(btn, "active", () => isActive());
50
55
  bindAttr(btn, "disabled", () => count() > 10);
51
56
  }
52
57
  ```
53
58
 
54
- ### HTTP client
59
+ ### HTTP Client
55
60
 
56
61
  ```ts
57
62
  import { createHttp } from "@tinyfx/runtime";
@@ -65,20 +70,16 @@ const users = await http.get<{ id: number; name: string }[]>("/users");
65
70
  ```ts
66
71
  import type { TinyFxContext } from "@tinyfx/runtime";
67
72
 
68
- export function init(el: HTMLElement, ctx: TinyFxContext) {
73
+ export function init(_el: HTMLElement, ctx: TinyFxContext) {
69
74
  console.log(ctx.params);
70
75
  ctx.navigate("/about");
71
76
  }
72
77
  ```
73
78
 
74
- ### Router + lifecycle hooks
79
+ ### Router + Lifecycle Hooks
75
80
 
76
81
  ```ts
77
- import { initRouter, onMount, onDestroy } from "@tinyfx/runtime";
78
-
79
- initRouter({
80
- "/": { page: "index", path: "/" },
81
- });
82
+ import { onMount, onDestroy } from "@tinyfx/runtime";
82
83
 
83
84
  onMount(() => {
84
85
  console.log("mounted");
@@ -89,21 +90,66 @@ onDestroy(() => {
89
90
  });
90
91
  ```
91
92
 
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`
93
+ ### Component Registration
94
+
95
+ ```ts
96
+ import { registerComponent } from "@tinyfx/runtime";
97
+
98
+ registerComponent("MyButton", (el, ctx) => {
99
+ el.addEventListener("click", () => console.log("clicked"));
100
+ return { el };
101
+ });
102
+ ```
103
+
104
+ ### Page Registration
105
+
106
+ ```ts
107
+ import { registerPage } from "@tinyfx/runtime";
108
+
109
+ registerPage("/", {
110
+ init(_el, _ctx) {
111
+ console.log("home page loaded");
112
+ },
113
+ });
114
+ ```
115
+
116
+ ### Initialization
117
+
118
+ The compiler-generated `tinyfx.gen.ts` calls `init()` automatically. You typically do not need to call it yourself.
119
+
120
+ ```ts
121
+ import { init } from "@tinyfx/runtime";
122
+
123
+ init({ routes, setupDirectives });
124
+ ```
125
+
126
+ ## API Overview
127
+
128
+ - `signal<T>(value: T)` — creates a reactive signal
129
+ - `effect(fn)` — registers a reactive effect
130
+ - `bindText(el, source)` — binds text content reactively
131
+ - `bindAttr(el, attr, source)` — binds an attribute reactively
132
+ - `bindClass(el, className, source)` — binds a class toggle reactively
133
+ - `createHttp(config?)` — creates a typed HTTP client
134
+ - `getParam(name)` — gets a single route parameter
135
+ - `getParams()` — gets all route parameters
136
+ - `navigate(path)` — triggers full browser navigation
137
+ - `goBack()` — goes back in browser history
138
+ - `onMount(fn)` — registers a mount callback
139
+ - `onDestroy(fn)` — registers a destroy callback
140
+ - `registerComponent(name, factory)` — registers a dynamic component
141
+ - `getComponentFactory(name)` — retrieves a registered component factory
142
+ - `mountComponents(ctx, root?)` — mounts all components in a root element
143
+ - `registerPage(route, module)` — registers a page module
144
+ - `getPageModule(route)` — retrieves a registered page module
145
+ - `runPageInit(route, ctx)` — runs page init if registered
146
+ - `markMounted(el)` — marks an element as mounted (returns true on first call)
147
+ - `isMounted(el)` — checks if an element has been mounted
148
+ - `matchPath(def, segments)` — matches a RouteDef against path segments
149
+ - `splitPath(pathname)` — splits a pathname into segments
150
+ - `init(config)` — unified bootstrap entry point
151
+ - `TinyFxContext` — type for page/component context
152
+ - `RouteMap`, `RouteMeta`, `RouteDef` — routing types
107
153
 
108
154
  ## License
109
155
 
@@ -108,4 +108,5 @@ export interface RequestOptions {
108
108
  timeout?: number;
109
109
  signal?: AbortSignal;
110
110
  params?: Record<string, string | number | boolean>;
111
+ json?: boolean;
111
112
  }
package/dist/http/http.js CHANGED
@@ -25,22 +25,26 @@ export function createHttp(config = {}) {
25
25
  const requestInterceptors = (_f = config.requestInterceptors) !== null && _f !== void 0 ? _f : [];
26
26
  const responseInterceptors = (_g = config.responseInterceptors) !== null && _g !== void 0 ? _g : [];
27
27
  async function request(method, url, body, options = {}) {
28
- var _a, _b;
28
+ var _a, _b, _c;
29
29
  const fullUrl = buildUrl(url, base, options.params);
30
30
  const timeoutMs = (_a = options.timeout) !== null && _a !== void 0 ? _a : defaultTimeout;
31
+ const useJson = (_b = options.json) !== null && _b !== void 0 ? _b : true;
31
32
  let attempts = 0;
32
33
  const maxAttempts = maxRetries + 1;
33
34
  while (attempts < maxAttempts) {
34
35
  attempts++;
35
36
  try {
36
37
  const headers = Object.assign(Object.assign({}, defaultHeaders), options.headers);
37
- if (body !== undefined && !headers["Content-Type"]) {
38
+ if (body !== undefined && useJson && !headers["Content-Type"]) {
38
39
  headers["Content-Type"] = "application/json";
39
40
  }
41
+ const serializedBody = body !== undefined
42
+ ? (useJson ? JSON.stringify(body) : body)
43
+ : undefined;
40
44
  let fetchOptions = {
41
45
  method,
42
46
  headers,
43
- body: body !== undefined ? JSON.stringify(body) : undefined,
47
+ body: serializedBody,
44
48
  signal: options.signal,
45
49
  };
46
50
  let requestUrl = fullUrl;
@@ -70,7 +74,7 @@ export function createHttp(config = {}) {
70
74
  catch (err) {
71
75
  clearTimeout(timeoutId);
72
76
  if (err instanceof Error && err.name === "AbortError") {
73
- if ((_b = options.signal) === null || _b === void 0 ? void 0 : _b.aborted) {
77
+ if ((_c = options.signal) === null || _c === void 0 ? void 0 : _c.aborted) {
74
78
  throw err; // User cancelled
75
79
  }
76
80
  throw timeoutError(requestUrl, timeoutMs);
@@ -109,7 +113,7 @@ export function createHttp(config = {}) {
109
113
  const text = await res.text();
110
114
  return (text || undefined);
111
115
  }
112
- catch (_c) {
116
+ catch (_d) {
113
117
  return undefined;
114
118
  }
115
119
  }
package/dist/init.js CHANGED
@@ -25,8 +25,10 @@ export function init(config) {
25
25
  }
26
26
  }
27
27
  if (!matchedPath) {
28
- console.warn(`[tinyfx] No route matched for pathname: "${pathname}". ` +
29
- "Check that a page file exists for this URL in src/pages/.");
28
+ if (__DEV__) {
29
+ console.warn(`[tinyfx] No route matched for pathname: "${pathname}". ` +
30
+ "Check that a page file exists for this URL in src/pages/.");
31
+ }
30
32
  return null;
31
33
  }
32
34
  initLifecycle();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinyfx/runtime",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Minimal frontend runtime — signals, DOM helpers, typed HTTP, DTO mapping",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",