@nmvuong92/fluxe 0.4.0 → 0.5.0
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/lib/core/container.d.ts +9 -0
- package/lib/core/container.js +46 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/server_factory.js +12 -5
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type Factory<T> = (c: Container) => T;
|
|
2
|
+
export interface Container {
|
|
3
|
+
register<T>(token: string, factory: Factory<T>): Container;
|
|
4
|
+
override<T>(token: string, factory: Factory<T>): Container;
|
|
5
|
+
has(token: string): boolean;
|
|
6
|
+
get<T>(token: string): T;
|
|
7
|
+
resolved(): string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function createContainer(): Container;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Copyright (c) 2026 nmvuong92
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/* Resolved Container — DI lười (lazy singleton). Register factory = O(1), KHÔNG instantiate;
|
|
4
|
+
* get() tạo lần đầu rồi memoize. Factory tự c.get(dep) → DI + thứ tự init tự nhiên (DFS) +
|
|
5
|
+
* phát hiện vòng (cycle). resolved() liệt kê token đã tạo → "chỉ module dùng mới bootstrap".
|
|
6
|
+
* DSA: Map provider + Map instance (O(1)); Set "đang giải" (cycle, O(depth)). */
|
|
7
|
+
export function createContainer() {
|
|
8
|
+
const providers = new Map();
|
|
9
|
+
const instances = new Map();
|
|
10
|
+
const resolving = new Set(); // DFS đang giải → bắt cycle
|
|
11
|
+
const c = {
|
|
12
|
+
register(token, factory) {
|
|
13
|
+
if (providers.has(token))
|
|
14
|
+
throw new Error(`Container: token '${token}' đã đăng ký (dùng override để ghi đè)`);
|
|
15
|
+
providers.set(token, factory);
|
|
16
|
+
return c;
|
|
17
|
+
},
|
|
18
|
+
override(token, factory) {
|
|
19
|
+
providers.set(token, factory);
|
|
20
|
+
instances.delete(token); // buộc tạo lại lần get sau
|
|
21
|
+
return c;
|
|
22
|
+
},
|
|
23
|
+
has: (token) => providers.has(token),
|
|
24
|
+
get(token) {
|
|
25
|
+
if (instances.has(token))
|
|
26
|
+
return instances.get(token); // memoized singleton
|
|
27
|
+
const f = providers.get(token);
|
|
28
|
+
if (!f)
|
|
29
|
+
throw new Error(`Container: chưa đăng ký token '${token}'`);
|
|
30
|
+
if (resolving.has(token)) {
|
|
31
|
+
throw new Error(`Container: phụ thuộc vòng (cycle) tại '${token}' — chuỗi: ${[...resolving, token].join(" → ")}`);
|
|
32
|
+
}
|
|
33
|
+
resolving.add(token);
|
|
34
|
+
try {
|
|
35
|
+
const inst = f(c); // factory có thể c.get(dep) → DI lười, thứ tự tự nhiên
|
|
36
|
+
instances.set(token, inst);
|
|
37
|
+
return inst;
|
|
38
|
+
}
|
|
39
|
+
finally {
|
|
40
|
+
resolving.delete(token);
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
resolved: () => [...instances.keys()],
|
|
44
|
+
};
|
|
45
|
+
return c;
|
|
46
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./core/wiring.ts";
|
|
|
6
6
|
export * from "./core/auth.ts";
|
|
7
7
|
export * from "./core/env.ts";
|
|
8
8
|
export * from "./core/config.ts";
|
|
9
|
+
export * from "./core/container.ts";
|
|
9
10
|
export * from "./core/i18n.ts";
|
|
10
11
|
export * from "./core/seo.ts";
|
|
11
12
|
export * from "./core/broker.ts";
|
package/lib/index.js
CHANGED
|
@@ -10,6 +10,7 @@ export * from "./core/wiring.js"; // backendFromManifest, backendsFromManifest
|
|
|
10
10
|
export * from "./core/auth.js"; // session HMAC, scrypt password, CSRF, RBAC
|
|
11
11
|
export * from "./core/env.js"; // loadEnv
|
|
12
12
|
export * from "./core/config.js"; // FluxeConfig, loadConfig (default ← ENV FLUXE_* ← override)
|
|
13
|
+
export * from "./core/container.js"; // createContainer — Resolved Container (DI lười, chỉ-used-bootstrap)
|
|
13
14
|
export * from "./core/i18n.js"; // createI18n, resolveLocale, translate, makeT, t(key, vars)
|
|
14
15
|
export * from "./core/seo.js"; // renderHead, renderSitemap, renderRobots, HeadMeta
|
|
15
16
|
export * from "./core/broker.js"; // pub/sub
|
package/lib/server_factory.js
CHANGED
|
@@ -14,6 +14,7 @@ import { FluxeError, toErrorPayload, renderErrorPage } from "./core/errors.js";
|
|
|
14
14
|
import { signSession, verifySession, parseCookie, hasRole, hashPassword, verifyPassword, newCsrfToken } from "./core/auth.js";
|
|
15
15
|
import { validateInput } from "./core/validate.js";
|
|
16
16
|
import { createBroker } from "./core/broker.js";
|
|
17
|
+
import { createContainer } from "./core/container.js";
|
|
17
18
|
import { createRateLimiter } from "./core/ratelimit.js";
|
|
18
19
|
import { createRecorder } from "./core/observe.js";
|
|
19
20
|
import { createPresence } from "./core/presence.js";
|
|
@@ -116,10 +117,13 @@ export function makeServer(manifest, cells, layouts = {}, opts = {}) {
|
|
|
116
117
|
// Backend GIẢI per-cell từ manifest (Resolution Plane) — cell/frontend giữ nguyên.
|
|
117
118
|
const backends = backendsFromManifest(manifest);
|
|
118
119
|
const backendFor = (id) => backends.byCell.get(id) ?? backends.default;
|
|
119
|
-
|
|
120
|
+
// Resolved Container: service realtime đăng ký LƯỜI — chỉ tạo khi thật sự dùng (SSE/action).
|
|
121
|
+
// App không realtime → broker/presence KHÔNG bao giờ bootstrap. resolved() ở /_fluxe/stats.
|
|
122
|
+
const container = createContainer();
|
|
123
|
+
container.register("broker", () => createBroker());
|
|
124
|
+
container.register("presence", () => createPresence());
|
|
120
125
|
const actionLimit = createRateLimiter(config.rateLimit); // per-IP cho action (FLUXE_RATELIMIT_*)
|
|
121
|
-
const recorder = createRecorder(); // request log (
|
|
122
|
-
const presence = createPresence(); // ai đang online per topic (Trục 4g)
|
|
126
|
+
const recorder = createRecorder(); // request log — chạy mỗi request → eager (luôn dùng)
|
|
123
127
|
const renderCache = createRenderCache({ maxKeys: config.renderCache.maxKeys }); // FLUXE_RENDERCACHE_MAX_KEYS
|
|
124
128
|
let clientJs; // ý A: đọc dist/client.js 1 lần (zero-copy: tái dùng buffer)
|
|
125
129
|
return http.createServer(async (req, res) => {
|
|
@@ -160,7 +164,7 @@ export function makeServer(manifest, cells, layouts = {}, opts = {}) {
|
|
|
160
164
|
const m = process.memoryUsage();
|
|
161
165
|
const c = process.cpuUsage();
|
|
162
166
|
res.writeHead(200, { "content-type": "application/json" });
|
|
163
|
-
return res.end(JSON.stringify({ rss: m.rss, heapUsed: m.heapUsed, cpuUser: c.user, cpuSystem: c.system, uptimeMs: Math.round(process.uptime() * 1000) }));
|
|
167
|
+
return res.end(JSON.stringify({ rss: m.rss, heapUsed: m.heapUsed, cpuUser: c.user, cpuSystem: c.system, uptimeMs: Math.round(process.uptime() * 1000), bootstrapped: container.resolved() }));
|
|
164
168
|
}
|
|
165
169
|
if (url.pathname === "/_fluxe/requests") {
|
|
166
170
|
// Observability: log request gần đây (timing/status). Prod: gate sau auth.
|
|
@@ -246,6 +250,9 @@ export function makeServer(manifest, cells, layouts = {}, opts = {}) {
|
|
|
246
250
|
const id = url.searchParams.get("id");
|
|
247
251
|
res.writeHead(200, { "content-type": "text/event-stream", "cache-control": "no-cache", connection: "keep-alive" });
|
|
248
252
|
res.write(`event: ready\ndata: {"topic":"${topic}"}\n\n`);
|
|
253
|
+
// Lần đầu có client SSE → broker + presence mới bootstrap (lazy qua container).
|
|
254
|
+
const broker = container.get("broker");
|
|
255
|
+
const presence = container.get("presence");
|
|
249
256
|
const offBroker = broker.subscribe(topic, (data) => res.write(`data: ${JSON.stringify(data)}\n\n`));
|
|
250
257
|
let offPresence = () => { };
|
|
251
258
|
if (id) {
|
|
@@ -301,7 +308,7 @@ export function makeServer(manifest, cells, layouts = {}, opts = {}) {
|
|
|
301
308
|
if (schema)
|
|
302
309
|
input = validateInput(schema, input); // sai → FluxeError 400 (caught)
|
|
303
310
|
const out = await fn({ input, backend, session });
|
|
304
|
-
broker.publish(cellId, { action: name, out }); // realtime: báo client khác
|
|
311
|
+
container.get("broker").publish(cellId, { action: name, out }); // realtime: báo client khác (broker lazy)
|
|
305
312
|
res.writeHead(200, { "content-type": "application/json", "x-fluxe-resolution": resolution, "x-fluxe-server-ms": String(Date.now() - t0) });
|
|
306
313
|
return res.end(JSON.stringify(out));
|
|
307
314
|
}
|