@coherent.js/integrations 1.0.0-rc.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/LICENSE +21 -0
- package/README.md +37 -0
- package/package.json +99 -0
- package/src/astro/index.js +88 -0
- package/src/express/coherent-express.js +160 -0
- package/src/express/index.js +36 -0
- package/src/fastify/coherent-fastify.js +125 -0
- package/src/fastify/index.js +13 -0
- package/src/koa/coherent-koa.js +124 -0
- package/src/koa/index.js +19 -0
- package/src/nextjs/coherent-nextjs.js +230 -0
- package/src/nextjs/index.js +21 -0
- package/src/remix/index.js +86 -0
- package/src/sveltekit/index.js +121 -0
- package/types/express/index.d.ts +134 -0
- package/types/fastify/index.d.ts +121 -0
- package/types/nextjs/index.d.ts +114 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Thomas Drouvin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @coherent.js/integrations
|
|
2
|
+
|
|
3
|
+
Framework integration adapters for Coherent.js — bridges between your chosen HTTP/SSG framework and the Coherent.js rendering engine.
|
|
4
|
+
|
|
5
|
+
## Subpath exports
|
|
6
|
+
|
|
7
|
+
- `@coherent.js/integrations/express` — Express.js adapter
|
|
8
|
+
- `@coherent.js/integrations/fastify` — Fastify adapter (added in 1.0.0)
|
|
9
|
+
- `@coherent.js/integrations/koa` — Koa adapter (added in 1.0.0)
|
|
10
|
+
- `@coherent.js/integrations/nextjs` — Next.js adapter (added in 1.0.0)
|
|
11
|
+
- `@coherent.js/integrations/astro` — Astro adapter (added in 1.0.0)
|
|
12
|
+
- `@coherent.js/integrations/remix` — Remix adapter (added in 1.0.0)
|
|
13
|
+
- `@coherent.js/integrations/sveltekit` — SvelteKit adapter (added in 1.0.0)
|
|
14
|
+
|
|
15
|
+
## Migration from pre-1.0
|
|
16
|
+
|
|
17
|
+
Each framework previously shipped as its own package (`@coherent.js/express`, etc.). Migrate by changing import paths:
|
|
18
|
+
|
|
19
|
+
```diff
|
|
20
|
+
- import { setupCoherent } from '@coherent.js/express';
|
|
21
|
+
+ import { setupCoherent } from '@coherent.js/integrations/express';
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Public API is unchanged — only the import path moves.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm add @coherent.js/core @coherent.js/integrations
|
|
30
|
+
pnpm add express # or fastify / koa / next / etc. — only the ones you use
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Framework peer dependencies are declared optional, so consumers only install the framework(s) they actually use.
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
MIT
|
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@coherent.js/integrations",
|
|
3
|
+
"version": "1.0.0-rc.1",
|
|
4
|
+
"description": "Framework integration adapters for Coherent.js: Express, Fastify, Koa, Next.js, Astro, Remix, SvelteKit.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./express": {
|
|
8
|
+
"types": "./types/express/index.d.ts",
|
|
9
|
+
"import": "./src/express/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./fastify": {
|
|
12
|
+
"types": "./types/fastify/index.d.ts",
|
|
13
|
+
"import": "./src/fastify/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./koa": {
|
|
16
|
+
"import": "./src/koa/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./nextjs": {
|
|
19
|
+
"types": "./types/nextjs/index.d.ts",
|
|
20
|
+
"import": "./src/nextjs/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./astro": {
|
|
23
|
+
"import": "./src/astro/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./remix": {
|
|
26
|
+
"import": "./src/remix/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./sveltekit": {
|
|
29
|
+
"import": "./src/sveltekit/index.js"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"src/",
|
|
34
|
+
"types/",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=22.0.0"
|
|
40
|
+
},
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/Tomdrouv1/coherent.js.git"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/Tomdrouv1/coherent.js",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/Tomdrouv1/coherent.js/issues"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@remix-run/server-runtime": ">=2.0.0",
|
|
52
|
+
"@sveltejs/kit": ">=2.0.0",
|
|
53
|
+
"astro": ">=4.0.0",
|
|
54
|
+
"express": ">=4.18.0 < 6.0.0",
|
|
55
|
+
"fastify": ">=4.0.0 < 6.0.0",
|
|
56
|
+
"koa": ">=2.13.0 < 4.0.0",
|
|
57
|
+
"next": ">=13.0.0",
|
|
58
|
+
"react": ">=18.0.0",
|
|
59
|
+
"@coherent.js/core": "1.0.0-rc.1"
|
|
60
|
+
},
|
|
61
|
+
"peerDependenciesMeta": {
|
|
62
|
+
"@remix-run/server-runtime": {
|
|
63
|
+
"optional": true
|
|
64
|
+
},
|
|
65
|
+
"@sveltejs/kit": {
|
|
66
|
+
"optional": true
|
|
67
|
+
},
|
|
68
|
+
"astro": {
|
|
69
|
+
"optional": true
|
|
70
|
+
},
|
|
71
|
+
"express": {
|
|
72
|
+
"optional": true
|
|
73
|
+
},
|
|
74
|
+
"fastify": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"koa": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"next": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"react": {
|
|
84
|
+
"optional": true
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"publishConfig": {
|
|
88
|
+
"access": "public",
|
|
89
|
+
"registry": "https://registry.npmjs.org/"
|
|
90
|
+
},
|
|
91
|
+
"sideEffects": false,
|
|
92
|
+
"scripts": {
|
|
93
|
+
"build": "node build.mjs",
|
|
94
|
+
"clean": "rm -rf dist/",
|
|
95
|
+
"test": "vitest run",
|
|
96
|
+
"test:watch": "vitest",
|
|
97
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/astro/index.js
|
|
2
|
+
//
|
|
3
|
+
// Public entry point for @coherent.js/integrations/astro. Provides
|
|
4
|
+
// server-side rendering of Coherent.js components within Astro projects.
|
|
5
|
+
//
|
|
6
|
+
// Astro must be installed as a peer dependency to use this integration.
|
|
7
|
+
//
|
|
8
|
+
// Usage:
|
|
9
|
+
// import { createAstroIntegration } from '@coherent.js/integrations/astro';
|
|
10
|
+
|
|
11
|
+
import { render } from '@coherent.js/core';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create an Astro integration for Coherent.js
|
|
15
|
+
*
|
|
16
|
+
* @param {Object} [options] - Integration options
|
|
17
|
+
* @param {boolean} [options.hydrate] - Enable client-side hydration
|
|
18
|
+
* @param {string} [options.hydrateScript] - Custom hydration script path
|
|
19
|
+
* @returns {Object} Astro integration configuration
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // astro.config.mjs
|
|
23
|
+
* import { createAstroIntegration } from '@coherent.js/integrations/astro';
|
|
24
|
+
* export default { integrations: [createAstroIntegration()] };
|
|
25
|
+
*/
|
|
26
|
+
export function createAstroIntegration(options = {}) {
|
|
27
|
+
return {
|
|
28
|
+
name: 'coherent',
|
|
29
|
+
hooks: {
|
|
30
|
+
'astro:config:setup': ({ addRenderer, updateConfig }) => {
|
|
31
|
+
addRenderer({
|
|
32
|
+
name: '@coherent.js/integrations/astro',
|
|
33
|
+
serverEntrypoint: '@coherent.js/integrations/astro',
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (options.hydrate) {
|
|
37
|
+
updateConfig({
|
|
38
|
+
vite: {
|
|
39
|
+
optimizeDeps: {
|
|
40
|
+
include: ['@coherent.js/client']
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
'astro:build:done': ({ logger }) => {
|
|
47
|
+
logger?.info('Coherent.js components built successfully');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Render a Coherent.js component to HTML string
|
|
55
|
+
*
|
|
56
|
+
* @param {Function|Object} Component - Coherent.js component
|
|
57
|
+
* @param {Object} [props] - Component props
|
|
58
|
+
* @param {Object} [renderOptions] - Rendering options
|
|
59
|
+
* @returns {string} Rendered HTML
|
|
60
|
+
*/
|
|
61
|
+
export function renderComponent(Component, props = {}, renderOptions = {}) {
|
|
62
|
+
const componentDef = typeof Component === 'function'
|
|
63
|
+
? Component(props)
|
|
64
|
+
: Component;
|
|
65
|
+
|
|
66
|
+
return render(componentDef, renderOptions);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Create a server-side component renderer for Astro
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} [options] - Renderer options
|
|
73
|
+
* @returns {Object} Astro renderer configuration
|
|
74
|
+
*/
|
|
75
|
+
export function createRenderer(options = {}) {
|
|
76
|
+
return {
|
|
77
|
+
name: '@coherent.js/astro-renderer',
|
|
78
|
+
check(Component) {
|
|
79
|
+
// Check if this looks like a Coherent.js component (function or plain object with tag keys)
|
|
80
|
+
return typeof Component === 'function' ||
|
|
81
|
+
(typeof Component === 'object' && Component !== null && !Array.isArray(Component));
|
|
82
|
+
},
|
|
83
|
+
renderToStaticMarkup(Component, props) {
|
|
84
|
+
const html = renderComponent(Component, props, options);
|
|
85
|
+
return { html };
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Express.js integration for Coherent.js
|
|
3
|
+
* Provides middleware and utilities for using Coherent.js with Express
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
render,
|
|
8
|
+
importPeerDependency,
|
|
9
|
+
renderWithTemplate,
|
|
10
|
+
renderComponentFactory,
|
|
11
|
+
isCoherentComponent
|
|
12
|
+
} from '@coherent.js/core';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Coherent.js Express middleware
|
|
16
|
+
* Automatically renders Coherent.js components and handles errors
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} options - Configuration options
|
|
19
|
+
* @param {boolean} options.enablePerformanceMonitoring - Enable performance monitoring
|
|
20
|
+
* @param {string} options.template - HTML template with {{content}} placeholder
|
|
21
|
+
* @returns {Function} Express middleware function
|
|
22
|
+
*/
|
|
23
|
+
export function coherentMiddleware(options = {}) {
|
|
24
|
+
const {
|
|
25
|
+
enablePerformanceMonitoring = false,
|
|
26
|
+
template = '<!DOCTYPE html>\n{{content}}'
|
|
27
|
+
} = options;
|
|
28
|
+
|
|
29
|
+
return (req, res, next) => {
|
|
30
|
+
// Store original send method
|
|
31
|
+
const originalSend = res.send;
|
|
32
|
+
|
|
33
|
+
// Override send method to handle Coherent.js objects
|
|
34
|
+
res.send = function(data) {
|
|
35
|
+
// If data is a Coherent.js object (plain object with a single key), render it
|
|
36
|
+
if (isCoherentComponent(data)) {
|
|
37
|
+
try {
|
|
38
|
+
// Use shared rendering utility
|
|
39
|
+
const finalHtml = renderWithTemplate(data, { enablePerformanceMonitoring, template });
|
|
40
|
+
|
|
41
|
+
// Set content type and send HTML
|
|
42
|
+
res.set('Content-Type', 'text/html');
|
|
43
|
+
return originalSend.call(this, finalHtml);
|
|
44
|
+
} catch (_error) {
|
|
45
|
+
console.error('Coherent.js rendering _error:', _error);
|
|
46
|
+
return next(_error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// For non-Coherent.js data, use original send method
|
|
51
|
+
return originalSend.call(this, data);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
next();
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create an Express route handler for Coherent.js components
|
|
60
|
+
*
|
|
61
|
+
* @param {Function} componentFactory - Function that returns a Coherent.js component
|
|
62
|
+
* @param {Object} options - Handler options
|
|
63
|
+
* @returns {Function} Express route handler
|
|
64
|
+
*/
|
|
65
|
+
export function createCoherentHandler(componentFactory, options = {}) {
|
|
66
|
+
return async (req, res, next) => {
|
|
67
|
+
try {
|
|
68
|
+
// Use shared rendering utility
|
|
69
|
+
const finalHtml = await renderComponentFactory(
|
|
70
|
+
componentFactory,
|
|
71
|
+
[req, res, next],
|
|
72
|
+
options
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Send HTML response
|
|
76
|
+
res.set('Content-Type', 'text/html');
|
|
77
|
+
res.send(finalHtml);
|
|
78
|
+
} catch (_error) {
|
|
79
|
+
console.error('Coherent.js handler _error:', _error);
|
|
80
|
+
next(_error);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Enhanced Express engine for Coherent.js views
|
|
87
|
+
*
|
|
88
|
+
* @param {string} filePath - Path to view file (not used in Coherent.js)
|
|
89
|
+
* @param {Object} options - View options containing Coherent.js component
|
|
90
|
+
* @param {Function} callback - Callback function
|
|
91
|
+
*/
|
|
92
|
+
export function enhancedExpressEngine(filePath, options, callback) {
|
|
93
|
+
try {
|
|
94
|
+
// Render Coherent.js component from options
|
|
95
|
+
const html = render(options);
|
|
96
|
+
callback(null, html);
|
|
97
|
+
} catch (_error) {
|
|
98
|
+
callback(_error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Setup Coherent.js with Express app
|
|
104
|
+
*
|
|
105
|
+
* @param {Object} app - Express app instance
|
|
106
|
+
* @param {Object} options - Setup options
|
|
107
|
+
*/
|
|
108
|
+
export function setupCoherent(app, options = {}) {
|
|
109
|
+
const {
|
|
110
|
+
useMiddleware = true,
|
|
111
|
+
useEngine = true,
|
|
112
|
+
engineName = 'coherent',
|
|
113
|
+
enablePerformanceMonitoring = false
|
|
114
|
+
} = options;
|
|
115
|
+
|
|
116
|
+
// Register enhanced engine
|
|
117
|
+
if (useEngine) {
|
|
118
|
+
app.engine(engineName, enhancedExpressEngine);
|
|
119
|
+
app.set('view engine', engineName);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Use middleware for automatic rendering
|
|
123
|
+
if (useMiddleware) {
|
|
124
|
+
app.use(coherentMiddleware({ enablePerformanceMonitoring }));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Create Express integration with dependency checking
|
|
130
|
+
* This function ensures Express is available before setting up the integration
|
|
131
|
+
*
|
|
132
|
+
* @param {Object} options - Setup options
|
|
133
|
+
* @returns {Promise<Function>} - Function to setup Express integration
|
|
134
|
+
*/
|
|
135
|
+
export async function createExpressIntegration(options = {}) {
|
|
136
|
+
try {
|
|
137
|
+
// Verify Express is available
|
|
138
|
+
await importPeerDependency('express', 'Express.js');
|
|
139
|
+
|
|
140
|
+
return function(app) {
|
|
141
|
+
if (!app || typeof app.use !== 'function') {
|
|
142
|
+
throw new Error('Invalid Express app instance provided');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
setupCoherent(app, options);
|
|
146
|
+
return app;
|
|
147
|
+
};
|
|
148
|
+
} catch (_error) {
|
|
149
|
+
throw _error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Export all utilities
|
|
154
|
+
export default {
|
|
155
|
+
coherentMiddleware,
|
|
156
|
+
createCoherentHandler,
|
|
157
|
+
enhancedExpressEngine,
|
|
158
|
+
setupCoherent,
|
|
159
|
+
createExpressIntegration
|
|
160
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/express/index.js
|
|
2
|
+
//
|
|
3
|
+
// Public entry point for @coherent.js/integrations/express. Re-exports the
|
|
4
|
+
// full runtime surface from coherent-express.js so the runtime matches the
|
|
5
|
+
// TypeScript declarations in ../../types/express/index.d.ts.
|
|
6
|
+
//
|
|
7
|
+
// `expressEngine` is preserved as a thin factory wrapper for backwards
|
|
8
|
+
// compatibility with consumers that imported it from the legacy
|
|
9
|
+
// @coherent.js/express package.
|
|
10
|
+
|
|
11
|
+
import { render } from '@coherent.js/core';
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
coherentMiddleware,
|
|
15
|
+
createCoherentHandler,
|
|
16
|
+
enhancedExpressEngine,
|
|
17
|
+
setupCoherent,
|
|
18
|
+
createExpressIntegration
|
|
19
|
+
} from './coherent-express.js';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Factory returning a classic Express view engine that renders a Coherent.js
|
|
23
|
+
* component tree passed via the `options` argument.
|
|
24
|
+
*
|
|
25
|
+
* @returns {(filePath: string, options: unknown, callback: (err: Error | null, html?: string) => void) => void}
|
|
26
|
+
*/
|
|
27
|
+
export function expressEngine() {
|
|
28
|
+
return (filePath, options, callback) => {
|
|
29
|
+
try {
|
|
30
|
+
const html = render(options);
|
|
31
|
+
callback(null, html);
|
|
32
|
+
} catch (_error) {
|
|
33
|
+
callback(_error);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fastify integration for Coherent.js
|
|
3
|
+
* Provides plugins and utilities for using Coherent.js with Fastify
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
renderWithTemplate,
|
|
8
|
+
renderComponentFactory
|
|
9
|
+
} from '@coherent.js/core';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Fastify plugin for Coherent.js
|
|
13
|
+
* Automatically renders Coherent.js components and handles errors
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} fastify - Fastify instance
|
|
16
|
+
* @param {Object} options - Plugin options
|
|
17
|
+
* @param {boolean} options.enablePerformanceMonitoring - Enable performance monitoring
|
|
18
|
+
* @param {string} options.template - HTML template with {{content}} placeholder
|
|
19
|
+
* @param {Function} done - Callback to signal plugin registration completion
|
|
20
|
+
*/
|
|
21
|
+
export function coherentFastify(fastify, options, done) {
|
|
22
|
+
const {
|
|
23
|
+
enablePerformanceMonitoring = false,
|
|
24
|
+
template = '<!DOCTYPE html>\n{{content}}'
|
|
25
|
+
} = options;
|
|
26
|
+
|
|
27
|
+
// Add decorator to check if an object is a Coherent.js component
|
|
28
|
+
fastify.decorateReply('isCoherentObject', (obj) => {
|
|
29
|
+
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const keys = Object.keys(obj);
|
|
34
|
+
return keys.length === 1;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Add decorator for rendering Coherent.js components
|
|
38
|
+
fastify.decorateReply('coherent', function(component, renderOptions = {}) {
|
|
39
|
+
const {
|
|
40
|
+
enablePerformanceMonitoring: renderPerformanceMonitoring = enablePerformanceMonitoring,
|
|
41
|
+
template: renderTemplate = template
|
|
42
|
+
} = renderOptions;
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// Use shared rendering utility
|
|
46
|
+
const finalHtml = renderWithTemplate(component, {
|
|
47
|
+
enablePerformanceMonitoring: renderPerformanceMonitoring,
|
|
48
|
+
template: renderTemplate
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Set content type and send HTML
|
|
52
|
+
this.header('Content-Type', 'text/html; charset=utf-8');
|
|
53
|
+
this.send(finalHtml);
|
|
54
|
+
} catch (_error) {
|
|
55
|
+
console.error('Coherent.js rendering _error:', _error);
|
|
56
|
+
this.status(500).send({
|
|
57
|
+
_error: 'Internal Server Error',
|
|
58
|
+
message: _error.message
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Hook to automatically render Coherent.js objects
|
|
64
|
+
fastify.addHook('onSend', async (request, reply, payload) => {
|
|
65
|
+
// If payload is a Coherent.js object, render it
|
|
66
|
+
if (reply.isCoherentObject(payload)) {
|
|
67
|
+
try {
|
|
68
|
+
// Use shared rendering utility
|
|
69
|
+
const finalHtml = renderWithTemplate(payload, { enablePerformanceMonitoring, template });
|
|
70
|
+
|
|
71
|
+
// Set content type and return HTML
|
|
72
|
+
reply.header('Content-Type', 'text/html; charset=utf-8');
|
|
73
|
+
return finalHtml;
|
|
74
|
+
} catch (_error) {
|
|
75
|
+
console.error('Coherent.js rendering _error:', _error);
|
|
76
|
+
throw _error;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// For non-Coherent.js data, return as-is
|
|
81
|
+
return payload;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
done();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Create a Fastify route handler for Coherent.js components
|
|
89
|
+
*
|
|
90
|
+
* @param {Function} componentFactory - Function that returns a Coherent.js component
|
|
91
|
+
* @param {Object} options - Handler options
|
|
92
|
+
* @returns {Function} Fastify route handler
|
|
93
|
+
*/
|
|
94
|
+
export function createHandler(componentFactory, options = {}) {
|
|
95
|
+
return async (request, reply) => {
|
|
96
|
+
try {
|
|
97
|
+
// Use shared rendering utility
|
|
98
|
+
const finalHtml = await renderComponentFactory(
|
|
99
|
+
componentFactory,
|
|
100
|
+
[request, reply],
|
|
101
|
+
options
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Send HTML response
|
|
105
|
+
reply.header('Content-Type', 'text/html; charset=utf-8');
|
|
106
|
+
return finalHtml;
|
|
107
|
+
} catch (_error) {
|
|
108
|
+
console.error('Coherent.js handler _error:', _error);
|
|
109
|
+
throw _error;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Setup Coherent.js with Fastify instance
|
|
116
|
+
*
|
|
117
|
+
* @param {Object} fastify - Fastify instance
|
|
118
|
+
* @param {Object} options - Setup options
|
|
119
|
+
*/
|
|
120
|
+
export function setupCoherent(fastify, options = {}) {
|
|
121
|
+
fastify.register(coherentFastify, options);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Export plugin as default for Fastify's plugin system
|
|
125
|
+
export default coherentFastify;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/fastify/index.js
|
|
2
|
+
//
|
|
3
|
+
// Public entry point for @coherent.js/integrations/fastify. Re-exports the
|
|
4
|
+
// full runtime surface from coherent-fastify.js so the runtime matches the
|
|
5
|
+
// TypeScript declarations in ../../types/fastify/index.d.ts.
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
coherentFastify,
|
|
9
|
+
createHandler,
|
|
10
|
+
setupCoherent
|
|
11
|
+
} from './coherent-fastify.js';
|
|
12
|
+
|
|
13
|
+
export { default } from './coherent-fastify.js';
|