gowm 1.0.0 → 1.0.2
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/CHANGELOG.md +25 -0
- package/package.json +14 -2
- package/src/bridge-browser.js +157 -0
- package/src/browser.js +77 -0
- package/src/loader-browser.js +113 -0
- package/src/loader-safe.js +184 -0
- package/src/loader.js +24 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
Toutes les modifications notables de ce projet seront documentées dans ce fichier.
|
|
4
4
|
|
|
5
|
+
## [1.0.2] - 2025-05-29
|
|
6
|
+
|
|
7
|
+
### Corrigé
|
|
8
|
+
- **Problème critique de bundling** : Correction de l'importation des modules Node.js `fs` et `path` qui causaient des erreurs avec Webpack 5 et autres bundlers modernes
|
|
9
|
+
- Importation dynamique sécurisée des modules Node.js pour éviter les erreurs de build
|
|
10
|
+
- Amélioration de la détection d'environnement (Node.js vs navigateur)
|
|
11
|
+
- Ajout de méthode `ensureNodeModules()` pour un chargement sûr des dépendances Node.js
|
|
12
|
+
|
|
13
|
+
### Ajouté
|
|
14
|
+
- Version sécurisée du loader (`loader-safe.js`) avec gestion d'erreurs améliorée
|
|
15
|
+
- Meilleure compatibilité avec les bundlers modernes (Webpack 5, Vite, etc.)
|
|
16
|
+
|
|
17
|
+
## [1.0.1] - 2025-05-29
|
|
18
|
+
|
|
19
|
+
### Corrigé
|
|
20
|
+
- **Compatibilité navigateur** : Résolution du problème avec les modules Node.js `fs` et `path` qui empêchaient l'utilisation dans React/Vue/navigateur
|
|
21
|
+
- Importation conditionnelle des modules Node.js uniquement en environnement serveur
|
|
22
|
+
- Ajout de points d'entrée séparés pour Node.js et navigateur dans `package.json`
|
|
23
|
+
- Création de versions navigateur dédiées (`loader-browser.js`, `bridge-browser.js`, `browser.js`)
|
|
24
|
+
|
|
25
|
+
### Ajouté
|
|
26
|
+
- Support ES modules pour les environnements navigateur
|
|
27
|
+
- Configuration `package.json` avec champs `browser`, `module`, et `exports`
|
|
28
|
+
- Documentation sur la résolution du problème de compatibilité (`BROWSER_SUPPORT.md`)
|
|
29
|
+
|
|
5
30
|
## [1.0.0] - 2025-05-28
|
|
6
31
|
|
|
7
32
|
### Ajouté
|
package/package.json
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gowm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Go WebAssembly Manager - Simplifiez l'intégration de modules WebAssembly Go dans vos projets JavaScript",
|
|
5
5
|
"main": "src/index.js",
|
|
6
|
+
"browser": "src/browser.js",
|
|
7
|
+
"module": "src/browser.js",
|
|
6
8
|
"types": "types/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"node": "./src/index.js",
|
|
12
|
+
"browser": "./src/browser.js",
|
|
13
|
+
"import": "./src/browser.js",
|
|
14
|
+
"require": "./src/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./hooks": "./hooks/useWasm.js",
|
|
17
|
+
"./composables": "./composables/useWasm.js"
|
|
18
|
+
},
|
|
7
19
|
"files": [
|
|
8
20
|
"src/",
|
|
9
21
|
"types/",
|
|
@@ -49,4 +61,4 @@
|
|
|
49
61
|
"devDependencies": {
|
|
50
62
|
"@types/node": "^20.0.0"
|
|
51
63
|
}
|
|
52
|
-
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
export default class WasmBridge {
|
|
2
|
+
constructor(wasmModule, options = {}) {
|
|
3
|
+
this.module = wasmModule;
|
|
4
|
+
this.callbacks = new Map();
|
|
5
|
+
this.name = options.name || 'unnamed';
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Appel de fonction avec gestion d'erreurs
|
|
9
|
+
call(funcName, ...args) {
|
|
10
|
+
try {
|
|
11
|
+
// Vérifier d'abord dans les exports WASM
|
|
12
|
+
if (this.module.exports && this.module.exports[funcName]) {
|
|
13
|
+
return this.module.exports[funcName](...args);
|
|
14
|
+
}
|
|
15
|
+
// Puis dans les fonctions globales JavaScript (pour Go WASM)
|
|
16
|
+
else if (globalThis[funcName] && typeof globalThis[funcName] === 'function') {
|
|
17
|
+
return globalThis[funcName](...args);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
throw new Error(`Function ${funcName} not found`);
|
|
21
|
+
}
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.error(`Error calling ${funcName}:`, error);
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Appel asynchrone de fonction
|
|
29
|
+
async callAsync(funcName, ...args) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
try {
|
|
32
|
+
const result = this.call(funcName, ...args);
|
|
33
|
+
if (result instanceof Promise) {
|
|
34
|
+
result.then(resolve).catch(reject);
|
|
35
|
+
} else {
|
|
36
|
+
resolve(result);
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
reject(error);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Créer un buffer en mémoire WASM
|
|
45
|
+
createBuffer(data) {
|
|
46
|
+
let buffer;
|
|
47
|
+
let size;
|
|
48
|
+
|
|
49
|
+
if (data instanceof Float64Array) {
|
|
50
|
+
buffer = data;
|
|
51
|
+
size = data.length * 8; // 8 bytes par float64
|
|
52
|
+
} else if (data instanceof Uint8Array) {
|
|
53
|
+
buffer = data;
|
|
54
|
+
size = data.length;
|
|
55
|
+
} else if (typeof data === 'string') {
|
|
56
|
+
const encoder = new TextEncoder();
|
|
57
|
+
buffer = encoder.encode(data);
|
|
58
|
+
size = buffer.length;
|
|
59
|
+
} else {
|
|
60
|
+
throw new Error('Unsupported data type for buffer');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Allouer de la mémoire dans WASM
|
|
64
|
+
const ptr = this.call('malloc', size);
|
|
65
|
+
if (!ptr) {
|
|
66
|
+
throw new Error('Failed to allocate memory in WASM');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Copier les données dans la mémoire WASM
|
|
70
|
+
const wasmMemory = new Uint8Array(this.module.exports.memory.buffer, ptr, size);
|
|
71
|
+
wasmMemory.set(buffer);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
ptr,
|
|
75
|
+
buffer: wasmMemory,
|
|
76
|
+
size,
|
|
77
|
+
free: () => {
|
|
78
|
+
try {
|
|
79
|
+
this.call('free', ptr);
|
|
80
|
+
} catch (e) {
|
|
81
|
+
// Ignorer les erreurs de libération
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Enregistrer un callback JavaScript appelable depuis WASM
|
|
88
|
+
registerCallback(name, callback) {
|
|
89
|
+
this.callbacks.set(name, callback);
|
|
90
|
+
globalThis[`__gowm_callback_${name}`] = callback;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Désinscrire un callback
|
|
94
|
+
unregisterCallback(name) {
|
|
95
|
+
this.callbacks.delete(name);
|
|
96
|
+
delete globalThis[`__gowm_callback_${name}`];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Obtenir la liste des fonctions disponibles
|
|
100
|
+
getAvailableFunctions() {
|
|
101
|
+
const functions = [];
|
|
102
|
+
|
|
103
|
+
// Fonctions exports WASM
|
|
104
|
+
if (this.module.exports) {
|
|
105
|
+
Object.keys(this.module.exports).forEach(key => {
|
|
106
|
+
if (typeof this.module.exports[key] === 'function') {
|
|
107
|
+
functions.push(key);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Fonctions globales (pour Go WASM)
|
|
113
|
+
Object.keys(globalThis).forEach(key => {
|
|
114
|
+
if (typeof globalThis[key] === 'function' && !key.startsWith('__gowm_')) {
|
|
115
|
+
functions.push(key);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return [...new Set(functions)].sort();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Vérifier si une fonction existe
|
|
123
|
+
hasFunction(funcName) {
|
|
124
|
+
return (
|
|
125
|
+
(this.module.exports && typeof this.module.exports[funcName] === 'function') ||
|
|
126
|
+
(typeof globalThis[funcName] === 'function')
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Obtenir les statistiques du module
|
|
131
|
+
getStats() {
|
|
132
|
+
return {
|
|
133
|
+
ready: this.module.ready,
|
|
134
|
+
functions: this.getAvailableFunctions(),
|
|
135
|
+
callbacks: Array.from(this.callbacks.keys()),
|
|
136
|
+
memoryUsage: this.module.exports.memory ? this.module.exports.memory.buffer.byteLength : 0,
|
|
137
|
+
name: this.name
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Nettoyage des ressources
|
|
142
|
+
cleanup() {
|
|
143
|
+
// Désinscrire tous les callbacks
|
|
144
|
+
for (const name of this.callbacks.keys()) {
|
|
145
|
+
this.unregisterCallback(name);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Arrêter le runtime Go si possible
|
|
149
|
+
if (this.module.go && typeof this.module.go.exit === 'function') {
|
|
150
|
+
try {
|
|
151
|
+
this.module.go.exit(0);
|
|
152
|
+
} catch (e) {
|
|
153
|
+
// Ignorer les erreurs
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
package/src/browser.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Version navigateur de GOWM sans dépendances Node.js
|
|
2
|
+
import WasmLoader from './loader-browser.js';
|
|
3
|
+
import WasmBridge from './bridge-browser.js';
|
|
4
|
+
|
|
5
|
+
class GoWM {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.loader = new WasmLoader();
|
|
8
|
+
this.modules = new Map();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async load(wasmPath, options = {}) {
|
|
12
|
+
const module = await this.loader.loadModule(wasmPath, options);
|
|
13
|
+
const bridge = new WasmBridge(module, options);
|
|
14
|
+
|
|
15
|
+
const moduleId = options.name || 'default';
|
|
16
|
+
this.modules.set(moduleId, { module, bridge });
|
|
17
|
+
|
|
18
|
+
return bridge;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get(name = 'default') {
|
|
22
|
+
const entry = this.modules.get(name);
|
|
23
|
+
return entry ? entry.bridge : null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async loadFromNPM(packageName, options = {}) {
|
|
27
|
+
// Pour le navigateur, on assume que le fichier est dans /wasm/{packageName}.wasm
|
|
28
|
+
const wasmPath = `/wasm/${packageName}.wasm`;
|
|
29
|
+
return this.load(wasmPath, { ...options, name: packageName });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
unload(name = 'default') {
|
|
33
|
+
const entry = this.modules.get(name);
|
|
34
|
+
if (entry) {
|
|
35
|
+
entry.bridge.cleanup();
|
|
36
|
+
this.loader.unloadModule(name);
|
|
37
|
+
return this.modules.delete(name);
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
listModules() {
|
|
43
|
+
return Array.from(this.modules.keys());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getStats() {
|
|
47
|
+
const stats = {};
|
|
48
|
+
for (const [name, entry] of this.modules) {
|
|
49
|
+
stats[name] = entry.bridge.getStats();
|
|
50
|
+
}
|
|
51
|
+
return stats;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
unloadAll() {
|
|
55
|
+
for (const name of this.modules.keys()) {
|
|
56
|
+
this.unload(name);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Instance globale
|
|
62
|
+
const gowm = new GoWM();
|
|
63
|
+
|
|
64
|
+
// API simplifiée
|
|
65
|
+
export const load = (wasmPath, options) => gowm.load(wasmPath, options);
|
|
66
|
+
export const get = (name) => gowm.get(name);
|
|
67
|
+
export const loadFromNPM = (packageName, options) => gowm.loadFromNPM(packageName, options);
|
|
68
|
+
export const unload = (name) => gowm.unload(name);
|
|
69
|
+
export const listModules = () => gowm.listModules();
|
|
70
|
+
export const getStats = () => gowm.getStats();
|
|
71
|
+
export const unloadAll = () => gowm.unloadAll();
|
|
72
|
+
|
|
73
|
+
// Export des classes
|
|
74
|
+
export { GoWM, WasmLoader, WasmBridge };
|
|
75
|
+
|
|
76
|
+
// Export par défaut
|
|
77
|
+
export default gowm;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
export default class WasmLoader {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.modules = new Map();
|
|
4
|
+
this.isNode = false; // Toujours faux en environnement navigateur
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
async loadModule(wasmPath, options = {}) {
|
|
8
|
+
const moduleId = options.name || wasmPath.split('/').pop().replace('.wasm', '');
|
|
9
|
+
|
|
10
|
+
if (this.modules.has(moduleId)) {
|
|
11
|
+
return this.modules.get(moduleId);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
// Charger wasm_exec.js si nécessaire
|
|
16
|
+
if (!globalThis.Go) {
|
|
17
|
+
await this.loadGoRuntime(options.goRuntimePath);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const go = new globalThis.Go();
|
|
21
|
+
|
|
22
|
+
const response = await fetch(wasmPath);
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
throw new Error(`Failed to fetch WASM file: ${response.status} ${response.statusText}`);
|
|
25
|
+
}
|
|
26
|
+
const wasmBytes = await response.arrayBuffer();
|
|
27
|
+
|
|
28
|
+
const result = await WebAssembly.instantiate(wasmBytes, go.importObject);
|
|
29
|
+
|
|
30
|
+
// Pré-initialisation pour optimiser les performances
|
|
31
|
+
if (options.preInit !== false) {
|
|
32
|
+
// Démarrer Go en arrière-plan pour éviter le blocage
|
|
33
|
+
go.run(result.instance);
|
|
34
|
+
|
|
35
|
+
// Attendre que le module soit prêt
|
|
36
|
+
await this.waitForReady(moduleId);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const module = {
|
|
40
|
+
instance: result.instance,
|
|
41
|
+
go: go,
|
|
42
|
+
exports: result.instance.exports,
|
|
43
|
+
ready: true
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
this.modules.set(moduleId, module);
|
|
47
|
+
return module;
|
|
48
|
+
|
|
49
|
+
} catch (error) {
|
|
50
|
+
throw new Error(`Failed to load WASM module ${moduleId}: ${error.message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async waitForReady(moduleId, timeout = 5000) {
|
|
55
|
+
const startTime = Date.now();
|
|
56
|
+
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const checkReady = () => {
|
|
59
|
+
// Vérifier plusieurs signaux de prêt
|
|
60
|
+
const isReady = globalThis.__gowm_ready ||
|
|
61
|
+
(globalThis.Go && globalThis.Go._initialized) ||
|
|
62
|
+
(globalThis.add && typeof globalThis.add === 'function');
|
|
63
|
+
|
|
64
|
+
if (isReady || Date.now() - startTime > timeout) {
|
|
65
|
+
if (isReady) {
|
|
66
|
+
resolve();
|
|
67
|
+
} else {
|
|
68
|
+
reject(new Error(`Module ${moduleId} failed to initialize within ${timeout}ms`));
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
setTimeout(checkReady, 10);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
checkReady();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async loadGoRuntime(customPath) {
|
|
79
|
+
const runtimePath = customPath || '/wasm_exec.js';
|
|
80
|
+
await this.loadScript(runtimePath);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
loadScript(src) {
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
const script = document.createElement('script');
|
|
86
|
+
script.src = src;
|
|
87
|
+
script.onload = resolve;
|
|
88
|
+
script.onerror = reject;
|
|
89
|
+
document.head.appendChild(script);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
getModule(name) {
|
|
94
|
+
return this.modules.get(name);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
unloadModule(name) {
|
|
98
|
+
const module = this.modules.get(name);
|
|
99
|
+
if (module && module.go) {
|
|
100
|
+
// Nettoyage des ressources Go
|
|
101
|
+
try {
|
|
102
|
+
module.go.exit(0);
|
|
103
|
+
} catch (e) {
|
|
104
|
+
// Ignorer les erreurs de nettoyage
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return this.modules.delete(name);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
listModules() {
|
|
111
|
+
return Array.from(this.modules.keys());
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// Version sécurisée du loader qui évite les problèmes de bundling
|
|
2
|
+
class WasmLoader {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.modules = new Map();
|
|
5
|
+
this.isNode = typeof window === 'undefined';
|
|
6
|
+
this.nodeModulesLoaded = false;
|
|
7
|
+
this.fs = null;
|
|
8
|
+
this.path = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async ensureNodeModules() {
|
|
12
|
+
if (this.isNode && !this.nodeModulesLoaded) {
|
|
13
|
+
try {
|
|
14
|
+
// Import dynamique pour éviter les problèmes avec les bundlers
|
|
15
|
+
this.fs = await this.dynamicRequire('fs');
|
|
16
|
+
this.path = await this.dynamicRequire('path');
|
|
17
|
+
this.nodeModulesLoaded = true;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.warn('Failed to load Node.js modules:', error.message);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async dynamicRequire(module) {
|
|
25
|
+
if (typeof require !== 'undefined') {
|
|
26
|
+
return require(module);
|
|
27
|
+
}
|
|
28
|
+
throw new Error(`Module ${module} not available`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async loadModule(wasmPath, options = {}) {
|
|
32
|
+
await this.ensureNodeModules();
|
|
33
|
+
|
|
34
|
+
const moduleId = options.name || (
|
|
35
|
+
this.isNode && this.path
|
|
36
|
+
? this.path.basename(wasmPath, '.wasm')
|
|
37
|
+
: wasmPath.split('/').pop().replace('.wasm', '')
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
if (this.modules.has(moduleId)) {
|
|
41
|
+
return this.modules.get(moduleId);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
// Charger wasm_exec.js si nécessaire
|
|
46
|
+
if (!globalThis.Go) {
|
|
47
|
+
await this.loadGoRuntime(options.goRuntimePath);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const go = new globalThis.Go();
|
|
51
|
+
let wasmBytes;
|
|
52
|
+
|
|
53
|
+
if (this.isNode && this.fs) {
|
|
54
|
+
if (!this.fs.existsSync(wasmPath)) {
|
|
55
|
+
throw new Error(`WASM file not found: ${wasmPath}`);
|
|
56
|
+
}
|
|
57
|
+
wasmBytes = this.fs.readFileSync(wasmPath);
|
|
58
|
+
} else {
|
|
59
|
+
const response = await fetch(wasmPath);
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
throw new Error(`Failed to fetch WASM file: ${response.status} ${response.statusText}`);
|
|
62
|
+
}
|
|
63
|
+
wasmBytes = await response.arrayBuffer();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const result = await WebAssembly.instantiate(wasmBytes, go.importObject);
|
|
67
|
+
|
|
68
|
+
// Pré-initialisation pour optimiser les performances
|
|
69
|
+
if (options.preInit !== false) {
|
|
70
|
+
// Démarrer Go en arrière-plan pour éviter le blocage
|
|
71
|
+
go.run(result.instance);
|
|
72
|
+
|
|
73
|
+
// Attendre que le module soit prêt
|
|
74
|
+
await this.waitForReady(moduleId);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const module = {
|
|
78
|
+
instance: result.instance,
|
|
79
|
+
go: go,
|
|
80
|
+
exports: result.instance.exports,
|
|
81
|
+
ready: true
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
this.modules.set(moduleId, module);
|
|
85
|
+
return module;
|
|
86
|
+
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw new Error(`Failed to load WASM module ${moduleId}: ${error.message}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async waitForReady(moduleId, timeout = 5000) {
|
|
93
|
+
const startTime = Date.now();
|
|
94
|
+
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
const checkReady = () => {
|
|
97
|
+
// Vérifier plusieurs signaux de prêt
|
|
98
|
+
const isReady = globalThis.__gowm_ready ||
|
|
99
|
+
(globalThis.Go && globalThis.Go._initialized) ||
|
|
100
|
+
(globalThis.add && typeof globalThis.add === 'function');
|
|
101
|
+
|
|
102
|
+
if (isReady || Date.now() - startTime > timeout) {
|
|
103
|
+
if (isReady) {
|
|
104
|
+
resolve();
|
|
105
|
+
} else {
|
|
106
|
+
reject(new Error(`Module ${moduleId} failed to initialize within ${timeout}ms`));
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
setTimeout(checkReady, 10);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
checkReady();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async loadGoRuntime(customPath) {
|
|
117
|
+
await this.ensureNodeModules();
|
|
118
|
+
|
|
119
|
+
const runtimePath = customPath || this.getDefaultRuntimePath();
|
|
120
|
+
|
|
121
|
+
if (this.isNode && this.fs) {
|
|
122
|
+
// Vérifier que le fichier existe
|
|
123
|
+
if (!this.fs.existsSync(runtimePath)) {
|
|
124
|
+
console.warn(`Go runtime not found at ${runtimePath}, using fallback`);
|
|
125
|
+
// Utiliser le fallback du dossier runtime
|
|
126
|
+
const fallbackPath = this.path ? this.path.join(__dirname, '../runtime/wasm_exec.js') : null;
|
|
127
|
+
if (fallbackPath && this.fs.existsSync(fallbackPath)) {
|
|
128
|
+
require(fallbackPath);
|
|
129
|
+
} else {
|
|
130
|
+
throw new Error(`Go runtime not found. Please ensure wasm_exec.js is available`);
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
require(runtimePath);
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
await this.loadScript(runtimePath);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
getDefaultRuntimePath() {
|
|
141
|
+
if (this.isNode && this.path) {
|
|
142
|
+
return this.path.join(__dirname, '../runtime/wasm_exec.js');
|
|
143
|
+
}
|
|
144
|
+
return '/wasm_exec.js';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
loadScript(src) {
|
|
148
|
+
return new Promise((resolve, reject) => {
|
|
149
|
+
if (typeof document === 'undefined') {
|
|
150
|
+
reject(new Error('Cannot load script in non-browser environment'));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const script = document.createElement('script');
|
|
155
|
+
script.src = src;
|
|
156
|
+
script.onload = resolve;
|
|
157
|
+
script.onerror = reject;
|
|
158
|
+
document.head.appendChild(script);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
getModule(name) {
|
|
163
|
+
return this.modules.get(name);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
unloadModule(name) {
|
|
167
|
+
const module = this.modules.get(name);
|
|
168
|
+
if (module && module.go) {
|
|
169
|
+
// Nettoyage des ressources Go
|
|
170
|
+
try {
|
|
171
|
+
module.go.exit(0);
|
|
172
|
+
} catch (e) {
|
|
173
|
+
// Ignorer les erreurs de nettoyage
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return this.modules.delete(name);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
listModules() {
|
|
180
|
+
return Array.from(this.modules.keys());
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = WasmLoader;
|
package/src/loader.js
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
1
|
class WasmLoader {
|
|
5
2
|
constructor() {
|
|
6
3
|
this.modules = new Map();
|
|
7
4
|
this.isNode = typeof window === 'undefined';
|
|
5
|
+
|
|
6
|
+
// Les modules Node.js seront importés de façon dynamique quand nécessaire
|
|
7
|
+
this.fs = null;
|
|
8
|
+
this.path = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async ensureNodeModules() {
|
|
12
|
+
if (this.isNode && !this.fs) {
|
|
13
|
+
this.fs = require('fs');
|
|
14
|
+
this.path = require('path');
|
|
15
|
+
}
|
|
8
16
|
}
|
|
9
17
|
|
|
10
18
|
async loadModule(wasmPath, options = {}) {
|
|
11
|
-
|
|
19
|
+
// S'assurer que les modules Node.js sont chargés si nécessaire
|
|
20
|
+
await this.ensureNodeModules();
|
|
21
|
+
|
|
22
|
+
const moduleId = options.name || (this.isNode ? this.path.basename(wasmPath, '.wasm') : wasmPath.split('/').pop().replace('.wasm', ''));
|
|
12
23
|
|
|
13
24
|
if (this.modules.has(moduleId)) {
|
|
14
25
|
return this.modules.get(moduleId);
|
|
@@ -24,10 +35,10 @@ class WasmLoader {
|
|
|
24
35
|
let wasmBytes;
|
|
25
36
|
|
|
26
37
|
if (this.isNode) {
|
|
27
|
-
if (!fs.existsSync(wasmPath)) {
|
|
38
|
+
if (!this.fs.existsSync(wasmPath)) {
|
|
28
39
|
throw new Error(`WASM file not found: ${wasmPath}`);
|
|
29
40
|
}
|
|
30
|
-
wasmBytes = fs.readFileSync(wasmPath);
|
|
41
|
+
wasmBytes = this.fs.readFileSync(wasmPath);
|
|
31
42
|
} else {
|
|
32
43
|
const response = await fetch(wasmPath);
|
|
33
44
|
if (!response.ok) {
|
|
@@ -87,15 +98,17 @@ class WasmLoader {
|
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
async loadGoRuntime(customPath) {
|
|
101
|
+
await this.ensureNodeModules();
|
|
102
|
+
|
|
90
103
|
const runtimePath = customPath || this.getDefaultRuntimePath();
|
|
91
104
|
|
|
92
105
|
if (this.isNode) {
|
|
93
106
|
// Vérifier que le fichier existe
|
|
94
|
-
if (!fs.existsSync(runtimePath)) {
|
|
107
|
+
if (!this.fs.existsSync(runtimePath)) {
|
|
95
108
|
console.warn(`Go runtime not found at ${runtimePath}, using fallback`);
|
|
96
109
|
// Utiliser le fallback du dossier runtime
|
|
97
|
-
const fallbackPath = path.join(__dirname, '../runtime/wasm_exec.js');
|
|
98
|
-
if (fs.existsSync(fallbackPath)) {
|
|
110
|
+
const fallbackPath = this.path.join(__dirname, '../runtime/wasm_exec.js');
|
|
111
|
+
if (this.fs.existsSync(fallbackPath)) {
|
|
99
112
|
require(fallbackPath);
|
|
100
113
|
} else {
|
|
101
114
|
throw new Error(`Go runtime not found. Please ensure wasm_exec.js is available`);
|
|
@@ -109,8 +122,8 @@ class WasmLoader {
|
|
|
109
122
|
}
|
|
110
123
|
|
|
111
124
|
getDefaultRuntimePath() {
|
|
112
|
-
if (this.isNode) {
|
|
113
|
-
return path.join(__dirname, '../runtime/wasm_exec.js');
|
|
125
|
+
if (this.isNode && this.path) {
|
|
126
|
+
return this.path.join(__dirname, '../runtime/wasm_exec.js');
|
|
114
127
|
}
|
|
115
128
|
return '/wasm_exec.js';
|
|
116
129
|
}
|