@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 +74 -28
- package/dist/http/data.d.ts +1 -0
- package/dist/http/http.js +9 -5
- package/dist/init.js +4 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
# @tinyfx/runtime
|
|
2
2
|
|
|
3
|
-
The TinyFX browser runtime: signals, DOM helpers, typed HTTP,
|
|
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 (`
|
|
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
|
|
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
|
|
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(
|
|
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 +
|
|
79
|
+
### Router + Lifecycle Hooks
|
|
75
80
|
|
|
76
81
|
```ts
|
|
77
|
-
import {
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
package/dist/http/data.d.ts
CHANGED
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:
|
|
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 ((
|
|
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 (
|
|
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
|
-
|
|
29
|
-
|
|
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();
|