baraqex 2.0.5 → 2.0.7
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/dist/browser.js +113 -2
- package/dist/browser.js.map +2 -2
- package/dist/core/jsx-runtime.d.ts +4 -0
- package/dist/core/jsx-runtime.d.ts.map +1 -1
- package/dist/core/renderer.d.ts +5 -4
- package/dist/core/renderer.d.ts.map +1 -1
- package/dist/index.js +113 -2
- package/dist/index.js.map +2 -2
- package/dist/wasm.d.ts +1 -1
- package/dist/wasm.d.ts.map +1 -1
- package/dist/wasm.js +13 -1
- package/dist/wasm.js.map +2 -2
- package/package.json +1 -1
- package/templates/fullstack-app/build-wasm.js +12 -3
- package/templates/fullstack-app/src/App.tsx +13 -8
- package/templates/fullstack-app/src/pages/Home.tsx +65 -17
- package/templates/fullstack-app/src/pages/WasmDemo.tsx +2 -1
package/dist/wasm.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface GoWasmInstance {
|
|
|
15
15
|
functions: Record<string, Function>;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Load Go WASM module in the browser (
|
|
18
|
+
* Load Go WASM module in the browser (cached per URL).
|
|
19
19
|
*/
|
|
20
20
|
export declare function loadGoWasm(wasmUrl: string, options?: GoWasmOptions): Promise<GoWasmInstance>;
|
|
21
21
|
/**
|
package/dist/wasm.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wasm.d.ts","sourceRoot":"","sources":["../src/wasm.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC;IAC/B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC;IAC3B,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CACrC;
|
|
1
|
+
{"version":3,"file":"wasm.d.ts","sourceRoot":"","sources":["../src/wasm.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,GAAG,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC;IAC/B,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC;IAC3B,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CACrC;AAQD;;GAEG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,cAAc,CAAC,CAiBzB;AA2ED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAWtE;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,OAAO,CASrC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAQ3C;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB;;;;;EAarE;AAGD,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,EAAE,EAAE,UAAU;YACZ,YAAY,EAAE,GAAG,CAAC;YAClB,GAAG,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,QAAQ,KAAK,IAAI,CAAC;SAC/C,CAAC;QACF,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KACpB;CACF"}
|
package/dist/wasm.js
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
// src/wasm.ts
|
|
2
|
-
|
|
2
|
+
var wasmCache = /* @__PURE__ */ new Map();
|
|
3
|
+
function loadGoWasm(wasmUrl, options = {}) {
|
|
3
4
|
if (typeof window === "undefined") {
|
|
4
5
|
throw new Error("loadGoWasm() is for browser use only. Use loadGoWasmFromFile() in Node.js.");
|
|
5
6
|
}
|
|
7
|
+
const cached = wasmCache.get(wasmUrl);
|
|
8
|
+
if (cached)
|
|
9
|
+
return cached;
|
|
10
|
+
const request = loadGoWasmInternal(wasmUrl, options).catch((error) => {
|
|
11
|
+
wasmCache.delete(wasmUrl);
|
|
12
|
+
throw error;
|
|
13
|
+
});
|
|
14
|
+
wasmCache.set(wasmUrl, request);
|
|
15
|
+
return request;
|
|
16
|
+
}
|
|
17
|
+
async function loadGoWasmInternal(wasmUrl, options = {}) {
|
|
6
18
|
try {
|
|
7
19
|
if (!window.Go) {
|
|
8
20
|
throw new Error("Go WASM runtime not available. Make sure wasm_exec.js is loaded.");
|
package/dist/wasm.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/wasm.ts"],
|
|
4
|
-
"sourcesContent": ["/**\r\n * WebAssembly integration for Frontend Hamroun\r\n * Works in both browser and Node.js environments\r\n */\r\n\r\nexport interface GoWasmOptions {\r\n debug?: boolean;\r\n goWasmPath?: string;\r\n importObject?: any;\r\n onLoad?: (instance: GoWasmInstance) => void;\r\n}\r\n\r\nexport interface GoWasmInstance {\r\n instance: WebAssembly.Instance;\r\n module: WebAssembly.Module;\r\n exports: WebAssembly.Exports;\r\n functions: Record<string, Function>;\r\n}\r\n\r\n/**\r\n * Load Go WASM module in the browser (
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["/**\r\n * WebAssembly integration for Frontend Hamroun\r\n * Works in both browser and Node.js environments\r\n */\r\n\r\nexport interface GoWasmOptions {\r\n debug?: boolean;\r\n goWasmPath?: string;\r\n importObject?: any;\r\n onLoad?: (instance: GoWasmInstance) => void;\r\n}\r\n\r\nexport interface GoWasmInstance {\r\n instance: WebAssembly.Instance;\r\n module: WebAssembly.Module;\r\n exports: WebAssembly.Exports;\r\n functions: Record<string, Function>;\r\n}\r\n\r\n/**\r\n * Cache loaded WASM instances per URL so visiting a route (or re-mounting a\r\n * component) doesn't re-fetch, re-compile, or re-run the module every time.\r\n */\r\nconst wasmCache = new Map<string, Promise<GoWasmInstance>>();\r\n\r\n/**\r\n * Load Go WASM module in the browser (cached per URL).\r\n */\r\nexport function loadGoWasm(\r\n wasmUrl: string,\r\n options: GoWasmOptions = {}\r\n): Promise<GoWasmInstance> {\r\n if (typeof window === 'undefined') {\r\n throw new Error('loadGoWasm() is for browser use only. Use loadGoWasmFromFile() in Node.js.');\r\n }\r\n\r\n const cached = wasmCache.get(wasmUrl);\r\n if (cached) return cached;\r\n\r\n const request = loadGoWasmInternal(wasmUrl, options)\r\n .catch((error) => {\r\n // Don't cache failures \u2014 allow a retry next time the module is requested.\r\n wasmCache.delete(wasmUrl);\r\n throw error;\r\n });\r\n\r\n wasmCache.set(wasmUrl, request);\r\n return request;\r\n}\r\n\r\nasync function loadGoWasmInternal(\r\n wasmUrl: string,\r\n options: GoWasmOptions = {}\r\n): Promise<GoWasmInstance> {\r\n try {\r\n // Check if Go WASM runtime is available (like your frontend code)\r\n if (!window.Go) {\r\n throw new Error('Go WASM runtime not available. Make sure wasm_exec.js is loaded.');\r\n }\r\n\r\n if (options.debug) {\r\n console.log('[WASM] Loading WASM from:', wasmUrl);\r\n }\r\n\r\n // Fetch the WASM file (exactly like your frontend code)\r\n const response = await fetch(wasmUrl);\r\n \r\n if (!response.ok) {\r\n throw new Error(`Failed to fetch WASM file: ${response.status}`);\r\n }\r\n\r\n const wasmBytes = await response.arrayBuffer();\r\n \r\n // Initialize Go runtime (exactly like your frontend code)\r\n const go = new window.Go();\r\n const wasmModule = await WebAssembly.instantiate(wasmBytes, go.importObject);\r\n \r\n // Run the Go program (exactly like your frontend code)\r\n go.run(wasmModule.instance);\r\n \r\n // Wait a bit for Go functions to be registered (like your frontend code)\r\n await new Promise(resolve => setTimeout(resolve, 100));\r\n \r\n if (options.debug) {\r\n console.log('[WASM] Go program started, checking for registered functions...');\r\n }\r\n\r\n // Extract exported functions from global scope (like your frontend code)\r\n const functions: Record<string, Function> = {};\r\n \r\n // Look for Go functions in global scope\r\n for (const key in window) {\r\n if (typeof window[key] === 'function' && key.startsWith('go')) {\r\n functions[key] = window[key] as Function;\r\n if (options.debug) {\r\n console.log(`[WASM] Found function: ${key}`);\r\n }\r\n }\r\n }\r\n\r\n const wasmInstance: GoWasmInstance = {\r\n instance: wasmModule.instance,\r\n module: wasmModule,\r\n exports: wasmModule.instance.exports,\r\n functions\r\n };\r\n\r\n if (options.onLoad) {\r\n options.onLoad(wasmInstance);\r\n }\r\n\r\n if (options.debug) {\r\n console.log(`[WASM] Module loaded successfully with ${Object.keys(functions).length} functions`);\r\n }\r\n\r\n return wasmInstance;\r\n\r\n } catch (error) {\r\n console.error('[WASM] Error loading Go WASM module:', error);\r\n throw error;\r\n }\r\n}\r\n\r\n/**\r\n * Call a Go WASM function (like your frontend code's callWasmFunction)\r\n */\r\nexport function callWasmFunction(funcName: string, ...args: any[]): any {\r\n if (typeof window === 'undefined') {\r\n throw new Error('callWasmFunction() is for browser use only.');\r\n }\r\n\r\n const func = (window as any)[funcName];\r\n if (typeof func !== 'function') {\r\n throw new Error(`Function ${funcName} not found. Make sure the WASM module is loaded.`);\r\n }\r\n \r\n return func(...args);\r\n}\r\n\r\n/**\r\n * Check if Go WASM is ready (like your frontend code's wasmReady state)\r\n */\r\nexport function isWasmReady(): boolean {\r\n if (typeof window === 'undefined') {\r\n return false;\r\n }\r\n\r\n // Check if Go runtime is available and at least one Go function exists\r\n return !!(window.Go && Object.keys(window).some(key => \r\n key.startsWith('go') && typeof window[key] === 'function'\r\n ));\r\n}\r\n\r\n/**\r\n * Get all available Go WASM functions\r\n */\r\nexport function getWasmFunctions(): string[] {\r\n if (typeof window === 'undefined') {\r\n return [];\r\n }\r\n\r\n return Object.keys(window).filter(key => \r\n key.startsWith('go') && typeof window[key] === 'function'\r\n );\r\n}\r\n\r\n/**\r\n * Hook for React components (like your frontend code's useEffect pattern)\r\n */\r\nexport function useGoWasm(wasmUrl: string, options: GoWasmOptions = {}) {\r\n if (typeof window === 'undefined') {\r\n throw new Error('useGoWasm() is for browser use only.');\r\n }\r\n\r\n // This would need to be implemented as a React hook in frontend-hamroun\r\n // For now, return the basic functionality\r\n return {\r\n loadWasm: () => loadGoWasm(wasmUrl, options),\r\n callFunction: callWasmFunction,\r\n isReady: isWasmReady,\r\n getFunctions: getWasmFunctions\r\n };\r\n}\r\n\r\n// Browser-specific type declarations\r\ndeclare global {\r\n interface Window {\r\n Go: new () => {\r\n importObject: any;\r\n run: (instance: WebAssembly.Instance) => void;\r\n };\r\n [key: string]: any;\r\n }\r\n}\r\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -63,11 +63,20 @@ function buildWasm() {
|
|
|
63
63
|
|
|
64
64
|
function copyRuntime() {
|
|
65
65
|
const root = goRoot();
|
|
66
|
-
const
|
|
66
|
+
const candidates = [
|
|
67
|
+
path.join(root, 'lib', 'wasm', 'wasm_exec.js'),
|
|
68
|
+
path.join(root, 'misc', 'wasm', 'wasm_exec.js')
|
|
69
|
+
];
|
|
70
|
+
const wasmExec = candidates.find((p) => existsSync(p));
|
|
67
71
|
const dest = path.join(outputDir, 'wasm_exec.js');
|
|
68
72
|
|
|
69
|
-
if (!
|
|
70
|
-
console.warn(
|
|
73
|
+
if (!wasmExec) {
|
|
74
|
+
console.warn(
|
|
75
|
+
'⚠️ wasm_exec.js not found in this Go install. ' +
|
|
76
|
+
'Copy it to public/wasm/wasm_exec.js manually:\n' +
|
|
77
|
+
` cp "${candidates.join('" OR "')}" ${dest}\n` +
|
|
78
|
+
'(location varies by Go version: misc/wasm or lib/wasm)'
|
|
79
|
+
);
|
|
71
80
|
return;
|
|
72
81
|
}
|
|
73
82
|
|
|
@@ -18,11 +18,14 @@ function parseRoute(pathname: string): string {
|
|
|
18
18
|
export function App({ route, initialState }: AppProps) {
|
|
19
19
|
const [current, setCurrent] = useState<string>(parseRoute(route || (typeof window !== 'undefined' ? window.location.pathname : '/')));
|
|
20
20
|
|
|
21
|
-
const navigate = (target: string) => {
|
|
21
|
+
const navigate = (e: any, target: string, href: string) => {
|
|
22
|
+
if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
|
|
23
|
+
return; // let the browser handle new-tab / new-window navigation
|
|
24
|
+
}
|
|
25
|
+
e.preventDefault();
|
|
22
26
|
setCurrent(target);
|
|
23
27
|
if (typeof window !== 'undefined') {
|
|
24
|
-
|
|
25
|
-
window.history.pushState(null, '', path);
|
|
28
|
+
window.history.pushState(null, '', href);
|
|
26
29
|
}
|
|
27
30
|
};
|
|
28
31
|
|
|
@@ -36,22 +39,24 @@ export function App({ route, initialState }: AppProps) {
|
|
|
36
39
|
<div className="min-h-screen flex flex-col bg-gray-50">
|
|
37
40
|
<header className="bg-blue-700 text-white shadow">
|
|
38
41
|
<div className="max-w-5xl mx-auto px-4 py-4 flex items-center justify-between">
|
|
39
|
-
<
|
|
42
|
+
<a href="/" onClick={(e: any) => navigate(e, 'home', '/')} className="text-xl font-bold hover:text-blue-100">
|
|
40
43
|
Baraqex Full-Stack
|
|
41
|
-
</
|
|
44
|
+
</a>
|
|
42
45
|
<nav>
|
|
43
46
|
<ul className="flex space-x-6">
|
|
44
47
|
{navItems.map((item) => (
|
|
45
48
|
<li key={item.key}>
|
|
46
|
-
<
|
|
47
|
-
|
|
49
|
+
<a
|
|
50
|
+
href={item.href}
|
|
51
|
+
onClick={(e: any) => navigate(e, item.key, item.href)}
|
|
52
|
+
aria-current={current === item.key ? 'page' : undefined}
|
|
48
53
|
className={
|
|
49
54
|
'hover:text-blue-200 ' +
|
|
50
55
|
(current === item.key ? 'underline font-semibold' : '')
|
|
51
56
|
}
|
|
52
57
|
>
|
|
53
58
|
{item.label}
|
|
54
|
-
</
|
|
59
|
+
</a>
|
|
55
60
|
</li>
|
|
56
61
|
))}
|
|
57
62
|
</ul>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx, useState } from 'baraqex';
|
|
1
|
+
import { jsx, useState, useEffect } from 'baraqex';
|
|
2
2
|
|
|
3
3
|
export interface HomeState {
|
|
4
4
|
users?: any[];
|
|
@@ -6,19 +6,62 @@ export interface HomeState {
|
|
|
6
6
|
serverTime?: string;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
+
let usersCache: any[] | null = null;
|
|
10
|
+
let usersRequest: Promise<any[]> | null = null;
|
|
11
|
+
|
|
12
|
+
async function fetchUsers(): Promise<any[]> {
|
|
13
|
+
if (usersRequest) return usersRequest;
|
|
14
|
+
usersRequest = fetch('/api/users').then((res) => {
|
|
15
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
16
|
+
return res.json();
|
|
17
|
+
}).finally(() => {
|
|
18
|
+
usersRequest = null;
|
|
19
|
+
});
|
|
20
|
+
return usersRequest;
|
|
21
|
+
}
|
|
22
|
+
|
|
9
23
|
export function HomePage({ initialState }: { initialState?: any }) {
|
|
10
24
|
const initial: HomeState = initialState || { users: [], posts: [] };
|
|
11
|
-
const [users, setUsers] = useState<any[]>(initial.users || []);
|
|
25
|
+
const [users, setUsers] = useState<any[]>(usersCache || initial.users || []);
|
|
26
|
+
const [loading, setLoading] = useState(usersCache === null && !initial.users);
|
|
12
27
|
const [message, setMessage] = useState('');
|
|
13
28
|
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (usersCache) {
|
|
31
|
+
setUsers(usersCache);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
let cancelled = false;
|
|
35
|
+
setLoading(true);
|
|
36
|
+
fetchUsers()
|
|
37
|
+
.then((data) => {
|
|
38
|
+
if (cancelled) return;
|
|
39
|
+
usersCache = data;
|
|
40
|
+
setUsers(data);
|
|
41
|
+
setLoading(false);
|
|
42
|
+
})
|
|
43
|
+
.catch((e: any) => {
|
|
44
|
+
if (cancelled) return;
|
|
45
|
+
setLoading(false);
|
|
46
|
+
setMessage('Failed to load data: ' + e.message);
|
|
47
|
+
});
|
|
48
|
+
return () => {
|
|
49
|
+
cancelled = true;
|
|
50
|
+
};
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
14
53
|
const refresh = async () => {
|
|
54
|
+
setLoading(true);
|
|
55
|
+
setMessage('');
|
|
15
56
|
try {
|
|
16
|
-
|
|
17
|
-
const data = await
|
|
57
|
+
usersCache = null;
|
|
58
|
+
const data = await fetchUsers();
|
|
18
59
|
setUsers(data);
|
|
19
60
|
setMessage('Data refreshed from /api/users');
|
|
20
61
|
} catch (e: any) {
|
|
21
62
|
setMessage('Failed to load data: ' + e.message);
|
|
63
|
+
} finally {
|
|
64
|
+
setLoading(false);
|
|
22
65
|
}
|
|
23
66
|
};
|
|
24
67
|
|
|
@@ -44,24 +87,29 @@ export function HomePage({ initialState }: { initialState?: any }) {
|
|
|
44
87
|
<h2 className="text-xl font-semibold text-gray-800">Users (fetched via API)</h2>
|
|
45
88
|
<button
|
|
46
89
|
onClick={refresh}
|
|
47
|
-
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
|
90
|
+
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-40"
|
|
91
|
+
disabled={loading}
|
|
48
92
|
>
|
|
49
|
-
Refresh
|
|
93
|
+
{loading ? 'Loading…' : 'Refresh'}
|
|
50
94
|
</button>
|
|
51
95
|
</div>
|
|
52
96
|
{message && <p className="text-green-700 text-sm mb-3">{message}</p>}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
97
|
+
{loading && users.length === 0 ? (
|
|
98
|
+
<p className="text-gray-500 text-sm">Loading users…</p>
|
|
99
|
+
) : (
|
|
100
|
+
<ul className="divide-y divide-gray-200">
|
|
101
|
+
{(users || []).map((user: any) => (
|
|
102
|
+
<li key={user.id} className="py-2 flex items-center justify-between">
|
|
103
|
+
<span className="font-medium">{user.name}</span>
|
|
104
|
+
<span className="text-sm text-gray-500">{user.email}</span>
|
|
105
|
+
</li>
|
|
106
|
+
))}
|
|
107
|
+
</ul>
|
|
108
|
+
)}
|
|
109
|
+
{!loading && (users || []).length === 0 && !message && (
|
|
110
|
+
<p className="text-gray-500 text-sm">No users loaded yet.</p>
|
|
63
111
|
)}
|
|
64
112
|
</section>
|
|
65
113
|
</div>
|
|
66
114
|
);
|
|
67
|
-
}
|
|
115
|
+
}
|
|
@@ -3,7 +3,7 @@ import { loadGoWasm } from 'baraqex';
|
|
|
3
3
|
|
|
4
4
|
export function WasmDemo() {
|
|
5
5
|
const [ready, setReady] = useState(false);
|
|
6
|
-
const [status, setStatus] = useState('
|
|
6
|
+
const [status, setStatus] = useState('Preparing Go WASM…');
|
|
7
7
|
const [output, setOutput] = useState('');
|
|
8
8
|
const [a, setA] = useState('5');
|
|
9
9
|
const [b, setB] = useState('7');
|
|
@@ -13,6 +13,7 @@ export function WasmDemo() {
|
|
|
13
13
|
let cancelled = false;
|
|
14
14
|
(async () => {
|
|
15
15
|
try {
|
|
16
|
+
setStatus('Loading Go WASM module…');
|
|
16
17
|
await loadGoWasm('/wasm/example.wasm');
|
|
17
18
|
if (cancelled) return;
|
|
18
19
|
batchUpdates(() => {
|