Skip to content

Commit c337845

Browse files
committed
app.js changes
1 parent 2f9743c commit c337845

File tree

1 file changed

+240
-0
lines changed

1 file changed

+240
-0
lines changed

stores/app.js

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
export const useAppStore = defineStore("app", () => {
2+
const stores = []
3+
4+
function registerStore(store) {
5+
const isAlreadyRegistered = stores.some(
6+
(registeredStore) => registeredStore.$id === store.$id,
7+
)
8+
if (isAlreadyRegistered) {
9+
console.log(
10+
`[AppStore] Store "${store.$id}" already registered, skipping`,
11+
)
12+
return
13+
}
14+
console.log("[AppStore] Registering store", store.$id)
15+
stores.push(store)
16+
}
17+
18+
function exportStores() {
19+
const snapshot = {}
20+
let exportCount = 0
21+
22+
for (const store of stores) {
23+
if (!store.exportStores) continue
24+
const storeId = store.$id
25+
try {
26+
snapshot[storeId] = store.exportStores()
27+
exportCount++
28+
} catch (error) {
29+
console.error(`[AppStore] Error exporting store "${storeId}":`, error)
30+
}
31+
}
32+
console.log(
33+
`[AppStore] Exported ${exportCount} stores; snapshot keys:`,
34+
Object.keys(snapshot),
35+
)
36+
return snapshot
37+
}
38+
39+
async function importStores(snapshot) {
40+
if (!snapshot) {
41+
console.warn("[AppStore] import called with invalid snapshot")
42+
return
43+
}
44+
console.log("[AppStore] Import snapshot keys:", Object.keys(snapshot || {}))
45+
46+
let importedCount = 0
47+
const notFoundStores = []
48+
for (const store of stores) {
49+
if (!store.importStores) continue
50+
const storeId = store.$id
51+
if (!snapshot[storeId]) {
52+
notFoundStores.push(storeId)
53+
continue
54+
}
55+
try {
56+
await store.importStores(snapshot[storeId])
57+
importedCount++
58+
} catch (error) {
59+
console.error(`[AppStore] Error importing store "${storeId}":`, error)
60+
}
61+
}
62+
if (notFoundStores.length > 0) {
63+
console.warn(
64+
`[AppStore] Stores not found in snapshot: ${notFoundStores.join(", ")}`,
65+
)
66+
}
67+
console.log(`[AppStore] Imported ${importedCount} stores`)
68+
}
69+
70+
// Extension loading system
71+
const loadedExtensions = ref(new Map())
72+
const extensionAPI = ref(null)
73+
74+
function setExtensionAPI(api) {
75+
extensionAPI.value = api
76+
}
77+
78+
async function loadExtension(path) {
79+
try {
80+
// Check if already loaded by path
81+
if (loadedExtensions.value.has(path)) {
82+
console.warn(`[AppStore] Extension already loaded from this path: ${path}`)
83+
throw new Error('This extension file is already loaded')
84+
}
85+
86+
if (!extensionAPI.value) {
87+
throw new Error("Extension API not initialized")
88+
}
89+
90+
let finalURL = path
91+
92+
// For blob URLs, we need to rewrite imports to use available modules
93+
if (path.startsWith('blob:')) {
94+
const response = await fetch(path)
95+
let code = await response.text()
96+
97+
// Replace Vue imports - handle all patterns including multiline
98+
code = code.replace(
99+
/import\s+(?:{([^}]+)}|\*\s+as\s+(\w+)|(\w+))\s+from\s+["']vue["'];?/gs,
100+
(match, namedImports, namespaceImport, defaultImport) => {
101+
if (namedImports) {
102+
// Named imports: import { ref, computed } from 'vue'
103+
// Convert 'as' to ':' for object destructuring
104+
const converted = namedImports.replace(/\s+as\s+/g, ': ')
105+
return `const {${converted}} = window.__VUE_RUNTIME__;`
106+
} else if (namespaceImport) {
107+
// Namespace import: import * as Vue from 'vue'
108+
return `const ${namespaceImport} = window.__VUE_RUNTIME__;`
109+
} else if (defaultImport) {
110+
// Default import: import Vue from 'vue'
111+
return `const ${defaultImport} = window.__VUE_RUNTIME__;`
112+
}
113+
return match
114+
}
115+
)
116+
117+
// Replace Pinia imports
118+
code = code.replace(
119+
/import\s+(?:{([^}]+)}|\*\s+as\s+(\w+)|(\w+))\s+from\s+["']pinia["'];?/gs,
120+
(match, namedImports, namespaceImport, defaultImport) => {
121+
if (namedImports) {
122+
const converted = namedImports.replace(/\s+as\s+/g, ': ')
123+
return `const {${converted}} = window.__PINIA__;`
124+
} else if (namespaceImport) {
125+
return `const ${namespaceImport} = window.__PINIA__;`
126+
} else if (defaultImport) {
127+
return `const ${defaultImport} = window.__PINIA__;`
128+
}
129+
return match
130+
}
131+
)
132+
133+
// Replace @geode/* imports - just comment them out for now
134+
code = code.replace(
135+
/import\s+[^;]+from\s+["']@geode\/[^"']+["'];?/gs,
136+
(match) => `/* ${match} */ // External dependency - resolved at runtime\n`
137+
)
138+
139+
// Replace @ogw_* imports
140+
code = code.replace(
141+
/import\s+[^;]+from\s+["']@ogw_[^"']+["'];?/gs,
142+
(match) => `/* ${match} */ // External dependency - resolved at runtime\n`
143+
)
144+
145+
console.log('[AppStore] Rewritten extension code preview:', code.substring(0, 800))
146+
147+
// Create new blob with rewritten code
148+
const newBlob = new Blob([code], { type: 'application/javascript' })
149+
finalURL = URL.createObjectURL(newBlob)
150+
}
151+
152+
// Dynamic import of the extension module
153+
const extensionModule = await import(/* @vite-ignore */ finalURL)
154+
155+
// Clean up temporary blob URL
156+
if (finalURL !== path && finalURL.startsWith('blob:')) {
157+
URL.revokeObjectURL(finalURL)
158+
}
159+
160+
// Check for duplicate extension by name
161+
const extensionName = extensionModule.metadata?.name
162+
if (extensionName) {
163+
const alreadyLoaded = Array.from(loadedExtensions.value.values()).find(
164+
ext => ext.metadata?.name === extensionName
165+
)
166+
if (alreadyLoaded) {
167+
console.warn(`[AppStore] Extension "${extensionName}" is already loaded`)
168+
throw new Error(`Extension "${extensionName}" is already loaded. Please unload it first.`)
169+
}
170+
}
171+
172+
// Check if extension has an install function
173+
if (typeof extensionModule.install === 'function') {
174+
// Call install with the Extension API and the extension path
175+
await extensionModule.install(extensionAPI.value, path)
176+
177+
// Store the loaded extension
178+
const extensionData = {
179+
module: extensionModule,
180+
path,
181+
loadedAt: new Date().toISOString(),
182+
metadata: extensionModule.metadata || {},
183+
}
184+
loadedExtensions.value.set(path, extensionData)
185+
186+
console.log(`[AppStore] Extension loaded successfully: ${path}`)
187+
188+
return extensionModule
189+
} else {
190+
throw new Error('Extension must export an install function')
191+
}
192+
} catch (error) {
193+
console.error(`[AppStore] Failed to load extension from ${path}:`, error)
194+
throw error
195+
}
196+
}
197+
198+
function getLoadedExtensions() {
199+
return Array.from(loadedExtensions.value.values())
200+
}
201+
202+
function unloadExtension(path) {
203+
if (loadedExtensions.value.has(path)) {
204+
const extensionData = loadedExtensions.value.get(path)
205+
206+
// Call uninstall function if it exists
207+
if (extensionData.module && typeof extensionData.module.uninstall === 'function') {
208+
try {
209+
extensionData.module.uninstall(extensionAPI.value, path)
210+
console.log(`[AppStore] Extension uninstall called: ${path}`)
211+
} catch (error) {
212+
console.error(`[AppStore] Error calling uninstall for ${path}:`, error)
213+
}
214+
}
215+
216+
// Clean up all tools registered by this extension
217+
if (extensionAPI.value) {
218+
extensionAPI.value.unregisterToolsByExtension(path)
219+
}
220+
221+
// Remove from loaded extensions
222+
loadedExtensions.value.delete(path)
223+
console.log(`[AppStore] Extension unloaded: ${path}`)
224+
return true
225+
}
226+
return false
227+
}
228+
229+
return {
230+
stores,
231+
registerStore,
232+
exportStores,
233+
importStores,
234+
loadedExtensions,
235+
setExtensionAPI,
236+
loadExtension,
237+
getLoadedExtensions,
238+
unloadExtension,
239+
}
240+
})

0 commit comments

Comments
 (0)