@ossido-labs/ossido-router 0.1.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.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +5 -0
  3. package/dist/esm/components/CriticalCss.d.ts +13 -0
  4. package/dist/esm/components/CriticalCss.js +22 -0
  5. package/dist/esm/components/CriticalCss.js.map +1 -0
  6. package/dist/esm/components/Link.d.ts +30 -0
  7. package/dist/esm/components/Link.js +47 -0
  8. package/dist/esm/components/Link.js.map +1 -0
  9. package/dist/esm/components/Matches.d.ts +7 -0
  10. package/dist/esm/components/Matches.js +22 -0
  11. package/dist/esm/components/Matches.js.map +1 -0
  12. package/dist/esm/components/NotFound.d.ts +5 -0
  13. package/dist/esm/components/NotFound.js +22 -0
  14. package/dist/esm/components/NotFound.js.map +1 -0
  15. package/dist/esm/components/NotFoundDefaultContent.d.ts +7 -0
  16. package/dist/esm/components/NotFoundDefaultContent.js +29 -0
  17. package/dist/esm/components/NotFoundDefaultContent.js.map +1 -0
  18. package/dist/esm/components/OssidoErrorBoundary.d.ts +39 -0
  19. package/dist/esm/components/OssidoErrorBoundary.js +48 -0
  20. package/dist/esm/components/OssidoErrorBoundary.js.map +1 -0
  21. package/dist/esm/components/Redirect.d.ts +11 -0
  22. package/dist/esm/components/Redirect.js +21 -0
  23. package/dist/esm/components/Redirect.js.map +1 -0
  24. package/dist/esm/components/RouteDataLoader.d.ts +21 -0
  25. package/dist/esm/components/RouteDataLoader.js +27 -0
  26. package/dist/esm/components/RouteDataLoader.js.map +1 -0
  27. package/dist/esm/components/RouteMatch.d.ts +15 -0
  28. package/dist/esm/components/RouteMatch.js +81 -0
  29. package/dist/esm/components/RouteMatch.js.map +1 -0
  30. package/dist/esm/components/RouterContext.d.ts +56 -0
  31. package/dist/esm/components/RouterContext.js +32 -0
  32. package/dist/esm/components/RouterContext.js.map +1 -0
  33. package/dist/esm/components/RouterContextProvider.d.ts +10 -0
  34. package/dist/esm/components/RouterContextProvider.js +122 -0
  35. package/dist/esm/components/RouterContextProvider.js.map +1 -0
  36. package/dist/esm/components/RouterProvider.d.ts +15 -0
  37. package/dist/esm/components/RouterProvider.js +28 -0
  38. package/dist/esm/components/RouterProvider.js.map +1 -0
  39. package/dist/esm/data/resourceCache.d.ts +75 -0
  40. package/dist/esm/data/resourceCache.js +148 -0
  41. package/dist/esm/data/resourceCache.js.map +1 -0
  42. package/dist/esm/hooks/useRoute.d.ts +14 -0
  43. package/dist/esm/hooks/useRoute.js +22 -0
  44. package/dist/esm/hooks/useRoute.js.map +1 -0
  45. package/dist/esm/hooks/useRouter.d.ts +47 -0
  46. package/dist/esm/hooks/useRouter.js +49 -0
  47. package/dist/esm/hooks/useRouter.js.map +1 -0
  48. package/dist/esm/hot.d.ts +9 -0
  49. package/dist/esm/hot.js +32 -0
  50. package/dist/esm/hot.js.map +1 -0
  51. package/dist/esm/index.d.ts +9 -0
  52. package/dist/esm/index.js +9 -0
  53. package/dist/esm/route.d.ts +70 -0
  54. package/dist/esm/route.js +50 -0
  55. package/dist/esm/route.js.map +1 -0
  56. package/dist/esm/router.d.ts +27 -0
  57. package/dist/esm/router.js +52 -0
  58. package/dist/esm/router.js.map +1 -0
  59. package/dist/esm/types.d.ts +63 -0
  60. package/dist/esm/utils/from-url-to-parsed-location.d.ts +2 -0
  61. package/dist/esm/utils/from-url-to-parsed-location.js +15 -0
  62. package/dist/esm/utils/from-url-to-parsed-location.js.map +1 -0
  63. package/dist/esm/utils/match-route.d.ts +16 -0
  64. package/dist/esm/utils/match-route.js +72 -0
  65. package/dist/esm/utils/match-route.js.map +1 -0
  66. package/dist/esm/utils/preload-route-chain.d.ts +16 -0
  67. package/dist/esm/utils/preload-route-chain.js +31 -0
  68. package/dist/esm/utils/preload-route-chain.js.map +1 -0
  69. package/dist/esm/utils/view-transition.d.ts +18 -0
  70. package/dist/esm/utils/view-transition.js +35 -0
  71. package/dist/esm/utils/view-transition.js.map +1 -0
  72. package/dist/esm/utils.d.ts +6 -0
  73. package/dist/esm/utils.js +20 -0
  74. package/dist/esm/utils.js.map +1 -0
  75. package/package.json +64 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preload-route-chain.js","names":[],"sources":["../../../src/utils/preload-route-chain.ts"],"sourcesContent":["import type { Route } from '../route';\nimport type { RouterInstanceType } from '../router';\n\nimport { matchRoute } from './match-route';\n\n/**\n * Preload the code of the route matching `pathname` and of every layout that\n * wraps it, so a subsequent render commits the components eagerly instead of\n * suspending on their `React.lazy` chunk.\n *\n * Used ahead of the *initial* render on both sides — the server before\n * streaming (a suspending route would push the page content into an\n * out-of-order late chunk, painting an empty shell first) and the client\n * before hydration (so the first client render matches that inline HTML).\n * Client-side navigation has its own preload in `RouterContext`.\n *\n * A failed chunk load resolves anyway: the render then falls back to the lazy\n * component, which surfaces the load error through the route's error boundary.\n */\nexport async function preloadRouteChain(\n router: RouterInstanceType,\n pathname?: string,\n): Promise<void> {\n const matched = matchRoute(router.routesById, pathname);\n if (!matched) return;\n\n const pending: Array<Promise<void>> = [];\n for (\n let node: Route | undefined = matched;\n node;\n node = node.isRoot ? undefined : node.options.getParentRoute?.()\n ) {\n const preload = node.component.preload;\n if (preload) pending.push(preload().catch(() => undefined));\n }\n\n await Promise.all(pending);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAmBA,eAAsB,kBACpB,QACA,UACe;CACf,MAAM,UAAU,WAAW,OAAO,YAAY,QAAQ;CACtD,IAAI,CAAC,SAAS;CAEd,MAAM,UAAgC,CAAC;CACvC,KACE,IAAI,OAA0B,SAC9B,MACA,OAAO,KAAK,SAAS,SAAY,KAAK,QAAQ,iBAAiB,GAC/D;EACA,MAAM,UAAU,KAAK,UAAU;EAC/B,IAAI,SAAS,QAAQ,KAAK,QAAQ,CAAC,CAAC,YAAY,MAAS,CAAC;CAC5D;CAEA,MAAM,QAAQ,IAAI,OAAO;AAC3B"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Whether the app opted into view transitions at build time. Baked in via the
3
+ * vite `define` (`__OSSIDO_VIEW_TRANSITIONS__`); `false` when the define is
4
+ * absent (tests / SSR), keeping transitions opt-in.
5
+ */
6
+ export declare const VIEW_TRANSITIONS_ENABLED: boolean;
7
+ /**
8
+ * Run a router `commit` inside `document.startViewTransition` when `enabled`, the
9
+ * API is supported, and the user hasn't requested reduced motion — otherwise run
10
+ * it directly.
11
+ *
12
+ * The commit's React state updates are wrapped in `flushSync` so the DOM shows
13
+ * the new page before the API captures its "after" snapshot; React state updates
14
+ * are async and wouldn't be applied in time otherwise. This is safe because the
15
+ * router prefetches the destination's data + code before committing, so the new
16
+ * route renders synchronously (no Suspense fallback flash).
17
+ */
18
+ export declare function runCommit(commit: () => void, enabled: boolean): void;
@@ -0,0 +1,35 @@
1
+ import { flushSync } from "react-dom";
2
+
3
+ //#region src/utils/view-transition.ts
4
+ /**
5
+ * Whether the app opted into view transitions at build time. Baked in via the
6
+ * vite `define` (`__OSSIDO_VIEW_TRANSITIONS__`); `false` when the define is
7
+ * absent (tests / SSR), keeping transitions opt-in.
8
+ */
9
+ const VIEW_TRANSITIONS_ENABLED = typeof __OSSIDO_VIEW_TRANSITIONS__ !== "undefined" && __OSSIDO_VIEW_TRANSITIONS__;
10
+ /**
11
+ * Run a router `commit` inside `document.startViewTransition` when `enabled`, the
12
+ * API is supported, and the user hasn't requested reduced motion — otherwise run
13
+ * it directly.
14
+ *
15
+ * The commit's React state updates are wrapped in `flushSync` so the DOM shows
16
+ * the new page before the API captures its "after" snapshot; React state updates
17
+ * are async and wouldn't be applied in time otherwise. This is safe because the
18
+ * router prefetches the destination's data + code before committing, so the new
19
+ * route renders synchronously (no Suspense fallback flash).
20
+ */
21
+ function runCommit(commit, enabled) {
22
+ const doc = typeof document !== "undefined" ? document : void 0;
23
+ const prefersReducedMotion = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)").matches === true;
24
+ if (enabled && doc?.startViewTransition && !prefersReducedMotion) {
25
+ doc.startViewTransition(() => {
26
+ flushSync(commit);
27
+ });
28
+ return;
29
+ }
30
+ commit();
31
+ }
32
+
33
+ //#endregion
34
+ export { VIEW_TRANSITIONS_ENABLED, runCommit };
35
+ //# sourceMappingURL=view-transition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view-transition.js","names":[],"sources":["../../../src/utils/view-transition.ts"],"sourcesContent":["import { flushSync } from 'react-dom';\n\ndeclare const __OSSIDO_VIEW_TRANSITIONS__: boolean;\n\n/**\n * Whether the app opted into view transitions at build time. Baked in via the\n * vite `define` (`__OSSIDO_VIEW_TRANSITIONS__`); `false` when the define is\n * absent (tests / SSR), keeping transitions opt-in.\n */\nexport const VIEW_TRANSITIONS_ENABLED =\n typeof __OSSIDO_VIEW_TRANSITIONS__ !== 'undefined' &&\n __OSSIDO_VIEW_TRANSITIONS__;\n\n/** A `Document` that may expose the View Transitions API. */\ninterface ViewTransitionDocument {\n startViewTransition?: (callback: () => void) => unknown;\n}\n\n/**\n * Run a router `commit` inside `document.startViewTransition` when `enabled`, the\n * API is supported, and the user hasn't requested reduced motion — otherwise run\n * it directly.\n *\n * The commit's React state updates are wrapped in `flushSync` so the DOM shows\n * the new page before the API captures its \"after\" snapshot; React state updates\n * are async and wouldn't be applied in time otherwise. This is safe because the\n * router prefetches the destination's data + code before committing, so the new\n * route renders synchronously (no Suspense fallback flash).\n */\nexport function runCommit(commit: () => void, enabled: boolean): void {\n const doc =\n typeof document !== 'undefined'\n ? (document as unknown as ViewTransitionDocument)\n : undefined;\n\n const prefersReducedMotion =\n typeof window !== 'undefined' &&\n window.matchMedia?.('(prefers-reduced-motion: reduce)').matches === true;\n\n if (enabled && doc?.startViewTransition && !prefersReducedMotion) {\n doc.startViewTransition(() => {\n flushSync(commit);\n });\n return;\n }\n\n commit();\n}\n"],"mappings":";;;;;;;;AASA,MAAa,2BACX,OAAO,gCAAgC,eACvC;;;;;;;;;;;;AAkBF,SAAgB,UAAU,QAAoB,SAAwB;CACpE,MAAM,MACJ,OAAO,aAAa,cACf,WACD;CAEN,MAAM,uBACJ,OAAO,WAAW,eAClB,OAAO,aAAa,kCAAkC,CAAC,CAAC,YAAY;CAEtE,IAAI,WAAW,KAAK,uBAAuB,CAAC,sBAAsB;EAChE,IAAI,0BAA0B;GAC5B,UAAU,MAAM;EAClB,CAAC;EACD;CACF;CAEA,OAAO;AACT"}
@@ -0,0 +1,6 @@
1
+ import type { Segment } from './types';
2
+ export declare function joinPaths(paths: Array<string | undefined>): string;
3
+ export declare function parsePathname(pathname?: string): Array<Segment>;
4
+ export declare function trimPathLeft(path: string): string;
5
+ export declare function trimPathRight(path: string): string;
6
+ export declare function trimPath(path: string): string;
@@ -0,0 +1,20 @@
1
+ //#region src/utils.ts
2
+ function joinPaths(paths) {
3
+ return cleanPath(paths.filter(Boolean).join("/"));
4
+ }
5
+ function cleanPath(path) {
6
+ return path.replace(/\/{2,}/g, "/");
7
+ }
8
+ function trimPathLeft(path) {
9
+ return path === "/" ? path : path.replace(/^\/{1,}/, "");
10
+ }
11
+ function trimPathRight(path) {
12
+ return path === "/" ? path : path.replace(/\/{1,}$/, "");
13
+ }
14
+ function trimPath(path) {
15
+ return trimPathRight(trimPathLeft(path));
16
+ }
17
+
18
+ //#endregion
19
+ export { joinPaths, trimPath, trimPathLeft, trimPathRight };
20
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../src/utils.ts"],"sourcesContent":["import type { Segment } from './types';\n\nexport function joinPaths(paths: Array<string | undefined>): string {\n return cleanPath(paths.filter(Boolean).join('/'));\n}\n\nfunction cleanPath(path: string): string {\n // remove double slashes\n return path.replace(/\\/{2,}/g, '/');\n}\n\nexport function parsePathname(pathname?: string): Array<Segment> {\n if (!pathname) {\n return [];\n }\n\n pathname = cleanPath(pathname);\n\n const segments: Array<Segment> = [];\n\n if (pathname.slice(0, 1) === '/') {\n pathname = pathname.substring(1);\n segments.push({\n type: 'pathname',\n value: '/',\n });\n }\n\n if (!pathname) {\n return segments;\n }\n\n // Remove empty segments and '.' segments\n const split = pathname.split('/').filter(Boolean);\n\n segments.push(\n ...split.map((part): Segment => {\n if (part === '$' || part === '*') {\n return {\n type: 'wildcard',\n value: part,\n };\n }\n\n if (part.charAt(0) === '$') {\n return {\n type: 'param',\n value: part,\n };\n }\n\n return {\n type: 'pathname',\n value: part,\n };\n }),\n );\n\n if (pathname.slice(-1) === '/') {\n pathname = pathname.substring(1);\n segments.push({\n type: 'pathname',\n value: '/',\n });\n }\n\n return segments;\n}\nexport function trimPathLeft(path: string): string {\n return path === '/' ? path : path.replace(/^\\/{1,}/, '');\n}\nexport function trimPathRight(path: string): string {\n return path === '/' ? path : path.replace(/\\/{1,}$/, '');\n}\nexport function trimPath(path: string): string {\n return trimPathRight(trimPathLeft(path));\n}\n"],"mappings":";AAEA,SAAgB,UAAU,OAA0C;CAClE,OAAO,UAAU,MAAM,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC;AAClD;AAEA,SAAS,UAAU,MAAsB;CAEvC,OAAO,KAAK,QAAQ,WAAW,GAAG;AACpC;AA2DA,SAAgB,aAAa,MAAsB;CACjD,OAAO,SAAS,MAAM,OAAO,KAAK,QAAQ,WAAW,EAAE;AACzD;AACA,SAAgB,cAAc,MAAsB;CAClD,OAAO,SAAS,MAAM,OAAO,KAAK,QAAQ,WAAW,EAAE;AACzD;AACA,SAAgB,SAAS,MAAsB;CAC7C,OAAO,cAAc,aAAa,IAAI,CAAC;AACzC"}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@ossido-labs/ossido-router",
3
+ "version": "0.1.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "description": "React routing component for the framework ossido. Ossido is the react/rust fullstack framework",
8
+ "homepage": "https://ossido.dev",
9
+ "scripts": {
10
+ "dev": "tsdown --watch",
11
+ "build": "tsdown && tsc -p tsconfig.build.json",
12
+ "prepack": "bun run build",
13
+ "lint": "oxlint",
14
+ "format": "oxfmt --check .",
15
+ "format:fix": "oxfmt .",
16
+ "typecheck": "tsc --noEmit",
17
+ "test:watch": "vitest",
18
+ "test": "vitest run"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/ossido-labs/ossido.git",
23
+ "directory": "packages/ossido-router"
24
+ },
25
+ "keywords": [],
26
+ "author": "Chris Schofield <chris@childishforces.com>",
27
+ "license": "MIT",
28
+ "type": "module",
29
+ "types": "dist/esm/index.d.ts",
30
+ "main": "dist/esm/index.js",
31
+ "module": "dist/esm/index.js",
32
+ "files": [
33
+ "dist",
34
+ "README.md"
35
+ ],
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/esm/index.d.ts",
39
+ "default": "./dist/esm/index.js"
40
+ },
41
+ "./package.json": "./package.json"
42
+ },
43
+ "peerDependencies": {
44
+ "react": ">=19.0.0",
45
+ "react-dom": ">=19.0.0"
46
+ },
47
+ "dependencies": {
48
+ "react-intersection-observer": "^9.13.0",
49
+ "@ossido-labs/ossido-ui": "0.1.0"
50
+ },
51
+ "devDependencies": {
52
+ "@testing-library/react": "16.3.0",
53
+ "@types/react": "19.1.8",
54
+ "@types/react-dom": "19.1.6",
55
+ "@vitejs/plugin-react-swc": "4.3.2",
56
+ "happy-dom": "18.0.1",
57
+ "react": "^19.2.8",
58
+ "react-dom": "^19.2.8",
59
+ "tsdown": "0.22.14",
60
+ "vite": "^8.1.5",
61
+ "vite-config": "1.0.0",
62
+ "vitest": "4.1.10"
63
+ }
64
+ }