@tinyfx/runtime 0.1.8 → 0.2.0
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/dist/__tests__/http-errors.test.d.ts +1 -0
- package/dist/__tests__/http-errors.test.js +65 -0
- package/dist/__tests__/mount-state.test.d.ts +1 -0
- package/dist/__tests__/mount-state.test.js +30 -0
- package/dist/__tests__/page-registry.test.d.ts +1 -0
- package/dist/__tests__/page-registry.test.js +38 -0
- package/dist/__tests__/path-matcher.test.d.ts +1 -0
- package/dist/__tests__/path-matcher.test.js +43 -0
- package/dist/__tests__/registry.test.d.ts +1 -0
- package/dist/__tests__/registry.test.js +67 -0
- package/dist/__tests__/router-merged.test.d.ts +1 -0
- package/dist/__tests__/router-merged.test.js +33 -0
- package/dist/__tests__/signals.test.d.ts +1 -0
- package/dist/__tests__/signals.test.js +93 -0
- package/dist/context.d.ts +15 -0
- package/dist/context.js +0 -2
- package/dist/dom.d.ts +35 -0
- package/dist/dom.js +35 -0
- package/dist/each.d.ts +10 -0
- package/dist/each.js +24 -0
- package/dist/http/data.d.ts +84 -17
- package/dist/http/data.js +15 -23
- package/dist/http/helper.d.ts +20 -0
- package/dist/http/helper.js +20 -0
- package/dist/http/http.d.ts +13 -0
- package/dist/http/http.js +19 -6
- package/dist/index.d.ts +10 -2
- package/dist/index.js +9 -1
- package/dist/init.d.ts +12 -0
- package/dist/init.js +42 -0
- package/dist/mount-state.d.ts +17 -0
- package/dist/mount-state.js +27 -0
- package/dist/page-registry.d.ts +31 -0
- package/dist/page-registry.js +38 -0
- package/dist/registry.d.ts +31 -0
- package/dist/registry.js +49 -0
- package/dist/router/active-links.d.ts +3 -0
- package/dist/router/active-links.js +3 -0
- package/dist/router/index.d.ts +21 -22
- package/dist/router/index.js +12 -35
- package/dist/router/lifecycle.d.ts +28 -2
- package/dist/router/lifecycle.js +28 -2
- package/dist/router/navigate.d.ts +8 -0
- package/dist/router/navigate.js +8 -0
- package/dist/router/params.d.ts +15 -7
- package/dist/router/params.js +16 -25
- package/dist/router/path-matcher.d.ts +30 -0
- package/dist/router/path-matcher.js +45 -0
- package/dist/router/types.d.ts +15 -2
- package/dist/signals.d.ts +28 -7
- package/dist/signals.js +41 -21
- package/package.json +6 -2
package/dist/http/data.js
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
super(`HTTP ${status} ${statusText}: ${method} ${url}`);
|
|
4
|
-
this.status = status;
|
|
5
|
-
this.statusText = statusText;
|
|
6
|
-
this.url = url;
|
|
7
|
-
this.method = method;
|
|
8
|
-
this.response = response;
|
|
9
|
-
this.name = "HttpError";
|
|
10
|
-
}
|
|
1
|
+
export function httpError(status, statusText, url, method, response) {
|
|
2
|
+
return { kind: "http", status, statusText, url, method, response };
|
|
11
3
|
}
|
|
12
|
-
export
|
|
13
|
-
|
|
14
|
-
super(`Request timeout after ${timeout}ms: ${url}`);
|
|
15
|
-
this.url = url;
|
|
16
|
-
this.timeout = timeout;
|
|
17
|
-
this.name = "HttpTimeoutError";
|
|
18
|
-
}
|
|
4
|
+
export function timeoutError(url, timeout) {
|
|
5
|
+
return { kind: "timeout", url, timeout };
|
|
19
6
|
}
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
7
|
+
export function parseError(message, url) {
|
|
8
|
+
return { kind: "parse", message, url };
|
|
9
|
+
}
|
|
10
|
+
export function isHttpError(err) {
|
|
11
|
+
return typeof err === "object" && err !== null && "kind" in err && err.kind === "http";
|
|
12
|
+
}
|
|
13
|
+
export function isTimeoutError(err) {
|
|
14
|
+
return typeof err === "object" && err !== null && "kind" in err && err.kind === "timeout";
|
|
15
|
+
}
|
|
16
|
+
export function isParseError(err) {
|
|
17
|
+
return typeof err === "object" && err !== null && "kind" in err && err.kind === "parse";
|
|
26
18
|
}
|
package/dist/http/helper.d.ts
CHANGED
|
@@ -1,2 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a request URL by combining a base path with optional query params.
|
|
3
|
+
*
|
|
4
|
+
* @param url - The request path or URL fragment
|
|
5
|
+
* @param base - The configured base URL prefix
|
|
6
|
+
* @param params - Optional query parameters to append
|
|
7
|
+
* @returns The final request URL string
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* buildUrl("/posts", "/api", { page: 2, published: true });
|
|
11
|
+
*/
|
|
1
12
|
export declare function buildUrl(url: string, base: string, params?: Record<string, string | number | boolean>): string;
|
|
13
|
+
/**
|
|
14
|
+
* Waits for a number of milliseconds.
|
|
15
|
+
*
|
|
16
|
+
* @param ms - The delay duration in milliseconds
|
|
17
|
+
* @returns A promise that resolves after the delay completes
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* await sleep(250);
|
|
21
|
+
*/
|
|
2
22
|
export declare function sleep(ms: number): Promise<void>;
|
package/dist/http/helper.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Builds a request URL by combining a base path with optional query params.
|
|
3
|
+
*
|
|
4
|
+
* @param url - The request path or URL fragment
|
|
5
|
+
* @param base - The configured base URL prefix
|
|
6
|
+
* @param params - Optional query parameters to append
|
|
7
|
+
* @returns The final request URL string
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* buildUrl("/posts", "/api", { page: 2, published: true });
|
|
11
|
+
*/
|
|
1
12
|
export function buildUrl(url, base, params) {
|
|
2
13
|
const fullUrl = base + url;
|
|
3
14
|
if (!params || Object.keys(params).length === 0)
|
|
@@ -8,6 +19,15 @@ export function buildUrl(url, base, params) {
|
|
|
8
19
|
});
|
|
9
20
|
return urlObj.toString().replace(urlObj.origin, '');
|
|
10
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Waits for a number of milliseconds.
|
|
24
|
+
*
|
|
25
|
+
* @param ms - The delay duration in milliseconds
|
|
26
|
+
* @returns A promise that resolves after the delay completes
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* await sleep(250);
|
|
30
|
+
*/
|
|
11
31
|
export async function sleep(ms) {
|
|
12
32
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
13
33
|
}
|
package/dist/http/http.d.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
import { HttpConfig, RequestOptions } from "./data";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a typed HTTP client around `fetch`.
|
|
4
|
+
*
|
|
5
|
+
* The client exposes `get`, `post`, `put`, `patch`, `del`, and `delete`
|
|
6
|
+
* helpers and applies the default configuration to every request.
|
|
7
|
+
*
|
|
8
|
+
* @param config - Default HTTP configuration such as base URL, headers, and retry policy
|
|
9
|
+
* @returns An object containing typed request methods
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const http = createHttp({ baseUrl: "/api", timeout: 5000 });
|
|
13
|
+
* const posts = await http.get<{ id: number; title: string }[]>("/posts");
|
|
14
|
+
*/
|
|
2
15
|
export declare function createHttp(config?: HttpConfig): {
|
|
3
16
|
get: <T>(url: string, options?: RequestOptions) => Promise<T>;
|
|
4
17
|
post: <T>(url: string, body?: unknown, options?: RequestOptions) => Promise<T>;
|
package/dist/http/http.js
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
// @tinyfx/runtime — HTTP client
|
|
2
2
|
// Thin, typed wrapper over fetch.
|
|
3
|
-
import {
|
|
3
|
+
import { httpError, timeoutError, parseError, isHttpError } from "./data";
|
|
4
4
|
import { buildUrl, sleep } from "./helper";
|
|
5
|
+
/**
|
|
6
|
+
* Creates a typed HTTP client around `fetch`.
|
|
7
|
+
*
|
|
8
|
+
* The client exposes `get`, `post`, `put`, `patch`, `del`, and `delete`
|
|
9
|
+
* helpers and applies the default configuration to every request.
|
|
10
|
+
*
|
|
11
|
+
* @param config - Default HTTP configuration such as base URL, headers, and retry policy
|
|
12
|
+
* @returns An object containing typed request methods
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const http = createHttp({ baseUrl: "/api", timeout: 5000 });
|
|
16
|
+
* const posts = await http.get<{ id: number; title: string }[]>("/posts");
|
|
17
|
+
*/
|
|
5
18
|
export function createHttp(config = {}) {
|
|
6
19
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
7
20
|
const base = (_a = config.baseUrl) !== null && _a !== void 0 ? _a : "";
|
|
@@ -60,7 +73,7 @@ export function createHttp(config = {}) {
|
|
|
60
73
|
if ((_b = options.signal) === null || _b === void 0 ? void 0 : _b.aborted) {
|
|
61
74
|
throw err; // User cancelled
|
|
62
75
|
}
|
|
63
|
-
throw
|
|
76
|
+
throw timeoutError(requestUrl, timeoutMs);
|
|
64
77
|
}
|
|
65
78
|
throw err;
|
|
66
79
|
}
|
|
@@ -72,7 +85,7 @@ export function createHttp(config = {}) {
|
|
|
72
85
|
res = await interceptor(res);
|
|
73
86
|
}
|
|
74
87
|
if (!res.ok) {
|
|
75
|
-
throw
|
|
88
|
+
throw httpError(res.status, res.statusText, requestUrl, method, res);
|
|
76
89
|
}
|
|
77
90
|
// Parse response based on content type
|
|
78
91
|
const contentType = res.headers.get("content-type") || "";
|
|
@@ -85,7 +98,7 @@ export function createHttp(config = {}) {
|
|
|
85
98
|
return JSON.parse(text);
|
|
86
99
|
}
|
|
87
100
|
catch (err) {
|
|
88
|
-
throw
|
|
101
|
+
throw parseError(`Failed to parse JSON response: ${err instanceof Error ? err.message : "Unknown error"}`, requestUrl);
|
|
89
102
|
}
|
|
90
103
|
}
|
|
91
104
|
if (contentType.includes("text/")) {
|
|
@@ -102,8 +115,8 @@ export function createHttp(config = {}) {
|
|
|
102
115
|
}
|
|
103
116
|
catch (err) {
|
|
104
117
|
// Retry logic for network errors or 5xx errors
|
|
105
|
-
const
|
|
106
|
-
|
|
118
|
+
const is5xx = isHttpError(err) && err.status >= 500 && err.status < 600;
|
|
119
|
+
const shouldRetry = attempts < maxAttempts && (!isHttpError(err) || is5xx);
|
|
107
120
|
if (shouldRetry) {
|
|
108
121
|
await sleep(retryDelay * attempts);
|
|
109
122
|
continue;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
export { signal, effect } from "./signals";
|
|
2
|
+
export { registerInstance, destroyNode } from "./each";
|
|
2
3
|
export { bindText, bindAttr, bindClass } from "./dom";
|
|
3
4
|
export { createHttp } from "./http";
|
|
4
|
-
export {
|
|
5
|
+
export { registerComponent, getComponentFactory, mountComponents } from "./registry";
|
|
6
|
+
export { registerPage, getPageModule, runPageInit } from "./page-registry";
|
|
7
|
+
export { markMounted, isMounted } from "./mount-state";
|
|
8
|
+
export { matchPath, splitPath } from "./router/path-matcher";
|
|
9
|
+
export type { RouteDef } from "./router/path-matcher";
|
|
10
|
+
export { init } from "./init";
|
|
11
|
+
export type { InitConfig } from "./init";
|
|
12
|
+
export { getParam, getParams, navigate, goBack } from "./router/index";
|
|
5
13
|
export { onMount, onDestroy } from "./router/lifecycle";
|
|
6
14
|
export type { TinyFxContext } from "./context";
|
|
7
|
-
export type { RouteMap, RouteMeta } from "./router/
|
|
15
|
+
export type { RouteMap, RouteMeta } from "./router/index";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
// @tinyfx/runtime — barrel export
|
|
2
2
|
export { signal, effect } from "./signals";
|
|
3
|
+
export { registerInstance, destroyNode } from "./each";
|
|
3
4
|
export { bindText, bindAttr, bindClass } from "./dom";
|
|
4
5
|
export { createHttp } from "./http";
|
|
5
|
-
|
|
6
|
+
// New decoupled APIs
|
|
7
|
+
export { registerComponent, getComponentFactory, mountComponents } from "./registry";
|
|
8
|
+
export { registerPage, getPageModule, runPageInit } from "./page-registry";
|
|
9
|
+
export { markMounted, isMounted } from "./mount-state";
|
|
10
|
+
export { matchPath, splitPath } from "./router/path-matcher";
|
|
11
|
+
export { init } from "./init";
|
|
12
|
+
// Keep existing router exports
|
|
13
|
+
export { getParam, getParams, navigate, goBack } from "./router/index";
|
|
6
14
|
export { onMount, onDestroy } from "./router/lifecycle";
|
package/dist/init.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { TinyFxContext } from "./context";
|
|
2
|
+
import type { RouteDef } from "./router/path-matcher";
|
|
3
|
+
export interface InitConfig {
|
|
4
|
+
routes: Record<string, RouteDef>;
|
|
5
|
+
setupDirectives?: (ctx: TinyFxContext) => void;
|
|
6
|
+
}
|
|
7
|
+
export interface InitResult {
|
|
8
|
+
matchedPath: string;
|
|
9
|
+
params: Record<string, string>;
|
|
10
|
+
instances: any[];
|
|
11
|
+
}
|
|
12
|
+
export declare function init(config: InitConfig): InitResult | null;
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// @tinyfx/runtime — unified initialization
|
|
2
|
+
// Single entry point for TinyFX bootstrap.
|
|
3
|
+
import { matchPath, splitPath } from "./router/path-matcher";
|
|
4
|
+
import { initLifecycle } from "./router/lifecycle";
|
|
5
|
+
import { highlightActiveLinks } from "./router/active-links";
|
|
6
|
+
import { navigate } from "./router/index";
|
|
7
|
+
import { setParams } from "./router/params";
|
|
8
|
+
import { mountComponents } from "./registry";
|
|
9
|
+
import { runPageInit } from "./page-registry";
|
|
10
|
+
let initialized = false;
|
|
11
|
+
export function init(config) {
|
|
12
|
+
if (initialized)
|
|
13
|
+
return null;
|
|
14
|
+
initialized = true;
|
|
15
|
+
const pathname = window.location.pathname;
|
|
16
|
+
const pathSegments = splitPath(pathname);
|
|
17
|
+
let matchedPath = null;
|
|
18
|
+
let params = {};
|
|
19
|
+
for (const [path, def] of Object.entries(config.routes)) {
|
|
20
|
+
const result = matchPath(def, pathSegments);
|
|
21
|
+
if (result) {
|
|
22
|
+
matchedPath = path;
|
|
23
|
+
params = result;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
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/.");
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
initLifecycle();
|
|
33
|
+
highlightActiveLinks(pathname);
|
|
34
|
+
setParams(params);
|
|
35
|
+
const ctx = { params, navigate };
|
|
36
|
+
const instances = mountComponents(ctx);
|
|
37
|
+
if (config.setupDirectives) {
|
|
38
|
+
config.setupDirectives(ctx);
|
|
39
|
+
}
|
|
40
|
+
runPageInit(matchedPath, ctx);
|
|
41
|
+
return { matchedPath, params, instances };
|
|
42
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mark an element as mounted. Returns false if already mounted.
|
|
3
|
+
*
|
|
4
|
+
* @param el - The element to mark
|
|
5
|
+
* @returns true if newly marked, false if already mounted
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* if (!markMounted(el)) return undefined;
|
|
9
|
+
*/
|
|
10
|
+
export declare function markMounted(el: HTMLElement): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Check if an element has been marked as mounted.
|
|
13
|
+
*
|
|
14
|
+
* @param el - The element to check
|
|
15
|
+
* @returns true if the element is marked as mounted
|
|
16
|
+
*/
|
|
17
|
+
export declare function isMounted(el: HTMLElement): boolean;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// @tinyfx/runtime — mount state tracking
|
|
2
|
+
// WeakSet-based mount tracking that is safe for minifier mangling.
|
|
3
|
+
const mounted = new WeakSet();
|
|
4
|
+
/**
|
|
5
|
+
* Mark an element as mounted. Returns false if already mounted.
|
|
6
|
+
*
|
|
7
|
+
* @param el - The element to mark
|
|
8
|
+
* @returns true if newly marked, false if already mounted
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* if (!markMounted(el)) return undefined;
|
|
12
|
+
*/
|
|
13
|
+
export function markMounted(el) {
|
|
14
|
+
if (mounted.has(el))
|
|
15
|
+
return false;
|
|
16
|
+
mounted.add(el);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if an element has been marked as mounted.
|
|
21
|
+
*
|
|
22
|
+
* @param el - The element to check
|
|
23
|
+
* @returns true if the element is marked as mounted
|
|
24
|
+
*/
|
|
25
|
+
export function isMounted(el) {
|
|
26
|
+
return mounted.has(el);
|
|
27
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TinyFxContext } from "./context";
|
|
2
|
+
type PageModule = {
|
|
3
|
+
init?: (el: HTMLElement, ctx: TinyFxContext) => void;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Register a page module by its route path.
|
|
7
|
+
*
|
|
8
|
+
* @param path - The route path (e.g., "/blog/:slug")
|
|
9
|
+
* @param module - The page module object with optional init()
|
|
10
|
+
* @returns Nothing
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* registerPage("/blog/:slug", PageModule);
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerPage(path: string, module: PageModule): void;
|
|
16
|
+
/**
|
|
17
|
+
* Retrieve a registered page module.
|
|
18
|
+
*
|
|
19
|
+
* @param path - The route path to look up
|
|
20
|
+
* @returns The page module, or undefined if not registered
|
|
21
|
+
*/
|
|
22
|
+
export declare function getPageModule(path: string): PageModule | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Run the init() function for a registered page module.
|
|
25
|
+
*
|
|
26
|
+
* @param path - The route path of the page to initialize
|
|
27
|
+
* @param ctx - The TinyFx context to pass to page init
|
|
28
|
+
* @returns Nothing
|
|
29
|
+
*/
|
|
30
|
+
export declare function runPageInit(path: string, ctx: TinyFxContext): void;
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// @tinyfx/runtime — page module registry
|
|
2
|
+
// Decoupled page module registration for tree-shakable bundles.
|
|
3
|
+
const modules = new Map();
|
|
4
|
+
/**
|
|
5
|
+
* Register a page module by its route path.
|
|
6
|
+
*
|
|
7
|
+
* @param path - The route path (e.g., "/blog/:slug")
|
|
8
|
+
* @param module - The page module object with optional init()
|
|
9
|
+
* @returns Nothing
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* registerPage("/blog/:slug", PageModule);
|
|
13
|
+
*/
|
|
14
|
+
export function registerPage(path, module) {
|
|
15
|
+
modules.set(path, module);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Retrieve a registered page module.
|
|
19
|
+
*
|
|
20
|
+
* @param path - The route path to look up
|
|
21
|
+
* @returns The page module, or undefined if not registered
|
|
22
|
+
*/
|
|
23
|
+
export function getPageModule(path) {
|
|
24
|
+
return modules.get(path);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Run the init() function for a registered page module.
|
|
28
|
+
*
|
|
29
|
+
* @param path - The route path of the page to initialize
|
|
30
|
+
* @param ctx - The TinyFx context to pass to page init
|
|
31
|
+
* @returns Nothing
|
|
32
|
+
*/
|
|
33
|
+
export function runPageInit(path, ctx) {
|
|
34
|
+
const mod = modules.get(path);
|
|
35
|
+
if (typeof (mod === null || mod === void 0 ? void 0 : mod.init) === "function") {
|
|
36
|
+
mod.init(document.body, ctx);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TinyFxContext } from "./context";
|
|
2
|
+
type ComponentFactory = (el: HTMLElement, ctx: TinyFxContext) => any;
|
|
3
|
+
/**
|
|
4
|
+
* Register a component factory by name.
|
|
5
|
+
*
|
|
6
|
+
* @param name - The component name (matches `data-component` attribute)
|
|
7
|
+
* @param factory - A function that mounts the component and returns an instance
|
|
8
|
+
* @returns Nothing
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* registerComponent("Button", (el, ctx) => {
|
|
12
|
+
* return ButtonModule.mount(el, ctx);
|
|
13
|
+
* });
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerComponent(name: string, factory: ComponentFactory): void;
|
|
16
|
+
/**
|
|
17
|
+
* Retrieve a registered component factory.
|
|
18
|
+
*
|
|
19
|
+
* @param name - The component name to look up
|
|
20
|
+
* @returns The factory function, or undefined if not registered
|
|
21
|
+
*/
|
|
22
|
+
export declare function getComponentFactory(name: string): ComponentFactory | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Mount all registered components found within a root element.
|
|
25
|
+
*
|
|
26
|
+
* @param ctx - The TinyFx context to pass to each component
|
|
27
|
+
* @param root - The root element to scan (defaults to document)
|
|
28
|
+
* @returns Array of mounted component instances
|
|
29
|
+
*/
|
|
30
|
+
export declare function mountComponents(ctx: TinyFxContext, root?: Document | HTMLElement): any[];
|
|
31
|
+
export {};
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// @tinyfx/runtime — component registry
|
|
2
|
+
// Decoupled component registration for tree-shakable bundles.
|
|
3
|
+
const factories = new Map();
|
|
4
|
+
/**
|
|
5
|
+
* Register a component factory by name.
|
|
6
|
+
*
|
|
7
|
+
* @param name - The component name (matches `data-component` attribute)
|
|
8
|
+
* @param factory - A function that mounts the component and returns an instance
|
|
9
|
+
* @returns Nothing
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* registerComponent("Button", (el, ctx) => {
|
|
13
|
+
* return ButtonModule.mount(el, ctx);
|
|
14
|
+
* });
|
|
15
|
+
*/
|
|
16
|
+
export function registerComponent(name, factory) {
|
|
17
|
+
factories.set(name, factory);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Retrieve a registered component factory.
|
|
21
|
+
*
|
|
22
|
+
* @param name - The component name to look up
|
|
23
|
+
* @returns The factory function, or undefined if not registered
|
|
24
|
+
*/
|
|
25
|
+
export function getComponentFactory(name) {
|
|
26
|
+
return factories.get(name);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Mount all registered components found within a root element.
|
|
30
|
+
*
|
|
31
|
+
* @param ctx - The TinyFx context to pass to each component
|
|
32
|
+
* @param root - The root element to scan (defaults to document)
|
|
33
|
+
* @returns Array of mounted component instances
|
|
34
|
+
*/
|
|
35
|
+
export function mountComponents(ctx, root = document) {
|
|
36
|
+
const instances = [];
|
|
37
|
+
root.querySelectorAll("[data-component]").forEach((el) => {
|
|
38
|
+
const name = el.getAttribute("data-component");
|
|
39
|
+
if (!name)
|
|
40
|
+
return;
|
|
41
|
+
const factory = factories.get(name);
|
|
42
|
+
if (!factory)
|
|
43
|
+
return;
|
|
44
|
+
const inst = factory(el, ctx);
|
|
45
|
+
if (inst)
|
|
46
|
+
instances.push(inst);
|
|
47
|
+
});
|
|
48
|
+
return instances;
|
|
49
|
+
}
|
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
* Only the pathname portion of each href is compared (query strings and
|
|
6
6
|
* fragments are ignored).
|
|
7
7
|
*
|
|
8
|
+
* @param pathname - The pathname to compare each link against
|
|
9
|
+
* @returns Nothing
|
|
10
|
+
*
|
|
8
11
|
* @example
|
|
9
12
|
* // In a nav: <a href="/blog">Blog</a>
|
|
10
13
|
* // On the /blog page this element will get data-active="true"
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
* Only the pathname portion of each href is compared (query strings and
|
|
7
7
|
* fragments are ignored).
|
|
8
8
|
*
|
|
9
|
+
* @param pathname - The pathname to compare each link against
|
|
10
|
+
* @returns Nothing
|
|
11
|
+
*
|
|
9
12
|
* @example
|
|
10
13
|
* // In a nav: <a href="/blog">Blog</a>
|
|
11
14
|
* // On the /blog page this element will get data-active="true"
|
package/dist/router/index.d.ts
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
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
1
|
/**
|
|
7
|
-
*
|
|
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
|
-
* });
|
|
2
|
+
* Metadata for a single route produced by the compiler.
|
|
23
3
|
*/
|
|
24
|
-
export
|
|
4
|
+
export interface RouteMeta {
|
|
5
|
+
/** The page name, derived from the source filename. */
|
|
6
|
+
page: string;
|
|
7
|
+
/** The declared URL pattern, e.g. "/blog/:slug". */
|
|
8
|
+
path: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The full route map emitted by `tinyfx build` into `tinyfx.gen.ts`.
|
|
12
|
+
*/
|
|
13
|
+
export type RouteMap = Record<string, RouteMeta>;
|
|
14
|
+
/**
|
|
15
|
+
* Navigate to the given path.
|
|
16
|
+
* This is a full browser navigation — not a DOM swap.
|
|
17
|
+
*/
|
|
18
|
+
export declare function navigate(path: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Go back one step in the browser history.
|
|
21
|
+
*/
|
|
22
|
+
export declare function goBack(): void;
|
|
23
|
+
export { getParam, getParams } from "./params";
|
package/dist/router/index.js
CHANGED
|
@@ -1,38 +1,15 @@
|
|
|
1
|
-
// @tinyfx/runtime — router
|
|
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 };
|
|
1
|
+
// @tinyfx/runtime — router utilities
|
|
7
2
|
/**
|
|
8
|
-
*
|
|
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
|
-
* });
|
|
3
|
+
* Navigate to the given path.
|
|
4
|
+
* This is a full browser navigation — not a DOM swap.
|
|
24
5
|
*/
|
|
25
|
-
export function
|
|
26
|
-
|
|
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/.");
|
|
6
|
+
export function navigate(path) {
|
|
7
|
+
window.location.href = path;
|
|
38
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Go back one step in the browser history.
|
|
11
|
+
*/
|
|
12
|
+
export function goBack() {
|
|
13
|
+
window.history.go(-1);
|
|
14
|
+
}
|
|
15
|
+
export { getParam, getParams } from "./params";
|
|
@@ -4,21 +4,47 @@ type LifecycleCallback = () => void;
|
|
|
4
4
|
*
|
|
5
5
|
* - If the document is still loading, the callback runs after DOMContentLoaded.
|
|
6
6
|
* - If the document is already loaded, the callback runs immediately.
|
|
7
|
+
*
|
|
8
|
+
* @param fn - The callback to run on mount
|
|
9
|
+
* @returns Nothing
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* onMount(() => {
|
|
13
|
+
* console.log("page ready");
|
|
14
|
+
* });
|
|
7
15
|
*/
|
|
8
16
|
export declare function onMount(fn: LifecycleCallback): void;
|
|
9
17
|
/**
|
|
10
18
|
* Register a callback to run when the user navigates away from this page.
|
|
11
19
|
* Fires on the browser's `pagehide` event.
|
|
20
|
+
*
|
|
21
|
+
* @param fn - The callback to run during page teardown
|
|
22
|
+
* @returns Nothing
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* onDestroy(() => {
|
|
26
|
+
* console.log("page leaving");
|
|
27
|
+
* });
|
|
12
28
|
*/
|
|
13
29
|
export declare function onDestroy(fn: LifecycleCallback): void;
|
|
14
30
|
/**
|
|
15
31
|
* Trigger all registered `onMount` callbacks.
|
|
16
|
-
* Called by `
|
|
32
|
+
* Called by `init()` after matching the current route.
|
|
33
|
+
*
|
|
34
|
+
* @returns Nothing
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* flushMount();
|
|
17
38
|
*/
|
|
18
39
|
export declare function flushMount(): void;
|
|
19
40
|
/**
|
|
20
41
|
* Wire up registered `onDestroy` callbacks to the `pagehide` event.
|
|
21
|
-
* Called by `
|
|
42
|
+
* Called by `init()` after matching the current route.
|
|
43
|
+
*
|
|
44
|
+
* @returns Nothing
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* initLifecycle();
|
|
22
48
|
*/
|
|
23
49
|
export declare function initLifecycle(): void;
|
|
24
50
|
export {};
|