@nmvuong92/fluxe 0.3.0 → 0.4.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/cli.js CHANGED
@@ -17,6 +17,10 @@ export const COMMANDS = {
17
17
  desc: "Auto-discovery: quét app/cells/* → app/app.ts (dev/resolve tự gọi)",
18
18
  shell: () => SYNC,
19
19
  },
20
+ config: {
21
+ desc: "In config đã giải (default ← ENV FLUXE_* ← override)",
22
+ shell: () => `tsx scripts/config.ts`,
23
+ },
20
24
  gen: {
21
25
  desc: "Codegen contract → types TS/Go/Rust (.fluxe/gen)",
22
26
  shell: () => `tsx scripts/codegen.ts`,
@@ -0,0 +1,98 @@
1
+ import { z } from "zod";
2
+ declare const Schema: z.ZodObject<{
3
+ env: z.ZodEnum<["development", "production", "test"]>;
4
+ secret: z.ZodString;
5
+ port: z.ZodNumber;
6
+ defaultBackend: z.ZodEnum<["memory", "go", "rust"]>;
7
+ rateLimit: z.ZodObject<{
8
+ capacity: z.ZodNumber;
9
+ refillPerSec: z.ZodNumber;
10
+ maxKeys: z.ZodNumber;
11
+ }, "strip", z.ZodTypeAny, {
12
+ capacity: number;
13
+ refillPerSec: number;
14
+ maxKeys: number;
15
+ }, {
16
+ capacity: number;
17
+ refillPerSec: number;
18
+ maxKeys: number;
19
+ }>;
20
+ renderCache: z.ZodObject<{
21
+ maxKeys: z.ZodNumber;
22
+ }, "strip", z.ZodTypeAny, {
23
+ maxKeys: number;
24
+ }, {
25
+ maxKeys: number;
26
+ }>;
27
+ upload: z.ZodObject<{
28
+ maxBytes: z.ZodNumber;
29
+ }, "strip", z.ZodTypeAny, {
30
+ maxBytes: number;
31
+ }, {
32
+ maxBytes: number;
33
+ }>;
34
+ i18n: z.ZodObject<{
35
+ defaultLocale: z.ZodString;
36
+ }, "strip", z.ZodTypeAny, {
37
+ defaultLocale: string;
38
+ }, {
39
+ defaultLocale: string;
40
+ }>;
41
+ }, "strip", z.ZodTypeAny, {
42
+ env: "development" | "production" | "test";
43
+ secret: string;
44
+ port: number;
45
+ defaultBackend: "memory" | "go" | "rust";
46
+ rateLimit: {
47
+ capacity: number;
48
+ refillPerSec: number;
49
+ maxKeys: number;
50
+ };
51
+ renderCache: {
52
+ maxKeys: number;
53
+ };
54
+ upload: {
55
+ maxBytes: number;
56
+ };
57
+ i18n: {
58
+ defaultLocale: string;
59
+ };
60
+ }, {
61
+ env: "development" | "production" | "test";
62
+ secret: string;
63
+ port: number;
64
+ defaultBackend: "memory" | "go" | "rust";
65
+ rateLimit: {
66
+ capacity: number;
67
+ refillPerSec: number;
68
+ maxKeys: number;
69
+ };
70
+ renderCache: {
71
+ maxKeys: number;
72
+ };
73
+ upload: {
74
+ maxBytes: number;
75
+ };
76
+ i18n: {
77
+ defaultLocale: string;
78
+ };
79
+ }>;
80
+ export type FluxeConfig = z.infer<typeof Schema>;
81
+ type Src = Record<string, string | undefined>;
82
+ export declare const ENV_KEYS: {
83
+ readonly NODE_ENV: "env";
84
+ readonly FLUXE_SECRET: "secret";
85
+ readonly "PORT (ho\u1EB7c FLUXE_PORT)": "port";
86
+ readonly FLUXE_BACKEND: "defaultBackend";
87
+ readonly FLUXE_RATELIMIT_CAPACITY: "rateLimit.capacity";
88
+ readonly FLUXE_RATELIMIT_REFILL: "rateLimit.refillPerSec";
89
+ readonly FLUXE_RATELIMIT_MAX_KEYS: "rateLimit.maxKeys";
90
+ readonly FLUXE_RENDERCACHE_MAX_KEYS: "renderCache.maxKeys";
91
+ readonly FLUXE_UPLOAD_MAX_BYTES: "upload.maxBytes";
92
+ readonly FLUXE_LOCALE_DEFAULT: "i18n.defaultLocale";
93
+ };
94
+ type DeepPartial<T> = {
95
+ [K in keyof T]?: T[K] extends object ? Partial<T[K]> : T[K];
96
+ };
97
+ export declare function loadConfig(source?: Src, overrides?: DeepPartial<FluxeConfig>): FluxeConfig;
98
+ export {};
@@ -0,0 +1,66 @@
1
+ // Copyright (c) 2026 nmvuong92
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /* Config core — DEFAULT + override qua ENV (quy ước FLUXE_*), kiểu Laravel config().
4
+ * Một nguồn sự thật cho mọi tham số tinh chỉnh của engine. Thứ tự ưu tiên:
5
+ * default < ENV (FLUXE_*) < override truyền tay.
6
+ * Mọi giá trị quan trọng ĐỀU expose ra ENV + tài liệu (xem docs/reference/configuration). */
7
+ import { z } from "zod";
8
+ const Schema = z.object({
9
+ env: z.enum(["development", "production", "test"]),
10
+ secret: z.string().min(8),
11
+ port: z.coerce.number().int().positive(),
12
+ defaultBackend: z.enum(["memory", "go", "rust"]),
13
+ rateLimit: z.object({
14
+ capacity: z.coerce.number().int().positive(),
15
+ refillPerSec: z.coerce.number().positive(),
16
+ maxKeys: z.coerce.number().int().positive(),
17
+ }),
18
+ renderCache: z.object({ maxKeys: z.coerce.number().int().positive() }),
19
+ upload: z.object({ maxBytes: z.coerce.number().int().positive() }),
20
+ i18n: z.object({ defaultLocale: z.string().min(2) }),
21
+ });
22
+ /* Bảng ENV — TÊN biến ↔ field. Dùng cho loadConfig + sinh tài liệu. */
23
+ export const ENV_KEYS = {
24
+ "NODE_ENV": "env",
25
+ "FLUXE_SECRET": "secret",
26
+ "PORT (hoặc FLUXE_PORT)": "port",
27
+ "FLUXE_BACKEND": "defaultBackend",
28
+ "FLUXE_RATELIMIT_CAPACITY": "rateLimit.capacity",
29
+ "FLUXE_RATELIMIT_REFILL": "rateLimit.refillPerSec",
30
+ "FLUXE_RATELIMIT_MAX_KEYS": "rateLimit.maxKeys",
31
+ "FLUXE_RENDERCACHE_MAX_KEYS": "renderCache.maxKeys",
32
+ "FLUXE_UPLOAD_MAX_BYTES": "upload.maxBytes",
33
+ "FLUXE_LOCALE_DEFAULT": "i18n.defaultLocale",
34
+ };
35
+ const num = (s, k, d) => {
36
+ const v = s[k];
37
+ return v == null || v === "" ? d : Number(v);
38
+ };
39
+ function merge(base, o) {
40
+ return {
41
+ ...base,
42
+ ...o,
43
+ rateLimit: { ...base.rateLimit, ...o.rateLimit },
44
+ renderCache: { ...base.renderCache, ...o.renderCache },
45
+ upload: { ...base.upload, ...o.upload },
46
+ i18n: { ...base.i18n, ...o.i18n },
47
+ };
48
+ }
49
+ /* Giải config: default ← ENV (FLUXE_*) ← override. Validate Zod (sai → ném ngay, fail-fast). */
50
+ export function loadConfig(source = process.env, overrides = {}) {
51
+ const fromEnv = {
52
+ env: source.NODE_ENV || "development",
53
+ secret: source.FLUXE_SECRET || "dev-secret-change-me",
54
+ port: num(source, "PORT", num(source, "FLUXE_PORT", 5180)),
55
+ defaultBackend: source.FLUXE_BACKEND || "memory",
56
+ rateLimit: {
57
+ capacity: num(source, "FLUXE_RATELIMIT_CAPACITY", 30),
58
+ refillPerSec: num(source, "FLUXE_RATELIMIT_REFILL", 10),
59
+ maxKeys: num(source, "FLUXE_RATELIMIT_MAX_KEYS", 5000),
60
+ },
61
+ renderCache: { maxKeys: num(source, "FLUXE_RENDERCACHE_MAX_KEYS", 256) },
62
+ upload: { maxBytes: num(source, "FLUXE_UPLOAD_MAX_BYTES", 10 * 1024 * 1024) },
63
+ i18n: { defaultLocale: source.FLUXE_LOCALE_DEFAULT || "en" },
64
+ };
65
+ return Schema.parse(merge(fromEnv, overrides));
66
+ }
package/lib/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from "./core/resolver.ts";
5
5
  export * from "./core/wiring.ts";
6
6
  export * from "./core/auth.ts";
7
7
  export * from "./core/env.ts";
8
+ export * from "./core/config.ts";
8
9
  export * from "./core/i18n.ts";
9
10
  export * from "./core/seo.ts";
10
11
  export * from "./core/broker.ts";
package/lib/index.js CHANGED
@@ -9,6 +9,7 @@ export * from "./core/resolver.js"; // resolve, ResolutionProfile/Manifest, Cell
9
9
  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
+ export * from "./core/config.js"; // FluxeConfig, loadConfig (default ← ENV FLUXE_* ← override)
12
13
  export * from "./core/i18n.js"; // createI18n, resolveLocale, translate, makeT, t(key, vars)
13
14
  export * from "./core/seo.js"; // renderHead, renderSitemap, renderRobots, HeadMeta
14
15
  export * from "./core/broker.js"; // pub/sub
@@ -10,9 +10,10 @@ type LayoutEntry = LayoutMeta & {
10
10
  type LayoutMap = Record<string, LayoutEntry>;
11
11
  import { type I18n } from "./core/i18n.ts";
12
12
  import { type Storage } from "./storage/types.ts";
13
+ import { type FluxeConfig } from "./core/config.ts";
13
14
  export declare function makeServer(manifest: ResolutionManifest, cells: CellDef<any, any>[], layouts?: LayoutMap, opts?: {
14
15
  i18n?: I18n;
15
16
  storage?: Storage;
16
- maxUpload?: number;
17
+ config?: FluxeConfig;
17
18
  }): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
18
19
  export {};
@@ -23,6 +23,7 @@ import { parseChaos } from "./core/chaos.js";
23
23
  import { resolveLocale, makeT } from "./core/i18n.js";
24
24
  import { parseMultipart, boundaryFromContentType } from "./core/multipart.js";
25
25
  import { makeKey } from "./storage/types.js";
26
+ import { loadConfig } from "./core/config.js";
26
27
  import { createMemoryBackend } from "./backends/memory.js";
27
28
  import { createHttpBackend } from "./backends/http.js";
28
29
  // Build backend theo ngôn ngữ (cho live swap trong devtools).
@@ -92,7 +93,8 @@ function renderBodyToString(node) {
92
93
  export function makeServer(manifest, cells, layouts = {}, opts = {}) {
93
94
  const i18n = opts.i18n;
94
95
  const storage = opts.storage;
95
- const MAX_UPLOAD = opts.maxUpload ?? 10 * 1024 * 1024; // 10MB mặc định
96
+ const config = opts.config ?? loadConfig(); // default ENV (FLUXE_*) override
97
+ const MAX_UPLOAD = config.upload.maxBytes;
96
98
  const readBodyBuffer = (req) => new Promise((resolve, reject) => {
97
99
  const chunks = [];
98
100
  let size = 0;
@@ -115,10 +117,10 @@ export function makeServer(manifest, cells, layouts = {}, opts = {}) {
115
117
  const backends = backendsFromManifest(manifest);
116
118
  const backendFor = (id) => backends.byCell.get(id) ?? backends.default;
117
119
  const broker = createBroker(); // realtime pub/sub (Trục 4g, bản 1-node)
118
- const actionLimit = createRateLimiter({ capacity: 30, refillPerSec: 10 }); // per-IP cho action
120
+ const actionLimit = createRateLimiter(config.rateLimit); // per-IP cho action (FLUXE_RATELIMIT_*)
119
121
  const recorder = createRecorder(); // request log (observability)
120
122
  const presence = createPresence(); // ai đang online per topic (Trục 4g)
121
- const renderCache = createRenderCache({ maxKeys: 256 }); // memoize HTML cell static (key route, gate etag)
123
+ const renderCache = createRenderCache({ maxKeys: config.renderCache.maxKeys }); // FLUXE_RENDERCACHE_MAX_KEYS
122
124
  let clientJs; // ý A: đọc dist/client.js 1 lần (zero-copy: tái dùng buffer)
123
125
  return http.createServer(async (req, res) => {
124
126
  const url = new URL(req.url, "http://localhost");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nmvuong92/fluxe",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "fluxe — khung fullstack tối giản polyglot (RCA: Resolved Cell Architecture).",
5
5
  "license": "Apache-2.0",
6
6
  "author": "nmvuong92",