@buenojs/bueno 0.8.4 → 0.8.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.
- package/README.md +136 -16
- package/dist/cli/{index.js → bin.js} +412 -331
- package/dist/container/index.js +250 -0
- package/dist/context/index.js +219 -0
- package/dist/database/index.js +493 -0
- package/dist/frontend/index.js +7697 -0
- package/dist/health/index.js +364 -0
- package/dist/i18n/index.js +345 -0
- package/dist/index.js +11043 -6482
- package/dist/jobs/index.js +819 -0
- package/dist/lock/index.js +367 -0
- package/dist/logger/index.js +281 -0
- package/dist/metrics/index.js +289 -0
- package/dist/middleware/index.js +77 -0
- package/dist/migrations/index.js +571 -0
- package/dist/modules/index.js +3346 -0
- package/dist/notification/index.js +484 -0
- package/dist/observability/index.js +331 -0
- package/dist/openapi/index.js +776 -0
- package/dist/orm/index.js +1356 -0
- package/dist/router/index.js +886 -0
- package/dist/rpc/index.js +691 -0
- package/dist/schema/index.js +400 -0
- package/dist/telemetry/index.js +595 -0
- package/dist/template/index.js +640 -0
- package/dist/templates/index.js +640 -0
- package/dist/testing/index.js +1111 -0
- package/dist/types/index.js +60 -0
- package/package.json +121 -27
- package/src/cache/index.ts +2 -1
- package/src/cli/bin.ts +2 -2
- package/src/cli/commands/build.ts +183 -165
- package/src/cli/commands/dev.ts +96 -89
- package/src/cli/commands/generate.ts +142 -111
- package/src/cli/commands/help.ts +20 -16
- package/src/cli/commands/index.ts +3 -6
- package/src/cli/commands/migration.ts +124 -105
- package/src/cli/commands/new.ts +294 -232
- package/src/cli/commands/start.ts +81 -79
- package/src/cli/core/args.ts +68 -50
- package/src/cli/core/console.ts +89 -95
- package/src/cli/core/index.ts +4 -4
- package/src/cli/core/prompt.ts +65 -62
- package/src/cli/core/spinner.ts +23 -20
- package/src/cli/index.ts +46 -38
- package/src/cli/templates/database/index.ts +37 -18
- package/src/cli/templates/database/mysql.ts +3 -3
- package/src/cli/templates/database/none.ts +2 -2
- package/src/cli/templates/database/postgresql.ts +3 -3
- package/src/cli/templates/database/sqlite.ts +3 -3
- package/src/cli/templates/deploy.ts +29 -26
- package/src/cli/templates/docker.ts +41 -30
- package/src/cli/templates/frontend/index.ts +33 -15
- package/src/cli/templates/frontend/none.ts +2 -2
- package/src/cli/templates/frontend/react.ts +18 -18
- package/src/cli/templates/frontend/solid.ts +15 -15
- package/src/cli/templates/frontend/svelte.ts +17 -17
- package/src/cli/templates/frontend/vue.ts +15 -15
- package/src/cli/templates/generators/index.ts +29 -29
- package/src/cli/templates/generators/types.ts +21 -21
- package/src/cli/templates/index.ts +6 -6
- package/src/cli/templates/project/api.ts +37 -36
- package/src/cli/templates/project/default.ts +25 -25
- package/src/cli/templates/project/fullstack.ts +28 -26
- package/src/cli/templates/project/index.ts +55 -16
- package/src/cli/templates/project/minimal.ts +17 -12
- package/src/cli/templates/project/types.ts +10 -5
- package/src/cli/templates/project/website.ts +14 -14
- package/src/cli/utils/fs.ts +55 -41
- package/src/cli/utils/index.ts +3 -3
- package/src/cli/utils/strings.ts +47 -33
- package/src/cli/utils/version.ts +14 -8
- package/src/config/env-validation.ts +100 -0
- package/src/config/env.ts +169 -41
- package/src/config/index.ts +28 -20
- package/src/config/loader.ts +25 -16
- package/src/config/merge.ts +21 -10
- package/src/config/types.ts +545 -25
- package/src/config/validation.ts +215 -7
- package/src/container/forward-ref.ts +22 -22
- package/src/container/index.ts +34 -12
- package/src/context/index.ts +11 -1
- package/src/database/index.ts +7 -190
- package/src/database/orm/builder.ts +457 -0
- package/src/database/orm/casts/index.ts +130 -0
- package/src/database/orm/casts/types.ts +25 -0
- package/src/database/orm/compiler.ts +304 -0
- package/src/database/orm/hooks/index.ts +114 -0
- package/src/database/orm/index.ts +61 -0
- package/src/database/orm/model-registry.ts +59 -0
- package/src/database/orm/model.ts +821 -0
- package/src/database/orm/relationships/base.ts +146 -0
- package/src/database/orm/relationships/belongs-to-many.ts +179 -0
- package/src/database/orm/relationships/belongs-to.ts +56 -0
- package/src/database/orm/relationships/has-many.ts +45 -0
- package/src/database/orm/relationships/has-one.ts +41 -0
- package/src/database/orm/relationships/index.ts +11 -0
- package/src/database/orm/scopes/index.ts +55 -0
- package/src/events/__tests__/event-system.test.ts +235 -0
- package/src/events/config.ts +238 -0
- package/src/events/example-usage.ts +185 -0
- package/src/events/index.ts +278 -0
- package/src/events/manager.ts +385 -0
- package/src/events/registry.ts +182 -0
- package/src/events/types.ts +124 -0
- package/src/frontend/api-routes.ts +65 -23
- package/src/frontend/bundler.ts +76 -34
- package/src/frontend/console-client.ts +2 -2
- package/src/frontend/console-stream.ts +94 -38
- package/src/frontend/dev-server.ts +94 -46
- package/src/frontend/file-router.ts +61 -19
- package/src/frontend/frameworks/index.ts +37 -10
- package/src/frontend/frameworks/react.ts +10 -8
- package/src/frontend/frameworks/solid.ts +11 -9
- package/src/frontend/frameworks/svelte.ts +15 -9
- package/src/frontend/frameworks/vue.ts +13 -11
- package/src/frontend/hmr-client.ts +12 -10
- package/src/frontend/hmr.ts +146 -103
- package/src/frontend/index.ts +14 -5
- package/src/frontend/islands.ts +41 -22
- package/src/frontend/isr.ts +59 -37
- package/src/frontend/layout.ts +36 -21
- package/src/frontend/ssr/react.ts +74 -27
- package/src/frontend/ssr/solid.ts +54 -20
- package/src/frontend/ssr/svelte.ts +48 -14
- package/src/frontend/ssr/vue.ts +50 -18
- package/src/frontend/ssr.ts +83 -39
- package/src/frontend/types.ts +91 -56
- package/src/health/index.ts +21 -9
- package/src/i18n/engine.ts +305 -0
- package/src/i18n/index.ts +38 -0
- package/src/i18n/loader.ts +218 -0
- package/src/i18n/middleware.ts +164 -0
- package/src/i18n/negotiator.ts +162 -0
- package/src/i18n/types.ts +158 -0
- package/src/index.ts +179 -27
- package/src/jobs/drivers/memory.ts +315 -0
- package/src/jobs/drivers/redis.ts +459 -0
- package/src/jobs/index.ts +30 -0
- package/src/jobs/queue.ts +281 -0
- package/src/jobs/types.ts +295 -0
- package/src/jobs/worker.ts +380 -0
- package/src/logger/index.ts +1 -3
- package/src/logger/transports/index.ts +62 -22
- package/src/metrics/index.ts +25 -16
- package/src/migrations/index.ts +9 -0
- package/src/modules/filters.ts +13 -17
- package/src/modules/guards.ts +49 -26
- package/src/modules/index.ts +409 -298
- package/src/modules/interceptors.ts +58 -20
- package/src/modules/lazy.ts +11 -19
- package/src/modules/lifecycle.ts +15 -7
- package/src/modules/metadata.ts +15 -5
- package/src/modules/pipes.ts +94 -72
- package/src/notification/channels/base.ts +68 -0
- package/src/notification/channels/email.ts +105 -0
- package/src/notification/channels/push.ts +104 -0
- package/src/notification/channels/sms.ts +105 -0
- package/src/notification/channels/whatsapp.ts +104 -0
- package/src/notification/index.ts +48 -0
- package/src/notification/service.ts +354 -0
- package/src/notification/types.ts +344 -0
- package/src/observability/__tests__/observability.test.ts +483 -0
- package/src/observability/breadcrumbs.ts +114 -0
- package/src/observability/index.ts +136 -0
- package/src/observability/interceptor.ts +85 -0
- package/src/observability/service.ts +303 -0
- package/src/observability/trace.ts +37 -0
- package/src/observability/types.ts +196 -0
- package/src/openapi/__tests__/decorators.test.ts +335 -0
- package/src/openapi/__tests__/document-builder.test.ts +285 -0
- package/src/openapi/__tests__/route-scanner.test.ts +334 -0
- package/src/openapi/__tests__/schema-generator.test.ts +275 -0
- package/src/openapi/decorators.ts +328 -0
- package/src/openapi/document-builder.ts +274 -0
- package/src/openapi/index.ts +112 -0
- package/src/openapi/metadata.ts +112 -0
- package/src/openapi/route-scanner.ts +289 -0
- package/src/openapi/schema-generator.ts +256 -0
- package/src/openapi/swagger-module.ts +166 -0
- package/src/openapi/types.ts +398 -0
- package/src/orm/index.ts +10 -0
- package/src/rpc/index.ts +3 -1
- package/src/schema/index.ts +9 -0
- package/src/security/index.ts +15 -6
- package/src/ssg/index.ts +9 -8
- package/src/telemetry/index.ts +76 -22
- package/src/template/index.ts +7 -0
- package/src/templates/engine.ts +224 -0
- package/src/templates/index.ts +9 -0
- package/src/templates/loader.ts +331 -0
- package/src/templates/renderers/markdown.ts +212 -0
- package/src/templates/renderers/simple.ts +269 -0
- package/src/templates/types.ts +154 -0
- package/src/testing/index.ts +100 -27
- package/src/types/optional-deps.d.ts +347 -187
- package/src/validation/index.ts +92 -2
- package/src/validation/schemas.ts +536 -0
- package/tests/integration/fullstack.test.ts +4 -4
- package/tests/unit/database.test.ts +2 -72
- package/tests/unit/env-validation.test.ts +166 -0
- package/tests/unit/events.test.ts +910 -0
- package/tests/unit/i18n.test.ts +455 -0
- package/tests/unit/jobs.test.ts +493 -0
- package/tests/unit/notification.test.ts +988 -0
- package/tests/unit/observability.test.ts +453 -0
- package/tests/unit/orm/builder.test.ts +323 -0
- package/tests/unit/orm/casts.test.ts +179 -0
- package/tests/unit/orm/compiler.test.ts +220 -0
- package/tests/unit/orm/eager-loading.test.ts +285 -0
- package/tests/unit/orm/hooks.test.ts +191 -0
- package/tests/unit/orm/model.test.ts +373 -0
- package/tests/unit/orm/relationships.test.ts +303 -0
- package/tests/unit/orm/scopes.test.ts +74 -0
- package/tests/unit/templates-simple.test.ts +53 -0
- package/tests/unit/templates.test.ts +454 -0
- package/tests/unit/validation.test.ts +18 -24
- package/tsconfig.json +11 -3
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Event,
|
|
3
|
+
type EventCategory,
|
|
4
|
+
EventContext,
|
|
5
|
+
EventError,
|
|
6
|
+
type EventRegistry,
|
|
7
|
+
type EventRegistryOptions,
|
|
8
|
+
type EventRegistryState,
|
|
9
|
+
type EventStats,
|
|
10
|
+
} from "./types";
|
|
11
|
+
|
|
12
|
+
export class EventRegistryImpl implements EventRegistry {
|
|
13
|
+
private state: EventRegistryState;
|
|
14
|
+
|
|
15
|
+
constructor(options: EventRegistryOptions = {}) {
|
|
16
|
+
this.state = {
|
|
17
|
+
events: new Map(),
|
|
18
|
+
categories: new Map(),
|
|
19
|
+
config: {
|
|
20
|
+
maxEvents: options.maxEvents || 1000,
|
|
21
|
+
retentionPeriod: options.retentionPeriod || 24 * 60 * 60 * 1000, // 24 hours
|
|
22
|
+
cleanupInterval: options.cleanupInterval || 60 * 60 * 1000, // 1 hour
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
this.setupCleanup();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
registerEvent(event: Event): void {
|
|
30
|
+
if (this.state.events.size >= this.state.config.maxEvents) {
|
|
31
|
+
this.cleanupOldEvents();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
this.state.events.set(event.id, event);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getEvent(id: string): Event | undefined {
|
|
38
|
+
return this.state.events.get(id);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getEventsByCategory(category: string): Event[] {
|
|
42
|
+
return Array.from(this.state.events.values()).filter(
|
|
43
|
+
(event) => event.context?.category === category,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getAllEvents(): Event[] {
|
|
48
|
+
return Array.from(this.state.events.values());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getEventCategories(): EventCategory[] {
|
|
52
|
+
return Array.from(this.state.categories.values());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
registerEventCategory(category: EventCategory): void {
|
|
56
|
+
this.state.categories.set(category.name, category);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getEventStats(): EventStats {
|
|
60
|
+
return {
|
|
61
|
+
totalEvents: this.state.events.size,
|
|
62
|
+
eventsPerSecond: this.calculateEventsPerSecond(),
|
|
63
|
+
listenersCount: 0, // Not tracked in registry
|
|
64
|
+
errorsCount: 0, // Not tracked in registry
|
|
65
|
+
averageProcessingTime: 0, // Not tracked in registry
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
clearEvents(): void {
|
|
70
|
+
this.state.events.clear();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private setupCleanup(): void {
|
|
74
|
+
setInterval(() => {
|
|
75
|
+
this.cleanupOldEvents();
|
|
76
|
+
}, this.state.config.cleanupInterval);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private cleanupOldEvents(): void {
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
const retentionTime = now - this.state.config.retentionPeriod;
|
|
82
|
+
|
|
83
|
+
for (const [id, event] of this.state.events.entries()) {
|
|
84
|
+
if (event.timestamp.getTime() < retentionTime) {
|
|
85
|
+
this.state.events.delete(id);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private calculateEventsPerSecond(): number {
|
|
91
|
+
const now = Date.now();
|
|
92
|
+
const oneSecondAgo = now - 1000;
|
|
93
|
+
let count = 0;
|
|
94
|
+
|
|
95
|
+
for (const event of this.state.events.values()) {
|
|
96
|
+
if (event.timestamp.getTime() > oneSecondAgo) {
|
|
97
|
+
count++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return count;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getEventHistory(options: {
|
|
105
|
+
category?: string;
|
|
106
|
+
startTime?: Date;
|
|
107
|
+
endTime?: Date;
|
|
108
|
+
limit?: number;
|
|
109
|
+
}): Event[] {
|
|
110
|
+
let events = Array.from(this.state.events.values());
|
|
111
|
+
|
|
112
|
+
if (options.category) {
|
|
113
|
+
events = events.filter(
|
|
114
|
+
(event) => event.context?.category === options.category,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (options.startTime) {
|
|
119
|
+
events = events.filter((event) => event.timestamp >= options.startTime);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (options.endTime) {
|
|
123
|
+
events = events.filter((event) => event.timestamp <= options.endTime);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Sort first before applying limit
|
|
127
|
+
events = events.sort(
|
|
128
|
+
(a, b) => b.timestamp.getTime() - a.timestamp.getTime(),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
if (options.limit) {
|
|
132
|
+
events = events.slice(0, options.limit);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return events;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
searchEvents(query: string): Event[] {
|
|
139
|
+
const lowerQuery = query.toLowerCase();
|
|
140
|
+
return Array.from(this.state.events.values()).filter(
|
|
141
|
+
(event) =>
|
|
142
|
+
event.name.toLowerCase().includes(lowerQuery) ||
|
|
143
|
+
(event.data &&
|
|
144
|
+
JSON.stringify(event.data).toLowerCase().includes(lowerQuery)),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
exportEvents(): Event[] {
|
|
149
|
+
return Array.from(this.state.events.values());
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
importEvents(events: Event[]): void {
|
|
153
|
+
events.forEach((event) => this.registerEvent(event));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
getEventTimeline(): { timestamp: Date; eventCount: number }[] {
|
|
157
|
+
const timeline: { timestamp: Date; eventCount: number }[] = [];
|
|
158
|
+
const eventsByTime = new Map();
|
|
159
|
+
|
|
160
|
+
for (const event of this.state.events.values()) {
|
|
161
|
+
const timeKey = new Date(
|
|
162
|
+
event.timestamp.getTime() - (event.timestamp.getTime() % (60 * 1000)),
|
|
163
|
+
);
|
|
164
|
+
const count = eventsByTime.get(timeKey) || 0;
|
|
165
|
+
eventsByTime.set(timeKey, count + 1);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for (const [time, count] of eventsByTime.entries()) {
|
|
169
|
+
timeline.push({ timestamp: time, eventCount: count });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return timeline.sort(
|
|
173
|
+
(a, b) => a.timestamp.getTime() - b.timestamp.getTime(),
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function createEventRegistry(
|
|
179
|
+
options?: EventRegistryOptions,
|
|
180
|
+
): EventRegistry {
|
|
181
|
+
return new EventRegistryImpl(options);
|
|
182
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export interface Event {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
timestamp: Date;
|
|
5
|
+
data: Record<string, any>;
|
|
6
|
+
context?: EventContext;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface EventContext {
|
|
10
|
+
userId?: string;
|
|
11
|
+
sessionId?: string;
|
|
12
|
+
requestId?: string;
|
|
13
|
+
ipAddress?: string;
|
|
14
|
+
userAgent?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface EventListener {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
handler: EventHandler;
|
|
21
|
+
filter?: EventFilter;
|
|
22
|
+
priority?: number;
|
|
23
|
+
once?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type EventHandler = (event: Event) => Promise<void> | void;
|
|
27
|
+
|
|
28
|
+
export type EventFilter = (event: Event) => boolean;
|
|
29
|
+
|
|
30
|
+
export interface EventCategory {
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
events: string[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface EventManagerConfig {
|
|
37
|
+
maxListeners?: number;
|
|
38
|
+
eventCategories?: EventCategory[];
|
|
39
|
+
middleware?: EventMiddleware[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type EventMiddleware = (
|
|
43
|
+
event: Event,
|
|
44
|
+
next: () => Promise<void>,
|
|
45
|
+
) => Promise<void>;
|
|
46
|
+
|
|
47
|
+
export interface EventRegistry {
|
|
48
|
+
registerEvent(event: Event): void;
|
|
49
|
+
getEvent(id: string): Event | undefined;
|
|
50
|
+
getEventsByCategory(category: string): Event[];
|
|
51
|
+
getAllEvents(): Event[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface EventManager {
|
|
55
|
+
emit(event: Event): Promise<void>;
|
|
56
|
+
emitSync(event: Event): void;
|
|
57
|
+
on(event: string, listener: EventHandler): () => void;
|
|
58
|
+
once(event: string, listener: EventHandler): () => void;
|
|
59
|
+
off(event: string, listener: EventHandler): void;
|
|
60
|
+
addListener(listener: EventListener): void;
|
|
61
|
+
removeListener(listener: EventListener): void;
|
|
62
|
+
addFilter(filter: EventFilter): void;
|
|
63
|
+
removeFilter(filter: EventFilter): void;
|
|
64
|
+
addMiddleware(middleware: EventMiddleware): void;
|
|
65
|
+
removeMiddleware(middleware: EventMiddleware): void;
|
|
66
|
+
getListeners(event: string): EventListener[];
|
|
67
|
+
hasListeners(event: string): boolean;
|
|
68
|
+
clearListeners(event?: string): void;
|
|
69
|
+
getEventCategories(): EventCategory[];
|
|
70
|
+
registerEventCategory(category: EventCategory): void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface EventError extends Error {
|
|
74
|
+
event: Event;
|
|
75
|
+
originalError?: Error;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export interface EventStats {
|
|
79
|
+
totalEvents: number;
|
|
80
|
+
eventsPerSecond: number;
|
|
81
|
+
listenersCount: number;
|
|
82
|
+
errorsCount: number;
|
|
83
|
+
averageProcessingTime: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface EventOptions {
|
|
87
|
+
context?: EventContext;
|
|
88
|
+
timestamp?: Date;
|
|
89
|
+
id?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface EventListenerOptions {
|
|
93
|
+
priority?: number;
|
|
94
|
+
once?: boolean;
|
|
95
|
+
filter?: EventFilter;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface EventManagerOptions {
|
|
99
|
+
maxListeners?: number;
|
|
100
|
+
eventCategories?: EventCategory[];
|
|
101
|
+
middleware?: EventMiddleware[];
|
|
102
|
+
errorHandling?: "throw" | "log" | "ignore";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface EventRegistryOptions {
|
|
106
|
+
maxEvents?: number;
|
|
107
|
+
retentionPeriod?: number;
|
|
108
|
+
cleanupInterval?: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface EventManagerState {
|
|
112
|
+
listeners: Map<string, EventListener[]>;
|
|
113
|
+
filters: EventFilter[];
|
|
114
|
+
middleware: EventMiddleware[];
|
|
115
|
+
categories: EventCategory[];
|
|
116
|
+
stats: EventStats;
|
|
117
|
+
config: EventManagerConfig;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface EventRegistryState {
|
|
121
|
+
events: Map<string, Event>;
|
|
122
|
+
categories: Map<string, EventCategory>;
|
|
123
|
+
config: EventRegistryOptions;
|
|
124
|
+
}
|
|
@@ -9,24 +9,32 @@
|
|
|
9
9
|
* - Type-safe response helpers
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { type Logger, createLogger } from "../logger/index.js";
|
|
13
13
|
import type {
|
|
14
|
+
APIContext,
|
|
15
|
+
APIMiddleware,
|
|
16
|
+
APIResponse,
|
|
14
17
|
APIRouteConfig,
|
|
15
|
-
PartialAPIRouteConfig,
|
|
16
18
|
APIRouteDefinition,
|
|
17
19
|
APIRouteHandler,
|
|
18
|
-
APIContext,
|
|
19
|
-
APIResponse,
|
|
20
|
-
APIMiddleware,
|
|
21
20
|
APIRouteModule,
|
|
22
21
|
HTTPMethod,
|
|
22
|
+
PartialAPIRouteConfig,
|
|
23
23
|
} from "./types.js";
|
|
24
24
|
|
|
25
25
|
// ============= Constants =============
|
|
26
26
|
|
|
27
27
|
const DEFAULT_API_DIR = "pages/api";
|
|
28
28
|
const SUPPORTED_EXTENSIONS = [".ts", ".js"];
|
|
29
|
-
const SUPPORTED_METHODS: HTTPMethod[] = [
|
|
29
|
+
const SUPPORTED_METHODS: HTTPMethod[] = [
|
|
30
|
+
"GET",
|
|
31
|
+
"POST",
|
|
32
|
+
"PUT",
|
|
33
|
+
"PATCH",
|
|
34
|
+
"DELETE",
|
|
35
|
+
"HEAD",
|
|
36
|
+
"OPTIONS",
|
|
37
|
+
];
|
|
30
38
|
|
|
31
39
|
// ============= API Route Manager Class =============
|
|
32
40
|
|
|
@@ -102,7 +110,10 @@ export class APIRouteManager {
|
|
|
102
110
|
/**
|
|
103
111
|
* Process a single API file
|
|
104
112
|
*/
|
|
105
|
-
private async processAPIFile(
|
|
113
|
+
private async processAPIFile(
|
|
114
|
+
filePath: string,
|
|
115
|
+
basePath: string,
|
|
116
|
+
): Promise<void> {
|
|
106
117
|
const fullPath = `${basePath}/${filePath}`;
|
|
107
118
|
const routePath = this.filePathToRoute(filePath);
|
|
108
119
|
|
|
@@ -136,14 +147,18 @@ export class APIRouteManager {
|
|
|
136
147
|
};
|
|
137
148
|
|
|
138
149
|
this.routes.set(routePath, route);
|
|
139
|
-
this.logger.debug(
|
|
150
|
+
this.logger.debug(
|
|
151
|
+
`Processed API route: ${routePath} [${methods.join(", ")}]`,
|
|
152
|
+
);
|
|
140
153
|
}
|
|
141
154
|
|
|
142
155
|
/**
|
|
143
156
|
* Load middlewares from _middleware.ts files
|
|
144
157
|
*/
|
|
145
158
|
private async loadMiddlewares(basePath: string): Promise<void> {
|
|
146
|
-
const glob = new Bun.Glob(
|
|
159
|
+
const glob = new Bun.Glob(
|
|
160
|
+
`**/_middleware{${this.config.extensions.join(",")}}`,
|
|
161
|
+
);
|
|
147
162
|
|
|
148
163
|
try {
|
|
149
164
|
for await (const file of glob.scan(basePath)) {
|
|
@@ -261,7 +276,10 @@ export class APIRouteManager {
|
|
|
261
276
|
/**
|
|
262
277
|
* Match a request to an API route
|
|
263
278
|
*/
|
|
264
|
-
match(
|
|
279
|
+
match(
|
|
280
|
+
method: string,
|
|
281
|
+
pathname: string,
|
|
282
|
+
): { route: APIRouteDefinition; params: Record<string, string> } | null {
|
|
265
283
|
for (const route of this.routes.values()) {
|
|
266
284
|
const match = pathname.match(route.regex);
|
|
267
285
|
if (match) {
|
|
@@ -316,8 +334,11 @@ export class APIRouteManager {
|
|
|
316
334
|
} catch (error) {
|
|
317
335
|
this.logger.error(`API error: ${pathname}`, error);
|
|
318
336
|
return this.jsonResponse(
|
|
319
|
-
{
|
|
320
|
-
|
|
337
|
+
{
|
|
338
|
+
error: "Internal Server Error",
|
|
339
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
340
|
+
},
|
|
341
|
+
500,
|
|
321
342
|
);
|
|
322
343
|
}
|
|
323
344
|
}
|
|
@@ -339,7 +360,7 @@ export class APIRouteManager {
|
|
|
339
360
|
*/
|
|
340
361
|
private async createContext(
|
|
341
362
|
request: Request,
|
|
342
|
-
params: Record<string, string
|
|
363
|
+
params: Record<string, string>,
|
|
343
364
|
): Promise<APIContext> {
|
|
344
365
|
const url = new URL(request.url);
|
|
345
366
|
|
|
@@ -361,7 +382,10 @@ export class APIRouteManager {
|
|
|
361
382
|
body = await request.text();
|
|
362
383
|
}
|
|
363
384
|
} catch (error) {
|
|
364
|
-
this.logger.warn(
|
|
385
|
+
this.logger.warn(
|
|
386
|
+
"Failed to parse request body",
|
|
387
|
+
error as Record<string, unknown>,
|
|
388
|
+
);
|
|
365
389
|
}
|
|
366
390
|
}
|
|
367
391
|
|
|
@@ -419,7 +443,7 @@ export class APIRouteManager {
|
|
|
419
443
|
context: APIContext,
|
|
420
444
|
middlewares: APIMiddleware[],
|
|
421
445
|
route: APIRouteDefinition,
|
|
422
|
-
method: HTTPMethod
|
|
446
|
+
method: HTTPMethod,
|
|
423
447
|
): Promise<Response> {
|
|
424
448
|
let index = 0;
|
|
425
449
|
|
|
@@ -509,7 +533,9 @@ export class APIRouteManager {
|
|
|
509
533
|
/**
|
|
510
534
|
* Create an API route manager
|
|
511
535
|
*/
|
|
512
|
-
export function createAPIRouteManager(
|
|
536
|
+
export function createAPIRouteManager(
|
|
537
|
+
config: PartialAPIRouteConfig = {},
|
|
538
|
+
): APIRouteManager {
|
|
513
539
|
return new APIRouteManager(config);
|
|
514
540
|
}
|
|
515
541
|
|
|
@@ -518,7 +544,11 @@ export function createAPIRouteManager(config: PartialAPIRouteConfig = {}): APIRo
|
|
|
518
544
|
/**
|
|
519
545
|
* Create a JSON response
|
|
520
546
|
*/
|
|
521
|
-
export function json(
|
|
547
|
+
export function json(
|
|
548
|
+
data: unknown,
|
|
549
|
+
status = 200,
|
|
550
|
+
headers?: Record<string, string>,
|
|
551
|
+
): Response {
|
|
522
552
|
return new Response(JSON.stringify(data), {
|
|
523
553
|
status,
|
|
524
554
|
headers: {
|
|
@@ -531,7 +561,11 @@ export function json(data: unknown, status = 200, headers?: Record<string, strin
|
|
|
531
561
|
/**
|
|
532
562
|
* Create a text response
|
|
533
563
|
*/
|
|
534
|
-
export function text(
|
|
564
|
+
export function text(
|
|
565
|
+
data: string,
|
|
566
|
+
status = 200,
|
|
567
|
+
headers?: Record<string, string>,
|
|
568
|
+
): Response {
|
|
535
569
|
return new Response(data, {
|
|
536
570
|
status,
|
|
537
571
|
headers: {
|
|
@@ -544,7 +578,11 @@ export function text(data: string, status = 200, headers?: Record<string, string
|
|
|
544
578
|
/**
|
|
545
579
|
* Create an HTML response
|
|
546
580
|
*/
|
|
547
|
-
export function html(
|
|
581
|
+
export function html(
|
|
582
|
+
data: string,
|
|
583
|
+
status = 200,
|
|
584
|
+
headers?: Record<string, string>,
|
|
585
|
+
): Response {
|
|
548
586
|
return new Response(data, {
|
|
549
587
|
status,
|
|
550
588
|
headers: {
|
|
@@ -621,8 +659,10 @@ export function noContent(): Response {
|
|
|
621
659
|
* Check if a file is an API route file
|
|
622
660
|
*/
|
|
623
661
|
export function isAPIRouteFile(filename: string): boolean {
|
|
624
|
-
return
|
|
625
|
-
|
|
662
|
+
return (
|
|
663
|
+
SUPPORTED_EXTENSIONS.some((ext) => filename.endsWith(ext)) &&
|
|
664
|
+
!filename.includes("_middleware")
|
|
665
|
+
);
|
|
626
666
|
}
|
|
627
667
|
|
|
628
668
|
/**
|
|
@@ -636,5 +676,7 @@ export function isMiddlewareFile(filename: string): boolean {
|
|
|
636
676
|
* Get HTTP methods from module
|
|
637
677
|
*/
|
|
638
678
|
export function getModuleMethods(module: APIRouteModule): HTTPMethod[] {
|
|
639
|
-
return SUPPORTED_METHODS.filter(
|
|
640
|
-
|
|
679
|
+
return SUPPORTED_METHODS.filter(
|
|
680
|
+
(method) => typeof module[method] === "function",
|
|
681
|
+
);
|
|
682
|
+
}
|