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 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.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
- const moduleId = options.name || path.basename(wasmPath, '.wasm');
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
  }