@tinyfx/runtime 0.1.6 → 0.1.9

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.
Files changed (49) hide show
  1. package/README.md +48 -121
  2. package/dist/__tests__/mount-state.test.d.ts +1 -0
  3. package/dist/__tests__/mount-state.test.js +30 -0
  4. package/dist/__tests__/page-registry.test.d.ts +1 -0
  5. package/dist/__tests__/page-registry.test.js +38 -0
  6. package/dist/__tests__/path-matcher.test.d.ts +1 -0
  7. package/dist/__tests__/path-matcher.test.js +43 -0
  8. package/dist/__tests__/registry.test.d.ts +1 -0
  9. package/dist/__tests__/registry.test.js +67 -0
  10. package/dist/context.d.ts +19 -0
  11. package/dist/context.js +1 -0
  12. package/dist/dom.d.ts +35 -0
  13. package/dist/dom.js +35 -0
  14. package/dist/each.d.ts +10 -0
  15. package/dist/each.js +24 -0
  16. package/dist/http/data.d.ts +76 -0
  17. package/dist/http/data.js +36 -0
  18. package/dist/http/helper.d.ts +20 -0
  19. package/dist/http/helper.js +20 -0
  20. package/dist/http/http.d.ts +13 -0
  21. package/dist/http/http.js +13 -0
  22. package/dist/http.d.ts +2 -10
  23. package/dist/http.js +1 -38
  24. package/dist/index.d.ts +12 -8
  25. package/dist/index.js +11 -7
  26. package/dist/init.d.ts +12 -0
  27. package/dist/init.js +40 -0
  28. package/dist/mount-state.d.ts +17 -0
  29. package/dist/mount-state.js +27 -0
  30. package/dist/page-registry.d.ts +31 -0
  31. package/dist/page-registry.js +38 -0
  32. package/dist/registry.d.ts +31 -0
  33. package/dist/registry.js +49 -0
  34. package/dist/router/active-links.d.ts +3 -0
  35. package/dist/router/active-links.js +3 -0
  36. package/dist/router/index.d.ts +4 -0
  37. package/dist/router/index.js +4 -1
  38. package/dist/router/lifecycle.d.ts +26 -0
  39. package/dist/router/lifecycle.js +31 -1
  40. package/dist/router/navigate.d.ts +8 -0
  41. package/dist/router/navigate.js +8 -0
  42. package/dist/router/params.d.ts +20 -0
  43. package/dist/router/params.js +22 -0
  44. package/dist/router/path-matcher.d.ts +30 -0
  45. package/dist/router/path-matcher.js +45 -0
  46. package/dist/router/types.d.ts +15 -2
  47. package/dist/signals.d.ts +41 -0
  48. package/dist/signals.js +41 -0
  49. package/package.json +6 -3
@@ -1,3 +1,15 @@
1
+ /**
2
+ * Error thrown for non-success HTTP responses.
3
+ *
4
+ * @example
5
+ * try {
6
+ * await http.get("/posts");
7
+ * } catch (error) {
8
+ * if (error instanceof HttpError) {
9
+ * console.error(error.status, error.url);
10
+ * }
11
+ * }
12
+ */
1
13
  export declare class HttpError extends Error {
2
14
  readonly status: number;
3
15
  readonly statusText: string;
@@ -6,15 +18,51 @@ export declare class HttpError extends Error {
6
18
  readonly response?: Response | undefined;
7
19
  constructor(status: number, statusText: string, url: string, method: string, response?: Response | undefined);
8
20
  }
21
+ /**
22
+ * Error thrown when a request times out.
23
+ *
24
+ * @example
25
+ * try {
26
+ * await http.get("/slow", { timeout: 100 });
27
+ * } catch (error) {
28
+ * if (error instanceof HttpTimeoutError) {
29
+ * console.error(error.timeout);
30
+ * }
31
+ * }
32
+ */
9
33
  export declare class HttpTimeoutError extends Error {
10
34
  readonly url: string;
11
35
  readonly timeout: number;
12
36
  constructor(url: string, timeout: number);
13
37
  }
38
+ /**
39
+ * Error thrown when a JSON response cannot be parsed.
40
+ *
41
+ * @example
42
+ * try {
43
+ * await http.get("/broken-json");
44
+ * } catch (error) {
45
+ * if (error instanceof HttpParseError) {
46
+ * console.error(error.url);
47
+ * }
48
+ * }
49
+ */
14
50
  export declare class HttpParseError extends Error {
15
51
  readonly url: string;
16
52
  constructor(message: string, url: string);
17
53
  }
54
+ /**
55
+ * Hook that can rewrite an outgoing request before `fetch` runs.
56
+ *
57
+ * @example
58
+ * const addToken: RequestInterceptor = (url, options) => ({
59
+ * url,
60
+ * options: {
61
+ * ...options,
62
+ * headers: { ...options.headers, Authorization: "Bearer token" },
63
+ * },
64
+ * });
65
+ */
18
66
  export interface RequestInterceptor {
19
67
  (url: string, options: RequestInit): Promise<{
20
68
  url: string;
@@ -24,9 +72,28 @@ export interface RequestInterceptor {
24
72
  options: RequestInit;
25
73
  };
26
74
  }
75
+ /**
76
+ * Hook that can inspect or replace a response before TinyFX handles it.
77
+ *
78
+ * @example
79
+ * const logResponse: ResponseInterceptor = (response) => {
80
+ * console.log(response.status);
81
+ * return response;
82
+ * };
83
+ */
27
84
  export interface ResponseInterceptor {
28
85
  (response: Response): Promise<Response> | Response;
29
86
  }
87
+ /**
88
+ * Default options for an HTTP client created by `createHttp()`.
89
+ *
90
+ * @example
91
+ * const config: HttpConfig = {
92
+ * baseUrl: "/api",
93
+ * timeout: 5000,
94
+ * retries: 1,
95
+ * };
96
+ */
30
97
  export interface HttpConfig {
31
98
  baseUrl?: string;
32
99
  headers?: Record<string, string>;
@@ -36,6 +103,15 @@ export interface HttpConfig {
36
103
  requestInterceptors?: RequestInterceptor[];
37
104
  responseInterceptors?: ResponseInterceptor[];
38
105
  }
106
+ /**
107
+ * Per-request overrides for an HTTP call.
108
+ *
109
+ * @example
110
+ * const options: RequestOptions = {
111
+ * params: { page: 2 },
112
+ * timeout: 1000,
113
+ * };
114
+ */
39
115
  export interface RequestOptions {
40
116
  headers?: Record<string, string>;
41
117
  timeout?: number;
package/dist/http/data.js CHANGED
@@ -1,3 +1,15 @@
1
+ /**
2
+ * Error thrown for non-success HTTP responses.
3
+ *
4
+ * @example
5
+ * try {
6
+ * await http.get("/posts");
7
+ * } catch (error) {
8
+ * if (error instanceof HttpError) {
9
+ * console.error(error.status, error.url);
10
+ * }
11
+ * }
12
+ */
1
13
  export class HttpError extends Error {
2
14
  constructor(status, statusText, url, method, response) {
3
15
  super(`HTTP ${status} ${statusText}: ${method} ${url}`);
@@ -9,6 +21,18 @@ export class HttpError extends Error {
9
21
  this.name = "HttpError";
10
22
  }
11
23
  }
24
+ /**
25
+ * Error thrown when a request times out.
26
+ *
27
+ * @example
28
+ * try {
29
+ * await http.get("/slow", { timeout: 100 });
30
+ * } catch (error) {
31
+ * if (error instanceof HttpTimeoutError) {
32
+ * console.error(error.timeout);
33
+ * }
34
+ * }
35
+ */
12
36
  export class HttpTimeoutError extends Error {
13
37
  constructor(url, timeout) {
14
38
  super(`Request timeout after ${timeout}ms: ${url}`);
@@ -17,6 +41,18 @@ export class HttpTimeoutError extends Error {
17
41
  this.name = "HttpTimeoutError";
18
42
  }
19
43
  }
44
+ /**
45
+ * Error thrown when a JSON response cannot be parsed.
46
+ *
47
+ * @example
48
+ * try {
49
+ * await http.get("/broken-json");
50
+ * } catch (error) {
51
+ * if (error instanceof HttpParseError) {
52
+ * console.error(error.url);
53
+ * }
54
+ * }
55
+ */
20
56
  export class HttpParseError extends Error {
21
57
  constructor(message, url) {
22
58
  super(message);
@@ -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>;
@@ -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
  }
@@ -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
@@ -2,6 +2,19 @@
2
2
  // Thin, typed wrapper over fetch.
3
3
  import { HttpError, HttpParseError, HttpTimeoutError } 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 : "";
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,11 +1,15 @@
1
- export { signal, effect, Signal } from "./signals";
1
+ export { signal, effect } from "./signals";
2
+ export { registerInstance, destroyNode } from "./each";
2
3
  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";
7
- export { initRouter } from "./router/index";
8
- export { getParam } from "./router/params";
4
+ export { createHttp } from "./http";
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 { initRouter, getParam, navigate, goBack } from "./router/index";
9
13
  export { onMount, onDestroy } from "./router/lifecycle";
10
- export { navigate, goBack } from "./router/navigate";
14
+ export type { TinyFxContext } from "./context";
11
15
  export type { RouteMap, RouteMeta } from "./router/types";
package/dist/index.js CHANGED
@@ -1,10 +1,14 @@
1
1
  // @tinyfx/runtime — barrel export
2
- export { signal, effect, Signal } from "./signals";
2
+ export { signal, effect } from "./signals";
3
+ export { registerInstance, destroyNode } from "./each";
3
4
  export { bindText, bindAttr, bindClass } from "./dom";
4
- export { createHttp } from "./http/http";
5
- export { Container, createToken } from "./di";
6
- // Router hard file-based router
7
- export { initRouter } from "./router/index";
8
- export { getParam } from "./router/params";
5
+ export { createHttp } from "./http";
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 { initRouter, getParam, navigate, goBack } from "./router/index";
9
14
  export { onMount, onDestroy } from "./router/lifecycle";
10
- export { navigate, goBack } from "./router/navigate";
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,40 @@
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/navigate";
7
+ import { mountComponents } from "./registry";
8
+ import { runPageInit } from "./page-registry";
9
+ let initialized = false;
10
+ export function init(config) {
11
+ if (initialized)
12
+ return null;
13
+ initialized = true;
14
+ const pathname = window.location.pathname;
15
+ const pathSegments = splitPath(pathname);
16
+ let matchedPath = null;
17
+ let params = {};
18
+ for (const [path, def] of Object.entries(config.routes)) {
19
+ const result = matchPath(def, pathSegments);
20
+ if (result) {
21
+ matchedPath = path;
22
+ params = result;
23
+ break;
24
+ }
25
+ }
26
+ if (!matchedPath) {
27
+ console.warn(`[tinyfx] No route matched for pathname: "${pathname}". ` +
28
+ "Check that a page file exists for this URL in src/pages/.");
29
+ return null;
30
+ }
31
+ initLifecycle();
32
+ highlightActiveLinks(pathname);
33
+ const ctx = { params, navigate };
34
+ const instances = mountComponents(ctx);
35
+ if (config.setupDirectives) {
36
+ config.setupDirectives(ctx);
37
+ }
38
+ runPageInit(matchedPath, ctx);
39
+ return { matchedPath, params, instances };
40
+ }
@@ -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 {};
@@ -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"
@@ -1,5 +1,8 @@
1
1
  import type { RouteMap } from "./types";
2
+ import { navigate, goBack } from "./navigate";
3
+ import { getParam } from "./params";
2
4
  export type { RouteMap };
5
+ export { navigate, goBack, getParam };
3
6
  /**
4
7
  * Initialise the TinyFX router for the current page.
5
8
  *
@@ -9,6 +12,7 @@ export type { RouteMap };
9
12
  * navigation links.
10
13
  *
11
14
  * @param routes - The route map emitted by `tinyfx build`.
15
+ * @returns Nothing
12
16
  *
13
17
  * @example
14
18
  * // tinyfx.gen.ts (auto-generated — do not edit)
@@ -1,7 +1,9 @@
1
1
  // @tinyfx/runtime — router entry point
2
- import { resolveParams } from "./params";
2
+ import { navigate, goBack } from "./navigate";
3
+ import { getParam, resolveParams } from "./params";
3
4
  import { initLifecycle } from "./lifecycle";
4
5
  import { highlightActiveLinks } from "./active-links";
6
+ export { navigate, goBack, getParam };
5
7
  /**
6
8
  * Initialise the TinyFX router for the current page.
7
9
  *
@@ -11,6 +13,7 @@ import { highlightActiveLinks } from "./active-links";
11
13
  * navigation links.
12
14
  *
13
15
  * @param routes - The route map emitted by `tinyfx build`.
16
+ * @returns Nothing
14
17
  *
15
18
  * @example
16
19
  * // tinyfx.gen.ts (auto-generated — do not edit)