@mantiq/core 0.0.1

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 (82) hide show
  1. package/README.md +19 -0
  2. package/package.json +65 -0
  3. package/src/application/Application.ts +241 -0
  4. package/src/cache/CacheManager.ts +180 -0
  5. package/src/cache/FileCacheStore.ts +113 -0
  6. package/src/cache/MemcachedCacheStore.ts +115 -0
  7. package/src/cache/MemoryCacheStore.ts +62 -0
  8. package/src/cache/NullCacheStore.ts +39 -0
  9. package/src/cache/RedisCacheStore.ts +125 -0
  10. package/src/cache/events.ts +52 -0
  11. package/src/config/ConfigRepository.ts +115 -0
  12. package/src/config/env.ts +26 -0
  13. package/src/container/Container.ts +198 -0
  14. package/src/container/ContextualBindingBuilder.ts +21 -0
  15. package/src/contracts/Cache.ts +49 -0
  16. package/src/contracts/Config.ts +24 -0
  17. package/src/contracts/Container.ts +68 -0
  18. package/src/contracts/DriverManager.ts +16 -0
  19. package/src/contracts/Encrypter.ts +32 -0
  20. package/src/contracts/EventDispatcher.ts +32 -0
  21. package/src/contracts/ExceptionHandler.ts +20 -0
  22. package/src/contracts/Hasher.ts +19 -0
  23. package/src/contracts/Middleware.ts +23 -0
  24. package/src/contracts/Request.ts +54 -0
  25. package/src/contracts/Response.ts +19 -0
  26. package/src/contracts/Router.ts +62 -0
  27. package/src/contracts/ServiceProvider.ts +31 -0
  28. package/src/contracts/Session.ts +47 -0
  29. package/src/encryption/Encrypter.ts +197 -0
  30. package/src/encryption/errors.ts +30 -0
  31. package/src/errors/ConfigKeyNotFoundError.ts +7 -0
  32. package/src/errors/ContainerResolutionError.ts +13 -0
  33. package/src/errors/ForbiddenError.ts +7 -0
  34. package/src/errors/HttpError.ts +16 -0
  35. package/src/errors/MantiqError.ts +16 -0
  36. package/src/errors/NotFoundError.ts +7 -0
  37. package/src/errors/TokenMismatchError.ts +10 -0
  38. package/src/errors/TooManyRequestsError.ts +10 -0
  39. package/src/errors/UnauthorizedError.ts +7 -0
  40. package/src/errors/ValidationError.ts +10 -0
  41. package/src/exceptions/DevErrorPage.ts +564 -0
  42. package/src/exceptions/Handler.ts +118 -0
  43. package/src/hashing/Argon2Hasher.ts +46 -0
  44. package/src/hashing/BcryptHasher.ts +36 -0
  45. package/src/hashing/HashManager.ts +80 -0
  46. package/src/helpers/abort.ts +46 -0
  47. package/src/helpers/app.ts +17 -0
  48. package/src/helpers/cache.ts +12 -0
  49. package/src/helpers/config.ts +15 -0
  50. package/src/helpers/encrypt.ts +22 -0
  51. package/src/helpers/env.ts +1 -0
  52. package/src/helpers/hash.ts +20 -0
  53. package/src/helpers/response.ts +69 -0
  54. package/src/helpers/route.ts +24 -0
  55. package/src/helpers/session.ts +11 -0
  56. package/src/http/Cookie.ts +26 -0
  57. package/src/http/Kernel.ts +252 -0
  58. package/src/http/Request.ts +249 -0
  59. package/src/http/Response.ts +112 -0
  60. package/src/http/UploadedFile.ts +56 -0
  61. package/src/index.ts +97 -0
  62. package/src/macroable/Macroable.ts +174 -0
  63. package/src/middleware/Cors.ts +91 -0
  64. package/src/middleware/EncryptCookies.ts +101 -0
  65. package/src/middleware/Pipeline.ts +66 -0
  66. package/src/middleware/StartSession.ts +90 -0
  67. package/src/middleware/TrimStrings.ts +32 -0
  68. package/src/middleware/VerifyCsrfToken.ts +130 -0
  69. package/src/providers/CoreServiceProvider.ts +97 -0
  70. package/src/routing/ResourceRegistrar.ts +64 -0
  71. package/src/routing/Route.ts +40 -0
  72. package/src/routing/RouteCollection.ts +50 -0
  73. package/src/routing/RouteMatcher.ts +92 -0
  74. package/src/routing/Router.ts +280 -0
  75. package/src/routing/events.ts +19 -0
  76. package/src/session/SessionManager.ts +75 -0
  77. package/src/session/Store.ts +192 -0
  78. package/src/session/handlers/CookieSessionHandler.ts +42 -0
  79. package/src/session/handlers/FileSessionHandler.ts +79 -0
  80. package/src/session/handlers/MemorySessionHandler.ts +35 -0
  81. package/src/websocket/WebSocketContext.ts +20 -0
  82. package/src/websocket/WebSocketKernel.ts +60 -0
@@ -0,0 +1,198 @@
1
+ import type { Bindable, Container, Constructor, Resolvable } from '../contracts/Container.ts'
2
+ import { ContainerResolutionError } from '../errors/ContainerResolutionError.ts'
3
+ import { ContextualBindingBuilder } from './ContextualBindingBuilder.ts'
4
+
5
+ type Binding<T> = {
6
+ concrete: Resolvable<T>
7
+ singleton: boolean
8
+ }
9
+
10
+ export class ContainerImpl implements Container {
11
+ private bindings = new Map<Bindable<any>, Binding<any>>()
12
+ private instances = new Map<Bindable<any>, any>()
13
+ private aliases = new Map<string | symbol, Bindable<any>>()
14
+ /** contextual[concrete][abstract] = resolvable */
15
+ private contextual = new Map<Constructor<any>, Map<Bindable<any>, Resolvable<any>>>()
16
+ /** Track resolution stack for circular dependency detection */
17
+ private resolving = new Set<Bindable<any>>()
18
+
19
+ // ── Registration ──────────────────────────────────────────────────────────
20
+
21
+ bind<T>(abstract: Bindable<T>, concrete: Resolvable<T>): void {
22
+ this.bindings.set(abstract, { concrete, singleton: false })
23
+ // Clear any cached singleton instance if re-binding
24
+ this.instances.delete(abstract)
25
+ }
26
+
27
+ singleton<T>(abstract: Bindable<T>, concrete: Resolvable<T>): void {
28
+ this.bindings.set(abstract, { concrete, singleton: true })
29
+ this.instances.delete(abstract)
30
+ }
31
+
32
+ instance<T>(abstract: Bindable<T>, instance: T): void {
33
+ this.instances.set(abstract, instance)
34
+ }
35
+
36
+ // ── Resolution ────────────────────────────────────────────────────────────
37
+
38
+ make<T>(abstract: Bindable<T>): T {
39
+ // Resolve aliases
40
+ const resolved = this.resolveAlias(abstract)
41
+
42
+ // Return cached singleton instance
43
+ if (this.instances.has(resolved)) {
44
+ return this.instances.get(resolved) as T
45
+ }
46
+
47
+ // Detect circular dependencies
48
+ if (this.resolving.has(resolved)) {
49
+ throw new ContainerResolutionError(
50
+ resolved,
51
+ 'circular_dependency',
52
+ `Circular dependency detected while resolving ${String(resolved)}`,
53
+ )
54
+ }
55
+
56
+ this.resolving.add(resolved)
57
+
58
+ try {
59
+ const binding = this.bindings.get(resolved)
60
+
61
+ let instance: T
62
+
63
+ if (binding) {
64
+ instance = this.build<T>(binding.concrete)
65
+
66
+ if (binding.singleton) {
67
+ this.instances.set(resolved, instance)
68
+ }
69
+ } else if (typeof resolved === 'function') {
70
+ // Auto-resolution: try to instantiate the class directly
71
+ instance = this.autoResolve<T>(resolved as Constructor<T>)
72
+ } else {
73
+ throw new ContainerResolutionError(
74
+ resolved,
75
+ 'not_bound',
76
+ `No binding found for '${String(resolved)}'`,
77
+ )
78
+ }
79
+
80
+ return instance
81
+ } finally {
82
+ this.resolving.delete(resolved)
83
+ }
84
+ }
85
+
86
+ makeOrDefault<T>(abstract: Bindable<T>, defaultValue: T): T {
87
+ try {
88
+ return this.make(abstract)
89
+ } catch {
90
+ return defaultValue
91
+ }
92
+ }
93
+
94
+ has(abstract: Bindable<any>): boolean {
95
+ const resolved = this.resolveAlias(abstract)
96
+ return this.bindings.has(resolved) || this.instances.has(resolved)
97
+ }
98
+
99
+ // ── Contextual Binding ────────────────────────────────────────────────────
100
+
101
+ when(concrete: Constructor<any>): ContextualBindingBuilder {
102
+ return new ContextualBindingBuilder(concrete, (abstract, resolvable) => {
103
+ if (!this.contextual.has(concrete)) {
104
+ this.contextual.set(concrete, new Map())
105
+ }
106
+ this.contextual.get(concrete)!.set(abstract, resolvable)
107
+ })
108
+ }
109
+
110
+ // ── Aliases ───────────────────────────────────────────────────────────────
111
+
112
+ alias(abstract: Bindable<any>, alias: string | symbol): void {
113
+ this.aliases.set(alias, abstract)
114
+ }
115
+
116
+ // ── Flush ─────────────────────────────────────────────────────────────────
117
+
118
+ flush(): void {
119
+ this.bindings.clear()
120
+ this.instances.clear()
121
+ this.aliases.clear()
122
+ this.contextual.clear()
123
+ this.resolving.clear()
124
+ }
125
+
126
+ // ── Method injection ──────────────────────────────────────────────────────
127
+
128
+ call<T>(target: object, method: string, extraParams?: Record<string, any>): T {
129
+ const fn = (target as any)[method]
130
+ if (typeof fn !== 'function') {
131
+ throw new ContainerResolutionError(
132
+ method,
133
+ 'not_bound',
134
+ `Method '${method}' does not exist on target`,
135
+ )
136
+ }
137
+ // @internal: Basic call without deep reflection; packages can override if needed
138
+ const params = extraParams ? Object.values(extraParams) : []
139
+ return fn.apply(target, params) as T
140
+ }
141
+
142
+ // ── Private helpers ───────────────────────────────────────────────────────
143
+
144
+ private resolveAlias(abstract: Bindable<any>): Bindable<any> {
145
+ if (typeof abstract === 'string' || typeof abstract === 'symbol') {
146
+ return this.aliases.get(abstract as string | symbol) ?? abstract
147
+ }
148
+ return abstract
149
+ }
150
+
151
+ private build<T>(concrete: Resolvable<T>): T {
152
+ if (typeof concrete === 'function' && concrete.prototype === undefined) {
153
+ // Arrow function / factory
154
+ return (concrete as (c: Container) => T)(this)
155
+ }
156
+
157
+ if (typeof concrete === 'function') {
158
+ const ctor = concrete as Constructor<T>
159
+ // Check if it looks like a factory (no prototype.constructor === Function means arrow)
160
+ try {
161
+ return this.autoResolve(ctor)
162
+ } catch {
163
+ // If autoResolve fails, try calling as factory
164
+ return (concrete as (c: Container) => T)(this)
165
+ }
166
+ }
167
+
168
+ return (concrete as (c: Container) => T)(this)
169
+ }
170
+
171
+ private autoResolve<T>(ctor: Constructor<T>, parentCtor?: Constructor<any>): T {
172
+ // Try instantiating with no args first (common case)
173
+ try {
174
+ // Check for contextual bindings if we have a parent
175
+ const contextMap = parentCtor ? this.contextual.get(parentCtor) : undefined
176
+
177
+ // Attempt construction. For classes with required deps this will fail
178
+ // but we attempt it for zero-dep classes.
179
+ if (ctor.length === 0) {
180
+ return new ctor()
181
+ }
182
+
183
+ // If we can't auto-resolve (no metadata), throw clearly
184
+ throw new ContainerResolutionError(
185
+ ctor,
186
+ 'unresolvable_parameter',
187
+ `'${ctor.name}' has ${ctor.length} constructor parameter(s) that cannot be auto-resolved. Register an explicit binding.`,
188
+ )
189
+ } catch (err) {
190
+ if (err instanceof ContainerResolutionError) throw err
191
+ throw new ContainerResolutionError(
192
+ ctor,
193
+ 'unresolvable_parameter',
194
+ String(err),
195
+ )
196
+ }
197
+ }
198
+ }
@@ -0,0 +1,21 @@
1
+ import type { Bindable, Resolvable } from '../contracts/Container.ts'
2
+
3
+ type GiveFn = (abstract: Bindable<any>, concrete: Resolvable<any>) => void
4
+
5
+ export class ContextualBindingBuilder {
6
+ private needsAbstract!: Bindable<any>
7
+
8
+ constructor(
9
+ private readonly concrete: new (...args: any[]) => any,
10
+ private readonly giveFn: GiveFn,
11
+ ) {}
12
+
13
+ needs<T>(abstract: Bindable<T>): { give<U extends T>(concrete: Resolvable<U>): void } {
14
+ this.needsAbstract = abstract
15
+ return {
16
+ give: <U>(concrete: Resolvable<U>) => {
17
+ this.giveFn(this.needsAbstract, concrete as Resolvable<any>)
18
+ },
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Contract for cache store implementations.
3
+ */
4
+ export interface CacheStore {
5
+ /**
6
+ * Retrieve an item from the cache.
7
+ * Returns `undefined` if not found or expired.
8
+ */
9
+ get<T = unknown>(key: string): Promise<T | undefined>
10
+
11
+ /**
12
+ * Store an item in the cache.
13
+ * @param ttl Time-to-live in seconds. `undefined` = forever.
14
+ */
15
+ put(key: string, value: unknown, ttl?: number): Promise<void>
16
+
17
+ /**
18
+ * Remove an item from the cache.
19
+ */
20
+ forget(key: string): Promise<boolean>
21
+
22
+ /**
23
+ * Check if an item exists (and is not expired).
24
+ */
25
+ has(key: string): Promise<boolean>
26
+
27
+ /**
28
+ * Remove all items from the cache.
29
+ */
30
+ flush(): Promise<void>
31
+
32
+ /**
33
+ * Increment a numeric value in the cache.
34
+ * Returns the new value.
35
+ */
36
+ increment(key: string, value?: number): Promise<number>
37
+
38
+ /**
39
+ * Decrement a numeric value in the cache.
40
+ * Returns the new value.
41
+ */
42
+ decrement(key: string, value?: number): Promise<number>
43
+
44
+ /**
45
+ * Store an item in the cache if the key does not already exist.
46
+ * Returns true if the value was stored, false if key already exists.
47
+ */
48
+ add(key: string, value: unknown, ttl?: number): Promise<boolean>
49
+ }
@@ -0,0 +1,24 @@
1
+ export interface Config {
2
+ /**
3
+ * Get a config value using dot-notation.
4
+ * @param key - Dot-notation key: 'database.connections.sqlite.path'
5
+ * @param defaultValue - Returned if key doesn't exist
6
+ * @throws ConfigKeyNotFoundError if key doesn't exist and no default provided
7
+ */
8
+ get<T = any>(key: string, defaultValue?: T): T
9
+
10
+ /**
11
+ * Set a config value at runtime (not persisted).
12
+ */
13
+ set(key: string, value: any): void
14
+
15
+ /**
16
+ * Check if a key exists.
17
+ */
18
+ has(key: string): boolean
19
+
20
+ /**
21
+ * Get all config as a flat object.
22
+ */
23
+ all(): Record<string, any>
24
+ }
@@ -0,0 +1,68 @@
1
+ export type Constructor<T = any> = new (...args: any[]) => T
2
+ export type Bindable<T = any> = Constructor<T> | symbol | string
3
+ export type Resolvable<T = any> = Constructor<T> | ((container: Container) => T)
4
+
5
+ export interface ContextualBindingBuilder {
6
+ needs<T>(abstract: Bindable<T>): { give<U extends T>(concrete: Resolvable<U>): void }
7
+ }
8
+
9
+ export interface Container {
10
+ /**
11
+ * Register a transient binding. A new instance is created on each resolve.
12
+ * @param abstract - The interface/class/symbol to bind
13
+ * @param concrete - The implementation class or factory function
14
+ */
15
+ bind<T>(abstract: Bindable<T>, concrete: Resolvable<T>): void
16
+
17
+ /**
18
+ * Register a singleton binding. Created once, cached forever.
19
+ * @param abstract - The interface/class/symbol to bind
20
+ * @param concrete - The implementation class or factory function
21
+ */
22
+ singleton<T>(abstract: Bindable<T>, concrete: Resolvable<T>): void
23
+
24
+ /**
25
+ * Register an existing instance as a singleton.
26
+ * @param abstract - The interface/class/symbol to bind
27
+ * @param instance - The pre-created instance
28
+ */
29
+ instance<T>(abstract: Bindable<T>, instance: T): void
30
+
31
+ /**
32
+ * Resolve a binding from the container.
33
+ * @param abstract - The interface/class/symbol to resolve
34
+ * @throws ContainerResolutionError if the binding can't be resolved
35
+ */
36
+ make<T>(abstract: Bindable<T>): T
37
+
38
+ /**
39
+ * Resolve a binding, or return the default if not bound.
40
+ */
41
+ makeOrDefault<T>(abstract: Bindable<T>, defaultValue: T): T
42
+
43
+ /**
44
+ * Check if a binding exists.
45
+ */
46
+ has(abstract: Bindable<any>): boolean
47
+
48
+ /**
49
+ * Start building a contextual binding.
50
+ * @example container.when(UserController).needs(Logger).give(UserLogger)
51
+ */
52
+ when(concrete: Constructor<any>): ContextualBindingBuilder
53
+
54
+ /**
55
+ * Register an alias for an abstract.
56
+ */
57
+ alias(abstract: Bindable<any>, alias: string | symbol): void
58
+
59
+ /**
60
+ * Remove all bindings and cached instances.
61
+ */
62
+ flush(): void
63
+
64
+ /**
65
+ * Call a method on an object, injecting its dependencies.
66
+ */
67
+ call<T>(target: object, method: string, extraParams?: Record<string, any>): T
68
+ }
@@ -0,0 +1,16 @@
1
+ export interface DriverManager<T> {
2
+ /**
3
+ * Get a driver instance by name. Returns the default driver if omitted.
4
+ */
5
+ driver(name?: string): T
6
+
7
+ /**
8
+ * Register a custom driver factory.
9
+ */
10
+ extend(name: string, factory: () => T): void
11
+
12
+ /**
13
+ * Returns the configured default driver name.
14
+ */
15
+ getDefaultDriver(): string
16
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Contract for encryption services.
3
+ *
4
+ * All implementations must provide symmetric encrypt/decrypt using
5
+ * a consistent serialization format.
6
+ */
7
+ export interface Encrypter {
8
+ /**
9
+ * Encrypt a string value.
10
+ */
11
+ encrypt(value: string): Promise<string>
12
+
13
+ /**
14
+ * Decrypt a string value.
15
+ */
16
+ decrypt(encrypted: string): Promise<string>
17
+
18
+ /**
19
+ * Encrypt a value (serialized as JSON).
20
+ */
21
+ encryptObject(value: unknown): Promise<string>
22
+
23
+ /**
24
+ * Decrypt and deserialize a JSON value.
25
+ */
26
+ decryptObject<T = unknown>(encrypted: string): Promise<T>
27
+
28
+ /**
29
+ * Get the encryption key.
30
+ */
31
+ getKey(): CryptoKey | ArrayBuffer
32
+ }
@@ -0,0 +1,32 @@
1
+ import type { Constructor } from './Container.ts'
2
+
3
+ export abstract class Event {
4
+ readonly timestamp: Date = new Date()
5
+ }
6
+
7
+ export abstract class Listener {
8
+ abstract handle(event: Event): void | Promise<void>
9
+
10
+ shouldQueue: boolean = false
11
+ queue?: string
12
+ connection?: string
13
+ }
14
+
15
+ export type EventHandler = (event: Event) => void | Promise<void>
16
+
17
+ export interface EventDispatcher {
18
+ /**
19
+ * Dispatch an event to all registered listeners.
20
+ */
21
+ emit(event: Event): Promise<void>
22
+
23
+ /**
24
+ * Register a listener for an event class.
25
+ */
26
+ on(eventClass: Constructor<Event>, listener: Constructor<Listener> | EventHandler): void
27
+
28
+ /**
29
+ * Remove all listeners for an event class.
30
+ */
31
+ forget(eventClass: Constructor<Event>): void
32
+ }
@@ -0,0 +1,20 @@
1
+ import type { Constructor } from './Container.ts'
2
+ import type { MantiqRequest } from './Request.ts'
3
+
4
+ export interface ExceptionHandler {
5
+ /**
6
+ * Report the exception (log it, send to error tracker, etc.).
7
+ * Called for every exception unless it's in the dontReport list.
8
+ */
9
+ report(error: Error): Promise<void>
10
+
11
+ /**
12
+ * Render the exception as an HTTP response.
13
+ */
14
+ render(request: MantiqRequest, error: unknown): Response
15
+
16
+ /**
17
+ * Exception classes that should not be reported (e.g., 404s).
18
+ */
19
+ dontReport: Constructor<Error>[]
20
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Contract for hashing services.
3
+ */
4
+ export interface Hasher {
5
+ /**
6
+ * Hash a plain-text value.
7
+ */
8
+ make(value: string): Promise<string>
9
+
10
+ /**
11
+ * Check a plain-text value against a hash.
12
+ */
13
+ check(value: string, hashedValue: string): Promise<boolean>
14
+
15
+ /**
16
+ * Check if the given hash needs to be re-hashed (e.g. cost changed).
17
+ */
18
+ needsRehash(hashedValue: string): boolean
19
+ }
@@ -0,0 +1,23 @@
1
+ import type { MantiqRequest } from './Request.ts'
2
+
3
+ export type NextFunction = () => Promise<Response>
4
+
5
+ export interface Middleware {
6
+ /**
7
+ * Handle an incoming request.
8
+ * Call next() to pass to the next middleware or route handler.
9
+ * Return a Response without calling next() to short-circuit.
10
+ */
11
+ handle(request: MantiqRequest, next: NextFunction): Promise<Response>
12
+
13
+ /**
14
+ * Optional: runs after the response is sent to the client.
15
+ * Useful for logging, cleanup, analytics.
16
+ */
17
+ terminate?(request: MantiqRequest, response: Response): Promise<void>
18
+
19
+ /**
20
+ * Optional: set parameters parsed from the middleware alias string (e.g., 'throttle:60,1').
21
+ */
22
+ setParameters?(params: string[]): void
23
+ }
@@ -0,0 +1,54 @@
1
+ import type { UploadedFile } from '../http/UploadedFile.ts'
2
+ import type { SessionStore } from '../session/Store.ts'
3
+
4
+ export interface MantiqRequest {
5
+ // ── HTTP basics ──────────────────────────────────────────────────────────
6
+ method(): string
7
+ path(): string
8
+ url(): string
9
+ fullUrl(): string
10
+
11
+ // ── Input ────────────────────────────────────────────────────────────────
12
+ query(key: string, defaultValue?: string): string
13
+ query(): Record<string, string>
14
+ input(key: string, defaultValue?: any): Promise<any>
15
+ input(): Promise<Record<string, any>>
16
+ only(...keys: string[]): Promise<Record<string, any>>
17
+ except(...keys: string[]): Promise<Record<string, any>>
18
+ has(...keys: string[]): boolean
19
+ filled(...keys: string[]): Promise<boolean>
20
+
21
+ // ── Headers & metadata ───────────────────────────────────────────────────
22
+ header(key: string, defaultValue?: string): string | undefined
23
+ headers(): Record<string, string>
24
+ cookie(key: string, defaultValue?: string): string | undefined
25
+ setCookies(cookies: Record<string, string>): void
26
+ ip(): string
27
+ userAgent(): string
28
+ accepts(...types: string[]): string | false
29
+ expectsJson(): boolean
30
+ isJson(): boolean
31
+
32
+ // ── Files ────────────────────────────────────────────────────────────────
33
+ file(key: string): UploadedFile | null
34
+ files(key: string): UploadedFile[]
35
+ hasFile(key: string): boolean
36
+
37
+ // ── Route params ─────────────────────────────────────────────────────────
38
+ param(key: string, defaultValue?: any): any
39
+ params(): Record<string, any>
40
+ setRouteParams(params: Record<string, any>): void
41
+
42
+ // ── Session ──────────────────────────────────────────────────────────────
43
+ session(): SessionStore
44
+ setSession(session: SessionStore): void
45
+ hasSession(): boolean
46
+
47
+ // ── Auth ─────────────────────────────────────────────────────────────────
48
+ user<T = any>(): T | null
49
+ isAuthenticated(): boolean
50
+ setUser(user: any): void
51
+
52
+ // ── Raw ──────────────────────────────────────────────────────────────────
53
+ raw(): Request
54
+ }
@@ -0,0 +1,19 @@
1
+ export interface CookieOptions {
2
+ maxAge?: number
3
+ expires?: Date
4
+ path?: string
5
+ domain?: string
6
+ secure?: boolean
7
+ httpOnly?: boolean
8
+ sameSite?: 'Strict' | 'Lax' | 'None'
9
+ }
10
+
11
+ export interface MantiqResponseBuilder {
12
+ status(code: number): this
13
+ header(key: string, value: string): this
14
+ withHeaders(headers: Record<string, string>): this
15
+ cookie(name: string, value: string, options?: CookieOptions): this
16
+ json(data: any): Response
17
+ html(content: string): Response
18
+ redirect(url: string): Response
19
+ }
@@ -0,0 +1,62 @@
1
+ import type { Constructor } from './Container.ts'
2
+ import type { MantiqRequest } from './Request.ts'
3
+
4
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS'
5
+
6
+ export type RouteAction =
7
+ | [Constructor<any>, string]
8
+ | ((request: MantiqRequest) => any)
9
+ | string
10
+
11
+ export interface RouteGroupOptions {
12
+ prefix?: string
13
+ middleware?: string[]
14
+ as?: string
15
+ namespace?: string
16
+ }
17
+
18
+ export interface RouteMatch {
19
+ action: RouteAction
20
+ params: Record<string, any>
21
+ middleware: string[]
22
+ routeName?: string
23
+ }
24
+
25
+ export interface RouteDefinition {
26
+ method: HttpMethod | HttpMethod[]
27
+ path: string
28
+ action: RouteAction
29
+ name?: string
30
+ middleware: string[]
31
+ wheres: Record<string, RegExp>
32
+ }
33
+
34
+ export interface Router {
35
+ get(path: string, action: RouteAction): RouterRoute
36
+ post(path: string, action: RouteAction): RouterRoute
37
+ put(path: string, action: RouteAction): RouterRoute
38
+ patch(path: string, action: RouteAction): RouterRoute
39
+ delete(path: string, action: RouteAction): RouterRoute
40
+ options(path: string, action: RouteAction): RouterRoute
41
+ match(methods: HttpMethod[], path: string, action: RouteAction): RouterRoute
42
+ any(path: string, action: RouteAction): RouterRoute
43
+ resource(name: string, controller: Constructor<any>): void
44
+ apiResource(name: string, controller: Constructor<any>): void
45
+ group(options: RouteGroupOptions, callback: (router: Router) => void): void
46
+ url(name: string, params?: Record<string, any>, absolute?: boolean): string
47
+ resolve(request: MantiqRequest): RouteMatch
48
+ routes(): RouteDefinition[]
49
+ model(param: string, model: Constructor<any>): void
50
+ bind(param: string, resolver: (value: string) => Promise<any>): void
51
+ /** Register controller classes for string-based resolution ('AuthController@login') */
52
+ controllers(map: Record<string, Constructor<any>>): void
53
+ }
54
+
55
+ export interface RouterRoute {
56
+ name(name: string): this
57
+ middleware(...middleware: string[]): this
58
+ where(param: string, pattern: string | RegExp): this
59
+ whereNumber(param: string): this
60
+ whereAlpha(param: string): this
61
+ whereUuid(param: string): this
62
+ }
@@ -0,0 +1,31 @@
1
+ import type { Bindable, Container } from './Container.ts'
2
+
3
+ export abstract class ServiceProvider {
4
+ constructor(protected app: Container) {}
5
+
6
+ /**
7
+ * Register bindings in the container.
8
+ * Called for ALL providers before any boot() methods are called.
9
+ * Do NOT resolve dependencies here — other providers may not be registered yet.
10
+ */
11
+ register(): void | Promise<void> {}
12
+
13
+ /**
14
+ * Boot the service. Called after all providers are registered.
15
+ * Safe to resolve dependencies from the container.
16
+ */
17
+ boot(): void | Promise<void> {}
18
+
19
+ /**
20
+ * If true, this provider is lazy-loaded.
21
+ * It is registered but not booted until one of its bindings is first resolved.
22
+ */
23
+ deferred: boolean = false
24
+
25
+ /**
26
+ * The bindings this provider offers (used for deferred loading).
27
+ */
28
+ provides(): Bindable<any>[] {
29
+ return []
30
+ }
31
+ }