@aponiajs/common 0.0.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/README.md ADDED
@@ -0,0 +1,8 @@
1
+ # @aponiajs/common
2
+
3
+ Platform-neutral decorators and public contracts for Aponia modules,
4
+ controllers, routes, and dependency injection.
5
+
6
+ The public application authoring API includes `@Module()`, `@Controller()`,
7
+ HTTP method decorators, `@Injectable()`, and `@Inject()`. This package does not
8
+ depend on Elysia, Bun runtime APIs, or another Aponia package.
@@ -0,0 +1,169 @@
1
+ import "reflect-metadata";
2
+ //#region src/token.d.ts
3
+ declare const tokenType: unique symbol;
4
+ type Constructor<T, TArguments extends readonly unknown[] = readonly unknown[]> = new (...arguments_: TArguments) => T;
5
+ type ClassToken<T> = abstract new (...arguments_: never[]) => T;
6
+ interface InjectionToken<T> {
7
+ readonly id: symbol;
8
+ readonly description: string;
9
+ readonly [tokenType]?: T;
10
+ }
11
+ type Token<T> = ClassToken<T> | InjectionToken<T>;
12
+ type TokenValue<TToken> = TToken extends Token<infer TValue> ? TValue : never;
13
+ type TokenValues<TTokens extends readonly Token<unknown>[]> = { readonly [TIndex in keyof TTokens]: TokenValue<TTokens[TIndex]>; };
14
+ declare function createToken<T>(description: string): InjectionToken<T>;
15
+ declare function tokenName(token: Token<unknown>): string;
16
+ //#endregion
17
+ //#region src/controller.d.ts
18
+ interface ControllerDefinition {
19
+ readonly kind: string;
20
+ readonly token: Token<unknown>;
21
+ readonly inject: readonly Token<unknown>[];
22
+ readonly useClass: Constructor<unknown, never[]>;
23
+ }
24
+ //#endregion
25
+ //#region src/provider.d.ts
26
+ type ProviderScope = "singleton";
27
+ interface ProviderBase<T> {
28
+ readonly provide: Token<T>;
29
+ readonly scope?: ProviderScope;
30
+ }
31
+ interface ValueProvider<T> extends ProviderBase<T> {
32
+ readonly kind: "value";
33
+ readonly useValue: T;
34
+ }
35
+ interface FactoryProvider<T, TDependencies extends readonly Token<unknown>[] = readonly Token<unknown>[]> extends ProviderBase<T> {
36
+ readonly kind: "factory";
37
+ readonly inject: TDependencies;
38
+ readonly useFactory: (...dependencies: TokenValues<TDependencies>) => T;
39
+ }
40
+ interface ClassProvider<T, TDependencies extends readonly Token<unknown>[] = readonly Token<unknown>[]> extends ProviderBase<T> {
41
+ readonly kind: "class";
42
+ readonly inject: TDependencies;
43
+ readonly useClass: Constructor<T, TokenValues<TDependencies>>;
44
+ }
45
+ interface AliasProvider<T> extends ProviderBase<T> {
46
+ readonly kind: "alias";
47
+ readonly useExisting: Token<T>;
48
+ }
49
+ type Provider = {
50
+ readonly kind: "value";
51
+ readonly provide: Token<unknown>;
52
+ readonly useValue: unknown;
53
+ } | {
54
+ readonly kind: "factory";
55
+ readonly provide: Token<unknown>;
56
+ readonly inject: readonly Token<unknown>[];
57
+ readonly useFactory: (...dependencies: never[]) => unknown;
58
+ } | {
59
+ readonly kind: "class";
60
+ readonly provide: Token<unknown>;
61
+ readonly inject: readonly Token<unknown>[];
62
+ readonly useClass: Constructor<unknown, never[]>;
63
+ } | {
64
+ readonly kind: "alias";
65
+ readonly provide: Token<unknown>;
66
+ readonly useExisting: Token<unknown>;
67
+ };
68
+ declare function provideValue<T>(provide: Token<T>, useValue: T): ValueProvider<T>;
69
+ declare function provideFactory<T, const TDependencies extends readonly Token<unknown>[]>(provide: Token<T>, inject: TDependencies, useFactory: (...dependencies: TokenValues<TDependencies>) => T): FactoryProvider<T, TDependencies>;
70
+ declare function provideClass<T, const TDependencies extends readonly Token<unknown>[]>(useClass: ClassToken<T> & Constructor<T, TokenValues<TDependencies>>, inject: TDependencies): ClassProvider<T, TDependencies>;
71
+ declare function provideAlias<T>(provide: Token<T>, useExisting: Token<T>): AliasProvider<T>;
72
+ //#endregion
73
+ //#region src/module.d.ts
74
+ interface ModuleDefinition {
75
+ readonly id: string;
76
+ readonly imports: readonly ModuleDefinition[];
77
+ readonly controllers: readonly ControllerDefinition[];
78
+ readonly providers: readonly Provider[];
79
+ readonly exports: readonly Token<unknown>[];
80
+ }
81
+ interface ModuleOptions {
82
+ readonly id: string;
83
+ readonly imports?: readonly ModuleDefinition[];
84
+ readonly controllers?: readonly ControllerDefinition[];
85
+ readonly providers?: readonly Provider[];
86
+ readonly exports?: readonly Token<unknown>[];
87
+ }
88
+ declare function defineModule<const TOptions extends ModuleOptions>(options: TOptions): ModuleDefinition & TOptions;
89
+ //#endregion
90
+ //#region src/decorators.d.ts
91
+ type ModuleClass = ClassToken<unknown>;
92
+ type ModuleImport = ModuleClass | ModuleDefinition;
93
+ type ModuleProvider = ClassToken<unknown> | Provider;
94
+ interface ModuleMetadata {
95
+ readonly imports?: readonly ModuleImport[];
96
+ readonly controllers?: readonly ClassToken<unknown>[];
97
+ readonly providers?: readonly ModuleProvider[];
98
+ readonly exports?: readonly Token<unknown>[];
99
+ }
100
+ interface ControllerMetadata {
101
+ readonly path: string;
102
+ }
103
+ type RequestMethod = "DELETE" | "GET" | "PATCH" | "POST" | "PUT";
104
+ interface RouteMetadata {
105
+ readonly method: RequestMethod;
106
+ readonly path: string;
107
+ readonly propertyKey: string | symbol;
108
+ }
109
+ declare function Module(metadata: ModuleMetadata): ClassDecorator;
110
+ declare function Injectable(): ClassDecorator;
111
+ declare function Controller(path?: string): ClassDecorator;
112
+ declare function Inject(token: Token<unknown>): ParameterDecorator;
113
+ declare const Delete: (path?: string) => MethodDecorator;
114
+ declare const Get: (path?: string) => MethodDecorator;
115
+ declare const Patch: (path?: string) => MethodDecorator;
116
+ declare const Post: (path?: string) => MethodDecorator;
117
+ declare const Put: (path?: string) => MethodDecorator;
118
+ declare function getModuleMetadata(target: ModuleClass): Readonly<ModuleMetadata> | undefined;
119
+ declare function getControllerMetadata(target: ClassToken<unknown>): Readonly<ControllerMetadata> | undefined;
120
+ declare function getRouteMetadata(target: ClassToken<unknown>): readonly RouteMetadata[];
121
+ declare function getConstructorDependencies(target: ClassToken<unknown>): readonly Token<unknown>[];
122
+ //#endregion
123
+ //#region src/error.d.ts
124
+ type AponiaErrorCode = "MODULE_CYCLE" | "DUPLICATE_MODULE" | "DUPLICATE_PROVIDER" | "INVALID_EXPORT" | "AMBIGUOUS_PROVIDER" | "MISSING_PROVIDER" | "PROVIDER_CYCLE" | "INVALID_CONTROLLER" | "INVALID_MODULE" | "APPLICATION_NOT_LISTENING" | "UNSUPPORTED_CONTROLLER";
125
+ declare class AponiaError extends Error {
126
+ readonly code: AponiaErrorCode;
127
+ readonly details: Readonly<Record<string, unknown>>;
128
+ constructor(code: AponiaErrorCode, message: string, details?: Readonly<Record<string, unknown>>);
129
+ }
130
+ //#endregion
131
+ //#region src/logger.d.ts
132
+ type LogLevel = "fatal" | "error" | "warn" | "log" | "debug" | "verbose";
133
+ interface LoggerService {
134
+ log(message: unknown, ...optionalParameters: unknown[]): void;
135
+ fatal(message: unknown, ...optionalParameters: unknown[]): void;
136
+ error(message: unknown, ...optionalParameters: unknown[]): void;
137
+ warn(message: unknown, ...optionalParameters: unknown[]): void;
138
+ debug?(message: unknown, ...optionalParameters: unknown[]): void;
139
+ verbose?(message: unknown, ...optionalParameters: unknown[]): void;
140
+ }
141
+ interface ConsoleLoggerOptions {
142
+ readonly logLevels?: readonly LogLevel[];
143
+ readonly timestamp?: boolean;
144
+ readonly prefix?: string;
145
+ readonly json?: boolean;
146
+ readonly colors?: boolean;
147
+ readonly context?: string;
148
+ readonly compact?: boolean | number;
149
+ readonly depth?: number;
150
+ }
151
+ declare class ConsoleLogger implements LoggerService {
152
+ #private;
153
+ static lastTimestampAt: number | undefined;
154
+ constructor();
155
+ constructor(context: string, options?: ConsoleLoggerOptions);
156
+ constructor(options: ConsoleLoggerOptions);
157
+ setContext(context: string): void;
158
+ resetContext(): void;
159
+ isLevelEnabled(level: LogLevel): boolean;
160
+ log(message: unknown, ...optionalParameters: unknown[]): void;
161
+ fatal(message: unknown, ...optionalParameters: unknown[]): void;
162
+ error(message: unknown, ...optionalParameters: unknown[]): void;
163
+ warn(message: unknown, ...optionalParameters: unknown[]): void;
164
+ debug(message: unknown, ...optionalParameters: unknown[]): void;
165
+ verbose(message: unknown, ...optionalParameters: unknown[]): void;
166
+ }
167
+ declare class Logger extends ConsoleLogger {}
168
+ //#endregion
169
+ export { type AliasProvider, AponiaError, type AponiaErrorCode, type ClassProvider, type ClassToken, ConsoleLogger, type ConsoleLoggerOptions, type Constructor, Controller, type ControllerDefinition, type ControllerMetadata, Delete, type FactoryProvider, Get, Inject, Injectable, type InjectionToken, type LogLevel, Logger, type LoggerService, Module, type ModuleClass, type ModuleDefinition, type ModuleImport, type ModuleMetadata, type ModuleOptions, type ModuleProvider, Patch, Post, type Provider, type ProviderScope, Put, type RequestMethod, type RouteMetadata, type Token, type TokenValue, type TokenValues, type ValueProvider, createToken, defineModule, getConstructorDependencies, getControllerMetadata, getModuleMetadata, getRouteMetadata, provideAlias, provideClass, provideFactory, provideValue, tokenName };
package/dist/index.mjs ADDED
@@ -0,0 +1,257 @@
1
+ import "reflect-metadata";
2
+ import { inspect } from "node:util";
3
+ //#region src/decorators.ts
4
+ const modules = /* @__PURE__ */ new WeakMap();
5
+ const controllers = /* @__PURE__ */ new WeakMap();
6
+ const routes = /* @__PURE__ */ new WeakMap();
7
+ const injectedTokens = /* @__PURE__ */ new WeakMap();
8
+ function Module(metadata) {
9
+ const normalized = Object.freeze({
10
+ imports: Object.freeze([...metadata.imports ?? []]),
11
+ controllers: Object.freeze([...metadata.controllers ?? []]),
12
+ providers: Object.freeze([...metadata.providers ?? []]),
13
+ exports: Object.freeze([...metadata.exports ?? []])
14
+ });
15
+ return (target) => {
16
+ modules.set(target, normalized);
17
+ };
18
+ }
19
+ function Injectable() {
20
+ return () => {};
21
+ }
22
+ function Controller(path = "") {
23
+ return (target) => {
24
+ controllers.set(target, Object.freeze({ path }));
25
+ };
26
+ }
27
+ function Inject(token) {
28
+ return (target, _propertyKey, parameterIndex) => {
29
+ const constructor = typeof target === "function" ? target : target.constructor;
30
+ const parameters = injectedTokens.get(constructor) ?? /* @__PURE__ */ new Map();
31
+ parameters.set(parameterIndex, token);
32
+ injectedTokens.set(constructor, parameters);
33
+ };
34
+ }
35
+ const Delete = createRouteDecorator("DELETE");
36
+ const Get = createRouteDecorator("GET");
37
+ const Patch = createRouteDecorator("PATCH");
38
+ const Post = createRouteDecorator("POST");
39
+ const Put = createRouteDecorator("PUT");
40
+ function getModuleMetadata(target) {
41
+ return modules.get(target);
42
+ }
43
+ function getControllerMetadata(target) {
44
+ return controllers.get(target);
45
+ }
46
+ function getRouteMetadata(target) {
47
+ return Object.freeze([...routes.get(target.prototype) ?? []]);
48
+ }
49
+ function getConstructorDependencies(target) {
50
+ const reflected = Reflect.getMetadata("design:paramtypes", target) ?? [];
51
+ const explicit = injectedTokens.get(target);
52
+ const explicitLength = explicit ? Math.max(0, ...[...explicit.keys()].map((index) => index + 1)) : 0;
53
+ const length = Math.max(reflected.length, explicitLength);
54
+ return Object.freeze(Array.from({ length }, (_, index) => explicit?.get(index) ?? asToken(reflected[index], target)));
55
+ }
56
+ function createRouteDecorator(method) {
57
+ return (path = "") => (target, propertyKey) => {
58
+ const controllerRoutes = routes.get(target) ?? [];
59
+ controllerRoutes.push(Object.freeze({
60
+ method,
61
+ path,
62
+ propertyKey
63
+ }));
64
+ routes.set(target, controllerRoutes);
65
+ };
66
+ }
67
+ function asToken(value, target) {
68
+ if (typeof value === "function") return value;
69
+ throw new TypeError(`Cannot resolve a constructor dependency for "${target.name}". Use @Inject(token) for non-class tokens.`);
70
+ }
71
+ //#endregion
72
+ //#region src/error.ts
73
+ var AponiaError = class extends Error {
74
+ code;
75
+ details;
76
+ constructor(code, message, details = {}) {
77
+ super(message);
78
+ this.name = "AponiaError";
79
+ this.code = code;
80
+ this.details = Object.freeze({ ...details });
81
+ }
82
+ };
83
+ //#endregion
84
+ //#region src/logger.ts
85
+ const logLevelOrder = [
86
+ "fatal",
87
+ "error",
88
+ "warn",
89
+ "log",
90
+ "debug",
91
+ "verbose"
92
+ ];
93
+ const colorByLevel = {
94
+ fatal: 31,
95
+ error: 31,
96
+ warn: 33,
97
+ log: 32,
98
+ debug: 35,
99
+ verbose: 36
100
+ };
101
+ var ConsoleLogger = class ConsoleLogger {
102
+ static lastTimestampAt;
103
+ #options;
104
+ #originalContext;
105
+ #context;
106
+ constructor(contextOrOptions = {}, options = {}) {
107
+ const context = typeof contextOrOptions === "string" ? contextOrOptions : contextOrOptions.context;
108
+ const suppliedOptions = typeof contextOrOptions === "string" ? options : contextOrOptions;
109
+ const json = suppliedOptions.json ?? false;
110
+ this.#context = context ?? "";
111
+ this.#originalContext = this.#context;
112
+ this.#options = {
113
+ ...suppliedOptions,
114
+ colors: suppliedOptions.colors ?? !json,
115
+ json,
116
+ prefix: suppliedOptions.prefix ?? "Aponia",
117
+ timestamp: suppliedOptions.timestamp ?? false
118
+ };
119
+ }
120
+ setContext(context) {
121
+ this.#context = context;
122
+ }
123
+ resetContext() {
124
+ this.#context = this.#originalContext;
125
+ }
126
+ isLevelEnabled(level) {
127
+ const configured = this.#options.logLevels;
128
+ if (!configured) return true;
129
+ if (configured.length === 0) return false;
130
+ const maximumLevel = Math.max(...configured.map((item) => logLevelOrder.indexOf(item)));
131
+ return logLevelOrder.indexOf(level) <= maximumLevel;
132
+ }
133
+ log(message, ...optionalParameters) {
134
+ this.#print("log", message, optionalParameters);
135
+ }
136
+ fatal(message, ...optionalParameters) {
137
+ this.#print("fatal", message, optionalParameters);
138
+ }
139
+ error(message, ...optionalParameters) {
140
+ this.#print("error", message, optionalParameters);
141
+ }
142
+ warn(message, ...optionalParameters) {
143
+ this.#print("warn", message, optionalParameters);
144
+ }
145
+ debug(message, ...optionalParameters) {
146
+ this.#print("debug", message, optionalParameters);
147
+ }
148
+ verbose(message, ...optionalParameters) {
149
+ this.#print("verbose", message, optionalParameters);
150
+ }
151
+ #print(level, message, optionalParameters) {
152
+ if (!this.isLevelEnabled(level)) return;
153
+ const context = typeof optionalParameters.at(-1) === "string" ? optionalParameters.at(-1) : this.#context;
154
+ const timestamp = Date.now();
155
+ const timestampDifference = this.#timestampDifference(timestamp);
156
+ const output = this.#options.json ? this.#formatJson(level, message, context, timestamp) : this.#formatText(level, message, context, timestampDifference);
157
+ process[level === "error" || level === "fatal" ? "stderr" : "stdout"].write(`${output}\n`);
158
+ }
159
+ #formatJson(level, message, context, timestamp) {
160
+ return JSON.stringify({
161
+ level,
162
+ pid: process.pid,
163
+ timestamp,
164
+ message,
165
+ ...context ? { context } : {}
166
+ });
167
+ }
168
+ #formatText(level, message, context, timestampDifference) {
169
+ return `${this.#colorize(`[${this.#options.prefix}] ${process.pid} -`, level)} ${new Intl.DateTimeFormat(void 0, {
170
+ year: "numeric",
171
+ month: "2-digit",
172
+ day: "2-digit",
173
+ hour: "numeric",
174
+ minute: "2-digit",
175
+ second: "2-digit"
176
+ }).format(Date.now())} ${this.#colorize(level.toUpperCase().padStart(7, " "), level)} ${context ? `${this.#ansi(`[${context}]`, 33)} ` : ""}${this.#colorize(this.#stringify(message), level)}${timestampDifference ? this.#ansi(timestampDifference, 33) : timestampDifference}`;
177
+ }
178
+ #timestampDifference(timestamp) {
179
+ const previousTimestamp = ConsoleLogger.lastTimestampAt;
180
+ ConsoleLogger.lastTimestampAt = timestamp;
181
+ return previousTimestamp && this.#options.timestamp ? ` +${timestamp - previousTimestamp}ms` : "";
182
+ }
183
+ #stringify(message) {
184
+ if (typeof message === "string") return message;
185
+ if (typeof message === "function") return message.name || message.toString();
186
+ return inspect(message, {
187
+ colors: this.#options.colors,
188
+ compact: this.#options.compact ?? true,
189
+ depth: this.#options.depth ?? 5,
190
+ breakLength: Number.POSITIVE_INFINITY
191
+ });
192
+ }
193
+ #colorize(message, level) {
194
+ return this.#ansi(message, colorByLevel[level]);
195
+ }
196
+ #ansi(message, color) {
197
+ return this.#options.colors ? `\u001B[${color}m${message}\u001B[0m` : message;
198
+ }
199
+ };
200
+ var Logger = class extends ConsoleLogger {};
201
+ //#endregion
202
+ //#region src/module.ts
203
+ function defineModule(options) {
204
+ return Object.freeze({
205
+ ...options,
206
+ imports: Object.freeze([...options.imports ?? []]),
207
+ controllers: Object.freeze([...options.controllers ?? []]),
208
+ providers: Object.freeze([...options.providers ?? []]),
209
+ exports: Object.freeze([...options.exports ?? []])
210
+ });
211
+ }
212
+ //#endregion
213
+ //#region src/provider.ts
214
+ function provideValue(provide, useValue) {
215
+ return Object.freeze({
216
+ kind: "value",
217
+ provide,
218
+ useValue
219
+ });
220
+ }
221
+ function provideFactory(provide, inject, useFactory) {
222
+ return Object.freeze({
223
+ kind: "factory",
224
+ provide,
225
+ inject,
226
+ useFactory
227
+ });
228
+ }
229
+ function provideClass(useClass, inject) {
230
+ return Object.freeze({
231
+ kind: "class",
232
+ provide: useClass,
233
+ inject,
234
+ useClass
235
+ });
236
+ }
237
+ function provideAlias(provide, useExisting) {
238
+ return Object.freeze({
239
+ kind: "alias",
240
+ provide,
241
+ useExisting
242
+ });
243
+ }
244
+ //#endregion
245
+ //#region src/token.ts
246
+ function createToken(description) {
247
+ return Object.freeze({
248
+ id: Symbol(description),
249
+ description
250
+ });
251
+ }
252
+ function tokenName(token) {
253
+ if (typeof token === "function") return token.name || "<anonymous class>";
254
+ return token.description;
255
+ }
256
+ //#endregion
257
+ export { AponiaError, ConsoleLogger, Controller, Delete, Get, Inject, Injectable, Logger, Module, Patch, Post, Put, createToken, defineModule, getConstructorDependencies, getControllerMetadata, getModuleMetadata, getRouteMetadata, provideAlias, provideClass, provideFactory, provideValue, tokenName };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@aponiajs/common",
3
+ "version": "0.0.0",
4
+ "description": "Platform-neutral contracts for Aponia modules and dependency injection.",
5
+ "homepage": "https://github.com/aponiajs/aponiajs#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/aponiajs/aponiajs/issues"
8
+ },
9
+ "license": "MIT",
10
+ "author": "AponiaJS contributors",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/aponiajs/aponiajs.git",
14
+ "directory": "packages/common"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "type": "module",
20
+ "exports": {
21
+ ".": "./dist/index.mjs",
22
+ "./package.json": "./package.json"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "scripts": {
28
+ "build": "bunx --bun vp pack",
29
+ "dev": "bunx --bun vp pack --watch",
30
+ "test": "bun test",
31
+ "test:vite-plus": "vp test",
32
+ "check": "bunx --bun vp check",
33
+ "prepublishOnly": "bun run build"
34
+ },
35
+ "dependencies": {
36
+ "reflect-metadata": "^0.2.2"
37
+ }
38
+ }