wip: migrate db ops to use ipc (nodeintegration is disabled in latest app)

This commit is contained in:
dragonwocky 2023-05-07 22:37:56 +10:00
parent 8e809d4233
commit bf07257ae8
Signed by: dragonwocky
GPG Key ID: 7998D08F7D7BD7A8
16 changed files with 297 additions and 363 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
Copyright (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -2,7 +2,7 @@
/**
* notion-enhancer
* (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/

View File

@ -35,8 +35,8 @@
"notion-enhancer"
],
"dependencies": {
"@electron/asar": "^3.2.2",
"@electron/asar": "^3.2.4",
"arg": "^5.0.2",
"chalk-template": "^0.4.0"
"chalk-template": "^1.0.0"
}
}

View File

@ -1,6 +1,6 @@
/**
* notion-enhancer
* (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/

View File

@ -1,6 +1,6 @@
/**
* notion-enhancer
* (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
@ -40,7 +40,7 @@ const patches = {
schemePrefix.length,
-(search.length + hash.length) || undefined
)}\`;
callback({
return callback({
data: require("fs").createReadStream(require("path").resolve(\`\${__dirname}/\${filePath}\`)),
headers: { "content-type": require("notion-enhancer/vendor/content-types.min.js").get(fileExt) },
});
@ -52,8 +52,8 @@ const patches = {
"main/systemMenu": async (scriptContent) => {
// exposes template for modification
const searchValue = "electron_1.Menu.setApplicationMenu(menu);",
replaceValue = `${searchValue} return template;`;
const searchValue = "}\nexports.setupSystemMenu = setupSystemMenu;",
replaceValue = ` return template;\n${searchValue}`;
if (scriptContent.includes(replaceValue)) return scriptContent;
return scriptContent.replace(searchValue, replaceValue);
},

View File

@ -1,6 +1,6 @@
/**
* notion-enhancer
* (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/

View File

@ -1,88 +0,0 @@
/**
* notion-enhancer
* (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
"use strict";
const platform = navigator.userAgent.includes("Firefox")
? "firefox"
: "chromium",
version = chrome.runtime.getManifest().version,
enhancerUrl = (target) => chrome.runtime.getURL(target);
const readFile = async (file) => {
file = file.startsWith("http") ? file : enhancerUrl(file);
const res = await fetch(file);
return await res.text();
},
readJson = async (file) => {
file = file.startsWith("http") ? file : enhancerUrl(file);
const res = await fetch(file);
return await res.json();
},
reloadApp = () => {
chrome.runtime.sendMessage({
channel: "notion-enhancer",
message: "reload-app",
});
};
const sendMessage = (channel, message) => {
chrome.runtime.sendMessage({ channel, message });
},
onMessage = (channel, listener) => {
chrome.runtime.onMessage.addListener((msg) => {
if (msg?.channel === channel) listener(msg.message);
});
};
const initDatabase = (namespace, fallbacks = {}) => {
if (Array.isArray(namespace)) namespace = namespace.join("__");
namespace = namespace ? namespace + "__" : "";
const namespaceify = (key) =>
key.startsWith(namespace) ? key : namespace + key;
return {
get: async (key) => {
const fallback = fallbacks[key];
key = namespaceify(key);
return (await chrome.storage.local.get([key]))[key] ?? fallback;
},
set: (key, value) => {
key = namespaceify(key);
return chrome.storage.local.set({ [key]: value });
},
remove: (keys) => {
keys = Array.isArray(keys) ? keys : [keys];
keys = keys.map(namespaceify);
return chrome.storage.local.remove(keys);
},
export: async () => {
const obj = await chrome.storage.local.get();
if (!namespace) return obj;
const entries = Object.entries(obj)
.filter(([key]) => key.startsWith(namespace))
.map(([key, value]) => [key.slice(namespace.length), value]);
return Object.fromEntries(entries);
},
import: (obj) => {
const entries = Object.entries(obj) //
.map(([key, value]) => [namespace + key, value]);
return chrome.storage.local.set(Object.fromEntries(entries));
},
};
};
globalThis.__enhancerApi ??= {};
Object.assign(globalThis.__enhancerApi, {
platform,
version,
enhancerUrl,
readFile,
readJson,
reloadApp,
sendMessage,
onMessage,
initDatabase,
});

View File

@ -1,160 +0,0 @@
/**
* notion-enhancer
* (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
"use strict";
const fs = require("fs"),
path = require("path"),
notionRequire = (target) => require(`../../../${target}`);
const platform = process.platform,
version = require("notion-enhancer/package.json").version,
enhancerUrl = (target) =>
`notion://www.notion.so/__notion-enhancer/${target.replace(/^\//, "")}`;
const readFile = (file) => {
// prettier-ignore
file = file.replace(/^https:\/\/www\.notion\.so\//, "notion://www.notion.so/");
const useFetch = file.startsWith("http") || file.startsWith("notion://");
if (useFetch) return fetch(file).then((res) => res.text());
return fs.readFileSync(path.resolve(`${__dirname}/../${file}`), "utf-8");
},
readJson = (file) => {
// prettier-ignore
file = file.replace(/^https:\/\/www\.notion\.so\//, "notion://www.notion.so/");
const useFetch = file.startsWith("http") || file.startsWith("notion://");
if (useFetch) return fetch(file).then((res) => res.json());
return require(path.resolve(`${__dirname}/../${file}`));
},
reloadApp = () => {
const { app, ipcRenderer } = require("electron");
if (app) {
const args = process.argv.slice(1).filter((arg) => arg !== "--startup");
app.relaunch({ args });
app.exit();
} else ipcRenderer.send("notion-enhancer", "reload-app");
};
const sendMessage = (channel, message) => {
const { ipcRenderer } = require("electron");
ipcRenderer.send(channel, message);
},
onMessage = (channel, listener) => {
const { ipcRenderer } = require("electron");
ipcRenderer.on(channel, listener);
};
let __db, __statements, __transactions;
const initDatabase = (namespace, fallbacks = {}) => {
if (Array.isArray(namespace)) namespace = namespace.join("__");
namespace = namespace ? namespace + "__" : "";
const namespaceify = (key) =>
key.startsWith(namespace) ? key : namespace + key;
__db ??= (async () => {
const { app, ipcRenderer } = require("electron"),
isRenderer = process?.type === "renderer",
userData = isRenderer
? await ipcRenderer.invoke("notion-enhancer", "get-user-data-folder")
: app.getPath("userData");
const table = "settings",
sqlite = require("better-sqlite3"),
db = sqlite(path.resolve(`${userData}/notion-enhancer.db`)),
init = db.prepare(`CREATE TABLE IF NOT EXISTS ${table} (
key TEXT PRIMARY KEY,
value TEXT
)`);
init.run();
// schema:
// - ("agreedToTerms") -> string: semver
// - ("lastTelemetryPing") -> string: iso
// - ("telemetryEnabled") -> boolean
// - ("profileIds") -> $profileId[]
// - ("activeProfile") -> $profileId
// - $profileId: ("profileName") -> string
// - $profileId__enabledMods: ($modId) -> boolean
// - $profileId__$modId: ($optionKey) -> value
__statements = {
insert: db.prepare(`INSERT INTO ${table} (key, value) VALUES (?, ?)`),
update: db.prepare(`UPDATE ${table} SET value = ? WHERE key = ?`),
select: db.prepare(`SELECT * FROM ${table} WHERE key = ? LIMIT 1`),
delete: db.prepare(`DELETE FROM ${table} WHERE key = ?`),
dump: db.prepare(`SELECT * FROM ${table}`),
};
__transactions = {
remove: db.transaction((arr) => {
arr.forEach((key) => __statements.delete.run(key));
}),
set: db.transaction((obj) => {
for (const key in obj) {
if (__statements.select.get(key) === undefined) {
__statements.insert.run(key, obj[key]);
} else __statements.update.run(obj[key], key);
}
}),
};
return db;
})();
return {
get: async (key) => {
await __db;
const fallback = fallbacks[key];
key = namespaceify(key);
try {
const value = JSON.parse(__statements.select.get(key)?.value);
return value ?? fallback;
} catch {}
return fallback;
},
set: async (key, value) => {
await __db;
key = namespaceify(key);
value = JSON.stringify(value);
__transactions.set({ [key]: value });
return true;
},
remove: async (keys) => {
await __db;
keys = Array.isArray(keys) ? keys : [keys];
keys = keys.map(namespaceify);
__transactions.remove(keys);
return true;
},
export: async () => {
await __db;
const entries = __statements.dump
.all()
.filter(({ key }) => key.startsWith(namespace))
.map(({ key, value }) => [key.slice(namespace.length), value]);
return Object.fromEntries(entries);
},
import: async (obj) => {
await __db;
const entries = Object.entries(obj) //
.map(([key, value]) => [key.slice(namespace.length), value]);
__transactions.set(Object.fromEntries(entries));
return true;
},
};
};
globalThis.__enhancerApi ??= {};
Object.assign(globalThis.__enhancerApi, {
notionRequire,
platform,
version,
enhancerUrl,
readFile,
readJson,
reloadApp,
sendMessage,
onMessage,
initDatabase,
});

View File

@ -49,7 +49,7 @@ attachObserver();
let keyListeners = [];
// accelerators approximately match electron accelerators.
// logic used when recording hotkeys in menu matches logic used
// when triggering hotkeys detection should be reliable.
// when triggering hotkeys => detection should be reliable.
// default hotkeys using "alt" may trigger an altcode or
// accented character on some keyboard layouts (not recommended).
const modifierAliases = [

View File

@ -1,6 +1,6 @@
/**
* notion-enhancer
* (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/

150
src/api/system.js Normal file
View File

@ -0,0 +1,150 @@
/**
* notion-enhancer
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
"use strict";
const IS_ELECTRON = typeof module !== "undefined";
// expected values: 'linux', 'win32', 'darwin' (== macos), 'firefox'
// and 'chromium' (inc. chromium-based browsers like edge and brave)
// other possible values: 'aix', 'freebsd', 'openbsd', 'sunos'
const platform = IS_ELECTRON
? process.platform
: navigator.userAgent.includes("Firefox")
? "firefox"
: "chromium",
// currently installed version of the notion-enhancer
version = IS_ELECTRON
? require("notion-enhancer/package.json").version
: chrome.runtime.getManifest().version,
// forms a url to a notion-enhancer asset or source file
// that can be accessed reliably over http
enhancerUrl = (target) =>
IS_ELECTRON
? `notion://www.notion.so/__notion-enhancer/${target.replace(/^\//, "")}`
: chrome.runtime.getURL(target),
// should only be used from an electron main process, does nothing elsewhere
notionRequire = (target) => IS_ELECTRON && require(`../../../${target}`);
let __port;
const onMessage = (channel, listener) => {
// from worker to client
if (IS_ELECTRON) {
const { ipcRenderer } = require("electron");
ipcRenderer.on(channel, listener);
} else {
__port ??= chrome.runtime.connect();
__port.onMessage.addListener((msg) => {
if (msg?.channel !== channel || msg?.invocation) return;
listener(msg.message);
});
}
},
sendMessage = (channel, message) => {
// to worker from client
if (IS_ELECTRON) {
const { ipcRenderer } = require("electron");
ipcRenderer.send(channel, message);
} else {
__port ??= chrome.runtime.connect();
__port.postMessage({ channel, message });
}
},
invokeInWorker = (channel, message) => {
if (IS_ELECTRON) {
const { ipcRenderer } = require("electron");
return ipcRenderer.invoke(channel, message);
} else {
// polyfills the electron.ipcRenderer.invoke method in
// the browser: uses a long-lived ipc connection to
// pass messages and handle responses asynchronously
let fulfilled;
__port ??= chrome.runtime.connect();
const id = crypto.randomUUID();
return new Promise((res, rej) => {
__port.onMessage.addListener((msg) => {
if (msg?.invocation !== id || fulfilled) return;
fulfilled = true;
res(msg.message);
});
__port.postMessage({ channel, message, invocation: id });
});
}
};
const readFile = (file) => {
if (IS_ELECTRON) {
// read directly from filesys if possible,
// treating notion-enhancer/src as fs root
if (!file.startsWith("http")) {
const fsp = require("fs/promises"),
{ resolve } = require("path");
return fsp.readFile(resolve(`${__dirname}/../${file}`), "utf-8");
}
// prefer using versions of files cached by the app
// or routed through the notion-enhancer's url interception
const notionProtocol = "notion://www.notion.so/";
file = file.replace(/^https:\/\/www\.notion\.so\//, notionProtocol);
} else file = file.startsWith("http") ? file : enhancerUrl(file);
return fetch(file).then((res) => res.text());
},
readJson = (file) => {
// as above, uses require instead of readFile
// and res.json() instead of res.text() to return
// json content of file in object form
if (IS_ELECTRON) {
if (!file.startsWith("http")) {
const { resolve } = require("path");
return require(resolve(`${__dirname}/../${file}`), "utf-8");
}
const notionProtocol = "notion://www.notion.so/";
file = file.replace(/^https:\/\/www\.notion\.so\//, notionProtocol);
} else file = file.startsWith("http") ? file : enhancerUrl(file);
return fetch(file).then((res) => res.json());
},
reloadApp = () => {
if (IS_ELECTRON && require("electron").app) {
const { app } = require("electron"),
args = process.argv.slice(1).filter((arg) => arg !== "--startup");
app.relaunch({ args });
app.exit();
} else sendMessage("notion-enhancer", "reload-app");
};
const initDatabase = (namespace, fallbacks = {}) => {
// all db operations are performed via ipc:
// with nodeintegration disabled, sqlite cannot
// be require()-d from the renderer process
const operation = (type, args = {}) =>
invokeInWorker("notion-enhancer:db", {
namespace,
fallbacks,
operation: type,
...args,
});
return {
get: (key) => operation("get", { key }),
set: (key, value) => operation("set", { key, value }),
remove: (keys) => operation("remove", { keys }),
export: () => operation("export"),
import: (obj) => operation("import", { obj }),
};
};
globalThis.__enhancerApi ??= {};
Object.assign(globalThis.__enhancerApi, {
platform,
version,
enhancerUrl,
notionRequire,
onMessage,
sendMessage,
invokeInWorker,
readFile,
readJson,
reloadApp,
initDatabase,
});

View File

@ -43,9 +43,9 @@ function List({ id, mods, description }) {
const _get = () => isEnabled(mod.id),
_set = async (enabled) => {
await setEnabled(mod.id, enabled);
// only one theme of each mode may be
// enabled at a time ∴ disable others
// on theme of same mode enabled
// only one theme may be enabled per
// mode at a time => auto-disable other
// enabled themes of matching mode
if (enabled && id === "themes") {
const isDark = mod.tags.includes("dark"),
isLight = mod.tags.includes("light");

View File

@ -197,9 +197,9 @@ useState(["rerender"], async () => {
if (!theme || !icon) return;
// chrome extensions run in an isolated execution context
// but extension:// pages can access chrome apis
// notion-enhancer api is imported directly
// => notion-enhancer api is imported directly
if (typeof globalThis.__enhancerApi === "undefined") {
await import("../../api/browser.js");
await import("../../api/system.js");
// in electron this isn't necessary, as a) scripts are
// not running in an isolated execution context and b)
// the notion:// protocol csp bypass allows scripts to

View File

@ -14,7 +14,7 @@ const isElectron = () => {
};
if (isElectron()) {
require("./api/electron.cjs");
require("./api/system.js");
require("./api/mods.js");
const { enhancerUrl } = globalThis.__enhancerApi,
{ getMods, isEnabled, modDatabase } = globalThis.__enhancerApi;
@ -48,7 +48,7 @@ if (isElectron()) {
} else {
// clientStyles
// clientScripts
import(chrome.runtime.getURL("/api/browser.js")).then(() => {
import(chrome.runtime.getURL("/api/system.js")).then(() => {
import(chrome.runtime.getURL("/load.mjs"));
});
}

View File

@ -1,19 +1,14 @@
/*
* notion-enhancer
* (c) 2022 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (c) 2023 dragonwocky <thedragonring.bod@gmail.com> (https://dragonwocky.me/)
* (https://notion-enhancer.github.io/) under the MIT license
*/
"use strict";
const isElectron = () => {
try {
return typeof module !== "undefined";
} catch {}
return false;
};
const IS_ELECTRON = typeof module !== "undefined";
if (isElectron()) {
if (IS_ELECTRON) {
const { app, ipcMain } = require("electron"),
reloadApp = () => {
const args = process.argv.slice(1).filter((arg) => arg !== "--startup");
@ -23,7 +18,7 @@ if (isElectron()) {
ipcMain.on("notion-enhancer", (_event, message) => {
if (message === "open-menu") {
//
// todo
} else if (message === "reload-app") {
reloadApp();
}
@ -62,6 +57,8 @@ if (isElectron()) {
notionTabs.forEach((tab) => chrome.tabs.reload(tab.id));
};
// listen for invoke: https://developer.chrome.com/docs/extensions/mv3/messaging/
chrome.action.onClicked.addListener(openEnhancerMenu);
chrome.runtime.onMessage.addListener((msg, sender) => {
if (msg?.channel !== "notion-enhancer") return;
@ -78,3 +75,110 @@ if (isElectron()) {
}
});
}
let __db, __statements, __transactions;
const initDatabase = (namespace, fallbacks = {}) => {
if (Array.isArray(namespace)) namespace = namespace.join("__");
namespace = namespace ? namespace + "__" : "";
const namespaceify = (key) =>
key.startsWith(namespace) ? key : namespace + key;
// schema:
// - ("agreedToTerms") -> string: semver
// - ("lastTelemetryPing") -> string: iso
// - ("telemetryEnabled") -> boolean
// - ("profileIds") -> $profileId[]
// - ("activeProfile") -> $profileId
// - $profileId: ("profileName") -> string
// - $profileId__enabledMods: ($modId) -> boolean
// - $profileId__$modId: ($optionKey) -> value
__db ??= (async () => {
if (!IS_ELECTRON) return;
const table = "kvstore",
{ app } = require("electron"),
{ resolve } = require("path"),
sqlite = require("better-sqlite3"),
db = sqlite(resolve(`${app.getPath("userData")}/notion-enhancer.db`)),
init = db.prepare(`CREATE TABLE IF NOT EXISTS ${table} (
key TEXT PRIMARY KEY,
value TEXT
)`);
init.run();
__statements = {
insert: db.prepare(`INSERT INTO ${table} (key, value) VALUES (?, ?)`),
update: db.prepare(`UPDATE ${table} SET value = ? WHERE key = ?`),
select: db.prepare(`SELECT * FROM ${table} WHERE key = ? LIMIT 1`),
delete: db.prepare(`DELETE FROM ${table} WHERE key = ?`),
dump: db.prepare(`SELECT * FROM ${table}`),
};
__transactions = {
remove: db.transaction((arr) => {
arr.forEach((key) => __statements.delete.run(key));
}),
set: db.transaction((obj) => {
for (const key in obj) {
if (__statements.select.get(key) === undefined) {
__statements.insert.run(key, obj[key]);
} else __statements.update.run(obj[key], key);
}
}),
};
return db;
})();
return {
async get(key) {
await __db;
let value;
const fallback = fallbacks[key];
key = namespaceify(key);
if (IS_ELECTRON) {
try {
value = JSON.parse(__statements.select.get(key)?.value);
} catch {}
} else value = (await chrome.storage.local.get([key]))[key];
return value ?? fallback;
},
async set(key, value) {
await __db;
key = namespaceify(key);
return IS_ELECTRON
? // returns true instead of transaction completion data type
(__transactions.set({ [key]: JSON.stringify(value) }), true)
: chrome.storage.local.set({ [key]: value });
},
async remove(keys) {
await __db;
keys = Array.isArray(keys) ? keys : [keys];
keys = keys.map(namespaceify);
return IS_ELECTRON
? (__transactions.remove(keys), true)
: chrome.storage.local.remove(keys);
},
async export() {
await __db;
// returns key/value pairs within scope w/out namespace
// prefix e.g. to streamline importing from one profile and
// then into another (where a diff. namespace is used)
let entries = IS_ELECTRON
? __statements.dump.all().map(({ key, value }) => [key, value])
: Object.entries(await chrome.storage.local.get());
entries = entries
.filter(([key]) => key.startsWith(namespace))
.map(([key, value]) => [key.slice(namespace.length), value]);
return Object.fromEntries(entries);
},
async import(obj) {
await __db;
let entries = Object.entries(obj);
entries = entries.map(([key, value]) => [namespace + key, value]);
entries = Object.fromEntries(entries);
return IS_ELECTRON
? (__transactions.set(entries), true)
: chrome.storage.local.set(entries);
},
};
};

102
yarn.lock
View File

@ -5,54 +5,17 @@ __metadata:
version: 6
cacheKey: 8
"@electron/asar@npm:^3.2.2":
version: 3.2.2
resolution: "@electron/asar@npm:3.2.2"
"@electron/asar@npm:^3.2.4":
version: 3.2.4
resolution: "@electron/asar@npm:3.2.4"
dependencies:
"@types/glob": ^7.1.1
chromium-pickle-js: ^0.2.0
commander: ^5.0.0
glob: ^7.1.6
minimatch: ^3.0.4
dependenciesMeta:
"@types/glob":
optional: true
bin:
asar: bin/asar.js
checksum: 38a3b4a47180f2033a599421175f03706941ba05a32591a639127f6374e0007c6a7c8bde550129de394f4072a0bf39c24aea202540fe1faba6d74b4181c007a8
languageName: node
linkType: hard
"@types/glob@npm:^7.1.1":
version: 7.2.0
resolution: "@types/glob@npm:7.2.0"
dependencies:
"@types/minimatch": "*"
"@types/node": "*"
checksum: 6ae717fedfdfdad25f3d5a568323926c64f52ef35897bcac8aca8e19bc50c0bd84630bbd063e5d52078b2137d8e7d3c26eabebd1a2f03ff350fff8a91e79fc19
languageName: node
linkType: hard
"@types/minimatch@npm:*":
version: 5.1.2
resolution: "@types/minimatch@npm:5.1.2"
checksum: 0391a282860c7cb6fe262c12b99564732401bdaa5e395bee9ca323c312c1a0f45efbf34dce974682036e857db59a5c9b1da522f3d6055aeead7097264c8705a8
languageName: node
linkType: hard
"@types/node@npm:*":
version: 18.11.11
resolution: "@types/node@npm:18.11.11"
checksum: c4b1176a8f1714a3ee3fc2a5e1d568b0cd50209000282db5c68154b3c975952928dbb834ef3a0ce55bd7b345ae29f2cbf4a34635a070294d135a24254231386a
languageName: node
linkType: hard
"ansi-styles@npm:^4.1.0":
version: 4.3.0
resolution: "ansi-styles@npm:4.3.0"
dependencies:
color-convert: ^2.0.1
checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4
checksum: 06e3e8fe7c894f7e7727410af5a9957ec77088f775b22441acf4ef718a9e6642a4dc1672f77ee1ce325fc367c8d59ac1e02f7db07869c8ced8a00132a3b54643
languageName: node
linkType: hard
@ -80,22 +43,19 @@ __metadata:
languageName: node
linkType: hard
"chalk-template@npm:^0.4.0":
version: 0.4.0
resolution: "chalk-template@npm:0.4.0"
"chalk-template@npm:^1.0.0":
version: 1.0.0
resolution: "chalk-template@npm:1.0.0"
dependencies:
chalk: ^4.1.2
checksum: 6c706802a79a7963cbce18f022b046fe86e438a67843151868852f80ea7346e975a6a9749991601e7e5d3b6a6c4852a04c53dc966a9a3d04031bd0e0ed53c819
chalk: ^5.2.0
checksum: 2cd8ae86d7e2ccc546a8fa93871931f7e5731b812e867be1bb77487f83ad6bd657dd25483a99dd0e4d615163551fde4457e48995597b69d58b8f1f633bc21952
languageName: node
linkType: hard
"chalk@npm:^4.1.2":
version: 4.1.2
resolution: "chalk@npm:4.1.2"
dependencies:
ansi-styles: ^4.1.0
supports-color: ^7.1.0
checksum: fe75c9d5c76a7a98d45495b91b2172fa3b7a09e0cc9370e5c8feb1c567b85c4288e2b3fded7cfdd7359ac28d6b3844feb8b82b8686842e93d23c827c417e83fc
"chalk@npm:^5.2.0":
version: 5.2.0
resolution: "chalk@npm:5.2.0"
checksum: 03d8060277de6cf2fd567dc25fcf770593eb5bb85f460ce443e49255a30ff1242edd0c90a06a03803b0466ff0687a939b41db1757bec987113e83de89a003caa
languageName: node
linkType: hard
@ -106,22 +66,6 @@ __metadata:
languageName: node
linkType: hard
"color-convert@npm:^2.0.1":
version: 2.0.1
resolution: "color-convert@npm:2.0.1"
dependencies:
color-name: ~1.1.4
checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336
languageName: node
linkType: hard
"color-name@npm:~1.1.4":
version: 1.1.4
resolution: "color-name@npm:1.1.4"
checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610
languageName: node
linkType: hard
"commander@npm:^5.0.0":
version: 5.1.0
resolution: "commander@npm:5.1.0"
@ -157,13 +101,6 @@ __metadata:
languageName: node
linkType: hard
"has-flag@npm:^4.0.0":
version: 4.0.0
resolution: "has-flag@npm:4.0.0"
checksum: 261a1357037ead75e338156b1f9452c016a37dcd3283a972a30d9e4a87441ba372c8b81f818cd0fbcd9c0354b4ae7e18b9e1afa1971164aef6d18c2b6095a8ad
languageName: node
linkType: hard
"inflight@npm:^1.0.4":
version: 1.0.6
resolution: "inflight@npm:1.0.6"
@ -194,9 +131,9 @@ __metadata:
version: 0.0.0-use.local
resolution: "notion-enhancer@workspace:."
dependencies:
"@electron/asar": ^3.2.2
"@electron/asar": ^3.2.4
arg: ^5.0.2
chalk-template: ^0.4.0
chalk-template: ^1.0.0
bin:
notion-enhancer: bin.mjs
languageName: unknown
@ -218,15 +155,6 @@ __metadata:
languageName: node
linkType: hard
"supports-color@npm:^7.1.0":
version: 7.2.0
resolution: "supports-color@npm:7.2.0"
dependencies:
has-flag: ^4.0.0
checksum: 3dda818de06ebbe5b9653e07842d9479f3555ebc77e9a0280caf5a14fb877ffee9ed57007c3b78f5a6324b8dbeec648d9e97a24e2ed9fdb81ddc69ea07100f4a
languageName: node
linkType: hard
"wrappy@npm:1":
version: 1.0.2
resolution: "wrappy@npm:1.0.2"