@timber-js/app 0.1.3 → 0.1.5

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 (103) hide show
  1. package/dist/_chunks/{interception-DIaZN1bF.js → interception-c-a3uODY.js} +8 -8
  2. package/dist/_chunks/interception-c-a3uODY.js.map +1 -0
  3. package/dist/_chunks/{registry-DUIpYD_x.js → registry-BfPM41ri.js} +1 -1
  4. package/dist/_chunks/{registry-DUIpYD_x.js.map → registry-BfPM41ri.js.map} +1 -1
  5. package/dist/_chunks/{request-context-D6XHINkR.js → request-context-BzES06i1.js} +2 -1
  6. package/dist/_chunks/request-context-BzES06i1.js.map +1 -0
  7. package/dist/_chunks/{use-cookie-8ZlA0rr3.js → use-cookie-HcvNlW4L.js} +1 -1
  8. package/dist/_chunks/{use-cookie-8ZlA0rr3.js.map → use-cookie-HcvNlW4L.js.map} +1 -1
  9. package/dist/adapters/cloudflare.d.ts +2 -2
  10. package/dist/adapters/cloudflare.js +4 -4
  11. package/dist/adapters/cloudflare.js.map +1 -1
  12. package/dist/adapters/nitro.d.ts +1 -1
  13. package/dist/adapters/nitro.js +4 -4
  14. package/dist/adapters/nitro.js.map +1 -1
  15. package/dist/cache/index.js.map +1 -1
  16. package/dist/{_chunks/error-boundary-dj-WO5uq.js → client/error-boundary.js} +4 -2
  17. package/dist/client/error-boundary.js.map +1 -0
  18. package/dist/client/form.d.ts +1 -1
  19. package/dist/client/index.js +10 -9
  20. package/dist/client/index.js.map +1 -1
  21. package/dist/client/link.d.ts.map +1 -1
  22. package/dist/client/slot-error-fallback.d.ts +13 -0
  23. package/dist/client/slot-error-fallback.d.ts.map +1 -0
  24. package/dist/client/use-link-status.d.ts +1 -1
  25. package/dist/client/use-navigation-pending.d.ts +1 -1
  26. package/dist/content/index.d.ts +1 -1
  27. package/dist/cookies/define-cookie.d.ts +2 -2
  28. package/dist/cookies/index.d.ts +1 -1
  29. package/dist/cookies/index.d.ts.map +1 -1
  30. package/dist/cookies/index.js +4 -4
  31. package/dist/cookies/index.js.map +1 -1
  32. package/dist/index.js +23 -22
  33. package/dist/index.js.map +1 -1
  34. package/dist/plugins/dev-logs.d.ts +1 -1
  35. package/dist/plugins/dynamic-transform.d.ts +1 -1
  36. package/dist/plugins/shims.d.ts +5 -0
  37. package/dist/plugins/shims.d.ts.map +1 -1
  38. package/dist/routing/codegen.d.ts +1 -1
  39. package/dist/routing/index.js +1 -1
  40. package/dist/search-params/codecs.d.ts +2 -2
  41. package/dist/search-params/create.d.ts +1 -1
  42. package/dist/search-params/index.js +5 -5
  43. package/dist/search-params/index.js.map +1 -1
  44. package/dist/server/action-client.d.ts +1 -1
  45. package/dist/server/dev-fetch-instrumentation.d.ts +22 -0
  46. package/dist/server/dev-fetch-instrumentation.d.ts.map +1 -0
  47. package/dist/server/dev-logger.d.ts.map +1 -1
  48. package/dist/server/form-flash.d.ts +1 -1
  49. package/dist/server/index.js +4 -4
  50. package/dist/server/index.js.map +1 -1
  51. package/dist/server/primitives.d.ts +15 -0
  52. package/dist/server/primitives.d.ts.map +1 -1
  53. package/dist/server/request-context.d.ts +32 -0
  54. package/dist/server/request-context.d.ts.map +1 -1
  55. package/dist/server/route-element-builder.d.ts.map +1 -1
  56. package/dist/server/rsc-entry/index.d.ts.map +1 -1
  57. package/dist/server/slot-resolver.d.ts.map +1 -1
  58. package/dist/server/ssr-entry.d.ts.map +1 -1
  59. package/dist/shims/headers.d.ts +5 -7
  60. package/dist/shims/headers.d.ts.map +1 -1
  61. package/dist/shims/link.d.ts +1 -1
  62. package/dist/shims/link.d.ts.map +1 -1
  63. package/dist/shims/navigation.d.ts +5 -15
  64. package/dist/shims/navigation.d.ts.map +1 -1
  65. package/package.json +1 -1
  66. package/src/adapters/cloudflare.ts +4 -4
  67. package/src/adapters/nitro.ts +4 -4
  68. package/src/cache/index.ts +1 -1
  69. package/src/client/form.tsx +1 -1
  70. package/src/client/index.ts +1 -1
  71. package/src/client/link.tsx +2 -0
  72. package/src/client/slot-error-fallback.tsx +16 -0
  73. package/src/client/use-link-status.ts +1 -1
  74. package/src/client/use-navigation-pending.ts +1 -1
  75. package/src/content/index.ts +1 -1
  76. package/src/cookies/define-cookie.ts +2 -2
  77. package/src/cookies/index.ts +2 -6
  78. package/src/plugins/cache-transform.ts +1 -1
  79. package/src/plugins/dev-logs.ts +2 -2
  80. package/src/plugins/dynamic-transform.ts +2 -2
  81. package/src/plugins/shims.ts +48 -22
  82. package/src/routing/codegen.ts +9 -9
  83. package/src/search-params/codecs.ts +2 -2
  84. package/src/search-params/create.ts +3 -3
  85. package/src/search-params/index.ts +1 -1
  86. package/src/server/action-client.ts +1 -1
  87. package/src/server/asset-headers.ts +1 -1
  88. package/src/server/dev-fetch-instrumentation.ts +96 -0
  89. package/src/server/dev-logger.ts +49 -0
  90. package/src/server/form-flash.ts +1 -1
  91. package/src/server/index.ts +1 -1
  92. package/src/server/primitives.ts +24 -1
  93. package/src/server/request-context.ts +7 -1
  94. package/src/server/route-element-builder.ts +2 -5
  95. package/src/server/rsc-entry/index.ts +33 -1
  96. package/src/server/slot-resolver.ts +20 -1
  97. package/src/server/ssr-entry.ts +21 -5
  98. package/src/shims/headers.ts +5 -7
  99. package/src/shims/link.ts +3 -1
  100. package/src/shims/navigation.ts +7 -17
  101. package/dist/_chunks/error-boundary-dj-WO5uq.js.map +0 -1
  102. package/dist/_chunks/interception-DIaZN1bF.js.map +0 -1
  103. package/dist/_chunks/request-context-D6XHINkR.js.map +0 -1
@@ -33,7 +33,7 @@ export interface ServerLogPayload {
33
33
  * (render errors, action errors, route handler errors, etc.).
34
34
  *
35
35
  * Handles both monorepo paths (timber-app/src/plugins/) and installed
36
- * package paths (@timber/app/dist/plugins/).
36
+ * package paths (@timber-js/app/dist/plugins/).
37
37
  */
38
38
  export declare function isFrameworkInternalCaller(): boolean;
39
39
  /**
@@ -39,7 +39,7 @@ interface TransformResult {
39
39
  *
40
40
  * Output:
41
41
  * ```tsx
42
- * import { markDynamic as __markDynamic } from '@timber/app/runtime';
42
+ * import { markDynamic as __markDynamic } from '@timber-js/app/runtime';
43
43
  * export default async function AddToCartButton({ productId }) {
44
44
  * __markDynamic();
45
45
  * const user = await getUser()
@@ -5,6 +5,11 @@
5
5
  * shim implementations. This enables Next.js-compatible libraries
6
6
  * (nuqs, next-intl, etc.) to work unmodified.
7
7
  *
8
+ * NOTE: This plugin does NOT resolve @timber-js/app/* subpath imports.
9
+ * Those are handled by Vite's native package.json `exports` resolution,
10
+ * which maps them to dist/ files. This ensures a single module instance
11
+ * for shared modules like request-context (ALS singleton).
12
+ *
8
13
  * Design doc: 18-build-system.md §"Shim Map"
9
14
  */
10
15
  import type { Plugin } from 'vite';
@@ -1 +1 @@
1
- {"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/plugins/shims.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAoEhD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAiFvD"}
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,CA6GvD"}
@@ -22,7 +22,7 @@ export interface CodegenOptions {
22
22
  /**
23
23
  * Generate a TypeScript declaration file string from a scanned route tree.
24
24
  *
25
- * The output is a `declare module '@timber/app'` block containing the Routes
25
+ * The output is a `declare module '@timber-js/app'` block containing the Routes
26
26
  * interface that maps every route path to its params and searchParams shape.
27
27
  */
28
28
  export declare function generateRouteMap(tree: RouteTree, options?: CodegenOptions): string;
@@ -1,2 +1,2 @@
1
- import { a as DEFAULT_PAGE_EXTENSIONS, i as scanRoutes, n as generateRouteMap, o as INTERCEPTION_MARKERS, r as classifySegment, t as collectInterceptionRewrites } from "../_chunks/interception-DIaZN1bF.js";
1
+ import { a as DEFAULT_PAGE_EXTENSIONS, i as scanRoutes, n as generateRouteMap, o as INTERCEPTION_MARKERS, r as classifySegment, t as collectInterceptionRewrites } from "../_chunks/interception-c-a3uODY.js";
2
2
  export { DEFAULT_PAGE_EXTENSIONS, INTERCEPTION_MARKERS, classifySegment, collectInterceptionRewrites, generateRouteMap, scanRoutes };
@@ -30,7 +30,7 @@ type StandardSchemaResult<Output> = {
30
30
  * Serialize: uses `String()` for primitives, `null` for null/undefined.
31
31
  *
32
32
  * ```ts
33
- * import { fromSchema } from '@timber/app/search-params'
33
+ * import { fromSchema } from '@timber-js/app/search-params'
34
34
  * import { z } from 'zod/v4'
35
35
  *
36
36
  * const pageCodec = fromSchema(z.coerce.number().int().min(1).default(1))
@@ -42,7 +42,7 @@ export declare function fromSchema<T>(schema: StandardSchemaV1<T>): SearchParamC
42
42
  * and repeated query keys (`?tag=a&tag=b`).
43
43
  *
44
44
  * ```ts
45
- * import { fromArraySchema } from '@timber/app/search-params'
45
+ * import { fromArraySchema } from '@timber-js/app/search-params'
46
46
  * import { z } from 'zod/v4'
47
47
  *
48
48
  * const tagsCodec = fromArraySchema(z.array(z.string()).default([]))
@@ -89,7 +89,7 @@ export interface SearchParamsDefinition<T extends Record<string, unknown>> {
89
89
  * Create a SearchParamsDefinition from a codec map and optional URL key aliases.
90
90
  *
91
91
  * ```ts
92
- * import { createSearchParams, fromSchema } from '@timber/app/search-params'
92
+ * import { createSearchParams, fromSchema } from '@timber-js/app/search-params'
93
93
  * import { z } from 'zod/v4'
94
94
  *
95
95
  * export default createSearchParams({
@@ -1,4 +1,4 @@
1
- import { n as registerSearchParams, t as getSearchParams } from "../_chunks/registry-DUIpYD_x.js";
1
+ import { n as registerSearchParams, t as getSearchParams } from "../_chunks/registry-BfPM41ri.js";
2
2
  //#region src/search-params/create.ts
3
3
  /**
4
4
  * Convert URLSearchParams or a plain record to a normalized record
@@ -27,7 +27,7 @@ function getDefaultSerialized(codec) {
27
27
  * Create a SearchParamsDefinition from a codec map and optional URL key aliases.
28
28
  *
29
29
  * ```ts
30
- * import { createSearchParams, fromSchema } from '@timber/app/search-params'
30
+ * import { createSearchParams, fromSchema } from '@timber-js/app/search-params'
31
31
  * import { z } from 'zod/v4'
32
32
  *
33
33
  * export default createSearchParams({
@@ -110,7 +110,7 @@ function buildDefinition(codecMap, urlKeys) {
110
110
  return buildDefinition(pickedCodecs, pickedUrlKeys);
111
111
  }
112
112
  function useQueryStates(_options) {
113
- throw new Error("useQueryStates() can only be called in a client component. Import from @timber/app/client instead.");
113
+ throw new Error("useQueryStates() can only be called in a client component. Import from @timber-js/app/client instead.");
114
114
  }
115
115
  return {
116
116
  parse,
@@ -148,7 +148,7 @@ function validateSync(schema, value) {
148
148
  * Serialize: uses `String()` for primitives, `null` for null/undefined.
149
149
  *
150
150
  * ```ts
151
- * import { fromSchema } from '@timber/app/search-params'
151
+ * import { fromSchema } from '@timber-js/app/search-params'
152
152
  * import { z } from 'zod/v4'
153
153
  *
154
154
  * const pageCodec = fromSchema(z.coerce.number().int().min(1).default(1))
@@ -173,7 +173,7 @@ function fromSchema(schema) {
173
173
  * and repeated query keys (`?tag=a&tag=b`).
174
174
  *
175
175
  * ```ts
176
- * import { fromArraySchema } from '@timber/app/search-params'
176
+ * import { fromArraySchema } from '@timber-js/app/search-params'
177
177
  * import { z } from 'zod/v4'
178
178
  *
179
179
  * const tagsCodec = fromArraySchema(z.array(z.string()).default([]))
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/search-params/create.ts","../../src/search-params/codecs.ts","../../src/search-params/analyze.ts"],"sourcesContent":["/**\n * createSearchParams — factory for SearchParamsDefinition<T>.\n *\n * Creates a typed, composable definition for a route's search parameters.\n * Supports codec protocol, URL key aliasing, default-omission serialization,\n * and composition via .extend() / .pick().\n *\n * Design doc: design/09-typescript.md §\"Typed searchParams — search-params.ts\"\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A codec that converts between URL string values and typed values.\n *\n * nuqs parsers (parseAsInteger, parseAsString, etc.) implement this\n * interface natively — no adapter needed.\n */\nexport interface SearchParamCodec<T> {\n /** URL string → typed value. Receives undefined when the param is absent. */\n parse(value: string | string[] | undefined): T;\n /** Typed value → URL string. Return null to omit from URL. */\n serialize(value: T): string | null;\n}\n\n/** Infer the output type of a codec. */\nexport type InferCodec<C> = C extends SearchParamCodec<infer T> ? T : never;\n\n/** Map of property names to codecs. */\nexport type CodecMap<T extends Record<string, unknown>> = {\n [K in keyof T]: SearchParamCodec<T[K]>;\n};\n\n/** Options for useQueryStates setter. */\nexport interface SetParamsOptions {\n /** Update URL without server roundtrip (default: false). */\n shallow?: boolean;\n /** Scroll to top after update (default: true). */\n scroll?: boolean;\n /** 'push' (default) or 'replace' for history state. */\n history?: 'push' | 'replace';\n}\n\n/** Setter function returned by useQueryStates. */\nexport type SetParams<T> = (values: Partial<T>, options?: SetParamsOptions) => void;\n\n/** Options for useQueryStates hook. */\nexport interface QueryStatesOptions {\n /** Update URL without server roundtrip (default: false). */\n shallow?: boolean;\n /** Scroll to top after update (default: true). */\n scroll?: boolean;\n /** 'push' (default) or 'replace' for history state. */\n history?: 'push' | 'replace';\n}\n\n/** Options for createSearchParams and .extend(). */\nexport interface SearchParamsOptions<Keys extends string = string> {\n /** Map property names to different URL query parameter keys. */\n urlKeys?: Partial<Record<Keys, string>>;\n}\n\n/**\n * A fully typed, composable search params definition.\n *\n * Returned by createSearchParams(). Carries a phantom _type property\n * for build-time type extraction.\n */\nexport interface SearchParamsDefinition<T extends Record<string, unknown>> {\n /** Parse raw URL search params into typed values. */\n parse(raw: URLSearchParams | Record<string, string | string[] | undefined>): T;\n\n /** Client hook — reads current URL params and returns typed values + setter. */\n useQueryStates(options?: QueryStatesOptions): [T, SetParams<T>];\n\n /** Extend with additional codecs. Key collisions are a type error. */\n extend<U extends Record<string, SearchParamCodec<unknown>>>(\n codecs: U,\n options?: SearchParamsOptions<string>\n ): SearchParamsDefinition<T & { [K in keyof U]: InferCodec<U[K]> }>;\n\n /** Pick a subset of keys. Preserves codecs and aliases. */\n pick<K extends keyof T & string>(...keys: K[]): SearchParamsDefinition<Pick<T, K>>;\n\n /** Serialize values to a query string (no leading '?'), omitting defaults. */\n serialize(values: Partial<T>): string;\n\n /** Build a full path with query string, omitting defaults. */\n href(pathname: string, values: Partial<T>): string;\n\n /** Build a URLSearchParams instance, omitting defaults. */\n toSearchParams(values: Partial<T>): URLSearchParams;\n\n /** Read-only codec map for spreading into .extend(). Aliases NOT carried. */\n codecs: { [K in keyof T]: SearchParamCodec<T[K]> };\n\n /** Read-only URL key alias map. Maps property names to URL query parameter keys. */\n readonly urlKeys: Readonly<Record<string, string>>;\n\n /**\n * Phantom property for build-time type extraction.\n * Never set at runtime — exists only in the type system.\n */\n readonly _type?: T;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert URLSearchParams or a plain record to a normalized record\n * where repeated keys produce arrays.\n */\nfunction normalizeRaw(\n raw: URLSearchParams | Record<string, string | string[] | undefined>\n): Record<string, string | string[] | undefined> {\n if (raw instanceof URLSearchParams) {\n const result: Record<string, string | string[] | undefined> = {};\n for (const key of new Set(raw.keys())) {\n const values = raw.getAll(key);\n result[key] = values.length === 1 ? values[0] : values;\n }\n return result;\n }\n return raw;\n}\n\n/**\n * Compute the serialized default value for a codec. Used for\n * default-omission: when serialize(value) === serialize(parse(undefined)),\n * the field is omitted from the URL.\n */\nfunction getDefaultSerialized<T>(codec: SearchParamCodec<T>): string | null {\n return codec.serialize(codec.parse(undefined));\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a SearchParamsDefinition from a codec map and optional URL key aliases.\n *\n * ```ts\n * import { createSearchParams, fromSchema } from '@timber/app/search-params'\n * import { z } from 'zod/v4'\n *\n * export default createSearchParams({\n * page: fromSchema(z.coerce.number().int().min(1).default(1)),\n * q: { parse: (v) => v ?? null, serialize: (v) => v },\n * }, {\n * urlKeys: { q: 'search' },\n * })\n * ```\n */\nexport function createSearchParams<C extends Record<string, SearchParamCodec<unknown>>>(\n codecs: C,\n options?: SearchParamsOptions<Extract<keyof C, string>>\n): SearchParamsDefinition<{ [K in keyof C]: InferCodec<C[K]> }> {\n type T = { [K in keyof C]: InferCodec<C[K]> };\n const urlKeys: Record<string, string> = {};\n if (options?.urlKeys) {\n for (const [k, v] of Object.entries(options.urlKeys)) {\n if (v !== undefined) urlKeys[k] = v;\n }\n }\n\n return buildDefinition<T>(codecs as unknown as CodecMap<T>, urlKeys);\n}\n\n/**\n * Internal: build a SearchParamsDefinition from a typed codec map and url keys.\n */\nfunction buildDefinition<T extends Record<string, unknown>>(\n codecMap: CodecMap<T>,\n urlKeys: Record<string, string>\n): SearchParamsDefinition<T> {\n // Pre-compute default serialized values for omission check\n const defaultSerialized: Record<string, string | null> = {};\n for (const key of Object.keys(codecMap)) {\n defaultSerialized[key] = getDefaultSerialized(codecMap[key as keyof T]);\n }\n\n function getUrlKey(prop: string): string {\n return urlKeys[prop] ?? prop;\n }\n\n // ---- parse ----\n function parse(raw: URLSearchParams | Record<string, string | string[] | undefined>): T {\n const normalized = normalizeRaw(raw);\n const result: Record<string, unknown> = {};\n\n for (const prop of Object.keys(codecMap)) {\n const urlKey = getUrlKey(prop);\n const rawValue = normalized[urlKey];\n result[prop] = (codecMap[prop as keyof T] as SearchParamCodec<unknown>).parse(rawValue);\n }\n\n return result as T;\n }\n\n // ---- serialize ----\n function serialize(values: Partial<T>): string {\n const parts: string[] = [];\n\n for (const prop of Object.keys(codecMap)) {\n if (!(prop in values)) continue;\n const codec = codecMap[prop as keyof T] as SearchParamCodec<unknown>;\n const serialized = codec.serialize(values[prop as keyof T] as unknown);\n\n // Omit if serialized value matches the default\n if (serialized === defaultSerialized[prop]) continue;\n if (serialized === null) continue;\n\n parts.push(`${encodeURIComponent(getUrlKey(prop))}=${encodeURIComponent(serialized)}`);\n }\n\n return parts.join('&');\n }\n\n // ---- href ----\n function href(pathname: string, values: Partial<T>): string {\n const qs = serialize(values);\n return qs ? `${pathname}?${qs}` : pathname;\n }\n\n // ---- toSearchParams ----\n function toSearchParams(values: Partial<T>): URLSearchParams {\n const usp = new URLSearchParams();\n\n for (const prop of Object.keys(codecMap)) {\n if (!(prop in values)) continue;\n const codec = codecMap[prop as keyof T] as SearchParamCodec<unknown>;\n const serialized = codec.serialize(values[prop as keyof T] as unknown);\n\n if (serialized === defaultSerialized[prop]) continue;\n if (serialized === null) continue;\n\n usp.set(getUrlKey(prop), serialized);\n }\n\n return usp;\n }\n\n // ---- extend ----\n function extend<U extends Record<string, SearchParamCodec<unknown>>>(\n newCodecs: U,\n extendOptions?: SearchParamsOptions<string>\n ): SearchParamsDefinition<T & { [K in keyof U]: InferCodec<U[K]> }> {\n type Combined = T & { [K in keyof U]: InferCodec<U[K]> };\n\n const combinedCodecs = {\n ...codecMap,\n ...newCodecs,\n } as unknown as CodecMap<Combined>;\n\n // Merge URL keys: extend options override, but do NOT inherit from base\n // (aliases are route-level, not carried through .codecs)\n const combinedUrlKeys: Record<string, string> = { ...urlKeys };\n if (extendOptions?.urlKeys) {\n for (const [k, v] of Object.entries(extendOptions.urlKeys)) {\n if (v !== undefined) combinedUrlKeys[k] = v;\n }\n }\n\n return buildDefinition<Combined>(combinedCodecs, combinedUrlKeys);\n }\n\n // ---- pick ----\n function pick<K extends keyof T & string>(...keys: K[]): SearchParamsDefinition<Pick<T, K>> {\n const pickedCodecs: Record<string, SearchParamCodec<unknown>> = {};\n const pickedUrlKeys: Record<string, string> = {};\n\n for (const key of keys) {\n pickedCodecs[key] = codecMap[key] as SearchParamCodec<unknown>;\n if (key in urlKeys) {\n pickedUrlKeys[key] = urlKeys[key];\n }\n }\n\n return buildDefinition<Pick<T, K>>(\n pickedCodecs as unknown as CodecMap<Pick<T, K>>,\n pickedUrlKeys\n );\n }\n\n // ---- useQueryStates ----\n // This is a placeholder that will be replaced by the client runtime.\n // At import time in a server context, calling this throws.\n // The actual implementation wraps nuqs and lives in @timber/app/client.\n function useQueryStates(_options?: QueryStatesOptions): [T, SetParams<T>] {\n throw new Error(\n 'useQueryStates() can only be called in a client component. ' +\n 'Import from @timber/app/client instead.'\n );\n }\n\n const definition: SearchParamsDefinition<T> = {\n parse,\n useQueryStates,\n extend,\n pick,\n serialize,\n href,\n toSearchParams,\n codecs: codecMap,\n urlKeys: Object.freeze({ ...urlKeys }),\n };\n\n return definition;\n}\n","/**\n * Built-in codecs and the fromSchema bridge for Standard Schema-compatible\n * validation libraries (Zod, Valibot, ArkType).\n *\n * Design doc: design/09-typescript.md §\"The SearchParamCodec Protocol\"\n */\n\nimport type { SearchParamCodec } from './create.js';\n\n// ---------------------------------------------------------------------------\n// Standard Schema interface (subset)\n//\n// Standard Schema (https://github.com/standard-schema/standard-schema) defines\n// a minimal interface that Zod ≥3.24, Valibot ≥1.0, and ArkType all implement.\n// We depend only on `~standard.validate` to avoid coupling to any specific lib.\n// ---------------------------------------------------------------------------\n\ninterface StandardSchemaV1<Output = unknown> {\n '~standard': {\n validate(value: unknown): StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;\n };\n}\n\ntype StandardSchemaResult<Output> =\n | { value: Output; issues?: undefined }\n | { value?: undefined; issues: ReadonlyArray<{ message: string }> };\n\n// ---------------------------------------------------------------------------\n// Sync validate helper\n// ---------------------------------------------------------------------------\n\n/**\n * Zod v4's ~standard.validate() signature includes Promise in the return union\n * to satisfy the Standard Schema spec, but in practice Zod always validates\n * synchronously for the schema types we use. We assert the result is sync and\n * throw if it isn't — search params parsing must be synchronous.\n */\nfunction validateSync<Output>(\n schema: StandardSchemaV1<Output>,\n value: unknown\n): StandardSchemaResult<Output> {\n const result = schema['~standard'].validate(value);\n if (result instanceof Promise) {\n throw new Error(\n '[timber] fromSchema: schema returned a Promise — only sync schemas are supported for search params.'\n );\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// fromSchema — bridge from Standard Schema to SearchParamCodec\n// ---------------------------------------------------------------------------\n\n/**\n * Bridge a Standard Schema-compatible schema (Zod, Valibot, ArkType) to a\n * SearchParamCodec.\n *\n * Parse: coerces the raw URL string through the schema. On validation failure,\n * parses `undefined` to get the schema's default value (the schema should have\n * a `.default()` call). If that also fails, returns `undefined`.\n *\n * Serialize: uses `String()` for primitives, `null` for null/undefined.\n *\n * ```ts\n * import { fromSchema } from '@timber/app/search-params'\n * import { z } from 'zod/v4'\n *\n * const pageCodec = fromSchema(z.coerce.number().int().min(1).default(1))\n * ```\n */\nexport function fromSchema<T>(schema: StandardSchemaV1<T>): SearchParamCodec<T> {\n return {\n parse(value: string | string[] | undefined): T {\n // For array inputs, take the last value (consistent with URLSearchParams.get())\n const input = Array.isArray(value) ? value[value.length - 1] : value;\n\n // Try parsing the raw value\n const result = validateSync(schema, input);\n if (!result.issues) {\n return result.value;\n }\n\n // On failure, try parsing undefined to get the default\n const defaultResult = validateSync(schema, undefined);\n if (!defaultResult.issues) {\n return defaultResult.value;\n }\n\n // No default available — return undefined (codec design choice)\n return undefined as T;\n },\n\n serialize(value: T): string | null {\n if (value === null || value === undefined) {\n return null;\n }\n return String(value);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// fromArraySchema — bridge for array-valued search params\n// ---------------------------------------------------------------------------\n\n/**\n * Bridge a Standard Schema for array values. Handles both single strings\n * and repeated query keys (`?tag=a&tag=b`).\n *\n * ```ts\n * import { fromArraySchema } from '@timber/app/search-params'\n * import { z } from 'zod/v4'\n *\n * const tagsCodec = fromArraySchema(z.array(z.string()).default([]))\n * ```\n */\nexport function fromArraySchema<T>(schema: StandardSchemaV1<T>): SearchParamCodec<T> {\n return {\n parse(value: string | string[] | undefined): T {\n // Coerce single string to array for array schemas\n let input: unknown = value;\n if (typeof value === 'string') {\n input = [value];\n } else if (value === undefined) {\n input = undefined;\n }\n\n const result = validateSync(schema, input);\n if (!result.issues) {\n return result.value;\n }\n\n // On failure, try undefined for default\n const defaultResult = validateSync(schema, undefined);\n if (!defaultResult.issues) {\n return defaultResult.value;\n }\n\n return undefined as T;\n },\n\n serialize(value: T): string | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (Array.isArray(value)) {\n return value.length === 0 ? null : value.join(',');\n }\n return String(value);\n },\n };\n}\n","/**\n * Static analyzability checker for search-params.ts files.\n *\n * Validates that a search-params.ts file's default export is statically\n * analyzable — a createSearchParams() call or a chain of .extend()/.pick()\n * calls on a SearchParamsDefinition.\n *\n * Non-analyzable files produce a hard build error with a diagnostic.\n *\n * Design doc: design/09-typescript.md §\"Static Analyzability\"\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Result of analyzing a search-params.ts file. */\nexport interface AnalyzeResult {\n /** Whether the file is statically analyzable. */\n valid: boolean;\n /** Error details when valid is false. */\n error?: AnalyzeError;\n}\n\n/** Diagnostic error for non-analyzable search-params.ts. */\nexport interface AnalyzeError {\n /** Absolute file path. */\n filePath: string;\n /** Description of the non-analyzable expression. */\n expression: string;\n /** Suggested fix. */\n suggestion: string;\n}\n\n// ---------------------------------------------------------------------------\n// AST-free source analysis\n//\n// We use a lightweight regex-based approach to validate the structure of the\n// default export. This avoids requiring a TypeScript compiler instance at\n// build time for the initial validation pass. The full type extraction\n// (reading T from SearchParamsDefinition<T>) still happens via the TypeScript\n// compiler in the codegen step — this module just validates the *shape*.\n// ---------------------------------------------------------------------------\n\n/**\n * Patterns that indicate a valid default export:\n *\n * 1. `export default createSearchParams(...)`\n * 2. `export default someVar.extend(...)`\n * 3. `export default someVar.pick(...)`\n * 4. `export default someVar.extend(...).extend(...)` (chained)\n * 5. `export default someVar.extend(...).pick(...)` (chained)\n * 6. `export default createSearchParams(...).extend(...)`\n *\n * Invalid patterns:\n * - `export default someFunction(...)` (arbitrary factory)\n * - `export default condition ? a : b` (runtime conditional)\n * - `export default variable` (opaque reference without call)\n */\n\n/**\n * Analyze a search-params.ts file source for static analyzability.\n *\n * @param source - The file content as a string\n * @param filePath - Absolute path to the file (for diagnostics)\n */\nexport function analyzeSearchParams(source: string, filePath: string): AnalyzeResult {\n // Strip comments to avoid false matches\n const stripped = stripComments(source);\n\n // Find the default export\n const defaultExport = extractDefaultExport(stripped);\n\n if (!defaultExport) {\n return {\n valid: false,\n error: {\n filePath,\n expression: '(no default export found)',\n suggestion:\n 'search-params.ts must have a default export. Use: export default createSearchParams({ ... })',\n },\n };\n }\n\n // Validate the expression\n if (isValidExpression(defaultExport.trim())) {\n return { valid: true };\n }\n\n return {\n valid: false,\n error: {\n filePath,\n expression: defaultExport.trim(),\n suggestion:\n 'The default export must be a createSearchParams() call, or a chain of ' +\n '.extend() / .pick() calls on a SearchParamsDefinition. Arbitrary factory ' +\n 'functions and runtime conditionals are not supported.',\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Strip single-line and multi-line comments from source. */\nfunction stripComments(source: string): string {\n // Remove multi-line comments\n let result = source.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n // Remove single-line comments\n result = result.replace(/\\/\\/.*$/gm, '');\n return result;\n}\n\n/**\n * Extract the expression from `export default <expr>`.\n *\n * Handles both:\n * export default createSearchParams(...)\n * export default expr\\n (terminated by newline or semicolon before next statement)\n */\nfunction extractDefaultExport(source: string): string | undefined {\n // Match `export default` followed by the expression\n const match = source.match(\n /export\\s+default\\s+([\\s\\S]+?)(?:;|\\n(?=export|import|const|let|var|function|class|type|interface|declare))/\n );\n if (match) {\n return match[1];\n }\n\n // Fallback: match everything after `export default` to end of file\n const fallback = source.match(/export\\s+default\\s+([\\s\\S]+)$/);\n if (fallback) {\n return fallback[1].replace(/;\\s*$/, '');\n }\n\n return undefined;\n}\n\n/**\n * Check if an expression is a valid statically-analyzable pattern.\n *\n * Valid patterns:\n * - Starts with `createSearchParams(`\n * - Contains `.extend(` or `.pick(` chains (possibly starting with createSearchParams or a variable)\n * - A variable identifier followed by chaining\n */\nfunction isValidExpression(expr: string): boolean {\n // Normalize whitespace\n const normalized = expr.replace(/\\s+/g, ' ').trim();\n\n // Pattern 1: starts with createSearchParams(\n if (normalized.startsWith('createSearchParams(')) {\n return true;\n }\n\n // Pattern 2: chain ending with .extend(...) or .pick(...)\n // This covers: someVar.extend(...), createSearchParams(...).extend(...).pick(...), etc.\n if (/\\.(extend|pick)\\s*\\(/.test(normalized)) {\n // Reject ternaries and other conditional patterns\n if (/\\?/.test(normalized) && /:/.test(normalized)) {\n return false;\n }\n // Reject function declarations/expressions\n if (/^\\s*(function|=>|\\()/.test(normalized)) {\n return false;\n }\n return true;\n }\n\n return false;\n}\n\n/**\n * Format an AnalyzeError into a human-readable build error message.\n */\nexport function formatAnalyzeError(error: AnalyzeError): string {\n return [\n `[timber] Non-analyzable search-params.ts`,\n ``,\n ` File: ${error.filePath}`,\n ` Expression: ${error.expression}`,\n ``,\n ` ${error.suggestion}`,\n ``,\n ` The framework must be able to statically extract the type from your`,\n ` search-params.ts at build time. Dynamic values, conditionals, and`,\n ` arbitrary factory functions prevent this analysis.`,\n ].join('\\n');\n}\n"],"mappings":";;;;;;AAoHA,SAAS,aACP,KAC+C;AAC/C,KAAI,eAAe,iBAAiB;EAClC,MAAM,SAAwD,EAAE;AAChE,OAAK,MAAM,OAAO,IAAI,IAAI,IAAI,MAAM,CAAC,EAAE;GACrC,MAAM,SAAS,IAAI,OAAO,IAAI;AAC9B,UAAO,OAAO,OAAO,WAAW,IAAI,OAAO,KAAK;;AAElD,SAAO;;AAET,QAAO;;;;;;;AAQT,SAAS,qBAAwB,OAA2C;AAC1E,QAAO,MAAM,UAAU,MAAM,MAAM,KAAA,EAAU,CAAC;;;;;;;;;;;;;;;;;AAsBhD,SAAgB,mBACd,QACA,SAC8D;CAE9D,MAAM,UAAkC,EAAE;AAC1C,KAAI,SAAS;OACN,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,QAAQ,QAAQ,CAClD,KAAI,MAAM,KAAA,EAAW,SAAQ,KAAK;;AAItC,QAAO,gBAAmB,QAAkC,QAAQ;;;;;AAMtE,SAAS,gBACP,UACA,SAC2B;CAE3B,MAAM,oBAAmD,EAAE;AAC3D,MAAK,MAAM,OAAO,OAAO,KAAK,SAAS,CACrC,mBAAkB,OAAO,qBAAqB,SAAS,KAAgB;CAGzE,SAAS,UAAU,MAAsB;AACvC,SAAO,QAAQ,SAAS;;CAI1B,SAAS,MAAM,KAAyE;EACtF,MAAM,aAAa,aAAa,IAAI;EACpC,MAAM,SAAkC,EAAE;AAE1C,OAAK,MAAM,QAAQ,OAAO,KAAK,SAAS,EAAE;GAExC,MAAM,WAAW,WADF,UAAU,KAAK;AAE9B,UAAO,QAAS,SAAS,MAA+C,MAAM,SAAS;;AAGzF,SAAO;;CAIT,SAAS,UAAU,QAA4B;EAC7C,MAAM,QAAkB,EAAE;AAE1B,OAAK,MAAM,QAAQ,OAAO,KAAK,SAAS,EAAE;AACxC,OAAI,EAAE,QAAQ,QAAS;GAEvB,MAAM,aADQ,SAAS,MACE,UAAU,OAAO,MAA4B;AAGtE,OAAI,eAAe,kBAAkB,MAAO;AAC5C,OAAI,eAAe,KAAM;AAEzB,SAAM,KAAK,GAAG,mBAAmB,UAAU,KAAK,CAAC,CAAC,GAAG,mBAAmB,WAAW,GAAG;;AAGxF,SAAO,MAAM,KAAK,IAAI;;CAIxB,SAAS,KAAK,UAAkB,QAA4B;EAC1D,MAAM,KAAK,UAAU,OAAO;AAC5B,SAAO,KAAK,GAAG,SAAS,GAAG,OAAO;;CAIpC,SAAS,eAAe,QAAqC;EAC3D,MAAM,MAAM,IAAI,iBAAiB;AAEjC,OAAK,MAAM,QAAQ,OAAO,KAAK,SAAS,EAAE;AACxC,OAAI,EAAE,QAAQ,QAAS;GAEvB,MAAM,aADQ,SAAS,MACE,UAAU,OAAO,MAA4B;AAEtE,OAAI,eAAe,kBAAkB,MAAO;AAC5C,OAAI,eAAe,KAAM;AAEzB,OAAI,IAAI,UAAU,KAAK,EAAE,WAAW;;AAGtC,SAAO;;CAIT,SAAS,OACP,WACA,eACkE;EAGlE,MAAM,iBAAiB;GACrB,GAAG;GACH,GAAG;GACJ;EAID,MAAM,kBAA0C,EAAE,GAAG,SAAS;AAC9D,MAAI,eAAe;QACZ,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,cAAc,QAAQ,CACxD,KAAI,MAAM,KAAA,EAAW,iBAAgB,KAAK;;AAI9C,SAAO,gBAA0B,gBAAgB,gBAAgB;;CAInE,SAAS,KAAiC,GAAG,MAA+C;EAC1F,MAAM,eAA0D,EAAE;EAClE,MAAM,gBAAwC,EAAE;AAEhD,OAAK,MAAM,OAAO,MAAM;AACtB,gBAAa,OAAO,SAAS;AAC7B,OAAI,OAAO,QACT,eAAc,OAAO,QAAQ;;AAIjC,SAAO,gBACL,cACA,cACD;;CAOH,SAAS,eAAe,UAAkD;AACxE,QAAM,IAAI,MACR,qGAED;;AAeH,QAZ8C;EAC5C;EACA;EACA;EACA;EACA;EACA;EACA;EACA,QAAQ;EACR,SAAS,OAAO,OAAO,EAAE,GAAG,SAAS,CAAC;EACvC;;;;;;;;;;ACjRH,SAAS,aACP,QACA,OAC8B;CAC9B,MAAM,SAAS,OAAO,aAAa,SAAS,MAAM;AAClD,KAAI,kBAAkB,QACpB,OAAM,IAAI,MACR,sGACD;AAEH,QAAO;;;;;;;;;;;;;;;;;;;AAwBT,SAAgB,WAAc,QAAkD;AAC9E,QAAO;EACL,MAAM,OAAyC;GAK7C,MAAM,SAAS,aAAa,QAHd,MAAM,QAAQ,MAAM,GAAG,MAAM,MAAM,SAAS,KAAK,MAGrB;AAC1C,OAAI,CAAC,OAAO,OACV,QAAO,OAAO;GAIhB,MAAM,gBAAgB,aAAa,QAAQ,KAAA,EAAU;AACrD,OAAI,CAAC,cAAc,OACjB,QAAO,cAAc;;EAOzB,UAAU,OAAyB;AACjC,OAAI,UAAU,QAAQ,UAAU,KAAA,EAC9B,QAAO;AAET,UAAO,OAAO,MAAM;;EAEvB;;;;;;;;;;;;;AAkBH,SAAgB,gBAAmB,QAAkD;AACnF,QAAO;EACL,MAAM,OAAyC;GAE7C,IAAI,QAAiB;AACrB,OAAI,OAAO,UAAU,SACnB,SAAQ,CAAC,MAAM;YACN,UAAU,KAAA,EACnB,SAAQ,KAAA;GAGV,MAAM,SAAS,aAAa,QAAQ,MAAM;AAC1C,OAAI,CAAC,OAAO,OACV,QAAO,OAAO;GAIhB,MAAM,gBAAgB,aAAa,QAAQ,KAAA,EAAU;AACrD,OAAI,CAAC,cAAc,OACjB,QAAO,cAAc;;EAMzB,UAAU,OAAyB;AACjC,OAAI,UAAU,QAAQ,UAAU,KAAA,EAC9B,QAAO;AAET,OAAI,MAAM,QAAQ,MAAM,CACtB,QAAO,MAAM,WAAW,IAAI,OAAO,MAAM,KAAK,IAAI;AAEpD,UAAO,OAAO,MAAM;;EAEvB;;;;;;;;;;;;;;;;;;;;;;;;;ACrFH,SAAgB,oBAAoB,QAAgB,UAAiC;CAKnF,MAAM,gBAAgB,qBAHL,cAAc,OAAO,CAGc;AAEpD,KAAI,CAAC,cACH,QAAO;EACL,OAAO;EACP,OAAO;GACL;GACA,YAAY;GACZ,YACE;GACH;EACF;AAIH,KAAI,kBAAkB,cAAc,MAAM,CAAC,CACzC,QAAO,EAAE,OAAO,MAAM;AAGxB,QAAO;EACL,OAAO;EACP,OAAO;GACL;GACA,YAAY,cAAc,MAAM;GAChC,YACE;GAGH;EACF;;;AAQH,SAAS,cAAc,QAAwB;CAE7C,IAAI,SAAS,OAAO,QAAQ,qBAAqB,GAAG;AAEpD,UAAS,OAAO,QAAQ,aAAa,GAAG;AACxC,QAAO;;;;;;;;;AAUT,SAAS,qBAAqB,QAAoC;CAEhE,MAAM,QAAQ,OAAO,MACnB,6GACD;AACD,KAAI,MACF,QAAO,MAAM;CAIf,MAAM,WAAW,OAAO,MAAM,gCAAgC;AAC9D,KAAI,SACF,QAAO,SAAS,GAAG,QAAQ,SAAS,GAAG;;;;;;;;;;AAc3C,SAAS,kBAAkB,MAAuB;CAEhD,MAAM,aAAa,KAAK,QAAQ,QAAQ,IAAI,CAAC,MAAM;AAGnD,KAAI,WAAW,WAAW,sBAAsB,CAC9C,QAAO;AAKT,KAAI,uBAAuB,KAAK,WAAW,EAAE;AAE3C,MAAI,KAAK,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,CAC/C,QAAO;AAGT,MAAI,uBAAuB,KAAK,WAAW,CACzC,QAAO;AAET,SAAO;;AAGT,QAAO;;;;;AAMT,SAAgB,mBAAmB,OAA6B;AAC9D,QAAO;EACL;EACA;EACA,WAAW,MAAM;EACjB,iBAAiB,MAAM;EACvB;EACA,KAAK,MAAM;EACX;EACA;EACA;EACA;EACD,CAAC,KAAK,KAAK"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/search-params/create.ts","../../src/search-params/codecs.ts","../../src/search-params/analyze.ts"],"sourcesContent":["/**\n * createSearchParams — factory for SearchParamsDefinition<T>.\n *\n * Creates a typed, composable definition for a route's search parameters.\n * Supports codec protocol, URL key aliasing, default-omission serialization,\n * and composition via .extend() / .pick().\n *\n * Design doc: design/09-typescript.md §\"Typed searchParams — search-params.ts\"\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * A codec that converts between URL string values and typed values.\n *\n * nuqs parsers (parseAsInteger, parseAsString, etc.) implement this\n * interface natively — no adapter needed.\n */\nexport interface SearchParamCodec<T> {\n /** URL string → typed value. Receives undefined when the param is absent. */\n parse(value: string | string[] | undefined): T;\n /** Typed value → URL string. Return null to omit from URL. */\n serialize(value: T): string | null;\n}\n\n/** Infer the output type of a codec. */\nexport type InferCodec<C> = C extends SearchParamCodec<infer T> ? T : never;\n\n/** Map of property names to codecs. */\nexport type CodecMap<T extends Record<string, unknown>> = {\n [K in keyof T]: SearchParamCodec<T[K]>;\n};\n\n/** Options for useQueryStates setter. */\nexport interface SetParamsOptions {\n /** Update URL without server roundtrip (default: false). */\n shallow?: boolean;\n /** Scroll to top after update (default: true). */\n scroll?: boolean;\n /** 'push' (default) or 'replace' for history state. */\n history?: 'push' | 'replace';\n}\n\n/** Setter function returned by useQueryStates. */\nexport type SetParams<T> = (values: Partial<T>, options?: SetParamsOptions) => void;\n\n/** Options for useQueryStates hook. */\nexport interface QueryStatesOptions {\n /** Update URL without server roundtrip (default: false). */\n shallow?: boolean;\n /** Scroll to top after update (default: true). */\n scroll?: boolean;\n /** 'push' (default) or 'replace' for history state. */\n history?: 'push' | 'replace';\n}\n\n/** Options for createSearchParams and .extend(). */\nexport interface SearchParamsOptions<Keys extends string = string> {\n /** Map property names to different URL query parameter keys. */\n urlKeys?: Partial<Record<Keys, string>>;\n}\n\n/**\n * A fully typed, composable search params definition.\n *\n * Returned by createSearchParams(). Carries a phantom _type property\n * for build-time type extraction.\n */\nexport interface SearchParamsDefinition<T extends Record<string, unknown>> {\n /** Parse raw URL search params into typed values. */\n parse(raw: URLSearchParams | Record<string, string | string[] | undefined>): T;\n\n /** Client hook — reads current URL params and returns typed values + setter. */\n useQueryStates(options?: QueryStatesOptions): [T, SetParams<T>];\n\n /** Extend with additional codecs. Key collisions are a type error. */\n extend<U extends Record<string, SearchParamCodec<unknown>>>(\n codecs: U,\n options?: SearchParamsOptions<string>\n ): SearchParamsDefinition<T & { [K in keyof U]: InferCodec<U[K]> }>;\n\n /** Pick a subset of keys. Preserves codecs and aliases. */\n pick<K extends keyof T & string>(...keys: K[]): SearchParamsDefinition<Pick<T, K>>;\n\n /** Serialize values to a query string (no leading '?'), omitting defaults. */\n serialize(values: Partial<T>): string;\n\n /** Build a full path with query string, omitting defaults. */\n href(pathname: string, values: Partial<T>): string;\n\n /** Build a URLSearchParams instance, omitting defaults. */\n toSearchParams(values: Partial<T>): URLSearchParams;\n\n /** Read-only codec map for spreading into .extend(). Aliases NOT carried. */\n codecs: { [K in keyof T]: SearchParamCodec<T[K]> };\n\n /** Read-only URL key alias map. Maps property names to URL query parameter keys. */\n readonly urlKeys: Readonly<Record<string, string>>;\n\n /**\n * Phantom property for build-time type extraction.\n * Never set at runtime — exists only in the type system.\n */\n readonly _type?: T;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Convert URLSearchParams or a plain record to a normalized record\n * where repeated keys produce arrays.\n */\nfunction normalizeRaw(\n raw: URLSearchParams | Record<string, string | string[] | undefined>\n): Record<string, string | string[] | undefined> {\n if (raw instanceof URLSearchParams) {\n const result: Record<string, string | string[] | undefined> = {};\n for (const key of new Set(raw.keys())) {\n const values = raw.getAll(key);\n result[key] = values.length === 1 ? values[0] : values;\n }\n return result;\n }\n return raw;\n}\n\n/**\n * Compute the serialized default value for a codec. Used for\n * default-omission: when serialize(value) === serialize(parse(undefined)),\n * the field is omitted from the URL.\n */\nfunction getDefaultSerialized<T>(codec: SearchParamCodec<T>): string | null {\n return codec.serialize(codec.parse(undefined));\n}\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a SearchParamsDefinition from a codec map and optional URL key aliases.\n *\n * ```ts\n * import { createSearchParams, fromSchema } from '@timber-js/app/search-params'\n * import { z } from 'zod/v4'\n *\n * export default createSearchParams({\n * page: fromSchema(z.coerce.number().int().min(1).default(1)),\n * q: { parse: (v) => v ?? null, serialize: (v) => v },\n * }, {\n * urlKeys: { q: 'search' },\n * })\n * ```\n */\nexport function createSearchParams<C extends Record<string, SearchParamCodec<unknown>>>(\n codecs: C,\n options?: SearchParamsOptions<Extract<keyof C, string>>\n): SearchParamsDefinition<{ [K in keyof C]: InferCodec<C[K]> }> {\n type T = { [K in keyof C]: InferCodec<C[K]> };\n const urlKeys: Record<string, string> = {};\n if (options?.urlKeys) {\n for (const [k, v] of Object.entries(options.urlKeys)) {\n if (v !== undefined) urlKeys[k] = v;\n }\n }\n\n return buildDefinition<T>(codecs as unknown as CodecMap<T>, urlKeys);\n}\n\n/**\n * Internal: build a SearchParamsDefinition from a typed codec map and url keys.\n */\nfunction buildDefinition<T extends Record<string, unknown>>(\n codecMap: CodecMap<T>,\n urlKeys: Record<string, string>\n): SearchParamsDefinition<T> {\n // Pre-compute default serialized values for omission check\n const defaultSerialized: Record<string, string | null> = {};\n for (const key of Object.keys(codecMap)) {\n defaultSerialized[key] = getDefaultSerialized(codecMap[key as keyof T]);\n }\n\n function getUrlKey(prop: string): string {\n return urlKeys[prop] ?? prop;\n }\n\n // ---- parse ----\n function parse(raw: URLSearchParams | Record<string, string | string[] | undefined>): T {\n const normalized = normalizeRaw(raw);\n const result: Record<string, unknown> = {};\n\n for (const prop of Object.keys(codecMap)) {\n const urlKey = getUrlKey(prop);\n const rawValue = normalized[urlKey];\n result[prop] = (codecMap[prop as keyof T] as SearchParamCodec<unknown>).parse(rawValue);\n }\n\n return result as T;\n }\n\n // ---- serialize ----\n function serialize(values: Partial<T>): string {\n const parts: string[] = [];\n\n for (const prop of Object.keys(codecMap)) {\n if (!(prop in values)) continue;\n const codec = codecMap[prop as keyof T] as SearchParamCodec<unknown>;\n const serialized = codec.serialize(values[prop as keyof T] as unknown);\n\n // Omit if serialized value matches the default\n if (serialized === defaultSerialized[prop]) continue;\n if (serialized === null) continue;\n\n parts.push(`${encodeURIComponent(getUrlKey(prop))}=${encodeURIComponent(serialized)}`);\n }\n\n return parts.join('&');\n }\n\n // ---- href ----\n function href(pathname: string, values: Partial<T>): string {\n const qs = serialize(values);\n return qs ? `${pathname}?${qs}` : pathname;\n }\n\n // ---- toSearchParams ----\n function toSearchParams(values: Partial<T>): URLSearchParams {\n const usp = new URLSearchParams();\n\n for (const prop of Object.keys(codecMap)) {\n if (!(prop in values)) continue;\n const codec = codecMap[prop as keyof T] as SearchParamCodec<unknown>;\n const serialized = codec.serialize(values[prop as keyof T] as unknown);\n\n if (serialized === defaultSerialized[prop]) continue;\n if (serialized === null) continue;\n\n usp.set(getUrlKey(prop), serialized);\n }\n\n return usp;\n }\n\n // ---- extend ----\n function extend<U extends Record<string, SearchParamCodec<unknown>>>(\n newCodecs: U,\n extendOptions?: SearchParamsOptions<string>\n ): SearchParamsDefinition<T & { [K in keyof U]: InferCodec<U[K]> }> {\n type Combined = T & { [K in keyof U]: InferCodec<U[K]> };\n\n const combinedCodecs = {\n ...codecMap,\n ...newCodecs,\n } as unknown as CodecMap<Combined>;\n\n // Merge URL keys: extend options override, but do NOT inherit from base\n // (aliases are route-level, not carried through .codecs)\n const combinedUrlKeys: Record<string, string> = { ...urlKeys };\n if (extendOptions?.urlKeys) {\n for (const [k, v] of Object.entries(extendOptions.urlKeys)) {\n if (v !== undefined) combinedUrlKeys[k] = v;\n }\n }\n\n return buildDefinition<Combined>(combinedCodecs, combinedUrlKeys);\n }\n\n // ---- pick ----\n function pick<K extends keyof T & string>(...keys: K[]): SearchParamsDefinition<Pick<T, K>> {\n const pickedCodecs: Record<string, SearchParamCodec<unknown>> = {};\n const pickedUrlKeys: Record<string, string> = {};\n\n for (const key of keys) {\n pickedCodecs[key] = codecMap[key] as SearchParamCodec<unknown>;\n if (key in urlKeys) {\n pickedUrlKeys[key] = urlKeys[key];\n }\n }\n\n return buildDefinition<Pick<T, K>>(\n pickedCodecs as unknown as CodecMap<Pick<T, K>>,\n pickedUrlKeys\n );\n }\n\n // ---- useQueryStates ----\n // This is a placeholder that will be replaced by the client runtime.\n // At import time in a server context, calling this throws.\n // The actual implementation wraps nuqs and lives in @timber-js/app/client.\n function useQueryStates(_options?: QueryStatesOptions): [T, SetParams<T>] {\n throw new Error(\n 'useQueryStates() can only be called in a client component. ' +\n 'Import from @timber-js/app/client instead.'\n );\n }\n\n const definition: SearchParamsDefinition<T> = {\n parse,\n useQueryStates,\n extend,\n pick,\n serialize,\n href,\n toSearchParams,\n codecs: codecMap,\n urlKeys: Object.freeze({ ...urlKeys }),\n };\n\n return definition;\n}\n","/**\n * Built-in codecs and the fromSchema bridge for Standard Schema-compatible\n * validation libraries (Zod, Valibot, ArkType).\n *\n * Design doc: design/09-typescript.md §\"The SearchParamCodec Protocol\"\n */\n\nimport type { SearchParamCodec } from './create.js';\n\n// ---------------------------------------------------------------------------\n// Standard Schema interface (subset)\n//\n// Standard Schema (https://github.com/standard-schema/standard-schema) defines\n// a minimal interface that Zod ≥3.24, Valibot ≥1.0, and ArkType all implement.\n// We depend only on `~standard.validate` to avoid coupling to any specific lib.\n// ---------------------------------------------------------------------------\n\ninterface StandardSchemaV1<Output = unknown> {\n '~standard': {\n validate(value: unknown): StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;\n };\n}\n\ntype StandardSchemaResult<Output> =\n | { value: Output; issues?: undefined }\n | { value?: undefined; issues: ReadonlyArray<{ message: string }> };\n\n// ---------------------------------------------------------------------------\n// Sync validate helper\n// ---------------------------------------------------------------------------\n\n/**\n * Zod v4's ~standard.validate() signature includes Promise in the return union\n * to satisfy the Standard Schema spec, but in practice Zod always validates\n * synchronously for the schema types we use. We assert the result is sync and\n * throw if it isn't — search params parsing must be synchronous.\n */\nfunction validateSync<Output>(\n schema: StandardSchemaV1<Output>,\n value: unknown\n): StandardSchemaResult<Output> {\n const result = schema['~standard'].validate(value);\n if (result instanceof Promise) {\n throw new Error(\n '[timber] fromSchema: schema returned a Promise — only sync schemas are supported for search params.'\n );\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// fromSchema — bridge from Standard Schema to SearchParamCodec\n// ---------------------------------------------------------------------------\n\n/**\n * Bridge a Standard Schema-compatible schema (Zod, Valibot, ArkType) to a\n * SearchParamCodec.\n *\n * Parse: coerces the raw URL string through the schema. On validation failure,\n * parses `undefined` to get the schema's default value (the schema should have\n * a `.default()` call). If that also fails, returns `undefined`.\n *\n * Serialize: uses `String()` for primitives, `null` for null/undefined.\n *\n * ```ts\n * import { fromSchema } from '@timber-js/app/search-params'\n * import { z } from 'zod/v4'\n *\n * const pageCodec = fromSchema(z.coerce.number().int().min(1).default(1))\n * ```\n */\nexport function fromSchema<T>(schema: StandardSchemaV1<T>): SearchParamCodec<T> {\n return {\n parse(value: string | string[] | undefined): T {\n // For array inputs, take the last value (consistent with URLSearchParams.get())\n const input = Array.isArray(value) ? value[value.length - 1] : value;\n\n // Try parsing the raw value\n const result = validateSync(schema, input);\n if (!result.issues) {\n return result.value;\n }\n\n // On failure, try parsing undefined to get the default\n const defaultResult = validateSync(schema, undefined);\n if (!defaultResult.issues) {\n return defaultResult.value;\n }\n\n // No default available — return undefined (codec design choice)\n return undefined as T;\n },\n\n serialize(value: T): string | null {\n if (value === null || value === undefined) {\n return null;\n }\n return String(value);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// fromArraySchema — bridge for array-valued search params\n// ---------------------------------------------------------------------------\n\n/**\n * Bridge a Standard Schema for array values. Handles both single strings\n * and repeated query keys (`?tag=a&tag=b`).\n *\n * ```ts\n * import { fromArraySchema } from '@timber-js/app/search-params'\n * import { z } from 'zod/v4'\n *\n * const tagsCodec = fromArraySchema(z.array(z.string()).default([]))\n * ```\n */\nexport function fromArraySchema<T>(schema: StandardSchemaV1<T>): SearchParamCodec<T> {\n return {\n parse(value: string | string[] | undefined): T {\n // Coerce single string to array for array schemas\n let input: unknown = value;\n if (typeof value === 'string') {\n input = [value];\n } else if (value === undefined) {\n input = undefined;\n }\n\n const result = validateSync(schema, input);\n if (!result.issues) {\n return result.value;\n }\n\n // On failure, try undefined for default\n const defaultResult = validateSync(schema, undefined);\n if (!defaultResult.issues) {\n return defaultResult.value;\n }\n\n return undefined as T;\n },\n\n serialize(value: T): string | null {\n if (value === null || value === undefined) {\n return null;\n }\n if (Array.isArray(value)) {\n return value.length === 0 ? null : value.join(',');\n }\n return String(value);\n },\n };\n}\n","/**\n * Static analyzability checker for search-params.ts files.\n *\n * Validates that a search-params.ts file's default export is statically\n * analyzable — a createSearchParams() call or a chain of .extend()/.pick()\n * calls on a SearchParamsDefinition.\n *\n * Non-analyzable files produce a hard build error with a diagnostic.\n *\n * Design doc: design/09-typescript.md §\"Static Analyzability\"\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Result of analyzing a search-params.ts file. */\nexport interface AnalyzeResult {\n /** Whether the file is statically analyzable. */\n valid: boolean;\n /** Error details when valid is false. */\n error?: AnalyzeError;\n}\n\n/** Diagnostic error for non-analyzable search-params.ts. */\nexport interface AnalyzeError {\n /** Absolute file path. */\n filePath: string;\n /** Description of the non-analyzable expression. */\n expression: string;\n /** Suggested fix. */\n suggestion: string;\n}\n\n// ---------------------------------------------------------------------------\n// AST-free source analysis\n//\n// We use a lightweight regex-based approach to validate the structure of the\n// default export. This avoids requiring a TypeScript compiler instance at\n// build time for the initial validation pass. The full type extraction\n// (reading T from SearchParamsDefinition<T>) still happens via the TypeScript\n// compiler in the codegen step — this module just validates the *shape*.\n// ---------------------------------------------------------------------------\n\n/**\n * Patterns that indicate a valid default export:\n *\n * 1. `export default createSearchParams(...)`\n * 2. `export default someVar.extend(...)`\n * 3. `export default someVar.pick(...)`\n * 4. `export default someVar.extend(...).extend(...)` (chained)\n * 5. `export default someVar.extend(...).pick(...)` (chained)\n * 6. `export default createSearchParams(...).extend(...)`\n *\n * Invalid patterns:\n * - `export default someFunction(...)` (arbitrary factory)\n * - `export default condition ? a : b` (runtime conditional)\n * - `export default variable` (opaque reference without call)\n */\n\n/**\n * Analyze a search-params.ts file source for static analyzability.\n *\n * @param source - The file content as a string\n * @param filePath - Absolute path to the file (for diagnostics)\n */\nexport function analyzeSearchParams(source: string, filePath: string): AnalyzeResult {\n // Strip comments to avoid false matches\n const stripped = stripComments(source);\n\n // Find the default export\n const defaultExport = extractDefaultExport(stripped);\n\n if (!defaultExport) {\n return {\n valid: false,\n error: {\n filePath,\n expression: '(no default export found)',\n suggestion:\n 'search-params.ts must have a default export. Use: export default createSearchParams({ ... })',\n },\n };\n }\n\n // Validate the expression\n if (isValidExpression(defaultExport.trim())) {\n return { valid: true };\n }\n\n return {\n valid: false,\n error: {\n filePath,\n expression: defaultExport.trim(),\n suggestion:\n 'The default export must be a createSearchParams() call, or a chain of ' +\n '.extend() / .pick() calls on a SearchParamsDefinition. Arbitrary factory ' +\n 'functions and runtime conditionals are not supported.',\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Strip single-line and multi-line comments from source. */\nfunction stripComments(source: string): string {\n // Remove multi-line comments\n let result = source.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n // Remove single-line comments\n result = result.replace(/\\/\\/.*$/gm, '');\n return result;\n}\n\n/**\n * Extract the expression from `export default <expr>`.\n *\n * Handles both:\n * export default createSearchParams(...)\n * export default expr\\n (terminated by newline or semicolon before next statement)\n */\nfunction extractDefaultExport(source: string): string | undefined {\n // Match `export default` followed by the expression\n const match = source.match(\n /export\\s+default\\s+([\\s\\S]+?)(?:;|\\n(?=export|import|const|let|var|function|class|type|interface|declare))/\n );\n if (match) {\n return match[1];\n }\n\n // Fallback: match everything after `export default` to end of file\n const fallback = source.match(/export\\s+default\\s+([\\s\\S]+)$/);\n if (fallback) {\n return fallback[1].replace(/;\\s*$/, '');\n }\n\n return undefined;\n}\n\n/**\n * Check if an expression is a valid statically-analyzable pattern.\n *\n * Valid patterns:\n * - Starts with `createSearchParams(`\n * - Contains `.extend(` or `.pick(` chains (possibly starting with createSearchParams or a variable)\n * - A variable identifier followed by chaining\n */\nfunction isValidExpression(expr: string): boolean {\n // Normalize whitespace\n const normalized = expr.replace(/\\s+/g, ' ').trim();\n\n // Pattern 1: starts with createSearchParams(\n if (normalized.startsWith('createSearchParams(')) {\n return true;\n }\n\n // Pattern 2: chain ending with .extend(...) or .pick(...)\n // This covers: someVar.extend(...), createSearchParams(...).extend(...).pick(...), etc.\n if (/\\.(extend|pick)\\s*\\(/.test(normalized)) {\n // Reject ternaries and other conditional patterns\n if (/\\?/.test(normalized) && /:/.test(normalized)) {\n return false;\n }\n // Reject function declarations/expressions\n if (/^\\s*(function|=>|\\()/.test(normalized)) {\n return false;\n }\n return true;\n }\n\n return false;\n}\n\n/**\n * Format an AnalyzeError into a human-readable build error message.\n */\nexport function formatAnalyzeError(error: AnalyzeError): string {\n return [\n `[timber] Non-analyzable search-params.ts`,\n ``,\n ` File: ${error.filePath}`,\n ` Expression: ${error.expression}`,\n ``,\n ` ${error.suggestion}`,\n ``,\n ` The framework must be able to statically extract the type from your`,\n ` search-params.ts at build time. Dynamic values, conditionals, and`,\n ` arbitrary factory functions prevent this analysis.`,\n ].join('\\n');\n}\n"],"mappings":"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"}
@@ -174,7 +174,7 @@ export declare function createActionClient<TCtx = Record<string, never>>(config?
174
174
  * @example
175
175
  * ```ts
176
176
  * 'use server'
177
- * import { validated } from '@timber/app/server'
177
+ * import { validated } from '@timber-js/app/server'
178
178
  * import { z } from 'zod'
179
179
  *
180
180
  * export const createTodo = validated(
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Dev-mode fetch instrumentation — patches globalThis.fetch to create OTEL
3
+ * spans for every fetch call, giving visibility into async data fetching
4
+ * in the dev request log tree.
5
+ *
6
+ * Only activated in dev mode — zero overhead in production.
7
+ *
8
+ * The spans are automatically children of the active OTEL context (e.g. a
9
+ * timber.page or timber.layout span), so they appear nested under the
10
+ * component that initiated the fetch in the dev log tree.
11
+ *
12
+ * Design ref: 17-logging.md §"Dev Logging", LOCAL-289
13
+ */
14
+ export type DevFetchCleanup = () => void;
15
+ /**
16
+ * Patch globalThis.fetch to wrap every call in an OTEL span.
17
+ *
18
+ * Returns a cleanup function that restores the original fetch.
19
+ * Only call this in dev mode.
20
+ */
21
+ export declare function instrumentDevFetch(): DevFetchCleanup;
22
+ //# sourceMappingURL=dev-fetch-instrumentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-fetch-instrumentation.d.ts","sourceRoot":"","sources":["../../src/server/dev-fetch-instrumentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,MAAM,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC;AAEzC;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CA+CpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"dev-logger.d.ts","sourceRoot":"","sources":["../../src/server/dev-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3E,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AA8LD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,UAAU,CAMnE;AAID;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,CAgDtF;AA6DD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,CAc1F;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CA6BxD"}
1
+ {"version":3,"file":"dev-logger.d.ts","sourceRoot":"","sources":["../../src/server/dev-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3E,MAAM,WAAW,eAAe;IAC9B,qCAAqC;IACrC,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAmMD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,eAAe,GAAG,UAAU,CAMnE;AAID;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,CAgDtF;AAyGD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,MAAM,CAc1F;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CA6BxD"}
@@ -51,7 +51,7 @@ export interface FormFlashData {
51
51
  *
52
52
  * ```tsx
53
53
  * // app/contact/page.tsx (server component)
54
- * import { getFormFlash } from '@timber/app/server'
54
+ * import { getFormFlash } from '@timber-js/app/server'
55
55
  *
56
56
  * export default function ContactPage() {
57
57
  * const flash = getFormFlash()
@@ -1,8 +1,8 @@
1
1
  import { a as warnDenyAfterFlush, c as warnRedirectInAccess, d as warnSlowSlotWithoutSuspense, f as warnStaticRequestApi, i as warnCacheRequestProps, l as warnRedirectInSlotAccess, n as WarningId, o as warnDenyInSuspense, p as warnSuspenseWrappingChildren, r as setViteServer, s as warnDynamicApiInStaticBuild, t as formatSize, u as warnRedirectInSuspense } from "../_chunks/format-DNt20Kt8.js";
2
2
  import { i as getMetadataRouteServePath, n as classifyMetadataRoute, r as getMetadataRouteAutoLink, t as METADATA_ROUTE_CONVENTIONS } from "../_chunks/metadata-routes-BDnswgRO.js";
3
- import { a as markResponseFlushed, c as setCookieSecrets, i as headers, l as setMutableCookieContext, n as cookies, o as runWithRequestContext, r as getSetCookieHeaders, s as searchParams, t as applyRequestHeaderOverlay, u as setParsedSearchParams } from "../_chunks/request-context-D6XHINkR.js";
3
+ import { a as markResponseFlushed, c as setCookieSecrets, i as headers, l as setMutableCookieContext, n as cookies, o as runWithRequestContext, r as getSetCookieHeaders, s as searchParams, t as applyRequestHeaderOverlay, u as setParsedSearchParams } from "../_chunks/request-context-BzES06i1.js";
4
4
  import { a as replaceTraceId, c as spanId, i as getTraceStore, l as traceId, n as generateTraceId, o as runWithTraceId, r as getOtelTraceId, s as setSpanAttribute, t as addSpanEvent, u as withSpan } from "../_chunks/tracing-BtOwb8O6.js";
5
- import { t as TimberErrorBoundary } from "../_chunks/error-boundary-dj-WO5uq.js";
5
+ import { TimberErrorBoundary } from "../client/error-boundary.js";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  //#region src/server/primitives.ts
8
8
  /**
@@ -2595,7 +2595,7 @@ function createActionClient(config = {}) {
2595
2595
  * @example
2596
2596
  * ```ts
2597
2597
  * 'use server'
2598
- * import { validated } from '@timber/app/server'
2598
+ * import { validated } from '@timber-js/app/server'
2599
2599
  * import { z } from 'zod'
2600
2600
  *
2601
2601
  * export const createTodo = validated(
@@ -2678,7 +2678,7 @@ var formFlashAls = new AsyncLocalStorage();
2678
2678
  *
2679
2679
  * ```tsx
2680
2680
  * // app/contact/page.tsx (server component)
2681
- * import { getFormFlash } from '@timber/app/server'
2681
+ * import { getFormFlash } from '@timber-js/app/server'
2682
2682
  *
2683
2683
  * export default function ContactPage() {
2684
2684
  * const flash = getFormFlash()