@coherent.js/runtime 1.0.0-beta.2
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 +1119 -0
- package/dist/coherent-browser.cjs +304 -0
- package/dist/coherent-browser.cjs.map +7 -0
- package/dist/coherent-browser.js +279 -0
- package/dist/coherent-browser.js.map +7 -0
- package/dist/coherent-edge.cjs +458 -0
- package/dist/coherent-edge.cjs.map +7 -0
- package/dist/coherent-edge.js +430 -0
- package/dist/coherent-edge.js.map +7 -0
- package/dist/coherent-node.cjs +330 -0
- package/dist/coherent-node.cjs.map +7 -0
- package/dist/coherent-node.js +304 -0
- package/dist/coherent-node.js.map +7 -0
- package/dist/coherent-standalone.esm.js +279 -0
- package/dist/coherent-standalone.esm.js.map +7 -0
- package/dist/coherent-standalone.js +2 -0
- package/dist/coherent-standalone.js.map +7 -0
- package/dist/coherent-static.cjs +356 -0
- package/dist/coherent-static.cjs.map +7 -0
- package/dist/coherent-static.js +331 -0
- package/dist/coherent-static.js.map +7 -0
- package/dist/coherent-universal.cjs +3294 -0
- package/dist/coherent-universal.cjs.map +7 -0
- package/dist/coherent-universal.js +3254 -0
- package/dist/coherent-universal.js.map +7 -0
- package/dist/coherent-universal.min.js +2 -0
- package/dist/coherent-universal.min.js.map +7 -0
- package/package.json +92 -0
- package/types/browser.d.ts +2 -0
- package/types/edge.d.ts +2 -0
- package/types/index.d.ts +118 -0
- package/types/static.d.ts +2 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/runtimes/node.js
|
|
21
|
+
var node_exports = {};
|
|
22
|
+
__export(node_exports, {
|
|
23
|
+
NodeRuntime: () => NodeRuntime,
|
|
24
|
+
createNodeRuntime: () => createNodeRuntime,
|
|
25
|
+
default: () => node_default
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(node_exports);
|
|
28
|
+
var import_core = require("@coherent.js/core");
|
|
29
|
+
var import_http = require("http");
|
|
30
|
+
var NodeRuntime = class {
|
|
31
|
+
constructor(options = {}) {
|
|
32
|
+
this.options = {
|
|
33
|
+
port: 3e3,
|
|
34
|
+
host: "localhost",
|
|
35
|
+
caching: true,
|
|
36
|
+
framework: null,
|
|
37
|
+
// 'express', 'fastify', 'koa', or null for standalone
|
|
38
|
+
headers: {
|
|
39
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
40
|
+
"Cache-Control": "public, max-age=3600",
|
|
41
|
+
...options.headers
|
|
42
|
+
},
|
|
43
|
+
...options
|
|
44
|
+
};
|
|
45
|
+
this.componentRegistry = /* @__PURE__ */ new Map();
|
|
46
|
+
this.routeRegistry = /* @__PURE__ */ new Map();
|
|
47
|
+
this.middleware = [];
|
|
48
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
49
|
+
this.renderCount = 0;
|
|
50
|
+
this.server = null;
|
|
51
|
+
if (this.options.caching) {
|
|
52
|
+
this.initializeCacheCleanup();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
initializeCacheCleanup() {
|
|
56
|
+
setInterval(() => {
|
|
57
|
+
if (this.cache.size > 1e3) {
|
|
58
|
+
const entries = Array.from(this.cache.entries());
|
|
59
|
+
const toDelete = entries.slice(0, entries.length - 500);
|
|
60
|
+
toDelete.forEach(([key]) => this.cache.delete(key));
|
|
61
|
+
}
|
|
62
|
+
}, 3e5);
|
|
63
|
+
}
|
|
64
|
+
// Component management
|
|
65
|
+
registerComponent(name, component) {
|
|
66
|
+
this.componentRegistry.set(name, component);
|
|
67
|
+
return component;
|
|
68
|
+
}
|
|
69
|
+
getComponent(name) {
|
|
70
|
+
return this.componentRegistry.get(name);
|
|
71
|
+
}
|
|
72
|
+
// Route management
|
|
73
|
+
addRoute(pattern, handler) {
|
|
74
|
+
this.routeRegistry.set(pattern, handler);
|
|
75
|
+
}
|
|
76
|
+
matchRoute(pathname) {
|
|
77
|
+
for (const [pattern, handler] of this.routeRegistry.entries()) {
|
|
78
|
+
const match = this.matchPattern(pattern, pathname);
|
|
79
|
+
if (match) {
|
|
80
|
+
return { handler, params: match.params };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
matchPattern(pattern, pathname) {
|
|
86
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
87
|
+
const pathParts = pathname.split("/").filter(Boolean);
|
|
88
|
+
if (patternParts.length !== pathParts.length) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
const params = {};
|
|
92
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
93
|
+
const patternPart = patternParts[i];
|
|
94
|
+
const pathPart = pathParts[i];
|
|
95
|
+
if (patternPart.startsWith(":")) {
|
|
96
|
+
params[patternPart.slice(1)] = pathPart;
|
|
97
|
+
} else if (patternPart !== pathPart) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { params };
|
|
102
|
+
}
|
|
103
|
+
// Rendering
|
|
104
|
+
async renderComponent(component, props = {}, options = {}) {
|
|
105
|
+
try {
|
|
106
|
+
const resolvedComponent = typeof component === "string" ? this.getComponent(component) : component;
|
|
107
|
+
if (!resolvedComponent) {
|
|
108
|
+
throw new Error(`Component not found: ${component}`);
|
|
109
|
+
}
|
|
110
|
+
const cacheKey = this.generateCacheKey(resolvedComponent, props);
|
|
111
|
+
if (this.options.caching && this.cache.has(cacheKey)) {
|
|
112
|
+
const cached = this.cache.get(cacheKey);
|
|
113
|
+
if (Date.now() - cached.timestamp < (options.cacheMaxAge || 3e5)) {
|
|
114
|
+
return cached.html;
|
|
115
|
+
}
|
|
116
|
+
this.cache.delete(cacheKey);
|
|
117
|
+
}
|
|
118
|
+
const vdom = resolvedComponent(props);
|
|
119
|
+
const html = (0, import_core.render)(vdom);
|
|
120
|
+
if (this.options.caching && options.cacheable !== false) {
|
|
121
|
+
this.cache.set(cacheKey, {
|
|
122
|
+
html,
|
|
123
|
+
timestamp: Date.now()
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
this.renderCount++;
|
|
127
|
+
return html;
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error("Node render error:", error);
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
generateCacheKey(component, props) {
|
|
134
|
+
const componentName = component.name || "anonymous";
|
|
135
|
+
const propsHash = this.hashObject(props);
|
|
136
|
+
return `${componentName}-${propsHash}`;
|
|
137
|
+
}
|
|
138
|
+
hashObject(obj) {
|
|
139
|
+
const str = JSON.stringify(obj, Object.keys(obj).sort());
|
|
140
|
+
let hash = 0;
|
|
141
|
+
for (let i = 0; i < str.length; i++) {
|
|
142
|
+
const char = str.charCodeAt(i);
|
|
143
|
+
hash = (hash << 5) - hash + char;
|
|
144
|
+
hash = hash & hash;
|
|
145
|
+
}
|
|
146
|
+
return hash.toString(36);
|
|
147
|
+
}
|
|
148
|
+
// HTTP Request handling (for standalone mode)
|
|
149
|
+
async handleRequest(req, res) {
|
|
150
|
+
try {
|
|
151
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
152
|
+
const pathname = url.pathname;
|
|
153
|
+
const context = {
|
|
154
|
+
req,
|
|
155
|
+
res,
|
|
156
|
+
url,
|
|
157
|
+
pathname,
|
|
158
|
+
params: {},
|
|
159
|
+
query: Object.fromEntries(url.searchParams),
|
|
160
|
+
method: req.method,
|
|
161
|
+
headers: req.headers,
|
|
162
|
+
runtime: this,
|
|
163
|
+
state: {}
|
|
164
|
+
};
|
|
165
|
+
let middlewareIndex = 0;
|
|
166
|
+
const next = async () => {
|
|
167
|
+
if (middlewareIndex < this.middleware.length) {
|
|
168
|
+
const middleware = this.middleware[middlewareIndex++];
|
|
169
|
+
return await middleware(context, next);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
if (this.middleware.length > 0) {
|
|
173
|
+
await next();
|
|
174
|
+
}
|
|
175
|
+
if (res.headersSent) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const match = this.matchRoute(pathname);
|
|
179
|
+
if (!match) {
|
|
180
|
+
res.writeHead(404, { "Content-Type": "text/html" });
|
|
181
|
+
res.end("<h1>404 Not Found</h1>");
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
context.params = match.params;
|
|
185
|
+
const result = await match.handler(context);
|
|
186
|
+
if (res.headersSent) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (typeof result === "string") {
|
|
190
|
+
res.writeHead(200, this.options.headers);
|
|
191
|
+
res.end(result);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (result && typeof result === "object") {
|
|
195
|
+
if (result.component) {
|
|
196
|
+
const html = await this.renderComponent(
|
|
197
|
+
result.component,
|
|
198
|
+
result.props || {},
|
|
199
|
+
result.options || {}
|
|
200
|
+
);
|
|
201
|
+
res.writeHead(result.status || 200, {
|
|
202
|
+
...this.options.headers,
|
|
203
|
+
...result.headers
|
|
204
|
+
});
|
|
205
|
+
res.end(html);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (result.json !== void 0) {
|
|
209
|
+
res.writeHead(result.status || 200, {
|
|
210
|
+
"Content-Type": "application/json",
|
|
211
|
+
...result.headers
|
|
212
|
+
});
|
|
213
|
+
res.end(JSON.stringify(result.json));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (result.redirect) {
|
|
217
|
+
res.writeHead(result.status || 302, {
|
|
218
|
+
"Location": result.redirect
|
|
219
|
+
});
|
|
220
|
+
res.end();
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
224
|
+
res.end(JSON.stringify(result));
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
res.writeHead(200, this.options.headers);
|
|
228
|
+
res.end(String(result));
|
|
229
|
+
} catch (error) {
|
|
230
|
+
console.error("Request handling error:", error);
|
|
231
|
+
if (!res.headersSent) {
|
|
232
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
233
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
// Create app factory
|
|
238
|
+
createApp() {
|
|
239
|
+
const app = {
|
|
240
|
+
// Component registration
|
|
241
|
+
component: (name, component) => this.registerComponent(name, component),
|
|
242
|
+
// Routing
|
|
243
|
+
get: (pattern, handler) => this.addRoute(pattern, handler),
|
|
244
|
+
post: (pattern, handler) => this.addRoute(pattern, handler),
|
|
245
|
+
put: (pattern, handler) => this.addRoute(pattern, handler),
|
|
246
|
+
delete: (pattern, handler) => this.addRoute(pattern, handler),
|
|
247
|
+
route: (pattern, handler) => this.addRoute(pattern, handler),
|
|
248
|
+
// Middleware support
|
|
249
|
+
use: (middleware) => {
|
|
250
|
+
if (typeof middleware !== "function") {
|
|
251
|
+
throw new Error("Middleware must be a function");
|
|
252
|
+
}
|
|
253
|
+
this.middleware.push(middleware);
|
|
254
|
+
return app;
|
|
255
|
+
},
|
|
256
|
+
// Server control
|
|
257
|
+
listen: (port, callback) => {
|
|
258
|
+
const serverPort = port || this.options.port;
|
|
259
|
+
this.server = (0, import_http.createServer)((req, res) => this.handleRequest(req, res));
|
|
260
|
+
this.server.listen(serverPort, this.options.host, () => {
|
|
261
|
+
console.log(`Coherent.js Node runtime listening on http://${this.options.host}:${serverPort}`);
|
|
262
|
+
if (callback) callback();
|
|
263
|
+
});
|
|
264
|
+
return this.server;
|
|
265
|
+
},
|
|
266
|
+
close: (callback) => {
|
|
267
|
+
if (this.server) {
|
|
268
|
+
this.server.close(callback);
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
// Utilities
|
|
272
|
+
render: (component, props, options) => this.renderComponent(component, props, options),
|
|
273
|
+
getRuntime: () => this,
|
|
274
|
+
getStats: () => ({
|
|
275
|
+
renderCount: this.renderCount,
|
|
276
|
+
cacheSize: this.cache.size,
|
|
277
|
+
componentCount: this.componentRegistry.size,
|
|
278
|
+
routeCount: this.routeRegistry.size,
|
|
279
|
+
middlewareCount: this.middleware.length
|
|
280
|
+
})
|
|
281
|
+
};
|
|
282
|
+
return app;
|
|
283
|
+
}
|
|
284
|
+
// Framework integration helpers
|
|
285
|
+
expressMiddleware() {
|
|
286
|
+
return async (req, res, next) => {
|
|
287
|
+
req.coherent = {
|
|
288
|
+
render: async (component, props, options) => {
|
|
289
|
+
const html = await this.renderComponent(component, props, options);
|
|
290
|
+
res.send(html);
|
|
291
|
+
},
|
|
292
|
+
runtime: this
|
|
293
|
+
};
|
|
294
|
+
next();
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
fastifyPlugin() {
|
|
298
|
+
return async (fastify) => {
|
|
299
|
+
fastify.decorate("coherent", {
|
|
300
|
+
render: async (component, props, renderOptions) => {
|
|
301
|
+
return await this.renderComponent(component, props, renderOptions);
|
|
302
|
+
},
|
|
303
|
+
runtime: this
|
|
304
|
+
});
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
koaMiddleware() {
|
|
308
|
+
return async (ctx, next) => {
|
|
309
|
+
ctx.coherent = {
|
|
310
|
+
render: async (component, props, options) => {
|
|
311
|
+
const html = await this.renderComponent(component, props, options);
|
|
312
|
+
ctx.type = "html";
|
|
313
|
+
ctx.body = html;
|
|
314
|
+
},
|
|
315
|
+
runtime: this
|
|
316
|
+
};
|
|
317
|
+
await next();
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
function createNodeRuntime(options = {}) {
|
|
322
|
+
return new NodeRuntime(options);
|
|
323
|
+
}
|
|
324
|
+
var node_default = NodeRuntime;
|
|
325
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
326
|
+
0 && (module.exports = {
|
|
327
|
+
NodeRuntime,
|
|
328
|
+
createNodeRuntime
|
|
329
|
+
});
|
|
330
|
+
//# sourceMappingURL=coherent-node.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/runtimes/node.js"],
|
|
4
|
+
"sourcesContent": ["/**\n * Node.js Runtime - For Node.js with framework integrations\n * Provides server-side rendering with Express, Fastify, Koa support\n */\n\nimport { render } from '@coherent.js/core';\nimport { createServer } from 'http';\n\nexport class NodeRuntime {\n constructor(options = {}) {\n this.options = {\n port: 3000,\n host: 'localhost',\n caching: true,\n framework: null, // 'express', 'fastify', 'koa', or null for standalone\n headers: {\n 'Content-Type': 'text/html; charset=utf-8',\n 'Cache-Control': 'public, max-age=3600',\n ...options.headers\n },\n ...options\n };\n \n this.componentRegistry = new Map();\n this.routeRegistry = new Map();\n this.middleware = [];\n this.cache = new Map();\n this.renderCount = 0;\n this.server = null;\n \n // Initialize cache cleanup if caching is enabled\n if (this.options.caching) {\n this.initializeCacheCleanup();\n }\n }\n\n initializeCacheCleanup() {\n // Simple LRU-style cleanup\n setInterval(() => {\n if (this.cache.size > 1000) {\n const entries = Array.from(this.cache.entries());\n const toDelete = entries.slice(0, entries.length - 500);\n toDelete.forEach(([key]) => this.cache.delete(key));\n }\n }, 300000); // Every 5 minutes\n }\n\n // Component management\n registerComponent(name, component) {\n this.componentRegistry.set(name, component);\n return component;\n }\n\n getComponent(name) {\n return this.componentRegistry.get(name);\n }\n\n // Route management\n addRoute(pattern, handler) {\n this.routeRegistry.set(pattern, handler);\n }\n\n matchRoute(pathname) {\n for (const [pattern, handler] of this.routeRegistry.entries()) {\n const match = this.matchPattern(pattern, pathname);\n if (match) {\n return { handler, params: match.params };\n }\n }\n return null;\n }\n\n matchPattern(pattern, pathname) {\n const patternParts = pattern.split('/').filter(Boolean);\n const pathParts = pathname.split('/').filter(Boolean);\n\n if (patternParts.length !== pathParts.length) {\n return null;\n }\n\n const params = {};\n \n for (let i = 0; i < patternParts.length; i++) {\n const patternPart = patternParts[i];\n const pathPart = pathParts[i];\n\n if (patternPart.startsWith(':')) {\n params[patternPart.slice(1)] = pathPart;\n } else if (patternPart !== pathPart) {\n return null;\n }\n }\n\n return { params };\n }\n\n // Rendering\n async renderComponent(component, props = {}, options = {}) {\n \n try {\n // Resolve component\n const resolvedComponent = typeof component === 'string' \n ? this.getComponent(component) \n : component;\n\n if (!resolvedComponent) {\n throw new Error(`Component not found: ${component}`);\n }\n\n // Check cache\n const cacheKey = this.generateCacheKey(resolvedComponent, props);\n if (this.options.caching && this.cache.has(cacheKey)) {\n const cached = this.cache.get(cacheKey);\n if (Date.now() - cached.timestamp < (options.cacheMaxAge || 300000)) {\n return cached.html;\n }\n this.cache.delete(cacheKey);\n }\n\n // Render component\n const vdom = resolvedComponent(props);\n const html = render(vdom);\n\n // Cache result\n if (this.options.caching && options.cacheable !== false) {\n this.cache.set(cacheKey, {\n html,\n timestamp: Date.now()\n });\n }\n\n this.renderCount++;\n \n return html;\n } catch (error) {\n console.error('Node render error:', error);\n throw error;\n }\n }\n\n generateCacheKey(component, props) {\n const componentName = component.name || 'anonymous';\n const propsHash = this.hashObject(props);\n return `${componentName}-${propsHash}`;\n }\n\n hashObject(obj) {\n const str = JSON.stringify(obj, Object.keys(obj).sort());\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash;\n }\n return hash.toString(36);\n }\n\n // HTTP Request handling (for standalone mode)\n async handleRequest(req, res) {\n try {\n const url = new URL(req.url, `http://${req.headers.host}`);\n const pathname = url.pathname;\n \n // Create context\n const context = {\n req,\n res,\n url,\n pathname,\n params: {},\n query: Object.fromEntries(url.searchParams),\n method: req.method,\n headers: req.headers,\n runtime: this,\n state: {}\n };\n\n // Execute middleware chain\n let middlewareIndex = 0;\n const next = async () => {\n if (middlewareIndex < this.middleware.length) {\n const middleware = this.middleware[middlewareIndex++];\n return await middleware(context, next);\n }\n };\n\n // Run middleware\n if (this.middleware.length > 0) {\n await next();\n }\n\n // Check if middleware already sent a response\n if (res.headersSent) {\n return;\n }\n \n // Find matching route\n const match = this.matchRoute(pathname);\n \n if (!match) {\n res.writeHead(404, { 'Content-Type': 'text/html' });\n res.end('<h1>404 Not Found</h1>');\n return;\n }\n\n // Add route params to context\n context.params = match.params;\n\n // Execute route handler\n const result = await match.handler(context);\n \n // Handle different response types\n if (res.headersSent) {\n return; // Handler already sent response\n }\n\n if (typeof result === 'string') {\n res.writeHead(200, this.options.headers);\n res.end(result);\n return;\n }\n\n if (result && typeof result === 'object') {\n if (result.component) {\n const html = await this.renderComponent(\n result.component, \n result.props || {}, \n result.options || {}\n );\n res.writeHead(result.status || 200, {\n ...this.options.headers,\n ...result.headers\n });\n res.end(html);\n return;\n }\n\n if (result.json !== undefined) {\n res.writeHead(result.status || 200, {\n 'Content-Type': 'application/json',\n ...result.headers\n });\n res.end(JSON.stringify(result.json));\n return;\n }\n\n if (result.redirect) {\n res.writeHead(result.status || 302, {\n 'Location': result.redirect\n });\n res.end();\n return;\n }\n\n // Default: send as JSON\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(result));\n return;\n }\n\n // Fallback\n res.writeHead(200, this.options.headers);\n res.end(String(result));\n\n } catch (error) {\n console.error('Request handling error:', error);\n \n if (!res.headersSent) {\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: error.message }));\n }\n }\n }\n\n // Create app factory\n createApp() {\n const app = {\n // Component registration\n component: (name, component) => this.registerComponent(name, component),\n \n // Routing\n get: (pattern, handler) => this.addRoute(pattern, handler),\n post: (pattern, handler) => this.addRoute(pattern, handler),\n put: (pattern, handler) => this.addRoute(pattern, handler),\n delete: (pattern, handler) => this.addRoute(pattern, handler),\n route: (pattern, handler) => this.addRoute(pattern, handler),\n \n // Middleware support\n use: (middleware) => {\n if (typeof middleware !== 'function') {\n throw new Error('Middleware must be a function');\n }\n this.middleware.push(middleware);\n return app;\n },\n \n // Server control\n listen: (port, callback) => {\n const serverPort = port || this.options.port;\n this.server = createServer((req, res) => this.handleRequest(req, res));\n \n this.server.listen(serverPort, this.options.host, () => {\n console.log(`Coherent.js Node runtime listening on http://${this.options.host}:${serverPort}`);\n if (callback) callback();\n });\n \n return this.server;\n },\n \n close: (callback) => {\n if (this.server) {\n this.server.close(callback);\n }\n },\n \n // Utilities\n render: (component, props, options) => this.renderComponent(component, props, options),\n getRuntime: () => this,\n getStats: () => ({\n renderCount: this.renderCount,\n cacheSize: this.cache.size,\n componentCount: this.componentRegistry.size,\n routeCount: this.routeRegistry.size,\n middlewareCount: this.middleware.length\n })\n };\n\n return app;\n }\n\n // Framework integration helpers\n expressMiddleware() {\n return async (req, res, next) => {\n req.coherent = {\n render: async (component, props, options) => {\n const html = await this.renderComponent(component, props, options);\n res.send(html);\n },\n runtime: this\n };\n next();\n };\n }\n\n fastifyPlugin() {\n return async (fastify) => {\n fastify.decorate('coherent', {\n render: async (component, props, renderOptions) => {\n return await this.renderComponent(component, props, renderOptions);\n },\n runtime: this\n });\n };\n }\n\n koaMiddleware() {\n return async (ctx, next) => {\n ctx.coherent = {\n render: async (component, props, options) => {\n const html = await this.renderComponent(component, props, options);\n ctx.type = 'html';\n ctx.body = html;\n },\n runtime: this\n };\n await next();\n };\n }\n}\n\n/**\n * Create a Node.js runtime instance\n */\nexport function createNodeRuntime(options = {}) {\n return new NodeRuntime(options);\n}\n\nexport default NodeRuntime;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// src/runtimes/node.js
|
|
2
|
+
import { render } from "@coherent.js/core";
|
|
3
|
+
import { createServer } from "http";
|
|
4
|
+
var NodeRuntime = class {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.options = {
|
|
7
|
+
port: 3e3,
|
|
8
|
+
host: "localhost",
|
|
9
|
+
caching: true,
|
|
10
|
+
framework: null,
|
|
11
|
+
// 'express', 'fastify', 'koa', or null for standalone
|
|
12
|
+
headers: {
|
|
13
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
14
|
+
"Cache-Control": "public, max-age=3600",
|
|
15
|
+
...options.headers
|
|
16
|
+
},
|
|
17
|
+
...options
|
|
18
|
+
};
|
|
19
|
+
this.componentRegistry = /* @__PURE__ */ new Map();
|
|
20
|
+
this.routeRegistry = /* @__PURE__ */ new Map();
|
|
21
|
+
this.middleware = [];
|
|
22
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
23
|
+
this.renderCount = 0;
|
|
24
|
+
this.server = null;
|
|
25
|
+
if (this.options.caching) {
|
|
26
|
+
this.initializeCacheCleanup();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
initializeCacheCleanup() {
|
|
30
|
+
setInterval(() => {
|
|
31
|
+
if (this.cache.size > 1e3) {
|
|
32
|
+
const entries = Array.from(this.cache.entries());
|
|
33
|
+
const toDelete = entries.slice(0, entries.length - 500);
|
|
34
|
+
toDelete.forEach(([key]) => this.cache.delete(key));
|
|
35
|
+
}
|
|
36
|
+
}, 3e5);
|
|
37
|
+
}
|
|
38
|
+
// Component management
|
|
39
|
+
registerComponent(name, component) {
|
|
40
|
+
this.componentRegistry.set(name, component);
|
|
41
|
+
return component;
|
|
42
|
+
}
|
|
43
|
+
getComponent(name) {
|
|
44
|
+
return this.componentRegistry.get(name);
|
|
45
|
+
}
|
|
46
|
+
// Route management
|
|
47
|
+
addRoute(pattern, handler) {
|
|
48
|
+
this.routeRegistry.set(pattern, handler);
|
|
49
|
+
}
|
|
50
|
+
matchRoute(pathname) {
|
|
51
|
+
for (const [pattern, handler] of this.routeRegistry.entries()) {
|
|
52
|
+
const match = this.matchPattern(pattern, pathname);
|
|
53
|
+
if (match) {
|
|
54
|
+
return { handler, params: match.params };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
matchPattern(pattern, pathname) {
|
|
60
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
61
|
+
const pathParts = pathname.split("/").filter(Boolean);
|
|
62
|
+
if (patternParts.length !== pathParts.length) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const params = {};
|
|
66
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
67
|
+
const patternPart = patternParts[i];
|
|
68
|
+
const pathPart = pathParts[i];
|
|
69
|
+
if (patternPart.startsWith(":")) {
|
|
70
|
+
params[patternPart.slice(1)] = pathPart;
|
|
71
|
+
} else if (patternPart !== pathPart) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { params };
|
|
76
|
+
}
|
|
77
|
+
// Rendering
|
|
78
|
+
async renderComponent(component, props = {}, options = {}) {
|
|
79
|
+
try {
|
|
80
|
+
const resolvedComponent = typeof component === "string" ? this.getComponent(component) : component;
|
|
81
|
+
if (!resolvedComponent) {
|
|
82
|
+
throw new Error(`Component not found: ${component}`);
|
|
83
|
+
}
|
|
84
|
+
const cacheKey = this.generateCacheKey(resolvedComponent, props);
|
|
85
|
+
if (this.options.caching && this.cache.has(cacheKey)) {
|
|
86
|
+
const cached = this.cache.get(cacheKey);
|
|
87
|
+
if (Date.now() - cached.timestamp < (options.cacheMaxAge || 3e5)) {
|
|
88
|
+
return cached.html;
|
|
89
|
+
}
|
|
90
|
+
this.cache.delete(cacheKey);
|
|
91
|
+
}
|
|
92
|
+
const vdom = resolvedComponent(props);
|
|
93
|
+
const html = render(vdom);
|
|
94
|
+
if (this.options.caching && options.cacheable !== false) {
|
|
95
|
+
this.cache.set(cacheKey, {
|
|
96
|
+
html,
|
|
97
|
+
timestamp: Date.now()
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
this.renderCount++;
|
|
101
|
+
return html;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error("Node render error:", error);
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
generateCacheKey(component, props) {
|
|
108
|
+
const componentName = component.name || "anonymous";
|
|
109
|
+
const propsHash = this.hashObject(props);
|
|
110
|
+
return `${componentName}-${propsHash}`;
|
|
111
|
+
}
|
|
112
|
+
hashObject(obj) {
|
|
113
|
+
const str = JSON.stringify(obj, Object.keys(obj).sort());
|
|
114
|
+
let hash = 0;
|
|
115
|
+
for (let i = 0; i < str.length; i++) {
|
|
116
|
+
const char = str.charCodeAt(i);
|
|
117
|
+
hash = (hash << 5) - hash + char;
|
|
118
|
+
hash = hash & hash;
|
|
119
|
+
}
|
|
120
|
+
return hash.toString(36);
|
|
121
|
+
}
|
|
122
|
+
// HTTP Request handling (for standalone mode)
|
|
123
|
+
async handleRequest(req, res) {
|
|
124
|
+
try {
|
|
125
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
126
|
+
const pathname = url.pathname;
|
|
127
|
+
const context = {
|
|
128
|
+
req,
|
|
129
|
+
res,
|
|
130
|
+
url,
|
|
131
|
+
pathname,
|
|
132
|
+
params: {},
|
|
133
|
+
query: Object.fromEntries(url.searchParams),
|
|
134
|
+
method: req.method,
|
|
135
|
+
headers: req.headers,
|
|
136
|
+
runtime: this,
|
|
137
|
+
state: {}
|
|
138
|
+
};
|
|
139
|
+
let middlewareIndex = 0;
|
|
140
|
+
const next = async () => {
|
|
141
|
+
if (middlewareIndex < this.middleware.length) {
|
|
142
|
+
const middleware = this.middleware[middlewareIndex++];
|
|
143
|
+
return await middleware(context, next);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
if (this.middleware.length > 0) {
|
|
147
|
+
await next();
|
|
148
|
+
}
|
|
149
|
+
if (res.headersSent) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const match = this.matchRoute(pathname);
|
|
153
|
+
if (!match) {
|
|
154
|
+
res.writeHead(404, { "Content-Type": "text/html" });
|
|
155
|
+
res.end("<h1>404 Not Found</h1>");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
context.params = match.params;
|
|
159
|
+
const result = await match.handler(context);
|
|
160
|
+
if (res.headersSent) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (typeof result === "string") {
|
|
164
|
+
res.writeHead(200, this.options.headers);
|
|
165
|
+
res.end(result);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (result && typeof result === "object") {
|
|
169
|
+
if (result.component) {
|
|
170
|
+
const html = await this.renderComponent(
|
|
171
|
+
result.component,
|
|
172
|
+
result.props || {},
|
|
173
|
+
result.options || {}
|
|
174
|
+
);
|
|
175
|
+
res.writeHead(result.status || 200, {
|
|
176
|
+
...this.options.headers,
|
|
177
|
+
...result.headers
|
|
178
|
+
});
|
|
179
|
+
res.end(html);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (result.json !== void 0) {
|
|
183
|
+
res.writeHead(result.status || 200, {
|
|
184
|
+
"Content-Type": "application/json",
|
|
185
|
+
...result.headers
|
|
186
|
+
});
|
|
187
|
+
res.end(JSON.stringify(result.json));
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
if (result.redirect) {
|
|
191
|
+
res.writeHead(result.status || 302, {
|
|
192
|
+
"Location": result.redirect
|
|
193
|
+
});
|
|
194
|
+
res.end();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
198
|
+
res.end(JSON.stringify(result));
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
res.writeHead(200, this.options.headers);
|
|
202
|
+
res.end(String(result));
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error("Request handling error:", error);
|
|
205
|
+
if (!res.headersSent) {
|
|
206
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
207
|
+
res.end(JSON.stringify({ error: error.message }));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Create app factory
|
|
212
|
+
createApp() {
|
|
213
|
+
const app = {
|
|
214
|
+
// Component registration
|
|
215
|
+
component: (name, component) => this.registerComponent(name, component),
|
|
216
|
+
// Routing
|
|
217
|
+
get: (pattern, handler) => this.addRoute(pattern, handler),
|
|
218
|
+
post: (pattern, handler) => this.addRoute(pattern, handler),
|
|
219
|
+
put: (pattern, handler) => this.addRoute(pattern, handler),
|
|
220
|
+
delete: (pattern, handler) => this.addRoute(pattern, handler),
|
|
221
|
+
route: (pattern, handler) => this.addRoute(pattern, handler),
|
|
222
|
+
// Middleware support
|
|
223
|
+
use: (middleware) => {
|
|
224
|
+
if (typeof middleware !== "function") {
|
|
225
|
+
throw new Error("Middleware must be a function");
|
|
226
|
+
}
|
|
227
|
+
this.middleware.push(middleware);
|
|
228
|
+
return app;
|
|
229
|
+
},
|
|
230
|
+
// Server control
|
|
231
|
+
listen: (port, callback) => {
|
|
232
|
+
const serverPort = port || this.options.port;
|
|
233
|
+
this.server = createServer((req, res) => this.handleRequest(req, res));
|
|
234
|
+
this.server.listen(serverPort, this.options.host, () => {
|
|
235
|
+
console.log(`Coherent.js Node runtime listening on http://${this.options.host}:${serverPort}`);
|
|
236
|
+
if (callback) callback();
|
|
237
|
+
});
|
|
238
|
+
return this.server;
|
|
239
|
+
},
|
|
240
|
+
close: (callback) => {
|
|
241
|
+
if (this.server) {
|
|
242
|
+
this.server.close(callback);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
// Utilities
|
|
246
|
+
render: (component, props, options) => this.renderComponent(component, props, options),
|
|
247
|
+
getRuntime: () => this,
|
|
248
|
+
getStats: () => ({
|
|
249
|
+
renderCount: this.renderCount,
|
|
250
|
+
cacheSize: this.cache.size,
|
|
251
|
+
componentCount: this.componentRegistry.size,
|
|
252
|
+
routeCount: this.routeRegistry.size,
|
|
253
|
+
middlewareCount: this.middleware.length
|
|
254
|
+
})
|
|
255
|
+
};
|
|
256
|
+
return app;
|
|
257
|
+
}
|
|
258
|
+
// Framework integration helpers
|
|
259
|
+
expressMiddleware() {
|
|
260
|
+
return async (req, res, next) => {
|
|
261
|
+
req.coherent = {
|
|
262
|
+
render: async (component, props, options) => {
|
|
263
|
+
const html = await this.renderComponent(component, props, options);
|
|
264
|
+
res.send(html);
|
|
265
|
+
},
|
|
266
|
+
runtime: this
|
|
267
|
+
};
|
|
268
|
+
next();
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
fastifyPlugin() {
|
|
272
|
+
return async (fastify) => {
|
|
273
|
+
fastify.decorate("coherent", {
|
|
274
|
+
render: async (component, props, renderOptions) => {
|
|
275
|
+
return await this.renderComponent(component, props, renderOptions);
|
|
276
|
+
},
|
|
277
|
+
runtime: this
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
koaMiddleware() {
|
|
282
|
+
return async (ctx, next) => {
|
|
283
|
+
ctx.coherent = {
|
|
284
|
+
render: async (component, props, options) => {
|
|
285
|
+
const html = await this.renderComponent(component, props, options);
|
|
286
|
+
ctx.type = "html";
|
|
287
|
+
ctx.body = html;
|
|
288
|
+
},
|
|
289
|
+
runtime: this
|
|
290
|
+
};
|
|
291
|
+
await next();
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
function createNodeRuntime(options = {}) {
|
|
296
|
+
return new NodeRuntime(options);
|
|
297
|
+
}
|
|
298
|
+
var node_default = NodeRuntime;
|
|
299
|
+
export {
|
|
300
|
+
NodeRuntime,
|
|
301
|
+
createNodeRuntime,
|
|
302
|
+
node_default as default
|
|
303
|
+
};
|
|
304
|
+
//# sourceMappingURL=coherent-node.js.map
|