@mantiq/core 0.5.5 → 0.5.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mantiq/core",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "Service container, router, middleware, HTTP kernel, config, and exception handler",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -175,7 +175,7 @@ export class Application extends ContainerImpl {
175
175
 
176
176
  try {
177
177
  const glob = new Bun.Glob('*/package.json')
178
- for await (const file of glob.scan({ cwd: nodeModulesDir, absolute: false })) {
178
+ for await (const file of glob.scan({ cwd: nodeModulesDir, absolute: false, followSymlinks: true })) {
179
179
  try {
180
180
  const pkgJson = JSON.parse(
181
181
  await Bun.file(`${nodeModulesDir}/${file}`).text()
@@ -188,12 +188,14 @@ export class Application extends ContainerImpl {
188
188
  if (mod[providerName] && typeof mod[providerName] === 'function') {
189
189
  providers.push(mod[providerName])
190
190
  }
191
- } catch {
192
- // Skip packages that can't be loaded
191
+ } catch (e) {
192
+ if (process.env.APP_DEBUG === 'true') {
193
+ console.warn(`[Mantiq] Failed to load provider from ${file}:`, (e as Error)?.message ?? e)
194
+ }
193
195
  }
194
196
  }
195
197
  } catch {
196
- // node_modules/@mantiq doesn't exist
198
+ // node_modules/@mantiq doesn't exist — expected when no @mantiq packages installed
197
199
  }
198
200
 
199
201
  return providers
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Dump values to console and die (exit process).
3
+ * Laravel's dd() equivalent.
4
+ *
5
+ * @example dd(user)
6
+ * @example dd(user, request.query(), 'debug')
7
+ */
8
+ export function dd(...args: any[]): never {
9
+ dump(...args)
10
+ process.exit(1)
11
+ }
12
+
13
+ /**
14
+ * Dump values to console with colorized, inspected output.
15
+ * Like dd() but doesn't exit.
16
+ *
17
+ * @example dump(user)
18
+ * @example dump(user, request.query())
19
+ */
20
+ export function dump(...args: any[]): void {
21
+ for (const arg of args) {
22
+ if (arg === null) {
23
+ console.log('\x1b[2mnull\x1b[0m')
24
+ } else if (arg === undefined) {
25
+ console.log('\x1b[2mundefined\x1b[0m')
26
+ } else if (typeof arg === 'string') {
27
+ console.log(`\x1b[32m"${arg}"\x1b[0m`)
28
+ } else if (typeof arg === 'number' || typeof arg === 'bigint') {
29
+ console.log(`\x1b[33m${arg}\x1b[0m`)
30
+ } else if (typeof arg === 'boolean') {
31
+ console.log(`\x1b[35m${arg}\x1b[0m`)
32
+ } else if (arg instanceof Error) {
33
+ console.log(`\x1b[31m${arg.constructor.name}: ${arg.message}\x1b[0m`)
34
+ if (arg.stack) {
35
+ const frames = arg.stack.split('\n').slice(1, 6).map(l => ` \x1b[2m${l.trim()}\x1b[0m`)
36
+ console.log(frames.join('\n'))
37
+ }
38
+ } else if (Array.isArray(arg)) {
39
+ console.dir(arg, { depth: 4, colors: true })
40
+ } else if (typeof arg === 'object') {
41
+ // Model instances — use toObject() if available
42
+ if (typeof arg.toObject === 'function') {
43
+ console.log(`\x1b[36m${arg.constructor?.name ?? 'Object'}\x1b[0m`)
44
+ console.dir(arg.toObject(), { depth: 4, colors: true })
45
+ } else {
46
+ console.dir(arg, { depth: 4, colors: true })
47
+ }
48
+ } else {
49
+ console.log(arg)
50
+ }
51
+ }
52
+ }
@@ -0,0 +1,71 @@
1
+ import { Application } from '../application/Application.ts'
2
+
3
+ /**
4
+ * Get the application base path or a path relative to it.
5
+ *
6
+ * @example base_path() // '/Users/you/my-app'
7
+ * @example base_path('config') // '/Users/you/my-app/config'
8
+ */
9
+ export function base_path(path: string = ''): string {
10
+ return Application.getInstance().basePath_(path)
11
+ }
12
+
13
+ /**
14
+ * Get the app directory path.
15
+ *
16
+ * @example app_path() // '/Users/you/my-app/app'
17
+ * @example app_path('Models') // '/Users/you/my-app/app/Models'
18
+ */
19
+ export function app_path(path: string = ''): string {
20
+ return base_path(path ? `app/${path}` : 'app')
21
+ }
22
+
23
+ /**
24
+ * Get the config directory path.
25
+ *
26
+ * @example config_path() // '/Users/you/my-app/config'
27
+ * @example config_path('app.ts') // '/Users/you/my-app/config/app.ts'
28
+ */
29
+ export function config_path(path: string = ''): string {
30
+ return Application.getInstance().configPath(path)
31
+ }
32
+
33
+ /**
34
+ * Get the database directory path.
35
+ *
36
+ * @example database_path() // '/Users/you/my-app/database'
37
+ * @example database_path('migrations') // '/Users/you/my-app/database/migrations'
38
+ */
39
+ export function database_path(path: string = ''): string {
40
+ return base_path(path ? `database/${path}` : 'database')
41
+ }
42
+
43
+ /**
44
+ * Get the storage directory path.
45
+ *
46
+ * @example storage_path() // '/Users/you/my-app/storage'
47
+ * @example storage_path('logs') // '/Users/you/my-app/storage/logs'
48
+ */
49
+ export function storage_path(path: string = ''): string {
50
+ return Application.getInstance().storagePath(path)
51
+ }
52
+
53
+ /**
54
+ * Get the public directory path.
55
+ *
56
+ * @example public_path() // '/Users/you/my-app/public'
57
+ * @example public_path('build') // '/Users/you/my-app/public/build'
58
+ */
59
+ export function public_path(path: string = ''): string {
60
+ return base_path(path ? `public/${path}` : 'public')
61
+ }
62
+
63
+ /**
64
+ * Get the resources directory path.
65
+ *
66
+ * @example resource_path() // '/Users/you/my-app/resources'
67
+ * @example resource_path('views') // '/Users/you/my-app/resources/views'
68
+ */
69
+ export function resource_path(path: string = ''): string {
70
+ return base_path(path ? `resources/${path}` : 'resources')
71
+ }
@@ -100,8 +100,8 @@ export class HttpKernel {
100
100
  const request = MantiqRequest.fromBun(bunRequest)
101
101
 
102
102
  try {
103
- // Combine prepend + global + append middleware
104
- const allMiddleware = [...this.prependMiddleware, ...this.globalMiddleware, ...this.appendMiddleware]
103
+ // Combine prepend + global + append middleware (deduplicated, preserving order)
104
+ const allMiddleware = [...new Set([...this.prependMiddleware, ...this.globalMiddleware, ...this.appendMiddleware])]
105
105
  const globalClasses = this.resolveMiddlewareList(allMiddleware)
106
106
 
107
107
  const response = await new Pipeline(this.container)
package/src/index.ts CHANGED
@@ -100,3 +100,5 @@ export { response, json, html, redirect, noContent, stream, download } from './h
100
100
  export { hash, hashCheck } from './helpers/hash.ts'
101
101
  export { cache } from './helpers/cache.ts'
102
102
  export { session } from './helpers/session.ts'
103
+ export { dd, dump } from './helpers/dd.ts'
104
+ export { base_path, app_path, config_path, database_path, storage_path, public_path, resource_path } from './helpers/paths.ts'
@@ -19,7 +19,7 @@ import { serializeCookie } from '../http/Cookie.ts'
19
19
  */
20
20
  export class VerifyCsrfToken implements Middleware {
21
21
  /** URIs that should be excluded from CSRF verification. */
22
- protected except: string[] = []
22
+ protected except: string[] = ['/api/*']
23
23
 
24
24
  constructor(private readonly encrypter: AesEncrypter) {}
25
25
 
@@ -39,8 +39,12 @@ export class VerifyCsrfToken implements Middleware {
39
39
  // Read-only methods don't need CSRF
40
40
  if (['GET', 'HEAD', 'OPTIONS'].includes(method)) return false
41
41
 
42
- // Check exclusions
43
42
  const path = request.path()
43
+
44
+ // API routes are always excluded — token auth is inherently CSRF-safe
45
+ if (path.startsWith('/api/') || path === '/api') return false
46
+
47
+ // Check user-defined exclusions
44
48
  return !this.except.some((pattern) => {
45
49
  if (pattern.endsWith('*')) {
46
50
  return path.startsWith(pattern.slice(0, -1))
@@ -77,7 +81,9 @@ export class VerifyCsrfToken implements Middleware {
77
81
  const xsrfHeader = request.header('x-xsrf-token')
78
82
  if (xsrfHeader) {
79
83
  try {
80
- return await this.encrypter.decrypt(xsrfHeader)
84
+ // Cookie values may be URL-encoded — decode before decrypting
85
+ const decoded = decodeURIComponent(xsrfHeader)
86
+ return await this.encrypter.decrypt(decoded)
81
87
  } catch {
82
88
  return null
83
89
  }
@@ -110,7 +110,7 @@ export class CoreServiceProvider extends ServiceProvider {
110
110
  // Set default global middleware stack (can be overridden in config/app.ts)
111
111
  const configRepo = this.app.make(ConfigRepository)
112
112
  const globalMiddleware = configRepo.get('app.middleware', [
113
- 'cors', 'encrypt.cookies', 'session',
113
+ 'cors', 'encrypt.cookies', 'session', 'csrf',
114
114
  ]) as string[]
115
115
  kernel.setGlobalMiddleware(globalMiddleware)
116
116
  }