spine-framework 0.3.121 → 0.3.122
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.
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module cil-state
|
|
3
|
+
* @audience both
|
|
4
|
+
* @layer shared-core
|
|
5
|
+
* @stability experimental
|
|
6
|
+
*
|
|
7
|
+
* Shared state-machine helpers for the CIL ingestion pipeline.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { adminDb } from './db'
|
|
11
|
+
|
|
12
|
+
export type FileStatus = 'discovered' | 'pass2_complete' | 'pass3_complete' | 'pass4_complete' | 'pass4b_complete' | 'pass5_complete'
|
|
13
|
+
|
|
14
|
+
export interface FileTreeNode {
|
|
15
|
+
id?: string | null
|
|
16
|
+
name: string
|
|
17
|
+
path: string
|
|
18
|
+
type: 'file' | 'directory'
|
|
19
|
+
extension?: string
|
|
20
|
+
language?: string
|
|
21
|
+
status?: FileStatus
|
|
22
|
+
children?: FileTreeNode[]
|
|
23
|
+
selected?: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check whether a file path is inside any of the selected paths.
|
|
28
|
+
* A file is selected if its path equals a selected path, or starts with a selected directory path + '/'.
|
|
29
|
+
*/
|
|
30
|
+
export function isSelectedPath(filePath: string, selectedPaths: string[]): boolean {
|
|
31
|
+
if (selectedPaths.length === 0) return true
|
|
32
|
+
return selectedPaths.some(sp => {
|
|
33
|
+
if (filePath === sp) return true
|
|
34
|
+
if (sp === '') return true
|
|
35
|
+
return filePath.startsWith(sp + '/')
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Extract the file status from a cil_file item.
|
|
41
|
+
*/
|
|
42
|
+
export function getFileStatus(fileItem: any): FileStatus {
|
|
43
|
+
return fileItem.data?.status || 'discovered'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Update the status of a cil_file item.
|
|
48
|
+
*/
|
|
49
|
+
export async function updateFileStatus(ctx: any, fileItem: any, status: FileStatus, extraData: Record<string, any> = {}): Promise<void> {
|
|
50
|
+
const { create, update } = await import('../admin-data')
|
|
51
|
+
const updateCtx = { ...ctx, query: { ...(ctx.query || {}), entity: 'items', id: fileItem.id } }
|
|
52
|
+
await update(updateCtx, {
|
|
53
|
+
data: {
|
|
54
|
+
...fileItem.data,
|
|
55
|
+
...extraData,
|
|
56
|
+
status,
|
|
57
|
+
last_status_at: new Date().toISOString(),
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Build a nested file tree from a flat list of discovered files.
|
|
64
|
+
*/
|
|
65
|
+
export function buildFileTree(fileRecords: { file_path: string; id: string; language: string; status: FileStatus; extension: string }[]): FileTreeNode {
|
|
66
|
+
const root: FileTreeNode = { name: 'root', path: '', type: 'directory', children: [] }
|
|
67
|
+
|
|
68
|
+
for (const record of fileRecords) {
|
|
69
|
+
const parts = record.file_path.split('/')
|
|
70
|
+
let current = root
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < parts.length; i++) {
|
|
73
|
+
const part = parts[i]
|
|
74
|
+
const isFile = i === parts.length - 1
|
|
75
|
+
const currentPath = parts.slice(0, i + 1).join('/')
|
|
76
|
+
|
|
77
|
+
let child = current.children?.find(c => c.name === part)
|
|
78
|
+
if (!child) {
|
|
79
|
+
child = {
|
|
80
|
+
name: part,
|
|
81
|
+
path: currentPath,
|
|
82
|
+
type: isFile ? 'file' : 'directory',
|
|
83
|
+
children: isFile ? undefined : []
|
|
84
|
+
}
|
|
85
|
+
if (isFile) {
|
|
86
|
+
child.id = record.id
|
|
87
|
+
child.language = record.language
|
|
88
|
+
child.status = record.status
|
|
89
|
+
child.extension = record.extension
|
|
90
|
+
}
|
|
91
|
+
current.children!.push(child)
|
|
92
|
+
}
|
|
93
|
+
current = child
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return root
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Update the status of a file node in the persisted tree by its file item ID.
|
|
102
|
+
*/
|
|
103
|
+
export function updateTreeNodeStatus(tree: FileTreeNode, fileId: string, status: FileStatus): void {
|
|
104
|
+
if (tree.id === fileId) {
|
|
105
|
+
tree.status = status
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
if (tree.children) {
|
|
109
|
+
for (const child of tree.children) {
|
|
110
|
+
updateTreeNodeStatus(child, fileId, status)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Persist the updated tree back to the project item.
|
|
117
|
+
*/
|
|
118
|
+
export async function saveProjectTree(ctx: any, projectId: string, tree: FileTreeNode, extraData: Record<string, any> = {}): Promise<void> {
|
|
119
|
+
const { update } = await import('../admin-data')
|
|
120
|
+
const { data: projItem } = await adminDb.from('items').select('id, data').eq('id', projectId).maybeSingle()
|
|
121
|
+
if (!projItem) return
|
|
122
|
+
const updateCtx = { ...ctx, query: { ...(ctx.query || {}), entity: 'items', id: projItem.id } }
|
|
123
|
+
await update(updateCtx, {
|
|
124
|
+
data: {
|
|
125
|
+
...projItem.data,
|
|
126
|
+
file_tree: tree,
|
|
127
|
+
...extraData,
|
|
128
|
+
last_tree_update: new Date().toISOString(),
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
}
|