cms-renderer 0.0.0 → 0.1.1

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 (51) hide show
  1. package/dist/chunk-HVKFEZBT.js +116 -0
  2. package/dist/chunk-HVKFEZBT.js.map +1 -0
  3. package/dist/chunk-JHKDRASN.js +39 -0
  4. package/dist/chunk-JHKDRASN.js.map +1 -0
  5. package/dist/chunk-RPM73PQZ.js +17 -0
  6. package/dist/chunk-RPM73PQZ.js.map +1 -0
  7. package/dist/lib/block-renderer.d.ts +32 -0
  8. package/dist/lib/block-renderer.js +7 -0
  9. package/dist/lib/block-renderer.js.map +1 -0
  10. package/dist/lib/cms-api.d.ts +25 -0
  11. package/dist/lib/cms-api.js +7 -0
  12. package/dist/lib/cms-api.js.map +1 -0
  13. package/dist/lib/data-utils.d.ts +218 -0
  14. package/dist/lib/data-utils.js +247 -0
  15. package/dist/lib/data-utils.js.map +1 -0
  16. package/dist/lib/image/lazy-load.d.ts +75 -0
  17. package/dist/lib/image/lazy-load.js +83 -0
  18. package/dist/lib/image/lazy-load.js.map +1 -0
  19. package/dist/lib/markdown-utils.d.ts +172 -0
  20. package/dist/lib/markdown-utils.js +137 -0
  21. package/dist/lib/markdown-utils.js.map +1 -0
  22. package/dist/lib/renderer.d.ts +40 -0
  23. package/dist/lib/renderer.js +371 -0
  24. package/dist/lib/renderer.js.map +1 -0
  25. package/{lib/result.ts → dist/lib/result.d.ts} +32 -146
  26. package/dist/lib/result.js +37 -0
  27. package/dist/lib/result.js.map +1 -0
  28. package/dist/lib/schema.d.ts +15 -0
  29. package/dist/lib/schema.js +35 -0
  30. package/dist/lib/schema.js.map +1 -0
  31. package/{lib/trpc.ts → dist/lib/trpc.d.ts} +6 -4
  32. package/dist/lib/trpc.js +7 -0
  33. package/dist/lib/trpc.js.map +1 -0
  34. package/dist/lib/types.d.ts +163 -0
  35. package/dist/lib/types.js +1 -0
  36. package/dist/lib/types.js.map +1 -0
  37. package/package.json +50 -11
  38. package/.turbo/turbo-check-types.log +0 -2
  39. package/lib/__tests__/enrich-block-images.test.ts +0 -394
  40. package/lib/block-renderer.tsx +0 -60
  41. package/lib/cms-api.ts +0 -86
  42. package/lib/data-utils.ts +0 -572
  43. package/lib/image/lazy-load.ts +0 -209
  44. package/lib/markdown-utils.ts +0 -368
  45. package/lib/renderer.tsx +0 -189
  46. package/lib/schema.ts +0 -74
  47. package/lib/types.ts +0 -201
  48. package/next.config.ts +0 -39
  49. package/postcss.config.mjs +0 -5
  50. package/tsconfig.json +0 -12
  51. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,116 @@
1
+ // lib/result.ts
2
+ function ok(value) {
3
+ return { ok: true, value };
4
+ }
5
+ function err(error) {
6
+ return { ok: false, error };
7
+ }
8
+ function isOk(result) {
9
+ return result.ok === true;
10
+ }
11
+ function isErr(result) {
12
+ return result.ok === false;
13
+ }
14
+ function unwrap(result) {
15
+ if (isOk(result)) {
16
+ return result.value;
17
+ }
18
+ throw result.error;
19
+ }
20
+ function unwrapOr(result, defaultValue) {
21
+ if (isOk(result)) {
22
+ return result.value;
23
+ }
24
+ return defaultValue;
25
+ }
26
+ function unwrapOrElse(result, fn) {
27
+ if (isOk(result)) {
28
+ return result.value;
29
+ }
30
+ return fn(result.error);
31
+ }
32
+ function map(result, fn) {
33
+ if (isOk(result)) {
34
+ return ok(fn(result.value));
35
+ }
36
+ return result;
37
+ }
38
+ function mapErr(result, fn) {
39
+ if (isErr(result)) {
40
+ return err(fn(result.error));
41
+ }
42
+ return result;
43
+ }
44
+ function flatMap(result, fn) {
45
+ if (isOk(result)) {
46
+ return fn(result.value);
47
+ }
48
+ return result;
49
+ }
50
+ async function fromPromise(promise) {
51
+ try {
52
+ const value = await promise;
53
+ return ok(value);
54
+ } catch (error) {
55
+ return err(error instanceof Error ? error : new Error(String(error)));
56
+ }
57
+ }
58
+ function tryCatch(fn) {
59
+ try {
60
+ return ok(fn());
61
+ } catch (error) {
62
+ return err(error instanceof Error ? error : new Error(String(error)));
63
+ }
64
+ }
65
+ async function tryCatchAsync(fn) {
66
+ try {
67
+ const value = await fn();
68
+ return ok(value);
69
+ } catch (error) {
70
+ return err(error instanceof Error ? error : new Error(String(error)));
71
+ }
72
+ }
73
+ function toTuple(result) {
74
+ if (isOk(result)) {
75
+ return [void 0, result.value];
76
+ }
77
+ return [result.error, void 0];
78
+ }
79
+ async function handle(fn) {
80
+ try {
81
+ const value = await fn();
82
+ return [void 0, value];
83
+ } catch (error) {
84
+ const err2 = error instanceof Error ? error : new Error(String(error));
85
+ return [err2, void 0];
86
+ }
87
+ }
88
+ function handleSync(fn) {
89
+ try {
90
+ const value = fn();
91
+ return [void 0, value];
92
+ } catch (error) {
93
+ const err2 = error instanceof Error ? error : new Error(String(error));
94
+ return [err2, void 0];
95
+ }
96
+ }
97
+
98
+ export {
99
+ ok,
100
+ err,
101
+ isOk,
102
+ isErr,
103
+ unwrap,
104
+ unwrapOr,
105
+ unwrapOrElse,
106
+ map,
107
+ mapErr,
108
+ flatMap,
109
+ fromPromise,
110
+ tryCatch,
111
+ tryCatchAsync,
112
+ toTuple,
113
+ handle,
114
+ handleSync
115
+ };
116
+ //# sourceMappingURL=chunk-HVKFEZBT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../lib/result.ts"],"sourcesContent":["/**\n * Result Type Utilities\n *\n * Go-style error handling with discriminated union types.\n * Provides type-safe success/error handling without exceptions.\n *\n * @example\n * ```ts\n * const [err, data] = await handle(async () => {\n * const response = await fetch(url);\n * if (!response.ok) throw new Error(`HTTP ${response.status}`);\n * return response.json();\n * });\n *\n * if (err) {\n * console.error('Failed:', err.message);\n * return { error: err.message };\n * }\n * return { data };\n * ```\n */\n\n// -----------------------------------------------------------------------------\n// Result Type\n// -----------------------------------------------------------------------------\n\n/**\n * A discriminated union representing either success or failure.\n *\n * @template T - The success value type\n * @template E - The error type (defaults to Error)\n *\n * @example\n * ```ts\n * function divide(a: number, b: number): Result<number, string> {\n * if (b === 0) return err('Division by zero');\n * return ok(a / b);\n * }\n *\n * const result = divide(10, 2);\n * if (isOk(result)) {\n * console.log('Result:', result.value); // 5\n * } else {\n * console.error('Error:', result.error);\n * }\n * ```\n */\nexport type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };\n\n// -----------------------------------------------------------------------------\n// Constructors\n// -----------------------------------------------------------------------------\n\n/**\n * Creates a success Result.\n *\n * @param value - The success value\n * @returns A success Result containing the value\n *\n * @example\n * ```ts\n * const result = ok(42);\n * // { ok: true, value: 42 }\n * ```\n */\nexport function ok<T>(value: T): Result<T, never> {\n return { ok: true, value };\n}\n\n/**\n * Creates a failure Result.\n *\n * @param error - The error value\n * @returns A failure Result containing the error\n *\n * @example\n * ```ts\n * const result = err(new Error('Something went wrong'));\n * // { ok: false, error: Error('Something went wrong') }\n * ```\n */\nexport function err<E>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n\n// -----------------------------------------------------------------------------\n// Type Guards\n// -----------------------------------------------------------------------------\n\n/**\n * Type guard to check if a Result is successful.\n *\n * @param result - The Result to check\n * @returns true if the Result is a success\n *\n * @example\n * ```ts\n * const result = fetchData();\n * if (isOk(result)) {\n * // TypeScript knows result.value exists here\n * console.log(result.value);\n * }\n * ```\n */\nexport function isOk<T, E>(result: Result<T, E>): result is { ok: true; value: T } {\n return result.ok === true;\n}\n\n/**\n * Type guard to check if a Result is a failure.\n *\n * @param result - The Result to check\n * @returns true if the Result is a failure\n *\n * @example\n * ```ts\n * const result = fetchData();\n * if (isErr(result)) {\n * // TypeScript knows result.error exists here\n * console.error(result.error);\n * }\n * ```\n */\nexport function isErr<T, E>(result: Result<T, E>): result is { ok: false; error: E } {\n return result.ok === false;\n}\n\n// -----------------------------------------------------------------------------\n// Extractors\n// -----------------------------------------------------------------------------\n\n/**\n * Extracts the value from a Result, throwing if it's an error.\n *\n * @param result - The Result to unwrap\n * @returns The success value\n * @throws The error if Result is a failure\n *\n * @example\n * ```ts\n * const result = ok(42);\n * const value = unwrap(result); // 42\n *\n * const errorResult = err(new Error('fail'));\n * const value2 = unwrap(errorResult); // throws Error('fail')\n * ```\n */\nexport function unwrap<T, E>(result: Result<T, E>): T {\n if (isOk(result)) {\n return result.value;\n }\n throw result.error;\n}\n\n/**\n * Extracts the value from a Result, returning a default on error.\n *\n * @param result - The Result to unwrap\n * @param defaultValue - The value to return if Result is an error\n * @returns The success value or the default value\n *\n * @example\n * ```ts\n * const result = err(new Error('fail'));\n * const value = unwrapOr(result, 0); // 0\n *\n * const okResult = ok(42);\n * const value2 = unwrapOr(okResult, 0); // 42\n * ```\n */\nexport function unwrapOr<T, E>(result: Result<T, E>, defaultValue: T): T {\n if (isOk(result)) {\n return result.value;\n }\n return defaultValue;\n}\n\n/**\n * Extracts the value from a Result, computing a default on error.\n *\n * @param result - The Result to unwrap\n * @param fn - Function to compute the default value from the error\n * @returns The success value or the computed default\n *\n * @example\n * ```ts\n * const result = err(new Error('not found'));\n * const value = unwrapOrElse(result, (e) => {\n * console.error('Error:', e.message);\n * return [];\n * });\n * ```\n */\nexport function unwrapOrElse<T, E>(result: Result<T, E>, fn: (error: E) => T): T {\n if (isOk(result)) {\n return result.value;\n }\n return fn(result.error);\n}\n\n// -----------------------------------------------------------------------------\n// Transformers\n// -----------------------------------------------------------------------------\n\n/**\n * Maps a successful Result's value.\n *\n * @param result - The Result to map\n * @param fn - Function to transform the value\n * @returns A new Result with the transformed value, or the original error\n *\n * @example\n * ```ts\n * const result = ok(5);\n * const doubled = map(result, (n) => n * 2); // ok(10)\n *\n * const errorResult = err('fail');\n * const still = map(errorResult, (n) => n * 2); // err('fail')\n * ```\n */\nexport function map<T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> {\n if (isOk(result)) {\n return ok(fn(result.value));\n }\n return result;\n}\n\n/**\n * Maps a failed Result's error.\n *\n * @param result - The Result to map\n * @param fn - Function to transform the error\n * @returns A new Result with the transformed error, or the original value\n *\n * @example\n * ```ts\n * const result = err('not found');\n * const mapped = mapErr(result, (e) => new Error(e)); // err(Error('not found'))\n * ```\n */\nexport function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> {\n if (isErr(result)) {\n return err(fn(result.error));\n }\n return result;\n}\n\n/**\n * Chains Result-returning operations.\n *\n * @param result - The Result to chain from\n * @param fn - Function that returns a new Result\n * @returns The chained Result\n *\n * @example\n * ```ts\n * function parse(input: string): Result<number, string> {\n * const n = parseInt(input, 10);\n * return isNaN(n) ? err('not a number') : ok(n);\n * }\n *\n * function double(n: number): Result<number, string> {\n * return ok(n * 2);\n * }\n *\n * const result = flatMap(parse('5'), double); // ok(10)\n * const fail = flatMap(parse('abc'), double); // err('not a number')\n * ```\n */\nexport function flatMap<T, U, E>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, E>\n): Result<U, E> {\n if (isOk(result)) {\n return fn(result.value);\n }\n return result;\n}\n\n// -----------------------------------------------------------------------------\n// Async Helpers\n// -----------------------------------------------------------------------------\n\n/**\n * Wraps a Promise in a Result type.\n *\n * @param promise - The Promise to wrap\n * @returns A Promise that resolves to a Result\n *\n * @example\n * ```ts\n * const result = await fromPromise(fetch('/api/data'));\n * if (isErr(result)) {\n * console.error('Fetch failed:', result.error);\n * return;\n * }\n * const response = result.value;\n * ```\n */\nexport async function fromPromise<T>(promise: Promise<T>): Promise<Result<T, Error>> {\n try {\n const value = await promise;\n return ok(value);\n } catch (error) {\n return err(error instanceof Error ? error : new Error(String(error)));\n }\n}\n\n/**\n * Wraps a throwing function in a Result type.\n *\n * @param fn - The function to wrap\n * @returns A Result containing the return value or the thrown error\n *\n * @example\n * ```ts\n * const result = tryCatch(() => JSON.parse(input));\n * if (isErr(result)) {\n * console.error('Invalid JSON:', result.error);\n * return null;\n * }\n * return result.value;\n * ```\n */\nexport function tryCatch<T>(fn: () => T): Result<T, Error> {\n try {\n return ok(fn());\n } catch (error) {\n return err(error instanceof Error ? error : new Error(String(error)));\n }\n}\n\n/**\n * Wraps an async function in a Result type.\n *\n * @param fn - The async function to wrap\n * @returns A Promise that resolves to a Result\n *\n * @example\n * ```ts\n * const result = await tryCatchAsync(async () => {\n * const response = await fetch('/api/data');\n * if (!response.ok) throw new Error(`HTTP ${response.status}`);\n * return response.json();\n * });\n * ```\n */\nexport async function tryCatchAsync<T>(fn: () => Promise<T>): Promise<Result<T, Error>> {\n try {\n const value = await fn();\n return ok(value);\n } catch (error) {\n return err(error instanceof Error ? error : new Error(String(error)));\n }\n}\n\n// -----------------------------------------------------------------------------\n// Tuple Helpers (Go-style)\n// -----------------------------------------------------------------------------\n\n/**\n * Go-style tuple for error handling: [error, value]\n *\n * @example\n * ```ts\n * const [err, data] = await handle(fetchData);\n * if (err) {\n * console.error(err);\n * return;\n * }\n * console.log(data);\n * ```\n */\nexport type GoTuple<T, E = Error> = [E, undefined] | [undefined, T];\n\n/**\n * Converts a Result to a Go-style tuple.\n *\n * @param result - The Result to convert\n * @returns A tuple of [error, value]\n *\n * @example\n * ```ts\n * const result = await fetchData();\n * const [err, data] = toTuple(result);\n * ```\n */\nexport function toTuple<T, E>(result: Result<T, E>): GoTuple<T, E> {\n if (isOk(result)) {\n return [undefined, result.value];\n }\n return [result.error, undefined];\n}\n\n/**\n * Wraps an async function and returns a Go-style tuple.\n *\n * @param fn - The async function to wrap\n * @returns A Promise that resolves to [error, value] tuple\n *\n * @example\n * ```ts\n * const [err, data] = await handle(async () => {\n * const res = await fetch('/api/users');\n * if (!res.ok) throw new Error(`HTTP ${res.status}`);\n * return res.json();\n * });\n *\n * if (err) {\n * console.error('Failed to fetch users:', err.message);\n * return [];\n * }\n * return data;\n * ```\n */\nexport async function handle<T>(fn: () => Promise<T>): Promise<GoTuple<T, Error>> {\n try {\n const value = await fn();\n return [undefined, value];\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return [err, undefined];\n }\n}\n\n/**\n * Wraps a synchronous function and returns a Go-style tuple.\n *\n * @param fn - The function to wrap\n * @returns A tuple of [error, value]\n *\n * @example\n * ```ts\n * const [err, parsed] = handleSync(() => JSON.parse(input));\n * if (err) {\n * console.error('Invalid JSON:', err.message);\n * return null;\n * }\n * return parsed;\n * ```\n */\nexport function handleSync<T>(fn: () => T): GoTuple<T, Error> {\n try {\n const value = fn();\n return [undefined, value];\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n return [err, undefined];\n }\n}\n"],"mappings":";AAiEO,SAAS,GAAM,OAA4B;AAChD,SAAO,EAAE,IAAI,MAAM,MAAM;AAC3B;AAcO,SAAS,IAAO,OAA4B;AACjD,SAAO,EAAE,IAAI,OAAO,MAAM;AAC5B;AAqBO,SAAS,KAAW,QAAwD;AACjF,SAAO,OAAO,OAAO;AACvB;AAiBO,SAAS,MAAY,QAAyD;AACnF,SAAO,OAAO,OAAO;AACvB;AAsBO,SAAS,OAAa,QAAyB;AACpD,MAAI,KAAK,MAAM,GAAG;AAChB,WAAO,OAAO;AAAA,EAChB;AACA,QAAM,OAAO;AACf;AAkBO,SAAS,SAAe,QAAsB,cAAoB;AACvE,MAAI,KAAK,MAAM,GAAG;AAChB,WAAO,OAAO;AAAA,EAChB;AACA,SAAO;AACT;AAkBO,SAAS,aAAmB,QAAsB,IAAwB;AAC/E,MAAI,KAAK,MAAM,GAAG;AAChB,WAAO,OAAO;AAAA,EAChB;AACA,SAAO,GAAG,OAAO,KAAK;AACxB;AAsBO,SAAS,IAAa,QAAsB,IAAmC;AACpF,MAAI,KAAK,MAAM,GAAG;AAChB,WAAO,GAAG,GAAG,OAAO,KAAK,CAAC;AAAA,EAC5B;AACA,SAAO;AACT;AAeO,SAAS,OAAgB,QAAsB,IAAmC;AACvF,MAAI,MAAM,MAAM,GAAG;AACjB,WAAO,IAAI,GAAG,OAAO,KAAK,CAAC;AAAA,EAC7B;AACA,SAAO;AACT;AAwBO,SAAS,QACd,QACA,IACc;AACd,MAAI,KAAK,MAAM,GAAG;AAChB,WAAO,GAAG,OAAO,KAAK;AAAA,EACxB;AACA,SAAO;AACT;AAsBA,eAAsB,YAAe,SAAgD;AACnF,MAAI;AACF,UAAM,QAAQ,MAAM;AACpB,WAAO,GAAG,KAAK;AAAA,EACjB,SAAS,OAAO;AACd,WAAO,IAAI,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,EACtE;AACF;AAkBO,SAAS,SAAY,IAA+B;AACzD,MAAI;AACF,WAAO,GAAG,GAAG,CAAC;AAAA,EAChB,SAAS,OAAO;AACd,WAAO,IAAI,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,EACtE;AACF;AAiBA,eAAsB,cAAiB,IAAiD;AACtF,MAAI;AACF,UAAM,QAAQ,MAAM,GAAG;AACvB,WAAO,GAAG,KAAK;AAAA,EACjB,SAAS,OAAO;AACd,WAAO,IAAI,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,EACtE;AACF;AAiCO,SAAS,QAAc,QAAqC;AACjE,MAAI,KAAK,MAAM,GAAG;AAChB,WAAO,CAAC,QAAW,OAAO,KAAK;AAAA,EACjC;AACA,SAAO,CAAC,OAAO,OAAO,MAAS;AACjC;AAuBA,eAAsB,OAAU,IAAkD;AAChF,MAAI;AACF,UAAM,QAAQ,MAAM,GAAG;AACvB,WAAO,CAAC,QAAW,KAAK;AAAA,EAC1B,SAAS,OAAO;AACd,UAAMA,OAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,WAAO,CAACA,MAAK,MAAS;AAAA,EACxB;AACF;AAkBO,SAAS,WAAc,IAAgC;AAC5D,MAAI;AACF,UAAM,QAAQ,GAAG;AACjB,WAAO,CAAC,QAAW,KAAK;AAAA,EAC1B,SAAS,OAAO;AACd,UAAMA,OAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACpE,WAAO,CAACA,MAAK,MAAS;AAAA,EACxB;AACF;","names":["err"]}
@@ -0,0 +1,39 @@
1
+ // lib/cms-api.ts
2
+ import { createTRPCClient, httpBatchLink } from "@trpc/client";
3
+ import superjson from "superjson";
4
+ function getCmsApiUrl(cmsUrl) {
5
+ return `${cmsUrl}/api/trpc`;
6
+ }
7
+ function createFetchWithApiKey(apiKey) {
8
+ return async (url, options) => {
9
+ let finalUrl = url;
10
+ if (apiKey) {
11
+ const urlObj = new URL(url.toString());
12
+ urlObj.searchParams.set("api_key", apiKey);
13
+ finalUrl = urlObj.toString();
14
+ }
15
+ const response = await fetch(finalUrl, options);
16
+ return response;
17
+ };
18
+ }
19
+ function createCmsClient(options) {
20
+ const url = getCmsApiUrl(options.cmsUrl);
21
+ console.log("[CMS API] Creating client with URL:", url);
22
+ return createTRPCClient({
23
+ links: [
24
+ httpBatchLink({
25
+ url,
26
+ transformer: superjson,
27
+ fetch: createFetchWithApiKey(options.apiKey)
28
+ })
29
+ ]
30
+ });
31
+ }
32
+ function getCmsClient(options) {
33
+ return createCmsClient(options);
34
+ }
35
+
36
+ export {
37
+ getCmsClient
38
+ };
39
+ //# sourceMappingURL=chunk-JHKDRASN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../lib/cms-api.ts"],"sourcesContent":["/**\n * CMS API Client\n *\n * Creates an HTTP-based tRPC client for calling the CMS API.\n * Used in Server Components to fetch routes and blocks from the CMS.\n */\n\nimport type { AppRouter } from '@repo/cms-schema/trpc';\nimport { type CreateTRPCClient, createTRPCClient, httpBatchLink } from '@trpc/client';\nimport superjson from 'superjson';\n\n/** Type alias for the CMS API client */\ntype CmsClient = CreateTRPCClient<AppRouter>;\n\n/**\n * Get the CMS API URL from the provided base URL.\n */\nfunction getCmsApiUrl(cmsUrl: string): string {\n return `${cmsUrl}/api/trpc`;\n}\n\n/** Options for creating a CMS client */\nexport interface CmsClientOptions {\n /** CMS API base URL (e.g., 'http://localhost:3000') */\n cmsUrl: string;\n /** API key for authentication (passed as query parameter) */\n apiKey?: string;\n}\n\n/**\n * Create a custom fetch function that appends API key as query parameter.\n */\nfunction createFetchWithApiKey(apiKey?: string) {\n return async (url: URL | RequestInfo, options?: RequestInit): Promise<Response> => {\n let finalUrl = url;\n\n // Append api_key to URL if provided\n if (apiKey) {\n const urlObj = new URL(url.toString());\n urlObj.searchParams.set('api_key', apiKey);\n finalUrl = urlObj.toString();\n }\n\n const response = await fetch(finalUrl, options);\n\n return response;\n };\n}\n\n/**\n * Create a tRPC client for the CMS API.\n */\nfunction createCmsClient(options: CmsClientOptions): CmsClient {\n const url = getCmsApiUrl(options.cmsUrl);\n console.log('[CMS API] Creating client with URL:', url);\n\n return createTRPCClient<AppRouter>({\n links: [\n httpBatchLink({\n url,\n transformer: superjson,\n fetch: createFetchWithApiKey(options.apiKey),\n }),\n ],\n });\n}\n\n/**\n * Get a CMS client for the specified CMS URL.\n */\nexport function getCmsClient(options: CmsClientOptions): CmsClient {\n return createCmsClient(options);\n}\n"],"mappings":";AAQA,SAAgC,kBAAkB,qBAAqB;AACvE,OAAO,eAAe;AAQtB,SAAS,aAAa,QAAwB;AAC5C,SAAO,GAAG,MAAM;AAClB;AAaA,SAAS,sBAAsB,QAAiB;AAC9C,SAAO,OAAO,KAAwB,YAA6C;AACjF,QAAI,WAAW;AAGf,QAAI,QAAQ;AACV,YAAM,SAAS,IAAI,IAAI,IAAI,SAAS,CAAC;AACrC,aAAO,aAAa,IAAI,WAAW,MAAM;AACzC,iBAAW,OAAO,SAAS;AAAA,IAC7B;AAEA,UAAM,WAAW,MAAM,MAAM,UAAU,OAAO;AAE9C,WAAO;AAAA,EACT;AACF;AAKA,SAAS,gBAAgB,SAAsC;AAC7D,QAAM,MAAM,aAAa,QAAQ,MAAM;AACvC,UAAQ,IAAI,uCAAuC,GAAG;AAEtD,SAAO,iBAA4B;AAAA,IACjC,OAAO;AAAA,MACL,cAAc;AAAA,QACZ;AAAA,QACA,aAAa;AAAA,QACb,OAAO,sBAAsB,QAAQ,MAAM;AAAA,MAC7C,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAKO,SAAS,aAAa,SAAsC;AACjE,SAAO,gBAAgB,OAAO;AAChC;","names":[]}
@@ -0,0 +1,17 @@
1
+ // lib/block-renderer.tsx
2
+ import { jsx } from "react/jsx-runtime";
3
+ function BlockRenderer({ block, registry }) {
4
+ const Component = registry[block.type];
5
+ if (!Component) {
6
+ if (process.env.NODE_ENV === "development") {
7
+ console.warn(`[BlockRenderer] Unknown block type: ${block.type}`);
8
+ }
9
+ return null;
10
+ }
11
+ return /* @__PURE__ */ jsx(Component, { content: block.content });
12
+ }
13
+
14
+ export {
15
+ BlockRenderer
16
+ };
17
+ //# sourceMappingURL=chunk-RPM73PQZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../lib/block-renderer.tsx"],"sourcesContent":["/**\n * Block Renderer Component\n *\n * Dispatches block data to the appropriate component using the ComponentMap pattern.\n * This is the main entry point for rendering blocks from the CMS.\n */\n\nimport type { BlockComponentRegistry, BlockData } from './types';\n\n// -----------------------------------------------------------------------------\n// Props\n// -----------------------------------------------------------------------------\n\ninterface BlockRendererProps {\n /**\n * The block data to render.\n * Must have a `type` field that maps to a registered component.\n */\n block: BlockData;\n registry: Partial<BlockComponentRegistry>;\n}\n\n// -----------------------------------------------------------------------------\n// Component\n// -----------------------------------------------------------------------------\n\n/**\n * Renders a single block by dispatching to the appropriate component.\n *\n * Uses the ComponentMap pattern: the block's `type` field determines which\n * component renders the block's `content`.\n *\n * @example\n * ```tsx\n * // Render a single block\n * <BlockRenderer block={{ type: 'header', content: { headline: 'Hello' } }} />\n *\n * // Render an array of blocks\n * {page.blocks.map((block, index) => (\n * <BlockRenderer key={index} block={block} />\n * ))}\n * ```\n */\nexport function BlockRenderer({ block, registry }: BlockRendererProps) {\n const Component = registry[block.type];\n\n if (!Component) {\n // Log warning in development, render nothing in production\n if (process.env.NODE_ENV === 'development') {\n console.warn(`[BlockRenderer] Unknown block type: ${block.type}`);\n }\n return null;\n }\n\n // TypeScript cannot narrow the content type through the component lookup,\n // so we use a type assertion here. Runtime safety is guaranteed by the\n // discriminated union and the blockComponents registry.\n // biome-ignore lint/suspicious/noExplicitAny: Type safety ensured by BlockData discriminated union\n return <Component content={block.content as any} />;\n}\n"],"mappings":";AA0DS;AAfF,SAAS,cAAc,EAAE,OAAO,SAAS,GAAuB;AACrE,QAAM,YAAY,SAAS,MAAM,IAAI;AAErC,MAAI,CAAC,WAAW;AAEd,QAAI,QAAQ,IAAI,aAAa,eAAe;AAC1C,cAAQ,KAAK,uCAAuC,MAAM,IAAI,EAAE;AAAA,IAClE;AACA,WAAO;AAAA,EACT;AAMA,SAAO,oBAAC,aAAU,SAAS,MAAM,SAAgB;AACnD;","names":[]}
@@ -0,0 +1,32 @@
1
+ import * as react from 'react';
2
+ import { BlockData, BlockComponentRegistry } from './types.js';
3
+ import '@repo/cms-schema/blocks';
4
+
5
+ interface BlockRendererProps {
6
+ /**
7
+ * The block data to render.
8
+ * Must have a `type` field that maps to a registered component.
9
+ */
10
+ block: BlockData;
11
+ registry: Partial<BlockComponentRegistry>;
12
+ }
13
+ /**
14
+ * Renders a single block by dispatching to the appropriate component.
15
+ *
16
+ * Uses the ComponentMap pattern: the block's `type` field determines which
17
+ * component renders the block's `content`.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * // Render a single block
22
+ * <BlockRenderer block={{ type: 'header', content: { headline: 'Hello' } }} />
23
+ *
24
+ * // Render an array of blocks
25
+ * {page.blocks.map((block, index) => (
26
+ * <BlockRenderer key={index} block={block} />
27
+ * ))}
28
+ * ```
29
+ */
30
+ declare function BlockRenderer({ block, registry }: BlockRendererProps): react.JSX.Element | null;
31
+
32
+ export { BlockRenderer };
@@ -0,0 +1,7 @@
1
+ import {
2
+ BlockRenderer
3
+ } from "../chunk-RPM73PQZ.js";
4
+ export {
5
+ BlockRenderer
6
+ };
7
+ //# sourceMappingURL=block-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,25 @@
1
+ import { AppRouter } from '@repo/cms-schema/trpc';
2
+ import { CreateTRPCClient } from '@trpc/client';
3
+
4
+ /**
5
+ * CMS API Client
6
+ *
7
+ * Creates an HTTP-based tRPC client for calling the CMS API.
8
+ * Used in Server Components to fetch routes and blocks from the CMS.
9
+ */
10
+
11
+ /** Type alias for the CMS API client */
12
+ type CmsClient = CreateTRPCClient<AppRouter>;
13
+ /** Options for creating a CMS client */
14
+ interface CmsClientOptions {
15
+ /** CMS API base URL (e.g., 'http://localhost:3000') */
16
+ cmsUrl: string;
17
+ /** API key for authentication (passed as query parameter) */
18
+ apiKey?: string;
19
+ }
20
+ /**
21
+ * Get a CMS client for the specified CMS URL.
22
+ */
23
+ declare function getCmsClient(options: CmsClientOptions): CmsClient;
24
+
25
+ export { type CmsClientOptions, getCmsClient };
@@ -0,0 +1,7 @@
1
+ import {
2
+ getCmsClient
3
+ } from "../chunk-JHKDRASN.js";
4
+ export {
5
+ getCmsClient
6
+ };
7
+ //# sourceMappingURL=cms-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,218 @@
1
+ import { Result } from './result.js';
2
+ import { BlockData } from './types.js';
3
+ import '@repo/cms-schema/blocks';
4
+ import 'react';
5
+
6
+ /**
7
+ * Data Fetching Utilities
8
+ *
9
+ * Three implementations showing the progression from naive to production-ready:
10
+ * - v1 (Naive): Direct await, crashes on any error
11
+ * - v2 (Defensive): Try-catch with null fallback
12
+ * - v3 (Robust): Result type, retry logic, timeout support
13
+ *
14
+ * The robust version (v3) is exported as the default `fetchPage`.
15
+ */
16
+
17
+ /**
18
+ * Page structure returned by the tRPC API.
19
+ */
20
+ interface Page {
21
+ slug: string;
22
+ title: string;
23
+ blocks: BlockData[];
24
+ }
25
+ /**
26
+ * Error codes for data fetching failures.
27
+ */
28
+ declare const FetchErrorCode: {
29
+ readonly INVALID_SLUG: "INVALID_SLUG";
30
+ readonly NOT_FOUND: "NOT_FOUND";
31
+ readonly NETWORK_ERROR: "NETWORK_ERROR";
32
+ readonly TIMEOUT: "TIMEOUT";
33
+ readonly PARSE_ERROR: "PARSE_ERROR";
34
+ readonly SERVER_ERROR: "SERVER_ERROR";
35
+ readonly RETRY_EXHAUSTED: "RETRY_EXHAUSTED";
36
+ };
37
+ type FetchErrorCode = (typeof FetchErrorCode)[keyof typeof FetchErrorCode];
38
+ /**
39
+ * Structured error for data fetching failures.
40
+ */
41
+ interface FetchError {
42
+ code: FetchErrorCode;
43
+ message: string;
44
+ cause?: Error;
45
+ retryCount?: number;
46
+ }
47
+ /**
48
+ * Options for robust data fetching.
49
+ */
50
+ interface FetchOptions {
51
+ /**
52
+ * Timeout in milliseconds.
53
+ * @default 10000 (10 seconds)
54
+ */
55
+ timeout?: number;
56
+ /**
57
+ * Number of retry attempts for transient failures.
58
+ * @default 3
59
+ */
60
+ retries?: number;
61
+ /**
62
+ * Base delay between retries in milliseconds.
63
+ * Uses exponential backoff: delay * 2^attempt
64
+ * @default 1000 (1 second)
65
+ */
66
+ retryDelay?: number;
67
+ /**
68
+ * AbortSignal for cancellation support.
69
+ */
70
+ signal?: AbortSignal;
71
+ }
72
+ /**
73
+ * Naive data fetcher - crashes on any error.
74
+ *
75
+ * DO NOT USE IN PRODUCTION. This is for demonstration only.
76
+ *
77
+ * Problems:
78
+ * - No input validation
79
+ * - No error handling
80
+ * - Will crash the entire app on network failure
81
+ * - No timeout protection
82
+ * - No retry logic for transient failures
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * // This throws if slug is invalid or network fails
87
+ * const page = await fetchPageV1('demo');
88
+ * ```
89
+ */
90
+ declare function fetchPageV1(slug: string): Promise<Page>;
91
+ /**
92
+ * Defensive data fetcher - catches errors but loses context.
93
+ *
94
+ * Better than v1 but still problematic:
95
+ * - Returns null on any error (loses error details)
96
+ * - Caller can't distinguish between 404 and network failure
97
+ * - No retry logic for transient failures
98
+ * - No timeout protection
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * const page = await fetchPageV2(slug);
103
+ * if (!page) {
104
+ * // What went wrong? 404? Network? Timeout? We don't know.
105
+ * return <NotFound />;
106
+ * }
107
+ * ```
108
+ */
109
+ declare function fetchPageV2(slug: string): Promise<Page | null>;
110
+ /**
111
+ * Robust data fetcher - production-ready with full error context.
112
+ *
113
+ * Features:
114
+ * - Input validation with specific error codes
115
+ * - Result type for explicit error handling
116
+ * - Automatic retry with exponential backoff
117
+ * - Timeout support via AbortController
118
+ * - Distinguishes between error types (404 vs network vs timeout)
119
+ * - Preserves error context for debugging
120
+ *
121
+ * @param slug - Page slug to fetch
122
+ * @param options - Fetch options (timeout, retries, etc.)
123
+ * @returns Result containing the page data or a structured error
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * const result = await fetchPageV3('demo', {
128
+ * timeout: 5000,
129
+ * retries: 3,
130
+ * });
131
+ *
132
+ * if (!result.ok) {
133
+ * switch (result.error.code) {
134
+ * case 'NOT_FOUND':
135
+ * return <NotFoundPage slug={slug} />;
136
+ * case 'TIMEOUT':
137
+ * return <TimeoutError onRetry={handleRetry} />;
138
+ * case 'NETWORK_ERROR':
139
+ * return <NetworkError message={result.error.message} />;
140
+ * case 'RETRY_EXHAUSTED':
141
+ * return <RetryExhausted attempts={result.error.retryCount} />;
142
+ * default:
143
+ * return <GenericError />;
144
+ * }
145
+ * }
146
+ *
147
+ * return <PageRenderer page={result.value} />;
148
+ * ```
149
+ */
150
+ declare function fetchPageV3(slug: string, options?: FetchOptions): Promise<Result<Page, FetchError>>;
151
+ /**
152
+ * Production-ready page fetcher.
153
+ *
154
+ * This is an alias for `fetchPageV3` - the robust implementation
155
+ * with validation, retry logic, and Result-based error handling.
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * import { fetchPage } from '@/lib/data-utils';
160
+ *
161
+ * const result = await fetchPage('demo');
162
+ * if (!result.ok) {
163
+ * // Handle error with full context
164
+ * console.error(`[${result.error.code}] ${result.error.message}`);
165
+ * return null;
166
+ * }
167
+ * return result.value;
168
+ * ```
169
+ */
170
+ declare const fetchPage: typeof fetchPageV3;
171
+ /**
172
+ * Validates a page slug format.
173
+ *
174
+ * @param slug - Slug to validate
175
+ * @returns true if slug is valid format
176
+ */
177
+ declare function isValidSlug(slug: string): boolean;
178
+ /**
179
+ * Normalizes a slug (lowercase, trim, replace spaces with hyphens).
180
+ *
181
+ * @param input - Raw input to normalize
182
+ * @returns Normalized slug
183
+ */
184
+ declare function normalizeSlug(input: string): string;
185
+ /**
186
+ * Prefetches multiple pages in parallel with Result types.
187
+ *
188
+ * @param slugs - Array of page slugs to fetch
189
+ * @param options - Fetch options applied to all requests
190
+ * @returns Array of Results for each page
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * const results = await prefetchPages(['home', 'about', 'blog']);
195
+ * const errors = results.filter(r => !r.ok);
196
+ * if (errors.length > 0) {
197
+ * console.warn(`${errors.length} pages failed to load`);
198
+ * }
199
+ * ```
200
+ */
201
+ declare function prefetchPages(slugs: string[], options?: FetchOptions): Promise<Result<Page, FetchError>[]>;
202
+ /**
203
+ * Fetches a page with stale-while-revalidate semantics.
204
+ * Returns cached data immediately if available, then updates in background.
205
+ *
206
+ * @param slug - Page slug to fetch
207
+ * @param cache - Cache storage (e.g., Map, localStorage wrapper)
208
+ * @param options - Fetch options
209
+ * @returns Cached data or fresh fetch result
210
+ */
211
+ declare function fetchPageWithSWR(slug: string, cache: Map<string, {
212
+ data: Page;
213
+ timestamp: number;
214
+ }>, options?: FetchOptions & {
215
+ maxAge?: number;
216
+ }): Promise<Result<Page, FetchError>>;
217
+
218
+ export { type FetchError, FetchErrorCode, type FetchOptions, type Page, fetchPage, fetchPageV1, fetchPageV2, fetchPageV3, fetchPageWithSWR, isValidSlug, normalizeSlug, prefetchPages };