spine-framework 0.3.121 → 0.3.123

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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-framework",
3
- "version": "0.3.121",
3
+ "version": "0.3.123",
4
4
  "description": "Multi-tenant, modular application platform for modern SaaS systems",
5
5
  "type": "module",
6
6
  "bin": {