@flyo/nitro-next 1.13.0 → 2.0.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.
- package/dist/client.d.mts +15 -3
- package/dist/client.d.ts +15 -3
- package/dist/client.js.map +1 -1
- package/dist/client.mjs.map +1 -1
- package/dist/proxy.d.mts +6 -18
- package/dist/proxy.d.ts +6 -18
- package/dist/proxy.js +2 -1
- package/dist/proxy.js.map +1 -1
- package/dist/proxy.mjs +2 -1
- package/dist/proxy.mjs.map +1 -1
- package/dist/server.d.mts +136 -189
- package/dist/server.d.ts +136 -189
- package/dist/server.js +158 -187
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +157 -177
- package/dist/server.mjs.map +1 -1
- package/package.json +1 -1
package/dist/client.d.mts
CHANGED
|
@@ -21,10 +21,22 @@ type WysiwygJson = WysiwygNode | WysiwygNode[] | {
|
|
|
21
21
|
type: 'doc';
|
|
22
22
|
content: WysiwygNode[];
|
|
23
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* The minimal block shape `editable()` needs to wire live-editing.
|
|
26
|
+
*
|
|
27
|
+
* `editable()` reads only `uid`, so it deliberately accepts more than the full
|
|
28
|
+
* {@link Block}. The per-block types generated from a project's OpenAPI schema
|
|
29
|
+
* (e.g. `BlockHero`) are NOT structurally assignable to `Block`: their
|
|
30
|
+
* `content`/`config`/`slots` carry an `_empty` marker that clashes with
|
|
31
|
+
* `Block`'s index signatures (`{ [key: string]: BlockSlotValue }`). Typing
|
|
32
|
+
* against the read-surface keeps both the generic `Block` and those generated
|
|
33
|
+
* subtypes assignable, so callers don't need `as unknown as Block` casts.
|
|
34
|
+
*/
|
|
35
|
+
type EditableBlock = Pick<Block, 'uid'>;
|
|
24
36
|
/**
|
|
25
37
|
* Helper function to get editable props
|
|
26
38
|
*/
|
|
27
|
-
declare function editable(block:
|
|
39
|
+
declare function editable(block: EditableBlock): {
|
|
28
40
|
'data-flyo-uid'?: string;
|
|
29
41
|
};
|
|
30
42
|
/**
|
|
@@ -146,10 +158,10 @@ declare function FlyoMetric({ entity }: {
|
|
|
146
158
|
* ```
|
|
147
159
|
*/
|
|
148
160
|
declare function EditableSection({ block, children, className, as: Tag, }: {
|
|
149
|
-
block:
|
|
161
|
+
block: EditableBlock;
|
|
150
162
|
children: React.ReactNode;
|
|
151
163
|
className?: string;
|
|
152
164
|
as?: React.ElementType;
|
|
153
165
|
}): react_jsx_runtime.JSX.Element;
|
|
154
166
|
|
|
155
|
-
export { EditableSection, FlyoCdnLoader, FlyoClientWrapper, FlyoMetric, FlyoWysiwyg, type WysiwygJson, type WysiwygNode, editable, isProd };
|
|
167
|
+
export { type EditableBlock, EditableSection, FlyoCdnLoader, FlyoClientWrapper, FlyoMetric, FlyoWysiwyg, type WysiwygJson, type WysiwygNode, editable, isProd };
|
package/dist/client.d.ts
CHANGED
|
@@ -21,10 +21,22 @@ type WysiwygJson = WysiwygNode | WysiwygNode[] | {
|
|
|
21
21
|
type: 'doc';
|
|
22
22
|
content: WysiwygNode[];
|
|
23
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* The minimal block shape `editable()` needs to wire live-editing.
|
|
26
|
+
*
|
|
27
|
+
* `editable()` reads only `uid`, so it deliberately accepts more than the full
|
|
28
|
+
* {@link Block}. The per-block types generated from a project's OpenAPI schema
|
|
29
|
+
* (e.g. `BlockHero`) are NOT structurally assignable to `Block`: their
|
|
30
|
+
* `content`/`config`/`slots` carry an `_empty` marker that clashes with
|
|
31
|
+
* `Block`'s index signatures (`{ [key: string]: BlockSlotValue }`). Typing
|
|
32
|
+
* against the read-surface keeps both the generic `Block` and those generated
|
|
33
|
+
* subtypes assignable, so callers don't need `as unknown as Block` casts.
|
|
34
|
+
*/
|
|
35
|
+
type EditableBlock = Pick<Block, 'uid'>;
|
|
24
36
|
/**
|
|
25
37
|
* Helper function to get editable props
|
|
26
38
|
*/
|
|
27
|
-
declare function editable(block:
|
|
39
|
+
declare function editable(block: EditableBlock): {
|
|
28
40
|
'data-flyo-uid'?: string;
|
|
29
41
|
};
|
|
30
42
|
/**
|
|
@@ -146,10 +158,10 @@ declare function FlyoMetric({ entity }: {
|
|
|
146
158
|
* ```
|
|
147
159
|
*/
|
|
148
160
|
declare function EditableSection({ block, children, className, as: Tag, }: {
|
|
149
|
-
block:
|
|
161
|
+
block: EditableBlock;
|
|
150
162
|
children: React.ReactNode;
|
|
151
163
|
className?: string;
|
|
152
164
|
as?: React.ElementType;
|
|
153
165
|
}): react_jsx_runtime.JSX.Element;
|
|
154
166
|
|
|
155
|
-
export { EditableSection, FlyoCdnLoader, FlyoClientWrapper, FlyoMetric, FlyoWysiwyg, type WysiwygJson, type WysiwygNode, editable, isProd };
|
|
167
|
+
export { type EditableBlock, EditableSection, FlyoCdnLoader, FlyoClientWrapper, FlyoMetric, FlyoWysiwyg, type WysiwygJson, type WysiwygNode, editable, isProd };
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\nimport { highlightAndClick, wysiwyg, reload, scrollTo} from '@flyo/nitro-js-bridge';\nimport { Block, Entity } from \"@flyo/nitro-typescript\";\nimport type { ImageLoaderProps } from 'next/image';\n\nconst FLYO_CDN_HOST = 'storage.flyo.cloud';\n\n/**\n * Check if running in production environment\n */\nexport const isProd = process.env.NODE_ENV === 'production';\n\n/**\n * Type for WYSIWYG node structure\n */\nexport interface WysiwygNode {\n type: string;\n content?: WysiwygNode[];\n [key: string]: unknown;\n}\n\n/**\n * Type for WYSIWYG JSON that can be a node, array of nodes, or doc structure\n */\nexport type WysiwygJson = WysiwygNode | WysiwygNode[] | { type: 'doc'; content: WysiwygNode[] };\n\n/**\n * Helper function to get editable props\n */\nexport function editable(block: Block): { 'data-flyo-uid'?: string } {\n if (typeof block.uid === 'string' && block.uid.trim() !== '') {\n return { 'data-flyo-uid': block.uid };\n }\n return {};\n}\n\n/**\n * Internal client component that sets up live editing functionality\n */\nexport function FlyoClientWrapper({ \n children,\n}: { \n children: React.ReactNode;\n}) {\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n reload();\n \n scrollTo();\n\n const wireAll = () => {\n const elements = document.querySelectorAll('[data-flyo-uid]');\n elements.forEach((el) => {\n const uid = el.getAttribute('data-flyo-uid');\n if (uid && el instanceof HTMLElement) {\n highlightAndClick(uid, el);\n }\n });\n };\n\n wireAll();\n\n const observer = new MutationObserver((mutations) => {\n const hasRelevantChanges = mutations.some(mutation => \n Array.from(mutation.addedNodes).some(node => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element;\n return element.hasAttribute('data-flyo-uid') || \n element.querySelector('[data-flyo-uid]');\n }\n return false;\n })\n );\n\n if (hasRelevantChanges) {\n wireAll();\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n observer.disconnect();\n };\n }, []);\n\n return <>{children}</>;\n}\n\n/**\n * WYSIWYG component for rendering ProseMirror/TipTap JSON content\n * \n * Uses the `wysiwyg()` function from `@flyo/nitro-js-bridge` to convert\n * nodes to HTML. All consecutive non-custom nodes are joined into a single\n * HTML string so no extra wrapper `<div>` elements are added around each node.\n * \n * The component wraps all output in a single `<div>` with an optional\n * `className` (defaults to `\"wysiwyg\"`).\n * \n * @example\n * ```tsx\n * import { FlyoWysiwyg } from '@flyo/nitro-next/client';\n * import CustomImage from './CustomImage';\n * \n * export default function MyComponent({ block }) {\n * return (\n * <FlyoWysiwyg \n * json={block.content.json} \n * className=\"wysiwyg\"\n * components={{\n * image: CustomImage\n * }} \n * />\n * );\n * }\n * ```\n */\nexport function FlyoWysiwyg({\n json,\n className = 'wysiwyg',\n components = {},\n}: {\n json: WysiwygJson;\n className?: string;\n components?: Record<string, React.ComponentType<{ node: WysiwygNode }>>;\n}) {\n let nodes: WysiwygNode[] = [];\n\n if (json) {\n if (Array.isArray(json)) {\n nodes = json;\n } else if ('type' in json && json.type === 'doc' && Array.isArray(json.content)) {\n nodes = json.content;\n } else {\n nodes = [json as WysiwygNode];\n }\n }\n\n // If no custom components are provided, render all nodes as a single HTML block\n const hasCustomComponents = nodes.some((node) => components[node.type]);\n\n if (!hasCustomComponents) {\n const html = nodes.map((node) => wysiwyg(node)).join('');\n return <div className={className} dangerouslySetInnerHTML={{ __html: html }} />;\n }\n\n // When custom components are used, group consecutive non-custom nodes\n // into single HTML blocks to avoid extra wrapper elements.\n const groups: ({ type: 'custom'; component: React.ComponentType<{ node: WysiwygNode }>; node: WysiwygNode } | { type: 'html'; html: string })[] = [];\n\n for (const node of nodes) {\n const Component = components[node.type];\n if (Component) {\n groups.push({ type: 'custom', component: Component, node });\n } else {\n const html = wysiwyg(node);\n const last = groups[groups.length - 1];\n if (last && last.type === 'html') {\n last.html += html;\n } else {\n groups.push({ type: 'html', html });\n }\n }\n }\n\n return (\n <div className={className}>\n {groups.map((group, index) => {\n if (group.type === 'custom') {\n return <group.component key={index} node={group.node} />;\n }\n return <div key={index} dangerouslySetInnerHTML={{ __html: group.html }} />;\n })}\n </div>\n );\n}\n\n/**\n * Image loader for Flyo CDN that automatically handles image transformations.\n * Adds Flyo CDN host if not already present and applies width transformations.\n * \n * @param src - The image source URL (relative or absolute)\n * @param width - The desired width for the image\n * @returns Transformed image URL with Flyo CDN parameters\n * \n * @example\n * ```tsx\n * <Image\n * loader={FlyoCdnLoader}\n * src=\"me.png\"\n * alt=\"Picture\"\n * width={500}\n * height={500}\n * />\n * ```\n */\nexport function FlyoCdnLoader({ src, width }: ImageLoaderProps): string {\n let imageUrl = src;\n\n // If src doesn't contain the Flyo CDN host, prefix it\n if (!src.includes(FLYO_CDN_HOST)) {\n // Remove leading slash if present to avoid double slashes\n const cleanSrc = src.startsWith('/') ? src.slice(1) : src;\n imageUrl = `https://${FLYO_CDN_HOST}/${cleanSrc}`;\n }\n\n // Append Flyo CDN transformation parameters\n return `${imageUrl}/thumb/${width}xnull?format=webp`;\n}\n\n/**\n * FlyoMetric component for tracking entity metrics in production\n * \n * Automatically sends a metric tracking request to the Flyo API when:\n * - The environment is production (NODE_ENV === 'production')\n * - The entity has a metric API URL configured\n * \n * @param entity - The entity object containing entity_metric.api\n * \n * @example\n * ```tsx\n * import { FlyoMetric } from '@flyo/nitro-next/client';\n * \n * export default function BlogPost(props: RouteParams) {\n * return nitroEntityRoute(props, {\n * resolver,\n * render: (entity: Entity) => (\n * <>\n * <FlyoMetric entity={entity} />\n * <article>\n * <h1>{entity.entity?.entity_title}</h1>\n * </article>\n * </>\n * )\n * });\n * }\n * ```\n */\nexport function FlyoMetric({ entity }: { entity: Entity }) {\n useEffect(() => {\n // Only track metrics in production and if API URL is available\n if (isProd && entity?.entity?.entity_metric?.api) {\n fetch(entity.entity.entity_metric.api);\n }\n }, [entity]);\n\n // This component doesn't render anything\n return null;\n}\n\n/**\n * A thin client wrapper that applies `editable()` to a root element while\n * allowing server-rendered children (e.g. `NitroSlot`) to be passed in.\n *\n * In Next.js, a file marked `'use client'` turns all of its imports into\n * client modules, so you cannot import server-only components like\n * `NitroSlot` directly. The workaround is to keep the server part separate\n * and pass it into this client wrapper via `children`.\n *\n * @example\n * ```tsx\n * // components/HeroBanner.tsx (server component – no 'use client')\n * import { Block } from '@flyo/nitro-typescript';\n * import { NitroSlot } from '@flyo/nitro-next/server';\n * import { EditableSection } from '@flyo/nitro-next/client';\n *\n * export function HeroBanner({ block }: { block: Block }) {\n * return (\n * <EditableSection block={block} className=\"hero\">\n * <h2>{block?.content?.title}</h2>\n * <NitroSlot slot={block.slots?.content} />\n * </EditableSection>\n * );\n * }\n * ```\n */\nexport function EditableSection({\n block,\n children,\n className,\n as: Tag = 'section',\n}: {\n block: Block;\n children: React.ReactNode;\n className?: string;\n as?: React.ElementType;\n}) {\n return (\n <Tag {...editable(block)} className={className}>\n {children}\n </Tag>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\nimport { highlightAndClick, wysiwyg, reload, scrollTo} from '@flyo/nitro-js-bridge';\nimport { Block, Entity } from \"@flyo/nitro-typescript\";\nimport type { ImageLoaderProps } from 'next/image';\n\nconst FLYO_CDN_HOST = 'storage.flyo.cloud';\n\n/**\n * Check if running in production environment\n */\nexport const isProd = process.env.NODE_ENV === 'production';\n\n/**\n * Type for WYSIWYG node structure\n */\nexport interface WysiwygNode {\n type: string;\n content?: WysiwygNode[];\n [key: string]: unknown;\n}\n\n/**\n * Type for WYSIWYG JSON that can be a node, array of nodes, or doc structure\n */\nexport type WysiwygJson = WysiwygNode | WysiwygNode[] | { type: 'doc'; content: WysiwygNode[] };\n\n/**\n * The minimal block shape `editable()` needs to wire live-editing.\n *\n * `editable()` reads only `uid`, so it deliberately accepts more than the full\n * {@link Block}. The per-block types generated from a project's OpenAPI schema\n * (e.g. `BlockHero`) are NOT structurally assignable to `Block`: their\n * `content`/`config`/`slots` carry an `_empty` marker that clashes with\n * `Block`'s index signatures (`{ [key: string]: BlockSlotValue }`). Typing\n * against the read-surface keeps both the generic `Block` and those generated\n * subtypes assignable, so callers don't need `as unknown as Block` casts.\n */\nexport type EditableBlock = Pick<Block, 'uid'>;\n\n/**\n * Helper function to get editable props\n */\nexport function editable(block: EditableBlock): { 'data-flyo-uid'?: string } {\n if (typeof block.uid === 'string' && block.uid.trim() !== '') {\n return { 'data-flyo-uid': block.uid };\n }\n return {};\n}\n\n/**\n * Internal client component that sets up live editing functionality\n */\nexport function FlyoClientWrapper({ \n children,\n}: { \n children: React.ReactNode;\n}) {\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n reload();\n \n scrollTo();\n\n const wireAll = () => {\n const elements = document.querySelectorAll('[data-flyo-uid]');\n elements.forEach((el) => {\n const uid = el.getAttribute('data-flyo-uid');\n if (uid && el instanceof HTMLElement) {\n highlightAndClick(uid, el);\n }\n });\n };\n\n wireAll();\n\n const observer = new MutationObserver((mutations) => {\n const hasRelevantChanges = mutations.some(mutation => \n Array.from(mutation.addedNodes).some(node => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element;\n return element.hasAttribute('data-flyo-uid') || \n element.querySelector('[data-flyo-uid]');\n }\n return false;\n })\n );\n\n if (hasRelevantChanges) {\n wireAll();\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n observer.disconnect();\n };\n }, []);\n\n return <>{children}</>;\n}\n\n/**\n * WYSIWYG component for rendering ProseMirror/TipTap JSON content\n * \n * Uses the `wysiwyg()` function from `@flyo/nitro-js-bridge` to convert\n * nodes to HTML. All consecutive non-custom nodes are joined into a single\n * HTML string so no extra wrapper `<div>` elements are added around each node.\n * \n * The component wraps all output in a single `<div>` with an optional\n * `className` (defaults to `\"wysiwyg\"`).\n * \n * @example\n * ```tsx\n * import { FlyoWysiwyg } from '@flyo/nitro-next/client';\n * import CustomImage from './CustomImage';\n * \n * export default function MyComponent({ block }) {\n * return (\n * <FlyoWysiwyg \n * json={block.content.json} \n * className=\"wysiwyg\"\n * components={{\n * image: CustomImage\n * }} \n * />\n * );\n * }\n * ```\n */\nexport function FlyoWysiwyg({\n json,\n className = 'wysiwyg',\n components = {},\n}: {\n json: WysiwygJson;\n className?: string;\n components?: Record<string, React.ComponentType<{ node: WysiwygNode }>>;\n}) {\n let nodes: WysiwygNode[] = [];\n\n if (json) {\n if (Array.isArray(json)) {\n nodes = json;\n } else if ('type' in json && json.type === 'doc' && Array.isArray(json.content)) {\n nodes = json.content;\n } else {\n nodes = [json as WysiwygNode];\n }\n }\n\n // If no custom components are provided, render all nodes as a single HTML block\n const hasCustomComponents = nodes.some((node) => components[node.type]);\n\n if (!hasCustomComponents) {\n const html = nodes.map((node) => wysiwyg(node)).join('');\n return <div className={className} dangerouslySetInnerHTML={{ __html: html }} />;\n }\n\n // When custom components are used, group consecutive non-custom nodes\n // into single HTML blocks to avoid extra wrapper elements.\n const groups: ({ type: 'custom'; component: React.ComponentType<{ node: WysiwygNode }>; node: WysiwygNode } | { type: 'html'; html: string })[] = [];\n\n for (const node of nodes) {\n const Component = components[node.type];\n if (Component) {\n groups.push({ type: 'custom', component: Component, node });\n } else {\n const html = wysiwyg(node);\n const last = groups[groups.length - 1];\n if (last && last.type === 'html') {\n last.html += html;\n } else {\n groups.push({ type: 'html', html });\n }\n }\n }\n\n return (\n <div className={className}>\n {groups.map((group, index) => {\n if (group.type === 'custom') {\n return <group.component key={index} node={group.node} />;\n }\n return <div key={index} dangerouslySetInnerHTML={{ __html: group.html }} />;\n })}\n </div>\n );\n}\n\n/**\n * Image loader for Flyo CDN that automatically handles image transformations.\n * Adds Flyo CDN host if not already present and applies width transformations.\n * \n * @param src - The image source URL (relative or absolute)\n * @param width - The desired width for the image\n * @returns Transformed image URL with Flyo CDN parameters\n * \n * @example\n * ```tsx\n * <Image\n * loader={FlyoCdnLoader}\n * src=\"me.png\"\n * alt=\"Picture\"\n * width={500}\n * height={500}\n * />\n * ```\n */\nexport function FlyoCdnLoader({ src, width }: ImageLoaderProps): string {\n let imageUrl = src;\n\n // If src doesn't contain the Flyo CDN host, prefix it\n if (!src.includes(FLYO_CDN_HOST)) {\n // Remove leading slash if present to avoid double slashes\n const cleanSrc = src.startsWith('/') ? src.slice(1) : src;\n imageUrl = `https://${FLYO_CDN_HOST}/${cleanSrc}`;\n }\n\n // Append Flyo CDN transformation parameters\n return `${imageUrl}/thumb/${width}xnull?format=webp`;\n}\n\n/**\n * FlyoMetric component for tracking entity metrics in production\n * \n * Automatically sends a metric tracking request to the Flyo API when:\n * - The environment is production (NODE_ENV === 'production')\n * - The entity has a metric API URL configured\n * \n * @param entity - The entity object containing entity_metric.api\n * \n * @example\n * ```tsx\n * import { FlyoMetric } from '@flyo/nitro-next/client';\n * \n * export default function BlogPost(props: RouteParams) {\n * return nitroEntityRoute(props, {\n * resolver,\n * render: (entity: Entity) => (\n * <>\n * <FlyoMetric entity={entity} />\n * <article>\n * <h1>{entity.entity?.entity_title}</h1>\n * </article>\n * </>\n * )\n * });\n * }\n * ```\n */\nexport function FlyoMetric({ entity }: { entity: Entity }) {\n useEffect(() => {\n // Only track metrics in production and if API URL is available\n if (isProd && entity?.entity?.entity_metric?.api) {\n fetch(entity.entity.entity_metric.api);\n }\n }, [entity]);\n\n // This component doesn't render anything\n return null;\n}\n\n/**\n * A thin client wrapper that applies `editable()` to a root element while\n * allowing server-rendered children (e.g. `NitroSlot`) to be passed in.\n *\n * In Next.js, a file marked `'use client'` turns all of its imports into\n * client modules, so you cannot import server-only components like\n * `NitroSlot` directly. The workaround is to keep the server part separate\n * and pass it into this client wrapper via `children`.\n *\n * @example\n * ```tsx\n * // components/HeroBanner.tsx (server component – no 'use client')\n * import { Block } from '@flyo/nitro-typescript';\n * import { NitroSlot } from '@flyo/nitro-next/server';\n * import { EditableSection } from '@flyo/nitro-next/client';\n *\n * export function HeroBanner({ block }: { block: Block }) {\n * return (\n * <EditableSection block={block} className=\"hero\">\n * <h2>{block?.content?.title}</h2>\n * <NitroSlot slot={block.slots?.content} />\n * </EditableSection>\n * );\n * }\n * ```\n */\nexport function EditableSection({\n block,\n children,\n className,\n as: Tag = 'section',\n}: {\n block: EditableBlock;\n children: React.ReactNode;\n className?: string;\n as?: React.ElementType;\n}) {\n return (\n <Tag {...editable(block)} className={className}>\n {children}\n </Tag>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/client.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\nimport { highlightAndClick, wysiwyg, reload, scrollTo} from '@flyo/nitro-js-bridge';\nimport { Block, Entity } from \"@flyo/nitro-typescript\";\nimport type { ImageLoaderProps } from 'next/image';\n\nconst FLYO_CDN_HOST = 'storage.flyo.cloud';\n\n/**\n * Check if running in production environment\n */\nexport const isProd = process.env.NODE_ENV === 'production';\n\n/**\n * Type for WYSIWYG node structure\n */\nexport interface WysiwygNode {\n type: string;\n content?: WysiwygNode[];\n [key: string]: unknown;\n}\n\n/**\n * Type for WYSIWYG JSON that can be a node, array of nodes, or doc structure\n */\nexport type WysiwygJson = WysiwygNode | WysiwygNode[] | { type: 'doc'; content: WysiwygNode[] };\n\n/**\n * Helper function to get editable props\n */\nexport function editable(block: Block): { 'data-flyo-uid'?: string } {\n if (typeof block.uid === 'string' && block.uid.trim() !== '') {\n return { 'data-flyo-uid': block.uid };\n }\n return {};\n}\n\n/**\n * Internal client component that sets up live editing functionality\n */\nexport function FlyoClientWrapper({ \n children,\n}: { \n children: React.ReactNode;\n}) {\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n reload();\n \n scrollTo();\n\n const wireAll = () => {\n const elements = document.querySelectorAll('[data-flyo-uid]');\n elements.forEach((el) => {\n const uid = el.getAttribute('data-flyo-uid');\n if (uid && el instanceof HTMLElement) {\n highlightAndClick(uid, el);\n }\n });\n };\n\n wireAll();\n\n const observer = new MutationObserver((mutations) => {\n const hasRelevantChanges = mutations.some(mutation => \n Array.from(mutation.addedNodes).some(node => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element;\n return element.hasAttribute('data-flyo-uid') || \n element.querySelector('[data-flyo-uid]');\n }\n return false;\n })\n );\n\n if (hasRelevantChanges) {\n wireAll();\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n observer.disconnect();\n };\n }, []);\n\n return <>{children}</>;\n}\n\n/**\n * WYSIWYG component for rendering ProseMirror/TipTap JSON content\n * \n * Uses the `wysiwyg()` function from `@flyo/nitro-js-bridge` to convert\n * nodes to HTML. All consecutive non-custom nodes are joined into a single\n * HTML string so no extra wrapper `<div>` elements are added around each node.\n * \n * The component wraps all output in a single `<div>` with an optional\n * `className` (defaults to `\"wysiwyg\"`).\n * \n * @example\n * ```tsx\n * import { FlyoWysiwyg } from '@flyo/nitro-next/client';\n * import CustomImage from './CustomImage';\n * \n * export default function MyComponent({ block }) {\n * return (\n * <FlyoWysiwyg \n * json={block.content.json} \n * className=\"wysiwyg\"\n * components={{\n * image: CustomImage\n * }} \n * />\n * );\n * }\n * ```\n */\nexport function FlyoWysiwyg({\n json,\n className = 'wysiwyg',\n components = {},\n}: {\n json: WysiwygJson;\n className?: string;\n components?: Record<string, React.ComponentType<{ node: WysiwygNode }>>;\n}) {\n let nodes: WysiwygNode[] = [];\n\n if (json) {\n if (Array.isArray(json)) {\n nodes = json;\n } else if ('type' in json && json.type === 'doc' && Array.isArray(json.content)) {\n nodes = json.content;\n } else {\n nodes = [json as WysiwygNode];\n }\n }\n\n // If no custom components are provided, render all nodes as a single HTML block\n const hasCustomComponents = nodes.some((node) => components[node.type]);\n\n if (!hasCustomComponents) {\n const html = nodes.map((node) => wysiwyg(node)).join('');\n return <div className={className} dangerouslySetInnerHTML={{ __html: html }} />;\n }\n\n // When custom components are used, group consecutive non-custom nodes\n // into single HTML blocks to avoid extra wrapper elements.\n const groups: ({ type: 'custom'; component: React.ComponentType<{ node: WysiwygNode }>; node: WysiwygNode } | { type: 'html'; html: string })[] = [];\n\n for (const node of nodes) {\n const Component = components[node.type];\n if (Component) {\n groups.push({ type: 'custom', component: Component, node });\n } else {\n const html = wysiwyg(node);\n const last = groups[groups.length - 1];\n if (last && last.type === 'html') {\n last.html += html;\n } else {\n groups.push({ type: 'html', html });\n }\n }\n }\n\n return (\n <div className={className}>\n {groups.map((group, index) => {\n if (group.type === 'custom') {\n return <group.component key={index} node={group.node} />;\n }\n return <div key={index} dangerouslySetInnerHTML={{ __html: group.html }} />;\n })}\n </div>\n );\n}\n\n/**\n * Image loader for Flyo CDN that automatically handles image transformations.\n * Adds Flyo CDN host if not already present and applies width transformations.\n * \n * @param src - The image source URL (relative or absolute)\n * @param width - The desired width for the image\n * @returns Transformed image URL with Flyo CDN parameters\n * \n * @example\n * ```tsx\n * <Image\n * loader={FlyoCdnLoader}\n * src=\"me.png\"\n * alt=\"Picture\"\n * width={500}\n * height={500}\n * />\n * ```\n */\nexport function FlyoCdnLoader({ src, width }: ImageLoaderProps): string {\n let imageUrl = src;\n\n // If src doesn't contain the Flyo CDN host, prefix it\n if (!src.includes(FLYO_CDN_HOST)) {\n // Remove leading slash if present to avoid double slashes\n const cleanSrc = src.startsWith('/') ? src.slice(1) : src;\n imageUrl = `https://${FLYO_CDN_HOST}/${cleanSrc}`;\n }\n\n // Append Flyo CDN transformation parameters\n return `${imageUrl}/thumb/${width}xnull?format=webp`;\n}\n\n/**\n * FlyoMetric component for tracking entity metrics in production\n * \n * Automatically sends a metric tracking request to the Flyo API when:\n * - The environment is production (NODE_ENV === 'production')\n * - The entity has a metric API URL configured\n * \n * @param entity - The entity object containing entity_metric.api\n * \n * @example\n * ```tsx\n * import { FlyoMetric } from '@flyo/nitro-next/client';\n * \n * export default function BlogPost(props: RouteParams) {\n * return nitroEntityRoute(props, {\n * resolver,\n * render: (entity: Entity) => (\n * <>\n * <FlyoMetric entity={entity} />\n * <article>\n * <h1>{entity.entity?.entity_title}</h1>\n * </article>\n * </>\n * )\n * });\n * }\n * ```\n */\nexport function FlyoMetric({ entity }: { entity: Entity }) {\n useEffect(() => {\n // Only track metrics in production and if API URL is available\n if (isProd && entity?.entity?.entity_metric?.api) {\n fetch(entity.entity.entity_metric.api);\n }\n }, [entity]);\n\n // This component doesn't render anything\n return null;\n}\n\n/**\n * A thin client wrapper that applies `editable()` to a root element while\n * allowing server-rendered children (e.g. `NitroSlot`) to be passed in.\n *\n * In Next.js, a file marked `'use client'` turns all of its imports into\n * client modules, so you cannot import server-only components like\n * `NitroSlot` directly. The workaround is to keep the server part separate\n * and pass it into this client wrapper via `children`.\n *\n * @example\n * ```tsx\n * // components/HeroBanner.tsx (server component – no 'use client')\n * import { Block } from '@flyo/nitro-typescript';\n * import { NitroSlot } from '@flyo/nitro-next/server';\n * import { EditableSection } from '@flyo/nitro-next/client';\n *\n * export function HeroBanner({ block }: { block: Block }) {\n * return (\n * <EditableSection block={block} className=\"hero\">\n * <h2>{block?.content?.title}</h2>\n * <NitroSlot slot={block.slots?.content} />\n * </EditableSection>\n * );\n * }\n * ```\n */\nexport function EditableSection({\n block,\n children,\n className,\n as: Tag = 'section',\n}: {\n block: Block;\n children: React.ReactNode;\n className?: string;\n as?: React.ElementType;\n}) {\n return (\n <Tag {...editable(block)} className={className}>\n {children}\n </Tag>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect } from 'react';\nimport { highlightAndClick, wysiwyg, reload, scrollTo} from '@flyo/nitro-js-bridge';\nimport { Block, Entity } from \"@flyo/nitro-typescript\";\nimport type { ImageLoaderProps } from 'next/image';\n\nconst FLYO_CDN_HOST = 'storage.flyo.cloud';\n\n/**\n * Check if running in production environment\n */\nexport const isProd = process.env.NODE_ENV === 'production';\n\n/**\n * Type for WYSIWYG node structure\n */\nexport interface WysiwygNode {\n type: string;\n content?: WysiwygNode[];\n [key: string]: unknown;\n}\n\n/**\n * Type for WYSIWYG JSON that can be a node, array of nodes, or doc structure\n */\nexport type WysiwygJson = WysiwygNode | WysiwygNode[] | { type: 'doc'; content: WysiwygNode[] };\n\n/**\n * The minimal block shape `editable()` needs to wire live-editing.\n *\n * `editable()` reads only `uid`, so it deliberately accepts more than the full\n * {@link Block}. The per-block types generated from a project's OpenAPI schema\n * (e.g. `BlockHero`) are NOT structurally assignable to `Block`: their\n * `content`/`config`/`slots` carry an `_empty` marker that clashes with\n * `Block`'s index signatures (`{ [key: string]: BlockSlotValue }`). Typing\n * against the read-surface keeps both the generic `Block` and those generated\n * subtypes assignable, so callers don't need `as unknown as Block` casts.\n */\nexport type EditableBlock = Pick<Block, 'uid'>;\n\n/**\n * Helper function to get editable props\n */\nexport function editable(block: EditableBlock): { 'data-flyo-uid'?: string } {\n if (typeof block.uid === 'string' && block.uid.trim() !== '') {\n return { 'data-flyo-uid': block.uid };\n }\n return {};\n}\n\n/**\n * Internal client component that sets up live editing functionality\n */\nexport function FlyoClientWrapper({ \n children,\n}: { \n children: React.ReactNode;\n}) {\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n\n reload();\n \n scrollTo();\n\n const wireAll = () => {\n const elements = document.querySelectorAll('[data-flyo-uid]');\n elements.forEach((el) => {\n const uid = el.getAttribute('data-flyo-uid');\n if (uid && el instanceof HTMLElement) {\n highlightAndClick(uid, el);\n }\n });\n };\n\n wireAll();\n\n const observer = new MutationObserver((mutations) => {\n const hasRelevantChanges = mutations.some(mutation => \n Array.from(mutation.addedNodes).some(node => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element;\n return element.hasAttribute('data-flyo-uid') || \n element.querySelector('[data-flyo-uid]');\n }\n return false;\n })\n );\n\n if (hasRelevantChanges) {\n wireAll();\n }\n });\n\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n observer.disconnect();\n };\n }, []);\n\n return <>{children}</>;\n}\n\n/**\n * WYSIWYG component for rendering ProseMirror/TipTap JSON content\n * \n * Uses the `wysiwyg()` function from `@flyo/nitro-js-bridge` to convert\n * nodes to HTML. All consecutive non-custom nodes are joined into a single\n * HTML string so no extra wrapper `<div>` elements are added around each node.\n * \n * The component wraps all output in a single `<div>` with an optional\n * `className` (defaults to `\"wysiwyg\"`).\n * \n * @example\n * ```tsx\n * import { FlyoWysiwyg } from '@flyo/nitro-next/client';\n * import CustomImage from './CustomImage';\n * \n * export default function MyComponent({ block }) {\n * return (\n * <FlyoWysiwyg \n * json={block.content.json} \n * className=\"wysiwyg\"\n * components={{\n * image: CustomImage\n * }} \n * />\n * );\n * }\n * ```\n */\nexport function FlyoWysiwyg({\n json,\n className = 'wysiwyg',\n components = {},\n}: {\n json: WysiwygJson;\n className?: string;\n components?: Record<string, React.ComponentType<{ node: WysiwygNode }>>;\n}) {\n let nodes: WysiwygNode[] = [];\n\n if (json) {\n if (Array.isArray(json)) {\n nodes = json;\n } else if ('type' in json && json.type === 'doc' && Array.isArray(json.content)) {\n nodes = json.content;\n } else {\n nodes = [json as WysiwygNode];\n }\n }\n\n // If no custom components are provided, render all nodes as a single HTML block\n const hasCustomComponents = nodes.some((node) => components[node.type]);\n\n if (!hasCustomComponents) {\n const html = nodes.map((node) => wysiwyg(node)).join('');\n return <div className={className} dangerouslySetInnerHTML={{ __html: html }} />;\n }\n\n // When custom components are used, group consecutive non-custom nodes\n // into single HTML blocks to avoid extra wrapper elements.\n const groups: ({ type: 'custom'; component: React.ComponentType<{ node: WysiwygNode }>; node: WysiwygNode } | { type: 'html'; html: string })[] = [];\n\n for (const node of nodes) {\n const Component = components[node.type];\n if (Component) {\n groups.push({ type: 'custom', component: Component, node });\n } else {\n const html = wysiwyg(node);\n const last = groups[groups.length - 1];\n if (last && last.type === 'html') {\n last.html += html;\n } else {\n groups.push({ type: 'html', html });\n }\n }\n }\n\n return (\n <div className={className}>\n {groups.map((group, index) => {\n if (group.type === 'custom') {\n return <group.component key={index} node={group.node} />;\n }\n return <div key={index} dangerouslySetInnerHTML={{ __html: group.html }} />;\n })}\n </div>\n );\n}\n\n/**\n * Image loader for Flyo CDN that automatically handles image transformations.\n * Adds Flyo CDN host if not already present and applies width transformations.\n * \n * @param src - The image source URL (relative or absolute)\n * @param width - The desired width for the image\n * @returns Transformed image URL with Flyo CDN parameters\n * \n * @example\n * ```tsx\n * <Image\n * loader={FlyoCdnLoader}\n * src=\"me.png\"\n * alt=\"Picture\"\n * width={500}\n * height={500}\n * />\n * ```\n */\nexport function FlyoCdnLoader({ src, width }: ImageLoaderProps): string {\n let imageUrl = src;\n\n // If src doesn't contain the Flyo CDN host, prefix it\n if (!src.includes(FLYO_CDN_HOST)) {\n // Remove leading slash if present to avoid double slashes\n const cleanSrc = src.startsWith('/') ? src.slice(1) : src;\n imageUrl = `https://${FLYO_CDN_HOST}/${cleanSrc}`;\n }\n\n // Append Flyo CDN transformation parameters\n return `${imageUrl}/thumb/${width}xnull?format=webp`;\n}\n\n/**\n * FlyoMetric component for tracking entity metrics in production\n * \n * Automatically sends a metric tracking request to the Flyo API when:\n * - The environment is production (NODE_ENV === 'production')\n * - The entity has a metric API URL configured\n * \n * @param entity - The entity object containing entity_metric.api\n * \n * @example\n * ```tsx\n * import { FlyoMetric } from '@flyo/nitro-next/client';\n * \n * export default function BlogPost(props: RouteParams) {\n * return nitroEntityRoute(props, {\n * resolver,\n * render: (entity: Entity) => (\n * <>\n * <FlyoMetric entity={entity} />\n * <article>\n * <h1>{entity.entity?.entity_title}</h1>\n * </article>\n * </>\n * )\n * });\n * }\n * ```\n */\nexport function FlyoMetric({ entity }: { entity: Entity }) {\n useEffect(() => {\n // Only track metrics in production and if API URL is available\n if (isProd && entity?.entity?.entity_metric?.api) {\n fetch(entity.entity.entity_metric.api);\n }\n }, [entity]);\n\n // This component doesn't render anything\n return null;\n}\n\n/**\n * A thin client wrapper that applies `editable()` to a root element while\n * allowing server-rendered children (e.g. `NitroSlot`) to be passed in.\n *\n * In Next.js, a file marked `'use client'` turns all of its imports into\n * client modules, so you cannot import server-only components like\n * `NitroSlot` directly. The workaround is to keep the server part separate\n * and pass it into this client wrapper via `children`.\n *\n * @example\n * ```tsx\n * // components/HeroBanner.tsx (server component – no 'use client')\n * import { Block } from '@flyo/nitro-typescript';\n * import { NitroSlot } from '@flyo/nitro-next/server';\n * import { EditableSection } from '@flyo/nitro-next/client';\n *\n * export function HeroBanner({ block }: { block: Block }) {\n * return (\n * <EditableSection block={block} className=\"hero\">\n * <h2>{block?.content?.title}</h2>\n * <NitroSlot slot={block.slots?.content} />\n * </EditableSection>\n * );\n * }\n * ```\n */\nexport function EditableSection({\n block,\n children,\n className,\n as: Tag = 'section',\n}: {\n block: EditableBlock;\n children: React.ReactNode;\n className?: string;\n as?: React.ElementType;\n}) {\n return (\n <Tag {...editable(block)} className={className}>\n {children}\n </Tag>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/proxy.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
|
-
import {
|
|
2
|
+
import { FlyoInstance } from './server.mjs';
|
|
3
3
|
import 'react';
|
|
4
4
|
import 'react/jsx-runtime';
|
|
5
5
|
import 'next';
|
|
@@ -9,37 +9,25 @@ import '@flyo/nitro-typescript';
|
|
|
9
9
|
* Nitro Next.js Proxy Factory
|
|
10
10
|
*
|
|
11
11
|
* Creates a Next.js middleware that handles cache control headers.
|
|
12
|
-
* Uses cache TTL values from the
|
|
12
|
+
* Uses cache TTL values from the Flyo instance's configuration state.
|
|
13
13
|
*
|
|
14
|
-
* @param
|
|
14
|
+
* @param flyo The Flyo instance returned by initNitro()
|
|
15
15
|
* @returns Next.js middleware function
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
18
|
* ```ts
|
|
19
19
|
* // src/middleware.ts
|
|
20
20
|
* import { createProxy } from '@flyo/nitro-next/proxy';
|
|
21
|
-
* import {
|
|
21
|
+
* import { flyo } from './flyo.config';
|
|
22
22
|
*
|
|
23
|
-
* export default createProxy(
|
|
23
|
+
* export default createProxy(flyo);
|
|
24
24
|
*
|
|
25
25
|
* export const config = {
|
|
26
26
|
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
27
27
|
* };
|
|
28
28
|
* ```
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```ts
|
|
32
|
-
* // flyo.config.tsx
|
|
33
|
-
* export const flyoConfig = initNitro({
|
|
34
|
-
* accessToken: process.env.FLYO_ACCESS_TOKEN!,
|
|
35
|
-
* baseUrl: process.env.SITE_URL || 'http://localhost:3000',
|
|
36
|
-
* liveEdit: process.env.FLYO_LIVE_EDIT === 'true',
|
|
37
|
-
* serverCacheTtl: 1200, // 20 minutes
|
|
38
|
-
* clientCacheTtl: 900, // 15 minutes
|
|
39
|
-
* });
|
|
40
|
-
* ```
|
|
41
29
|
*/
|
|
42
|
-
declare function createProxy(
|
|
30
|
+
declare function createProxy(flyo: FlyoInstance): () => NextResponse<unknown>;
|
|
43
31
|
/**
|
|
44
32
|
* Proxy matcher configuration
|
|
45
33
|
* Applies to all routes except Next.js internal routes
|
package/dist/proxy.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
|
-
import {
|
|
2
|
+
import { FlyoInstance } from './server.js';
|
|
3
3
|
import 'react';
|
|
4
4
|
import 'react/jsx-runtime';
|
|
5
5
|
import 'next';
|
|
@@ -9,37 +9,25 @@ import '@flyo/nitro-typescript';
|
|
|
9
9
|
* Nitro Next.js Proxy Factory
|
|
10
10
|
*
|
|
11
11
|
* Creates a Next.js middleware that handles cache control headers.
|
|
12
|
-
* Uses cache TTL values from the
|
|
12
|
+
* Uses cache TTL values from the Flyo instance's configuration state.
|
|
13
13
|
*
|
|
14
|
-
* @param
|
|
14
|
+
* @param flyo The Flyo instance returned by initNitro()
|
|
15
15
|
* @returns Next.js middleware function
|
|
16
16
|
*
|
|
17
17
|
* @example
|
|
18
18
|
* ```ts
|
|
19
19
|
* // src/middleware.ts
|
|
20
20
|
* import { createProxy } from '@flyo/nitro-next/proxy';
|
|
21
|
-
* import {
|
|
21
|
+
* import { flyo } from './flyo.config';
|
|
22
22
|
*
|
|
23
|
-
* export default createProxy(
|
|
23
|
+
* export default createProxy(flyo);
|
|
24
24
|
*
|
|
25
25
|
* export const config = {
|
|
26
26
|
* matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
27
27
|
* };
|
|
28
28
|
* ```
|
|
29
|
-
*
|
|
30
|
-
* @example
|
|
31
|
-
* ```ts
|
|
32
|
-
* // flyo.config.tsx
|
|
33
|
-
* export const flyoConfig = initNitro({
|
|
34
|
-
* accessToken: process.env.FLYO_ACCESS_TOKEN!,
|
|
35
|
-
* baseUrl: process.env.SITE_URL || 'http://localhost:3000',
|
|
36
|
-
* liveEdit: process.env.FLYO_LIVE_EDIT === 'true',
|
|
37
|
-
* serverCacheTtl: 1200, // 20 minutes
|
|
38
|
-
* clientCacheTtl: 900, // 15 minutes
|
|
39
|
-
* });
|
|
40
|
-
* ```
|
|
41
29
|
*/
|
|
42
|
-
declare function createProxy(
|
|
30
|
+
declare function createProxy(flyo: FlyoInstance): () => NextResponse<unknown>;
|
|
43
31
|
/**
|
|
44
32
|
* Proxy matcher configuration
|
|
45
33
|
* Applies to all routes except Next.js internal routes
|
package/dist/proxy.js
CHANGED
|
@@ -25,7 +25,8 @@ __export(proxy_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(proxy_exports);
|
|
27
27
|
var import_server = require("next/server");
|
|
28
|
-
function createProxy(
|
|
28
|
+
function createProxy(flyo) {
|
|
29
|
+
const { state } = flyo;
|
|
29
30
|
return function proxy() {
|
|
30
31
|
const res = import_server.NextResponse.next();
|
|
31
32
|
if (state.liveEdit) {
|
package/dist/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/proxy.ts"],"sourcesContent":["import { NextResponse } from 'next/server';\nimport type {
|
|
1
|
+
{"version":3,"sources":["../src/proxy.ts"],"sourcesContent":["import { NextResponse } from 'next/server';\nimport type { FlyoInstance } from './server';\n\n/**\n * Nitro Next.js Proxy Factory\n *\n * Creates a Next.js middleware that handles cache control headers.\n * Uses cache TTL values from the Flyo instance's configuration state.\n *\n * @param flyo The Flyo instance returned by initNitro()\n * @returns Next.js middleware function\n *\n * @example\n * ```ts\n * // src/middleware.ts\n * import { createProxy } from '@flyo/nitro-next/proxy';\n * import { flyo } from './flyo.config';\n *\n * export default createProxy(flyo);\n *\n * export const config = {\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\n * };\n * ```\n */\nexport function createProxy(flyo: FlyoInstance) {\n const { state } = flyo;\n\n return function proxy() {\n const res = NextResponse.next();\n\n if (state.liveEdit) {\n // Development or live edit mode - no caching\n res.headers.set('Vercel-CDN-Cache-Control', 'no-store');\n res.headers.set('CDN-Cache-Control', 'no-store');\n res.headers.set('Cache-Control', 'no-store');\n } else {\n // Production with caching enabled\n const cdn = state.serverCacheTtl > 0 ? `max-age=${state.serverCacheTtl}` : 'no-store';\n res.headers.set('Vercel-CDN-Cache-Control', cdn);\n res.headers.set('CDN-Cache-Control', cdn);\n\n if (state.clientCacheTtl > 0) {\n res.headers.set('Cache-Control', `max-age=${state.clientCacheTtl}`);\n } else {\n res.headers.set('Cache-Control', 'no-store');\n }\n }\n\n return res;\n };\n}\n\n\n/**\n * Proxy matcher configuration\n * Applies to all routes except Next.js internal routes\n */\nexport const config = {\n matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA6B;AAyBtB,SAAS,YAAY,MAAoB;AAC9C,QAAM,EAAE,MAAM,IAAI;AAElB,SAAO,SAAS,QAAQ;AACtB,UAAM,MAAM,2BAAa,KAAK;AAE9B,QAAI,MAAM,UAAU;AAElB,UAAI,QAAQ,IAAI,4BAA4B,UAAU;AACtD,UAAI,QAAQ,IAAI,qBAAqB,UAAU;AAC/C,UAAI,QAAQ,IAAI,iBAAiB,UAAU;AAAA,IAC7C,OAAO;AAEL,YAAM,MAAM,MAAM,iBAAiB,IAAI,WAAW,MAAM,cAAc,KAAK;AAC3E,UAAI,QAAQ,IAAI,4BAA4B,GAAG;AAC/C,UAAI,QAAQ,IAAI,qBAAqB,GAAG;AAExC,UAAI,MAAM,iBAAiB,GAAG;AAC5B,YAAI,QAAQ,IAAI,iBAAiB,WAAW,MAAM,cAAc,EAAE;AAAA,MACpE,OAAO;AACL,YAAI,QAAQ,IAAI,iBAAiB,UAAU;AAAA,MAC7C;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAOO,IAAM,SAAS;AAAA,EACpB,SAAS,CAAC,+CAA+C;AAC3D;","names":[]}
|
package/dist/proxy.mjs
CHANGED
package/dist/proxy.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/proxy.ts"],"sourcesContent":["import { NextResponse } from 'next/server';\nimport type {
|
|
1
|
+
{"version":3,"sources":["../src/proxy.ts"],"sourcesContent":["import { NextResponse } from 'next/server';\nimport type { FlyoInstance } from './server';\n\n/**\n * Nitro Next.js Proxy Factory\n *\n * Creates a Next.js middleware that handles cache control headers.\n * Uses cache TTL values from the Flyo instance's configuration state.\n *\n * @param flyo The Flyo instance returned by initNitro()\n * @returns Next.js middleware function\n *\n * @example\n * ```ts\n * // src/middleware.ts\n * import { createProxy } from '@flyo/nitro-next/proxy';\n * import { flyo } from './flyo.config';\n *\n * export default createProxy(flyo);\n *\n * export const config = {\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\n * };\n * ```\n */\nexport function createProxy(flyo: FlyoInstance) {\n const { state } = flyo;\n\n return function proxy() {\n const res = NextResponse.next();\n\n if (state.liveEdit) {\n // Development or live edit mode - no caching\n res.headers.set('Vercel-CDN-Cache-Control', 'no-store');\n res.headers.set('CDN-Cache-Control', 'no-store');\n res.headers.set('Cache-Control', 'no-store');\n } else {\n // Production with caching enabled\n const cdn = state.serverCacheTtl > 0 ? `max-age=${state.serverCacheTtl}` : 'no-store';\n res.headers.set('Vercel-CDN-Cache-Control', cdn);\n res.headers.set('CDN-Cache-Control', cdn);\n\n if (state.clientCacheTtl > 0) {\n res.headers.set('Cache-Control', `max-age=${state.clientCacheTtl}`);\n } else {\n res.headers.set('Cache-Control', 'no-store');\n }\n }\n\n return res;\n };\n}\n\n\n/**\n * Proxy matcher configuration\n * Applies to all routes except Next.js internal routes\n */\nexport const config = {\n matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\n};\n"],"mappings":";AAAA,SAAS,oBAAoB;AAyBtB,SAAS,YAAY,MAAoB;AAC9C,QAAM,EAAE,MAAM,IAAI;AAElB,SAAO,SAAS,QAAQ;AACtB,UAAM,MAAM,aAAa,KAAK;AAE9B,QAAI,MAAM,UAAU;AAElB,UAAI,QAAQ,IAAI,4BAA4B,UAAU;AACtD,UAAI,QAAQ,IAAI,qBAAqB,UAAU;AAC/C,UAAI,QAAQ,IAAI,iBAAiB,UAAU;AAAA,IAC7C,OAAO;AAEL,YAAM,MAAM,MAAM,iBAAiB,IAAI,WAAW,MAAM,cAAc,KAAK;AAC3E,UAAI,QAAQ,IAAI,4BAA4B,GAAG;AAC/C,UAAI,QAAQ,IAAI,qBAAqB,GAAG;AAExC,UAAI,MAAM,iBAAiB,GAAG;AAC5B,YAAI,QAAQ,IAAI,iBAAiB,WAAW,MAAM,cAAc,EAAE;AAAA,MACpE,OAAO;AACL,YAAI,QAAQ,IAAI,iBAAiB,UAAU;AAAA,MAC7C;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;AAOO,IAAM,SAAS;AAAA,EACpB,SAAS,CAAC,+CAA+C;AAC3D;","names":[]}
|