@timber-js/app 0.2.0-alpha.101 → 0.2.0-alpha.103

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 (80) hide show
  1. package/dist/_chunks/{actions-BsfxK6yu.js → actions-n9qTlpmY.js} +7 -10
  2. package/dist/_chunks/actions-n9qTlpmY.js.map +1 -0
  3. package/dist/_chunks/{build-output-helper-DXnW0qjz.js → build-output-helper-5TK2Emwp.js} +1 -1
  4. package/dist/_chunks/{build-output-helper-DXnW0qjz.js.map → build-output-helper-5TK2Emwp.js.map} +1 -1
  5. package/dist/_chunks/{define-k32T6NL2.js → define-Btmz9B20.js} +3 -3
  6. package/dist/_chunks/{define-k32T6NL2.js.map → define-Btmz9B20.js.map} +1 -1
  7. package/dist/_chunks/{define-B-Q_UMOD.js → define-Dn98bPbb.js} +3 -3
  8. package/dist/_chunks/{define-B-Q_UMOD.js.map → define-Dn98bPbb.js.map} +1 -1
  9. package/dist/_chunks/{format-Bcn-Iv1x.js → format-CYBGxKtc.js} +1 -1
  10. package/dist/_chunks/{format-Bcn-Iv1x.js.map → format-CYBGxKtc.js.map} +1 -1
  11. package/dist/_chunks/{handler-store-B-lqaGyh.js → handler-store-B_df5Npe.js} +1 -1
  12. package/dist/_chunks/{handler-store-B-lqaGyh.js.map → handler-store-B_df5Npe.js.map} +1 -1
  13. package/dist/_chunks/{logger-0m8MsKdc.js → logger-DDmK5skc.js} +2 -2
  14. package/dist/_chunks/{logger-0m8MsKdc.js.map → logger-DDmK5skc.js.map} +1 -1
  15. package/dist/_chunks/merge-search-params-Cm_KIWDX.js +41 -0
  16. package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +1 -0
  17. package/dist/_chunks/{metadata-routes-BU684ls2.js → metadata-routes-Cjmvi3rQ.js} +1 -1
  18. package/dist/_chunks/{metadata-routes-BU684ls2.js.map → metadata-routes-Cjmvi3rQ.js.map} +1 -1
  19. package/dist/_chunks/{navigation-root-yx917rG0.js → navigation-root-2ypaZQDx.js} +2 -2
  20. package/dist/_chunks/{navigation-root-yx917rG0.js.map → navigation-root-2ypaZQDx.js.map} +1 -1
  21. package/dist/_chunks/{registry-I2ss-lvy.js → registry-DB93kT5c.js} +1 -1
  22. package/dist/_chunks/{registry-I2ss-lvy.js.map → registry-DB93kT5c.js.map} +1 -1
  23. package/dist/_chunks/{router-ref-h3-UaCQv.js → router-ref-TkUdmqtM.js} +2 -2
  24. package/dist/_chunks/{router-ref-h3-UaCQv.js.map → router-ref-TkUdmqtM.js.map} +1 -1
  25. package/dist/_chunks/{schema-bridge-Cxu4l-7p.js → schema-bridge-C3xl_vfb.js} +1 -1
  26. package/dist/_chunks/{schema-bridge-Cxu4l-7p.js.map → schema-bridge-C3xl_vfb.js.map} +1 -1
  27. package/dist/_chunks/{segment-classify-BjfuctV2.js → segment-classify-CnfLLvir.js} +1 -1
  28. package/dist/_chunks/{segment-classify-BjfuctV2.js.map → segment-classify-CnfLLvir.js.map} +1 -1
  29. package/dist/_chunks/{segment-context-Dx_OizxD.js → segment-context-Dc-GbSTe.js} +1 -1
  30. package/dist/_chunks/{segment-context-Dx_OizxD.js.map → segment-context-Dc-GbSTe.js.map} +1 -1
  31. package/dist/_chunks/{ssr-data-B4CdH7rE.js → ssr-data-DzuI0bIV.js} +1 -1
  32. package/dist/_chunks/{ssr-data-B4CdH7rE.js.map → ssr-data-DzuI0bIV.js.map} +1 -1
  33. package/dist/_chunks/{stale-reload-Bab885FO.js → stale-reload-D6FlD3xn.js} +1 -1
  34. package/dist/_chunks/{stale-reload-Bab885FO.js.map → stale-reload-D6FlD3xn.js.map} +1 -1
  35. package/dist/_chunks/{tracing-C8V-YGsP.js → tracing-DvKLzCFa.js} +1 -1
  36. package/dist/_chunks/{tracing-C8V-YGsP.js.map → tracing-DvKLzCFa.js.map} +1 -1
  37. package/dist/_chunks/{use-query-states-B2XTqxDR.js → use-query-states-B-cLl8mx.js} +2 -2
  38. package/dist/_chunks/{use-query-states-B2XTqxDR.js.map → use-query-states-B-cLl8mx.js.map} +1 -1
  39. package/dist/_chunks/{use-segment-params-DlsOX1YS.js → use-segment-params-07V1TIxL.js} +3 -3
  40. package/dist/_chunks/{use-segment-params-DlsOX1YS.js.map → use-segment-params-07V1TIxL.js.map} +1 -1
  41. package/dist/_chunks/{merge-search-params-BphMdht_.js → validation-DfyuMoK2.js} +2 -40
  42. package/dist/_chunks/validation-DfyuMoK2.js.map +1 -0
  43. package/dist/_chunks/{walkers-Tg0Alwcg.js → walkers-DQn5NNrf.js} +4 -4
  44. package/dist/_chunks/{walkers-Tg0Alwcg.js.map → walkers-DQn5NNrf.js.map} +1 -1
  45. package/dist/_chunks/{warnings-Cg47l5sk.js → warnings-B30wpfaS.js} +2 -2
  46. package/dist/_chunks/{warnings-Cg47l5sk.js.map → warnings-B30wpfaS.js.map} +1 -1
  47. package/dist/adapters/cloudflare.js +1 -1
  48. package/dist/adapters/nitro.js +1 -1
  49. package/dist/cache/index.js +3 -3
  50. package/dist/client/error-boundary.js +2 -2
  51. package/dist/client/index.js +10 -117
  52. package/dist/client/index.js.map +1 -1
  53. package/dist/client/internal.js +7 -7
  54. package/dist/client/use-cookie.d.ts +4 -0
  55. package/dist/client/use-cookie.d.ts.map +1 -1
  56. package/dist/codec.js +1 -1
  57. package/dist/cookies/define-cookie.d.ts +5 -45
  58. package/dist/cookies/define-cookie.d.ts.map +1 -1
  59. package/dist/cookies/index.js +252 -1
  60. package/dist/cookies/index.js.map +1 -1
  61. package/dist/fonts/ast.d.ts.map +1 -1
  62. package/dist/index.js +89 -45
  63. package/dist/index.js.map +1 -1
  64. package/dist/routing/index.js +2 -2
  65. package/dist/search-params/index.js +3 -3
  66. package/dist/segment-params/index.js +1 -1
  67. package/dist/server/index.js +3 -3
  68. package/dist/server/internal.js +7 -7
  69. package/dist/server/request-context.d.ts.map +1 -1
  70. package/package.json +1 -1
  71. package/src/client/index.ts +0 -9
  72. package/src/client/use-cookie.ts +2 -2
  73. package/src/cookies/define-cookie.ts +100 -166
  74. package/src/fonts/ast.ts +64 -27
  75. package/src/server/request-context.ts +1 -8
  76. package/dist/_chunks/actions-BsfxK6yu.js.map +0 -1
  77. package/dist/_chunks/chunk-Cfpv9whK.js +0 -32
  78. package/dist/_chunks/define-cookie-BjpIt4UC.js +0 -194
  79. package/dist/_chunks/define-cookie-BjpIt4UC.js.map +0 -1
  80. package/dist/_chunks/merge-search-params-BphMdht_.js.map +0 -1
@@ -1,3 +1,3 @@
1
- import { i as INTERCEPTION_MARKERS, n as classifyUrlSegment, r as DEFAULT_PAGE_EXTENSIONS, t as classifySegment } from "../_chunks/segment-classify-BjfuctV2.js";
2
- import { i as scanRoutes, n as collectInterceptionRewrites, r as generateRouteMap, t as collectLeafRoutes } from "../_chunks/walkers-Tg0Alwcg.js";
1
+ import { i as INTERCEPTION_MARKERS, n as classifyUrlSegment, r as DEFAULT_PAGE_EXTENSIONS, t as classifySegment } from "../_chunks/segment-classify-CnfLLvir.js";
2
+ import { i as scanRoutes, n as collectInterceptionRewrites, r as generateRouteMap, t as collectLeafRoutes } from "../_chunks/walkers-DQn5NNrf.js";
3
3
  export { DEFAULT_PAGE_EXTENSIONS, INTERCEPTION_MARKERS, classifySegment, classifyUrlSegment, collectInterceptionRewrites, collectLeafRoutes, generateRouteMap, scanRoutes };
@@ -1,6 +1,6 @@
1
- import { n as registerSearchParams, t as getSearchParamsDefinition } from "../_chunks/registry-I2ss-lvy.js";
2
- import { i as isStandardSchema, n as fromSchema, r as isCodec } from "../_chunks/schema-bridge-Cxu4l-7p.js";
3
- import { n as defineSearchParams } from "../_chunks/define-B-Q_UMOD.js";
1
+ import { n as registerSearchParams, t as getSearchParamsDefinition } from "../_chunks/registry-DB93kT5c.js";
2
+ import { i as isStandardSchema, n as fromSchema, r as isCodec } from "../_chunks/schema-bridge-C3xl_vfb.js";
3
+ import { n as defineSearchParams } from "../_chunks/define-Dn98bPbb.js";
4
4
  //#region src/search-params/wrappers.ts
5
5
  /**
6
6
  * Wrap a nullable codec with a default value. When the inner codec returns
@@ -1,2 +1,2 @@
1
- import { r as defineSegmentParams } from "../_chunks/define-k32T6NL2.js";
1
+ import { r as defineSegmentParams } from "../_chunks/define-Btmz9B20.js";
2
2
  export { defineSegmentParams };
@@ -1,6 +1,6 @@
1
- import { a as getSpanId, d as withSpan, o as getTraceId, p as formFlashAls, t as addSpanEvent, v as isDebug, y as isDevMode } from "../_chunks/tracing-C8V-YGsP.js";
2
- import { t as formatSize } from "../_chunks/format-Bcn-Iv1x.js";
3
- import { E as getCookieJar, a as revalidateTag, b as getSegmentParams, c as DenySignal, f as deny, h as waitUntil, i as revalidatePath, l as RedirectSignal, m as redirectExternal, p as redirect, s as parseFormData, u as RedirectType, v as getHeaders } from "../_chunks/actions-BsfxK6yu.js";
1
+ import { a as getSpanId, d as withSpan, o as getTraceId, p as formFlashAls, t as addSpanEvent, v as isDebug, y as isDevMode } from "../_chunks/tracing-DvKLzCFa.js";
2
+ import { t as formatSize } from "../_chunks/format-CYBGxKtc.js";
3
+ import { E as getCookieJar, a as revalidateTag, b as getSegmentParams, c as DenySignal, f as deny, h as waitUntil, i as revalidatePath, l as RedirectSignal, m as redirectExternal, p as redirect, s as parseFormData, u as RedirectType, v as getHeaders } from "../_chunks/actions-n9qTlpmY.js";
4
4
  //#region src/server/sensitive-fields.ts
5
5
  /**
6
6
  * Sensitive field stripping — removes password/token/CVV-style fields
@@ -1,11 +1,11 @@
1
- import { c as replaceTraceId, d as withSpan, f as earlyHintsSenderAls, g as timingAls, i as getOtelTraceId, l as runWithTraceId, m as requestContextAls, o as getTraceId, r as generateTraceId, u as setSpanAttribute, v as isDebug } from "../_chunks/tracing-C8V-YGsP.js";
2
- import { a as warnRedirectInSuspense, c as warnSuspenseWrappingChildren, i as warnRedirectInAccess, n as setViteServer, o as warnSlowSlotWithoutSuspense, r as warnDenyInSuspense, s as warnStaticRequestApi, t as WarningId } from "../_chunks/warnings-Cg47l5sk.js";
3
- import { n as classifyUrlSegment } from "../_chunks/segment-classify-BjfuctV2.js";
4
- import { i as getMetadataRouteServePath, n as classifyMetadataRoute, r as getMetadataRouteAutoLink, t as METADATA_ROUTE_CONVENTIONS } from "../_chunks/metadata-routes-BU684ls2.js";
5
- import { a as logProxyError, c as logRequestReceived, d as logSwrRefetchFailed, f as logWaitUntilRejected, h as swallow, i as logMiddlewareShortCircuit, l as logRouteError, m as setLogger, n as logCacheMiss, o as logRenderError, p as logWaitUntilUnsupported, r as logMiddlewareError, s as logRequestCompleted, t as getLogger, u as logSlowRequest } from "../_chunks/logger-0m8MsKdc.js";
6
- import { C as setMutableCookieContext, D as getSetCookieHeaders, S as runWithRequestContext, T as getCookie, _ as getHeader, b as getSegmentParams, c as DenySignal, d as RenderError, g as applyRequestHeaderOverlay, l as RedirectSignal, n as executeAction, o as coerce, r as isRscActionRequest, t as buildNoJsResponse, w as setSegmentParams, x as markResponseFlushed, y as getSearchParams } from "../_chunks/actions-BsfxK6yu.js";
1
+ import { c as replaceTraceId, d as withSpan, f as earlyHintsSenderAls, g as timingAls, i as getOtelTraceId, l as runWithTraceId, m as requestContextAls, o as getTraceId, r as generateTraceId, u as setSpanAttribute, v as isDebug } from "../_chunks/tracing-DvKLzCFa.js";
2
+ import { a as warnRedirectInSuspense, c as warnSuspenseWrappingChildren, i as warnRedirectInAccess, n as setViteServer, o as warnSlowSlotWithoutSuspense, r as warnDenyInSuspense, s as warnStaticRequestApi, t as WarningId } from "../_chunks/warnings-B30wpfaS.js";
3
+ import { n as classifyUrlSegment } from "../_chunks/segment-classify-CnfLLvir.js";
4
+ import { i as getMetadataRouteServePath, n as classifyMetadataRoute, r as getMetadataRouteAutoLink, t as METADATA_ROUTE_CONVENTIONS } from "../_chunks/metadata-routes-Cjmvi3rQ.js";
5
+ import { a as logProxyError, c as logRequestReceived, d as logSwrRefetchFailed, f as logWaitUntilRejected, h as swallow, i as logMiddlewareShortCircuit, l as logRouteError, m as setLogger, n as logCacheMiss, o as logRenderError, p as logWaitUntilUnsupported, r as logMiddlewareError, s as logRequestCompleted, t as getLogger, u as logSlowRequest } from "../_chunks/logger-DDmK5skc.js";
6
+ import { C as setMutableCookieContext, D as getSetCookieHeaders, S as runWithRequestContext, T as getCookie, _ as getHeader, b as getSegmentParams, c as DenySignal, d as RenderError, g as applyRequestHeaderOverlay, l as RedirectSignal, n as executeAction, o as coerce, r as isRscActionRequest, t as buildNoJsResponse, w as setSegmentParams, x as markResponseFlushed, y as getSearchParams } from "../_chunks/actions-n9qTlpmY.js";
7
7
  import "../client/error-boundary.js";
8
- import "../_chunks/segment-context-Dx_OizxD.js";
8
+ import "../_chunks/segment-context-Dc-GbSTe.js";
9
9
  import { readFile } from "node:fs/promises";
10
10
  import { createElement } from "react";
11
11
  //#region src/server/server-timing.ts
@@ -1 +1 @@
1
- {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/server/request-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,iBAAiB,EAA4B,MAAM,mBAAmB,CAAC;AAQhF,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAQ7B;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,eAAe,CAS5C;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,eAAe,CASjD;AAkBD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAepE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,CAMhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAG/C;AAID;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,OAAO,EACP,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,MAAM,CAAC,QAAQ,CACnF,CAAC;AAIF;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAmBrE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAK9D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmBhE"}
1
+ {"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/server/request-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,iBAAiB,EAA4B,MAAM,mBAAmB,CAAC;AAMhF,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAQ7B;;;;;GAKG;AACH,wBAAgB,UAAU,IAAI,eAAe,CAS5C;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,eAAe,CASjD;AAaD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAepE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,CAMhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,CAG/C;AAID;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,OAAO,EACP,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,MAAM,CAAC,QAAQ,CACnF,CAAC;AAIF;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAmBrE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAK9D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAK1C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmBhE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.2.0-alpha.101",
3
+ "version": "0.2.0-alpha.103",
4
4
  "description": "Vite-native React framework built for Servers and Serverless Platforms — correct HTTP semantics, real status codes, pages that work without JavaScript",
5
5
  "keywords": [
6
6
  "cloudflare-workers",
@@ -83,15 +83,6 @@ export { useSegmentParams } from './use-segment-params';
83
83
  // Types still exported for advanced use.
84
84
  export type { ClientCookieOptions, CookieSetter } from './use-cookie';
85
85
 
86
- // Register the client cookie module with defineCookie's lazy reference.
87
- // This runs at module load time in the client/SSR environment, wiring up
88
- // the useCookie hook without a top-level import in define-cookie.ts.
89
- import * as _useCookieMod from './use-cookie.js';
90
- import { _registerUseCookieModule, _registerFromSchema } from '../cookies/define-cookie.js';
91
- import { fromSchema } from '../schema-bridge.js';
92
- _registerUseCookieModule(_useCookieMod);
93
- _registerFromSchema(fromSchema);
94
-
95
86
  // Register the client useSegmentParams hook with defineSegmentParams.
96
87
  import { useSegmentParams as _useSegmentParams } from './use-segment-params.js';
97
88
  import { _registerUseSegmentParams } from '../segment-params/define.js';
@@ -39,7 +39,7 @@ type Listener = () => void;
39
39
  const listeners = new Map<string, Set<Listener>>();
40
40
 
41
41
  /** Parse a cookie name from document.cookie. */
42
- function getCookieValue(name: string): string | undefined {
42
+ export function getCookieValue(name: string): string | undefined {
43
43
  if (typeof document === 'undefined') return undefined;
44
44
  const match = document.cookie.match(
45
45
  new RegExp('(?:^|;\\s*)' + name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\s*=\\s*([^;]*)')
@@ -48,7 +48,7 @@ function getCookieValue(name: string): string | undefined {
48
48
  }
49
49
 
50
50
  /** Serialize options into a cookie string suffix. */
51
- function serializeOptions(options?: ClientCookieOptions): string {
51
+ export function serializeOptions(options?: ClientCookieOptions): string {
52
52
  if (!options) return '; Path=/; SameSite=Lax';
53
53
  const parts: string[] = [];
54
54
  parts.push(`Path=${options.path ?? '/'}`);
@@ -5,10 +5,11 @@
5
5
  * with sync .get(), .set(), .delete() isomorphic methods (server + client)
6
6
  * and a .useCookie() client hook.
7
7
  *
8
- * Uses the registration pattern: server entry registers serverCookieImpl,
9
- * client entry registers clientCookieImpl. This avoids top-level value
10
- * imports from either environment and makes defineCookie isomorphic
11
- * without `typeof window` checks.
8
+ * Environment detection is call-time: on the server, getCookieJar() is
9
+ * used directly (ALS-backed). On the client, document.cookie helpers
10
+ * and the useCookie hook are used. No registration pattern — imports
11
+ * resolve via the shims plugin (client bundle gets a stub for
12
+ * @timber-js/app/server).
12
13
  *
13
14
  * Standard Schema objects (Zod, Valibot, ArkType) are auto-detected in
14
15
  * the codec option — no explicit fromSchema() wrapper needed.
@@ -71,119 +72,76 @@ export interface CookieDefinition<T> extends ServerOnlyCookieDefinition<T> {
71
72
  useCookie(): [T, (value: T) => void, () => void];
72
73
  }
73
74
 
74
- // ─── Registration Pattern ─────────────────────────────────────────────────
75
+ // ─── Direct Imports (no registration) ─────────────────────────────────────
75
76
  //
76
- // Server and client entries register their implementations at module load
77
- // time. This avoids dynamic imports (which lose ALS context) and typeof
78
- // window checks (which are fragile).
79
-
80
- /** Server-side cookie impl: reads from ALS-backed cookie jar. */
81
- export interface ServerCookieImpl {
82
- getCookieJar(): {
83
- get(name: string): string | undefined;
84
- set(name: string, value: string, options?: CookieOptions): void;
85
- delete(name: string, options?: Pick<CookieOptions, 'path' | 'domain'>): void;
86
- };
87
- }
88
-
89
- /** Client-side cookie impl: reads/writes document.cookie via useCookie hook. */
90
- export interface ClientCookieImpl {
91
- useCookie(
92
- name: string,
93
- options?: ClientCookieOptions
94
- ): [string | undefined, (value: string, options?: ClientCookieOptions) => void, () => void];
95
- getCookieValue(name: string): string | undefined;
96
- setCookieValue(name: string, value: string, options?: ClientCookieOptions): void;
97
- deleteCookieValue(name: string, options?: ClientCookieOptions): void;
98
- }
77
+ // Server: getCookieJar is imported from @timber-js/app/server. In the client
78
+ // bundle, the shims plugin replaces @timber-js/app/server with a stub module
79
+ // where getCookieJar throws on call. We detect the environment at call time
80
+ // by trying getCookieJar() — if it throws the stub error, we fall through
81
+ // to the client path.
82
+ //
83
+ // Client: useCookie, getCookieValue, notifyCookieChange
84
+ // are imported from use-cookie.ts. These are safe in all environments (no
85
+ // node:async_hooks dependency).
86
+ //
87
+ // Schema: fromSchema is a pure function — safe in all environments.
99
88
 
100
- let _serverImpl: ServerCookieImpl | undefined;
101
- let _clientImpl: ClientCookieImpl | undefined;
102
- let _fromSchemaFn: ((schema: StandardSchemaV1<unknown>) => CookieCodec<unknown>) | undefined;
89
+ import { getCookieJar } from '@timber-js/app/server';
90
+ import { fromSchema } from '../schema-bridge.js';
91
+ import {
92
+ useCookie as useRawCookie,
93
+ notifyCookieChange,
94
+ getCookieValue,
95
+ } from '../client/use-cookie.js';
103
96
 
104
- /**
105
- * Register the server-side cookie implementation.
106
- * Called by server entry at module load time.
107
- * @internal
108
- */
109
- export function _registerServerCookieImpl(impl: ServerCookieImpl): void {
110
- _serverImpl = impl;
111
- }
97
+ // ─── Environment Detection ────────────────────────────────────────────────
112
98
 
113
99
  /**
114
- * Register the client-side cookie implementation.
115
- * Called by client entry at module load time.
116
- * @internal
100
+ * Try to get the server cookie jar. Returns null if we're in the client
101
+ * environment (stub throws) or if there's no request context.
102
+ *
103
+ * On the server, getCookieJar() throws "outside of a request context" if
104
+ * called without an ALS store — this is a real error and is re-thrown.
105
+ * The client stub throws a different message — that's caught and returns null.
117
106
  */
118
- export function _registerClientCookieImpl(impl: ClientCookieImpl): void {
119
- _clientImpl = impl;
107
+ function tryGetServerCookieJar(): ReturnType<typeof getCookieJar> | null {
108
+ try {
109
+ return getCookieJar();
110
+ } catch (e) {
111
+ // Server-side "no request context" error — re-throw as a real bug.
112
+ if (e instanceof Error && e.message.includes('outside of a request context')) {
113
+ throw e;
114
+ }
115
+ // Client stub error or other — fall through to client path.
116
+ return null;
117
+ }
120
118
  }
121
119
 
122
- /**
123
- * Register the fromSchema bridge function.
124
- * Called by server/client entry at module load time.
125
- * @internal
126
- */
127
- export function _registerFromSchema(
128
- fn: (schema: StandardSchemaV1<unknown>) => CookieCodec<unknown>
129
- ): void {
130
- _fromSchemaFn = fn;
120
+ // ─── Client Cookie Helpers ────────────────────────────────────────────────
121
+
122
+ /** Write a cookie via document.cookie and notify useCookie subscribers. */
123
+ function setClientCookie(name: string, value: string, options?: ClientCookieOptions): void {
124
+ const parts: string[] = [`${name}=${encodeURIComponent(value)}`];
125
+ const path = options?.path ?? '/';
126
+ parts.push(`Path=${path}`);
127
+ if (options?.domain) parts.push(`Domain=${options.domain}`);
128
+ if (options?.maxAge !== undefined) parts.push(`Max-Age=${options.maxAge}`);
129
+ if (options?.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
130
+ const sameSite = options?.sameSite ?? 'lax';
131
+ parts.push(`SameSite=${sameSite.charAt(0).toUpperCase()}${sameSite.slice(1)}`);
132
+ if (options?.secure) parts.push('Secure');
133
+ document.cookie = parts.join('; ');
134
+ notifyCookieChange(name);
131
135
  }
132
136
 
133
- // Legacy registration for useCookie module — still used by client/index.ts
134
- let _useCookieModule: typeof import('../client/use-cookie.js') | undefined;
135
-
136
- function getUseCookieModule(): typeof import('../client/use-cookie.js') {
137
- if (!_useCookieModule) {
138
- throw new Error(
139
- '[timber] defineCookie().useCookie() requires @timber-js/app/client to be loaded. ' +
140
- 'This hook can only be used in client components.'
141
- );
142
- }
143
- return _useCookieModule;
144
- }
145
-
146
- /**
147
- * Register the client cookie module. Called by the client entry to wire
148
- * up the lazy reference without a top-level import.
149
- *
150
- * @internal — framework use only
151
- */
152
- export function _registerUseCookieModule(mod: typeof import('../client/use-cookie.js')): void {
153
- _useCookieModule = mod;
154
- // Also register the client impl from the module
155
- _clientImpl = {
156
- useCookie: mod.useCookie,
157
- getCookieValue: (name: string) => {
158
- if (typeof document === 'undefined') return undefined;
159
- const match = document.cookie.match(
160
- new RegExp('(?:^|;\\s*)' + name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\s*=\\s*([^;]*)')
161
- );
162
- return match ? decodeURIComponent(match[1]) : undefined;
163
- },
164
- setCookieValue: (name: string, value: string, options?: ClientCookieOptions) => {
165
- const parts: string[] = [`${name}=${encodeURIComponent(value)}`];
166
- const path = options?.path ?? '/';
167
- parts.push(`Path=${path}`);
168
- if (options?.domain) parts.push(`Domain=${options.domain}`);
169
- if (options?.maxAge !== undefined) parts.push(`Max-Age=${options.maxAge}`);
170
- if (options?.expires) parts.push(`Expires=${options.expires.toUTCString()}`);
171
- const sameSite = options?.sameSite ?? 'lax';
172
- parts.push(`SameSite=${sameSite.charAt(0).toUpperCase()}${sameSite.slice(1)}`);
173
- if (options?.secure) parts.push('Secure');
174
- document.cookie = parts.join('; ');
175
- // Notify useCookie subscribers so mounted hooks re-render
176
- mod.notifyCookieChange(name);
177
- },
178
- deleteCookieValue: (name: string, options?: ClientCookieOptions) => {
179
- const path = options?.path ?? '/';
180
- const domain = options?.domain;
181
- let cookieStr = `${name}=; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=${path}`;
182
- if (domain) cookieStr += `; Domain=${domain}`;
183
- document.cookie = cookieStr;
184
- mod.notifyCookieChange(name);
185
- },
186
- };
137
+ /** Delete a cookie via document.cookie and notify useCookie subscribers. */
138
+ function deleteClientCookie(name: string, options?: ClientCookieOptions): void {
139
+ const path = options?.path ?? '/';
140
+ const domain = options?.domain;
141
+ let cookieStr = `${name}=; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=${path}`;
142
+ if (domain) cookieStr += `; Domain=${domain}`;
143
+ document.cookie = cookieStr;
144
+ notifyCookieChange(name);
187
145
  }
188
146
 
189
147
  // ─── Standard Schema Auto-Detection ───────────────────────────────────────
@@ -199,14 +157,7 @@ function resolveCodec<T>(codecOrSchema: CookieCodec<T> | StandardSchemaV1<T>): C
199
157
 
200
158
  // Auto-detect Standard Schema
201
159
  if (typeof codecOrSchema === 'object' && codecOrSchema !== null && '~standard' in codecOrSchema) {
202
- if (!_fromSchemaFn) {
203
- throw new Error(
204
- '[timber] defineCookie: Standard Schema auto-detection requires @timber-js/app/server or ' +
205
- '@timber-js/app/client to be loaded. Pass an explicit Codec<T> with parse/serialize methods, ' +
206
- 'or ensure the framework entry module is imported.'
207
- );
208
- }
209
- return _fromSchemaFn(codecOrSchema as StandardSchemaV1<unknown>) as CookieCodec<T>;
160
+ return fromSchema(codecOrSchema as StandardSchemaV1<unknown>) as CookieCodec<T>;
210
161
  }
211
162
 
212
163
  throw new Error(
@@ -266,88 +217,73 @@ export function defineCookie<T, HttpOnly extends boolean = true>(
266
217
  };
267
218
  }
268
219
 
269
- function assertNotHttpOnlyClient(method: string): void {
270
- if (isHttpOnly && !_serverImpl) {
271
- throw new Error(
272
- `[timber] defineCookie('${name}').${method}() cannot be used with httpOnly cookies — ` +
273
- `the browser cannot access them. Set httpOnly: false to enable client access.`
274
- );
275
- }
276
- }
277
-
278
220
  const base: ServerOnlyCookieDefinition<T> = {
279
221
  name,
280
222
  options: resolvedOptions,
281
223
  codec,
282
224
 
283
225
  get(): T {
284
- if (_serverImpl) {
285
- const jar = _serverImpl.getCookieJar();
286
- const raw = jar.get(name);
287
- return codec.parse(raw);
226
+ const serverJar = tryGetServerCookieJar();
227
+ if (serverJar) {
228
+ return codec.parse(serverJar.get(name));
288
229
  }
289
230
  // Client path
290
- assertNotHttpOnlyClient('get');
291
- if (_clientImpl) {
292
- const raw = _clientImpl.getCookieValue(name);
293
- return codec.parse(raw);
231
+ if (isHttpOnly) {
232
+ throw new Error(
233
+ `[timber] defineCookie('${name}').get() cannot be used with httpOnly cookies — ` +
234
+ `the browser cannot access them. Set httpOnly: false to enable client access.`
235
+ );
294
236
  }
295
- throw new Error(
296
- `[timber] defineCookie('${name}').get() — no environment registered. ` +
297
- 'Ensure @timber-js/app/server or @timber-js/app/client is imported.'
298
- );
237
+ const raw = getCookieValue(name);
238
+ return codec.parse(raw);
299
239
  },
300
240
 
301
241
  set(value: T): void {
302
- if (_serverImpl) {
303
- const jar = _serverImpl.getCookieJar();
242
+ const serverJar = tryGetServerCookieJar();
243
+ if (serverJar) {
304
244
  const serialized = codec.serialize(value);
305
245
  if (serialized === null) {
306
- jar.delete(name, {
246
+ serverJar.delete(name, {
307
247
  path: resolvedOptions.path,
308
248
  domain: resolvedOptions.domain,
309
249
  });
310
250
  } else {
311
- jar.set(name, serialized, resolvedOptions);
251
+ serverJar.set(name, serialized, resolvedOptions);
312
252
  }
313
253
  return;
314
254
  }
315
255
  // Client path
316
- assertNotHttpOnlyClient('set');
317
- if (_clientImpl) {
318
- const serialized = codec.serialize(value);
319
- if (serialized === null) {
320
- _clientImpl.deleteCookieValue(name, getClientOpts());
321
- } else {
322
- _clientImpl.setCookieValue(name, serialized, getClientOpts());
323
- }
324
- return;
256
+ if (isHttpOnly) {
257
+ throw new Error(
258
+ `[timber] defineCookie('${name}').set() cannot be used with httpOnly cookies — ` +
259
+ `the browser cannot access them. Set httpOnly: false to enable client access.`
260
+ );
261
+ }
262
+ const serialized = codec.serialize(value);
263
+ if (serialized === null) {
264
+ deleteClientCookie(name, getClientOpts());
265
+ } else {
266
+ setClientCookie(name, serialized, getClientOpts());
325
267
  }
326
- throw new Error(
327
- `[timber] defineCookie('${name}').set() — no environment registered. ` +
328
- 'Ensure @timber-js/app/server or @timber-js/app/client is imported.'
329
- );
330
268
  },
331
269
 
332
270
  delete(): void {
333
- if (_serverImpl) {
334
- const jar = _serverImpl.getCookieJar();
335
- jar.delete(name, {
271
+ const serverJar = tryGetServerCookieJar();
272
+ if (serverJar) {
273
+ serverJar.delete(name, {
336
274
  path: resolvedOptions.path,
337
275
  domain: resolvedOptions.domain,
338
276
  });
339
277
  return;
340
278
  }
341
279
  // Client path
342
- assertNotHttpOnlyClient('delete');
343
- if (_clientImpl) {
344
- _clientImpl.deleteCookieValue(name, getClientOpts());
345
- return;
280
+ if (isHttpOnly) {
281
+ throw new Error(
282
+ `[timber] defineCookie('${name}').delete() cannot be used with httpOnly cookies — ` +
283
+ `the browser cannot access them. Set httpOnly: false to enable client access.`
284
+ );
346
285
  }
347
- throw new Error(
348
- `[timber] defineCookie('${name}').delete() — no environment registered. ` +
349
- 'Ensure @timber-js/app/server or @timber-js/app/client is imported.'
350
- );
286
+ deleteClientCookie(name, getClientOpts());
351
287
  },
352
288
  };
353
289
 
@@ -360,8 +296,6 @@ export function defineCookie<T, HttpOnly extends boolean = true>(
360
296
  ...base,
361
297
 
362
298
  useCookie(): [T, (value: T) => void, () => void] {
363
- const { useCookie: useRawCookie } = getUseCookieModule();
364
-
365
299
  const clientOpts = getClientOpts();
366
300
  const [raw, setRaw, deleteRaw] = useRawCookie(name, clientOpts);
367
301
  const parsed = codec.parse(raw);
package/src/fonts/ast.ts CHANGED
@@ -590,8 +590,8 @@ interface CallExpressionNode extends PositionedNode {
590
590
 
591
591
  interface VariableDeclaratorNode extends PositionedNode {
592
592
  type: 'VariableDeclarator';
593
- id: AstNode;
594
- init: AstNode | null;
593
+ id: PositionedNode;
594
+ init: PositionedNode | null;
595
595
  }
596
596
 
597
597
  interface VariableDeclarationNode extends PositionedNode {
@@ -601,7 +601,43 @@ interface VariableDeclarationNode extends PositionedNode {
601
601
 
602
602
  interface ExpressionStatementNode extends PositionedNode {
603
603
  type: 'ExpressionStatement';
604
- expression: AstNode;
604
+ expression: PositionedNode;
605
+ }
606
+
607
+ interface ExportNamedDeclarationNode extends PositionedNode {
608
+ type: 'ExportNamedDeclaration';
609
+ declaration: VariableDeclarationNode | ExpressionStatementNode | null;
610
+ }
611
+
612
+ // ── Type guards for AST narrowing ────────────────────────────────────────────
613
+ //
614
+ // OXC's parsed nodes all share the base `PositionedNode` shape with
615
+ // `type: string`. TypeScript can't narrow a union that includes a
616
+ // `string`-typed catch-all, so we use explicit type guards instead of
617
+ // bare `.type ===` checks.
618
+
619
+ function isImportDeclaration(node: PositionedNode): node is ImportDeclarationNode {
620
+ return node.type === 'ImportDeclaration';
621
+ }
622
+
623
+ function isVariableDeclaration(node: PositionedNode): node is VariableDeclarationNode {
624
+ return node.type === 'VariableDeclaration';
625
+ }
626
+
627
+ function isExpressionStatement(node: PositionedNode): node is ExpressionStatementNode {
628
+ return node.type === 'ExpressionStatement';
629
+ }
630
+
631
+ function isExportNamedDeclaration(node: PositionedNode): node is ExportNamedDeclarationNode {
632
+ return node.type === 'ExportNamedDeclaration';
633
+ }
634
+
635
+ function isCallExpression(node: PositionedNode): node is CallExpressionNode {
636
+ return node.type === 'CallExpression';
637
+ }
638
+
639
+ function isIdentifier(node: AstNode): node is IdentifierNode {
640
+ return node.type === 'Identifier';
605
641
  }
606
642
 
607
643
  interface ProgramNode extends PositionedNode {
@@ -627,8 +663,8 @@ export function findFontCallSites(source: string): FontAnalysis | null {
627
663
  const importNodesToRemove: Array<{ start: number; end: number }> = [];
628
664
 
629
665
  for (const stmt of program.body) {
630
- if (stmt.type !== 'ImportDeclaration') continue;
631
- const importDecl = stmt as unknown as ImportDeclarationNode;
666
+ if (!isImportDeclaration(stmt)) continue;
667
+ const importDecl = stmt;
632
668
  const specSource = importDecl.source.value;
633
669
 
634
670
  let provider: 'google' | 'local' | null = null;
@@ -658,7 +694,7 @@ export function findFontCallSites(source: string): FontAnalysis | null {
658
694
  // member expressions in the call-site walk.
659
695
  }
660
696
 
661
- importNodesToRemove.push({ start: importDecl.start, end: importDecl.end });
697
+ importNodesToRemove.push({ start: stmt.start, end: stmt.end });
662
698
  }
663
699
 
664
700
  if (bindings.length === 0) return null;
@@ -669,27 +705,30 @@ export function findFontCallSites(source: string): FontAnalysis | null {
669
705
  let dynamicCall: string | null = null;
670
706
 
671
707
  for (const stmt of program.body) {
708
+ // Unwrap `export const roboto = Roboto(...)` — the AST wraps the
709
+ // VariableDeclaration inside an ExportNamedDeclaration node.
710
+ const effectiveStmt =
711
+ isExportNamedDeclaration(stmt) && stmt.declaration ? stmt.declaration : stmt;
712
+
672
713
  // VariableDeclaration: `const inter = Inter({ ... })`
673
- if (stmt.type === 'VariableDeclaration') {
674
- const varDecl = stmt as unknown as VariableDeclarationNode;
675
- for (const declarator of varDecl.declarations) {
676
- if (!declarator.init || declarator.init.type !== 'CallExpression') continue;
677
- const call = declarator.init as unknown as CallExpressionNode;
714
+ if (isVariableDeclaration(effectiveStmt)) {
715
+ for (const declarator of effectiveStmt.declarations) {
716
+ if (!declarator.init) continue;
717
+ if (!isCallExpression(declarator.init)) continue;
718
+ const call = declarator.init;
678
719
  const site = matchCallExpression(call, bindingsByName, source);
679
720
  if (!site) continue;
680
721
 
681
722
  if (site === 'dynamic') {
682
- if (!dynamicCall) {
683
- const calleeName = (call.callee as IdentifierNode).name;
723
+ if (!dynamicCall && isIdentifier(call.callee)) {
684
724
  const argStart = call.arguments[0].start;
685
725
  const argEnd = call.arguments[0].end;
686
- dynamicCall = `${calleeName}(${source.slice(argStart, argEnd)})`;
726
+ dynamicCall = `${call.callee.name}(${source.slice(argStart, argEnd)})`;
687
727
  }
688
728
  continue;
689
729
  }
690
730
 
691
- const assignedName =
692
- declarator.id.type === 'Identifier' ? (declarator.id as IdentifierNode).name : null;
731
+ const assignedName = isIdentifier(declarator.id) ? declarator.id.name : null;
693
732
 
694
733
  const { line, column } = offsetToLineColumn(source, call.start);
695
734
  callSites.push({
@@ -705,10 +744,9 @@ export function findFontCallSites(source: string): FontAnalysis | null {
705
744
  }
706
745
 
707
746
  // ExpressionStatement: `Inter({ ... })` as a bare call
708
- if (stmt.type === 'ExpressionStatement') {
709
- const exprStmt = stmt as unknown as ExpressionStatementNode;
710
- if (exprStmt.expression.type === 'CallExpression') {
711
- const call = exprStmt.expression as unknown as CallExpressionNode;
747
+ if (isExpressionStatement(effectiveStmt)) {
748
+ if (isCallExpression(effectiveStmt.expression)) {
749
+ const call = effectiveStmt.expression;
712
750
  const site = matchCallExpression(call, bindingsByName, source);
713
751
  if (site && site !== 'dynamic') {
714
752
  const { line, column } = offsetToLineColumn(source, call.start);
@@ -716,16 +754,15 @@ export function findFontCallSites(source: string): FontAnalysis | null {
716
754
  binding: site.binding,
717
755
  assignedName: null,
718
756
  configNode: site.configNode,
719
- replaceStart: (exprStmt as PositionedNode).start,
720
- replaceEnd: (exprStmt as PositionedNode).end,
757
+ replaceStart: effectiveStmt.start,
758
+ replaceEnd: effectiveStmt.end,
721
759
  line,
722
760
  column,
723
761
  });
724
- } else if (site === 'dynamic' && !dynamicCall) {
725
- const calleeName = (call.callee as IdentifierNode).name;
762
+ } else if (site === 'dynamic' && !dynamicCall && isIdentifier(call.callee)) {
726
763
  const argStart = call.arguments[0].start;
727
764
  const argEnd = call.arguments[0].end;
728
- dynamicCall = `${calleeName}(${source.slice(argStart, argEnd)})`;
765
+ dynamicCall = `${call.callee.name}(${source.slice(argStart, argEnd)})`;
729
766
  }
730
767
  }
731
768
  }
@@ -817,8 +854,8 @@ function matchCallExpression(
817
854
  bindings: Map<string, FontBinding>,
818
855
  _source: string
819
856
  ): { binding: FontBinding; configNode: AstNode } | 'dynamic' | null {
820
- if (call.callee.type !== 'Identifier') return null;
821
- const calleeName = (call.callee as IdentifierNode).name;
857
+ if (!isIdentifier(call.callee)) return null;
858
+ const calleeName = call.callee.name;
822
859
  const binding = bindings.get(calleeName);
823
860
  if (!binding) return null;
824
861
 
@@ -17,9 +17,7 @@
17
17
  import { requestContextAls, type RequestContextStore } from './als-registry.js';
18
18
  import { _setGetSearchParamsFn } from '../search-params/define.js';
19
19
  import { _setGetSegmentParamsFn } from '../segment-params/define.js';
20
- import { consumeSeededCookies, getCookieJar } from './cookie-context.js';
21
- import { _registerServerCookieImpl, _registerFromSchema } from '../cookies/define-cookie.js';
22
- import { fromSchema } from '../schema-bridge.js';
20
+ import { consumeSeededCookies } from './cookie-context.js';
23
21
 
24
22
  // Re-export the ALS for framework-internal consumers that need direct access.
25
23
  export { requestContextAls };
@@ -87,11 +85,6 @@ _setGetSearchParamsFn(getSearchParams);
87
85
  // Same pattern as search params — dynamic imports lose ALS context. See TIM-523.
88
86
  _setGetSegmentParamsFn(getSegmentParams);
89
87
 
90
- // Eagerly register the server cookie impl with defineCookie so
91
- // .get()/.set()/.delete() are sync without dynamic imports.
92
- _registerServerCookieImpl({ getCookieJar });
93
- _registerFromSchema(fromSchema);
94
-
95
88
  /**
96
89
  * Returns the current request's coerced segment params.
97
90
  *