create-mantiq 0.5.15 → 0.5.17

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": "create-mantiq",
3
- "version": "0.5.15",
3
+ "version": "0.5.17",
4
4
  "description": "Scaffold a new MantiqJS application",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -188,6 +188,10 @@ if (kit) {
188
188
  { stub: 'api-only/routes/api.ts.stub', target: 'routes/api.ts' },
189
189
  { stub: 'shared/app/Http/Controllers/ApiAuthController.ts.stub', target: 'app/Http/Controllers/ApiAuthController.ts' },
190
190
  { stub: 'shared/app/Http/Controllers/UserController.ts.stub', target: 'app/Http/Controllers/UserController.ts' },
191
+ { stub: 'shared/app/Http/Requests/RegisterRequest.ts.stub', target: 'app/Http/Requests/RegisterRequest.ts' },
192
+ { stub: 'shared/app/Http/Requests/LoginRequest.ts.stub', target: 'app/Http/Requests/LoginRequest.ts' },
193
+ { stub: 'shared/app/Http/Requests/StoreUserRequest.ts.stub', target: 'app/Http/Requests/StoreUserRequest.ts' },
194
+ { stub: 'shared/app/Http/Requests/UpdateUserRequest.ts.stub', target: 'app/Http/Requests/UpdateUserRequest.ts' },
191
195
  { stub: 'shared/database/seeders/DatabaseSeeder.ts.stub', target: 'database/seeders/DatabaseSeeder.ts' },
192
196
  { stub: 'shared/database/factories/UserFactory.ts.stub', target: 'database/factories/UserFactory.ts' },
193
197
  ]
@@ -2,19 +2,23 @@ import type { Router } from '@mantiq/core'
2
2
  import { json } from '@mantiq/core'
3
3
  import { ApiAuthController } from '../app/Http/Controllers/ApiAuthController.ts'
4
4
  import { UserController } from '../app/Http/Controllers/UserController.ts'
5
+ import { RegisterRequest } from '../app/Http/Requests/RegisterRequest.ts'
6
+ import { LoginRequest } from '../app/Http/Requests/LoginRequest.ts'
7
+ import { StoreUserRequest } from '../app/Http/Requests/StoreUserRequest.ts'
8
+ import { UpdateUserRequest } from '../app/Http/Requests/UpdateUserRequest.ts'
5
9
 
6
10
  export default function (router: Router) {
7
11
  router.get('/ping', () => json({ status: 'ok', timestamp: new Date().toISOString() }))
8
12
 
9
- // Token auth — register/login return a bearer token
10
- router.post('/register', [ApiAuthController, 'register'])
11
- router.post('/login', [ApiAuthController, 'login'])
13
+ // Token auth — FormRequest auto-validates, controller receives validated data
14
+ router.post('/register', [ApiAuthController, 'register', RegisterRequest])
15
+ router.post('/login', [ApiAuthController, 'login', LoginRequest])
12
16
  router.post('/logout', [ApiAuthController, 'logout']).middleware('auth:api')
13
17
  router.get('/user', [ApiAuthController, 'user']).middleware('auth:api')
14
18
 
15
19
  // Users CRUD (protected)
16
20
  router.get('/users', [UserController, 'index']).middleware('auth:api')
17
- router.post('/users', [UserController, 'store']).middleware('auth:api')
18
- router.put('/users/:id', [UserController, 'update']).middleware('auth:api')
21
+ router.post('/users', [UserController, 'store', StoreUserRequest]).middleware('auth:api')
22
+ router.put('/users/:id', [UserController, 'update', UpdateUserRequest]).middleware('auth:api')
19
23
  router.delete('/users/:id', [UserController, 'destroy']).middleware('auth:api')
20
24
  }
@@ -1399,6 +1399,22 @@
1399
1399
  "stub": "app/Http/Controllers/UserController.ts.stub",
1400
1400
  "target": "app/Http/Controllers/UserController.ts"
1401
1401
  },
1402
+ {
1403
+ "stub": "app/Http/Requests/RegisterRequest.ts.stub",
1404
+ "target": "app/Http/Requests/RegisterRequest.ts"
1405
+ },
1406
+ {
1407
+ "stub": "app/Http/Requests/LoginRequest.ts.stub",
1408
+ "target": "app/Http/Requests/LoginRequest.ts"
1409
+ },
1410
+ {
1411
+ "stub": "app/Http/Requests/StoreUserRequest.ts.stub",
1412
+ "target": "app/Http/Requests/StoreUserRequest.ts"
1413
+ },
1414
+ {
1415
+ "stub": "app/Http/Requests/UpdateUserRequest.ts.stub",
1416
+ "target": "app/Http/Requests/UpdateUserRequest.ts"
1417
+ },
1402
1418
  {
1403
1419
  "stub": "routes/web.ts.stub",
1404
1420
  "target": "routes/web.ts"
@@ -1,5 +1,7 @@
1
1
  import { useState, useCallback, useEffect } from 'react'
2
2
 
3
+ declare global { interface Window { __MANTIQ_DATA__?: Record<string, any> } }
4
+
3
5
  interface MantiqAppProps {
4
6
  pages: Record<string, React.ComponentType<any>>
5
7
  initialData?: Record<string, any>
@@ -15,7 +17,7 @@ function initTheme() {
15
17
  initTheme()
16
18
 
17
19
  export function MantiqApp({ pages, initialData }: MantiqAppProps) {
18
- const windowData = typeof window !== 'undefined' ? (window as any).__MANTIQ_DATA__ : {}
20
+ const windowData = typeof window !== 'undefined' ? window.__MANTIQ_DATA__ : {}
19
21
  const initial = initialData ?? windowData
20
22
  const [page, setPage] = useState<string>(initial._page ?? 'Login')
21
23
  const [data, setData] = useState<Record<string, any>>(initial)
@@ -3,7 +3,9 @@ function getCookie(name: string): string | null {
3
3
  return match ? decodeURIComponent(match[1]!) : null
4
4
  }
5
5
 
6
- export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<{ ok: boolean; status: number; data: T }> {
6
+ type ApiResult<T> = { ok: true; status: number; data: T } | { ok: false; status: number; data: null }
7
+
8
+ export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<ApiResult<T>> {
7
9
  const headers: Record<string, string> = { Accept: 'application/json', ...(opts.headers as Record<string, string>) }
8
10
 
9
11
  // Attach XSRF token for CSRF protection on mutating requests
@@ -17,22 +19,23 @@ export async function api<T = any>(url: string, opts: RequestInit = {}): Promise
17
19
  // Session expired — redirect to login
18
20
  if (res.status === 401 || res.status === 419) {
19
21
  window.location.href = '/login'
20
- return { ok: false, status: res.status, data: null as any }
22
+ return { ok: false, status: res.status, data: null }
21
23
  }
22
24
 
23
25
  const ct = res.headers.get('content-type') ?? ''
24
26
  const data = ct.includes('json') ? await res.json() : null
25
- return { ok: res.ok, status: res.status, data }
27
+ if (!res.ok) return { ok: false, status: res.status, data: null }
28
+ return { ok: true, status: res.status, data }
26
29
  }
27
30
 
28
- export function post(url: string, body: object) {
29
- return api(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
31
+ export function post<T = any>(url: string, body: object) {
32
+ return api<T>(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
30
33
  }
31
34
 
32
- export function put(url: string, body: object) {
33
- return api(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
35
+ export function put<T = any>(url: string, body: object) {
36
+ return api<T>(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
34
37
  }
35
38
 
36
- export function del(url: string) {
37
- return api(url, { method: 'DELETE' })
39
+ export function del<T = any>(url: string) {
40
+ return api<T>(url, { method: 'DELETE' })
38
41
  }
@@ -8,33 +8,27 @@ import { User } from '../../Models/User.ts'
8
8
  * Issues bearer tokens instead of session cookies.
9
9
  */
10
10
  export class ApiAuthController {
11
- async register(request: MantiqRequest): Promise<Response> {
12
- const body = await request.input() as { name?: string; email?: string; password?: string; device_name?: string }
13
- if (!body.name || !body.email || !body.password) abort(422, 'Name, email and password are required.')
14
- if (body.password!.length < 6) abort(422, 'Password must be at least 6 characters.')
15
- if (await User.where('email', body.email).first()) abort(422, 'A user with this email already exists.')
11
+ async register(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
12
+ if (await User.where('email', data.email).first()) abort(422, 'A user with this email already exists.')
16
13
 
17
14
  const user = await User.create({
18
- name: body.name,
19
- email: body.email,
20
- password: await hash(body.password!),
15
+ name: data.name,
16
+ email: data.email,
17
+ password: await hash(data.password),
21
18
  })
22
19
 
23
- const { plainTextToken } = await (user as any).createToken(body.device_name ?? 'api')
20
+ const { plainTextToken } = await user.createToken(data.device_name ?? 'api')
24
21
 
25
22
  return json({ message: 'Registered.', user: user.toObject(), token: plainTextToken }, 201)
26
23
  }
27
24
 
28
- async login(request: MantiqRequest): Promise<Response> {
29
- const body = await request.input() as { email?: string; password?: string; device_name?: string }
30
- if (!body.email || !body.password) abort(422, 'Email and password are required.')
31
-
32
- const user = await User.where('email', body.email).first()
33
- if (!user || !(await hashCheck(body.password!, user.getAuthPassword()))) {
25
+ async login(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
26
+ const user = await User.where('email', data.email).first()
27
+ if (!user || !(await hashCheck(data.password, user.getAuthPassword()))) {
34
28
  abort(401, 'Invalid credentials.')
35
29
  }
36
30
 
37
- const { plainTextToken } = await (user as any).createToken(body.device_name ?? 'api')
31
+ const { plainTextToken } = await user!.createToken(data.device_name ?? 'api')
38
32
 
39
33
  return json({ message: 'Logged in.', user: user!.toObject(), token: plainTextToken })
40
34
  }
@@ -45,7 +39,7 @@ export class ApiAuthController {
45
39
  const user = await manager.guard('api').user()
46
40
 
47
41
  if (user) {
48
- const token = (user as any).currentAccessToken?.()
42
+ const token = user.currentAccessToken?.()
49
43
  if (token) await token.delete()
50
44
  }
51
45
 
@@ -58,6 +52,6 @@ export class ApiAuthController {
58
52
  const user = await manager.guard('api').user()
59
53
  if (!user) abort(401, 'Unauthenticated.')
60
54
 
61
- return json({ user: (user as any).toObject() })
55
+ return json({ user: user!.toObject() })
62
56
  }
63
57
  }
@@ -1,44 +1,37 @@
1
1
  import type { MantiqRequest } from '@mantiq/core'
2
- import { json, hash, hashCheck, abort } from '@mantiq/core'
2
+ import { json, hash, abort } from '@mantiq/core'
3
3
  import { auth } from '@mantiq/auth'
4
4
  import { User } from '../../Models/User.ts'
5
5
 
6
6
  export class AuthController {
7
- async register(request: MantiqRequest): Promise<Response> {
8
- const body = await request.input() as { name?: string; email?: string; password?: string }
9
- if (!body.name || !body.email || !body.password) abort(422, 'Name, email and password are required.')
10
- if (body.password!.length < 6) abort(422, 'Password must be at least 6 characters.')
11
-
12
- if (await User.where('email', body.email).first()) abort(422, 'A user with this email already exists.')
7
+ async register(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
8
+ if (await User.where('email', data.email).first()) abort(422, 'A user with this email already exists.')
13
9
 
14
10
  const user = await User.create({
15
- name: body.name,
16
- email: body.email,
17
- password: await hash(body.password!),
11
+ name: data.name,
12
+ email: data.email,
13
+ password: await hash(data.password),
18
14
  })
19
15
 
20
16
  const manager = auth()
21
17
  manager.setRequest(request)
22
- await manager.login(user as any)
18
+ await manager.login(user)
23
19
 
24
20
  return json({ message: 'Registered.', user: user.toObject() }, 201)
25
21
  }
26
22
 
27
- async login(request: MantiqRequest): Promise<Response> {
28
- const body = await request.input() as { email?: string; password?: string; remember?: boolean }
29
- if (!body.email || !body.password) abort(422, 'Email and password are required.')
30
-
23
+ async login(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
31
24
  const manager = auth()
32
25
  manager.setRequest(request)
33
26
 
34
27
  const success = await manager.attempt(
35
- { email: body.email!, password: body.password! },
36
- body.remember ?? false,
28
+ { email: data.email, password: data.password },
29
+ data.remember ?? false,
37
30
  )
38
31
  if (!success) abort(401, 'Invalid credentials.')
39
32
 
40
33
  const user = await manager.user()
41
- return json({ message: 'Logged in.', user })
34
+ return json({ message: 'Logged in.', user: user?.toObject() })
42
35
  }
43
36
 
44
37
  async logout(request: MantiqRequest): Promise<Response> {
@@ -10,9 +10,9 @@ async function getUser(request: MantiqRequest) {
10
10
  const user = await manager.user()
11
11
  if (!user) return null
12
12
  return {
13
- id: (user as any).getAttribute?.('id') ?? user.getAuthIdentifier(),
14
- name: (user as any).getAttribute?.('name') ?? '',
15
- email: (user as any).getAttribute?.('email') ?? '',
13
+ id: user.getAttribute('id') ?? user.getAuthIdentifier(),
14
+ name: user.getAttribute('name') ?? '',
15
+ email: user.getAttribute('email') ?? '',
16
16
  }
17
17
  }
18
18
 
@@ -11,47 +11,41 @@ export class UserController {
11
11
  const sortDir = request.query('dir') === 'asc' ? 'asc' : 'desc'
12
12
 
13
13
  const baseQuery = () => search
14
- ? User.query().where('name', 'LIKE', `%${search}%`).orWhere('email', 'LIKE', `%${search}%`)
14
+ ? User.where('name', 'LIKE', `%${search}%`).orWhere('email', 'LIKE', `%${search}%`)
15
15
  : User.query()
16
16
 
17
- const total = await (User.query() as any).count() as number
18
- const filteredTotal = await (baseQuery() as any).count() as number
19
- const users = await (baseQuery() as any)
17
+ const total = await User.count()
18
+ const filteredTotal = await baseQuery().count()
19
+ const users = await baseQuery()
20
20
  .orderBy(sortBy, sortDir)
21
21
  .limit(perPage)
22
22
  .offset((page - 1) * perPage)
23
- .get() as any[]
23
+ .get()
24
24
 
25
25
  return json({
26
- data: users.map((u: any) => u.toObject()),
26
+ data: users.map(u => u.toObject()),
27
27
  meta: { total, filtered_total: filteredTotal, page, per_page: perPage, last_page: Math.ceil(filteredTotal / perPage) },
28
28
  })
29
29
  }
30
30
 
31
- async store(request: MantiqRequest): Promise<Response> {
32
- const body = await request.input() as Record<string, any>
33
- if (!body?.name || !body?.email || !body?.password) abort(422, 'Name, email and password are required.')
34
-
35
- const existing = await User.where('email', body.email).first()
36
- if (existing) abort(422, 'Email already exists.')
31
+ async store(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
32
+ if (await User.where('email', data.email).first()) abort(422, 'Email already exists.')
37
33
 
38
34
  const user = await User.create({
39
- name: body.name,
40
- email: body.email,
41
- password: await hash(body.password),
35
+ name: data.name,
36
+ email: data.email,
37
+ password: await hash(data.password),
42
38
  })
43
39
 
44
40
  return json({ data: user.toObject() }, 201)
45
41
  }
46
42
 
47
- async update(request: MantiqRequest): Promise<Response> {
43
+ async update(request: MantiqRequest, data: Record<string, any>): Promise<Response> {
48
44
  const user = await User.find(Number(request.param('id')))
49
45
  if (!user) abort(404, 'User not found.')
50
-
51
- const body = await request.input() as Record<string, any>
52
- if (body.name) user.setAttribute('name', body.name)
53
- if (body.email) user.setAttribute('email', body.email)
54
- if (body.password) user.setAttribute('password', await hash(body.password))
46
+ if (data.name) user.setAttribute('name', data.name)
47
+ if (data.email) user.setAttribute('email', data.email)
48
+ if (data.password) user.setAttribute('password', await hash(data.password))
55
49
 
56
50
  await user.save()
57
51
  return json({ data: user.toObject() })
@@ -0,0 +1,10 @@
1
+ import { FormRequest } from '@mantiq/validation'
2
+
3
+ export class LoginRequest extends FormRequest {
4
+ rules() {
5
+ return {
6
+ email: 'required|email',
7
+ password: 'required|string',
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,11 @@
1
+ import { FormRequest } from '@mantiq/validation'
2
+
3
+ export class RegisterRequest extends FormRequest {
4
+ rules() {
5
+ return {
6
+ name: 'required|string|max:255',
7
+ email: 'required|email|max:255',
8
+ password: 'required|string|min:6',
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { FormRequest } from '@mantiq/validation'
2
+
3
+ export class StoreUserRequest extends FormRequest {
4
+ rules() {
5
+ return {
6
+ name: 'required|string|max:255',
7
+ email: 'required|email|max:255',
8
+ password: 'required|string|min:6',
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { FormRequest } from '@mantiq/validation'
2
+
3
+ export class UpdateUserRequest extends FormRequest {
4
+ rules() {
5
+ return {
6
+ name: 'string|max:255',
7
+ email: 'email|max:255',
8
+ password: 'string|min:6',
9
+ }
10
+ }
11
+ }
@@ -1,14 +1,16 @@
1
1
  import type { Router } from '@mantiq/core'
2
2
  import { json } from '@mantiq/core'
3
3
  import { UserController } from '../app/Http/Controllers/UserController.ts'
4
+ import { StoreUserRequest } from '../app/Http/Requests/StoreUserRequest.ts'
5
+ import { UpdateUserRequest } from '../app/Http/Requests/UpdateUserRequest.ts'
4
6
 
5
7
  export default function (router: Router) {
6
8
  // Public
7
9
  router.get('/ping', () => json({ status: 'ok', timestamp: new Date().toISOString() }))
8
10
 
9
- // Protected — session auth via cookies (stateful API for SPA)
11
+ // Protected — FormRequest auto-validates before controller runs
10
12
  router.get('/users', [UserController, 'index']).middleware('auth')
11
- router.post('/users', [UserController, 'store']).middleware('auth')
12
- router.put('/users/:id', [UserController, 'update']).middleware('auth')
13
+ router.post('/users', [UserController, 'store', StoreUserRequest]).middleware('auth')
14
+ router.put('/users/:id', [UserController, 'update', UpdateUserRequest]).middleware('auth')
13
15
  router.delete('/users/:id', [UserController, 'destroy']).middleware('auth')
14
16
  }
@@ -2,6 +2,8 @@ import type { Router } from '@mantiq/core'
2
2
  import { HomeController } from '../app/Http/Controllers/HomeController.ts'
3
3
  import { PageController } from '../app/Http/Controllers/PageController.ts'
4
4
  import { AuthController } from '../app/Http/Controllers/AuthController.ts'
5
+ import { RegisterRequest } from '../app/Http/Requests/RegisterRequest.ts'
6
+ import { LoginRequest } from '../app/Http/Requests/LoginRequest.ts'
5
7
 
6
8
  export default function (router: Router) {
7
9
  router.get('/', [HomeController, 'index'])
@@ -16,8 +18,8 @@ export default function (router: Router) {
16
18
  router.get('/account/security', [PageController, 'security']).middleware('auth')
17
19
  router.get('/account/preferences', [PageController, 'preferences']).middleware('auth')
18
20
 
19
- // Auth actions
20
- router.post('/login', [AuthController, 'login'])
21
- router.post('/register', [AuthController, 'register'])
21
+ // Auth actions — FormRequest auto-validates before controller runs
22
+ router.post('/login', [AuthController, 'login', LoginRequest])
23
+ router.post('/register', [AuthController, 'register', RegisterRequest])
22
24
  router.post('/logout', [AuthController, 'logout']).middleware('auth')
23
25
  }
@@ -9,7 +9,7 @@
9
9
  initialData?: Record<string, any>
10
10
  } = $props()
11
11
 
12
- const windowData = typeof window !== 'undefined' ? (window as any).__MANTIQ_DATA__ : {}
12
+ const windowData = typeof window !== 'undefined' ? (window as Record<string, any>).__MANTIQ_DATA__ ?? {} : {}
13
13
  const bootstrapData = (() => initialData ?? windowData)()
14
14
 
15
15
  let currentPage = $state<string>(bootstrapData._page ?? 'Login')
@@ -3,7 +3,9 @@ function getCookie(name: string): string | null {
3
3
  return match ? decodeURIComponent(match[1]!) : null
4
4
  }
5
5
 
6
- export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<{ ok: boolean; status: number; data: T }> {
6
+ type ApiResult<T> = { ok: true; status: number; data: T } | { ok: false; status: number; data: null }
7
+
8
+ export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<ApiResult<T>> {
7
9
  const headers: Record<string, string> = { Accept: 'application/json', ...(opts.headers as Record<string, string>) }
8
10
 
9
11
  // Attach XSRF token for CSRF protection on mutating requests
@@ -17,22 +19,23 @@ export async function api<T = any>(url: string, opts: RequestInit = {}): Promise
17
19
  // Session expired — redirect to login
18
20
  if (res.status === 401 || res.status === 419) {
19
21
  window.location.href = '/login'
20
- return { ok: false, status: res.status, data: null as any }
22
+ return { ok: false, status: res.status, data: null }
21
23
  }
22
24
 
23
25
  const ct = res.headers.get('content-type') ?? ''
24
26
  const data = ct.includes('json') ? await res.json() : null
25
- return { ok: res.ok, status: res.status, data }
27
+ if (!res.ok) return { ok: false, status: res.status, data: null }
28
+ return { ok: true, status: res.status, data }
26
29
  }
27
30
 
28
- export function post(url: string, body: object) {
29
- return api(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
31
+ export function post<T = any>(url: string, body: object) {
32
+ return api<T>(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
30
33
  }
31
34
 
32
- export function put(url: string, body: object) {
33
- return api(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
35
+ export function put<T = any>(url: string, body: object) {
36
+ return api<T>(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
34
37
  }
35
38
 
36
- export function del(url: string) {
37
- return api(url, { method: 'DELETE' })
39
+ export function del<T = any>(url: string) {
40
+ return api<T>(url, { method: 'DELETE' })
38
41
  }
@@ -1,10 +1,12 @@
1
+ declare global { interface Window { __MANTIQ_DATA__?: Record<string, any> } }
2
+
1
3
  import './style.css'
2
4
  import { mount } from 'svelte'
3
5
  import App from './App.svelte'
4
6
  import { pages } from './pages.ts'
5
7
 
6
8
  const target = document.getElementById('app')!
7
- const data = (window as any).__MANTIQ_DATA__ ?? {}
9
+ const data = window.__MANTIQ_DATA__ ?? {}
8
10
 
9
11
  // Clear SSR content and mount fresh — avoids hydration mismatches
10
12
  // with shadcn-svelte sidebar components
@@ -6,7 +6,8 @@ const props = defineProps<{
6
6
  initialData?: Record<string, any>
7
7
  }>()
8
8
 
9
- const windowData = typeof window !== 'undefined' ? (window as any).__MANTIQ_DATA__ : {}
9
+ declare global { interface Window { __MANTIQ_DATA__?: Record<string, any> } }
10
+ const windowData = typeof window !== 'undefined' ? window.__MANTIQ_DATA__ ?? {} : {}
10
11
  const initial = props.initialData ?? windowData
11
12
 
12
13
  const currentPage = ref<string>(initial._page ?? 'Login')
@@ -3,7 +3,9 @@ function getCookie(name: string): string | null {
3
3
  return match ? decodeURIComponent(match[1]!) : null
4
4
  }
5
5
 
6
- export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<{ ok: boolean; status: number; data: T }> {
6
+ type ApiResult<T> = { ok: true; status: number; data: T } | { ok: false; status: number; data: null }
7
+
8
+ export async function api<T = any>(url: string, opts: RequestInit = {}): Promise<ApiResult<T>> {
7
9
  const headers: Record<string, string> = { Accept: 'application/json', ...(opts.headers as Record<string, string>) }
8
10
 
9
11
  // Attach XSRF token for CSRF protection on mutating requests
@@ -17,22 +19,23 @@ export async function api<T = any>(url: string, opts: RequestInit = {}): Promise
17
19
  // Session expired — redirect to login
18
20
  if (res.status === 401 || res.status === 419) {
19
21
  window.location.href = '/login'
20
- return { ok: false, status: res.status, data: null as any }
22
+ return { ok: false, status: res.status, data: null }
21
23
  }
22
24
 
23
25
  const ct = res.headers.get('content-type') ?? ''
24
26
  const data = ct.includes('json') ? await res.json() : null
25
- return { ok: res.ok, status: res.status, data }
27
+ if (!res.ok) return { ok: false, status: res.status, data: null }
28
+ return { ok: true, status: res.status, data }
26
29
  }
27
30
 
28
- export function post(url: string, body: object) {
29
- return api(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
31
+ export function post<T = any>(url: string, body: object) {
32
+ return api<T>(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
30
33
  }
31
34
 
32
- export function put(url: string, body: object) {
33
- return api(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
35
+ export function put<T = any>(url: string, body: object) {
36
+ return api<T>(url, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) })
34
37
  }
35
38
 
36
- export function del(url: string) {
37
- return api(url, { method: 'DELETE' })
39
+ export function del<T = any>(url: string) {
40
+ return api<T>(url, { method: 'DELETE' })
38
41
  }
@@ -1,10 +1,12 @@
1
+ declare global { interface Window { __MANTIQ_DATA__?: Record<string, any> } }
2
+
1
3
  import './style.css'
2
4
  import { createApp, createSSRApp } from 'vue'
3
5
  import App from './App.vue'
4
6
  import { pages } from './pages.ts'
5
7
 
6
8
  const root = document.getElementById('app')!
7
- const data = (window as any).__MANTIQ_DATA__ ?? {}
9
+ const data = window.__MANTIQ_DATA__ ?? {}
8
10
 
9
11
  const app = root.innerHTML.trim()
10
12
  ? createSSRApp(App, { pages, initialData: data })