@stravigor/core 0.1.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 +45 -0
- package/package.json +83 -0
- package/src/auth/access_token.ts +122 -0
- package/src/auth/auth.ts +86 -0
- package/src/auth/index.ts +7 -0
- package/src/auth/middleware/authenticate.ts +64 -0
- package/src/auth/middleware/csrf.ts +62 -0
- package/src/auth/middleware/guest.ts +46 -0
- package/src/broadcast/broadcast_manager.ts +411 -0
- package/src/broadcast/client.ts +302 -0
- package/src/broadcast/index.ts +58 -0
- package/src/cache/cache_manager.ts +56 -0
- package/src/cache/cache_store.ts +31 -0
- package/src/cache/helpers.ts +74 -0
- package/src/cache/http_cache.ts +109 -0
- package/src/cache/index.ts +6 -0
- package/src/cache/memory_store.ts +63 -0
- package/src/cli/bootstrap.ts +37 -0
- package/src/cli/commands/generate_api.ts +74 -0
- package/src/cli/commands/generate_key.ts +46 -0
- package/src/cli/commands/generate_models.ts +48 -0
- package/src/cli/commands/migration_compare.ts +152 -0
- package/src/cli/commands/migration_fresh.ts +123 -0
- package/src/cli/commands/migration_generate.ts +79 -0
- package/src/cli/commands/migration_rollback.ts +53 -0
- package/src/cli/commands/migration_run.ts +44 -0
- package/src/cli/commands/queue_flush.ts +35 -0
- package/src/cli/commands/queue_retry.ts +34 -0
- package/src/cli/commands/queue_work.ts +40 -0
- package/src/cli/commands/scheduler_work.ts +45 -0
- package/src/cli/strav.ts +33 -0
- package/src/config/configuration.ts +105 -0
- package/src/config/loaders/base_loader.ts +69 -0
- package/src/config/loaders/env_loader.ts +112 -0
- package/src/config/loaders/typescript_loader.ts +56 -0
- package/src/config/types.ts +8 -0
- package/src/core/application.ts +4 -0
- package/src/core/container.ts +117 -0
- package/src/core/index.ts +3 -0
- package/src/core/inject.ts +39 -0
- package/src/database/database.ts +54 -0
- package/src/database/index.ts +30 -0
- package/src/database/introspector.ts +446 -0
- package/src/database/migration/differ.ts +308 -0
- package/src/database/migration/file_generator.ts +125 -0
- package/src/database/migration/index.ts +18 -0
- package/src/database/migration/runner.ts +133 -0
- package/src/database/migration/sql_generator.ts +378 -0
- package/src/database/migration/tracker.ts +76 -0
- package/src/database/migration/types.ts +189 -0
- package/src/database/query_builder.ts +474 -0
- package/src/encryption/encryption_manager.ts +209 -0
- package/src/encryption/helpers.ts +158 -0
- package/src/encryption/index.ts +3 -0
- package/src/encryption/types.ts +6 -0
- package/src/events/emitter.ts +101 -0
- package/src/events/index.ts +2 -0
- package/src/exceptions/errors.ts +75 -0
- package/src/exceptions/exception_handler.ts +126 -0
- package/src/exceptions/helpers.ts +25 -0
- package/src/exceptions/http_exception.ts +129 -0
- package/src/exceptions/index.ts +23 -0
- package/src/exceptions/strav_error.ts +11 -0
- package/src/generators/api_generator.ts +972 -0
- package/src/generators/config.ts +87 -0
- package/src/generators/doc_generator.ts +974 -0
- package/src/generators/index.ts +11 -0
- package/src/generators/model_generator.ts +586 -0
- package/src/generators/route_generator.ts +188 -0
- package/src/generators/test_generator.ts +1666 -0
- package/src/helpers/crypto.ts +4 -0
- package/src/helpers/env.ts +50 -0
- package/src/helpers/identity.ts +12 -0
- package/src/helpers/index.ts +4 -0
- package/src/helpers/strings.ts +67 -0
- package/src/http/context.ts +215 -0
- package/src/http/cookie.ts +59 -0
- package/src/http/cors.ts +163 -0
- package/src/http/index.ts +16 -0
- package/src/http/middleware.ts +39 -0
- package/src/http/rate_limit.ts +173 -0
- package/src/http/router.ts +556 -0
- package/src/http/server.ts +79 -0
- package/src/i18n/defaults/en/validation.json +20 -0
- package/src/i18n/helpers.ts +72 -0
- package/src/i18n/i18n_manager.ts +155 -0
- package/src/i18n/index.ts +4 -0
- package/src/i18n/middleware.ts +90 -0
- package/src/i18n/translator.ts +96 -0
- package/src/i18n/types.ts +17 -0
- package/src/logger/index.ts +6 -0
- package/src/logger/logger.ts +100 -0
- package/src/logger/request_logger.ts +19 -0
- package/src/logger/sinks/console_sink.ts +24 -0
- package/src/logger/sinks/file_sink.ts +24 -0
- package/src/logger/sinks/sink.ts +36 -0
- package/src/mail/css_inliner.ts +79 -0
- package/src/mail/helpers.ts +212 -0
- package/src/mail/index.ts +19 -0
- package/src/mail/mail_manager.ts +92 -0
- package/src/mail/transports/log_transport.ts +69 -0
- package/src/mail/transports/resend_transport.ts +59 -0
- package/src/mail/transports/sendgrid_transport.ts +77 -0
- package/src/mail/transports/smtp_transport.ts +48 -0
- package/src/mail/types.ts +80 -0
- package/src/notification/base_notification.ts +67 -0
- package/src/notification/channels/database_channel.ts +30 -0
- package/src/notification/channels/discord_channel.ts +43 -0
- package/src/notification/channels/email_channel.ts +37 -0
- package/src/notification/channels/webhook_channel.ts +45 -0
- package/src/notification/helpers.ts +214 -0
- package/src/notification/index.ts +20 -0
- package/src/notification/notification_manager.ts +126 -0
- package/src/notification/types.ts +122 -0
- package/src/orm/base_model.ts +351 -0
- package/src/orm/decorators.ts +127 -0
- package/src/orm/index.ts +4 -0
- package/src/policy/authorize.ts +44 -0
- package/src/policy/index.ts +3 -0
- package/src/policy/policy_result.ts +13 -0
- package/src/queue/index.ts +11 -0
- package/src/queue/queue.ts +338 -0
- package/src/queue/worker.ts +197 -0
- package/src/scheduler/cron.ts +140 -0
- package/src/scheduler/index.ts +7 -0
- package/src/scheduler/runner.ts +116 -0
- package/src/scheduler/schedule.ts +183 -0
- package/src/scheduler/scheduler.ts +47 -0
- package/src/schema/database_representation.ts +122 -0
- package/src/schema/define_association.ts +60 -0
- package/src/schema/define_schema.ts +46 -0
- package/src/schema/field_builder.ts +155 -0
- package/src/schema/field_definition.ts +66 -0
- package/src/schema/index.ts +21 -0
- package/src/schema/naming.ts +19 -0
- package/src/schema/postgres.ts +109 -0
- package/src/schema/registry.ts +157 -0
- package/src/schema/representation_builder.ts +479 -0
- package/src/schema/type_builder.ts +107 -0
- package/src/schema/types.ts +35 -0
- package/src/session/index.ts +4 -0
- package/src/session/middleware.ts +46 -0
- package/src/session/session.ts +308 -0
- package/src/session/session_manager.ts +81 -0
- package/src/storage/index.ts +13 -0
- package/src/storage/local_driver.ts +46 -0
- package/src/storage/s3_driver.ts +51 -0
- package/src/storage/storage.ts +43 -0
- package/src/storage/storage_manager.ts +59 -0
- package/src/storage/types.ts +42 -0
- package/src/storage/upload.ts +91 -0
- package/src/validation/index.ts +18 -0
- package/src/validation/rules.ts +170 -0
- package/src/validation/validate.ts +41 -0
- package/src/view/cache.ts +47 -0
- package/src/view/client/islands.ts +50 -0
- package/src/view/compiler.ts +185 -0
- package/src/view/engine.ts +139 -0
- package/src/view/escape.ts +14 -0
- package/src/view/index.ts +13 -0
- package/src/view/islands/island_builder.ts +161 -0
- package/src/view/islands/vue_plugin.ts +140 -0
- package/src/view/middleware/static.ts +35 -0
- package/src/view/tokenizer.ts +172 -0
- package/tsconfig.json +4 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { Middleware } from './middleware.ts'
|
|
2
|
+
import type Context from './context.ts'
|
|
3
|
+
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Store interface
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
export interface RateLimitInfo {
|
|
9
|
+
/** Total allowed requests in the window. */
|
|
10
|
+
limit: number
|
|
11
|
+
/** Remaining requests in the current window. */
|
|
12
|
+
remaining: number
|
|
13
|
+
/** Unix timestamp (ms) when the window resets. */
|
|
14
|
+
resetTime: number
|
|
15
|
+
/** Whether the current request exceeds the limit. */
|
|
16
|
+
exceeded: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Pluggable storage backend for rate limit counters.
|
|
21
|
+
* Implement this interface to use Redis, database, or distributed stores.
|
|
22
|
+
*/
|
|
23
|
+
export interface RateLimitStore {
|
|
24
|
+
increment(key: string, window: number, max: number): RateLimitInfo | Promise<RateLimitInfo>
|
|
25
|
+
reset(key: string): void | Promise<void>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
// In-memory store (fixed window)
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
interface WindowEntry {
|
|
33
|
+
count: number
|
|
34
|
+
resetTime: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* In-memory rate limit store using fixed time windows.
|
|
39
|
+
* Entries are lazily cleaned up on access. Suitable for single-process deployments.
|
|
40
|
+
*/
|
|
41
|
+
export class MemoryStore implements RateLimitStore {
|
|
42
|
+
private windows = new Map<string, WindowEntry>()
|
|
43
|
+
|
|
44
|
+
increment(key: string, window: number, max: number): RateLimitInfo {
|
|
45
|
+
const now = Date.now()
|
|
46
|
+
const entry = this.windows.get(key)
|
|
47
|
+
|
|
48
|
+
if (entry && now < entry.resetTime) {
|
|
49
|
+
entry.count++
|
|
50
|
+
return {
|
|
51
|
+
limit: max,
|
|
52
|
+
remaining: Math.max(0, max - entry.count),
|
|
53
|
+
resetTime: entry.resetTime,
|
|
54
|
+
exceeded: entry.count > max,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const resetTime = now + window
|
|
59
|
+
this.windows.set(key, { count: 1, resetTime })
|
|
60
|
+
|
|
61
|
+
if (this.windows.size > 10_000) this.cleanup(now)
|
|
62
|
+
|
|
63
|
+
return { limit: max, remaining: max - 1, resetTime, exceeded: false }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
reset(key: string): void {
|
|
67
|
+
this.windows.delete(key)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private cleanup(now: number): void {
|
|
71
|
+
for (const [key, entry] of this.windows) {
|
|
72
|
+
if (now >= entry.resetTime) this.windows.delete(key)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
// Options
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
|
|
81
|
+
export interface RateLimitOptions {
|
|
82
|
+
/** Time window in milliseconds. @default 60_000 */
|
|
83
|
+
window?: number
|
|
84
|
+
|
|
85
|
+
/** Maximum requests allowed in the window. @default 60 */
|
|
86
|
+
max?: number
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Extract the rate limit key from the request context.
|
|
90
|
+
* Defaults to client IP via X-Forwarded-For / X-Real-IP.
|
|
91
|
+
*/
|
|
92
|
+
keyExtractor?: (ctx: Context) => string
|
|
93
|
+
|
|
94
|
+
/** Return `true` to bypass the rate limit check. */
|
|
95
|
+
skip?: (ctx: Context) => boolean
|
|
96
|
+
|
|
97
|
+
/** Custom storage backend. Defaults to an in-memory fixed-window store. */
|
|
98
|
+
store?: RateLimitStore
|
|
99
|
+
|
|
100
|
+
/** Custom response when rate limit is exceeded. */
|
|
101
|
+
onLimitReached?: (ctx: Context, info: RateLimitInfo) => Response
|
|
102
|
+
|
|
103
|
+
/** Whether to add X-RateLimit-* headers to successful responses. @default true */
|
|
104
|
+
headers?: boolean
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Default key extractor
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
function defaultKeyExtractor(ctx: Context): string {
|
|
112
|
+
const forwarded = ctx.header('x-forwarded-for')
|
|
113
|
+
if (forwarded) return forwarded.split(',')[0]!.trim()
|
|
114
|
+
|
|
115
|
+
const realIp = ctx.header('x-real-ip')
|
|
116
|
+
if (realIp) return realIp
|
|
117
|
+
|
|
118
|
+
return 'unknown'
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// Middleware factory
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
export function rateLimit(options: RateLimitOptions = {}): Middleware {
|
|
126
|
+
const {
|
|
127
|
+
window = 60_000,
|
|
128
|
+
max = 60,
|
|
129
|
+
keyExtractor = defaultKeyExtractor,
|
|
130
|
+
skip,
|
|
131
|
+
store = new MemoryStore(),
|
|
132
|
+
onLimitReached,
|
|
133
|
+
headers: addHeaders = true,
|
|
134
|
+
} = options
|
|
135
|
+
|
|
136
|
+
return async (ctx, next) => {
|
|
137
|
+
if (skip?.(ctx)) return next()
|
|
138
|
+
|
|
139
|
+
const key = keyExtractor(ctx)
|
|
140
|
+
const info = await store.increment(key, window, max)
|
|
141
|
+
|
|
142
|
+
if (info.exceeded) {
|
|
143
|
+
if (onLimitReached) return onLimitReached(ctx, info)
|
|
144
|
+
|
|
145
|
+
const retryAfter = Math.ceil((info.resetTime - Date.now()) / 1000)
|
|
146
|
+
return new Response(JSON.stringify({ error: 'Too Many Requests' }), {
|
|
147
|
+
status: 429,
|
|
148
|
+
headers: {
|
|
149
|
+
'Content-Type': 'application/json',
|
|
150
|
+
'X-RateLimit-Limit': String(info.limit),
|
|
151
|
+
'X-RateLimit-Remaining': '0',
|
|
152
|
+
'X-RateLimit-Reset': String(Math.ceil(info.resetTime / 1000)),
|
|
153
|
+
'Retry-After': String(Math.max(0, retryAfter)),
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const response = await next()
|
|
159
|
+
|
|
160
|
+
if (!addHeaders) return response
|
|
161
|
+
|
|
162
|
+
const headers = new Headers(response.headers)
|
|
163
|
+
headers.set('X-RateLimit-Limit', String(info.limit))
|
|
164
|
+
headers.set('X-RateLimit-Remaining', String(info.remaining))
|
|
165
|
+
headers.set('X-RateLimit-Reset', String(Math.ceil(info.resetTime / 1000)))
|
|
166
|
+
|
|
167
|
+
return new Response(response.body, {
|
|
168
|
+
status: response.status,
|
|
169
|
+
statusText: response.statusText,
|
|
170
|
+
headers,
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
}
|