forked from lightningpixel/modly
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathassetLibraryService.ts
More file actions
68 lines (60 loc) · 3.13 KB
/
Copy pathassetLibraryService.ts
File metadata and controls
68 lines (60 loc) · 3.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import type {
AssetLibraryError,
AssetLibraryListResult,
AssetLibraryOpenRequest,
AssetLibraryOpenResult,
AssetLibraryReadRequest,
AssetLibraryReadResult,
} from '../../shared/types/assetLibrary'
import { projectAssetLibraryEntry, type ProjectedAssetLibraryEntry } from './assetLibraryProjection'
export interface AssetLibraryPreloadApi {
list: () => Promise<AssetLibraryListResult>
read: (request: AssetLibraryReadRequest) => Promise<AssetLibraryReadResult>
open: (request: AssetLibraryOpenRequest) => Promise<AssetLibraryOpenResult>
}
export type ProjectedAssetLibraryListResult =
| { success: true, entries: ProjectedAssetLibraryEntry[] }
| { success: false, error: AssetLibraryError }
export type ProjectedAssetLibraryReadResult =
| { success: true, entry: ProjectedAssetLibraryEntry, preview: Extract<AssetLibraryReadResult, { success: true }>['preview'] }
| { success: false, error: AssetLibraryError }
export type ProjectedAssetLibraryOpenResult =
| { success: true, entry: ProjectedAssetLibraryEntry }
| { success: false, error: AssetLibraryError }
function unsafeError(message = 'Workspace path must stay under Workflows/ or Exports/.'): AssetLibraryError {
return { code: 'unsafe-path', message }
}
function validateWorkspacePath(workspacePath: string): AssetLibraryError | null {
const trimmed = workspacePath.trim()
if (!trimmed || trimmed.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(trimmed) || trimmed.startsWith('\\\\')) return unsafeError()
if (/%2e|%2f|%5c/i.test(trimmed) || trimmed.split(/[\\/]+/).includes('..')) return unsafeError()
const normalized = trimmed.replace(/\\/g, '/')
if (!normalized.startsWith('Workflows/') && !normalized.startsWith('Exports/')) return unsafeError()
return null
}
export function createAssetLibraryService(api: AssetLibraryPreloadApi) {
return {
async list(): Promise<ProjectedAssetLibraryListResult> {
const result = await api.list()
if (!result.success) return result
return { success: true, entries: result.entries.map(projectAssetLibraryEntry) }
},
async read(request: AssetLibraryReadRequest): Promise<ProjectedAssetLibraryReadResult> {
const invalid = validateWorkspacePath(request.workspacePath) ?? (request.sourceWorkspacePath ? validateWorkspacePath(request.sourceWorkspacePath) : null)
if (invalid) return { success: false, error: invalid }
const result = await api.read(request)
if (!result.success) return result
return { success: true, entry: projectAssetLibraryEntry(result.entry), preview: result.preview }
},
async open(request: AssetLibraryOpenRequest): Promise<ProjectedAssetLibraryOpenResult> {
const invalid = validateWorkspacePath(request.workspacePath) ?? (request.sourceWorkspacePath ? validateWorkspacePath(request.sourceWorkspacePath) : null)
if (invalid) return { success: false, error: invalid }
const result = await api.open(request)
if (!result.success) return result
return { success: true, entry: projectAssetLibraryEntry(result.entry) }
},
}
}
export function getDefaultAssetLibraryService() {
return createAssetLibraryService(window.electron.workspace.library)
}