@timber-js/app 0.1.13 → 0.1.15

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.
@@ -1 +1 @@
1
- {"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/plugins/shims.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA6DhD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAuGvD"}
1
+ {"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/plugins/shims.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA6DhD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAkIvD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "Vite-native React framework for Cloudflare Workers — correct HTTP semantics, real status codes, pages that work without JavaScript",
5
5
  "keywords": [
6
6
  "cloudflare-workers",
@@ -42,21 +42,24 @@ const SSR_NOOP_ROUTER: AppRouterInstance = {
42
42
  *
43
43
  * Compatible with Next.js's `useRouter()` from `next/navigation`.
44
44
  *
45
- * Returns a no-op router during SSR or before the client router is bootstrapped,
46
- * so components that call useRouter() at the function level (e.g. TransitionLink)
47
- * do not crash during server-side rendering.
45
+ * Methods lazily resolve the global router when invoked (during user
46
+ * interaction) rather than capturing it at render time. This is critical
47
+ * because during hydration, React synchronously executes component render
48
+ * functions *before* the router is bootstrapped in browser-entry.ts.
49
+ * If we eagerly captured the router during render, components would get
50
+ * the SSR_NOOP_ROUTER and be stuck with silent no-ops forever.
51
+ *
52
+ * Returns safe no-ops during SSR (typeof window === 'undefined').
48
53
  */
49
54
  export function useRouter(): AppRouterInstance {
50
- let router;
51
- try {
52
- router = getRouter();
53
- } catch {
54
- // Router not yet bootstrapped — SSR or early client render before bootstrap().
55
+ // SSR guard — on the server there's no router and no window.
56
+ if (typeof window === 'undefined') {
55
57
  return SSR_NOOP_ROUTER;
56
58
  }
57
59
 
58
60
  return {
59
61
  push(href: string, options?: { scroll?: boolean }) {
62
+ const router = getRouter();
60
63
  // Wrap in startTransition so React 19 tracks the async navigation.
61
64
  // React 19's startTransition accepts async callbacks — it keeps
62
65
  // isPending=true until the returned promise resolves. This means
@@ -67,11 +70,13 @@ export function useRouter(): AppRouterInstance {
67
70
  });
68
71
  },
69
72
  replace(href: string, options?: { scroll?: boolean }) {
73
+ const router = getRouter();
70
74
  startTransition(async () => {
71
75
  await router.navigate(href, { scroll: options?.scroll, replace: true });
72
76
  });
73
77
  },
74
78
  refresh() {
79
+ const router = getRouter();
75
80
  startTransition(async () => {
76
81
  await router.refresh();
77
82
  });
@@ -83,6 +88,7 @@ export function useRouter(): AppRouterInstance {
83
88
  window.history.forward();
84
89
  },
85
90
  prefetch(href: string) {
91
+ const router = getRouter();
86
92
  router.prefetch(href);
87
93
  },
88
94
  };
@@ -177,11 +177,38 @@ export function timberShims(_ctx: PluginContext): Plugin {
177
177
  // Server modules must never be bundled into the browser — if this
178
178
  // module is reached, there is a broken import chain that needs fixing.
179
179
  if (id === '\0timber:server-empty') {
180
- return `throw new Error(
181
- "[timber] @timber-js/app/server was imported from client code. " +
180
+ // Export named stubs instead of throwing at evaluation time.
181
+ // Throwing at eval breaks the module system — the browser can't
182
+ // resolve named imports like `import { notFound } from '...'`.
183
+ // Instead, each stub throws at call time with a clear message.
184
+ return `
185
+ const msg = "[timber] @timber-js/app/server was imported from client code. " +
182
186
  "Server modules (headers, cookies, redirect, deny, etc.) cannot be used in client components. " +
183
- "If you need these APIs, move the import to a server component or middleware."
184
- );`;
187
+ "If you need these APIs, move the import to a server component or middleware.";
188
+ function stub() { throw new Error(msg); }
189
+ export const headers = stub;
190
+ export const cookies = stub;
191
+ export const searchParams = stub;
192
+ export const deny = stub;
193
+ export const notFound = stub;
194
+ export const redirect = stub;
195
+ export const permanentRedirect = stub;
196
+ export const redirectExternal = stub;
197
+ export const waitUntil = stub;
198
+ export const RenderError = stub;
199
+ export const RedirectType = {};
200
+ export const DenySignal = stub;
201
+ export const RedirectSignal = stub;
202
+ export const createPipeline = stub;
203
+ export const revalidatePath = stub;
204
+ export const revalidateTag = stub;
205
+ export const createActionClient = stub;
206
+ export const ActionError = stub;
207
+ export const validated = stub;
208
+ export const getFormFlash = stub;
209
+ export const parseFormData = stub;
210
+ export const coerce = stub;
211
+ `;
185
212
  }
186
213
  },
187
214
  };