@devscholar/node-with-gjs 0.0.0 → 0.0.1

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/README.md CHANGED
@@ -31,87 +31,7 @@ pacman -S gtk4 webkitgtk-6.0 gjs
31
31
 
32
32
  # Usage
33
33
 
34
- ## Runtime Support
35
-
36
- | Runtime | `gi://` URL Syntax | Loader Hooks |
37
- |---------|-------------------|--------------|
38
- | Node.js | ✅ Supported | ✅ `--experimental-loader` |
39
- | Bun | ❌ Not supported | ❌ No hooks mechanism |
40
- | Deno | ❌ Not supported | ❌ No hooks mechanism |
41
-
42
- ## Node.js
43
-
44
- Node.js supports the `gi://` URL syntax, which is consistent with GJS:
45
-
46
- ```typescript
47
- import Gtk from 'gi://Gtk?version=4.0';
48
- import WebKit from 'gi://WebKit?version=6.0';
49
-
50
- const app = new Gtk.Application({ application_id: 'org.example.app' });
51
- // ...
52
- ```
53
-
54
- ## Bun / Deno
55
-
56
- Bun and Deno do not support Node.js loader hooks, so you need to use the `loadGi` function instead:
57
-
58
- ```typescript
59
- import { loadGi } from './gi-loader.ts';
60
-
61
- const Gtk = loadGi('Gtk', '4.0');
62
- const WebKit = loadGi('WebKit', '6.0');
63
-
64
- const app = new Gtk.Application({ application_id: 'org.example.app' });
65
- // ...
66
- ```
67
-
68
- **Note:** The `loadGi` function also works with Node.js, useful for older Node.js versions or when not using experimental loader flags.
69
-
70
- # Examples
71
-
72
- ## Console Apps
73
-
74
- ### Console Input App
75
-
76
- ```bash
77
- node start.js examples/console/console-input/console-input.ts
78
- ```
79
-
80
- ### Await Delay App
81
-
82
- ```bash
83
- node start.js examples/console/await-delay/await-delay.ts
84
- ```
85
-
86
- ## GUI Apps
87
-
88
- ### GTK4 Counter App
89
-
90
- ```bash
91
- node start.js examples/gtk/counter/counter.ts
92
- ```
93
-
94
- ### GTK4 Drag Box App
95
-
96
- A drag box example that demonstrates high frequency IPC.
97
-
98
- ```bash
99
- node start.js examples/gtk/drag-box/drag-box.ts
100
- ```
101
-
102
- ### GTK4 WebKit Counter App
103
-
104
- ```bash
105
- node start.js examples/gtk-webkit/counter/counter.ts
106
- ```
107
-
108
- ### Adwaita Counter App (libadwaita)
109
-
110
- A counter example based on [libadwaita](https://gnome.pages.gitlab.gnome.org/libadwaita/), demonstrating how to use Adwaita-specific components like `Adw.ApplicationWindow` and `Adw.Clamp`.
111
-
112
- ```bash
113
- node start.js examples/adwaita/counter/counter.ts
114
- ```
34
+ For more examples and details, see the [node-with-gjs-examples README](https://github.com/devscholar/node-with-gjs-examples).
115
35
 
116
36
  # License
117
37
 
package/package.json CHANGED
@@ -1,9 +1,15 @@
1
1
  {
2
2
  "name": "@devscholar/node-with-gjs",
3
- "version": "0.0.0",
3
+ "version": "0.0.1",
4
4
  "description": "Node.js IPC Bridge for GJS",
5
- "main": "start.js",
5
+ "main": "./src/index.ts",
6
6
  "type": "module",
7
+ "exports": {
8
+ ".": "./src/index.ts"
9
+ },
10
+ "bin": {
11
+
12
+ },
7
13
  "scripts": {
8
14
 
9
15
  },
@@ -11,4 +17,4 @@
11
17
  "@types/node": "^20.0.0",
12
18
  "typescript": "^5.0.0"
13
19
  }
14
- }
20
+ }
package/scripts/host.js CHANGED
@@ -83,7 +83,9 @@ function ResolveArg(arg) {
83
83
 
84
84
  function executeCommand(cmd) {
85
85
  if (cmd.action === 'LoadNamespace') {
86
- imports.gi.versions[cmd.namespace] = cmd.version;
86
+ if (cmd.version) {
87
+ imports.gi.versions[cmd.namespace] = cmd.version;
88
+ }
87
89
  const ns = imports.gi[cmd.namespace];
88
90
  return ConvertToProtocol(ns);
89
91
  }
package/src/index.ts CHANGED
@@ -173,8 +173,46 @@ export function init() {
173
173
  initialize();
174
174
  }
175
175
 
176
- export function loadGiNamespace(namespace: string, version: string) {
176
+ // Internal function - not exposed to users
177
+ function loadGiNamespace(namespace: string, version: string | undefined) {
177
178
  initialize();
178
179
  const res = ipc!.send({ action: 'LoadNamespace', namespace, version });
179
180
  return createProxy(res);
180
181
  }
182
+
183
+ // Namespace cache to avoid creating multiple proxies for the same namespace
184
+ const namespaceCache = new Map<string, any>();
185
+
186
+ // GI namespace versions
187
+ const giVersions: Record<string, string> = {};
188
+
189
+ // Create the gi proxy with lazy loading and caching
190
+ const giProxy = new Proxy({} as any, {
191
+ get(_, namespace: string) {
192
+ if (namespace === 'versions') {
193
+ return new Proxy(giVersions, {
194
+ set(target, prop, value) {
195
+ target[prop as string] = value;
196
+ // Clear cache for this namespace when version changes
197
+ const cacheKey = `${prop as string}@default`;
198
+ namespaceCache.delete(cacheKey);
199
+ return true;
200
+ }
201
+ });
202
+ }
203
+
204
+ const version = giVersions[namespace];
205
+ const cacheKey = `${namespace}@${version || 'default'}`;
206
+
207
+ if (!namespaceCache.has(cacheKey)) {
208
+ namespaceCache.set(cacheKey, loadGiNamespace(namespace, version));
209
+ }
210
+
211
+ return namespaceCache.get(cacheKey);
212
+ }
213
+ });
214
+
215
+ // The main exports object - compatible with GJS imports
216
+ export const imports = {
217
+ gi: giProxy
218
+ };
@@ -1,66 +0,0 @@
1
- // Run: node start.js examples/adwaita/counter/counter.ts
2
- import { loadGi } from '../../../gi-loader.ts';
3
-
4
- const Gtk = loadGi('Gtk', '4.0');
5
- const Adw = loadGi('Adw', '1');
6
-
7
- let clickCount = 0;
8
-
9
- console.log("--- Adwaita Counter ---");
10
-
11
- const app = new Adw.Application({ application_id: 'org.adwaita.counter' });
12
-
13
- app.connect('activate', () => {
14
- const window = new Adw.ApplicationWindow({
15
- application: app,
16
- title: 'Adwaita Counter App',
17
- default_width: 400,
18
- default_height: 300
19
- });
20
-
21
- const toolbarView = new Adw.ToolbarView();
22
-
23
- const headerBar = new Adw.HeaderBar({
24
- title_widget: new Gtk.Label({ label: 'Adwaita Counter App' })
25
- });
26
- toolbarView.add_top_bar(headerBar);
27
-
28
- const box = new Gtk.Box({
29
- orientation: Gtk.Orientation.VERTICAL,
30
- spacing: 10,
31
- halign: Gtk.Align.CENTER,
32
- valign: Gtk.Align.CENTER
33
- });
34
- box.set_margin_start(20);
35
- box.set_margin_end(20);
36
- box.set_margin_top(20);
37
- box.set_margin_bottom(20);
38
-
39
- const label = new Gtk.Label({
40
- label: 'Clicks: 0',
41
- css_classes: ['title-1']
42
- });
43
-
44
- const button = new Gtk.Button({
45
- label: 'Click to Add',
46
- css_classes: ['suggested-action']
47
- });
48
-
49
- button.connect('clicked', () => {
50
- clickCount++;
51
- const message = `Clicked ${clickCount} times`;
52
- label.set_label(message);
53
- console.log(message);
54
- });
55
-
56
- box.append(label);
57
- box.append(button);
58
-
59
- toolbarView.set_content(box);
60
- window.set_content(toolbarView);
61
- window.present();
62
-
63
- console.log("Click the button to increase the counter...");
64
- });
65
-
66
- app.run([]);
@@ -1,11 +0,0 @@
1
- // Run: node start.js examples/console/await-delay/await-delay.ts
2
- import { loadGi } from '../../../gi-loader.ts';
3
-
4
- loadGi('GLib', '2.0');
5
-
6
- print('0s');
7
- await new Promise(resolve => setTimeout(resolve, 1000));
8
- print("1s");
9
- await new Promise(resolve => setTimeout(resolve, 1000));
10
- print("2s");
11
- await new Promise(resolve => setTimeout(resolve, 1000));
@@ -1,27 +0,0 @@
1
- // Run: node start.js examples/console/console-input/console-input.ts
2
- import { loadGi } from '../../../gi-loader.ts';
3
-
4
- const Gio = loadGi('Gio', '2.0');
5
-
6
- let GioUnix;
7
- try {
8
- GioUnix = loadGi('GioUnix', '2.0');
9
- } catch {
10
- GioUnix = Gio;
11
- }
12
-
13
- print("=== Greeting Program ===");
14
- print("Please enter your name: ");
15
-
16
- const stdin = new GioUnix.InputStream({ fd: 0, close_fd: false });
17
- const dataInput = new Gio.DataInputStream({ base_stream: stdin });
18
-
19
- const [name] = dataInput.read_line_utf8(null);
20
-
21
- if (name && name.trim() !== "") {
22
- print(`Hello, ${name}! Welcome to this program!`);
23
- } else {
24
- print("Hello, friend! Welcome to this program!");
25
- }
26
-
27
- print("Program ended.");
@@ -1,62 +0,0 @@
1
- // Run: node start.js examples/gtk/counter/counter.ts
2
- import { loadGi } from '../../../gi-loader.ts';
3
-
4
- const Gtk = loadGi('Gtk', '4.0');
5
-
6
- let clickCount = 0;
7
-
8
- console.log("--- GTK4 Counter ---");
9
-
10
- const app = new Gtk.Application({ application_id: 'org.gtk.counter' });
11
-
12
- app.connect('activate', () => {
13
- const window = new Gtk.ApplicationWindow({
14
- application: app,
15
- title: 'GTK Counter App',
16
- default_width: 400,
17
- default_height: 300
18
- });
19
-
20
- window.connect('close-request', () => {
21
- app.quit();
22
- return false;
23
- });
24
-
25
- const box = new Gtk.Box({
26
- orientation: Gtk.Orientation.VERTICAL,
27
- spacing: 10,
28
- halign: Gtk.Align.CENTER,
29
- valign: Gtk.Align.CENTER
30
- });
31
- box.set_margin_start(20);
32
- box.set_margin_end(20);
33
- box.set_margin_top(20);
34
- box.set_margin_bottom(20);
35
-
36
- const label = new Gtk.Label({
37
- label: 'Clicks: 0',
38
- css_classes: ['title-1']
39
- });
40
-
41
- const button = new Gtk.Button({
42
- label: 'Click to Add',
43
- css_classes: ['suggested-action']
44
- });
45
-
46
- button.connect('clicked', () => {
47
- clickCount++;
48
- const message = `Clicked ${clickCount} times`;
49
- label.set_label(message);
50
- console.log(message);
51
- });
52
-
53
- box.append(label);
54
- box.append(button);
55
-
56
- window.set_child(box);
57
- window.present();
58
-
59
- console.log("Click the button to increase the counter...");
60
- });
61
-
62
- app.run([]);
@@ -1,77 +0,0 @@
1
- // Run: node start.js examples/gtk/drag-box/drag-box.ts
2
- import { loadGi } from '../../../gi-loader.ts';
3
-
4
- const Gtk = loadGi('Gtk', '4.0');
5
-
6
- console.log("--- GTK4 Flicker-free Draggable Square (Cairo) ---");
7
-
8
- const app = new Gtk.Application({ application_id: 'org.gtk.dragbox' });
9
-
10
- app.connect('activate', () => {
11
- const window = new Gtk.ApplicationWindow({
12
- application: app,
13
- title: 'Drag Example (High Frequency IPC) ',
14
- default_width: 600,
15
- default_height: 400
16
- });
17
-
18
- const fixed = new Gtk.Fixed();
19
- fixed.set_hexpand(true);
20
- fixed.set_vexpand(true);
21
-
22
- const squareSize = 80;
23
- const drawingArea = new Gtk.DrawingArea();
24
- drawingArea.set_size_request(squareSize, squareSize);
25
-
26
- const drawFunction = (area: any, cr: any, width: number, height: number) => {
27
- cr.setSourceRGB(1.0, 0.2, 0.2);
28
- cr.rectangle(0, 0, width, height);
29
- cr.fill();
30
- };
31
-
32
- drawingArea.set_draw_func(drawFunction);
33
-
34
- let currentX = 260;
35
- let currentY = 160;
36
- fixed.put(drawingArea, currentX, currentY);
37
-
38
- const drag = new Gtk.GestureDrag();
39
-
40
- let isDragging = false;
41
- let dragStartX = 0;
42
- let dragStartY = 0;
43
-
44
- drag.connect('drag-begin', (gesture: any, startX: number, startY: number) => {
45
- if (startX >= currentX && startX <= currentX + squareSize &&
46
- startY >= currentY && startY <= currentY + squareSize) {
47
- isDragging = true;
48
- dragStartX = currentX;
49
- dragStartY = currentY;
50
- console.log(`✅ Drag started at: (${startX}, ${startY})`);
51
- }
52
- });
53
-
54
- drag.connect('drag-update', (gesture: any, offsetX: number, offsetY: number) => {
55
- if (!isDragging) return;
56
- const newX = dragStartX + offsetX;
57
- const newY = dragStartY + offsetY;
58
- fixed.move(drawingArea, newX, newY);
59
- });
60
-
61
- drag.connect('drag-end', (gesture: any, offsetX: number, offsetY: number) => {
62
- if (!isDragging) return;
63
- isDragging = false;
64
- currentX = dragStartX + offsetX;
65
- currentY = dragStartY + offsetY;
66
- console.log(`🛑 Drag ended at position: (${currentX}, ${currentY})`);
67
- });
68
-
69
- fixed.add_controller(drag);
70
-
71
- window.set_child(fixed);
72
- window.present();
73
-
74
- console.log("Window loaded. Try dragging the red square smoothly!");
75
- });
76
-
77
- app.run([]);
@@ -1,47 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <title>Counter</title>
5
- <meta charset="UTF-8">
6
- <style>
7
- body { font-family: Arial, sans-serif; padding: 20px; text-align: center; }
8
- h1 { color: #333; }
9
- #display { font-size: 24px; margin: 20px 0; }
10
- button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
11
- </style>
12
- <script>
13
- (function() {
14
- const originalConsole = window.console;
15
- window.console = {
16
- log: function(...args) {
17
- window.webkit.messageHandlers.console.postMessage(args.join(' '));
18
- },
19
- error: function(...args) {
20
- window.webkit.messageHandlers.console.postMessage('[ERROR] ' + args.join(' '));
21
- },
22
- warn: function(...args) {
23
- window.webkit.messageHandlers.console.postMessage('[WARN] ' + args.join(' '));
24
- }
25
- };
26
- })();
27
- </script>
28
- </head>
29
- <body>
30
- <h1>Counter App (WebKit)</h1>
31
- <p id="display">Clicks: 0</p>
32
- <button id="btn">Click to Add</button>
33
-
34
- <script>
35
- let clickCount = 0;
36
- const display = document.getElementById('display');
37
- const button = document.getElementById('btn');
38
-
39
- button.addEventListener('click', function() {
40
- clickCount++;
41
- const message = 'Button clicked ' + clickCount + ' times';
42
- display.textContent = message;
43
- console.log(message);
44
- });
45
- </script>
46
- </body>
47
- </html>
@@ -1,101 +0,0 @@
1
- // Run: node start.js examples/gtk-webkit/counter/counter.ts
2
- import { loadGi } from '../../../gi-loader.ts';
3
- import * as path from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
-
6
- const Gtk = loadGi('Gtk', '4.0');
7
- const WebKit = loadGi('WebKit', '6.0');
8
-
9
- const __filename = fileURLToPath(import.meta.url);
10
- const __dirname = path.dirname(__filename);
11
-
12
- console.log('--- GTK4 WebKit Counter ---');
13
-
14
- const app = new Gtk.Application({ application_id: 'org.gtk.webkitcounter' });
15
-
16
- app.connect('activate', () => {
17
- const window = new Gtk.ApplicationWindow({
18
- application: app,
19
- title: 'WebKit Counter App',
20
- default_width: 500,
21
- default_height: 400
22
- });
23
-
24
- const box = new Gtk.Box({
25
- orientation: Gtk.Orientation.VERTICAL,
26
- spacing: 0
27
- });
28
-
29
- const toolbar = new Gtk.Box({
30
- orientation: Gtk.Orientation.HORIZONTAL,
31
- spacing: 5
32
- });
33
- toolbar.set_margin_start(5);
34
- toolbar.set_margin_end(5);
35
- toolbar.set_margin_top(5);
36
- toolbar.set_margin_bottom(5);
37
-
38
- const htmlPath = path.join(__dirname, 'counter.html');
39
- const htmlUri = 'file://' + htmlPath;
40
-
41
- const backButton = new Gtk.Button({ label: '← Back' });
42
- const forwardButton = new Gtk.Button({ label: 'Forward →' });
43
- const urlEntry = new Gtk.Entry({
44
- placeholder_text: 'Enter URL or use default',
45
- text: htmlUri,
46
- hexpand: true
47
- });
48
-
49
- toolbar.append(backButton);
50
- toolbar.append(forwardButton);
51
- toolbar.append(urlEntry);
52
-
53
- const contentManager = new WebKit.UserContentManager();
54
-
55
- contentManager.connect('script-message-received', (manager, value) => {
56
- const message = value.to_string();
57
- if (message) print(`[WebView] ${message}`);
58
- });
59
- contentManager.register_script_message_handler('console', null);
60
-
61
- const webView = new WebKit.WebView({
62
- vexpand: true,
63
- hexpand: true,
64
- user_content_manager: contentManager
65
- });
66
-
67
- webView.load_uri(htmlUri);
68
-
69
- webView.connect('load-changed', (webview, loadEvent) => {
70
- if (loadEvent === WebKit.LoadEvent.FINISHED) {
71
- console.log('Page Loaded Successfully');
72
- urlEntry.set_text(webview.get_uri() || '');
73
- }
74
- });
75
-
76
- backButton.connect('clicked', () => {
77
- if (webView.can_go_back()) webView.go_back();
78
- });
79
-
80
- forwardButton.connect('clicked', () => {
81
- if (webView.can_go_forward()) webView.go_forward();
82
- });
83
-
84
- urlEntry.connect('activate', () => {
85
- let uri = urlEntry.get_text();
86
- if (!uri.startsWith('http://') && !uri.startsWith('https://') && !uri.startsWith('file://')) {
87
- uri = 'https://' + uri;
88
- }
89
- webView.load_uri(uri);
90
- });
91
-
92
- box.append(toolbar);
93
- box.append(webView);
94
-
95
- window.set_child(box);
96
- window.present();
97
-
98
- console.log("Click the button in the web view to increase the counter...");
99
- });
100
-
101
- app.run([]);
package/gi-loader.ts DELETED
@@ -1,13 +0,0 @@
1
- // gi-loader.ts
2
- // Universal GI namespace loader for Node.js, Bun, and Deno
3
- // Usage:
4
- // import { loadGi } from './gi-loader.ts';
5
- // const Gtk = loadGi('Gtk', '4.0');
6
-
7
- import { init, loadGiNamespace } from './src/index.ts';
8
-
9
- init();
10
-
11
- export function loadGi(namespace: string, version: string = '') {
12
- return loadGiNamespace(namespace, version);
13
- }
package/hook.js DELETED
@@ -1,42 +0,0 @@
1
- // hook.js
2
- export async function resolve(specifier, context, nextResolve) {
3
- if (specifier.startsWith('gi://')) {
4
- return {
5
- url: specifier,
6
- shortCircuit: true,
7
- format: 'module'
8
- };
9
- }
10
- return nextResolve(specifier, context);
11
- }
12
-
13
- export async function load(url, context, nextLoad) {
14
- if (url.startsWith('gi://')) {
15
- // Safely parse 'gi://Gtk?version=4.0'
16
- const bareUrl = url.replace('gi://', '');
17
- const [namespacePart, queryPart] = bareUrl.split('?');
18
-
19
- const namespace = namespacePart;
20
- let version = '';
21
-
22
- if (queryPart) {
23
- const params = new URLSearchParams(queryPart);
24
- version = params.get('version') || '';
25
- }
26
-
27
- const coreUrl = new URL('./src/index.ts', import.meta.url).href;
28
-
29
- const source = `
30
- import { init, loadGiNamespace } from '${coreUrl}';
31
- init();
32
- export default loadGiNamespace('${namespace}', '${version}');
33
- `;
34
-
35
- return {
36
- format: 'module',
37
- shortCircuit: true,
38
- source: source
39
- };
40
- }
41
- return nextLoad(url, context);
42
- }
package/start.js DELETED
@@ -1,105 +0,0 @@
1
- // start.js
2
- import { spawn } from 'child_process';
3
- import path from 'path';
4
- import { fileURLToPath } from 'url';
5
-
6
- const __filename = fileURLToPath(import.meta.url);
7
- const __dirname = path.dirname(__filename);
8
- const args = process.argv.slice(2);
9
-
10
- if (args.length === 0) {
11
- console.error('Usage: node start.js <script.ts> [--runtime=node|bun|deno]');
12
- console.error(' bun start.js <script.ts> [--runtime=node|bun|deno]');
13
- console.error(' deno run start.js <script.ts> [--runtime=node|bun|deno]');
14
- process.exit(1);
15
- }
16
-
17
- let runtime = null;
18
- let targetScript = null;
19
-
20
- for (let i = 0; i < args.length; i++) {
21
- if (args[i] === '--runtime' && args[i + 1]) {
22
- runtime = args[i + 1].toLowerCase();
23
- i++;
24
- } else if (!args[i].startsWith('--')) {
25
- targetScript = args[i];
26
- }
27
- }
28
-
29
- if (!targetScript) {
30
- console.error('Error: No script specified');
31
- process.exit(1);
32
- }
33
-
34
- targetScript = path.resolve(targetScript);
35
- const hookUrl = new URL('./hook.js', import.meta.url).href;
36
-
37
- if (!runtime) {
38
- runtime = detectRuntime();
39
- }
40
-
41
- const validRuntimes = ['node', 'bun', 'deno'];
42
- if (!validRuntimes.includes(runtime)) {
43
- console.error(`Error: Invalid runtime "${runtime}". Must be one of: ${validRuntimes.join(', ')}`);
44
- process.exit(1);
45
- }
46
-
47
- console.log(`Starting ${runtime.charAt(0).toUpperCase() + runtime.slice(1)}-GJS execution context...`);
48
-
49
- let proc;
50
-
51
- switch (runtime) {
52
- case 'bun':
53
- proc = spawnBun(targetScript);
54
- break;
55
- case 'deno':
56
- proc = spawnDeno(targetScript);
57
- break;
58
- case 'node':
59
- default:
60
- proc = spawnNode(targetScript, hookUrl);
61
- break;
62
- }
63
-
64
- proc.on('exit', (code) => {
65
- process.exit(code || 0);
66
- });
67
-
68
- function detectRuntime() {
69
- if (typeof Bun !== 'undefined') return 'bun';
70
- if (typeof Deno !== 'undefined') return 'deno';
71
- return 'node';
72
- }
73
-
74
- function spawnNode(targetScript, hookUrl) {
75
- return spawn(process.execPath, [
76
- '--no-warnings',
77
- '--experimental-loader', hookUrl,
78
- '--experimental-transform-types',
79
- targetScript
80
- ], {
81
- stdio: 'inherit',
82
- env: process.env
83
- });
84
- }
85
-
86
- function spawnBun(targetScript) {
87
- return spawn('bun', [
88
- 'run',
89
- targetScript
90
- ], {
91
- stdio: 'inherit',
92
- env: process.env
93
- });
94
- }
95
-
96
- function spawnDeno(targetScript) {
97
- return spawn('deno', [
98
- 'run',
99
- '--allow-all',
100
- targetScript
101
- ], {
102
- stdio: 'inherit',
103
- env: process.env
104
- });
105
- }