positron.js 1.0.0

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/ipc.js ADDED
@@ -0,0 +1,81 @@
1
+ const { warn } = require("./logs");
2
+
3
+ module.exports = class IpcRouter {
4
+ #handlers = new Map();
5
+
6
+ /**
7
+ * Registers a handler for a specific IPC channel. Returns a function to unregister the handler.
8
+ * @param {string} channel
9
+ * @param {function} fn
10
+ * @returns {function} A function to unregister the handler.
11
+ */
12
+ handle(channel, fn) {
13
+ if (!this.#handlers.has(channel)) {
14
+ this.#handlers.set(channel, new Set());
15
+ }
16
+
17
+ this.#handlers.get(channel).add(fn);
18
+
19
+ return () => {
20
+ this.#handlers.get(channel)?.delete(fn);
21
+
22
+ if (this.#handlers.get(channel)?.size === 0) {
23
+ this.#handlers.delete(channel);
24
+ }
25
+ };
26
+ }
27
+
28
+ /**
29
+ * @internal
30
+ */
31
+ dispatch(ws, msg) {
32
+ if (msg.event !== "ipcMessage") return false;
33
+
34
+ const { channel, payload } = msg.data;
35
+
36
+ const parsed =
37
+ typeof payload === "string"
38
+ ? JSON.parse(payload)
39
+ : payload;
40
+
41
+ const handlers = this.#handlers.get(channel);
42
+
43
+ if (!handlers || handlers.size === 0) {
44
+ warn(`[ipc] No handlers for channel "${channel}"`);
45
+ return false;
46
+ }
47
+
48
+ const reply = (data) =>
49
+ this.send(ws, msg.windowId, channel + "-reply", data);
50
+
51
+ const emit = (ch, data) =>
52
+ this.send(ws, msg.windowId, ch, data);
53
+
54
+ for (const handler of handlers) {
55
+ try {
56
+ handler(parsed, {
57
+ reply,
58
+ emit,
59
+ windowId: msg.windowId,
60
+ });
61
+ } catch (err) {
62
+ warn(`[ipc] Handler error on "${channel}": ${err.stack || err}`);
63
+ }
64
+ }
65
+
66
+ return true;
67
+ }
68
+
69
+ /** @internal */
70
+ send(ws, windowId, channel, data = null) {
71
+ if (ws && ws.readyState === WebSocket.OPEN) {
72
+ ws.send(JSON.stringify({
73
+ windowId,
74
+ command: "emitToRenderer",
75
+ args: [channel, JSON.stringify(data)],
76
+ }));
77
+ } else {
78
+ warn(`[ipc] Cannot emit to renderer: socket connection is not open.`);
79
+ }
80
+ }
81
+ }
package/logs.js ADDED
@@ -0,0 +1,19 @@
1
+ module.exports.success = (...msgs) => {
2
+ console.log(`\x1b[32m[SUCCESS] ${msgs.join(' ').trim()}\x1b[0m`.trim());
3
+ }
4
+
5
+ module.exports.error = (...msgs) => {
6
+ console.error(`\x1b[31m[ERROR] ${msgs.join(' ').trim()}\x1b[0m`.trim());
7
+ }
8
+
9
+ module.exports.info = (...msgs) => {
10
+ console.info(`\x1b[34m[INFO] ${msgs.join(' ').trim()}\x1b[0m`.trim());
11
+ }
12
+
13
+ module.exports.warn = (...msgs) => {
14
+ console.warn(`\x1b[33m[WARN] ${msgs.join(' ').trim()}\x1b[0m`.trim());
15
+ }
16
+
17
+ module.exports.special = (...msgs) => {
18
+ console.log(`\x1b[35m${msgs.join(' ').trim()}\x1b[0m`.trim());
19
+ }
package/menu.js ADDED
@@ -0,0 +1,100 @@
1
+ class MenuItem {
2
+
3
+ /**
4
+ * Creates a new MenuItem instance with the specified properties. The label is the text displayed for the menu item, the channel is the IPC channel to send when the item is clicked, and the payload is the data to send along with the click event. The key is an optional identifier for the menu item, and items can be used to create submenus. The separator property indicates whether this item is a separator, and click is a callback function that will be called when the item is clicked. The enabled property determines whether the menu item is enabled or disabled.
5
+ * @param {Object} options - The options for creating the MenuItem.
6
+ * @param {string} options.label - The text displayed for the menu item.
7
+ * @param {string} options.channel - The IPC channel to send when the item is clicked.
8
+ * @param {Object} options.payload - The data to send along with the click event.
9
+ * @param {string} [options.key] - An optional identifier for the menu item.
10
+ * @param {MenuItem[]} [options.items] - An array of MenuItem instances to create a submenu.
11
+ * @param {boolean} [options.separator=false] - Whether this item is a separator.
12
+ * @param {function} [options.click=()=>{}] - A callback function that will be called when the item is clicked.
13
+ * @param {boolean} [options.enabled=true] - Whether the menu item is enabled or disabled.
14
+ */
15
+
16
+ constructor({ label="", channel="", payload={}, key="", items=[], separator=false, click=()=>{}, enabled=true }) {
17
+ this.label = label;
18
+ this.channel = channel;
19
+ this.payload = JSON.stringify(payload);
20
+ this.key = key;
21
+ this.items = items ? items.map(i => new MenuItem(i)) : null;
22
+ this.enabled = enabled;
23
+ this.click = click;
24
+ this.separator = separator || false;
25
+ }
26
+
27
+ /**
28
+ * Converts the MenuItem instance into a JSON object that can be used to create a menu
29
+ * @internal
30
+ */
31
+ json() {
32
+ return {
33
+ label: this.label,
34
+ channel: this.channel,
35
+ payload: this.payload,
36
+ key: this.key,
37
+ items: this.items ? this.items.map(i => i.json()) : null,
38
+ separator: this.separator,
39
+ enabled: this.enabled,
40
+ click: this.click
41
+ }
42
+ }
43
+ }
44
+
45
+ class Separator extends MenuItem {
46
+ /**
47
+ * Creates a new Separator instance, which is a type of MenuItem that represents a separator in a menu.
48
+ */
49
+ constructor() {
50
+ super({ separator: true });
51
+ }
52
+ }
53
+
54
+
55
+ class Menu {
56
+
57
+ #template = []
58
+
59
+ /**
60
+ * Creates a new Menu instance with an optional template. The template is an array of MenuItem instances that define the structure of the menu. If no template is provided, an empty menu will be created.
61
+ * @param {MenuItem[]} template - An array of MenuItem instances that define the structure of the menu.
62
+ */
63
+ constructor(template = []) {
64
+ this.#template = template;
65
+ }
66
+
67
+ /**
68
+ * Gets the current template of the menu, which is an array of MenuItem instances that define the structure of the menu.
69
+ */
70
+ get template() {
71
+ return this.#template;
72
+ }
73
+
74
+ /**
75
+ * Adds an item to the menu.
76
+ * @param {MenuItem} item - The MenuItem instance to add.
77
+ * @returns {Menu} The menu instance for chaining.
78
+ */
79
+ addItem(item) {
80
+ this.#template.push(item.json());
81
+ return this;
82
+ }
83
+
84
+ /**
85
+ * Adds multiple items to the menu.
86
+ * @param {MenuItem[]} items - An array of MenuItem instances to add.
87
+ * @returns {Menu} The menu instance for chaining.
88
+ */
89
+ addItems(items) {
90
+ items.forEach(item => this.addItem(item));
91
+ return this;
92
+ }
93
+
94
+ }
95
+
96
+ module.exports = {
97
+ Menu,
98
+ MenuItem,
99
+ Separator
100
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "positron.js",
3
+ "description": "A lightweight cross-platform hybrid application framework designed to build desktop applications using a native compiled runtime driven by a Node.js main process.",
4
+ "repository": {
5
+ "url": "https://github.com/systemsoftware/positron-app"
6
+ },
7
+ "homepage": "https://positronjs.gitbook.io",
8
+ "version": "1.0.0",
9
+ "main": "index.js",
10
+ "scripts": {
11
+ "test": "node --test"
12
+ },
13
+ "keywords": [
14
+ "app",
15
+ "framework",
16
+ "desktop"
17
+ ],
18
+ "author": "Bryce",
19
+ "license": "MIT",
20
+ "type": "commonjs",
21
+ "dependencies": {
22
+ "@yao-pkg/pkg": "^6.20.0",
23
+ "esbuild": "^0.28.0",
24
+ "resedit": "^3.0.2",
25
+ "ws": "^8.20.1"
26
+ },
27
+ "bin": {
28
+ "positron": "./bin/positron.js"
29
+ }
30
+ }
package/packager.js ADDED
@@ -0,0 +1,260 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const { execSync } = require("child_process");
4
+ const { info, error, success } = require("./logs");
5
+ const https = require("https");
6
+ const esbuild = require("esbuild");
7
+ const findPackageJson = require("./findpackage");
8
+
9
+ const MAJOR_NODE_V = 24;
10
+
11
+ const ob = process.argv.includes("--obfuscate");
12
+
13
+ const arch = process.argv.includes("--x64") ? "x64" : process.argv.includes("--arm64") ? "arm64" : process.arch;
14
+
15
+ function performPackager() {
16
+ const appRoot = process.cwd();
17
+ const rootPackage = findPackageJson(appRoot)?.packageJson;
18
+
19
+ const appName = process.env.POSITRON_APP_NAME || rootPackage.productName || rootPackage.name || "PositronApp";
20
+ const distDir = path.join(appRoot, "dist");
21
+
22
+ if (fs.existsSync(distDir)) fs.rmSync(distDir, { recursive: true, force: true });
23
+ fs.mkdirSync(distDir, { recursive: true });
24
+
25
+ if (process.argv.includes("--mac") || process.argv.includes("--m")) {
26
+ packageMacOS(appRoot, distDir, appName);
27
+ } else if (process.argv.includes("--windows") || process.argv.includes("--w")) {
28
+ packageWindows(appRoot, distDir, appName);
29
+ } else {
30
+ process.platform === "win32" ? packageWindows(appRoot, distDir, appName) : packageMacOS(appRoot, distDir, appName);
31
+ }
32
+ }
33
+
34
+ function handleJavaScriptPipeline(appRoot, resourcesPath) {
35
+ const targetOutputFile = path.join(resourcesPath, "index.js");
36
+
37
+ info(`[Packager] Bundling JavaScript code with esbuild...`);
38
+ try {
39
+ esbuild.buildSync({
40
+ entryPoints: [path.join(appRoot, "index.js")],
41
+ bundle: true,
42
+ platform: "node",
43
+ target: `node${MAJOR_NODE_V}`,
44
+ outfile: targetOutputFile,
45
+ minify: ob,
46
+ sourcemap: false,
47
+ });
48
+ } catch (err) {
49
+ error("Fatal: esbuild bundling failed.");
50
+ process.exit(1);
51
+ }
52
+
53
+ copyAppAssets(appRoot, resourcesPath);
54
+
55
+ }
56
+
57
+ async function packageMacOS(appRoot, distDir, appName) {
58
+ const appBundlePath = path.join(distDir, `${appName}.app`);
59
+ const contentsPath = path.join(appBundlePath, "Contents");
60
+ const macosPath = path.join(contentsPath, "MacOS");
61
+ const resourcesPath = path.join(contentsPath, "Resources");
62
+
63
+ fs.mkdirSync(macosPath, { recursive: true });
64
+ fs.mkdirSync(resourcesPath, { recursive: true });
65
+
66
+ info(`[Packager] Creating macOS App Bundle structure...`);
67
+
68
+ const compiledBinary = path.join(appRoot, "bin", "positron-runtime");
69
+ if (!fs.existsSync(compiledBinary)) {
70
+ error("Fatal: Native compiled binary missing from bin/. Run build first.");
71
+ process.exit(1);
72
+ }
73
+ fs.copyFileSync(compiledBinary, path.join(macosPath, appName));
74
+ fs.chmodSync(path.join(macosPath, appName), "755");
75
+
76
+ const packageJsonPath = path.join(appRoot, "package.json");
77
+ const package = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
78
+
79
+ const plistContent = `<?xml version="1.0" encoding="UTF-8"?>
80
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
81
+ <plist version="1.0">
82
+ <dict>
83
+ <key>CFBundleExecutable</key>
84
+ <string>${appName}</string>
85
+ <key>CFBundleIdentifier</key>
86
+ <string>com.${package.author || "positron"}.${appName.toLowerCase()}</string>
87
+ <key>CFBundleName</key>
88
+ <string>${appName}</string>
89
+ <key>CFBundlePackageType</key>
90
+ <string>APPL</string>
91
+ <key>CFBundleShortVersionString</key>
92
+ <string>${package.version || "1.0.0"}</string>
93
+ <key>CFBundleIconFile</key>
94
+ <string>icon</string>
95
+ <key>NSHighResolutionCapable</key>
96
+ <true/>
97
+ <key>NSHumanReadableCopyright</key>
98
+ <string>${package.author || "Positron"}</string>
99
+ <key>LSApplicationCategoryType</key>
100
+ <string>${package.macCategory || "public.app-category.developer-tools"}</string>
101
+ </dict>
102
+ </plist>`;
103
+ fs.writeFileSync(path.join(contentsPath, "Info.plist"), plistContent);
104
+
105
+ handleJavaScriptPipeline(appRoot, resourcesPath);
106
+
107
+ const bundledJs = path.join(resourcesPath, "index.js");
108
+ if (fs.existsSync(bundledJs)) {
109
+ await compileWithPkg(bundledJs, "darwin", resourcesPath, "positron-backend");
110
+
111
+ // fs.renameSync(targetNodePath, path.join(resourcesPath, "positron-backend"));
112
+ }
113
+
114
+ fs.rmSync(path.join(resourcesPath, "icon.ico"), { force: true });
115
+
116
+
117
+ success(`Successfully packaged macOS app at: ${appBundlePath}`);
118
+ }
119
+
120
+ async function packageWindows(appRoot, distDir, appName) {
121
+ const outputFolder = path.join(distDir, appName);
122
+ fs.mkdirSync(outputFolder, { recursive: true });
123
+
124
+ info(`[Packager] Creating Windows App structure...`);
125
+
126
+ const binFolder = path.join(appRoot, "bin");
127
+
128
+ function copyDirRecursive(src, dest) {
129
+ fs.readdirSync(src, { withFileTypes: true }).forEach(entry => {
130
+ const srcPath = path.join(src, entry.name);
131
+ const destPath = path.join(dest, entry.name);
132
+ if (entry.isDirectory()) {
133
+ fs.mkdirSync(destPath, { recursive: true });
134
+ copyDirRecursive(srcPath, destPath);
135
+ } else {
136
+ fs.copyFileSync(srcPath, destPath);
137
+ }
138
+ });
139
+ }
140
+
141
+ // Ensure binary source folder exists
142
+ if (!fs.existsSync(binFolder)) {
143
+ error("Fatal: bin/ directory missing. Run build first.");
144
+ process.exit(1);
145
+ }
146
+
147
+ copyDirRecursive(binFolder, outputFolder);
148
+
149
+ const resourcesPath = path.join(outputFolder, "resources");
150
+ fs.mkdirSync(resourcesPath, { recursive: true });
151
+
152
+ handleJavaScriptPipeline(appRoot, resourcesPath);
153
+
154
+ const oldBinaryPath = path.join(outputFolder, "positron-runtime.exe");
155
+ const newBinaryPath = path.join(outputFolder, `${appName}.exe`);
156
+
157
+ if (fs.existsSync(oldBinaryPath)) {
158
+ fs.renameSync(oldBinaryPath, newBinaryPath);
159
+ } else if (!fs.existsSync(newBinaryPath)) {
160
+ error(`Fatal: Could not find base Windows executable template at ${oldBinaryPath}`);
161
+ process.exit(1);
162
+ }
163
+
164
+ const bundledJs = path.join(resourcesPath, "index.js");
165
+
166
+ if (fs.existsSync(bundledJs)) {
167
+ await compileWithPkg(bundledJs, "win32", resourcesPath, "positron-backend");
168
+ } else {
169
+ error(`[Packager] Fatal: Bundled JavaScript entry point missing at ${bundledJs}`);
170
+ process.exit(1);
171
+ }
172
+
173
+ fs.rmSync(path.join(resourcesPath, "icon.icns"), { force: true });
174
+ fs.rmSync(path.join(resourcesPath, "icon.ico"), { force: true });
175
+
176
+ const macBinaryPath = path.join(outputFolder, "positron-runtime");
177
+ if (fs.existsSync(macBinaryPath)) {
178
+ fs.rmSync(macBinaryPath);
179
+ }
180
+
181
+ // Clean out empty extraction shell folders if they exist
182
+ fs.readdirSync(resourcesPath).forEach(file => {
183
+ const fullPath = path.join(resourcesPath, file);
184
+ if (fs.statSync(fullPath).isDirectory() && file.startsWith("node-v") && file !== "node") {
185
+ fs.rmSync(fullPath, { recursive: true, force: true });
186
+ }
187
+ });
188
+
189
+ success(`Successfully packaged Windows app directory at: ${outputFolder}`);
190
+ }
191
+
192
+ function copyAppAssets(src, dest) {
193
+ const ignoreList = ["node_modules", "dist", "bin", ".git"];
194
+
195
+ function copyRecursive(currentSrc, currentDest) {
196
+ const items = fs.readdirSync(currentSrc);
197
+ for (const item of items) {
198
+ if (ignoreList.includes(item)) continue;
199
+
200
+ const srcPath = path.join(currentSrc, item);
201
+ const destPath = path.join(currentDest, item);
202
+ const stat = fs.statSync(srcPath);
203
+
204
+ if (stat.isFile() && (item === "package-lock.json" || item.endsWith(".log"))) {
205
+ continue;
206
+ }
207
+
208
+ if (stat.isDirectory()) {
209
+ fs.mkdirSync(destPath, { recursive: true });
210
+ copyRecursive(srcPath, destPath);
211
+ } else {
212
+ if (item.endsWith(".js")) continue;
213
+ fs.copyFileSync(srcPath, destPath);
214
+ }
215
+ }
216
+ }
217
+ copyRecursive(src, dest);
218
+ }
219
+
220
+
221
+ const { exec } = require("@yao-pkg/pkg");
222
+
223
+ async function compileWithPkg(bundledJsPath, targetPlatform, outputFolder, appName) {
224
+ info(`[Packager] Packaging application into a standalone binary...`);
225
+
226
+ let pkgTarget = "";
227
+ let finalBinaryName = appName;
228
+
229
+ info(`[Packager] Detected target platform: ${targetPlatform}, architecture: ${arch}`);
230
+
231
+ if (targetPlatform === "win32") {
232
+ pkgTarget = `node${MAJOR_NODE_V}-win-${arch}`;
233
+ finalBinaryName = `${appName}.exe`;
234
+ } else if (targetPlatform === "darwin") {
235
+ pkgTarget = `node${MAJOR_NODE_V}-macos-${arch}`;
236
+ }
237
+
238
+ const outputPath = path.join(outputFolder, finalBinaryName);
239
+
240
+ const pkgArgs = [
241
+ bundledJsPath,
242
+ "--target", pkgTarget,
243
+ "--output", outputPath
244
+ ];
245
+
246
+ try {
247
+ await exec(pkgArgs);
248
+
249
+ if (fs.existsSync(bundledJsPath)) {
250
+ fs.rmSync(bundledJsPath);
251
+ }
252
+
253
+ success(`[Packager] pkg compilation complete: ${outputPath}`);
254
+ } catch (err) {
255
+ error(`[Packager] pkg compilation failed: ${err.message}`);
256
+ process.exit(1);
257
+ }
258
+ }
259
+
260
+ module.exports = performPackager;
Binary file
Binary file
package/screen.js ADDED
@@ -0,0 +1,35 @@
1
+ const { execSync } = require('child_process');
2
+
3
+ /**
4
+ * Gets the screen size of the primary display. The implementation varies based on the operating system:
5
+ * - On Windows, it uses PowerShell to query the current horizontal and vertical resolution of the video controller.
6
+ * - On macOS, it uses the system_profiler command to extract the resolution information from the display data.
7
+ * If the platform is not supported or if there is an error during execution, it returns a default size of { width: 0, height: 0 }.
8
+ */
9
+ function getScreenSize() {
10
+ const platform = process.platform;
11
+
12
+ try {
13
+ if (platform === 'win32') {
14
+ const cmd = "powershell -command \"Get-CimInstance Win32_VideoController | Select-Object CurrentHorizontalResolution, CurrentVerticalResolution | Format-List\"";
15
+ const output = execSync(cmd).toString();
16
+ const width = output.match(/CurrentHorizontalResolution\s*:\s*(\d+)/)?.[1];
17
+ const height = output.match(/CurrentVerticalResolution\s*:\s*(\d+)/)?.[1];
18
+ return { width: parseInt(width), height: parseInt(height) };
19
+ }
20
+
21
+ if (platform === 'darwin') {
22
+ const cmd = "system_profiler SPDisplaysDataType | grep Resolution";
23
+ const output = execSync(cmd).toString();
24
+ const match = output.match(/(\d+) x (\d+)/);
25
+ return { width: parseInt(match[1]), height: parseInt(match[2]) };
26
+ }
27
+
28
+ } catch (error) {
29
+ console.error("Failed to fetch screen size:", error.message);
30
+ }
31
+
32
+ return { width: 0, height: 0 };
33
+ }
34
+
35
+ module.exports = { getScreenSize };
package/store.js ADDED
@@ -0,0 +1,104 @@
1
+ const { app } = require("./index")
2
+ const fs = require("fs")
3
+ const path = require("path")
4
+
5
+ module.exports = class DataStore {
6
+
7
+ id = ""
8
+
9
+ /**
10
+ * Creates a new store instance with the given id. The store will be in the user's data directory under the "stores" folder.
11
+ * @param {string} id
12
+ */
13
+ constructor(id) {
14
+
15
+ if(!id) throw new Error("Store id is required")
16
+ if (id.includes("/") || id.includes("\\") || id === ".." || id === ".") {
17
+ throw new Error("Invalid store id: cannot contain path traversals")
18
+ }
19
+
20
+ this.id = id
21
+
22
+ this.path = path.join(app.userData.getPath(), "stores", `${id}.json`)
23
+
24
+ if(!fs.existsSync(path.join(app.userData.getPath(), "stores"))) {
25
+ fs.mkdirSync(path.join(app.userData.getPath(), "stores"), { recursive: true })
26
+ }
27
+
28
+ if (!fs.existsSync(this.path)) {
29
+ fs.writeFileSync(this.path, JSON.stringify({}))
30
+ }
31
+
32
+ }
33
+
34
+ /**
35
+ * Gets the value of the given key from the store. If the key does not exist, it will return undefined.
36
+ * @param {string} key
37
+ * @returns {any} Value of the key
38
+ */
39
+ get(key) {
40
+ const data = JSON.parse(fs.readFileSync(this.path))
41
+ return data[key]
42
+ }
43
+
44
+ /**
45
+ * Sets the value of the given key in the store.
46
+ * @param {string} key
47
+ * @param {any} value
48
+ */
49
+ set(key, value) {
50
+ const data = JSON.parse(fs.readFileSync(this.path))
51
+ data[key] = value
52
+ fs.writeFileSync(this.path, JSON.stringify(data))
53
+ }
54
+
55
+ /**
56
+ * Deletes the given key from the store.
57
+ * @param {string} key The key to delete
58
+ */
59
+ delete(key) {
60
+ const data = JSON.parse(fs.readFileSync(this.path))
61
+ delete data[key]
62
+ fs.writeFileSync(this.path, JSON.stringify(data))
63
+ }
64
+
65
+ /**
66
+ * Clears all keys from the store.
67
+ */
68
+ clear() {
69
+ fs.writeFileSync(this.path, JSON.stringify({}))
70
+ }
71
+
72
+ /**
73
+ * Deletes the store file from the user's data directory.
74
+ */
75
+ rm() {
76
+ fs.rmSync(this.path)
77
+ }
78
+
79
+ /**
80
+ * Creates the store file if it does not exist. If the file already exists, it does nothing.
81
+ */
82
+ create() {
83
+ if (!fs.existsSync(this.path)) {
84
+ fs.writeFileSync(this.path, JSON.stringify({}))
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Checks if the store file exists in the user's data directory.
90
+ * @returns {boolean} True if the store file exists, false otherwise.
91
+ */
92
+ exists() {
93
+ return fs.existsSync(this.path)
94
+ }
95
+
96
+ /**
97
+ * Gets all keys and values from the store as an object.
98
+ * @returns {object} An object containing all keys and values from the store.
99
+ */
100
+ all() {
101
+ return JSON.parse(fs.readFileSync(this.path))
102
+ }
103
+
104
+ }