@coherent.js/koa 1.0.0-beta.3 → 1.0.0-beta.6
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/README.md +421 -0
- package/dist/index.cjs +5 -1629
- package/dist/index.cjs.map +4 -4
- package/dist/index.js +6 -1615
- package/dist/index.js.map +4 -4
- package/package.json +3 -2
- package/types/index.d.ts +31 -4
package/README.md
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# @coherent.js/koa
|
|
2
|
+
|
|
3
|
+
Koa.js adapter for Coherent.js - High-performance server-side rendering with Koa integration.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @coherent.js/koa
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @coherent.js/koa
|
|
11
|
+
# or
|
|
12
|
+
yarn add @coherent.js/koa
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Note:** You also need to install Koa and Coherent.js core:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install koa @coherent.js/core
|
|
19
|
+
# or
|
|
20
|
+
pnpm add koa @coherent.js/core
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Overview
|
|
24
|
+
|
|
25
|
+
The `@coherent.js/koa` package provides seamless integration between Coherent.js and Koa.js, enabling you to build high-performance server-side rendered applications with Koa's middleware ecosystem.
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
import Koa from 'koa';
|
|
31
|
+
import { createCoherentHandler } from '@coherent.js/koa';
|
|
32
|
+
|
|
33
|
+
// Create your Coherent.js component
|
|
34
|
+
function App({ name = 'World' }) {
|
|
35
|
+
return {
|
|
36
|
+
div: {
|
|
37
|
+
children: [
|
|
38
|
+
{ h1: { text: `Hello, ${name}!` } },
|
|
39
|
+
{ p: { text: 'This is rendered with Coherent.js' } }
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Create Koa app
|
|
46
|
+
const app = new Koa();
|
|
47
|
+
|
|
48
|
+
// Add Coherent.js handler
|
|
49
|
+
app.use(createCoherentHandler(App, {
|
|
50
|
+
// Optional configuration
|
|
51
|
+
template: ({ html, head, body }) => `
|
|
52
|
+
<!DOCTYPE html>
|
|
53
|
+
<html>
|
|
54
|
+
<head>
|
|
55
|
+
<title>Coherent.js Koa App</title>
|
|
56
|
+
${head}
|
|
57
|
+
</head>
|
|
58
|
+
<body>
|
|
59
|
+
<div id="app">${body}</div>
|
|
60
|
+
</body>
|
|
61
|
+
</html>
|
|
62
|
+
`
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
// Start server
|
|
66
|
+
const PORT = process.env.PORT || 3000;
|
|
67
|
+
app.listen(PORT, () => {
|
|
68
|
+
console.log(`Server running on http://localhost:${PORT}`);
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Features
|
|
73
|
+
|
|
74
|
+
### Middleware Integration
|
|
75
|
+
|
|
76
|
+
Use Koa middleware alongside Coherent.js rendering:
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
import Koa from 'koa';
|
|
80
|
+
import logger from 'koa-logger';
|
|
81
|
+
import { createCoherentHandler } from '@coherent.js/koa';
|
|
82
|
+
|
|
83
|
+
const app = new Koa();
|
|
84
|
+
|
|
85
|
+
// Add Koa middleware
|
|
86
|
+
app.use(logger());
|
|
87
|
+
|
|
88
|
+
// Add Coherent.js rendering
|
|
89
|
+
app.use(createCoherentHandler(App));
|
|
90
|
+
|
|
91
|
+
app.listen(3000);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Request Context Access
|
|
95
|
+
|
|
96
|
+
Access Koa request context in your components:
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
function App({ ctx }) {
|
|
100
|
+
// ctx is the Koa context object
|
|
101
|
+
const userAgent = ctx.get('User-Agent');
|
|
102
|
+
const ipAddress = ctx.ip;
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
div: {
|
|
106
|
+
children: [
|
|
107
|
+
{ p: { text: `Your IP: ${ipAddress}` } },
|
|
108
|
+
{ p: { text: `User Agent: ${userAgent}` } }
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Custom Routes
|
|
116
|
+
|
|
117
|
+
Handle specific routes with Coherent.js:
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
import Koa from 'koa';
|
|
121
|
+
import Router from '@koa/router';
|
|
122
|
+
import { createCoherentHandler } from '@coherent.js/koa';
|
|
123
|
+
|
|
124
|
+
const app = new Koa();
|
|
125
|
+
const router = new Router();
|
|
126
|
+
|
|
127
|
+
// Handle specific routes
|
|
128
|
+
router.get('/', createCoherentHandler(HomePage));
|
|
129
|
+
router.get('/about', createCoherentHandler(AboutPage));
|
|
130
|
+
router.get('/users/:id', createCoherentHandler(UserProfile));
|
|
131
|
+
|
|
132
|
+
app.use(router.routes());
|
|
133
|
+
app.listen(3000);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Error Handling
|
|
137
|
+
|
|
138
|
+
Integrate with Koa's error handling:
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
import Koa from 'koa';
|
|
142
|
+
import { createCoherentHandler } from '@coherent.js/koa';
|
|
143
|
+
|
|
144
|
+
const app = new Koa();
|
|
145
|
+
|
|
146
|
+
// Error handling middleware
|
|
147
|
+
app.use(async (ctx, next) => {
|
|
148
|
+
try {
|
|
149
|
+
await next();
|
|
150
|
+
} catch (err) {
|
|
151
|
+
ctx.status = err.status || 500;
|
|
152
|
+
ctx.body = { error: err.message };
|
|
153
|
+
ctx.app.emit('error', err, ctx);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Coherent.js handler
|
|
158
|
+
app.use(createCoherentHandler(App));
|
|
159
|
+
|
|
160
|
+
app.listen(3000);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Configuration Options
|
|
164
|
+
|
|
165
|
+
### Template Function
|
|
166
|
+
|
|
167
|
+
Customize the HTML template:
|
|
168
|
+
|
|
169
|
+
```javascript
|
|
170
|
+
app.use(createCoherentHandler(App, {
|
|
171
|
+
template: ({ html, head, body, ctx }) => `
|
|
172
|
+
<!DOCTYPE html>
|
|
173
|
+
<html lang="${ctx.acceptsLanguages()[0] || 'en'}">
|
|
174
|
+
<head>
|
|
175
|
+
<meta charset="utf-8">
|
|
176
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
177
|
+
${head}
|
|
178
|
+
</head>
|
|
179
|
+
<body>
|
|
180
|
+
<div id="app">${body}</div>
|
|
181
|
+
<script>
|
|
182
|
+
window.__INITIAL_DATA__ = ${JSON.stringify(ctx.state)};
|
|
183
|
+
</script>
|
|
184
|
+
</body>
|
|
185
|
+
</html>
|
|
186
|
+
`
|
|
187
|
+
}));
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Props Function
|
|
191
|
+
|
|
192
|
+
Customize component props based on Koa context:
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
app.use(createCoherentHandler(App, {
|
|
196
|
+
props: (ctx) => ({
|
|
197
|
+
userAgent: ctx.get('User-Agent'),
|
|
198
|
+
url: ctx.url,
|
|
199
|
+
user: ctx.state.user || null,
|
|
200
|
+
query: ctx.query
|
|
201
|
+
})
|
|
202
|
+
}));
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Error Component
|
|
206
|
+
|
|
207
|
+
Provide a custom error component:
|
|
208
|
+
|
|
209
|
+
```javascript
|
|
210
|
+
function ErrorPage({ error, status }) {
|
|
211
|
+
return {
|
|
212
|
+
div: {
|
|
213
|
+
className: 'error-page',
|
|
214
|
+
children: [
|
|
215
|
+
{ h1: { text: `Error ${status}` } },
|
|
216
|
+
{ p: { text: error.message } }
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
app.use(createCoherentHandler(App, {
|
|
223
|
+
errorComponent: ErrorPage
|
|
224
|
+
}));
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Performance Optimizations
|
|
228
|
+
|
|
229
|
+
### Caching
|
|
230
|
+
|
|
231
|
+
Enable response caching:
|
|
232
|
+
|
|
233
|
+
```javascript
|
|
234
|
+
import cache from 'koa-cache-lite';
|
|
235
|
+
|
|
236
|
+
app.use(cache({
|
|
237
|
+
'/api/*': 5 * 60 * 1000, // 5 minutes for API routes
|
|
238
|
+
'/static/*': 60 * 60 * 1000 // 1 hour for static assets
|
|
239
|
+
}));
|
|
240
|
+
|
|
241
|
+
app.use(createCoherentHandler(App));
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Compression
|
|
245
|
+
|
|
246
|
+
Add response compression:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
import compress from 'koa-compress';
|
|
250
|
+
|
|
251
|
+
app.use(compress({
|
|
252
|
+
threshold: 2048,
|
|
253
|
+
gzip: { flush: require('zlib').constants.Z_SYNC_FLUSH }
|
|
254
|
+
}));
|
|
255
|
+
|
|
256
|
+
app.use(createCoherentHandler(App));
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Advanced Usage
|
|
260
|
+
|
|
261
|
+
### Server-Side State Management
|
|
262
|
+
|
|
263
|
+
Manage state during server rendering:
|
|
264
|
+
|
|
265
|
+
```javascript
|
|
266
|
+
function App({ ctx }) {
|
|
267
|
+
// Create request-scoped state
|
|
268
|
+
const requestState = {
|
|
269
|
+
requestId: Math.random().toString(36),
|
|
270
|
+
renderStartTime: Date.now()
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
ctx.state.requestState = requestState;
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
div: {
|
|
277
|
+
children: [
|
|
278
|
+
{ p: { text: `Request ID: ${requestState.requestId}` } }
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Integration with Koa Router
|
|
286
|
+
|
|
287
|
+
Advanced routing with parameters:
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
import Router from '@koa/router';
|
|
291
|
+
|
|
292
|
+
const router = new Router();
|
|
293
|
+
|
|
294
|
+
router.get('/', createCoherentHandler(HomePage));
|
|
295
|
+
router.get('/users/:id', createCoherentHandler(UserProfile, {
|
|
296
|
+
props: (ctx) => ({
|
|
297
|
+
userId: ctx.params.id,
|
|
298
|
+
query: ctx.query
|
|
299
|
+
})
|
|
300
|
+
}));
|
|
301
|
+
router.get('/api/*', createCoherentHandler(ApiDocs));
|
|
302
|
+
|
|
303
|
+
app.use(router.routes());
|
|
304
|
+
app.use(router.allowedMethods());
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
## API Reference
|
|
308
|
+
|
|
309
|
+
### createCoherentHandler(component, options)
|
|
310
|
+
|
|
311
|
+
Create a Koa middleware for Coherent.js rendering.
|
|
312
|
+
|
|
313
|
+
**Parameters:**
|
|
314
|
+
- `component` - Coherent.js component function
|
|
315
|
+
- `options.template` - Function to customize HTML template
|
|
316
|
+
- `options.props` - Function to generate component props from Koa context
|
|
317
|
+
- `options.errorComponent` - Component to render on errors
|
|
318
|
+
- `options.enableHydration` - Boolean to enable client-side hydration (default: true)
|
|
319
|
+
|
|
320
|
+
**Returns:** Koa middleware function
|
|
321
|
+
|
|
322
|
+
### Options
|
|
323
|
+
|
|
324
|
+
- `template` - Function receiving { html, head, body, ctx } and returning HTML string
|
|
325
|
+
- `props` - Function receiving Koa context and returning component props
|
|
326
|
+
- `errorComponent` - Component for error rendering
|
|
327
|
+
- `enableHydration` - Enable/disable hydration support
|
|
328
|
+
|
|
329
|
+
## Examples
|
|
330
|
+
|
|
331
|
+
### Full Application Example
|
|
332
|
+
|
|
333
|
+
```javascript
|
|
334
|
+
import Koa from 'koa';
|
|
335
|
+
import Router from '@koa/router';
|
|
336
|
+
import bodyParser from 'koa-bodyparser';
|
|
337
|
+
import logger from 'koa-logger';
|
|
338
|
+
import session from 'koa-session';
|
|
339
|
+
import { createCoherentHandler } from '@coherent.js/koa';
|
|
340
|
+
|
|
341
|
+
// Components
|
|
342
|
+
function HomePage({ user }) {
|
|
343
|
+
return {
|
|
344
|
+
div: {
|
|
345
|
+
children: [
|
|
346
|
+
{ h1: { text: 'Welcome to Coherent.js + Koa' } },
|
|
347
|
+
user
|
|
348
|
+
? { p: { text: `Hello, ${user.name}!` } }
|
|
349
|
+
: { a: { href: '/login', text: 'Login' } }
|
|
350
|
+
]
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function LoginPage() {
|
|
356
|
+
return {
|
|
357
|
+
form: {
|
|
358
|
+
action: '/login',
|
|
359
|
+
method: 'POST',
|
|
360
|
+
children: [
|
|
361
|
+
{ input: { type: 'email', name: 'email', placeholder: 'Email' } },
|
|
362
|
+
{ input: { type: 'password', name: 'password', placeholder: 'Password' } },
|
|
363
|
+
{ button: { type: 'submit', text: 'Login' } }
|
|
364
|
+
]
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Create app
|
|
370
|
+
const app = new Koa();
|
|
371
|
+
const router = new Router();
|
|
372
|
+
|
|
373
|
+
// Middleware
|
|
374
|
+
app.use(logger());
|
|
375
|
+
app.use(bodyParser());
|
|
376
|
+
app.keys = ['secret-key'];
|
|
377
|
+
app.use(session(app));
|
|
378
|
+
|
|
379
|
+
// Routes
|
|
380
|
+
router.get('/', createCoherentHandler(HomePage, {
|
|
381
|
+
props: (ctx) => ({ user: ctx.session.user })
|
|
382
|
+
}));
|
|
383
|
+
|
|
384
|
+
router.get('/login', createCoherentHandler(LoginPage));
|
|
385
|
+
router.post('/login', async (ctx) => {
|
|
386
|
+
// Simple auth (in production, use proper authentication)
|
|
387
|
+
const { email, password } = ctx.request.body;
|
|
388
|
+
if (email && password) {
|
|
389
|
+
ctx.session.user = { name: email.split('@')[0], email };
|
|
390
|
+
ctx.redirect('/');
|
|
391
|
+
} else {
|
|
392
|
+
ctx.status = 400;
|
|
393
|
+
ctx.body = { error: 'Invalid credentials' };
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
router.get('/logout', (ctx) => {
|
|
398
|
+
ctx.session = null;
|
|
399
|
+
ctx.redirect('/');
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// Setup
|
|
403
|
+
app.use(router.routes());
|
|
404
|
+
app.use(router.allowedMethods());
|
|
405
|
+
|
|
406
|
+
const PORT = process.env.PORT || 3000;
|
|
407
|
+
app.listen(PORT, () => {
|
|
408
|
+
console.log(`Server running on http://localhost:${PORT}`);
|
|
409
|
+
});
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## Related Packages
|
|
413
|
+
|
|
414
|
+
- [@coherent.js/core](../core/README.md) - Core framework
|
|
415
|
+
- [@coherent.js/express](../express/README.md) - Express.js adapter
|
|
416
|
+
- [@coherent.js/fastify](../fastify/README.md) - Fastify adapter
|
|
417
|
+
- [@coherent.js/nextjs](../nextjs/README.md) - Next.js integration
|
|
418
|
+
|
|
419
|
+
## License
|
|
420
|
+
|
|
421
|
+
MIT
|