@noxfly/noxus 3.0.0-dev.3 → 3.0.0-dev.5

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.
Files changed (57) hide show
  1. package/README.md +114 -7
  2. package/dist/child.d.mts +7 -1
  3. package/dist/child.d.ts +7 -1
  4. package/dist/child.js +402 -862
  5. package/dist/child.js.map +1 -0
  6. package/dist/child.mjs +389 -850
  7. package/dist/child.mjs.map +1 -0
  8. package/dist/main.d.mts +171 -125
  9. package/dist/main.d.ts +171 -125
  10. package/dist/main.js +967 -886
  11. package/dist/main.js.map +1 -0
  12. package/dist/main.mjs +914 -834
  13. package/dist/main.mjs.map +1 -0
  14. package/dist/preload.js.map +1 -0
  15. package/dist/preload.mjs.map +1 -0
  16. package/dist/renderer.d.mts +17 -2
  17. package/dist/renderer.d.ts +17 -2
  18. package/dist/renderer.js +161 -118
  19. package/dist/renderer.js.map +1 -0
  20. package/dist/renderer.mjs +150 -106
  21. package/dist/renderer.mjs.map +1 -0
  22. package/package.json +10 -9
  23. package/.editorconfig +0 -16
  24. package/.github/copilot-instructions.md +0 -32
  25. package/.vscode/settings.json +0 -3
  26. package/eslint.config.js +0 -109
  27. package/scripts/postbuild.js +0 -31
  28. package/src/DI/app-injector.ts +0 -160
  29. package/src/DI/injector-explorer.ts +0 -143
  30. package/src/DI/token.ts +0 -53
  31. package/src/decorators/controller.decorator.ts +0 -58
  32. package/src/decorators/guards.decorator.ts +0 -15
  33. package/src/decorators/injectable.decorator.ts +0 -81
  34. package/src/decorators/method.decorator.ts +0 -66
  35. package/src/decorators/middleware.decorator.ts +0 -15
  36. package/src/index.ts +0 -10
  37. package/src/internal/app.ts +0 -217
  38. package/src/internal/bootstrap.ts +0 -109
  39. package/src/internal/exceptions.ts +0 -57
  40. package/src/internal/preload-bridge.ts +0 -75
  41. package/src/internal/renderer-client.ts +0 -338
  42. package/src/internal/renderer-events.ts +0 -110
  43. package/src/internal/request.ts +0 -97
  44. package/src/internal/router.ts +0 -353
  45. package/src/internal/routes.ts +0 -78
  46. package/src/internal/socket.ts +0 -73
  47. package/src/main.ts +0 -26
  48. package/src/non-electron-process.ts +0 -22
  49. package/src/preload.ts +0 -10
  50. package/src/renderer.ts +0 -13
  51. package/src/utils/forward-ref.ts +0 -31
  52. package/src/utils/logger.ts +0 -430
  53. package/src/utils/radix-tree.ts +0 -210
  54. package/src/utils/types.ts +0 -21
  55. package/src/window/window-manager.ts +0 -268
  56. package/tsconfig.json +0 -29
  57. package/tsup.config.ts +0 -50
@@ -1,160 +0,0 @@
1
- /**
2
- * @copyright 2025 NoxFly
3
- * @license MIT
4
- * @author NoxFly
5
- */
6
-
7
- import { ForwardReference } from '../utils/forward-ref';
8
- import { Type } from '../utils/types';
9
- import { Token, TokenKey } from './token';
10
-
11
- /**
12
- * Lifetime of a binding in the DI container.
13
- * - singleton: created once, shared for the lifetime of the app.
14
- * - scope: created once per request scope.
15
- * - transient: new instance every time it is resolved.
16
- */
17
- export type Lifetime = 'singleton' | 'scope' | 'transient';
18
-
19
- /**
20
- * Internal representation of a registered binding.
21
- */
22
- export interface IBinding<T = unknown> {
23
- lifetime: Lifetime;
24
- implementation: Type<T>;
25
- /** Explicit constructor dependencies, declared by the class itself. */
26
- deps: ReadonlyArray<TokenKey>;
27
- instance?: T;
28
- }
29
-
30
- function keyOf<T>(k: TokenKey<T>): Type<T> | Token<T> {
31
- return k;
32
- }
33
-
34
- /**
35
- * AppInjector is the core DI container.
36
- * It no longer uses reflect-metadata — all dependency information
37
- * comes from explicitly declared `deps` arrays on each binding.
38
- */
39
- export class AppInjector {
40
- public readonly bindings = new Map<Type<unknown> | Token<unknown>, IBinding<unknown>>();
41
- public readonly singletons = new Map<Type<unknown> | Token<unknown>, unknown>();
42
- public readonly scoped = new Map<Type<unknown> | Token<unknown>, unknown>();
43
-
44
- constructor(public readonly name: string | null = null) {}
45
-
46
- /**
47
- * Creates a child scope for per-request lifetime resolution.
48
- */
49
- public createScope(): AppInjector {
50
- const scope = new AppInjector();
51
- (scope as any).bindings = this.bindings;
52
- (scope as any).singletons = this.singletons;
53
- return scope;
54
- }
55
-
56
- /**
57
- * Registers a binding explicitly.
58
- */
59
- public register<T>(
60
- key: TokenKey<T>,
61
- implementation: Type<T>,
62
- lifetime: Lifetime,
63
- deps: ReadonlyArray<TokenKey> = [],
64
- ): void {
65
- const k = keyOf(key) as TokenKey<unknown>;
66
- if (!this.bindings.has(k)) {
67
- this.bindings.set(k, { lifetime, implementation: implementation as Type<unknown>, deps });
68
- }
69
- }
70
-
71
- /**
72
- * Resolves a dependency by token or class reference.
73
- */
74
- public resolve<T>(target: TokenKey<T> | ForwardReference<T>): T {
75
- if (target instanceof ForwardReference) {
76
- return this._resolveForwardRef(target);
77
- }
78
-
79
- const k = keyOf(target) as TokenKey<unknown>;
80
-
81
- if (this.singletons.has(k)) {
82
- return this.singletons.get(k) as T;
83
- }
84
-
85
- const binding = this.bindings.get(k);
86
-
87
- if (!binding) {
88
- const name = target instanceof Token
89
- ? target.description
90
- : (target as Type<unknown>).name
91
- ?? 'unknown';
92
-
93
- throw new Error(
94
- `[Noxus DI] No binding found for "${name}".\n`
95
- + `Did you forget to declare it in @Injectable({ deps }) or in bootstrapApplication({ singletons })?`,
96
- );
97
- }
98
-
99
- switch (binding.lifetime) {
100
- case 'transient':
101
- return this._instantiate(binding) as T;
102
-
103
- case 'scope': {
104
- if (this.scoped.has(k)) return this.scoped.get(k) as T;
105
- const inst = this._instantiate(binding);
106
- this.scoped.set(k, inst);
107
- return inst as T;
108
- }
109
-
110
- case 'singleton': {
111
- if (this.singletons.has(k)) return this.singletons.get(k) as T;
112
- const inst = this._instantiate(binding);
113
- this.singletons.set(k, inst);
114
- if (binding.instance === undefined) {
115
- (binding as IBinding<unknown>).instance = inst as unknown;
116
- }
117
- return inst as T;
118
- }
119
- }
120
- }
121
-
122
- // -------------------------------------------------------------------------
123
-
124
- private _resolveForwardRef<T>(ref: ForwardReference<T>): T {
125
- return new Proxy({} as object, {
126
- get: (_obj, prop, receiver) => {
127
- const realType = ref.forwardRefFn();
128
- const instance = this.resolve(realType) as Record<string | symbol, unknown>;
129
- const value = Reflect.get(instance, prop, receiver);
130
- return typeof value === 'function' ? (value as Function).bind(instance) : value;
131
- },
132
- set: (_obj, prop, value, receiver) => {
133
- const realType = ref.forwardRefFn();
134
- const instance = this.resolve(realType) as object;
135
- return Reflect.set(instance, prop, value, receiver);
136
- },
137
- getPrototypeOf: () => {
138
- const realType = ref.forwardRefFn();
139
- return (realType as unknown as { prototype: object }).prototype;
140
- },
141
- }) as T;
142
- }
143
-
144
- private _instantiate<T>(binding: IBinding<T>): T {
145
- const resolvedDeps = binding.deps.map((dep) => this.resolve(dep));
146
- return new binding.implementation(...resolvedDeps) as T;
147
- }
148
- }
149
-
150
- /**
151
- * The global root injector. All singletons live here.
152
- */
153
- export const RootInjector = new AppInjector('root');
154
-
155
- /**
156
- * Convenience function: resolve a token from the root injector.
157
- */
158
- export function inject<T>(t: TokenKey<T> | ForwardReference<T>): T {
159
- return RootInjector.resolve(t);
160
- }
@@ -1,143 +0,0 @@
1
- /**
2
- * @copyright 2025 NoxFly
3
- * @license MIT
4
- * @author NoxFly
5
- */
6
-
7
- import { Lifetime, RootInjector } from './app-injector';
8
- import { TokenKey } from './token';
9
- import { Type } from '../utils/types';
10
- import { Logger } from '../utils/logger';
11
- import { Guard, Middleware } from "src/main";
12
-
13
- export interface PendingRegistration {
14
- key: TokenKey;
15
- implementation: Type<unknown>;
16
- lifetime: Lifetime;
17
- deps: ReadonlyArray<TokenKey>;
18
- isController: boolean;
19
- pathPrefix?: string;
20
- }
21
-
22
- /**
23
- * InjectorExplorer accumulates registrations emitted by decorators
24
- * at import time, then flushes them in two phases (bind → resolve)
25
- * once bootstrapApplication triggers processing.
26
- *
27
- * Because deps are now explicit arrays (no reflect-metadata), this class
28
- * no longer needs to introspect constructor parameter types.
29
- */
30
- export class InjectorExplorer {
31
- private static readonly pending: PendingRegistration[] = [];
32
- private static processed = false;
33
- private static accumulating = false;
34
-
35
- // -------------------------------------------------------------------------
36
- // Public API
37
- // -------------------------------------------------------------------------
38
-
39
- public static enqueue(reg: PendingRegistration): void {
40
- if (InjectorExplorer.processed && !InjectorExplorer.accumulating) {
41
- InjectorExplorer._registerImmediate(reg);
42
- return;
43
- }
44
- InjectorExplorer.pending.push(reg);
45
- }
46
-
47
- /**
48
- * Two-phase flush of all pending registrations collected at startup.
49
- * Called by bootstrapApplication after app.whenReady().
50
- */
51
- public static processPending(singletonOverrides?: Map<TokenKey, unknown>): void {
52
- const queue = [...InjectorExplorer.pending];
53
- InjectorExplorer.pending.length = 0;
54
-
55
- InjectorExplorer._phaseOne(queue);
56
- InjectorExplorer._phaseTwo(queue, singletonOverrides);
57
-
58
- InjectorExplorer.processed = true;
59
- }
60
-
61
- /** Enters accumulation mode for lazy-loaded batches. */
62
- public static beginAccumulate(): void {
63
- InjectorExplorer.accumulating = true;
64
- }
65
-
66
- /**
67
- * Exits accumulation mode and flushes queued registrations
68
- * with the same two-phase guarantee as processPending.
69
- */
70
- public static flushAccumulated(
71
- routeGuards: Guard[] = [],
72
- routeMiddlewares: Middleware[] = [],
73
- pathPrefix = '',
74
- ): void {
75
- InjectorExplorer.accumulating = false;
76
- const queue = [...InjectorExplorer.pending];
77
- InjectorExplorer.pending.length = 0;
78
- InjectorExplorer._phaseOne(queue);
79
-
80
- // Stamp the path prefix on controller registrations
81
- for (const reg of queue) {
82
- if (reg.isController) reg.pathPrefix = pathPrefix;
83
- }
84
-
85
- InjectorExplorer._phaseTwo(queue, undefined, routeGuards, routeMiddlewares);
86
- }
87
-
88
- // -------------------------------------------------------------------------
89
- // Private helpers
90
- // -------------------------------------------------------------------------
91
-
92
- /** Phase 1: register all bindings without instantiating anything. */
93
- private static _phaseOne(queue: PendingRegistration[]): void {
94
- for (const reg of queue) {
95
- RootInjector.register(reg.key, reg.implementation, reg.lifetime, reg.deps);
96
- }
97
- }
98
-
99
- /** Phase 2: resolve singletons and register controllers in the router. */
100
- private static _phaseTwo(
101
- queue: PendingRegistration[],
102
- overrides?: Map<TokenKey, unknown>,
103
- routeGuards: Guard[] = [],
104
- routeMiddlewares: Middleware[] = [],
105
- ): void {
106
- for (const reg of queue) {
107
- // Apply value overrides (e.g. singleton instances provided via bootstrapApplication config)
108
- if (overrides?.has(reg.key)) {
109
- const override = overrides.get(reg.key);
110
- RootInjector.singletons.set(reg.key as any, override);
111
- Logger.log(`Registered ${reg.implementation.name} as singleton (overridden)`);
112
- continue;
113
- }
114
-
115
- if (reg.lifetime === 'singleton') {
116
- RootInjector.resolve(reg.key);
117
- }
118
-
119
- if (reg.isController) {
120
- // Lazily import Router to avoid circular dependency at module load time
121
- const { Router } = require('../internal/router') as { Router: { prototype: { registerController(t: Type<unknown>): void } } };
122
- const router = RootInjector.resolve(Router as any) as { registerController(t: Type<unknown>, pathPrefix: string, routeGuards: Guard[], routeMiddlewares: Middleware[]): void };
123
- router.registerController(reg.implementation, reg.pathPrefix ?? '', routeGuards, routeMiddlewares);
124
- } else if (reg.lifetime !== 'singleton') {
125
- Logger.log(`Registered ${reg.implementation.name} as ${reg.lifetime}`);
126
- }
127
- }
128
- }
129
-
130
- private static _registerImmediate(reg: PendingRegistration): void {
131
- RootInjector.register(reg.key, reg.implementation, reg.lifetime, reg.deps);
132
-
133
- if (reg.lifetime === 'singleton') {
134
- RootInjector.resolve(reg.key);
135
- }
136
-
137
- if (reg.isController) {
138
- const { Router } = require('../internal/router') as { Router: { prototype: { registerController(t: Type<unknown>): void } } };
139
- const router = RootInjector.resolve(Router as any) as { registerController(t: Type<unknown>): void };
140
- router.registerController(reg.implementation);
141
- }
142
- }
143
- }
package/src/DI/token.ts DELETED
@@ -1,53 +0,0 @@
1
- /**
2
- * @copyright 2025 NoxFly
3
- * @license MIT
4
- * @author NoxFly
5
- */
6
-
7
- import { Type } from '../utils/types';
8
-
9
- /**
10
- * A DI token uniquely identifies a dependency.
11
- * It can wrap a class (Type<T>) or be a named symbol token.
12
- *
13
- * Using tokens instead of reflect-metadata means dependencies are
14
- * declared explicitly — no magic type inference, no emitDecoratorMetadata.
15
- *
16
- * @example
17
- * // Class token (most common)
18
- * const MY_SERVICE = token(MyService);
19
- *
20
- * // Named symbol token (for interfaces or non-class values)
21
- * const DB_URL = token<string>('DB_URL');
22
- */
23
- export class Token<T> {
24
- public readonly description: string;
25
-
26
- constructor(
27
- public readonly target: Type<T> | string,
28
- ) {
29
- this.description = typeof target === 'string' ? target : target.name;
30
- }
31
-
32
- public toString(): string {
33
- return `Token(${this.description})`;
34
- }
35
- }
36
-
37
- /**
38
- * Creates a DI token for a class type or a named value.
39
- *
40
- * @example
41
- * export const MY_SERVICE = token(MyService);
42
- * export const DB_URL = token<string>('DB_URL');
43
- */
44
- export function token<T>(target: Type<T> | string): Token<T> {
45
- return new Token<T>(target);
46
- }
47
-
48
- /**
49
- * The key used to look up a class token in the registry.
50
- * For class tokens, the key is the class constructor itself.
51
- * For named tokens, the key is the Token instance.
52
- */
53
- export type TokenKey<T = unknown> = Type<T> | Token<T>;
@@ -1,58 +0,0 @@
1
- /**
2
- * @copyright 2025 NoxFly
3
- * @license MIT
4
- * @author NoxFly
5
- */
6
-
7
- import { InjectorExplorer } from '../DI/injector-explorer';
8
- import { TokenKey } from '../DI/token';
9
- import { Type } from '../utils/types';
10
-
11
- export interface ControllerOptions {
12
- /**
13
- * Explicit constructor dependencies.
14
- */
15
- deps?: ReadonlyArray<TokenKey>;
16
- }
17
-
18
- export interface IControllerMetadata {
19
- deps: ReadonlyArray<TokenKey>;
20
- }
21
-
22
- const controllerMetaMap = new WeakMap<object, IControllerMetadata>();
23
-
24
- /**
25
- * Marks a class as a Noxus controller.
26
- * Controllers are always scope-scoped injectables.
27
- * The route prefix and guards/middlewares are declared in defineRoutes(), not here.
28
- *
29
- * @example
30
- * @Controller({ deps: [UserService] })
31
- * export class UserController {
32
- * constructor(private svc: UserService) {}
33
- *
34
- * @Get('byId/:userId')
35
- * getUserById(req: Request) { ... }
36
- * }
37
- */
38
- export function Controller(options: ControllerOptions = {}): ClassDecorator {
39
- return (target) => {
40
- const meta: IControllerMetadata = {
41
- deps: options.deps ?? [],
42
- };
43
-
44
- controllerMetaMap.set(target, meta);
45
-
46
- InjectorExplorer.enqueue({
47
- key: target as unknown as Type<unknown>,
48
- implementation: target as unknown as Type<unknown>,
49
- lifetime: 'scope',
50
- deps: options.deps ?? [],
51
- isController: true,
52
- });
53
- };
54
- }
55
-
56
- export function getControllerMetadata(target: object): IControllerMetadata | undefined {
57
- return controllerMetaMap.get(target);
58
- }
@@ -1,15 +0,0 @@
1
- /**
2
- * @copyright 2025 NoxFly
3
- * @license MIT
4
- * @author NoxFly
5
- */
6
-
7
- import { Request } from '../internal/request';
8
- import { MaybeAsync } from '../utils/types';
9
-
10
- /**
11
- * A guard decides whether an incoming request should reach the handler.
12
- * Implement this interface and pass the class to @Controller({ guards }) or @Get('path', { guards }).
13
- */
14
-
15
- export type Guard = (request: Request) => MaybeAsync<boolean>;
@@ -1,81 +0,0 @@
1
- /**
2
- * @copyright 2025 NoxFly
3
- * @license MIT
4
- * @author NoxFly
5
- */
6
-
7
- import { Lifetime } from '../DI/app-injector';
8
- import { InjectorExplorer } from '../DI/injector-explorer';
9
- import { Token, TokenKey } from '../DI/token';
10
- import { Type } from '../utils/types';
11
-
12
- export interface InjectableOptions {
13
- /**
14
- * Lifetime of this injectable.
15
- * @default 'scope'
16
- */
17
- lifetime?: Lifetime;
18
-
19
- /**
20
- * Explicit list of constructor dependencies, in the same order as the constructor parameters.
21
- * Each entry is either a class constructor or a Token created with token().
22
- *
23
- * This replaces reflect-metadata / emitDecoratorMetadata entirely.
24
- *
25
- * @example
26
- * @Injectable({ lifetime: 'singleton', deps: [MyRepo, DB_URL] })
27
- * class MyService {
28
- * constructor(private repo: MyRepo, private dbUrl: string) {}
29
- * }
30
- */
31
- deps?: ReadonlyArray<TokenKey>;
32
- }
33
-
34
- /**
35
- * Marks a class as injectable into the Noxus DI container.
36
- *
37
- * Unlike the v2 @Injectable, this decorator:
38
- * - Does NOT require reflect-metadata or emitDecoratorMetadata.
39
- * - Requires you to declare deps explicitly when the class has constructor parameters.
40
- * - Supports standalone usage — no module declaration needed.
41
- *
42
- * @example
43
- * // No dependencies
44
- * @Injectable()
45
- * class Logger {}
46
- *
47
- * // With dependencies
48
- * @Injectable({ lifetime: 'singleton', deps: [Logger, MyRepo] })
49
- * class MyService {
50
- * constructor(private logger: Logger, private repo: MyRepo) {}
51
- * }
52
- *
53
- * // With a named token
54
- * const DB_URL = token<string>('DB_URL');
55
- *
56
- * @Injectable({ deps: [DB_URL] })
57
- * class DbService {
58
- * constructor(private url: string) {}
59
- * }
60
- */
61
- export function Injectable(options: InjectableOptions = {}): ClassDecorator {
62
- const { lifetime = 'scope', deps = [] } = options;
63
-
64
- return (target) => {
65
- if (typeof target !== 'function' || !target.prototype) {
66
- throw new Error(`@Injectable can only be applied to classes, not ${typeof target}`);
67
- }
68
-
69
- const key = target as unknown as Type<unknown>;
70
-
71
- InjectorExplorer.enqueue({
72
- key,
73
- implementation: key,
74
- lifetime,
75
- deps,
76
- isController: false,
77
- });
78
- };
79
- }
80
-
81
- export { Token, TokenKey };
@@ -1,66 +0,0 @@
1
- /**
2
- * @copyright 2025 NoxFly
3
- * @license MIT
4
- * @author NoxFly
5
- */
6
-
7
- import { Guard } from './guards.decorator';
8
- import { Middleware } from './middleware.decorator';
9
-
10
- export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'BATCH';
11
- export type AtomicHttpMethod = Exclude<HttpMethod, 'BATCH'>;
12
-
13
- const ATOMIC_METHODS = new Set<AtomicHttpMethod>(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
14
- export function isAtomicHttpMethod(m: unknown): m is AtomicHttpMethod {
15
- return typeof m === 'string' && ATOMIC_METHODS.has(m as AtomicHttpMethod);
16
- }
17
-
18
- export interface IRouteOptions {
19
- /**
20
- * Guards specific to this route (merged with controller guards).
21
- */
22
- guards?: Guard[];
23
- /**
24
- * Middlewares specific to this route (merged with controller middlewares).
25
- */
26
- middlewares?: Middleware[];
27
- }
28
-
29
- export interface IRouteMetadata {
30
- method: HttpMethod;
31
- path: string;
32
- handler: string;
33
- guards: Guard[];
34
- middlewares: Middleware[];
35
- }
36
-
37
- const routeMetaMap = new WeakMap<object, IRouteMetadata[]>();
38
-
39
- function createRouteDecorator(verb: HttpMethod) {
40
- return (path: string, options: IRouteOptions = {}): MethodDecorator => {
41
- return (target, propertyKey) => {
42
- const ctor = target.constructor;
43
- const existing: IRouteMetadata[] = routeMetaMap.get(ctor) ?? [];
44
-
45
- existing.push({
46
- method: verb,
47
- path: (path ?? '').trim().replace(/^\/|\/$/g, ''),
48
- handler: propertyKey as string,
49
- guards: options.guards ?? [],
50
- middlewares: options.middlewares ?? [],
51
- });
52
-
53
- routeMetaMap.set(ctor, existing);
54
- };
55
- };
56
- }
57
-
58
- export function getRouteMetadata(target: object): IRouteMetadata[] {
59
- return routeMetaMap.get(target) ?? [];
60
- }
61
-
62
- export const Get = createRouteDecorator('GET');
63
- export const Post = createRouteDecorator('POST');
64
- export const Put = createRouteDecorator('PUT');
65
- export const Patch = createRouteDecorator('PATCH');
66
- export const Delete = createRouteDecorator('DELETE');
@@ -1,15 +0,0 @@
1
- /**
2
- * @copyright 2025 NoxFly
3
- * @license MIT
4
- * @author NoxFly
5
- */
6
-
7
- import { IResponse, Request } from '../internal/request';
8
- import { MaybeAsync } from '../utils/types';
9
-
10
- /**
11
- * A middleware intercepts requests before they reach guards and the handler.
12
- * Implement this interface and pass the class to @Controller({ middlewares }) or per-route options.
13
- */
14
- export type Middleware = (request: Request, response: IResponse, next: NextFunction) => MaybeAsync<void>;
15
- export type NextFunction = () => Promise<void>;
package/src/index.ts DELETED
@@ -1,10 +0,0 @@
1
- /**
2
- * @copyright 2025 NoxFly
3
- * @license MIT
4
- * @author NoxFly
5
- *
6
- * Entry point for renderer process and preload consumers.
7
- */
8
-
9
- // src/index.ts — redirige vers renderer
10
- export * from './renderer';