spine-framework 0.3.38 → 0.3.39
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/config/components.json +25 -0
- package/config/deno.lock +108 -0
- package/config/package-lock.json +17183 -0
- package/config/postcss.config.cjs +10 -0
- package/config/tailwind.config.cjs +78 -0
- package/config/tsconfig.cli.json +18 -0
- package/config/tsconfig.json +41 -0
- package/config/tsconfig.node.json +17 -0
- package/config/tsconfig.node.tsbuildinfo +1 -0
- package/config/tsconfig.tsbuildinfo +1 -0
- package/config/vite.config.d.ts +2 -0
- package/config/vite.config.ts +72 -0
- package/netlify.toml +41 -0
- package/package.json +4 -1
- package/scripts/app-install-cli.ts +286 -0
- package/scripts/assemble-frontend.sh +79 -0
- package/scripts/assemble-functions.sh +62 -0
- package/scripts/assemble.sh +35 -0
- package/scripts/boundary-check.sh +106 -0
- package/scripts/build-manifest.sh +80 -0
- package/scripts/check-core-integrity.sh +82 -0
- package/scripts/ingest-chunks.cjs +202 -0
- package/scripts/kb-chunk-parser.cjs +312 -0
- package/scripts/kb-chunk-parser.ts +330 -0
- package/scripts/load-test-app-install.ts +484 -0
- package/scripts/netlify-dev-wrapper.sh +22 -0
- package/scripts/test-login.js +33 -0
- package/scripts/verify-integrity.sh +69 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
#!/usr/bin/env deno run --allow-read --allow-write
|
|
2
|
+
|
|
3
|
+
import { walk } from 'https://deno.land/std@0.168.0/fs/mod.ts'
|
|
4
|
+
import { readAll } from 'https://deno.land/std@0.168.0/streams/mod.ts'
|
|
5
|
+
|
|
6
|
+
interface ChunkMetadata {
|
|
7
|
+
chunk_id: string
|
|
8
|
+
file_path: string
|
|
9
|
+
line_start: number
|
|
10
|
+
line_end: number
|
|
11
|
+
chunk_type: 'function' | 'class' | 'interface' | 'config' | 'object'
|
|
12
|
+
purpose: string
|
|
13
|
+
hash: string
|
|
14
|
+
dependencies: string[]
|
|
15
|
+
dependents: string[]
|
|
16
|
+
source: {
|
|
17
|
+
source_type: 'core'
|
|
18
|
+
ref: string
|
|
19
|
+
line_start: number
|
|
20
|
+
line_end: number
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface ParsedChunk {
|
|
25
|
+
identifier: string
|
|
26
|
+
chunk_id: string
|
|
27
|
+
version: string
|
|
28
|
+
hash: string
|
|
29
|
+
macro: string
|
|
30
|
+
micro: string
|
|
31
|
+
inputs: Record<string, string>
|
|
32
|
+
outputs: string
|
|
33
|
+
depends_on: string[]
|
|
34
|
+
depended_by: string[]
|
|
35
|
+
side_effects: string[]
|
|
36
|
+
tags: string[]
|
|
37
|
+
code: string
|
|
38
|
+
metadata: ChunkMetadata
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class KBChunkParser {
|
|
42
|
+
private chunks: ParsedChunk[] = []
|
|
43
|
+
private currentChunk: Partial<ParsedChunk> = {}
|
|
44
|
+
private inChunk = false
|
|
45
|
+
private currentFile = ''
|
|
46
|
+
private currentLine = 0
|
|
47
|
+
|
|
48
|
+
async parseDirectory(dirPath: string): Promise<ParsedChunk[]> {
|
|
49
|
+
this.chunks = []
|
|
50
|
+
|
|
51
|
+
for await (const entry of walk(dirPath, {
|
|
52
|
+
includeDirs: false,
|
|
53
|
+
exts: ['.ts', '.tsx', '.js', '.jsx'],
|
|
54
|
+
skip: [/node_modules/, /\.git/, /dist/, /src\/v2-assembled/]
|
|
55
|
+
})) {
|
|
56
|
+
if (entry.isFile) {
|
|
57
|
+
await this.parseFile(entry.path)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return this.chunks
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async parseFile(filePath: string): Promise<void> {
|
|
65
|
+
this.currentFile = filePath
|
|
66
|
+
this.currentLine = 0
|
|
67
|
+
this.inChunk = false
|
|
68
|
+
this.currentChunk = {}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const content = await Deno.readTextFile(filePath)
|
|
72
|
+
const lines = content.split('\n')
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < lines.length; i++) {
|
|
75
|
+
this.currentLine = i + 1
|
|
76
|
+
const line = lines[i]
|
|
77
|
+
|
|
78
|
+
if (line.includes('CHUNK_START:')) {
|
|
79
|
+
this.handleChunkStart(line)
|
|
80
|
+
} else if (line.includes('CHUNK_END:')) {
|
|
81
|
+
this.handleChunkEnd()
|
|
82
|
+
} else if (this.inChunk && this.currentChunk.code !== undefined) {
|
|
83
|
+
this.currentChunk.code += line + '\n'
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(`Error parsing file ${filePath}:`, error.message)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private handleChunkStart(line: string): void {
|
|
92
|
+
const match = line.match(/CHUNK_START:\s*(.+?)\s*─/)
|
|
93
|
+
if (!match) {
|
|
94
|
+
console.warn(`Invalid CHUNK_START format at line ${this.currentLine}: ${line}`)
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const identifier = match[1].trim()
|
|
99
|
+
this.inChunk = true
|
|
100
|
+
this.currentChunk = {
|
|
101
|
+
identifier,
|
|
102
|
+
code: '',
|
|
103
|
+
metadata: {
|
|
104
|
+
chunk_id: '',
|
|
105
|
+
file_path: this.currentFile,
|
|
106
|
+
line_start: this.currentLine,
|
|
107
|
+
line_end: 0,
|
|
108
|
+
chunk_type: 'function',
|
|
109
|
+
purpose: '',
|
|
110
|
+
hash: '',
|
|
111
|
+
dependencies: [],
|
|
112
|
+
dependents: [],
|
|
113
|
+
source: {
|
|
114
|
+
source_type: 'core',
|
|
115
|
+
ref: this.currentFile,
|
|
116
|
+
line_start: this.currentLine,
|
|
117
|
+
line_end: 0
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private handleChunkEnd(): void {
|
|
124
|
+
if (!this.inChunk) {
|
|
125
|
+
console.warn(`CHUNK_END without CHUNK_START at line ${this.currentLine}`)
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const match = this.currentChunk.code?.match(/@chunk-id\s+(.+)/)
|
|
130
|
+
if (!match) {
|
|
131
|
+
console.warn(`Missing @chunk-id in chunk ${this.currentChunk.identifier}`)
|
|
132
|
+
this.inChunk = false
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const chunkId = match[1].trim()
|
|
137
|
+
const hash = this.generateHash(this.currentChunk.code || '')
|
|
138
|
+
|
|
139
|
+
// Parse JSDoc-style comments
|
|
140
|
+
const parsed = this.parseChunkDoc(this.currentChunk.code || '')
|
|
141
|
+
|
|
142
|
+
const chunk: ParsedChunk = {
|
|
143
|
+
identifier: this.currentChunk.identifier!,
|
|
144
|
+
chunk_id: chunkId,
|
|
145
|
+
version: '1.0.0',
|
|
146
|
+
hash,
|
|
147
|
+
macro: parsed.macro || '',
|
|
148
|
+
micro: parsed.micro || '',
|
|
149
|
+
inputs: parsed.inputs || {},
|
|
150
|
+
outputs: parsed.outputs || '',
|
|
151
|
+
depends_on: parsed.depends_on || [],
|
|
152
|
+
depended_by: parsed.depended_by || [],
|
|
153
|
+
side_effects: parsed.side_effects || [],
|
|
154
|
+
tags: parsed.tags || [],
|
|
155
|
+
code: (this.currentChunk.code || '').trim(),
|
|
156
|
+
metadata: {
|
|
157
|
+
...this.currentChunk.metadata!,
|
|
158
|
+
chunk_id: chunkId,
|
|
159
|
+
hash,
|
|
160
|
+
line_end: this.currentLine,
|
|
161
|
+
source: {
|
|
162
|
+
...this.currentChunk.metadata!.source,
|
|
163
|
+
line_end: this.currentLine
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this.chunks.push(chunk)
|
|
169
|
+
this.inChunk = false
|
|
170
|
+
this.currentChunk = {}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private parseChunkDoc(code: string): any {
|
|
174
|
+
const doc: any = {}
|
|
175
|
+
|
|
176
|
+
// Extract @chunk-id
|
|
177
|
+
const chunkIdMatch = code.match(/@chunk-id\s+(.+)/)
|
|
178
|
+
if (chunkIdMatch) {
|
|
179
|
+
doc.chunk_id = chunkIdMatch[1].trim()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Extract @version
|
|
183
|
+
const versionMatch = code.match(/@version\s+(.+)/)
|
|
184
|
+
if (versionMatch) {
|
|
185
|
+
doc.version = versionMatch[1].trim()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Extract @hash
|
|
189
|
+
const hashMatch = code.match(/@hash\s+(.+)/)
|
|
190
|
+
if (hashMatch) {
|
|
191
|
+
doc.hash = hashMatch[1].trim()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Extract @macro
|
|
195
|
+
const macroMatch = code.match(/@macro\s+(.+)/)
|
|
196
|
+
if (macroMatch) {
|
|
197
|
+
doc.macro = macroMatch[1].trim()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Extract @micro
|
|
201
|
+
const microMatch = code.match(/@micro\s+(.+)/)
|
|
202
|
+
if (microMatch) {
|
|
203
|
+
doc.micro = microMatch[1].trim()
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Extract @inputs
|
|
207
|
+
const inputsMatch = code.match(/@inputs\s+(.+)/)
|
|
208
|
+
if (inputsMatch) {
|
|
209
|
+
doc.inputs = this.parseKeyValuePairs(inputsMatch[1].trim())
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Extract @outputs
|
|
213
|
+
const outputsMatch = code.match(/@outputs\s+(.+)/)
|
|
214
|
+
if (outputsMatch) {
|
|
215
|
+
doc.outputs = outputsMatch[1].trim()
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Extract @depends-on
|
|
219
|
+
const dependsOnMatch = code.match(/@depends-on\s+(.+)/)
|
|
220
|
+
if (dependsOnMatch) {
|
|
221
|
+
doc.depends_on = this.parseList(dependsOnMatch[1].trim())
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Extract @depended-by
|
|
225
|
+
const dependedByMatch = code.match(/@depended-by\s+(.+)/)
|
|
226
|
+
if (dependedByMatch) {
|
|
227
|
+
doc.depended_by = this.parseList(dependedByMatch[1].trim())
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Extract @side-effects
|
|
231
|
+
const sideEffectsMatch = code.match(/@side-effects\s+(.+)/)
|
|
232
|
+
if (sideEffectsMatch) {
|
|
233
|
+
doc.side_effects = this.parseList(sideEffectsMatch[1].trim())
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Extract @tags
|
|
237
|
+
const tagsMatch = code.match(/@tags\s+(.+)/)
|
|
238
|
+
if (tagsMatch) {
|
|
239
|
+
doc.tags = this.parseList(tagsMatch[1].trim())
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return doc
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private parseKeyValuePairs(str: string): Record<string, string> {
|
|
246
|
+
const pairs: Record<string, string> = {}
|
|
247
|
+
const regex = /(\w+):\s*([^,]+)(?:,\s*|$)/g
|
|
248
|
+
let match
|
|
249
|
+
|
|
250
|
+
while ((match = regex.exec(str)) !== null) {
|
|
251
|
+
pairs[match[1]] = match[2].trim()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return pairs
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private parseList(str: string): string[] {
|
|
258
|
+
return str.split(',').map(s => s.trim()).filter(Boolean)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private generateHash(content: string): string {
|
|
262
|
+
// Simple hash function for demonstration
|
|
263
|
+
// In production, use crypto.subtle.digest
|
|
264
|
+
let hash = 0
|
|
265
|
+
for (let i = 0; i < content.length; i++) {
|
|
266
|
+
const char = content.charCodeAt(i)
|
|
267
|
+
hash = ((hash << 5) - hash) + char
|
|
268
|
+
hash = hash & hash // Convert to 32-bit integer
|
|
269
|
+
}
|
|
270
|
+
return Math.abs(hash).toString(16)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async saveChunks(outputFile: string): Promise<void> {
|
|
274
|
+
const output = {
|
|
275
|
+
generated_at: new Date().toISOString(),
|
|
276
|
+
total_chunks: this.chunks.length,
|
|
277
|
+
chunks: this.chunks
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
await Deno.writeTextFile(outputFile, JSON.stringify(output, null, 2))
|
|
281
|
+
console.log(`Saved ${this.chunks.length} chunks to ${outputFile}`)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// CLI usage
|
|
286
|
+
async function main() {
|
|
287
|
+
const args = Deno.args
|
|
288
|
+
|
|
289
|
+
if (args.length < 2) {
|
|
290
|
+
console.log('Usage: deno run --allow-read --allow-write kb-chunk-parser.ts <source-dir> <output-file>')
|
|
291
|
+
console.log('Example: deno run --allow-read --allow-write kb-chunk-parser.ts v2-core/src chunks.json')
|
|
292
|
+
Deno.exit(1)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const [sourceDir, outputFile] = args
|
|
296
|
+
|
|
297
|
+
if (!await Deno.stat(sourceDir).catch(() => false)) {
|
|
298
|
+
console.error(`Source directory not found: ${sourceDir}`)
|
|
299
|
+
Deno.exit(1)
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const parser = new KBChunkParser()
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
console.log(`Parsing chunks from ${sourceDir}...`)
|
|
306
|
+
const chunks = await parser.parseDirectory(sourceDir)
|
|
307
|
+
|
|
308
|
+
console.log(`Found ${chunks.length} chunks`)
|
|
309
|
+
|
|
310
|
+
await parser.saveChunks(outputFile)
|
|
311
|
+
|
|
312
|
+
// Display summary
|
|
313
|
+
console.log('\nSummary:')
|
|
314
|
+
console.log(`- Functions: ${chunks.filter(c => c.metadata.chunk_type === 'function').length}`)
|
|
315
|
+
console.log(`- Classes: ${chunks.filter(c => c.metadata.chunk_type === 'class').length}`)
|
|
316
|
+
console.log(`- Interfaces: ${chunks.filter(c => c.metadata.chunk_type === 'interface').length}`)
|
|
317
|
+
console.log(`- Configs: ${chunks.filter(c => c.metadata.chunk_type === 'config').length}`)
|
|
318
|
+
console.log(`- Objects: ${chunks.filter(c => c.metadata.chunk_type === 'object').length}`)
|
|
319
|
+
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.error('Error:', error.message)
|
|
322
|
+
Deno.exit(1)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (import.meta.main) {
|
|
327
|
+
await main()
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export { KBChunkParser, ParsedChunk }
|