From 1e47dd2c830fbed0f93da43ec3baeb98280cfe5f Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 23 Apr 2024 16:43:37 -0700 Subject: [PATCH 01/96] format: prettify --- tsconfig.json | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index e0fcb12..9aa57c4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,19 +10,16 @@ "preserveConstEnums": false, "sourceMap": false, "downlevelIteration": true, - "lib": [ "es2017", "dom" ], - "typeRoots": [ - "./typings", - "./node_modules/@types" - ] + "lib": ["es2017", "dom"], + "typeRoots": ["./typings", "./node_modules/@types"] }, - "include": [ "*.ts", "chrome/content", "typing", "node_modules/zotero-types", + "include": [ + "*.ts", + "chrome/content", + "typing", + "node_modules/zotero-types", "src/reference_network/bootstrap.ts", "src/reference_network/lib.ts" ], - "exclude": [ - "node_modules", - "**/*.spec.ts", - "typings" - ] + "exclude": ["node_modules", "**/*.spec.ts", "typings"] } From 0b289bca8a926982a324c9b60ae5961eea5224fd Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 23 Apr 2024 16:43:54 -0700 Subject: [PATCH 02/96] eslint: ignore build js --- .eslintrc.json | 45 ++++++++++----------------------------------- 1 file changed, 10 insertions(+), 35 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 40ac80b..803d03b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,6 +5,7 @@ "es6": true, "node": true }, + "ignorepatterns": ["eslintrc.json"], "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", @@ -60,10 +61,7 @@ "@typescript-eslint/consistent-type-assertions": "error", "@typescript-eslint/dot-notation": "error", "@typescript-eslint/explicit-module-boundary-types": "warn", - "@typescript-eslint/indent": [ - "error", - 2 - ], + "@typescript-eslint/indent": ["error", 2], "@typescript-eslint/member-delimiter-style": [ "error", { @@ -131,10 +129,7 @@ "@typescript-eslint/require-await": "error", "@typescript-eslint/restrict-plus-operands": "error", "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/semi": [ - "error", - "never" - ], + "@typescript-eslint/semi": ["error", "never"], "@typescript-eslint/triple-slash-reference": [ "error", { @@ -146,10 +141,7 @@ "@typescript-eslint/unbound-method": "error", "@typescript-eslint/unified-signatures": "error", "arrow-body-style": "error", - "arrow-parens": [ - "error", - "as-needed" - ], + "arrow-parens": ["error", "as-needed"], "brace-style": [ "error", "stroustrup", @@ -167,15 +159,9 @@ ], "complexity": "off", "constructor-super": "error", - "curly": [ - "error", - "multi-line" - ], + "curly": ["error", "multi-line"], "eol-last": "error", - "eqeqeq": [ - "error", - "smart" - ], + "eqeqeq": ["error", "smart"], "guard-for-in": "error", "id-blacklist": [ "error", @@ -191,10 +177,7 @@ ], "id-match": "error", "import/order": "off", - "linebreak-style": [ - "error", - "unix" - ], + "linebreak-style": ["error", "unix"], "max-classes-per-file": "off", "max-len": [ "warn", @@ -240,10 +223,7 @@ "no-unused-vars": "off", "no-var": "error", "object-shorthand": "error", - "one-var": [ - "off", - "never" - ], + "one-var": ["off", "never"], "prefer-arrow/prefer-arrow-functions": [ "error", { @@ -258,10 +238,7 @@ ], "prefer-object-spread": "error", "prefer-template": "error", - "quote-props": [ - "error", - "as-needed" - ], + "quote-props": ["error", "as-needed"], "radix": "off", "require-await": "off", "space-before-function-paren": [ @@ -276,9 +253,7 @@ "error", "always", { - "markers": [ - "/" - ] + "markers": ["/"] } ], "use-isnan": "error", From 3dbb422ca49c64542ff810781ff4b1828b008de9 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 25 Apr 2024 17:55:58 -0700 Subject: [PATCH 03/96] feat: queries for schema --- src/reference_network/database/sql/authorItem.sql | 7 +++++++ src/reference_network/database/sql/authors.sql | 5 +++++ src/reference_network/database/sql/graph.sql | 7 +++++++ src/reference_network/database/sql/items.sql | 4 ++++ 4 files changed, 23 insertions(+) create mode 100644 src/reference_network/database/sql/authorItem.sql create mode 100644 src/reference_network/database/sql/authors.sql create mode 100644 src/reference_network/database/sql/graph.sql create mode 100644 src/reference_network/database/sql/items.sql diff --git a/src/reference_network/database/sql/authorItem.sql b/src/reference_network/database/sql/authorItem.sql new file mode 100644 index 0000000..765efba --- /dev/null +++ b/src/reference_network/database/sql/authorItem.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS author_item_link ( + creatorID INTEGER, + itemID INTEGER, + PRIMARY KEY (creatorID, itemID), + FOREIGN KEY (creatorID) REFERENCES authors (creatorID), + FOREIGN KEY (itemID) REFERENCES items (itemID) +); \ No newline at end of file diff --git a/src/reference_network/database/sql/authors.sql b/src/reference_network/database/sql/authors.sql new file mode 100644 index 0000000..4c0839b --- /dev/null +++ b/src/reference_network/database/sql/authors.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS authors ( + creatorID INTEGER PRIMARY KEY, + ORCID TEXT, + name TEXT +); \ No newline at end of file diff --git a/src/reference_network/database/sql/graph.sql b/src/reference_network/database/sql/graph.sql new file mode 100644 index 0000000..3dae457 --- /dev/null +++ b/src/reference_network/database/sql/graph.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS graph ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + source TEXT, + type TEXT, + target TEXT, + data_source TEXT +); \ No newline at end of file diff --git a/src/reference_network/database/sql/items.sql b/src/reference_network/database/sql/items.sql new file mode 100644 index 0000000..3912b1a --- /dev/null +++ b/src/reference_network/database/sql/items.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS items ( + itemID INTEGER PRIMARY KEY, + updated_datetime DATETIME +); \ No newline at end of file From 3bb1b62ef8bdc099cd0b9c8ba8803f00a7994b81 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 25 Apr 2024 17:56:46 -0700 Subject: [PATCH 04/96] env: add sqlite --- package-lock.json | 1621 ++++++++++++++++++++++++++++++++++++++++++--- package.json | 7 + 2 files changed, 1551 insertions(+), 77 deletions(-) diff --git a/package-lock.json b/package-lock.json index 55f10d5..3818e4f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,11 +17,18 @@ "eslint-plugin-prefer-arrow": "^1.2.3", "mkdirp": "^3.0.1", "npm-run-all": "^4.1.5", + "reflect-metadata": "^0.2.2", "rimraf": "^5.0.1", + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7", "ts-node": "^10.9.1", + "typeorm": "^0.3.20", "typescript": "^5.1.3", "zotero-plugin": "^1.4.22", "zotero-types": "^1.0.15" + }, + "devDependencies": { + "@types/node": "^20.12.7" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -478,6 +485,12 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "optional": true + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -658,6 +671,77 @@ "node": ">= 8" } }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@octokit/auth-token": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", @@ -818,6 +902,20 @@ "node": ">=14.0.0" } }, + "node_modules/@sqltools/formatter": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", + "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", @@ -863,9 +961,9 @@ } }, "node_modules/@types/node": { - "version": "20.11.30", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.30.tgz", - "integrity": "sha512-dHM6ZxwlmuZaRmUPfv1p+KrdD1Dci04FbdEm/9wEMouFqxYoFl5aMkt0VMAUtYRQDyYvD41WJLukhq/ha3YuTw==", + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dependencies": { "undici-types": "~5.26.4" } @@ -1137,6 +1235,31 @@ "node": ">= 6.0.0" } }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1174,6 +1297,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + }, + "node_modules/app-root-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.1.0.tgz", + "integrity": "sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==", + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -1494,6 +1630,14 @@ "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -1566,6 +1710,106 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -1634,71 +1878,215 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "optional": true, "engines": { "node": ">=10" } }, - "node_modules/clp": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/clp/-/clp-4.0.12.tgz", - "integrity": "sha512-DOQX14xsm4mM06JVzB/5nTk+tBdst+mFffz1OK4TWPa6++M/J7TJWuciXVwCRxTFzY2f+FRQARUu8+uqgQBpUQ==", - "dependencies": { - "is-number": "^2.1.0", - "last-char": "^1.3.1", - "match-it": "^1.0.0" + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "optional": true, + "engines": { + "node": ">=6" } }, - "node_modules/clp/node_modules/is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", + "node_modules/cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", "dependencies": { - "kind-of": "^3.0.2" + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "bin": { + "highlight": "bin/highlight" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.0.0", + "npm": ">=5.0.0" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/cli-highlight/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dependencies": { - "color-name": "~1.1.4" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cli-highlight/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/cli-highlight/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=7.0.0" + "node": ">=8" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "optional": true, - "bin": { - "color-support": "bin.js" + "node_modules/cli-highlight/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "node_modules/cli-highlight/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, "engines": { - "node": ">=14" + "node": ">=10" } }, - "node_modules/comment-parser": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", - "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "node_modules/cli-highlight/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "engines": { - "node": ">= 12.0.0" + "node": ">=10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clp": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/clp/-/clp-4.0.12.tgz", + "integrity": "sha512-DOQX14xsm4mM06JVzB/5nTk+tBdst+mFffz1OK4TWPa6++M/J7TJWuciXVwCRxTFzY2f+FRQARUu8+uqgQBpUQ==", + "dependencies": { + "is-number": "^2.1.0", + "last-char": "^1.3.1", + "match-it": "^1.0.0" + } + }, + "node_modules/clp/node_modules/is-number": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", + "integrity": "sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==", + "dependencies": { + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "engines": { + "node": ">= 12.0.0" } }, "node_modules/compress-commons": { @@ -1866,6 +2254,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.10", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", + "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1894,6 +2287,14 @@ "node": ">=8" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1946,7 +2347,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "optional": true, "engines": { "node": ">=8" } @@ -2021,6 +2421,15 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -2029,6 +2438,15 @@ "once": "^1.4.0" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/epubjs": { "version": "0.3.93", "resolved": "https://registry.npmjs.org/epubjs/-/epubjs-0.3.93.tgz", @@ -2053,6 +2471,12 @@ "node": ">=10.0.0" } }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "optional": true + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2260,6 +2684,14 @@ "@esbuild/win32-x64": "0.18.20" } }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2669,6 +3101,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==" }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, "node_modules/ext": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", @@ -2737,6 +3177,11 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -2886,7 +3331,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "optional": true, "dependencies": { "minipass": "^3.0.0" }, @@ -2898,7 +3342,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "optional": true, "dependencies": { "yallist": "^4.0.0" }, @@ -2990,6 +3433,14 @@ "node": ">=8" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", @@ -3032,6 +3483,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + }, "node_modules/glob": { "version": "10.3.10", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", @@ -3234,11 +3690,39 @@ "node": ">= 0.4" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "engines": { + "node": "*" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "optional": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", @@ -3252,6 +3736,27 @@ "node": ">= 6" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -3307,6 +3812,21 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "optional": true + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3321,6 +3841,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, "node_modules/internal-slot": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", @@ -3342,6 +3867,19 @@ "node": ">= 0.10" } }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "optional": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -3518,6 +4056,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "optional": true + }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -3714,6 +4258,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "optional": true + }, "node_modules/jsdoc-type-pratt-parser": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", @@ -4035,6 +4585,57 @@ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/marks-pane": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/marks-pane/-/marks-pane-1.0.9.tgz", @@ -4112,20 +4713,19 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", "optional": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "minipass": "^3.0.0" }, "engines": { "node": ">= 8" } }, - "node_modules/minizlib/node_modules/minipass": { + "node_modules/minipass-collect/node_modules/minipass": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", @@ -4137,39 +4737,183 @@ "node": ">=8" } }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "optionalDependencies": { + "encoding": "^0.1.12" } }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/ms": { - "version": "2.1.2", + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nan": { "version": "2.19.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", "optional": true }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -4180,6 +4924,15 @@ "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -4190,6 +4943,25 @@ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, + "node_modules/node-abi": { + "version": "3.62.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.62.0.tgz", + "integrity": "sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", + "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", + "engines": { + "node": "^16 || ^18 || >= 20" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -4210,6 +4982,138 @@ } } }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "optional": true + }, + "node_modules/node-gyp/node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/node-gyp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "optional": true + }, + "node_modules/node-gyp/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -4579,6 +5483,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pako": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", @@ -4607,6 +5526,24 @@ "node": ">=4" } }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4750,6 +5687,80 @@ "node": ">= 0.4" } }, + "node_modules/prebuild-install": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", + "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prebuild-install/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prebuild-install/node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -4771,6 +5782,25 @@ "asap": "~2.0.3" } }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/properties-reader": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", @@ -4914,6 +5944,15 @@ "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz", "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==" }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4941,6 +5980,28 @@ } ] }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -5016,6 +6077,11 @@ "node": ">= 0.10" } }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", @@ -5033,6 +6099,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -5065,6 +6139,15 @@ "node": ">=4" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "optional": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5165,6 +6248,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, "node_modules/semver": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", @@ -5231,6 +6320,18 @@ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5338,8 +6439,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/simple-get": { "version": "3.1.1", @@ -5360,6 +6460,44 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "optional": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/source-map-generator": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/source-map-generator/-/source-map-generator-0.8.0.tgz", @@ -5405,6 +6543,64 @@ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==" }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "optional": true + }, + "node_modules/sqlite": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz", + "integrity": "sha512-oBkezXa2hnkfuJwUo44Hl9hS3er+YFtueifoajrgidvqsJRQFpc5fKoAkAor1O5ZnLoa28GBScfHXs8j0K358Q==" + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -5621,7 +6817,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "optional": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -5634,6 +6829,22 @@ "node": ">=10" } }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, "node_modules/tar-js": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/tar-js/-/tar-js-0.3.0.tgz", @@ -5661,7 +6872,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "optional": true, "engines": { "node": ">=8" } @@ -5670,7 +6880,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "optional": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -5683,6 +6892,25 @@ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -5796,6 +7024,17 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", @@ -5892,6 +7131,153 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typeorm": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz", + "integrity": "sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==", + "dependencies": { + "@sqltools/formatter": "^1.2.5", + "app-root-path": "^3.1.0", + "buffer": "^6.0.3", + "chalk": "^4.1.2", + "cli-highlight": "^2.1.11", + "dayjs": "^1.11.9", + "debug": "^4.3.4", + "dotenv": "^16.0.3", + "glob": "^10.3.10", + "mkdirp": "^2.1.3", + "reflect-metadata": "^0.2.1", + "sha.js": "^2.4.11", + "tslib": "^2.5.0", + "uuid": "^9.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "typeorm": "cli.js", + "typeorm-ts-node-commonjs": "cli-ts-node-commonjs.js", + "typeorm-ts-node-esm": "cli-ts-node-esm.js" + }, + "engines": { + "node": ">=16.13.0" + }, + "funding": { + "url": "https://opencollective.com/typeorm" + }, + "peerDependencies": { + "@google-cloud/spanner": "^5.18.0", + "@sap/hana-client": "^2.12.25", + "better-sqlite3": "^7.1.2 || ^8.0.0 || ^9.0.0", + "hdb-pool": "^0.1.6", + "ioredis": "^5.0.4", + "mongodb": "^5.8.0", + "mssql": "^9.1.1 || ^10.0.1", + "mysql2": "^2.2.5 || ^3.0.1", + "oracledb": "^6.3.0", + "pg": "^8.5.1", + "pg-native": "^3.0.0", + "pg-query-stream": "^4.0.0", + "redis": "^3.1.1 || ^4.0.0", + "sql.js": "^1.4.0", + "sqlite3": "^5.0.3", + "ts-node": "^10.7.0", + "typeorm-aurora-data-api-driver": "^2.0.0" + }, + "peerDependenciesMeta": { + "@google-cloud/spanner": { + "optional": true + }, + "@sap/hana-client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "hdb-pool": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "mssql": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-native": { + "optional": true + }, + "pg-query-stream": { + "optional": true + }, + "redis": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "ts-node": { + "optional": true + }, + "typeorm-aurora-data-api-driver": { + "optional": true + } + } + }, + "node_modules/typeorm/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/typeorm/node_modules/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typeorm/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/typescript": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", @@ -5923,6 +7309,24 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, "node_modules/universal-user-agent": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", @@ -5954,6 +7358,18 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -6212,11 +7628,62 @@ "node": ">=0.6.0" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 399cf28..a4caa4e 100644 --- a/package.json +++ b/package.json @@ -34,8 +34,12 @@ "eslint-plugin-prefer-arrow": "^1.2.3", "mkdirp": "^3.0.1", "npm-run-all": "^4.1.5", + "reflect-metadata": "^0.2.2", "rimraf": "^5.0.1", + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7", "ts-node": "^10.9.1", + "typeorm": "^0.3.20", "typescript": "^5.1.3", "zotero-plugin": "^1.4.22", "zotero-types": "^1.0.15" @@ -45,5 +49,8 @@ "updateLink": "https://github.com/RaymondWJang/reference-network/releases/download/v{version}/reference-network-{version}.xpi", "releaseURL": "https://github.com/RaymondWJang/reference-network/releases/download/release/", "bootstrapped": true + }, + "devDependencies": { + "@types/node": "^20.12.7" } } From f701259ca11af0b321c6ac06c084231f06fbd664 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 25 Apr 2024 17:56:58 -0700 Subject: [PATCH 05/96] feat: polish db schema --- TODO.md | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/TODO.md b/TODO.md index 3be4c59..ad0b82c 100644 --- a/TODO.md +++ b/TODO.md @@ -1,19 +1,27 @@ # DB Schema - graph - - source - - type - - target - - data_source - - id + - id INTEGER PRIMARY KEY AUTOINCREMENT + - source TEXT + - type TEXT + - target TEXT + - data_source TEXT + - items - - itemID - - updated_datetime + - itemID INTEGER PRIMARY KEY + - updated_datetime DATETIME + - authors - - creatorID - - ORCID - - name -- author-item link table + - creatorID INTEGER PRIMARY KEY + - ORCID TEXT + - name TEXT + +- author_item_link + - creatorID INTEGER + - itemID INTEGER + - FOREIGN KEY(creatorID) REFERENCES authors(creatorID) + - FOREIGN KEY(itemID) REFERENCES items(itemID) + - PRIMARY KEY (creatorID, itemID) # TODOs From 3e1006da356228817d1fe0ccc56ea64ccd043ac5 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 25 Apr 2024 17:57:58 -0700 Subject: [PATCH 06/96] chore: gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7347baa..7c08c20 100644 --- a/.gitignore +++ b/.gitignore @@ -163,6 +163,7 @@ cython_debug/ # javascript node_modules +.eslintcache .cache* gen xpi From 9a0b6ca926436556e78e3c7c926aff3bd68abded Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 30 Apr 2024 18:16:20 -0700 Subject: [PATCH 07/96] TODO: not working suddenly... --- src/reference_network/database/createDB.ts | 53 ++++++++++++++++++++++ src/reference_network/lib.ts | 42 ++++++++++------- 2 files changed, 79 insertions(+), 16 deletions(-) create mode 100644 src/reference_network/database/createDB.ts diff --git a/src/reference_network/database/createDB.ts b/src/reference_network/database/createDB.ts new file mode 100644 index 0000000..8362cce --- /dev/null +++ b/src/reference_network/database/createDB.ts @@ -0,0 +1,53 @@ +// import { Components } from "../lib"; +import * as fs from "fs"; + +declare global { + var Components: any; +} + +let Zotero = Components.classes["@zotero.org/Zotero;1"].getService( + Components.interfaces.nsISupports +).wrappedJSObject; + +let schemaFiles = [ + "graph.sql", + "items.sql", + "authors.sql", + "author_item_link.sql", +]; +let schemaPath = __dirname + "/schema/"; +let schemaQuery = ""; +for (let file of schemaFiles) { + schemaQuery += fs.readFileSync(schemaPath + file, "utf8"); +} + +console.log(schemaQuery); + +async function createDatabase() { + let db = new Zotero.DBConnection("reference-network"); + + // Check if the database connection is opened successfully + if (db) { + Zotero.debug("Database connected successfully."); + } else { + Zotero.debug("Failed to connect to the database."); + return; + } + + try { + await db.queryAsync(schemaQuery); + Zotero.debug("Database schema created successfully."); + } catch (error) { + Zotero.debug("Error creating database schema: " + error); + } + + // Check the database schema + let schema = await db.queryAsync( + "SELECT name FROM sqlite_master WHERE type='table';" + ); + console.log(schema); + + db.close(); +} + +createDatabase().catch((err) => Zotero.debug(err)); diff --git a/src/reference_network/lib.ts b/src/reference_network/lib.ts index 7ce6abd..843b959 100644 --- a/src/reference_network/lib.ts +++ b/src/reference_network/lib.ts @@ -1,39 +1,49 @@ -declare const Zotero: any -declare const Components: any +// Make sure the file is treated as a module +export {}; + +// Declare global variables +declare global { + var Zotero: any; +} + const { // classes: Cc, // interfaces: Ci, utils: Cu, -} = Components +} = Components; if (Zotero.platformMajorVersion < 102) { - Cu.importGlobalProperties(['URL']) + Cu.importGlobalProperties(["URL"]); } -Zotero.ReferenceNetwork = new class { +Zotero.ReferenceNetwork = new (class { log(msg) { - Zotero.debug(`Reference Network: ${ msg}`) + Zotero.debug(`Reference Network: ${msg}`); } foo() { // Global properties are imported above in Zotero 6 and included automatically in // Zotero 7 - const host = new URL('https://foo.com/path').host - this.log(`Host is ${host}`) + const host = new URL("https://foo.com/path").host; + this.log(`Host is ${host}`); - this.log(`Intensity is ${Zotero.Prefs.get('extensions.reference-network.intensity', true)}`) + this.log( + `Intensity is ${Zotero.Prefs.get( + "extensions.reference-network.intensity", + true + )}` + ); - this.log(Zotero.getMainWindow().speechSynthesis) + this.log(Zotero.getMainWindow().speechSynthesis); } toggleGreen(enabled) { - const docElem = Zotero.getMainWindow().document.documentElement + const docElem = Zotero.getMainWindow().document.documentElement; // Element#toggleAttribute() is not supported in Zotero 6 if (enabled) { - docElem.setAttribute('data-green-instead', 'true') - } - else { - docElem.removeAttribute('data-green-instead') + docElem.setAttribute("data-green-instead", "true"); + } else { + docElem.removeAttribute("data-green-instead"); } } -} +})(); From 183398a7da924ec5fcf2110129fac307c982c7a9 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 30 Apr 2024 18:49:50 -0700 Subject: [PATCH 08/96] update dev doc --- docs/plugin/dev.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/plugin/dev.md b/docs/plugin/dev.md index 43faa61..450bde4 100644 --- a/docs/plugin/dev.md +++ b/docs/plugin/dev.md @@ -7,6 +7,11 @@ Following: https://www.zotero.org/support/dev/client_coding/plugin_development - watch and rebuild the plugin with `npm run start` - you have to restart zotero between each change i think +To run zotero with the plugin in debug mode, run the following command: + +```sh +./zotero -purgecaches -zoteroDebugText +``` Weird things: - the build step was generating the extension id as `reference-network@gmail.com` because [`zotero-plugin/rdf` just does that for some reason](https://github.com/retorquere/zotero-plugin/blob/ab40ae4ba59d2b6a3fcce3222d415d9b5d72b14b/rdf.ts#L16) From 8aea44f86c425a7ab9717004a194eda2bee2628d Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 14 May 2024 15:37:14 -0700 Subject: [PATCH 09/96] docs: add api docs on dev.md --- docs/plugin/dev.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/plugin/dev.md b/docs/plugin/dev.md index 450bde4..f869109 100644 --- a/docs/plugin/dev.md +++ b/docs/plugin/dev.md @@ -16,3 +16,7 @@ To run zotero with the plugin in debug mode, run the following command: Weird things: - the build step was generating the extension id as `reference-network@gmail.com` because [`zotero-plugin/rdf` just does that for some reason](https://github.com/retorquere/zotero-plugin/blob/ab40ae4ba59d2b6a3fcce3222d415d9b5d72b14b/rdf.ts#L16) so the extension ID has to be updated in both the `package.json` file as well as the actual `manifest.json` file + + +Official API Documentation: +https://www.zotero.org/support/dev/client_coding/javascript_api \ No newline at end of file From ca654291ab487ed6ae3e02747305cf7470aa9259 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 14 May 2024 15:52:35 -0700 Subject: [PATCH 10/96] chore: add homepage url to manifest --- src/reference_network/manifest.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/reference_network/manifest.json b/src/reference_network/manifest.json index a25ad32..de249ad 100644 --- a/src/reference_network/manifest.json +++ b/src/reference_network/manifest.json @@ -3,6 +3,7 @@ "name": "Reference Network", "version": "0.0.1", "description": "Reference Network", + "homepage_url": "https://github.com/RaymondWJang/reference-network", "applications": { "zotero": { "id": "reference-network@example.com", From d06d532e47c0b73c71bbe23e82a2e7d6cfa11849 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 14 May 2024 19:59:04 -0700 Subject: [PATCH 11/96] feat: init create database --- src/database/createDB.ts | 26 ++++++++++++++++++++++++++ src/database/sql/authorItem.sql | 7 +++++++ src/database/sql/authors.sql | 5 +++++ src/database/sql/graph.sql | 7 +++++++ src/database/sql/items.sql | 4 ++++ 5 files changed, 49 insertions(+) create mode 100644 src/database/createDB.ts create mode 100644 src/database/sql/authorItem.sql create mode 100644 src/database/sql/authors.sql create mode 100644 src/database/sql/graph.sql create mode 100644 src/database/sql/items.sql diff --git a/src/database/createDB.ts b/src/database/createDB.ts new file mode 100644 index 0000000..6906010 --- /dev/null +++ b/src/database/createDB.ts @@ -0,0 +1,26 @@ +Zotero.ZoteroDBTest = { + testDBConnection: async function () { + try { + let results = await Zotero.DB.query( + "SELECT itemID, title FROM items LIMIT 10" + ); + Zotero.debug( + "Connected successfully. Sample items from Zotero's database:" + ); + results.forEach((item) => { + Zotero.debug(`Item ID: ${item.itemID}, Title: ${item.title}`); + }); + } catch (error) { + Zotero.debug("Error querying items table:", error); + } + }, +}; + +// Running the test function when Zotero starts +if (Zotero.initializationPromise) { + Zotero.initializationPromise.then(() => + Zotero.ZoteroDBTest.testDBConnection() + ); +} else { + Zotero.ZoteroDBTest.testDBConnection(); +} diff --git a/src/database/sql/authorItem.sql b/src/database/sql/authorItem.sql new file mode 100644 index 0000000..765efba --- /dev/null +++ b/src/database/sql/authorItem.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS author_item_link ( + creatorID INTEGER, + itemID INTEGER, + PRIMARY KEY (creatorID, itemID), + FOREIGN KEY (creatorID) REFERENCES authors (creatorID), + FOREIGN KEY (itemID) REFERENCES items (itemID) +); \ No newline at end of file diff --git a/src/database/sql/authors.sql b/src/database/sql/authors.sql new file mode 100644 index 0000000..4c0839b --- /dev/null +++ b/src/database/sql/authors.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS authors ( + creatorID INTEGER PRIMARY KEY, + ORCID TEXT, + name TEXT +); \ No newline at end of file diff --git a/src/database/sql/graph.sql b/src/database/sql/graph.sql new file mode 100644 index 0000000..3dae457 --- /dev/null +++ b/src/database/sql/graph.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS graph ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + source TEXT, + type TEXT, + target TEXT, + data_source TEXT +); \ No newline at end of file diff --git a/src/database/sql/items.sql b/src/database/sql/items.sql new file mode 100644 index 0000000..3912b1a --- /dev/null +++ b/src/database/sql/items.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS items ( + itemID INTEGER PRIMARY KEY, + updated_datetime DATETIME +); \ No newline at end of file From 1781374904acb20dcdd8c4b251351835c8f60ba4 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 14 May 2024 19:59:45 -0700 Subject: [PATCH 12/96] chore: cleanup tsconfig --- tsconfig.json | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 9aa57c4..d02a021 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,15 +11,8 @@ "sourceMap": false, "downlevelIteration": true, "lib": ["es2017", "dom"], - "typeRoots": ["./typings", "./node_modules/@types"] + "typeRoots": ["./node_modules/@types"] }, - "include": [ - "*.ts", - "chrome/content", - "typing", - "node_modules/zotero-types", - "src/reference_network/bootstrap.ts", - "src/reference_network/lib.ts" - ], - "exclude": ["node_modules", "**/*.spec.ts", "typings"] + "include": ["*.ts", "node_modules/zotero-types", "src/**/*.ts"], + "exclude": ["node_modules", "**/*.spec.ts"] } From c861f434db17f90576139ae81b01a3763d46928e Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 14 May 2024 20:00:09 -0700 Subject: [PATCH 13/96] chore: esbuild prettify --- esbuild.js | 92 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/esbuild.js b/esbuild.js index 3a8f39e..4e9cbbd 100644 --- a/esbuild.js +++ b/esbuild.js @@ -1,77 +1,89 @@ -const path = require('path') -const fs = require('fs') -const esbuild = require('esbuild') -const rmrf = require('rimraf') -rmrf.sync('gen') +const path = require("path"); +const fs = require("fs"); +const esbuild = require("esbuild"); +const rmrf = require("rimraf"); +rmrf.sync("gen"); -require('zotero-plugin/copy-assets') -require('zotero-plugin/rdf') -require('zotero-plugin/version') +require("zotero-plugin/copy-assets"); +require("zotero-plugin/rdf"); +require("zotero-plugin/version"); function js(src) { - return src.replace(/[.]ts$/, '.js') + return src.replace(/[.]ts$/, ".js"); } async function bundle(config) { config = { bundle: true, - format: 'iife', - target: ['firefox60'], + format: "iife", + target: ["firefox60"], inject: [], treeShaking: true, keepNames: true, ...config, - } + }; - let target + let target; if (config.outfile) { - target = config.outfile - } - else if (config.entryPoints.length === 1 && config.outdir) { - target = path.join(config.outdir, js(path.basename(config.entryPoints[0]))) - } - else { - target = `${config.outdir} [${config.entryPoints.map(js).join(', ')}]` + target = config.outfile; + } else if (config.entryPoints.length === 1 && config.outdir) { + target = path.join(config.outdir, js(path.basename(config.entryPoints[0]))); + } else { + target = `${config.outdir} [${config.entryPoints.map(js).join(", ")}]`; } - const exportGlobals = config.exportGlobals - delete config.exportGlobals + const exportGlobals = config.exportGlobals; + delete config.exportGlobals; if (exportGlobals) { - const esm = await esbuild.build({ ...config, logLevel: 'silent', format: 'esm', metafile: true, write: false }) - if (Object.values(esm.metafile.outputs).length !== 1) throw new Error('exportGlobals not supported for multiple outputs') + const esm = await esbuild.build({ + ...config, + logLevel: "silent", + format: "esm", + metafile: true, + write: false, + }); + if (Object.values(esm.metafile.outputs).length !== 1) + throw new Error("exportGlobals not supported for multiple outputs"); for (const output of Object.values(esm.metafile.outputs)) { if (output.entryPoint) { - config.globalName = escape(`{ ${output.exports.sort().join(', ')} }`).replace(/%/g, '$') + config.globalName = escape( + `{ ${output.exports.sort().join(", ")} }` + ).replace(/%/g, "$"); // make these var, not const, so they get hoisted and are available in the global scope. } } } - console.log('* bundling', target) - await esbuild.build(config) + console.log("* bundling", target); + await esbuild.build(config); if (exportGlobals) { await fs.promises.writeFile( target, - (await fs.promises.readFile(target, 'utf-8')).replace(config.globalName, unescape(config.globalName.replace(/[$]/g, '%'))) - ) + ( + await fs.promises.readFile(target, "utf-8") + ).replace( + config.globalName, + unescape(config.globalName.replace(/[$]/g, "%")) + ) + ); } } async function build() { await bundle({ exportGlobals: true, - entryPoints: [ 'src/reference_network/bootstrap.ts' ], - outdir: 'build', - banner: { js: 'var Zotero;\n' }, - }) + entryPoints: ["src/bootstrap.ts"], + outdir: "build", + banner: { js: "var Zotero;\n" }, + }); await bundle({ - entryPoints: [ 'src/reference_network/lib.ts' ], - outdir: 'build', - }) + entryPoints: ["src/reference-network.ts"], + outdir: "build", + }); } -build().catch(err => { - console.log(err) - process.exit(1) -}) +build().catch((err) => { + console.log(err); + process.exit(1); +}); From 0ba09c0dd73a73bd733ac59f5bb3dcc4aead8424 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 14 May 2024 20:02:09 -0700 Subject: [PATCH 14/96] chore: migrate to source folder --- src/bootstrap.ts | 123 ++++++++++++ src/{reference_network => }/install.rdf | 0 src/locale/en-US/reference-network.ftl | 0 src/{reference_network => }/manifest.json | 0 src/prefs.ts | 3 + src/reference-network.ts | 43 ++++ src/reference_network/bootstrap.ts | 189 ------------------ .../locale/en-US/reference-network.properties | 1 - src/reference_network/database/createDB.ts | 53 ----- .../database/sql/authorItem.sql | 7 - .../database/sql/authors.sql | 5 - src/reference_network/database/sql/graph.sql | 7 - src/reference_network/database/sql/items.sql | 4 - src/reference_network/lib.ts | 49 ----- .../locale/en-US/reference-network.ftl | 2 - src/reference_network/prefs.js | 1 - src/reference_network/style.css | 7 - src/style.css | 0 18 files changed, 169 insertions(+), 325 deletions(-) create mode 100644 src/bootstrap.ts rename src/{reference_network => }/install.rdf (100%) create mode 100644 src/locale/en-US/reference-network.ftl rename src/{reference_network => }/manifest.json (100%) create mode 100644 src/prefs.ts create mode 100644 src/reference-network.ts delete mode 100644 src/reference_network/bootstrap.ts delete mode 100644 src/reference_network/chrome/locale/en-US/reference-network.properties delete mode 100644 src/reference_network/database/createDB.ts delete mode 100644 src/reference_network/database/sql/authorItem.sql delete mode 100644 src/reference_network/database/sql/authors.sql delete mode 100644 src/reference_network/database/sql/graph.sql delete mode 100644 src/reference_network/database/sql/items.sql delete mode 100644 src/reference_network/lib.ts delete mode 100644 src/reference_network/locale/en-US/reference-network.ftl delete mode 100644 src/reference_network/prefs.js delete mode 100644 src/reference_network/style.css create mode 100644 src/style.css diff --git a/src/bootstrap.ts b/src/bootstrap.ts new file mode 100644 index 0000000..54652fa --- /dev/null +++ b/src/bootstrap.ts @@ -0,0 +1,123 @@ +var ReferenceNetwork; +var Services; +var Zotero; + +Zotero.log("Reference Network: Loading bootstrap"); + +// var stylesheetID = "reference-network-stylesheet"; +// var ftlID = "reference-network-ftl"; +// var menuitemID = "make-it-green-instead"; +// var addedElementIDs = [stylesheetID, ftlID, menuitemID]; + +function log(msg) { + Zotero.log(`Reference Network: ${msg}`); +} + +function install() { + log("Reference Network: Installed"); +} + +// function setDefaultPrefs(rootURI) { +// var branch = Services.prefs.getDefaultBranch(""); +// var obj = { +// pref(pref, value) { +// switch (typeof value) { +// case 'boolean': +// branch.setBoolPref(pref, value) +// break +// case 'string': +// branch.setStringPref(pref, value) +// break +// case 'number': +// branch.setIntPref(pref, value) +// break +// default: +// Zotero.logError(`Invalid type '${typeof(value)}' for pref '${pref}'`) +// } +// }, +// } +// Services.scriptloader.loadSubScript(`${rootURI}prefs.js`, obj); +// } + +async function startup({ + id, + version, + resourceURI, + rootURI = resourceURI.spec, +}) { + log(`Reference Network: Startup`); + + Zotero.PreferencePanes.register({ + pluginID: "reference-network@example.com", + src: rootURI + "preferences.xhtml", + scripts: [rootURI + "prefs.js"], + }); + + // Add DOM elements to the main Zotero pane + // var win = Zotero.getMainWindow(); + // if (win && win.ZoteroPane) { + // const zp = win.ZoteroPane; + // const doc = win.document; + // createElementNS() necessary in Zotero 6; createElement() defaults to HTML in Zotero 7 + // const HTML_NS = "http://www.w3.org/1999/xhtml"; + // const XUL_NS = + // "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + // const link1 = doc.createElementNS(HTML_NS, "link"); + // link1.id = stylesheetID; + // link1.type = "text/css"; + // link1.rel = "stylesheet"; + // link1.href = `${rootURI}style.css`; + // doc.documentElement.appendChild(link1); + + // const menuitem = doc.createElementNS(XUL_NS, "menuitem"); + // menuitem.id = menuitemID; + // menuitem.setAttribute("type", "checkbox"); + // menuitem.setAttribute("data-l10n-id", "make-it-green-instead"); + // menuitem.addEventListener("command", () => { + // Zotero.ReferenceNetwork.toggleGreen( + // menuitem.getAttribute("checked") === "true" + // ); + // }); + // doc.getElementById("menu_viewPopup").appendChild(menuitem); + + // Use strings from reference-network.ftl (Fluent) in Zotero 7 + // const link2 = doc.createElementNS(HTML_NS, "link"); + // link2.id = ftlID; + // link2.rel = "localization"; + // link2.href = "reference-network.ftl"; + // doc.documentElement.appendChild(link2); + // } + + Services.scriptloader.loadSubScript(`${rootURI}reference-network.ts`); + ReferenceNetwork.init({ id, version, rootURI }); + + // Zotero.ReferenceNetwork.foo(); +} + +function onMainWindowLoad({ window }) { + ReferenceNetwork.addToWindow(window); +} + +function onMainWindowUnload({ window }) { + ReferenceNetwork.removeFromWindow(window); +} + +export function shutdown() { + log(`Reference Network: Shutdown`); + + // Remove stylesheet + var zp = Zotero.getActiveZoteroPane(); + if (zp) { + // for (const id of addedElementIDs) { + // // ?. (null coalescing operator) not available in Zotero 6 + // const elem = zp.document.getElementById(id); + // if (elem) elem.remove(); + // } + } + + Zotero.ReferenceNetwork = undefined; +} + +function uninstall() { + log("Reference Network: Uninstalled"); +} diff --git a/src/reference_network/install.rdf b/src/install.rdf similarity index 100% rename from src/reference_network/install.rdf rename to src/install.rdf diff --git a/src/locale/en-US/reference-network.ftl b/src/locale/en-US/reference-network.ftl new file mode 100644 index 0000000..e69de29 diff --git a/src/reference_network/manifest.json b/src/manifest.json similarity index 100% rename from src/reference_network/manifest.json rename to src/manifest.json diff --git a/src/prefs.ts b/src/prefs.ts new file mode 100644 index 0000000..2aba614 --- /dev/null +++ b/src/prefs.ts @@ -0,0 +1,3 @@ +// Loaded in reference_network.ts + +// pref("extensixons.reference-network.intensity", 100); diff --git a/src/reference-network.ts b/src/reference-network.ts new file mode 100644 index 0000000..06264d8 --- /dev/null +++ b/src/reference-network.ts @@ -0,0 +1,43 @@ +var ReferenceNetwork = { + id: null, + version: null, + rootURI: null, + initialized: false, + addedElementIDs: [], + + init({ id, version, rootURI }) { + if (this.initialized) return; + this.id = id; + this.version = version; + this.rootURI = rootURI; + this.initialized = true; + }, + + log(msg) { + Zotero.log("Reference Network: " + msg); + }, + + toggleGreen(enabled) { + // `window` is the global object in overlay scope + let docElem = document.documentElement; + // Element#toggleAttribute() is not supported in Zotero 6 + if (enabled) { + docElem.setAttribute("data-green-instead", "true"); + } else { + docElem.removeAttribute("data-green-instead"); + } + }, + + async main() { + // `window` is the global object in overlay scope + var host = new URL("https://foo.com/path").host; + this.log(`Host is ${host}`); + + this.log( + `Intensity is ${Zotero.Prefs.get( + "extensions.make-it-red.intensity", + true + )}` + ); + }, +}; diff --git a/src/reference_network/bootstrap.ts b/src/reference_network/bootstrap.ts deleted file mode 100644 index 2b8b17a..0000000 --- a/src/reference_network/bootstrap.ts +++ /dev/null @@ -1,189 +0,0 @@ -/* eslint-disable prefer-arrow/prefer-arrow-functions, no-var, @typescript-eslint/no-unused-vars, no-caller, @typescript-eslint/explicit-module-boundary-types */ - -declare const dump: (msg: string) => void -declare const Components: any -declare const ChromeUtils: any -declare var Services: any -const { - interfaces: Ci, - results: Cr, - utils: Cu, - Constructor: CC, -} = Components - -var stylesheetID = 'reference-network-stylesheet' -var ftlID = 'reference-network-ftl' -var menuitemID = 'make-it-green-instead' -var addedElementIDs = [stylesheetID, ftlID, menuitemID] - -if (typeof Zotero == 'undefined') { - var Zotero -} - -function log(msg) { - Zotero.debug(`Reference Network: ${ msg}`) -} - -// In Zotero 6, bootstrap methods are called before Zotero is initialized, and using include.js -// to get the Zotero XPCOM service would risk breaking Zotero startup. Instead, wait for the main -// Zotero window to open and get the Zotero object from there. -// -// In Zotero 7, bootstrap methods are not called until Zotero is initialized, and the 'Zotero' is -// automatically made available. -async function waitForZotero() { - if (typeof Zotero != 'undefined') { - await Zotero.initializationPromise - return - } - - // eslint-disable-next-line @typescript-eslint/no-shadow - var { Services } = ChromeUtils.import('resource://gre/modules/Services.jsm') - var windows = Services.wm.getEnumerator('navigator:browser') - var found = false - while (windows.hasMoreElements()) { - const win = windows.getNext() - if (win.Zotero) { - Zotero = win.Zotero - found = true - break - } - } - if (!found) { - await new Promise(resolve => { - var listener = { - onOpenWindow(aWindow) { - // Wait for the window to finish loading - const domWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow) - domWindow.addEventListener('load', function() { - domWindow.removeEventListener('load', arguments.callee, false) - if (domWindow.Zotero) { - Services.wm.removeListener(listener) - Zotero = domWindow.Zotero - resolve(undefined) - } - }, false) - }, - } - Services.wm.addListener(listener) - }) - } - await Zotero.initializationPromise -} - - -// Loads default preferences from prefs.js in Zotero 6 -function setDefaultPrefs(rootURI) { - var branch = Services.prefs.getDefaultBranch('') - var obj = { - pref(pref, value) { - switch (typeof value) { - case 'boolean': - branch.setBoolPref(pref, value) - break - case 'string': - branch.setStringPref(pref, value) - break - case 'number': - branch.setIntPref(pref, value) - break - default: - Zotero.logError(`Invalid type '${typeof(value)}' for pref '${pref}'`) - } - }, - } - Services.scriptloader.loadSubScript(`${rootURI }prefs.js`, obj) -} - - -export async function install() { - await waitForZotero() - - log('Installed') -} - -export async function startup({ id, version, resourceURI, rootURI = resourceURI.spec }) { - await waitForZotero() - - log('Starting') - - // 'Services' may not be available in Zotero 6 - if (typeof Services == 'undefined') { - // eslint-disable-next-line @typescript-eslint/no-shadow - var { Services } = ChromeUtils.import('resource://gre/modules/Services.jsm') - } - - // Read prefs from prefs.js when the plugin in Zotero 6 - if (Zotero.platformMajorVersion < 102) { - setDefaultPrefs(rootURI) - } - - // Add DOM elements to the main Zotero pane - var win = Zotero.getMainWindow() - if (win && win.ZoteroPane) { - const zp = win.ZoteroPane - const doc = win.document - // createElementNS() necessary in Zotero 6; createElement() defaults to HTML in Zotero 7 - const HTML_NS = 'http://www.w3.org/1999/xhtml' - const XUL_NS = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' - const link1 = doc.createElementNS(HTML_NS, 'link') - link1.id = stylesheetID - link1.type = 'text/css' - link1.rel = 'stylesheet' - link1.href = `${rootURI }style.css` - doc.documentElement.appendChild(link1) - - const menuitem = doc.createElementNS(XUL_NS, 'menuitem') - menuitem.id = menuitemID - menuitem.setAttribute('type', 'checkbox') - menuitem.setAttribute('data-l10n-id', 'make-it-green-instead') - menuitem.addEventListener('command', () => { - Zotero.ReferenceNetwork.toggleGreen(menuitem.getAttribute('checked') === 'true') - }) - doc.getElementById('menu_viewPopup').appendChild(menuitem) - - // Use strings from reference-network.properties (legacy properties format) in Zotero 6 - // and from reference-network.ftl (Fluent) in Zotero 7 - if (Zotero.platformMajorVersion < 102) { - const stringBundle = Services.strings.createBundle('chrome://reference-network/locale/reference-network.properties') - Zotero.getMainWindow().document.getElementById('make-it-green-instead') - .setAttribute('label', stringBundle.GetStringFromName('makeItGreenInstead.label')) - } - else { - const link2 = doc.createElementNS(HTML_NS, 'link') - link2.id = ftlID - link2.rel = 'localization' - link2.href = 'reference-network.ftl' - doc.documentElement.appendChild(link2) - } - } - - Services.scriptloader.loadSubScript(`${rootURI }lib.js`) - Zotero.ReferenceNetwork.foo() -} - -export function shutdown() { - log('Shutting down') - - // Remove stylesheet - var zp = Zotero.getActiveZoteroPane() - if (zp) { - for (const id of addedElementIDs) { - // ?. (null coalescing operator) not available in Zotero 6 - const elem = zp.document.getElementById(id) - if (elem) elem.remove() - } - } - - Zotero.ReferenceNetwork = undefined -} - -export function uninstall() { - // `Zotero` object isn't available in `uninstall()` in Zotero 6, so log manually - if (typeof Zotero == 'undefined') { - dump('Reference Network: Uninstalled\n\n') - return - } - - log('Uninstalled') -} diff --git a/src/reference_network/chrome/locale/en-US/reference-network.properties b/src/reference_network/chrome/locale/en-US/reference-network.properties deleted file mode 100644 index 98f2112..0000000 --- a/src/reference_network/chrome/locale/en-US/reference-network.properties +++ /dev/null @@ -1 +0,0 @@ -makeItGreenInstead.label = Make It Green Instead diff --git a/src/reference_network/database/createDB.ts b/src/reference_network/database/createDB.ts deleted file mode 100644 index 8362cce..0000000 --- a/src/reference_network/database/createDB.ts +++ /dev/null @@ -1,53 +0,0 @@ -// import { Components } from "../lib"; -import * as fs from "fs"; - -declare global { - var Components: any; -} - -let Zotero = Components.classes["@zotero.org/Zotero;1"].getService( - Components.interfaces.nsISupports -).wrappedJSObject; - -let schemaFiles = [ - "graph.sql", - "items.sql", - "authors.sql", - "author_item_link.sql", -]; -let schemaPath = __dirname + "/schema/"; -let schemaQuery = ""; -for (let file of schemaFiles) { - schemaQuery += fs.readFileSync(schemaPath + file, "utf8"); -} - -console.log(schemaQuery); - -async function createDatabase() { - let db = new Zotero.DBConnection("reference-network"); - - // Check if the database connection is opened successfully - if (db) { - Zotero.debug("Database connected successfully."); - } else { - Zotero.debug("Failed to connect to the database."); - return; - } - - try { - await db.queryAsync(schemaQuery); - Zotero.debug("Database schema created successfully."); - } catch (error) { - Zotero.debug("Error creating database schema: " + error); - } - - // Check the database schema - let schema = await db.queryAsync( - "SELECT name FROM sqlite_master WHERE type='table';" - ); - console.log(schema); - - db.close(); -} - -createDatabase().catch((err) => Zotero.debug(err)); diff --git a/src/reference_network/database/sql/authorItem.sql b/src/reference_network/database/sql/authorItem.sql deleted file mode 100644 index 765efba..0000000 --- a/src/reference_network/database/sql/authorItem.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS author_item_link ( - creatorID INTEGER, - itemID INTEGER, - PRIMARY KEY (creatorID, itemID), - FOREIGN KEY (creatorID) REFERENCES authors (creatorID), - FOREIGN KEY (itemID) REFERENCES items (itemID) -); \ No newline at end of file diff --git a/src/reference_network/database/sql/authors.sql b/src/reference_network/database/sql/authors.sql deleted file mode 100644 index 4c0839b..0000000 --- a/src/reference_network/database/sql/authors.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE IF NOT EXISTS authors ( - creatorID INTEGER PRIMARY KEY, - ORCID TEXT, - name TEXT -); \ No newline at end of file diff --git a/src/reference_network/database/sql/graph.sql b/src/reference_network/database/sql/graph.sql deleted file mode 100644 index 3dae457..0000000 --- a/src/reference_network/database/sql/graph.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS graph ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - source TEXT, - type TEXT, - target TEXT, - data_source TEXT -); \ No newline at end of file diff --git a/src/reference_network/database/sql/items.sql b/src/reference_network/database/sql/items.sql deleted file mode 100644 index 3912b1a..0000000 --- a/src/reference_network/database/sql/items.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE IF NOT EXISTS items ( - itemID INTEGER PRIMARY KEY, - updated_datetime DATETIME -); \ No newline at end of file diff --git a/src/reference_network/lib.ts b/src/reference_network/lib.ts deleted file mode 100644 index 843b959..0000000 --- a/src/reference_network/lib.ts +++ /dev/null @@ -1,49 +0,0 @@ -// Make sure the file is treated as a module -export {}; - -// Declare global variables -declare global { - var Zotero: any; -} - -const { - // classes: Cc, - // interfaces: Ci, - utils: Cu, -} = Components; - -if (Zotero.platformMajorVersion < 102) { - Cu.importGlobalProperties(["URL"]); -} - -Zotero.ReferenceNetwork = new (class { - log(msg) { - Zotero.debug(`Reference Network: ${msg}`); - } - - foo() { - // Global properties are imported above in Zotero 6 and included automatically in - // Zotero 7 - const host = new URL("https://foo.com/path").host; - this.log(`Host is ${host}`); - - this.log( - `Intensity is ${Zotero.Prefs.get( - "extensions.reference-network.intensity", - true - )}` - ); - - this.log(Zotero.getMainWindow().speechSynthesis); - } - - toggleGreen(enabled) { - const docElem = Zotero.getMainWindow().document.documentElement; - // Element#toggleAttribute() is not supported in Zotero 6 - if (enabled) { - docElem.setAttribute("data-green-instead", "true"); - } else { - docElem.removeAttribute("data-green-instead"); - } - } -})(); diff --git a/src/reference_network/locale/en-US/reference-network.ftl b/src/reference_network/locale/en-US/reference-network.ftl deleted file mode 100644 index b1506a0..0000000 --- a/src/reference_network/locale/en-US/reference-network.ftl +++ /dev/null @@ -1,2 +0,0 @@ -make-it-green-instead = - .label = Make It Green Instead diff --git a/src/reference_network/prefs.js b/src/reference_network/prefs.js deleted file mode 100644 index c39c553..0000000 --- a/src/reference_network/prefs.js +++ /dev/null @@ -1 +0,0 @@ -pref("extensions.reference-network.intensity", 100); diff --git a/src/reference_network/style.css b/src/reference_network/style.css deleted file mode 100644 index 57e6e0a..0000000 --- a/src/reference_network/style.css +++ /dev/null @@ -1,7 +0,0 @@ -.row { - color: red; -} - -window[data-green-instead] .row { - color: green; -} diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..e69de29 From 3de87fb2aeaf66a3b79bca178fada65abed9cefc Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 14 May 2024 20:02:38 -0700 Subject: [PATCH 15/96] feat: global declare Zotero --- src/types/global.d.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/types/global.d.ts diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 0000000..d1a06ff --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,3 @@ +import "zotero-types"; + +declare const Zotero: _ZoteroTypes.Zotero; From d749f1dc5fefaedc250dd157e8ca1832fb4cd0a5 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 15 May 2024 15:35:17 -0700 Subject: [PATCH 16/96] feat: export functions in bootstrap (readable on Zotero!) --- src/bootstrap.ts | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 54652fa..b6e9685 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,6 +1,4 @@ var ReferenceNetwork; -var Services; -var Zotero; Zotero.log("Reference Network: Loading bootstrap"); @@ -13,7 +11,7 @@ function log(msg) { Zotero.log(`Reference Network: ${msg}`); } -function install() { +export function install() { log("Reference Network: Installed"); } @@ -39,13 +37,17 @@ function install() { // Services.scriptloader.loadSubScript(`${rootURI}prefs.js`, obj); // } -async function startup({ +export async function startup({ id, version, resourceURI, rootURI = resourceURI.spec, }) { log(`Reference Network: Startup`); + log(`ID: ${id}`); + log(`Version: ${version}`); + log(`Resource URI: ${resourceURI}`); + log(`Root URI: ${rootURI}`); Zotero.PreferencePanes.register({ pluginID: "reference-network@example.com", @@ -53,6 +55,7 @@ async function startup({ scripts: [rootURI + "prefs.js"], }); + log(`Registered preference pane`); // Add DOM elements to the main Zotero pane // var win = Zotero.getMainWindow(); // if (win && win.ZoteroPane) { @@ -88,17 +91,20 @@ async function startup({ // doc.documentElement.appendChild(link2); // } - Services.scriptloader.loadSubScript(`${rootURI}reference-network.ts`); + Services.scriptloader.loadSubScript(`${rootURI}reference-network.js`); + log(`Loaded reference-network.js`); + ReferenceNetwork.init({ id, version, rootURI }); + log(`Initialized Reference Network`); // Zotero.ReferenceNetwork.foo(); } -function onMainWindowLoad({ window }) { +export function onMainWindowLoad({ window }) { ReferenceNetwork.addToWindow(window); } -function onMainWindowUnload({ window }) { +export function onMainWindowUnload({ window }) { ReferenceNetwork.removeFromWindow(window); } @@ -118,6 +124,6 @@ export function shutdown() { Zotero.ReferenceNetwork = undefined; } -function uninstall() { +export function uninstall() { log("Reference Network: Uninstalled"); } From 7181e82efafab704a3ce7318b4c8615f110d5d5a Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 16 May 2024 14:24:54 -0700 Subject: [PATCH 17/96] chore: add node to gitignore --- .gitignore | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 131 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 7c08c20..5f9f475 100644 --- a/.gitignore +++ b/.gitignore @@ -162,8 +162,136 @@ cython_debug/ /data # javascript -node_modules -.eslintcache -.cache* gen xpi + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* From 47450785e544b01e607724f1250709daf7137672 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 16 May 2024 15:12:52 -0700 Subject: [PATCH 18/96] feat: can read global referencenetwork --- src/bootstrap.ts | 2 -- src/reference-network.ts | 2 +- src/types/global.d.ts | 22 ++++++++++++++++++++++ tsconfig.json | 7 ++++++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index b6e9685..bdbb425 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,5 +1,3 @@ -var ReferenceNetwork; - Zotero.log("Reference Network: Loading bootstrap"); // var stylesheetID = "reference-network-stylesheet"; diff --git a/src/reference-network.ts b/src/reference-network.ts index 06264d8..922a8ee 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -1,4 +1,4 @@ -var ReferenceNetwork = { +ReferenceNetwork = { id: null, version: null, rootURI: null, diff --git a/src/types/global.d.ts b/src/types/global.d.ts index d1a06ff..9fca48f 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,3 +1,25 @@ import "zotero-types"; declare const Zotero: _ZoteroTypes.Zotero; +declare var ReferenceNetwork: { + id: string | null; + version: string | null; + rootURI: string | null; + initialized: boolean; + addedElementIDs: string[]; + + init({ + id, + version, + rootURI, + }: { + id: string; + version: string; + rootURI: string; + }): void; + log(msg: string): void; + toggleGreen(enabled: boolean): void; + main(): Promise; +}; + +export {}; diff --git a/tsconfig.json b/tsconfig.json index d02a021..e043aa9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,11 @@ "lib": ["es2017", "dom"], "typeRoots": ["./node_modules/@types"] }, - "include": ["*.ts", "node_modules/zotero-types", "src/**/*.ts"], + "include": [ + "*.ts", + "node_modules/zotero-types", + "src/**/*.ts", + "src/types/global.d.ts" + ], "exclude": ["node_modules", "**/*.spec.ts"] } From f8c1833d919800ae78a76f28acd31f8dfc5ac971 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 17 May 2024 16:23:50 -0700 Subject: [PATCH 19/96] chore: declutter --- src/prefs.ts | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/prefs.ts diff --git a/src/prefs.ts b/src/prefs.ts deleted file mode 100644 index 2aba614..0000000 --- a/src/prefs.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Loaded in reference_network.ts - -// pref("extensixons.reference-network.intensity", 100); From 4186455b859bf1705331497c0a25368fcb3d8860 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 17 May 2024 16:24:13 -0700 Subject: [PATCH 20/96] feat: learned what d.ts is for (extending types from external libraries, basically) --- src/types/global.d.ts | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 9fca48f..b219c52 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,25 +1,8 @@ import "zotero-types"; declare const Zotero: _ZoteroTypes.Zotero; -declare var ReferenceNetwork: { - id: string | null; - version: string | null; - rootURI: string | null; - initialized: boolean; - addedElementIDs: string[]; - - init({ - id, - version, - rootURI, - }: { - id: string; - version: string; - rootURI: string; - }): void; - log(msg: string): void; - toggleGreen(enabled: boolean): void; - main(): Promise; -}; - -export {}; +declare global { + interface Window { + ZoteroPane: _ZoteroTypes.ZoteroPane; + } +} From 490ea229591894eb1f37eb0d1647214343b00ac8 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 17 May 2024 16:25:34 -0700 Subject: [PATCH 21/96] feat: move RN declar/def to module & remove togglegreen --- src/reference-network.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/reference-network.ts b/src/reference-network.ts index 922a8ee..c03552b 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -1,4 +1,6 @@ -ReferenceNetwork = { +// import { Zotero, ReferenceNetwork } from "./types/global"; + +export let ReferenceNetwork = { id: null, version: null, rootURI: null, @@ -17,16 +19,16 @@ ReferenceNetwork = { Zotero.log("Reference Network: " + msg); }, - toggleGreen(enabled) { - // `window` is the global object in overlay scope - let docElem = document.documentElement; - // Element#toggleAttribute() is not supported in Zotero 6 - if (enabled) { - docElem.setAttribute("data-green-instead", "true"); - } else { - docElem.removeAttribute("data-green-instead"); - } - }, + // toggleGreen(enabled) { + // // `window` is the global object in overlay scope + // let docElem = document.documentElement; + // // Element#toggleAttribute() is not supported in Zotero 6 + // if (enabled) { + // docElem.setAttribute("data-green-instead", "true"); + // } else { + // docElem.removeAttribute("data-green-instead"); + // } + // }, async main() { // `window` is the global object in overlay scope From 585214268b9d154a124754af638d4c5cbf7fba2d Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 17 May 2024 16:26:16 -0700 Subject: [PATCH 22/96] feat: deprecate unused functions from Make-It-Red --- src/bootstrap.ts | 64 ++++++++++++------------------------------------ 1 file changed, 15 insertions(+), 49 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index bdbb425..5857457 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,9 +1,6 @@ -Zotero.log("Reference Network: Loading bootstrap"); +import { ReferenceNetwork } from "./reference-network"; -// var stylesheetID = "reference-network-stylesheet"; -// var ftlID = "reference-network-ftl"; -// var menuitemID = "make-it-green-instead"; -// var addedElementIDs = [stylesheetID, ftlID, menuitemID]; +Zotero.log("Reference Network: Loading bootstrap"); function log(msg) { Zotero.log(`Reference Network: ${msg}`); @@ -13,28 +10,6 @@ export function install() { log("Reference Network: Installed"); } -// function setDefaultPrefs(rootURI) { -// var branch = Services.prefs.getDefaultBranch(""); -// var obj = { -// pref(pref, value) { -// switch (typeof value) { -// case 'boolean': -// branch.setBoolPref(pref, value) -// break -// case 'string': -// branch.setStringPref(pref, value) -// break -// case 'number': -// branch.setIntPref(pref, value) -// break -// default: -// Zotero.logError(`Invalid type '${typeof(value)}' for pref '${pref}'`) -// } -// }, -// } -// Services.scriptloader.loadSubScript(`${rootURI}prefs.js`, obj); -// } - export async function startup({ id, version, @@ -54,11 +29,14 @@ export async function startup({ }); log(`Registered preference pane`); + // Add DOM elements to the main Zotero pane - // var win = Zotero.getMainWindow(); - // if (win && win.ZoteroPane) { - // const zp = win.ZoteroPane; - // const doc = win.document; + let win = Zotero.getMainWindow(); + if (win && win.ZoteroPane) { + const zp = win.ZoteroPane; + const doc = win.document; + } + // createElementNS() necessary in Zotero 6; createElement() defaults to HTML in Zotero 7 // const HTML_NS = "http://www.w3.org/1999/xhtml"; // const XUL_NS = @@ -94,31 +72,19 @@ export async function startup({ ReferenceNetwork.init({ id, version, rootURI }); log(`Initialized Reference Network`); - - // Zotero.ReferenceNetwork.foo(); } -export function onMainWindowLoad({ window }) { - ReferenceNetwork.addToWindow(window); -} +// export function onMainWindowLoad({ window }) { +// ReferenceNetwork.addToWindow(window); +// } -export function onMainWindowUnload({ window }) { - ReferenceNetwork.removeFromWindow(window); -} +// export function onMainWindowUnload({ window }) { +// ReferenceNetwork.removeFromWindow(window); +// } export function shutdown() { log(`Reference Network: Shutdown`); - // Remove stylesheet - var zp = Zotero.getActiveZoteroPane(); - if (zp) { - // for (const id of addedElementIDs) { - // // ?. (null coalescing operator) not available in Zotero 6 - // const elem = zp.document.getElementById(id); - // if (elem) elem.remove(); - // } - } - Zotero.ReferenceNetwork = undefined; } From 391f55f817ac06a7f8bad4a66b190645137e589d Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 20 May 2024 20:44:13 -0700 Subject: [PATCH 23/96] temp: relax linting rules --- .eslintrc.json | 471 +++++++++++++++++++++++++------------------------ 1 file changed, 238 insertions(+), 233 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 803d03b..b630401 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -5,8 +5,9 @@ "es6": true, "node": true }, - "ignorepatterns": ["eslintrc.json"], "extends": [ + // "airbnb-base", + // "airbnb-typescript/base" "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/eslint-recommended", @@ -24,245 +25,249 @@ "@typescript-eslint/eslint-plugin" ], "rules": { - "@typescript-eslint/adjacent-overload-signatures": "error", - "@typescript-eslint/array-type": [ - "error", - { - "default": "array" - } - ], - "@typescript-eslint/await-thenable": "error", - "@typescript-eslint/ban-ts-comment": "warn", - "@typescript-eslint/ban-types": [ - "warn", - { - "types": { - "Object": { - "message": "Avoid using the `Object` type. Did you mean `object`?" - }, - "Function": { - "message": "Avoid using the `Function` type. Prefer a specific function type, like `() => void`." - }, - "Boolean": { - "message": "Avoid using the `Boolean` type. Did you mean `boolean`?" - }, - "Number": { - "message": "Avoid using the `Number` type. Did you mean `number`?" - }, - "String": { - "message": "Avoid using the `String` type. Did you mean `string`?" - }, - "Symbol": { - "message": "Avoid using the `Symbol` type. Did you mean `symbol`?" - } - } - } - ], - "@typescript-eslint/consistent-type-assertions": "error", - "@typescript-eslint/dot-notation": "error", - "@typescript-eslint/explicit-module-boundary-types": "warn", - "@typescript-eslint/indent": ["error", 2], - "@typescript-eslint/member-delimiter-style": [ - "error", - { - "multiline": { - "delimiter": "none", - "requireLast": false - }, - "singleline": { - "delimiter": "comma", - "requireLast": false - } - } - ], - "@typescript-eslint/member-ordering": "off", - "@typescript-eslint/naming-convention": "off", - "@typescript-eslint/no-array-constructor": "error", - "@typescript-eslint/no-empty-function": "error", - "@typescript-eslint/no-empty-interface": "error", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-extra-non-null-assertion": "error", - "@typescript-eslint/no-extra-semi": "error", - "@typescript-eslint/no-floating-promises": "error", - "@typescript-eslint/no-for-in-array": "error", - "@typescript-eslint/no-implied-eval": "off", - "@typescript-eslint/no-inferrable-types": "error", - "@typescript-eslint/no-misused-new": "error", - "@typescript-eslint/no-misused-promises": "error", - "@typescript-eslint/no-namespace": "error", - "@typescript-eslint/no-non-null-asserted-optional-chain": "error", - "@typescript-eslint/no-non-null-assertion": "warn", - "@typescript-eslint/no-parameter-properties": "off", - "@typescript-eslint/no-shadow": [ - "error", - { - "hoist": "all" - } - ], - "@typescript-eslint/no-this-alias": "error", - "@typescript-eslint/no-unnecessary-type-assertion": "error", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-return": "error", - "@typescript-eslint/no-unused-expressions": "error", + // "@typescript-eslint/adjacent-overload-signatures": "error", + // "@typescript-eslint/array-type": [ + // "error", + // { + // "default": "array" + // } + // ], + // "@typescript-eslint/await-thenable": "error", + // "@typescript-eslint/ban-ts-comment": "warn", + // "@typescript-eslint/ban-types": [ + // "warn", + // { + // "types": { + // "Object": { + // "message": "Avoid using the `Object` type. Did you mean `object`?" + // }, + // "Function": { + // "message": "Avoid using the `Function` type. Prefer a specific function type, like `() => void`." + // }, + // "Boolean": { + // "message": "Avoid using the `Boolean` type. Did you mean `boolean`?" + // }, + // "Number": { + // "message": "Avoid using the `Number` type. Did you mean `number`?" + // }, + // "String": { + // "message": "Avoid using the `String` type. Did you mean `string`?" + // }, + // "Symbol": { + // "message": "Avoid using the `Symbol` type. Did you mean `symbol`?" + // } + // } + // } + // ], + // "@typescript-eslint/consistent-type-assertions": "error", + // "@typescript-eslint/dot-notation": "error", + // "@typescript-eslint/explicit-module-boundary-types": "warn", + // "@typescript-eslint/indent": ["warn", 2], + // "@typescript-eslint/member-delimiter-style": [ + // "error", + // { + // "multiline": { + // "delimiter": "none", + // "requireLast": false + // }, + // "singleline": { + // "delimiter": "comma", + // "requireLast": false + // } + // } + // ], + // "@typescript-eslint/member-ordering": "off", + // "@typescript-eslint/naming-convention": "off", + // "@typescript-eslint/no-array-constructor": "error", + // "@typescript-eslint/no-empty-function": "error", + // "@typescript-eslint/no-empty-interface": "error", + "@typescript-eslint/no-explicit-any": "warn", + // "@typescript-eslint/no-extra-non-null-assertion": "error", + // "@typescript-eslint/no-extra-semi": "error", + // "@typescript-eslint/no-floating-promises": "error", + // "@typescript-eslint/no-for-in-array": "error", + // "@typescript-eslint/no-implied-eval": "off", + // "@typescript-eslint/no-inferrable-types": "error", + // "@typescript-eslint/no-misused-new": "error", + // "@typescript-eslint/no-misused-promises": "error", + // "@typescript-eslint/no-namespace": "error", + // "@typescript-eslint/no-non-null-asserted-optional-chain": "error", + // "@typescript-eslint/no-non-null-assertion": "warn", + // "@typescript-eslint/no-parameter-properties": "off", + // "@typescript-eslint/no-shadow": [ + // "error", + // { + // "hoist": "all" + // } + // ], + // "@typescript-eslint/no-this-alias": "error", + "@typescript-eslint/no-unsafe-argument": "warn", + // "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/no-unsafe-assignment": "warn", + "@typescript-eslint/no-unsafe-call": "warn", + "@typescript-eslint/no-unsafe-member-access": "warn", + "@typescript-eslint/no-unsafe-return": "warn", + "@typescript-eslint/no-unused-expressions": "warn", "@typescript-eslint/no-unused-vars": [ - "error", - { - "argsIgnorePattern": "^_" - } - ], - "@typescript-eslint/no-use-before-define": "off", - "@typescript-eslint/no-var-requires": "off", - "@typescript-eslint/prefer-as-const": "error", - "@typescript-eslint/prefer-for-of": "error", - "@typescript-eslint/prefer-function-type": "error", - "@typescript-eslint/prefer-namespace-keyword": "error", - "@typescript-eslint/prefer-regexp-exec": "off", - "@typescript-eslint/quotes": [ - "error", - "single", - { - "avoidEscape": true - } - ], - "@typescript-eslint/require-await": "error", - "@typescript-eslint/restrict-plus-operands": "error", - "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/semi": ["error", "never"], - "@typescript-eslint/triple-slash-reference": [ - "error", - { - "path": "always", - "types": "prefer-import", - "lib": "always" - } - ], - "@typescript-eslint/unbound-method": "error", - "@typescript-eslint/unified-signatures": "error", - "arrow-body-style": "error", - "arrow-parens": ["error", "as-needed"], - "brace-style": [ - "error", - "stroustrup", - { - "allowSingleLine": true - } - ], - "comma-dangle": [ - "error", - { - "objects": "always-multiline", - "arrays": "always-multiline", - "functions": "never" - } - ], - "complexity": "off", - "constructor-super": "error", - "curly": ["error", "multi-line"], - "eol-last": "error", - "eqeqeq": ["error", "smart"], - "guard-for-in": "error", - "id-blacklist": [ - "error", - "any", - "Number", - "number", - "String", - "string", - "Boolean", - "boolean", - "Undefined", - "undefined" - ], - "id-match": "error", - "import/order": "off", - "linebreak-style": ["error", "unix"], - "max-classes-per-file": "off", - "max-len": [ "warn", { - "code": 240 - } - ], - "new-parens": "off", - "no-array-constructor": "off", - "no-bitwise": "error", - "no-caller": "error", - "no-cond-assign": "off", - "no-console": "error", - "no-debugger": "error", - "no-empty": [ - "error", - { - "allowEmptyCatch": true - } - ], - "no-empty-function": "off", - "no-eval": "error", - "no-extra-semi": "off", - "no-fallthrough": "off", - "no-implied-eval": "off", - "no-invalid-this": "off", - "no-irregular-whitespace": "error", - "no-magic-numbers": "off", - "@typescript-eslint/no-magic-numbers": "off", - "no-new-wrappers": "error", - "no-redeclare": "error", - "no-throw-literal": "error", - "no-trailing-spaces": "error", - "no-undef-init": "error", - "no-underscore-dangle": [ - "error", - { - "allowAfterThis": true - } - ], - "no-unsafe-finally": "error", - "no-unused-labels": "error", - "no-unused-vars": "off", - "no-var": "error", - "object-shorthand": "error", - "one-var": ["off", "never"], - "prefer-arrow/prefer-arrow-functions": [ - "error", - { - "allowStandaloneDeclarations": true - } - ], - "prefer-const": [ - "error", - { - "destructuring": "all" - } - ], - "prefer-object-spread": "error", - "prefer-template": "error", - "quote-props": ["error", "as-needed"], - "radix": "off", - "require-await": "off", - "space-before-function-paren": [ - "error", - { - "anonymous": "never", - "named": "never", - "asyncArrow": "always" - } - ], - "spaced-comment": [ - "error", - "always", - { - "markers": ["/"] + "argsIgnorePattern": "^_" } ], - "use-isnan": "error", - "valid-typeof": "off", - "yoda": "error", - "@typescript-eslint/consistent-type-definitions": "off", - "no-new-func": "off" + // "@typescript-eslint/no-use-before-define": "off", + // "@typescript-eslint/no-var-requires": "off", + // "@typescript-eslint/prefer-as-const": "error", + // "@typescript-eslint/prefer-for-of": "error", + // "@typescript-eslint/prefer-function-type": "error", + // "@typescript-eslint/prefer-namespace-keyword": "error", + // "@typescript-eslint/prefer-regexp-exec": "off", + // "@typescript-eslint/quotes": [ + // "warn", + // "double", + // { + // "avoidEscape": true + // } + // ], + "@typescript-eslint/require-await": "warn", + // "@typescript-eslint/restrict-plus-operands": "error", + "@typescript-eslint/restrict-template-expressions": "warn", + // "@typescript-eslint/semi": ["error", "always"], + // "@typescript-eslint/triple-slash-reference": [ + // "error", + // { + // "path": "always", + // "types": "prefer-import", + // "lib": "always" + // } + // ], + "@typescript-eslint/unbound-method": "warn", + // "@typescript-eslint/unified-signatures": "error", + // "arrow-body-style": "error", + // "arrow-parens": ["error", "as-needed"], + // "brace-style": [ + // "warn", + // "stroustrup", + // { + // "allowSingleLine": true + // } + // ], + // "comma-dangle": [ + // "error", + // { + // "objects": "always-multiline", + // "arrays": "always-multiline", + // "functions": "never" + // } + // ], + // "complexity": "off", + // "constructor-super": "error", + // "curly": ["error", "multi-line"], + // "eol-last": "error", + // "eqeqeq": ["error", "smart"], + // "guard-for-in": "error", + // "id-blacklist": [ + // "error", + // "any", + // "Number", + // "number", + // "String", + // "string", + // "Boolean", + // "boolean", + // "Undefined", + // "undefined" + // ], + // "id-match": "error", + // "import/order": "off", + // "linebreak-style": ["error", "unix"], + // "max-classes-per-file": "off", + // "max-len": [ + // "warn", + // { + // "code": 240 + // } + // ], + // "new-parens": "off", + // "no-array-constructor": "off", + // "no-bitwise": "error", + // "no-caller": "error", + // "no-cond-assign": "off", + // "no-console": "error", + // "no-debugger": "error", + // "no-empty": [ + // "error", + // { + // "allowEmptyCatch": true + // } + // ], + // "no-empty-function": "off", + // "no-eval": "error", + // "no-extra-semi": "off", + // "no-fallthrough": "off", + // "no-implied-eval": "off", + // "no-invalid-this": "off", + // "no-irregular-whitespace": "error", + // "no-magic-numbers": "off", + // "@typescript-eslint/no-magic-numbers": "off", + // "no-new-wrappers": "error", + // "no-redeclare": "error", + // "no-throw-literal": "error", + // "no-trailing-spaces": "error", + "no-undef": "warn", + // "no-undef-init": "error", + // "no-underscore-dangle": [ + // "error", + // { + // "allowAfterThis": true + // } + // ], + // "no-unsafe-finally": "error", + // "no-unused-labels": "error", + // "no-unused-vars": "off", + "no-useless-escape": "warn" + // "no-var": "error", + // "object-shorthand": "error", + // "one-var": ["off", "never"], + // "prefer-arrow/prefer-arrow-functions": [ + // "error", + // { + // "allowStandaloneDeclarations": true + // } + // ], + // "prefer-const": [ + // "error", + // { + // "destructuring": "all" + // } + // ], + // "prefer-object-spread": "error", + // "prefer-template": "error", + // "quote-props": ["error", "as-needed"], + // "radix": "off", + // "require-await": "off", + // "space-before-function-paren": [ + // "error", + // { + // "anonymous": "never", + // "named": "never", + // "asyncArrow": "always" + // } + // ], + // "spaced-comment": [ + // "error", + // "always", + // { + // "markers": ["/"] + // } + // ], + // "use-isnan": "error", + // "valid-typeof": "off", + // "yoda": "error", + // "@typescript-eslint/consistent-type-definitions": "off", + // "no-new-func": "off" }, "ignorePatterns": [ + // "eslintrc.json", "webpack.config.ts", "util/*.ts", "minitests/*.ts", From 45f36df215cbe64ae418c196253bb35834b0aece Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 20 May 2024 20:44:43 -0700 Subject: [PATCH 24/96] feat: add airbnb linter --- package-lock.json | 282 +++++++++++++++++++++++++++------------------- package.json | 7 +- 2 files changed, 169 insertions(+), 120 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3818e4f..84d87cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,6 @@ "name": "reference-network", "version": "0.0.1", "dependencies": { - "@typescript-eslint/eslint-plugin": "^5.59.9", - "@typescript-eslint/parser": "^5.59.9", "esbuild": "^0.18.1", "eslint": "^8.42.0", "eslint-plugin-import": "^2.27.5", @@ -28,7 +26,10 @@ "zotero-types": "^1.0.15" }, "devDependencies": { - "@types/node": "^20.12.7" + "@types/node": "^20.12.7", + "@typescript-eslint/eslint-plugin": "^7.10.0", + "@typescript-eslint/parser": "^7.10.0", + "eslint-config-airbnb-typescript": "^18.0.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -998,31 +999,31 @@ "integrity": "sha512-resGa1LAQLXI1tKDWnggDn7+o6fL4dZo7a8FcQKctBzC937UYp9iogiQonLElYduhzBarUUsMe/ntx/++xYgAA==" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.10.0.tgz", + "integrity": "sha512-PzCr+a/KAef5ZawX7nbyNwBDtM1HdLIT53aSA2DDlxmxMngZ43O8SIePOeX8H5S+FHXeI6t97mTt/dDdzY4Fyw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/type-utils": "7.10.0", + "@typescript-eslint/utils": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -1031,24 +1032,26 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.10.0.tgz", + "integrity": "sha512-2EjZMA0LUW5V5tGQiaa2Gys+nKdfrn2xiTIBLR4fxmPmVSvgPcKNW+AE/ln9k0A4zDUti0J/GZXMDupQoI+e1w==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "debug": "^4.3.4" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -1057,15 +1060,16 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.10.0.tgz", + "integrity": "sha512-7L01/K8W/VGl7noe2mgH0K7BE29Sq6KAbVmxurj8GGaPDZXPr8EEQ2seOeAS+mEV9DnzxBQB6ax6qQQ5C6P4xg==", + "dev": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -1073,24 +1077,25 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.10.0.tgz", + "integrity": "sha512-D7tS4WDkJWrVkuzgm90qYw9RdgBcrWmbbRkrLA4d7Pg3w0ttVGDsvYGV19SH8gPR5L7OtcN5J1hTtyenO9xE9g==", + "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", + "@typescript-eslint/typescript-estree": "7.10.0", + "@typescript-eslint/utils": "7.10.0", "debug": "^4.3.4", - "tsutils": "^3.21.0" + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "*" + "eslint": "^8.56.0" }, "peerDependenciesMeta": { "typescript": { @@ -1099,11 +1104,12 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.10.0.tgz", + "integrity": "sha512-7fNj+Ya35aNyhuqrA1E/VayQX9Elwr8NKZ4WueClR3KwJ7Xx9jcCdOrLW04h51de/+gNbyFMs+IDxh5xIwfbNg==", + "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -1111,20 +1117,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.10.0.tgz", + "integrity": "sha512-LXFnQJjL9XIcxeVfqmNj60YhatpRLt6UhdlFwAkjNc6jSUlK8zQOl1oktAP8PlWFzPQC1jny/8Bai3/HPuvN5g==", + "dev": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/visitor-keys": "7.10.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -1136,41 +1144,63 @@ } } }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.10.0.tgz", + "integrity": "sha512-olzif1Fuo8R8m/qKkzJqT7qwy16CzPRWBvERS0uvyc+DHd8AKbO4Jb7kpAvVzMmZm8TrHnI7hvjN4I05zow+tg==", + "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.10.0", + "@typescript-eslint/types": "7.10.0", + "@typescript-eslint/typescript-estree": "7.10.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^8.56.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.10.0.tgz", + "integrity": "sha512-9ntIVgsi6gg6FIq9xjEO4VQJvwOqA3jaBFQJ/6TK5AvEup2+cECI6Fh7QiBxmfMHXU0V0J4RyPeOU1VDNzl9cg==", + "dev": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" + "@typescript-eslint/types": "7.10.0", + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || >=20.0.0" }, "funding": { "type": "opencollective", @@ -2108,6 +2138,12 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/confusing-browser-globals": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", + "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", + "dev": true + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -2757,6 +2793,48 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "dependencies": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "peerDependencies": { + "eslint": "^7.32.0 || ^8.2.0", + "eslint-plugin-import": "^2.25.2" + } + }, + "node_modules/eslint-config-airbnb-base/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-config-airbnb-typescript": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-18.0.0.tgz", + "integrity": "sha512-oc+Lxzgzsu8FQyFVa4QFaVKiitTYiiW3frB9KYW5OWdPrqFc7FzxgB20hP4cHMlr+MBzGcLl3jnCOVOydL9mIg==", + "dev": true, + "dependencies": { + "eslint-config-airbnb-base": "^15.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^7.0.0", + "@typescript-eslint/parser": "^7.0.0", + "eslint": "^8.56.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -2886,18 +2964,6 @@ "eslint": ">=2.0.0" } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", @@ -3000,14 +3066,6 @@ "node": ">=4.0" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" - } - }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -4919,11 +4977,6 @@ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==" - }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -5377,6 +5430,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.entries": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", + "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/object.fromentries": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", @@ -7005,25 +7072,6 @@ "strip-bom": "^3.0.0" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", diff --git a/package.json b/package.json index a4caa4e..52f7bf5 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,6 @@ }, "homepage": "https://github.com/RaymondWJang/reference-network", "dependencies": { - "@typescript-eslint/eslint-plugin": "^5.59.9", - "@typescript-eslint/parser": "^5.59.9", "esbuild": "^0.18.1", "eslint": "^8.42.0", "eslint-plugin-import": "^2.27.5", @@ -51,6 +49,9 @@ "bootstrapped": true }, "devDependencies": { - "@types/node": "^20.12.7" + "@types/node": "^20.12.7", + "@typescript-eslint/eslint-plugin": "^7.10.0", + "@typescript-eslint/parser": "^7.10.0", + "eslint-config-airbnb-typescript": "^18.0.0" } } From 97b847e22e4de36a013a4da726afc653e7c592f8 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 20 May 2024 20:48:50 -0700 Subject: [PATCH 25/96] chore: linting --- src/bootstrap.ts | 71 +++---- src/client.ts | 47 +++++ src/database/createDB.ts | 4 +- src/orchestrator.ts | 290 ++++++++++++++++++++++++++++ src/os.ts | 17 ++ src/osfile.js | 406 +++++++++++++++++++++++++++++++++++++++ src/reference-network.ts | 65 +++++-- 7 files changed, 829 insertions(+), 71 deletions(-) create mode 100644 src/client.ts create mode 100644 src/orchestrator.ts create mode 100644 src/os.ts create mode 100644 src/osfile.js diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 5857457..4a2632d 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -2,6 +2,19 @@ import { ReferenceNetwork } from "./reference-network"; Zotero.log("Reference Network: Loading bootstrap"); +const BOOTSTRAP_REASONS = { + 1: "APP_STARTUP", + 2: "APP_SHUTDOWN", + 3: "ADDON_ENABLE", + 4: "ADDON_DISABLE", + 5: "ADDON_INSTALL", + 6: "ADDON_UNINSTALL", + 7: "ADDON_UPGRADE", + 8: "ADDON_DOWNGRADE", +} as const; +type ReasonId = keyof typeof BOOTSTRAP_REASONS; +export type Reason = (typeof BOOTSTRAP_REASONS)[ReasonId]; + function log(msg) { Zotero.log(`Reference Network: ${msg}`); } @@ -16,74 +29,34 @@ export async function startup({ resourceURI, rootURI = resourceURI.spec, }) { - log(`Reference Network: Startup`); + log("Reference Network: Startup"); log(`ID: ${id}`); log(`Version: ${version}`); log(`Resource URI: ${resourceURI}`); log(`Root URI: ${rootURI}`); - Zotero.PreferencePanes.register({ + await Zotero.PreferencePanes.register({ pluginID: "reference-network@example.com", - src: rootURI + "preferences.xhtml", - scripts: [rootURI + "prefs.js"], - }); - - log(`Registered preference pane`); + src: `${rootURI}preferences.xhtml`, + scripts: [`${rootURI}prefs.js`], + }).then(() => log("Registered preference pane")); // Add DOM elements to the main Zotero pane - let win = Zotero.getMainWindow(); + const win = Zotero.getMainWindow(); if (win && win.ZoteroPane) { const zp = win.ZoteroPane; const doc = win.document; } - // createElementNS() necessary in Zotero 6; createElement() defaults to HTML in Zotero 7 - // const HTML_NS = "http://www.w3.org/1999/xhtml"; - // const XUL_NS = - // "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - // const link1 = doc.createElementNS(HTML_NS, "link"); - // link1.id = stylesheetID; - // link1.type = "text/css"; - // link1.rel = "stylesheet"; - // link1.href = `${rootURI}style.css`; - // doc.documentElement.appendChild(link1); - - // const menuitem = doc.createElementNS(XUL_NS, "menuitem"); - // menuitem.id = menuitemID; - // menuitem.setAttribute("type", "checkbox"); - // menuitem.setAttribute("data-l10n-id", "make-it-green-instead"); - // menuitem.addEventListener("command", () => { - // Zotero.ReferenceNetwork.toggleGreen( - // menuitem.getAttribute("checked") === "true" - // ); - // }); - // doc.getElementById("menu_viewPopup").appendChild(menuitem); - - // Use strings from reference-network.ftl (Fluent) in Zotero 7 - // const link2 = doc.createElementNS(HTML_NS, "link"); - // link2.id = ftlID; - // link2.rel = "localization"; - // link2.href = "reference-network.ftl"; - // doc.documentElement.appendChild(link2); - // } - Services.scriptloader.loadSubScript(`${rootURI}reference-network.js`); - log(`Loaded reference-network.js`); + log("Loaded reference-network.js"); ReferenceNetwork.init({ id, version, rootURI }); - log(`Initialized Reference Network`); + log("Initialized Reference Network"); } -// export function onMainWindowLoad({ window }) { -// ReferenceNetwork.addToWindow(window); -// } - -// export function onMainWindowUnload({ window }) { -// ReferenceNetwork.removeFromWindow(window); -// } - export function shutdown() { - log(`Reference Network: Shutdown`); + log("Reference Network: Shutdown"); Zotero.ReferenceNetwork = undefined; } diff --git a/src/client.ts b/src/client.ts new file mode 100644 index 0000000..d6d5370 --- /dev/null +++ b/src/client.ts @@ -0,0 +1,47 @@ +declare const Zotero: any; +declare const location: any; + +const worker = typeof location !== "undefined" && location.search; // this is false +export const is7 = worker + ? new URLSearchParams(location.search).get("is7") === "true" // location is chrome://zotero/content/zoteroPane.xhtml + : Zotero.platformMajorVersion >= 102; // 115 on mine, May 20, 2024 --> is7 is true + +function clientname(): string { + if (typeof location !== "undefined" && location.search) { + return new URLSearchParams(location.search).get("clientName"); + } + // if (process.versions.node) return 'Zotero' // testing + if (Zotero.clientName) return Zotero.clientName as string; + if (Zotero.ReferenceNetwork?.clientName) { + return Zotero.ReferenceNetwork.clientName as string; + } + throw new Error("Unable to detect clientName"); +} + +export const platform = { + name: "", + windows: false, + mac: false, + linux: false, +}; + +if (worker) { + platform.name = new URLSearchParams(location.search).get("platform"); + platform.windows = platform.name === "win"; + platform.mac = platform.name === "mac"; + platform.linux = platform.name === "lin"; +} else { + platform.name = Zotero.isWin + ? "win" + : Zotero.isMac + ? "mac" + : Zotero.isLinux + ? "lin" + : "unk"; + platform.windows = Zotero.isWin; + platform.mac = Zotero.isMac; + platform.linux = Zotero.isLinux; +} + +export const clientName = clientname(); +export const client = clientName.toLowerCase().replace("-", ""); diff --git a/src/database/createDB.ts b/src/database/createDB.ts index 6906010..8176709 100644 --- a/src/database/createDB.ts +++ b/src/database/createDB.ts @@ -1,7 +1,7 @@ Zotero.ZoteroDBTest = { - testDBConnection: async function () { + async testDBConnection() { try { - let results = await Zotero.DB.query( + const results = await Zotero.DB.query( "SELECT itemID, title FROM items LIMIT 10" ); Zotero.debug( diff --git a/src/orchestrator.ts b/src/orchestrator.ts new file mode 100644 index 0000000..dbcb4e0 --- /dev/null +++ b/src/orchestrator.ts @@ -0,0 +1,290 @@ +import type { Reason } from "./bootstrap"; + +import { Shim } from "./os"; +import { is7 } from "./client"; + +export type Actor = + | "start" + | "done" + | "auto-export" + | "translators" + | "TeXstudio" + | "abbreviator" + | "keymanager" + | "serializer" + | "cache" + | "sqlite" + | "git-push" + | "citekeysearch"; +export type PhaseID = "startup" | "shutdown"; +const $OS = is7 ? Shim : OS; + +type Handler = ( + reason: Reason, + task?: Task +) => void | string | Promise; + +interface TaskOptions { + description?: string; + startup?: Handler; + shutdown?: Handler; + needs?: Actor[]; +} + +export type Task = { + id: Actor; + description: string; + action: Handler; + needs: Actor[]; + needed: boolean; + + started: number; + finished: number; + milestones: Map; +}; + +export type Progress = ( + phase: string, + name: string, + done: number, + total: number, + message?: string +) => void; + +type Phase = { + started: number; + promises: Partial>>; + tasks: Partial>; +}; + +export class Orchestrator { + public id: string = Zotero.Utilities.generateObjectKey(); + + public started: number = Date.now(); + + public start: Actor = "start"; + + public done: Actor = "done"; + + private resolved = false; + + private phase: Record = { + startup: { + started: 0, + promises: {}, + tasks: {}, + }, + shutdown: { + started: 0, + promises: {}, + tasks: {}, + }, + }; + + public add( + id: Actor, + { startup, shutdown, needs, description }: TaskOptions + ): void { + if (!startup && !shutdown) throw new Error(`${id}: no-op task`); + if (this.phase.startup.tasks[id]) throw new Error(`${id} exists`); + if (id === this.start && needs) + throw new Error("start task cannot have dependencies"); + if (id === this.done && needs) + throw new Error("done task has dependencies auto-assigned"); + + this.phase.startup.tasks[id] = { + id, + description: description || id, + action: startup, + needs: needs || [], + needed: false, + started: 0, + finished: 0, + milestones: new Map(), + }; + + this.phase.shutdown.tasks[id] = { + ...this.phase.startup.tasks[id], + action: shutdown, + needs: [], + milestones: new Map(), + }; + } + + private resolve() { + if (this.resolved) throw new Error("orchestrator: resolve ran twice"); + this.resolved = true; + + const tasks: Task[] = Object.values(this.phase.startup.tasks); + + const has = { + start: this.phase.startup.tasks[this.start], + done: this.phase.startup.tasks[this.done], + }; + + for (const task of tasks) { + if (has.start && task.id !== this.start && !task.needs.length) { + task.needs.push(this.start); + } + + for (const needed of task.needs) { + if (!this.phase.startup.tasks[needed]) { + throw new Error( + `orchestrator: ${task.id} needs non-existent ${needed}` + ); + } + this.phase.startup.tasks[needed].needed = true; + } + + task.needs = [...new Set(task.needs)].sort(); + } + + if (has.done) { + has.done.needs = tasks + .filter((task) => task.id !== this.done && !task.needed) + .map((task) => task.id); + has.done.needs = [...new Set(has.done.needs)].sort(); + } + + for (const task of tasks) { + for (const needed of task.needs) { + const shutdown = this.phase.shutdown.tasks[needed]; + shutdown.needs.push(task.id); + shutdown.needs = [...new Set(shutdown.needs)].sort(); + } + } + + for (const task of Object.values(this.phase.shutdown.tasks)) { + for (const needed of task.needs) { + this.phase.shutdown.tasks[needed].needed = true; + } + } + } + + private async run( + phase: PhaseID, + reason: Reason, + progress?: Progress + ): Promise { + if (this.phase[phase].started) + throw new Error(`orchestrator: re-run of ${phase}`); + this.phase[phase].started = Date.now(); + const taskmap: Partial> = this.phase[phase].tasks; + const tasks: Task[] = Object.values(taskmap); + const { promises } = this.phase[phase]; + + const circular = Promise.reject(new Error("circular dependency")); + circular.catch(() => { + /* ignore */ + }); // prevent unhandled rejection + + const running = (): string[] => + tasks.filter((t: Task) => t.started && !t.finished).map((t) => t.id); + const time = (ts) => new Date(ts).toISOString(); + const line = (name: string, event: string, timestamp: number) => + // eslint-disable-line arrow-body-style + `reference network orchestrator: [${name.padEnd(50, " ")}] ${phase.padEnd( + 10, + " " + )} ${event.padEnd(15, " ")} at ${time( + timestamp + )} running [${running().join(", ")}]`; + const report = (name: string) => { + const task = taskmap[name]; + + for (const timestamp of [...task.milestones.keys()].sort()) { + Zotero.log( + line( + `${this.id}.${name}.${task.milestones.get(timestamp)}`, + "finished", + timestamp + ) + ); + } + Zotero.log( + line( + `${this.id}.${name}`, + task.finished ? "finished" : "started", + task.finished || task.started + ) + ); + + progress?.( + phase, + name, + tasks.filter((t) => t.finished).length, + tasks.length, + (task.finished ? running()[0] : task.description) || task.description + ); + }; + + const run = (name) => { + const promise: Promise = promises[name]; + if (promise != null) return promise; + + const task = taskmap[name]; + const { action, needs } = task; + if (!action) { + promises[name] = Promise.resolve(); + return; + } + + const needed = async () => { + await Promise.all(needs.map(run)); + // for (const dep of needs) await run(dep) + }; + + const perform = async (): Promise => { + Zotero.log(`orchestrator: task ${phase}.${name} starting`); + try { + const res = (await action(reason, task)) as Promise; + Zotero.log(`orchestrator: task ${phase}.${name} finished`); + return await res; + } catch (err) { + Zotero.log( + `orchestrator: error: task ${phase}.${name} failed: ${err}\n${err.stack}` + ); + throw err; + } + }; + + promises[name] = circular; + + return (promises[name] = needed() + .then(async () => { + task.started = Date.now(); + report(name); + + try { + return await perform(); + } finally { + task.finished = Date.now(); + report(name); + } + }) + + .catch((err) => { + Zotero.log( + `orchestrator: ${name}.${phase} ${reason || ""} error: ${err}` + ); + throw err; + })); + }; + + await Promise.all(Object.keys(taskmap).map(run)); + } + + public async startup(reason: Reason, progress?: Progress): Promise { + this.resolve(); + await this.run("startup", reason, progress); + progress?.("startup", "ready", 100, 100, "ready"); + } + + public async shutdown(reason: Reason): Promise { + if (!this.resolved) + throw new Error("orchestrator: shutdown before startup"); + await this.run("shutdown", reason); + } +} + +export const orchestrator = new Orchestrator(); diff --git a/src/os.ts b/src/os.ts new file mode 100644 index 0000000..8a26f78 --- /dev/null +++ b/src/os.ts @@ -0,0 +1,17 @@ +import { is7, platform } from "./client"; + +import { OS as $OS } from "./osfile"; + +export const Shim: any = is7 ? $OS : undefined; + +if (Shim) { + // no idea why it was decided the shim should not accept relative paths + const Path = platform.windows + ? { start: /.*\\/, end: /\\$/ } + : { start: /.*\//, end: /\/$/ }; + Shim.Path.basename = (path: string) => + path && + (Shim.Path.normalize(path) as string) + .replace(Path.end, "") + .replace(Path.start, ""); +} diff --git a/src/osfile.js b/src/osfile.js new file mode 100644 index 0000000..5af7d7c --- /dev/null +++ b/src/osfile.js @@ -0,0 +1,406 @@ +// +// Compatibility shims from the Mozilla codebase +// +export const OS = { + Constants: { + Path: { + get homeDir() { + return FileUtils.getDir("Home", []).path; + }, + + get libDir() { + return FileUtils.getDir("GreBinD", []).path; + }, + + get profileDir() { + return FileUtils.getDir("ProfD", []).path; + }, + + get tmpDir() { + return FileUtils.getDir("TmpD", []).path; + }, + }, + }, + + File: { + DirectoryIterator(path) { + let initialized = false; + const paths = []; + + async function init() { + paths.push(...(await IOUtils.getChildren(path))); + initialized = true; + } + + async function getEntry(path) { + const info = await IOUtils.stat(path); + return { + name: PathUtils.filename(path), + path, + isDir: info.type == "directory", + }; + } + + this.nextBatch = async function (num) { + if (!initialized) { + await init(); + } + const entries = []; + while (paths.length && num > 0) { + entries.push(await getEntry(paths.shift())); + num--; + } + return entries; + }; + + this.forEach = async function (func) { + if (!initialized) { + await init(); + } + let i = 0; + while (paths.length) { + const entry = await getEntry(paths.shift()); + await func(entry, i++, this); + } + }; + + this.close = function () {}; + }, + + Error(msg) { + this.message = msg; + this.stack = new Error().stack; + }, + + copy: wrapWrite(async (src, dest) => IOUtils.copy(src, dest)), + + async exists(path) { + try { + return await IOUtils.exists(path); + } catch (e) { + if (e.message.includes("NS_ERROR_FILE_UNRECOGNIZED_PATH")) { + dump(`${e.message}\n\n${e.stack}\n\n`); + Components.utils.reportError(e); + return false; + } + } + }, + + makeDir: wrapWrite(async (path, options = {}) => { + try { + return await IOUtils.makeDirectory(path, { + ignoreExisting: options.ignoreExisting !== false, + createAncestors: !!options.from, + permissions: options.unixMode, + }); + } catch (e) { + // Broken symlink + if (e.name == "InvalidAccessError") { + if ( + /Could not create directory because the target file(.+) exists and is not a directory/.test( + e.message + ) + ) { + const osFileError = new OS.File.Error(e.message); + osFileError.becauseExists = true; + throw osFileError; + } + } + } + }), + + move: wrapWrite(async (src, dest, options = {}) => { + if (options.noCopy) { + throw new Error("noCopy is no longer supported"); + } + + // Check noOverwrite + let destFileInfo = null; + try { + destFileInfo = await IOUtils.stat(dest); + } catch (e) { + if (e.name != "NotFoundError") { + throw e; + } + } + if (destFileInfo) { + if (destFileInfo.type == "directory") { + throw new Error( + "OS.File.move() destination cannot be a directory -- use IOUtils.move()" + ); + } + if (options.noOverwrite) { + const e = new OS.File.Error(); + e.becauseExists = true; + throw e; + } + } + + return IOUtils.move(src, dest, options); + }), + + async read(path, options = {}) { + if (options.encoding) { + if (!/^utf\-?8$/i.test(options.encoding)) { + throw new Error("Can only read UTF-8"); + } + return IOUtils.readUTF8(path); + } + return IOUtils.read(path, { + maxBytes: options.bytes, + }); + }, + + async remove(path, options = {}) { + return IOUtils.remove(path, options); + }, + + async removeDir(path, options = {}) { + return IOUtils.remove(path, { + recursive: true, + // OS.File.removeDir defaulted to ignoreAbsent: true + ignoreAbsent: options.ignoreAbsent !== false, + }); + }, + + async removeEmptyDir(path) { + return IOUtils.remove(path); + }, + + async setDates(path, atime, mtime) { + if (atime) { + await IOUtils.setAccessTime(path, atime.valueOf()); + } + return IOUtils.setModificationTime( + path, + mtime ? mtime.valueOf() : undefined + ); + }, + + async setPermissions(path, { unixMode, winAttributes } = {}) { + await IOUtils.setPermissions(path, unixMode); + if (winAttributes && Zotero.isWin) { + const { readOnly, hidden, system } = winAttributes; + await IOUtils.setWindowsAttributes(path, { readOnly, hidden, system }); + } + }, + + stat: async function stat(path) { + let info; + try { + info = await IOUtils.stat(path); + } catch (e) { + if (e.name == "NotFoundError") { + const osFileError = new this.Error("File not found"); + osFileError.becauseNoSuchFile = true; + throw osFileError; + } + throw e; + } + return { + isDir: info.type == "directory", + isSymLink: true, // Supposedly was broken in Firefox + size: info.size, + lastAccessDate: new Date(info.lastAccessed), + lastModificationDate: new Date(info.lastModified), + }; + }, + + async unixSymLink(pathTarget, pathCreate) { + if (await IOUtils.exists(pathCreate)) { + const osFileError = new this.Error(`${pathCreate} already exists`); + osFileError.becauseExists = true; + throw osFileError; + } + + // Copy of Zotero.File.createSymlink + const { ctypes } = ChromeUtils.importESModule( + "resource://gre/modules/ctypes.sys.mjs" + ); + + try { + if (Services.appinfo.OS === "Darwin") { + const libc = ctypes.open( + Services.appinfo.OS === "Darwin" ? "libSystem.B.dylib" : "libc.so" + ); + + const symlink = libc.declare( + "symlink", + ctypes.default_abi, + ctypes.int, // return value + ctypes.char.ptr, // target + ctypes.char.ptr // linkpath + ); + + if (symlink(pathTarget, pathCreate)) { + throw new Error(`Failed to create symlink at ${pathCreate}`); + } + } + // The above is failing with "invalid ELF header" for libc.so on GitHub Actions, so + // just use ln -s on non-macOS systems + else { + const ln = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + ln.initWithPath("/bin/ln"); + const process = Cc["@mozilla.org/process/util;1"].createInstance( + Ci.nsIProcess + ); + process.init(ln); + const args = ["-s", pathTarget, pathCreate]; + process.run(true, args, args.length); + } + } catch (e) { + dump(`${e.message}\n\n`); + throw new Error(`Failed to create symlink at ${pathCreate}`); + } + }, + + async writeAtomic(path, bytes, options = {}) { + if (options.backupTo) { + options.backupFile = options.backupTo; + } + if (options.noOverwrite) { + options.mode = "create"; + } + if (options.encoding == "utf-8") { + return IOUtils.writeUTF8(path, bytes, options); + } + return IOUtils.write(path, bytes, options); + }, + }, + + Path: { + basename(path) { + return PathUtils.filename(path); + }, + + dirname(path) { + return PathUtils.parent(path); + }, + + fromFileURI(uri) { + const url = new URL(uri); + if (url.protocol != "file:") { + throw new Error("fromFileURI expects a file URI"); + } + const path = this.normalize(decodeURIComponent(url.pathname)); + return path; + }, + + join(path, ...args) { + const platformSlash = Services.appinfo.OS == "WINNT" ? "\\" : "/"; + try { + if (args.length == 0) { + return path; + } + if (args.length == 1 && args[0].includes(platformSlash)) { + return PathUtils.joinRelative(path, ...args); + } + return PathUtils.join(path, ...args); + } catch (e) { + if (e.message.includes("NS_ERROR_FILE_UNRECOGNIZED_PATH")) { + Cu.reportError(`WARNING: ${e.message} -- update for IOUtils`); + return [path, ...args].join(platformSlash); + } + throw e; + } + }, + + // From Firefox 102 + normalize(path) { + const stack = []; + let absolute; + if (path.length >= 0 && path[0] == "/") { + absolute = true; + } else { + absolute = false; + } + path.split("/").forEach((v) => { + switch (v) { + case "": + case ".": // fallthrough + break; + case "..": + if (!stack.length) { + if (absolute) { + throw new Error( + "Path is ill-formed: attempting to go past root" + ); + } else { + stack.push(".."); + } + } else if (stack[stack.length - 1] == "..") { + stack.push(".."); + } else { + stack.pop(); + } + break; + default: + stack.push(v); + } + }); + const string = stack.join("/"); + return absolute ? `/${string}` : string; + }, + + split(path) { + if (Services.appinfo.OS == "WINNT") { + // winIsAbsolute() + const index = path.indexOf(":"); + const absolute = path.length > index + 1 && path[index + 1] == "\\"; + + return { + absolute, + winDrive: winGetDrive(path), + components: path.split("\\"), + }; + } + + return { + absolute: path.length && path[0] == "/", + components: path.split("/"), + }; + }, + + toFileURI(path) { + return PathUtils.toFileURI(path); + }, + }, +}; + +// From Fx60 ospath_win.jsm +var winGetDrive = function (path) { + if (path == null) { + throw new TypeError("path is invalid"); + } + + if (path.startsWith("\\\\")) { + // UNC path + if (path.length == 2) { + return null; + } + const index = path.indexOf("\\", 2); + if (index == -1) { + return path; + } + return path.slice(0, index); + } + // Non-UNC path + const index = path.indexOf(":"); + if (index <= 0) return null; + return path.slice(0, index + 1); +}; + +function wrapWrite(func) { + return async function () { + try { + return await func(...arguments); + } catch (e) { + if (DOMException.isInstance(e)) { + if (e.name == "NoModificationAllowedError") { + e.becauseExists = true; + } + } + throw e; + } + }; +} diff --git a/src/reference-network.ts b/src/reference-network.ts index c03552b..6b51d0b 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -1,38 +1,63 @@ -// import { Zotero, ReferenceNetwork } from "./types/global"; +import { Shim } from "./os"; +import { is7 } from "./client"; -export let ReferenceNetwork = { +const $OS = is7 ? Shim : OS; + +// import { orchestrator } from "./orchestrator"; +// import type { Reason } from "./bootstrap"; + +export const ReferenceNetwork = { id: null, version: null, rootURI: null, initialized: false, addedElementIDs: [], - init({ id, version, rootURI }) { - if (this.initialized) return; + log: (msg: string): void => { + Zotero.log(`Reference Network: ${msg}`); + }, + + init({ + id, + version, + rootURI, + }: { + id: string; + version: string; + rootURI: string; + }): void { + Zotero.log("Loading Reference Network: starting..."); + if (this.initialized) { + throw new Error("ReferenceNetwork is already running"); + } this.id = id; this.version = version; this.rootURI = rootURI; - this.initialized = true; - }, - log(msg) { - Zotero.log("Reference Network: " + msg); - }, + // Build Directory + this.dir = $OS.Path.join(Zotero.DataDirectory.dir, "reference-network"); + $OS.File.makeDir(this.dir, { ignoreExisting: true }); + + // orchestrator.add("start", { + // description: "zotero", + // startup: async (reason: Reason) => { + // // https://groups.google.com/d/msg/zotero-dev/QYNGxqTSpaQ/uvGObVNlCgAJ + // // this is what really takes long + // await Zotero.initializationPromise; + + // this.dir = $OS.Path.join(Zotero.DataDirectory.dir, "reference-network"); + // await $OS.File.makeDir(this.dir, { ignoreExisting: true }); + // // await Preference.startup(this.dir); + // // Events.startup(); + // }, + // }); - // toggleGreen(enabled) { - // // `window` is the global object in overlay scope - // let docElem = document.documentElement; - // // Element#toggleAttribute() is not supported in Zotero 6 - // if (enabled) { - // docElem.setAttribute("data-green-instead", "true"); - // } else { - // docElem.removeAttribute("data-green-instead"); - // } - // }, + this.initialized = true; + }, async main() { // `window` is the global object in overlay scope - var host = new URL("https://foo.com/path").host; + const { host } = new URL("https://foo.com/path"); this.log(`Host is ${host}`); this.log( From ee62c39f36caf08fc8edc4bbeaa94049e5d288fe Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 20 May 2024 21:01:36 -0700 Subject: [PATCH 26/96] chore: ignore linting esbuild.js --- .eslintrc.json | 3 ++- esbuild.js | 9 +++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index b630401..ba0f2d8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -267,7 +267,8 @@ // "no-new-func": "off" }, "ignorePatterns": [ - // "eslintrc.json", + "eslintrc.json", + "esbuild.js", "webpack.config.ts", "util/*.ts", "minitests/*.ts", diff --git a/esbuild.js b/esbuild.js index 4e9cbbd..0651d43 100644 --- a/esbuild.js +++ b/esbuild.js @@ -74,11 +74,16 @@ async function build() { exportGlobals: true, entryPoints: ["src/bootstrap.ts"], outdir: "build", - banner: { js: "var Zotero;\n" }, + // banner: { js: "var Zotero;\n" }, }); await bundle({ - entryPoints: ["src/reference-network.ts"], + entryPoints: [ + "src/reference-network.ts", + "src/client.ts", + "src/os.ts", + "src/orchestrator.ts", + ], outdir: "build", }); } From efd2496758bdbe94bfc2ced6000240f7495d0cd7 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 20 May 2024 21:28:26 -0700 Subject: [PATCH 27/96] chore: declutter style.css and install.rdf --- src/install.rdf | 34 ---------------------------------- src/style.css | 0 2 files changed, 34 deletions(-) delete mode 100644 src/install.rdf delete mode 100644 src/style.css diff --git a/src/install.rdf b/src/install.rdf deleted file mode 100644 index 7d0764c..0000000 --- a/src/install.rdf +++ /dev/null @@ -1,34 +0,0 @@ - - - - - .reference-network@example.com. - Reference Network - 1.2 - true - https://zotero-download.s3.amazonaws.com/tmp/reference-network/updates-1.1.json - https://github.com/RaymondWJang/reference-network - - - en-US - Reference Network - Reference Network - - - - Raymond W. Chang - 2 - chrome://reference-network/skin/icon.png - chrome://reference-network/skin/icon64.png - true - - - - zotero@chnm.gmu.edu - 6.0 - * - - - - diff --git a/src/style.css b/src/style.css deleted file mode 100644 index e69de29..0000000 From b2a1acbf40ececc5897696d9bbb44523c246ff54 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 20 May 2024 21:29:11 -0700 Subject: [PATCH 28/96] chore: reorganization --- esbuild.js | 6 +++--- src/{ => environment}/client.ts | 0 src/{ => environment}/os.ts | 0 src/{ => environment}/osfile.js | 0 src/{ => helpers}/orchestrator.ts | 6 +++--- 5 files changed, 6 insertions(+), 6 deletions(-) rename src/{ => environment}/client.ts (100%) rename src/{ => environment}/os.ts (100%) rename src/{ => environment}/osfile.js (100%) rename src/{ => helpers}/orchestrator.ts (98%) diff --git a/esbuild.js b/esbuild.js index 0651d43..eaadb99 100644 --- a/esbuild.js +++ b/esbuild.js @@ -80,9 +80,9 @@ async function build() { await bundle({ entryPoints: [ "src/reference-network.ts", - "src/client.ts", - "src/os.ts", - "src/orchestrator.ts", + "src/environment/client.ts", + "src/environment/os.ts", + "src/helpers/orchestrator.ts", ], outdir: "build", }); diff --git a/src/client.ts b/src/environment/client.ts similarity index 100% rename from src/client.ts rename to src/environment/client.ts diff --git a/src/os.ts b/src/environment/os.ts similarity index 100% rename from src/os.ts rename to src/environment/os.ts diff --git a/src/osfile.js b/src/environment/osfile.js similarity index 100% rename from src/osfile.js rename to src/environment/osfile.js diff --git a/src/orchestrator.ts b/src/helpers/orchestrator.ts similarity index 98% rename from src/orchestrator.ts rename to src/helpers/orchestrator.ts index dbcb4e0..478a585 100644 --- a/src/orchestrator.ts +++ b/src/helpers/orchestrator.ts @@ -1,7 +1,7 @@ -import type { Reason } from "./bootstrap"; +import type { Reason } from "../bootstrap"; -import { Shim } from "./os"; -import { is7 } from "./client"; +import { Shim } from "../environment/os"; +import { is7 } from "../environment/client"; export type Actor = | "start" From 0040d713444811e0a2dee58cf23d827782b7fdf5 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 20 May 2024 21:29:33 -0700 Subject: [PATCH 29/96] feat: manually add error tracing :'( --- src/bootstrap.ts | 6 +++--- src/reference-network.ts | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 4a2632d..05296bc 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -16,11 +16,11 @@ type ReasonId = keyof typeof BOOTSTRAP_REASONS; export type Reason = (typeof BOOTSTRAP_REASONS)[ReasonId]; function log(msg) { - Zotero.log(`Reference Network: ${msg}`); + Zotero.log(`Reference Network (bootstrap.ts): ${msg}`); } export function install() { - log("Reference Network: Installed"); + log("Installed"); } export async function startup({ @@ -29,7 +29,7 @@ export async function startup({ resourceURI, rootURI = resourceURI.spec, }) { - log("Reference Network: Startup"); + log("Startup"); log(`ID: ${id}`); log(`Version: ${version}`); log(`Resource URI: ${resourceURI}`); diff --git a/src/reference-network.ts b/src/reference-network.ts index 6b51d0b..9004b6b 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -1,5 +1,5 @@ -import { Shim } from "./os"; -import { is7 } from "./client"; +import { Shim } from "./environment/os"; +import { is7 } from "./environment/client"; const $OS = is7 ? Shim : OS; @@ -14,7 +14,7 @@ export const ReferenceNetwork = { addedElementIDs: [], log: (msg: string): void => { - Zotero.log(`Reference Network: ${msg}`); + Zotero.log(`Reference Network (reference-network.ts): ${msg}`); }, init({ @@ -26,7 +26,7 @@ export const ReferenceNetwork = { version: string; rootURI: string; }): void { - Zotero.log("Loading Reference Network: starting..."); + this.log("Loading ReferenceNetwork: starting..."); if (this.initialized) { throw new Error("ReferenceNetwork is already running"); } @@ -37,6 +37,7 @@ export const ReferenceNetwork = { // Build Directory this.dir = $OS.Path.join(Zotero.DataDirectory.dir, "reference-network"); $OS.File.makeDir(this.dir, { ignoreExisting: true }); + this.log(`Directory created at ${this.dir}`); // orchestrator.add("start", { // description: "zotero", From 1ce3e70a870d7fdf5d6190b14cef7d7c71038468 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 21 May 2024 16:12:03 -0700 Subject: [PATCH 30/96] linting: divide rules between ts and js --- .eslintrc.json | 87 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 60 insertions(+), 27 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index ba0f2d8..d29918c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -8,21 +8,54 @@ "extends": [ // "airbnb-base", // "airbnb-typescript/base" - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/eslint-recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking" + "eslint:recommended" + // "plugin:@typescript-eslint/recommended", + // "plugin:@typescript-eslint/eslint-recommended", + // "plugin:@typescript-eslint/recommended-requiring-type-checking" ], "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": "tsconfig.json", - "sourceType": "module" - }, "plugins": [ "eslint-plugin-import", - "eslint-plugin-prefer-arrow", - "@typescript-eslint", - "@typescript-eslint/eslint-plugin" + "eslint-plugin-prefer-arrow" + // "@typescript-eslint", + // "@typescript-eslint/eslint-plugin" + ], + "overrides": [ + { + "files": ["*.ts", "*.tsx"], + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + ], + "plugins": ["@typescript-eslint", "@typescript-eslint/eslint-plugin"], + "parserOptions": { + "project": "tsconfig.json", + "sourceType": "module" + }, + "rules": { + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-unsafe-argument": "warn", + "@typescript-eslint/no-unsafe-assignment": "warn", + "@typescript-eslint/no-unsafe-call": "warn", + "@typescript-eslint/no-unsafe-member-access": "warn", + "@typescript-eslint/no-unsafe-return": "warn", + "@typescript-eslint/no-unused-expressions": "warn", + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "argsIgnorePattern": "^_" + } + ], + "@typescript-eslint/require-await": "warn", + "@typescript-eslint/restrict-template-expressions": "warn", + "@typescript-eslint/unbound-method": "warn" + } + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } ], "rules": { // "@typescript-eslint/adjacent-overload-signatures": "error", @@ -81,7 +114,7 @@ // "@typescript-eslint/no-array-constructor": "error", // "@typescript-eslint/no-empty-function": "error", // "@typescript-eslint/no-empty-interface": "error", - "@typescript-eslint/no-explicit-any": "warn", + // "@typescript-eslint/no-explicit-any": "warn", // "@typescript-eslint/no-extra-non-null-assertion": "error", // "@typescript-eslint/no-extra-semi": "error", // "@typescript-eslint/no-floating-promises": "error", @@ -101,19 +134,19 @@ // } // ], // "@typescript-eslint/no-this-alias": "error", - "@typescript-eslint/no-unsafe-argument": "warn", + // "@typescript-eslint/no-unsafe-argument": "warn", // "@typescript-eslint/no-unnecessary-type-assertion": "error", - "@typescript-eslint/no-unsafe-assignment": "warn", - "@typescript-eslint/no-unsafe-call": "warn", - "@typescript-eslint/no-unsafe-member-access": "warn", - "@typescript-eslint/no-unsafe-return": "warn", - "@typescript-eslint/no-unused-expressions": "warn", - "@typescript-eslint/no-unused-vars": [ - "warn", - { - "argsIgnorePattern": "^_" - } - ], + // "@typescript-eslint/no-unsafe-assignment": "warn", + // "@typescript-eslint/no-unsafe-call": "warn", + // "@typescript-eslint/no-unsafe-member-access": "warn", + // "@typescript-eslint/no-unsafe-return": "warn", + // "@typescript-eslint/no-unused-expressions": "warn", + // "@typescript-eslint/no-unused-vars": [ + // "warn", + // { + // "argsIgnorePattern": "^_" + // } + // ], // "@typescript-eslint/no-use-before-define": "off", // "@typescript-eslint/no-var-requires": "off", // "@typescript-eslint/prefer-as-const": "error", @@ -128,9 +161,9 @@ // "avoidEscape": true // } // ], - "@typescript-eslint/require-await": "warn", + // "@typescript-eslint/require-await": "warn", // "@typescript-eslint/restrict-plus-operands": "error", - "@typescript-eslint/restrict-template-expressions": "warn", + // "@typescript-eslint/restrict-template-expressions": "warn", // "@typescript-eslint/semi": ["error", "always"], // "@typescript-eslint/triple-slash-reference": [ // "error", @@ -140,7 +173,7 @@ // "lib": "always" // } // ], - "@typescript-eslint/unbound-method": "warn", + // "@typescript-eslint/unbound-method": "warn", // "@typescript-eslint/unified-signatures": "error", // "arrow-body-style": "error", // "arrow-parens": ["error", "as-needed"], From a7dce7203c0d54fa075a3cca45d60b8e3c05f519 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 22 May 2024 19:13:46 -0700 Subject: [PATCH 31/96] feat: translate sql to typeorm --- src/database/entity/Items.ts | 14 ++++++++++++++ src/database/{sql => entity}/authorItem.sql | 0 src/database/entity/authorItem.ts | 20 ++++++++++++++++++++ src/database/{sql => entity}/authors.sql | 0 src/database/entity/authors.ts | 18 ++++++++++++++++++ src/database/{sql => entity}/graph.sql | 0 src/database/entity/graphs.ts | 19 +++++++++++++++++++ src/database/{sql => entity}/items.sql | 0 8 files changed, 71 insertions(+) create mode 100644 src/database/entity/Items.ts rename src/database/{sql => entity}/authorItem.sql (100%) create mode 100644 src/database/entity/authorItem.ts rename src/database/{sql => entity}/authors.sql (100%) create mode 100644 src/database/entity/authors.ts rename src/database/{sql => entity}/graph.sql (100%) create mode 100644 src/database/entity/graphs.ts rename src/database/{sql => entity}/items.sql (100%) diff --git a/src/database/entity/Items.ts b/src/database/entity/Items.ts new file mode 100644 index 0000000..5ea5769 --- /dev/null +++ b/src/database/entity/Items.ts @@ -0,0 +1,14 @@ +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; +import { AuthorItemLink } from "./authorItem"; + +@Entity() +export class Item { + @PrimaryGeneratedColumn() + itemID: number; + + @Column() + updated_datetime: Date; + + @OneToMany(() => AuthorItemLink, (authorItemLink) => authorItemLink.item) + authorItemLinks: AuthorItemLink[]; +} diff --git a/src/database/sql/authorItem.sql b/src/database/entity/authorItem.sql similarity index 100% rename from src/database/sql/authorItem.sql rename to src/database/entity/authorItem.sql diff --git a/src/database/entity/authorItem.ts b/src/database/entity/authorItem.ts new file mode 100644 index 0000000..753e814 --- /dev/null +++ b/src/database/entity/authorItem.ts @@ -0,0 +1,20 @@ +import { Entity, PrimaryGeneratedColumn, JoinColumn, ManyToOne } from "typeorm"; +import { Author } from "./authors"; +import { Item } from "./Items"; + +@Entity("author-item-link") +export class AuthorItemLink { + @PrimaryGeneratedColumn() + creatorID: number; + + @PrimaryGeneratedColumn() + itemID: number; + + @ManyToOne(() => Author, (author) => author.authorItemLinks) + @JoinColumn({ name: "authorID" }) + author: Author; + + @ManyToOne(() => Item, (item) => item.authorItemLinks) + @JoinColumn({ name: "itemID" }) + item: Item; +} diff --git a/src/database/sql/authors.sql b/src/database/entity/authors.sql similarity index 100% rename from src/database/sql/authors.sql rename to src/database/entity/authors.sql diff --git a/src/database/entity/authors.ts b/src/database/entity/authors.ts new file mode 100644 index 0000000..048254c --- /dev/null +++ b/src/database/entity/authors.ts @@ -0,0 +1,18 @@ +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; +import { AuthorItemLink } from "./authorItem"; + +@Entity() +export class Author { + @PrimaryGeneratedColumn() + authorID: number; + + @Column() + ORCID: string; + + @Column() + name: string; + + // Define the inverse side of the relationship + @OneToMany(() => AuthorItemLink, (authorItemLink) => authorItemLink.author) + authorItemLinks: AuthorItemLink[]; +} diff --git a/src/database/sql/graph.sql b/src/database/entity/graph.sql similarity index 100% rename from src/database/sql/graph.sql rename to src/database/entity/graph.sql diff --git a/src/database/entity/graphs.ts b/src/database/entity/graphs.ts new file mode 100644 index 0000000..8e6c1b6 --- /dev/null +++ b/src/database/entity/graphs.ts @@ -0,0 +1,19 @@ +import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; + +@Entity() +export class Graph { + @PrimaryGeneratedColumn() + graphID: number; + + @Column() + source: string; + + @Column() + type: string; + + @Column() + target: string; + + @Column() + data_source: string; +} diff --git a/src/database/sql/items.sql b/src/database/entity/items.sql similarity index 100% rename from src/database/sql/items.sql rename to src/database/entity/items.sql From 6f560d5977cae40031a901f17d0e2769872b69e5 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 22 May 2024 19:14:00 -0700 Subject: [PATCH 32/96] env: add tslib --- package-lock.json | 22 +++++++++------------- package.json | 1 + 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84d87cd..c52a023 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "sqlite": "^5.1.1", "sqlite3": "^5.1.7", "ts-node": "^10.9.1", + "tslib": "^2.6.2", "typeorm": "^0.3.20", "typescript": "^5.1.3", "zotero-plugin": "^1.4.22", @@ -2439,9 +2440,9 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dependencies": { "jake": "^10.8.5" }, @@ -7072,6 +7073,11 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -7321,11 +7327,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/typeorm/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, "node_modules/typescript": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", @@ -8090,11 +8091,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/zotero-plugin/node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, "node_modules/zotero-types": { "version": "1.3.20", "resolved": "https://registry.npmjs.org/zotero-types/-/zotero-types-1.3.20.tgz", diff --git a/package.json b/package.json index 52f7bf5..c9bae1d 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "sqlite": "^5.1.1", "sqlite3": "^5.1.7", "ts-node": "^10.9.1", + "tslib": "^2.6.2", "typeorm": "^0.3.20", "typescript": "^5.1.3", "zotero-plugin": "^1.4.22", From 998dd8a60356d9b214858378b676230bf29f9e40 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 22 May 2024 19:14:21 -0700 Subject: [PATCH 33/96] chore: modify tsconfig for typeorm --- tsconfig.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index e043aa9..414cd1c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,8 +11,11 @@ "sourceMap": false, "downlevelIteration": true, "lib": ["es2017", "dom"], - "typeRoots": ["./node_modules/@types"] + "typeRoots": ["./node_modules/@types"], + "emitDecoratorMetadata": true, + "experimentalDecorators": true }, + // "lib": ["es6"], // TypeORM: You may also need to enable es6 in the lib section of compiler options, or install es6-shim from @types. "include": [ "*.ts", "node_modules/zotero-types", From afada119e5e4e6b2cf0a81a705304a01558ce93c Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 22 May 2024 19:14:42 -0700 Subject: [PATCH 34/96] ???: temporary turn of linting warnings --- .eslintrc.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index d29918c..752b01f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -34,20 +34,20 @@ "sourceType": "module" }, "rules": { - "@typescript-eslint/no-explicit-any": "warn", - "@typescript-eslint/no-unsafe-argument": "warn", - "@typescript-eslint/no-unsafe-assignment": "warn", - "@typescript-eslint/no-unsafe-call": "warn", - "@typescript-eslint/no-unsafe-member-access": "warn", - "@typescript-eslint/no-unsafe-return": "warn", - "@typescript-eslint/no-unused-expressions": "warn", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/no-unused-expressions": "off", "@typescript-eslint/no-unused-vars": [ - "warn", + "off", { "argsIgnorePattern": "^_" } ], - "@typescript-eslint/require-await": "warn", + "@typescript-eslint/require-await": "off", "@typescript-eslint/restrict-template-expressions": "warn", "@typescript-eslint/unbound-method": "warn" } From cc6f92d3651524d7fc0f631d2acf79b05bf392b5 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 22 May 2024 19:15:46 -0700 Subject: [PATCH 35/96] feat: turn init to async --- src/bootstrap.ts | 5 +++-- src/reference-network.ts | 21 ++++++--------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 05296bc..d52c6ef 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -51,8 +51,9 @@ export async function startup({ Services.scriptloader.loadSubScript(`${rootURI}reference-network.js`); log("Loaded reference-network.js"); - ReferenceNetwork.init({ id, version, rootURI }); - log("Initialized Reference Network"); + await ReferenceNetwork.init({ id, version, rootURI }).then(() => + log("Initialized Reference Network") + ); } export function shutdown() { diff --git a/src/reference-network.ts b/src/reference-network.ts index 9004b6b..8d4cb30 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -17,7 +17,7 @@ export const ReferenceNetwork = { Zotero.log(`Reference Network (reference-network.ts): ${msg}`); }, - init({ + async init({ id, version, rootURI, @@ -25,7 +25,7 @@ export const ReferenceNetwork = { id: string; version: string; rootURI: string; - }): void { + }): Promise { this.log("Loading ReferenceNetwork: starting..."); if (this.initialized) { throw new Error("ReferenceNetwork is already running"); @@ -39,19 +39,10 @@ export const ReferenceNetwork = { $OS.File.makeDir(this.dir, { ignoreExisting: true }); this.log(`Directory created at ${this.dir}`); - // orchestrator.add("start", { - // description: "zotero", - // startup: async (reason: Reason) => { - // // https://groups.google.com/d/msg/zotero-dev/QYNGxqTSpaQ/uvGObVNlCgAJ - // // this is what really takes long - // await Zotero.initializationPromise; - - // this.dir = $OS.Path.join(Zotero.DataDirectory.dir, "reference-network"); - // await $OS.File.makeDir(this.dir, { ignoreExisting: true }); - // // await Preference.startup(this.dir); - // // Events.startup(); - // }, - // }); + // Attach New Database + await Zotero.DB.queryAsync("ATTACH DATABASE ? AS referencenetwork", [ + $OS.Path.join(Zotero.DataDirectory.dir, "reference-network.sqlite"), + ]); this.initialized = true; }, From 21aab170db7191c0eaa3d16eb32c5a09938ddf6f Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 22 May 2024 19:16:04 -0700 Subject: [PATCH 36/96] feat: typeorm db gen --- src/database/createDB.ts | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/database/createDB.ts b/src/database/createDB.ts index 8176709..1ce3387 100644 --- a/src/database/createDB.ts +++ b/src/database/createDB.ts @@ -1,26 +1,13 @@ -Zotero.ZoteroDBTest = { - async testDBConnection() { - try { - const results = await Zotero.DB.query( - "SELECT itemID, title FROM items LIMIT 10" - ); - Zotero.debug( - "Connected successfully. Sample items from Zotero's database:" - ); - results.forEach((item) => { - Zotero.debug(`Item ID: ${item.itemID}, Title: ${item.title}`); - }); - } catch (error) { - Zotero.debug("Error querying items table:", error); - } - }, -}; +import "reflect-metadata"; +import { DataSource } from "typeorm"; +import { Item } from "./entity/Items"; -// Running the test function when Zotero starts -if (Zotero.initializationPromise) { - Zotero.initializationPromise.then(() => - Zotero.ZoteroDBTest.testDBConnection() - ); -} else { - Zotero.ZoteroDBTest.testDBConnection(); -} +export const AppDataSource = new DataSource({ + type: "sqlite", + database: "database.sqlite", + synchronize: true, + logging: false, + entities: [Item], + migrations: [], + subscribers: [], +}); From 0258965c7e39c42d12f2b9df5e9a06f61764c469 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 30 May 2024 15:30:21 -0700 Subject: [PATCH 37/96] feat: ok, runs again. some of the comments in the bootstrap doesn't work. --- src/bootstrap.ts | 20 ++++++++++---------- src/database/createDB.ts | 17 +++++++++++++++-- src/reference-network.ts | 6 +++--- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index d52c6ef..18d9466 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -42,18 +42,18 @@ export async function startup({ }).then(() => log("Registered preference pane")); // Add DOM elements to the main Zotero pane - const win = Zotero.getMainWindow(); - if (win && win.ZoteroPane) { - const zp = win.ZoteroPane; - const doc = win.document; - } + // const win = Zotero.getMainWindow(); + // if (win && win.ZoteroPane) { + // const zp = win.ZoteroPane; + // const doc = win.document; + // } - Services.scriptloader.loadSubScript(`${rootURI}reference-network.js`); - log("Loaded reference-network.js"); + // Services.scriptloader.loadSubScript(`${rootURI}reference-network.js`); + // log("Loaded reference-network.js"); - await ReferenceNetwork.init({ id, version, rootURI }).then(() => - log("Initialized Reference Network") - ); + // await ReferenceNetwork.init({ id, version, rootURI }).then(() => + // log("Initialized Reference Network") + // ); } export function shutdown() { diff --git a/src/database/createDB.ts b/src/database/createDB.ts index 1ce3387..b5d827e 100644 --- a/src/database/createDB.ts +++ b/src/database/createDB.ts @@ -1,13 +1,26 @@ +import { AuthorItemLink } from "./entity/authorItem"; +import { Graph } from "./entity/graphs"; +import { Item } from "./entity/Items"; +import { Author } from "./entity/authors"; import "reflect-metadata"; import { DataSource } from "typeorm"; -import { Item } from "./entity/Items"; export const AppDataSource = new DataSource({ type: "sqlite", database: "database.sqlite", synchronize: true, logging: false, - entities: [Item], + entities: [Item, Author, AuthorItemLink, Graph], migrations: [], subscribers: [], }); + +AppDataSource.initialize() + .then(() => { + console.log("Connected to the database!"); + + // Add additional setup or testing code here + }) + .catch((error) => + console.log("Error during Data Source initialization:", error) + ); diff --git a/src/reference-network.ts b/src/reference-network.ts index 8d4cb30..357e674 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -1,5 +1,6 @@ import { Shim } from "./environment/os"; import { is7 } from "./environment/client"; +import { AppDataSource } from "./database/createDB"; const $OS = is7 ? Shim : OS; @@ -40,9 +41,8 @@ export const ReferenceNetwork = { this.log(`Directory created at ${this.dir}`); // Attach New Database - await Zotero.DB.queryAsync("ATTACH DATABASE ? AS referencenetwork", [ - $OS.Path.join(Zotero.DataDirectory.dir, "reference-network.sqlite"), - ]); + this.db = AppDataSource; + this.log("Database attached"); this.initialized = true; }, From 83e41a1ea1dab9b77fef17695e2fa9e2be92b809 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 30 May 2024 16:50:04 -0700 Subject: [PATCH 38/96] chore: remove sql files --- src/database/entity/authorItem.sql | 7 ------- src/database/entity/authors.sql | 5 ----- src/database/entity/graph.sql | 7 ------- src/database/entity/items.sql | 4 ---- 4 files changed, 23 deletions(-) delete mode 100644 src/database/entity/authorItem.sql delete mode 100644 src/database/entity/authors.sql delete mode 100644 src/database/entity/graph.sql delete mode 100644 src/database/entity/items.sql diff --git a/src/database/entity/authorItem.sql b/src/database/entity/authorItem.sql deleted file mode 100644 index 765efba..0000000 --- a/src/database/entity/authorItem.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS author_item_link ( - creatorID INTEGER, - itemID INTEGER, - PRIMARY KEY (creatorID, itemID), - FOREIGN KEY (creatorID) REFERENCES authors (creatorID), - FOREIGN KEY (itemID) REFERENCES items (itemID) -); \ No newline at end of file diff --git a/src/database/entity/authors.sql b/src/database/entity/authors.sql deleted file mode 100644 index 4c0839b..0000000 --- a/src/database/entity/authors.sql +++ /dev/null @@ -1,5 +0,0 @@ -CREATE TABLE IF NOT EXISTS authors ( - creatorID INTEGER PRIMARY KEY, - ORCID TEXT, - name TEXT -); \ No newline at end of file diff --git a/src/database/entity/graph.sql b/src/database/entity/graph.sql deleted file mode 100644 index 3dae457..0000000 --- a/src/database/entity/graph.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS graph ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - source TEXT, - type TEXT, - target TEXT, - data_source TEXT -); \ No newline at end of file diff --git a/src/database/entity/items.sql b/src/database/entity/items.sql deleted file mode 100644 index 3912b1a..0000000 --- a/src/database/entity/items.sql +++ /dev/null @@ -1,4 +0,0 @@ -CREATE TABLE IF NOT EXISTS items ( - itemID INTEGER PRIMARY KEY, - updated_datetime DATETIME -); \ No newline at end of file From b57bb7df408ef56fd069f7ac8cfa63481e499ef8 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 31 May 2024 14:33:05 -0700 Subject: [PATCH 39/96] TODO: How to initialize a db without bombing the bootstrap???? --- src/bootstrap.ts | 21 +++++++++++---------- src/database/createDB.ts | 36 +++++++++++++++--------------------- src/reference-network.ts | 34 +++++++++++++++++++++++++++------- 3 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 18d9466..7aad47b 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -42,18 +42,19 @@ export async function startup({ }).then(() => log("Registered preference pane")); // Add DOM elements to the main Zotero pane - // const win = Zotero.getMainWindow(); - // if (win && win.ZoteroPane) { - // const zp = win.ZoteroPane; - // const doc = win.document; - // } + const win = Zotero.getMainWindow(); + if (win && win.ZoteroPane) { + const zp = win.ZoteroPane; + const doc = win.document; + } - // Services.scriptloader.loadSubScript(`${rootURI}reference-network.js`); - // log("Loaded reference-network.js"); + Services.scriptloader.loadSubScript(`${rootURI}reference-network.js`); + log("Loaded reference-network.js"); - // await ReferenceNetwork.init({ id, version, rootURI }).then(() => - // log("Initialized Reference Network") - // ); + // This line is the problem + await ReferenceNetwork.init({ id, version, rootURI }).then(() => + log("Initialized Reference Network") + ); } export function shutdown() { diff --git a/src/database/createDB.ts b/src/database/createDB.ts index b5d827e..7c67e72 100644 --- a/src/database/createDB.ts +++ b/src/database/createDB.ts @@ -1,26 +1,20 @@ -import { AuthorItemLink } from "./entity/authorItem"; +// import { AuthorItemLink } from "./entity/authorItem"; import { Graph } from "./entity/graphs"; -import { Item } from "./entity/Items"; -import { Author } from "./entity/authors"; +// import { Item } from "./entity/Items"; +// import { Author } from "./entity/authors"; import "reflect-metadata"; import { DataSource } from "typeorm"; -export const AppDataSource = new DataSource({ - type: "sqlite", - database: "database.sqlite", - synchronize: true, - logging: false, - entities: [Item, Author, AuthorItemLink, Graph], - migrations: [], - subscribers: [], -}); +Zotero.log("Reference Network (createDB.ts): Creating database..."); -AppDataSource.initialize() - .then(() => { - console.log("Connected to the database!"); - - // Add additional setup or testing code here - }) - .catch((error) => - console.log("Error during Data Source initialization:", error) - ); +export const AppDataSource = null; +// export const AppDataSource = new DataSource({ +// type: "sqlite", +// database: "database.sqlite", +// synchronize: true, +// logging: false, +// entities: [Graph], +// // entities: [Item, Author, AuthorItemLink, Graph], +// migrations: [], +// subscribers: [], +// }); diff --git a/src/reference-network.ts b/src/reference-network.ts index 357e674..a3bbdae 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -1,6 +1,8 @@ import { Shim } from "./environment/os"; import { is7 } from "./environment/client"; -import { AppDataSource } from "./database/createDB"; +// import { AppDataSource } from "./database/createDB"; +import { DataSource } from "typeorm"; +import { Graph } from "./database/entity/graphs"; const $OS = is7 ? Shim : OS; @@ -40,8 +42,29 @@ export const ReferenceNetwork = { $OS.File.makeDir(this.dir, { ignoreExisting: true }); this.log(`Directory created at ${this.dir}`); - // Attach New Database - this.db = AppDataSource; + // Attach New Database: This is the problem + // ok, so I can't even load it. + this.log(`Creating database...`); + const AppDataSource = new DataSource({ + type: "sqlite", + database: "database.sqlite", + synchronize: true, + logging: false, + entities: [], + migrations: [], + subscribers: [], + }); + // this.log(`Database created`); + + // AppDataSource.initialize() + // .then(() => { + // console.log("Connected to the database!"); + + // // Add additional setup or testing code here + // }) + // .catch((error) => + // console.log("Error during Data Source initialization:", error) + // ); this.log("Database attached"); this.initialized = true; @@ -53,10 +76,7 @@ export const ReferenceNetwork = { this.log(`Host is ${host}`); this.log( - `Intensity is ${Zotero.Prefs.get( - "extensions.make-it-red.intensity", - true - )}` + `reference-network.ts: main() called with ${this.id}, ${this.version}, ${this.rootURI}` ); }, }; From 0ba8347d9145883d95526f93a584028797b32f1d Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 3 Jun 2024 18:50:27 -0700 Subject: [PATCH 40/96] feat: add zotero-toolkit --- package-lock.json | 20 ++++++++++++++++++++ package.json | 1 + 2 files changed, 21 insertions(+) diff --git a/package-lock.json b/package-lock.json index c52a023..c3eb315 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "typeorm": "^0.3.20", "typescript": "^5.1.3", "zotero-plugin": "^1.4.22", + "zotero-plugin-toolkit": "^2.3.32", "zotero-types": "^1.0.15" }, "devDependencies": { @@ -7856,6 +7857,25 @@ "zotero-start": "bin/start.py" } }, + "node_modules/zotero-plugin-toolkit": { + "version": "2.3.32", + "resolved": "https://registry.npmjs.org/zotero-plugin-toolkit/-/zotero-plugin-toolkit-2.3.32.tgz", + "integrity": "sha512-KslBcCiIfEvAjmaU7DsMPYH9JOzwpZaF0psKmx+4FfD31BZfyTbO4VLXdZ7oP22Ezs0bLlEDMFgfjdIQFd2s7A==", + "dependencies": { + "zotero-types": "^2.0.0" + } + }, + "node_modules/zotero-plugin-toolkit/node_modules/zotero-types": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/zotero-types/-/zotero-types-2.0.1.tgz", + "integrity": "sha512-mqL9TeV5yEX1rDO2eI2qzg88Ow+FNJUkPhM9+0vfzL9FWJNtb5KcoPffa71LcOkGTBzCsK+WAVRnYs+c50QzYQ==", + "dependencies": { + "@types/bluebird": "^3.5.42", + "@types/react": "17.0.2", + "epubjs": "^0.3.93", + "pdfjs-dist": "~3.8.0" + } + }, "node_modules/zotero-plugin/node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", diff --git a/package.json b/package.json index c9bae1d..f425f8a 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "typeorm": "^0.3.20", "typescript": "^5.1.3", "zotero-plugin": "^1.4.22", + "zotero-plugin-toolkit": "^2.3.32", "zotero-types": "^1.0.15" }, "xpi": { From 930c888e6b45c665dce1b94d17995654ea36dde1 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 3 Jun 2024 18:51:02 -0700 Subject: [PATCH 41/96] TODO: Cannot figure out why using typeorm breaks bootstrap... :( --- src/database/createDB.ts | 20 +++++++++----------- src/database/entity/graphs.sql | 7 +++++++ 2 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 src/database/entity/graphs.sql diff --git a/src/database/createDB.ts b/src/database/createDB.ts index 7c67e72..f6efc1a 100644 --- a/src/database/createDB.ts +++ b/src/database/createDB.ts @@ -7,14 +7,12 @@ import { DataSource } from "typeorm"; Zotero.log("Reference Network (createDB.ts): Creating database..."); -export const AppDataSource = null; -// export const AppDataSource = new DataSource({ -// type: "sqlite", -// database: "database.sqlite", -// synchronize: true, -// logging: false, -// entities: [Graph], -// // entities: [Item, Author, AuthorItemLink, Graph], -// migrations: [], -// subscribers: [], -// }); +export const AppDataSource = new DataSource({ + type: "sqlite", + database: "db.sqlite", + synchronize: true, + logging: true, + entities: [Graph], // [Item, Author, AuthorItemLink, Graph], + migrations: [], + subscribers: [], +}); diff --git a/src/database/entity/graphs.sql b/src/database/entity/graphs.sql new file mode 100644 index 0000000..3dae457 --- /dev/null +++ b/src/database/entity/graphs.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS graph ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + source TEXT, + type TEXT, + target TEXT, + data_source TEXT +); \ No newline at end of file From 745f2776ef03b5b5b88421dc97abe084c4e0a23e Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 3 Jun 2024 18:51:42 -0700 Subject: [PATCH 42/96] feat: can run query to make db/table/add rows with inline queries --- src/reference-network.ts | 87 ++++++++++++++++++++++++++++++++-------- 1 file changed, 71 insertions(+), 16 deletions(-) diff --git a/src/reference-network.ts b/src/reference-network.ts index a3bbdae..aeb3e43 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -1,14 +1,18 @@ import { Shim } from "./environment/os"; import { is7 } from "./environment/client"; // import { AppDataSource } from "./database/createDB"; -import { DataSource } from "typeorm"; -import { Graph } from "./database/entity/graphs"; +import { join } from "path"; +import ZoteroToolkit from "zotero-plugin-toolkit"; +/* Alternatively, import class you need to minify the plugin size + * ```ts + * import { BasicTool } from "zotero-plugin-toolkit/dist/basic"; + * import { UITool } from "zotero-plugin-toolkit/dist/tools/ui"; + */ +const ztoolkit = new ZoteroToolkit(); +ztoolkit.log("This is Zotero:", ztoolkit.getGlobal("Zotero")); const $OS = is7 ? Shim : OS; -// import { orchestrator } from "./orchestrator"; -// import type { Reason } from "./bootstrap"; - export const ReferenceNetwork = { id: null, version: null, @@ -45,17 +49,68 @@ export const ReferenceNetwork = { // Attach New Database: This is the problem // ok, so I can't even load it. this.log(`Creating database...`); - const AppDataSource = new DataSource({ - type: "sqlite", - database: "database.sqlite", - synchronize: true, - logging: false, - entities: [], - migrations: [], - subscribers: [], - }); - // this.log(`Database created`); + await Zotero.DB.queryAsync("ATTACH DATABASE ? AS referencenetwork", [ + $OS.Path.join(Zotero.DataDirectory.dir, "reference-network.sqlite"), + ]); + const tables: Record = {}; + for (const table of await Zotero.DB.columnQueryAsync( + `SELECT LOWER(REPLACE(name, '-', '')) + FROM referencenetwork.sqlite_master + WHERE type = 'table';` + )) { + tables[table] = true; + } + + if (tables.graph) { + this.log("Graph table exists"); + await Zotero.DB.queryAsync("DROP TABLE referencenetwork.graph"); + this.log("Graph table dropped"); + delete tables.graph; + } + + // Generate a Graph Table and add a few rows + if (!tables.graph) { + this.log("Creating Graph table..."); + await Zotero.DB.queryAsync(` + CREATE TABLE referencenetwork.graph ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + source TEXT, + type TEXT, + target TEXT, + data_source TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `); + this.log("Graph table created"); + } + // if graphs table is empty, add some rows + if ( + (await Zotero.DB.valueQueryAsync( + "SELECT COUNT(*) FROM referencenetwork.graph" + )) === 0 + ) { + this.log("Adding rows to Graph table..."); + await Zotero.DB.queryAsync(` + INSERT INTO referencenetwork.graph (source, type, target, data_source) + VALUES ('source1', 'type1', 'target1', 'data_source1'); + `); + await Zotero.DB.queryAsync(` + INSERT INTO referencenetwork.graph (source, type, target, data_source) + VALUES ('source2', 'type2', 'target2', 'data_source2'); + `); + await Zotero.DB.queryAsync(` + INSERT INTO referencenetwork.graph (source, type, target, data_source) + VALUES ('source3', 'type3', 'target3', 'data_source3'); + `); + this.log("Rows added to Graph table"); + } + + // for (const ddl of sqlStatements) { + // await connection.query(ddl); + // } + // this.db = new Zotero.DBConnection("referencenetwork"); // AppDataSource.initialize() // .then(() => { // console.log("Connected to the database!"); @@ -65,7 +120,7 @@ export const ReferenceNetwork = { // .catch((error) => // console.log("Error during Data Source initialization:", error) // ); - this.log("Database attached"); + // this.log("Database attached"); this.initialized = true; }, From e85bcc339e5eb709bbfd32ec4bad72b589b58b43 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 3 Jun 2024 18:55:59 -0700 Subject: [PATCH 43/96] refactor: let's do UI later --- src/reference-network.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/reference-network.ts b/src/reference-network.ts index aeb3e43..2cb6b00 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -1,15 +1,6 @@ import { Shim } from "./environment/os"; import { is7 } from "./environment/client"; // import { AppDataSource } from "./database/createDB"; -import { join } from "path"; -import ZoteroToolkit from "zotero-plugin-toolkit"; -/* Alternatively, import class you need to minify the plugin size - * ```ts - * import { BasicTool } from "zotero-plugin-toolkit/dist/basic"; - * import { UITool } from "zotero-plugin-toolkit/dist/tools/ui"; - */ -const ztoolkit = new ZoteroToolkit(); -ztoolkit.log("This is Zotero:", ztoolkit.getGlobal("Zotero")); const $OS = is7 ? Shim : OS; From 0af0ede6df7613b1c0568e7b98064f4269c841df Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 6 Jun 2024 11:55:04 -0700 Subject: [PATCH 44/96] TODO: can't import anything from typeORM. Find an alternative --- src/bootstrap.ts | 38 ++++++++++++++++++++++++++++++++++++++ src/reference-network.ts | 16 +++++----------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 7aad47b..936d518 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,5 +1,30 @@ +// import { DataSource } from "typeorm"; +// check is typeorm is importable +// import { Graph } from "./database/entity/graphs"; +import { OS as $OS } from "./environment/osfile"; import { ReferenceNetwork } from "./reference-network"; +// async function initializeTypeORM() { +// const AppDataSource = new DataSource({ +// type: "sqlite", +// database: $OS.Path.join( +// Zotero.DataDirectory.dir, +// "reference-network.sqlite" +// ), +// entities: [Graph], +// synchronize: true, +// }); + +// try { +// await AppDataSource.initialize(); +// Zotero.log("TypeORM DataSource initialized successfully."); +// return AppDataSource; +// } catch (error) { +// Zotero.log(`Error initializing TypeORM DataSource: ${error.message}`); +// throw error; // Re-throw if you want Zotero to handle it or handle it here +// } +// } + Zotero.log("Reference Network: Loading bootstrap"); const BOOTSTRAP_REASONS = { @@ -29,6 +54,19 @@ export async function startup({ resourceURI, rootURI = resourceURI.spec, }) { + // try { + // // Initialize TypeORM DataSource + // const dataSource = await initializeTypeORM(); + + // // Proceed with Zotero's startup process + // // Place other startup tasks here + // Zotero.log("Zotero started successfully."); + // } catch (error) { + // // Handle any errors that occur during initialization + // Zotero.log(`Error during startup: ${error.message}`); + // } + // log(typeof Graph); --> This throws an error + // log(typeof DataSource); --> This throws an error, as well log("Startup"); log(`ID: ${id}`); log(`Version: ${version}`); diff --git a/src/reference-network.ts b/src/reference-network.ts index 2cb6b00..38f1281 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -82,19 +82,13 @@ export const ReferenceNetwork = { )) === 0 ) { this.log("Adding rows to Graph table..."); - await Zotero.DB.queryAsync(` - INSERT INTO referencenetwork.graph (source, type, target, data_source) - VALUES ('source1', 'type1', 'target1', 'data_source1'); - `); - await Zotero.DB.queryAsync(` - INSERT INTO referencenetwork.graph (source, type, target, data_source) - VALUES ('source2', 'type2', 'target2', 'data_source2'); - `); - await Zotero.DB.queryAsync(` + for (let i = 0; i < 10; i++) { + await Zotero.DB.queryAsync(` INSERT INTO referencenetwork.graph (source, type, target, data_source) - VALUES ('source3', 'type3', 'target3', 'data_source3'); + VALUES ('source${i}', 'type${i}', 'target${i}', 'data_source${i}'); `); - this.log("Rows added to Graph table"); + this.log("Rows added to Graph table"); + } } // for (const ddl of sqlStatements) { From be6f874598e54ffe51f0392572e5c520b0427122 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 7 Jun 2024 15:16:05 -0700 Subject: [PATCH 45/96] feat: remove node-fetch --- package-lock.json | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3eb315..62853e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5017,26 +5017,6 @@ "node": "^16 || ^18 || >= 20" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "optional": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/node-gyp": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", From 75aa3781831e0c9d8cf00f16e6f03982216939f6 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 12:05:02 -0700 Subject: [PATCH 46/96] feat: move sql to .ts --- src/database/createDB.ts | 18 ----- src/database/databaseManager.ts | 106 ++++++++++++++++++++++++++++++ src/database/entities.ts | 89 +++++++++++++++++++++++++ src/database/entity/Items.ts | 14 ---- src/database/entity/authorItem.ts | 20 ------ src/database/entity/authors.ts | 18 ----- src/database/entity/graphs.sql | 7 -- src/database/entity/graphs.ts | 19 ------ src/database/queries.ts | 11 ++++ 9 files changed, 206 insertions(+), 96 deletions(-) delete mode 100644 src/database/createDB.ts create mode 100644 src/database/databaseManager.ts create mode 100644 src/database/entities.ts delete mode 100644 src/database/entity/Items.ts delete mode 100644 src/database/entity/authorItem.ts delete mode 100644 src/database/entity/authors.ts delete mode 100644 src/database/entity/graphs.sql delete mode 100644 src/database/entity/graphs.ts create mode 100644 src/database/queries.ts diff --git a/src/database/createDB.ts b/src/database/createDB.ts deleted file mode 100644 index f6efc1a..0000000 --- a/src/database/createDB.ts +++ /dev/null @@ -1,18 +0,0 @@ -// import { AuthorItemLink } from "./entity/authorItem"; -import { Graph } from "./entity/graphs"; -// import { Item } from "./entity/Items"; -// import { Author } from "./entity/authors"; -import "reflect-metadata"; -import { DataSource } from "typeorm"; - -Zotero.log("Reference Network (createDB.ts): Creating database..."); - -export const AppDataSource = new DataSource({ - type: "sqlite", - database: "db.sqlite", - synchronize: true, - logging: true, - entities: [Graph], // [Item, Author, AuthorItemLink, Graph], - migrations: [], - subscribers: [], -}); diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts new file mode 100644 index 0000000..2cc06f8 --- /dev/null +++ b/src/database/databaseManager.ts @@ -0,0 +1,106 @@ +import { Shim } from "../environment/os"; + +export class DatabaseManager { + private dir: string; + private dbPath: string; + + constructor(private rootDir: string) { + this.dir = Shim.Path.join(Zotero.DataDirectory.dir, "reference-network"); + this.dbPath = Shim.Path.join(this.dir, "reference-network.sqlite"); + } + + async initializeDatabase(): Promise { + // Ensure directory exists + Shim.File.makeDir(this.dir, { ignoreExisting: true }); + Zotero.log(`Directory created at ${this.dir}`); + + // Attach database + await Zotero.DB.queryAsync("ATTACH DATABASE ? AS referencenetwork", [ + this.dbPath, + ]); + Zotero.log(`Database attached from ${this.dbPath}`); + + // Check for existing tables and create if necessary + await this.createGraphTable(); + } + + private async createGraphTable(): Promise { + const exists = await Zotero.DB.valueQueryAsync( + "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='graph';" + ); + + if (exists) { + Zotero.log("Graph table exists"); + await Zotero.DB.queryAsync("DROP TABLE referencenetwork.graph"); + Zotero.log("Graph table dropped"); + } + + Zotero.log("Creating Graph table..."); + await Zotero.DB.queryAsync(` + CREATE TABLE referencenetwork.graph ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + source TEXT, + type TEXT, + target TEXT, + data_source TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `); + Zotero.log("Graph table created"); + } + + async addGraphRow( + source: string, + type: string, + target: string, + dataSource: string + ): Promise { + await Zotero.DB.queryAsync( + ` + INSERT INTO referencenetwork.graph (source, type, target, data_source) + VALUES (?, ?, ?, ?); + `, + [source, type, target, dataSource] + ); + } + + async getGraphRows(): Promise { + return await Zotero.DB.queryAsync("SELECT * FROM referencenetwork.graph"); + } + + async getGraphRow(id: number): Promise { + return await Zotero.DB.queryAsync( + "SELECT * FROM referencenetwork.graph WHERE id = ?", + [id] + ); + } + + async updateGraphRow( + id: number, + source: string, + type: string, + target: string, + dataSource: string + ): Promise { + await Zotero.DB.queryAsync( + ` + UPDATE referencenetwork.graph + SET source = ?, type = ?, target = ?, data_source = ? + WHERE id = ?; + `, + [source, type, target, dataSource, id] + ); + } + + async deleteGraphRow(id: number): Promise { + await Zotero.DB.queryAsync( + "DELETE FROM referencenetwork.graph WHERE id = ?", + [id] + ); + } + + async deleteGraphRows(): Promise { + await Zotero.DB.queryAsync("DELETE FROM referencenetwork.graph"); + } +} diff --git a/src/database/entities.ts b/src/database/entities.ts new file mode 100644 index 0000000..1795592 --- /dev/null +++ b/src/database/entities.ts @@ -0,0 +1,89 @@ +export const SQLQueries = { + authors: ` + CREATE TABLE IF NOT EXISTS referencenetwork.author ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + ORCID TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + graphs: ` + CREATE TABLE IF NOT EXISTS referencenetwork.graph ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + source TEXT, + type TEXT, + target TEXT, + data_source TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + items: ` + CREATE TABLE IF NOT EXISTS referencenetwork.item ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + itemID TEXT, + title TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + itemAuthors: ` + CREATE TABLE IF NOT EXISTS referencenetwork.item_author ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + itemID TEXT, + authorID TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + itemData: ` + CREATE TABLE IF NOT EXISTS referencenetwork.item_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + itemID TEXT, + fieldID TEXT, + valueID TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + itemDataValues: ` + CREATE TABLE IF NOT EXISTS referencenetwork.item_data_values ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + value TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + itemFields: ` + CREATE TABLE IF NOT EXISTS referencenetwork.item_fields ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + fieldName TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + itemTypes: ` + CREATE TABLE IF NOT EXISTS referencenetwork.item_types ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + typeName TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + libraries: ` + CREATE TABLE IF NOT EXISTS referencenetwork.library ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + libraryID TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + tags: ` + CREATE TABLE IF NOT EXISTS referencenetwork.tag ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + tagID TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, +}; diff --git a/src/database/entity/Items.ts b/src/database/entity/Items.ts deleted file mode 100644 index 5ea5769..0000000 --- a/src/database/entity/Items.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; -import { AuthorItemLink } from "./authorItem"; - -@Entity() -export class Item { - @PrimaryGeneratedColumn() - itemID: number; - - @Column() - updated_datetime: Date; - - @OneToMany(() => AuthorItemLink, (authorItemLink) => authorItemLink.item) - authorItemLinks: AuthorItemLink[]; -} diff --git a/src/database/entity/authorItem.ts b/src/database/entity/authorItem.ts deleted file mode 100644 index 753e814..0000000 --- a/src/database/entity/authorItem.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Entity, PrimaryGeneratedColumn, JoinColumn, ManyToOne } from "typeorm"; -import { Author } from "./authors"; -import { Item } from "./Items"; - -@Entity("author-item-link") -export class AuthorItemLink { - @PrimaryGeneratedColumn() - creatorID: number; - - @PrimaryGeneratedColumn() - itemID: number; - - @ManyToOne(() => Author, (author) => author.authorItemLinks) - @JoinColumn({ name: "authorID" }) - author: Author; - - @ManyToOne(() => Item, (item) => item.authorItemLinks) - @JoinColumn({ name: "itemID" }) - item: Item; -} diff --git a/src/database/entity/authors.ts b/src/database/entity/authors.ts deleted file mode 100644 index 048254c..0000000 --- a/src/database/entity/authors.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; -import { AuthorItemLink } from "./authorItem"; - -@Entity() -export class Author { - @PrimaryGeneratedColumn() - authorID: number; - - @Column() - ORCID: string; - - @Column() - name: string; - - // Define the inverse side of the relationship - @OneToMany(() => AuthorItemLink, (authorItemLink) => authorItemLink.author) - authorItemLinks: AuthorItemLink[]; -} diff --git a/src/database/entity/graphs.sql b/src/database/entity/graphs.sql deleted file mode 100644 index 3dae457..0000000 --- a/src/database/entity/graphs.sql +++ /dev/null @@ -1,7 +0,0 @@ -CREATE TABLE IF NOT EXISTS graph ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - source TEXT, - type TEXT, - target TEXT, - data_source TEXT -); \ No newline at end of file diff --git a/src/database/entity/graphs.ts b/src/database/entity/graphs.ts deleted file mode 100644 index 8e6c1b6..0000000 --- a/src/database/entity/graphs.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; - -@Entity() -export class Graph { - @PrimaryGeneratedColumn() - graphID: number; - - @Column() - source: string; - - @Column() - type: string; - - @Column() - target: string; - - @Column() - data_source: string; -} diff --git a/src/database/queries.ts b/src/database/queries.ts new file mode 100644 index 0000000..00b6df5 --- /dev/null +++ b/src/database/queries.ts @@ -0,0 +1,11 @@ +export const SQLQueries = { + getDOIs: ` + SELECT value -- iD.itemID, iDV.valueID, fieldName + FROM itemDataValues AS iDV + LEFT JOIN itemData AS iD ON iDV.valueID = iD.valueID + LEFT JOIN fields AS f ON iD.fieldID = f.fieldID + LEFT JOIN items AS i ON iD.itemID = i.itemID + WHERE fieldName = 'DOI' + AND libraryID = 1; + `, +}; From ea37d90bc03e4a6cb7e999263e76250f3423d494 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 14:25:18 -0700 Subject: [PATCH 47/96] feat: clarify entity/queries names --- src/database/entities.ts | 2 +- src/database/queries.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/entities.ts b/src/database/entities.ts index 1795592..4a6a468 100644 --- a/src/database/entities.ts +++ b/src/database/entities.ts @@ -1,4 +1,4 @@ -export const SQLQueries = { +export const entities = { authors: ` CREATE TABLE IF NOT EXISTS referencenetwork.author ( id INTEGER PRIMARY KEY AUTOINCREMENT, diff --git a/src/database/queries.ts b/src/database/queries.ts index 00b6df5..4e55367 100644 --- a/src/database/queries.ts +++ b/src/database/queries.ts @@ -1,4 +1,4 @@ -export const SQLQueries = { +export const queries = { getDOIs: ` SELECT value -- iD.itemID, iDV.valueID, fieldName FROM itemDataValues AS iDV From aac40af27a0256397a3eda79fb3c33f4998b3795 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 14:25:47 -0700 Subject: [PATCH 48/96] feat: remove deprecate comments --- src/bootstrap.ts | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 936d518..7aad47b 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,30 +1,5 @@ -// import { DataSource } from "typeorm"; -// check is typeorm is importable -// import { Graph } from "./database/entity/graphs"; -import { OS as $OS } from "./environment/osfile"; import { ReferenceNetwork } from "./reference-network"; -// async function initializeTypeORM() { -// const AppDataSource = new DataSource({ -// type: "sqlite", -// database: $OS.Path.join( -// Zotero.DataDirectory.dir, -// "reference-network.sqlite" -// ), -// entities: [Graph], -// synchronize: true, -// }); - -// try { -// await AppDataSource.initialize(); -// Zotero.log("TypeORM DataSource initialized successfully."); -// return AppDataSource; -// } catch (error) { -// Zotero.log(`Error initializing TypeORM DataSource: ${error.message}`); -// throw error; // Re-throw if you want Zotero to handle it or handle it here -// } -// } - Zotero.log("Reference Network: Loading bootstrap"); const BOOTSTRAP_REASONS = { @@ -54,19 +29,6 @@ export async function startup({ resourceURI, rootURI = resourceURI.spec, }) { - // try { - // // Initialize TypeORM DataSource - // const dataSource = await initializeTypeORM(); - - // // Proceed with Zotero's startup process - // // Place other startup tasks here - // Zotero.log("Zotero started successfully."); - // } catch (error) { - // // Handle any errors that occur during initialization - // Zotero.log(`Error during startup: ${error.message}`); - // } - // log(typeof Graph); --> This throws an error - // log(typeof DataSource); --> This throws an error, as well log("Startup"); log(`ID: ${id}`); log(`Version: ${version}`); From d55d653fb512493e701f4e68367960c3c535d60a Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 14:26:32 -0700 Subject: [PATCH 49/96] feat: port from typeorm to .ts dicts --- src/database/apiManager.ts | 25 ++++++++++ src/reference-network.ts | 100 ++++++++++++------------------------- 2 files changed, 58 insertions(+), 67 deletions(-) create mode 100644 src/database/apiManager.ts diff --git a/src/database/apiManager.ts b/src/database/apiManager.ts new file mode 100644 index 0000000..c765a58 --- /dev/null +++ b/src/database/apiManager.ts @@ -0,0 +1,25 @@ +export class ApiManager { + private apiUrl: string = "https://api.openalex.org/works/"; + private userAgent: string; + + constructor(apiUrl: string, userAgent: string) { + this.apiUrl = apiUrl; + this.userAgent = userAgent; + } + + async fetchData(doi: string): Promise { + const url = this.apiUrl + `https://doi.org/${encodeURIComponent(doi)}`; + try { + const response = await Zotero.HTTP.request("GET", url, { + headers: { + "User-Agent": this.userAgent, + }, + }); + const data = JSON.parse(response.responseText); + return data; + } catch (error) { + Zotero.logError(error); + throw error; // Re-throw to handle it in the calling function + } + } +} diff --git a/src/reference-network.ts b/src/reference-network.ts index 38f1281..958a05e 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -1,8 +1,6 @@ -import { Shim } from "./environment/os"; -import { is7 } from "./environment/client"; -// import { AppDataSource } from "./database/createDB"; - -const $OS = is7 ? Shim : OS; +import { queries } from "./database/queries"; +import { DatabaseManager } from "./database/databaseManager"; +import { ApiManager } from "./database/apiManager"; export const ReferenceNetwork = { id: null, @@ -10,11 +8,16 @@ export const ReferenceNetwork = { rootURI: null, initialized: false, addedElementIDs: [], + dbManager: new DatabaseManager(Zotero.DataDirectory.dir), log: (msg: string): void => { Zotero.log(`Reference Network (reference-network.ts): ${msg}`); }, + error: (msg: string, e: Error): void => { + Zotero.logError(e); + }, + async init({ id, version, @@ -32,50 +35,8 @@ export const ReferenceNetwork = { this.version = version; this.rootURI = rootURI; - // Build Directory - this.dir = $OS.Path.join(Zotero.DataDirectory.dir, "reference-network"); - $OS.File.makeDir(this.dir, { ignoreExisting: true }); - this.log(`Directory created at ${this.dir}`); - - // Attach New Database: This is the problem - // ok, so I can't even load it. - this.log(`Creating database...`); - await Zotero.DB.queryAsync("ATTACH DATABASE ? AS referencenetwork", [ - $OS.Path.join(Zotero.DataDirectory.dir, "reference-network.sqlite"), - ]); - const tables: Record = {}; - for (const table of await Zotero.DB.columnQueryAsync( - `SELECT LOWER(REPLACE(name, '-', '')) - FROM referencenetwork.sqlite_master - WHERE type = 'table';` - )) { - tables[table] = true; - } - - if (tables.graph) { - this.log("Graph table exists"); - await Zotero.DB.queryAsync("DROP TABLE referencenetwork.graph"); - this.log("Graph table dropped"); - delete tables.graph; - } + await this.dbManager.initializeDatabase(); - // Generate a Graph Table and add a few rows - if (!tables.graph) { - this.log("Creating Graph table..."); - await Zotero.DB.queryAsync(` - CREATE TABLE referencenetwork.graph ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - source TEXT, - type TEXT, - target TEXT, - data_source TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `); - this.log("Graph table created"); - } - // if graphs table is empty, add some rows if ( (await Zotero.DB.valueQueryAsync( "SELECT COUNT(*) FROM referencenetwork.graph" @@ -83,29 +44,34 @@ export const ReferenceNetwork = { ) { this.log("Adding rows to Graph table..."); for (let i = 0; i < 10; i++) { - await Zotero.DB.queryAsync(` - INSERT INTO referencenetwork.graph (source, type, target, data_source) - VALUES ('source${i}', 'type${i}', 'target${i}', 'data_source${i}'); - `); - this.log("Rows added to Graph table"); + await this.dbManager.addGraphRow( + `source-${i}`, + `type-${i}`, + `target-${i}`, + `data_source-${i}` + ); } - } + this.log("Rows added to Graph table"); - // for (const ddl of sqlStatements) { - // await connection.query(ddl); - // } + this.log("Grabbing all DOI's from Zotero..."); + const dois = await Zotero.DB.columnQueryAsync(queries.getDOIs); + this.log( + "DOI's grabbed: " + dois.length + "\nexamples: " + dois.slice(0, 5) + ); - // this.db = new Zotero.DBConnection("referencenetwork"); - // AppDataSource.initialize() - // .then(() => { - // console.log("Connected to the database!"); + const data = {}; - // // Add additional setup or testing code here - // }) - // .catch((error) => - // console.log("Error during Data Source initialization:", error) - // ); - // this.log("Database attached"); + const apiManager = new ApiManager("raymond.w.jang@gmail.com"); + + for (const doi of dois) { + // there is a better way to do it, by submitting all requests at once + try { + data[doi] = await apiManager.fetchData(doi); + } catch (e) { + this.error(`Error fetching data for DOI ${doi}`, e); + } + } + } this.initialized = true; }, From fd8f1c9d82e97655c6e737a6baaf6763840379a3 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 14:26:49 -0700 Subject: [PATCH 50/96] feat: flesh out databasemanager --- src/database/databaseManager.ts | 55 ++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts index 2cc06f8..f6685c2 100644 --- a/src/database/databaseManager.ts +++ b/src/database/databaseManager.ts @@ -1,3 +1,4 @@ +import { entities } from "./entities"; import { Shim } from "../environment/os"; export class DatabaseManager { @@ -21,33 +22,43 @@ export class DatabaseManager { Zotero.log(`Database attached from ${this.dbPath}`); // Check for existing tables and create if necessary - await this.createGraphTable(); + await this.forceCreateTable("referencenetwork", "graph"); } - private async createGraphTable(): Promise { - const exists = await Zotero.DB.valueQueryAsync( - "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='graph';" + private async checkTableExists( + schemaName: string, + tableName: string + ): Promise { + return await Zotero.DB.valueQueryAsync( + `SELECT COUNT(*) FROM ${schemaName}.sqlite_master WHERE type='table' AND name=${tableName}` ); + } - if (exists) { - Zotero.log("Graph table exists"); - await Zotero.DB.queryAsync("DROP TABLE referencenetwork.graph"); - Zotero.log("Graph table dropped"); - } + private async dropTable( + schemaName: string, + tableName: string + ): Promise { + Zotero.log(`Dropping ${tableName} table...`); + await Zotero.DB.queryAsync(`DROP TABLE ${schemaName}.${tableName};`); + } - Zotero.log("Creating Graph table..."); - await Zotero.DB.queryAsync(` - CREATE TABLE referencenetwork.graph ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - source TEXT, - type TEXT, - target TEXT, - data_source TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `); - Zotero.log("Graph table created"); + private async createTable( + schemaName: string, + tableName: string + ): Promise { + Zotero.log(`Creating ${tableName} table...`); + await Zotero.DB.queryAsync(entities[tableName]); + Zotero.log(`${tableName} created`); + } + + private async forceCreateTable( + schemaName: string, + tableName: string + ): Promise { + if (await this.checkTableExists(schemaName, tableName)) { + await this.dropTable(schemaName, tableName); + } + await this.createTable(schemaName, tableName); } async addGraphRow( From 9da0104fbec32cca9af2254bdd0d5f9e452f6e65 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 16:25:26 -0700 Subject: [PATCH 51/96] feat: align entity names with tables --- src/database/entities.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/database/entities.ts b/src/database/entities.ts index 4a6a468..0c76d1d 100644 --- a/src/database/entities.ts +++ b/src/database/entities.ts @@ -1,6 +1,6 @@ export const entities = { authors: ` - CREATE TABLE IF NOT EXISTS referencenetwork.author ( + CREATE TABLE IF NOT EXISTS referencenetwork.authors ( id INTEGER PRIMARY KEY AUTOINCREMENT, ORCID TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP, @@ -8,7 +8,7 @@ export const entities = { ); `, graphs: ` - CREATE TABLE IF NOT EXISTS referencenetwork.graph ( + CREATE TABLE IF NOT EXISTS referencenetwork.graphs ( id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT, type TEXT, @@ -19,7 +19,7 @@ export const entities = { ); `, items: ` - CREATE TABLE IF NOT EXISTS referencenetwork.item ( + CREATE TABLE IF NOT EXISTS referencenetwork.items ( id INTEGER PRIMARY KEY AUTOINCREMENT, itemID TEXT, title TEXT, @@ -28,7 +28,7 @@ export const entities = { ); `, itemAuthors: ` - CREATE TABLE IF NOT EXISTS referencenetwork.item_author ( + CREATE TABLE IF NOT EXISTS referencenetwork.item_authors ( id INTEGER PRIMARY KEY AUTOINCREMENT, itemID TEXT, authorID TEXT, @@ -71,7 +71,7 @@ export const entities = { ); `, libraries: ` - CREATE TABLE IF NOT EXISTS referencenetwork.library ( + CREATE TABLE IF NOT EXISTS referencenetwork.libraries ( id INTEGER PRIMARY KEY AUTOINCREMENT, libraryID TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP, @@ -79,7 +79,7 @@ export const entities = { ); `, tags: ` - CREATE TABLE IF NOT EXISTS referencenetwork.tag ( + CREATE TABLE IF NOT EXISTS referencenetwork.tags ( id INTEGER PRIMARY KEY AUTOINCREMENT, tagID TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP, From 2e63f2b50246b3f3c70f3072f95ff72e7b9fd4b0 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 16:26:04 -0700 Subject: [PATCH 52/96] feat: align entity names with tables --- src/database/databaseManager.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts index f6685c2..ecacbd0 100644 --- a/src/database/databaseManager.ts +++ b/src/database/databaseManager.ts @@ -22,7 +22,7 @@ export class DatabaseManager { Zotero.log(`Database attached from ${this.dbPath}`); // Check for existing tables and create if necessary - await this.forceCreateTable("referencenetwork", "graph"); + await this.forceCreateTable("referencenetwork", "graphs"); } private async checkTableExists( @@ -30,7 +30,7 @@ export class DatabaseManager { tableName: string ): Promise { return await Zotero.DB.valueQueryAsync( - `SELECT COUNT(*) FROM ${schemaName}.sqlite_master WHERE type='table' AND name=${tableName}` + `SELECT COUNT(*) FROM ${schemaName}.sqlite_master WHERE type='table' AND name='${tableName}'` ); } @@ -47,6 +47,7 @@ export class DatabaseManager { tableName: string ): Promise { Zotero.log(`Creating ${tableName} table...`); + Zotero.log(entities[tableName]); await Zotero.DB.queryAsync(entities[tableName]); Zotero.log(`${tableName} created`); } @@ -69,7 +70,7 @@ export class DatabaseManager { ): Promise { await Zotero.DB.queryAsync( ` - INSERT INTO referencenetwork.graph (source, type, target, data_source) + INSERT INTO referencenetwork.graphs (source, type, target, data_source) VALUES (?, ?, ?, ?); `, [source, type, target, dataSource] @@ -77,12 +78,12 @@ export class DatabaseManager { } async getGraphRows(): Promise { - return await Zotero.DB.queryAsync("SELECT * FROM referencenetwork.graph"); + return await Zotero.DB.queryAsync("SELECT * FROM referencenetwork.graphs"); } async getGraphRow(id: number): Promise { return await Zotero.DB.queryAsync( - "SELECT * FROM referencenetwork.graph WHERE id = ?", + "SELECT * FROM referencenetwork.graphs WHERE id = ?", [id] ); } @@ -96,7 +97,7 @@ export class DatabaseManager { ): Promise { await Zotero.DB.queryAsync( ` - UPDATE referencenetwork.graph + UPDATE referencenetwork.graphs SET source = ?, type = ?, target = ?, data_source = ? WHERE id = ?; `, @@ -106,12 +107,12 @@ export class DatabaseManager { async deleteGraphRow(id: number): Promise { await Zotero.DB.queryAsync( - "DELETE FROM referencenetwork.graph WHERE id = ?", + "DELETE FROM referencenetwork.graphs WHERE id = ?", [id] ); } async deleteGraphRows(): Promise { - await Zotero.DB.queryAsync("DELETE FROM referencenetwork.graph"); + await Zotero.DB.queryAsync("DELETE FROM referencenetwork.graphs"); } } From 5451dc6bcb2247bba719143e781c762abb9d49a8 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 16:26:17 -0700 Subject: [PATCH 53/96] feat: add batch doi fetch --- src/database/apiManager.ts | 46 +++++++++++++++++++++++++++++++++++--- src/reference-network.ts | 26 ++++++++++----------- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/database/apiManager.ts b/src/database/apiManager.ts index c765a58..bb21288 100644 --- a/src/database/apiManager.ts +++ b/src/database/apiManager.ts @@ -1,5 +1,5 @@ export class ApiManager { - private apiUrl: string = "https://api.openalex.org/works/"; + private apiUrl: string = "https://api.openalex.org/"; private userAgent: string; constructor(apiUrl: string, userAgent: string) { @@ -7,8 +7,9 @@ export class ApiManager { this.userAgent = userAgent; } - async fetchData(doi: string): Promise { - const url = this.apiUrl + `https://doi.org/${encodeURIComponent(doi)}`; + async fetchDOI(doi: string): Promise { + const url = + this.apiUrl + `works/https://doi.org/${encodeURIComponent(doi)}`; try { const response = await Zotero.HTTP.request("GET", url, { headers: { @@ -22,4 +23,43 @@ export class ApiManager { throw error; // Re-throw to handle it in the calling function } } + + async fetchDOIs(dois: string[], batchSize: number = 50): Promise { + let url: string = ""; + const data = []; // Array to collect data from all batches + + while (dois.length > batchSize) { + url = + this.apiUrl + `works?filter=doi:${dois.slice(0, batchSize).join("|")}`; + try { + const response = await Zotero.HTTP.request("GET", url, { + headers: { + "User-Agent": this.userAgent, + }, + }); + const dataBatch = JSON.parse(response.responseText); + data.push(dataBatch); // Store the batch data + } catch (error) { + Zotero.logError(error); + throw error; + } + dois = dois.slice(batchSize); + } + + url = this.apiUrl + `works?filter=doi:${dois.join("|")}`; + try { + const response = await Zotero.HTTP.request("GET", url, { + headers: { + "User-Agent": this.userAgent, + }, + }); + const dataBatch = JSON.parse(response.responseText); + data.push(dataBatch); // Store the last batch data + } catch (error) { + Zotero.logError(error); + throw error; + } + + return data; + } } diff --git a/src/reference-network.ts b/src/reference-network.ts index 958a05e..6324156 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -34,15 +34,16 @@ export const ReferenceNetwork = { this.id = id; this.version = version; this.rootURI = rootURI; + let data = []; await this.dbManager.initializeDatabase(); if ( (await Zotero.DB.valueQueryAsync( - "SELECT COUNT(*) FROM referencenetwork.graph" + "SELECT COUNT(*) FROM referencenetwork.graphs" )) === 0 ) { - this.log("Adding rows to Graph table..."); + this.log("Adding rows to Graphs table..."); for (let i = 0; i < 10; i++) { await this.dbManager.addGraphRow( `source-${i}`, @@ -59,20 +60,19 @@ export const ReferenceNetwork = { "DOI's grabbed: " + dois.length + "\nexamples: " + dois.slice(0, 5) ); - const data = {}; - - const apiManager = new ApiManager("raymond.w.jang@gmail.com"); + const apiManager = new ApiManager( + "https://api.openalex.org/", + "raymond.w.jang@gmail.com" + ); - for (const doi of dois) { - // there is a better way to do it, by submitting all requests at once - try { - data[doi] = await apiManager.fetchData(doi); - } catch (e) { - this.error(`Error fetching data for DOI ${doi}`, e); - } + try { + data = await apiManager.fetchDOIs(dois, 25); + } catch (e) { + this.error(e); } } - + Zotero.log(`data length is: ${Object.keys(data[0])}`); + Zotero.log(`${JSON.stringify(data[0].meta)}`); this.initialized = true; }, From b7e7380a48aeec738325e60c6ae4a4c59cd4736b Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 18:03:40 -0700 Subject: [PATCH 54/96] refactor: condense apimanager --- src/database/apiManager.ts | 40 +++++++++++++------------------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/src/database/apiManager.ts b/src/database/apiManager.ts index bb21288..4e75b79 100644 --- a/src/database/apiManager.ts +++ b/src/database/apiManager.ts @@ -1,10 +1,14 @@ export class ApiManager { private apiUrl: string = "https://api.openalex.org/"; private userAgent: string; + private headers: Record; constructor(apiUrl: string, userAgent: string) { this.apiUrl = apiUrl; this.userAgent = userAgent; + this.headers = { + "User-Agent": this.userAgent, + }; } async fetchDOI(doi: string): Promise { @@ -12,9 +16,7 @@ export class ApiManager { this.apiUrl + `works/https://doi.org/${encodeURIComponent(doi)}`; try { const response = await Zotero.HTTP.request("GET", url, { - headers: { - "User-Agent": this.userAgent, - }, + headers: this.headers, }); const data = JSON.parse(response.responseText); return data; @@ -25,39 +27,25 @@ export class ApiManager { } async fetchDOIs(dois: string[], batchSize: number = 50): Promise { - let url: string = ""; - const data = []; // Array to collect data from all batches + const data = []; + let index = 0; + + while (index < dois.length) { + const batchDOIs = dois.slice(index, index + batchSize); + const url = this.apiUrl + `works?filter=doi:${batchDOIs.join("|")}`; - while (dois.length > batchSize) { - url = - this.apiUrl + `works?filter=doi:${dois.slice(0, batchSize).join("|")}`; try { const response = await Zotero.HTTP.request("GET", url, { - headers: { - "User-Agent": this.userAgent, - }, + headers: this.headers, }); const dataBatch = JSON.parse(response.responseText); data.push(dataBatch); // Store the batch data } catch (error) { Zotero.logError(error); - throw error; + throw error; // Re-throw the error after logging it } - dois = dois.slice(batchSize); - } - url = this.apiUrl + `works?filter=doi:${dois.join("|")}`; - try { - const response = await Zotero.HTTP.request("GET", url, { - headers: { - "User-Agent": this.userAgent, - }, - }); - const dataBatch = JSON.parse(response.responseText); - data.push(dataBatch); // Store the last batch data - } catch (error) { - Zotero.logError(error); - throw error; + index += batchSize; // Move the index forward by batchSize } return data; From 14023493a83519d2a22d8985938207499f7ab6e9 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 18:04:20 -0700 Subject: [PATCH 55/96] refactor: condense ref-network --- src/reference-network.ts | 111 +++++++++++++++++---------------------- 1 file changed, 48 insertions(+), 63 deletions(-) diff --git a/src/reference-network.ts b/src/reference-network.ts index 6324156..67a786c 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -2,87 +2,72 @@ import { queries } from "./database/queries"; import { DatabaseManager } from "./database/databaseManager"; import { ApiManager } from "./database/apiManager"; -export const ReferenceNetwork = { - id: null, - version: null, - rootURI: null, - initialized: false, - addedElementIDs: [], - dbManager: new DatabaseManager(Zotero.DataDirectory.dir), +// Define an interface for initialization options +interface InitOptions { + id: string; + version: string; + rootURI: string; +} - log: (msg: string): void => { +export class ReferenceNetwork { + // I guess this is the .h of the __init__ method in Python 😭 + private id: string | null = null; + private version: string | null = null; + private rootURI: string | null = null; + private initialized: boolean = false; + private dbManager: DatabaseManager; + private apiManager: ApiManager; + + constructor() { + // I guess this is the .c of the __init__ method in Python 😭 + this.dbManager = new DatabaseManager(Zotero.DataDirectory.dir); + this.apiManager = new ApiManager( + "https://api.openalex.org/", + "raymond.w.jang@gmail.com" + ); + } + + private log(msg: string): void { Zotero.log(`Reference Network (reference-network.ts): ${msg}`); - }, + } - error: (msg: string, e: Error): void => { + private error(msg: string, e: Error): void { Zotero.logError(e); - }, + } - async init({ - id, - version, - rootURI, - }: { - id: string; - version: string; - rootURI: string; - }): Promise { + async init(options: InitOptions): Promise { this.log("Loading ReferenceNetwork: starting..."); if (this.initialized) { throw new Error("ReferenceNetwork is already running"); } - this.id = id; - this.version = version; - this.rootURI = rootURI; - let data = []; + this.id = options.id; + this.version = options.version; + this.rootURI = options.rootURI; await this.dbManager.initializeDatabase(); - if ( - (await Zotero.DB.valueQueryAsync( - "SELECT COUNT(*) FROM referencenetwork.graphs" - )) === 0 - ) { - this.log("Adding rows to Graphs table..."); - for (let i = 0; i < 10; i++) { - await this.dbManager.addGraphRow( - `source-${i}`, - `type-${i}`, - `target-${i}`, - `data_source-${i}` - ); - } - this.log("Rows added to Graph table"); - - this.log("Grabbing all DOI's from Zotero..."); - const dois = await Zotero.DB.columnQueryAsync(queries.getDOIs); - this.log( - "DOI's grabbed: " + dois.length + "\nexamples: " + dois.slice(0, 5) - ); - - const apiManager = new ApiManager( - "https://api.openalex.org/", - "raymond.w.jang@gmail.com" - ); + this.log("Grabbing all DOI's from Zotero..."); + const dois = await Zotero.DB.columnQueryAsync(queries.getDOIs); + this.log( + "DOI's grabbed: " + dois.length + "\nexamples: " + dois.slice(0, 5) + ); - try { - data = await apiManager.fetchDOIs(dois, 25); - } catch (e) { - this.error(e); - } + try { + const data = await this.apiManager.fetchDOIs(dois, 25); + this.log(`Data length is: ${Object.keys(data[0]).length}`); + this.log(`Data: ${JSON.stringify(data[0].meta)}`); + } catch (e) { + this.error("Failed to fetch DOIs", e); } - Zotero.log(`data length is: ${Object.keys(data[0])}`); - Zotero.log(`${JSON.stringify(data[0].meta)}`); + this.initialized = true; - }, + } async main() { - // `window` is the global object in overlay scope const { host } = new URL("https://foo.com/path"); this.log(`Host is ${host}`); - this.log( - `reference-network.ts: main() called with ${this.id}, ${this.version}, ${this.rootURI}` + `Main called with ID: ${this.id}, Version: ${this.version}, Root URI: ${this.rootURI}` ); - }, -}; + } +} From 05fd1ff523a7e3028bd11d94ef8bc5ea8ab2d465 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 18:33:02 -0700 Subject: [PATCH 56/96] TODO: figure out loadSubScript --- src/bootstrap.ts | 57 +++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 7aad47b..1147fa5 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,6 +1,4 @@ -import { ReferenceNetwork } from "./reference-network"; - -Zotero.log("Reference Network: Loading bootstrap"); +// import { ReferenceNetwork } from "./reference-network"; const BOOTSTRAP_REASONS = { 1: "APP_STARTUP", @@ -15,11 +13,11 @@ const BOOTSTRAP_REASONS = { type ReasonId = keyof typeof BOOTSTRAP_REASONS; export type Reason = (typeof BOOTSTRAP_REASONS)[ReasonId]; -function log(msg) { +function log(msg: string): void { Zotero.log(`Reference Network (bootstrap.ts): ${msg}`); } -export function install() { +export function install(): void { log("Installed"); } @@ -35,26 +33,41 @@ export async function startup({ log(`Resource URI: ${resourceURI}`); log(`Root URI: ${rootURI}`); - await Zotero.PreferencePanes.register({ - pluginID: "reference-network@example.com", - src: `${rootURI}preferences.xhtml`, - scripts: [`${rootURI}prefs.js`], - }).then(() => log("Registered preference pane")); + try { + await Zotero.PreferencePanes.register({ + pluginID: "reference-network@example.com", + src: `${rootURI}preferences.xhtml`, + scripts: [`${rootURI}prefs.js`], + }); + log("Registered preference pane"); - // Add DOM elements to the main Zotero pane - const win = Zotero.getMainWindow(); - if (win && win.ZoteroPane) { - const zp = win.ZoteroPane; - const doc = win.document; - } + // Add DOM elements to the main Zotero pane + const win = Zotero.getMainWindow(); + if (win && win.ZoteroPane) { + const zp = win.ZoteroPane; + const doc = win.document; + } - Services.scriptloader.loadSubScript(`${rootURI}reference-network.js`); - log("Loaded reference-network.js"); + // • Extensions and XUL Applications: Load additional scripts dynamically in response to certain events or conditions. + // This is particularly useful for extensions that need to modify their behavior without reloading the entire extension or application. + // • Modular Development: Allows developers to organize code into separate files and load them as needed, + // rather than loading all scripts at startup. + const scope = {}; + Services.scriptloader.loadSubScript( + `${rootURI}reference-network.js`, + scope + ); - // This line is the problem - await ReferenceNetwork.init({ id, version, rootURI }).then(() => - log("Initialized Reference Network") - ); + if (scope.ReferenceNetwork) { + const referenceNetwork = new scope.ReferenceNetwork(); + await referenceNetwork.init({ id, version, rootURI }); + log("Initialized Reference Network"); + } else { + log("Reference Network not found"); + } + } catch (error) { + log("Error during startup: " + error.message); + } } export function shutdown() { From 75201ff163ef652a0d449d1285204829a4fcb5b2 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 10 Jun 2024 18:36:53 -0700 Subject: [PATCH 57/96] feat: revert to normal import --- src/bootstrap.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 1147fa5..cd15905 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,4 +1,4 @@ -// import { ReferenceNetwork } from "./reference-network"; +import { ReferenceNetwork } from "./reference-network"; const BOOTSTRAP_REASONS = { 1: "APP_STARTUP", @@ -48,23 +48,15 @@ export async function startup({ const doc = win.document; } - // • Extensions and XUL Applications: Load additional scripts dynamically in response to certain events or conditions. - // This is particularly useful for extensions that need to modify their behavior without reloading the entire extension or application. - // • Modular Development: Allows developers to organize code into separate files and load them as needed, - // rather than loading all scripts at startup. const scope = {}; Services.scriptloader.loadSubScript( `${rootURI}reference-network.js`, scope ); - if (scope.ReferenceNetwork) { - const referenceNetwork = new scope.ReferenceNetwork(); - await referenceNetwork.init({ id, version, rootURI }); - log("Initialized Reference Network"); - } else { - log("Reference Network not found"); - } + const referenceNetwork = new scope.ReferenceNetwork(); + await referenceNetwork.init({ id, version, rootURI }); + log("Initialized Reference Network"); } catch (error) { log("Error during startup: " + error.message); } From 2f1e872bc16951bfe3067c3ac45f49e493510fb8 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 11 Jun 2024 10:12:59 -0700 Subject: [PATCH 58/96] TODO: no clue what this main function does... --- src/reference-network.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/reference-network.ts b/src/reference-network.ts index 67a786c..ce0f328 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -62,12 +62,12 @@ export class ReferenceNetwork { this.initialized = true; } +} - async main() { - const { host } = new URL("https://foo.com/path"); - this.log(`Host is ${host}`); - this.log( - `Main called with ID: ${this.id}, Version: ${this.version}, Root URI: ${this.rootURI}` - ); - } +function main() { + const { host } = new URL("https://foo.com/path"); + this.log(`Host is ${host}`); + this.log( + `Main called with ID: ${this.id}, Version: ${this.version}, Root URI: ${this.rootURI}` + ); } From 7918321bec9100b996f22308a7a8553af25719a5 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 11 Jun 2024 13:55:37 -0700 Subject: [PATCH 59/96] feat: revert to vanilla import --- src/bootstrap.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index cd15905..c302423 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -48,13 +48,14 @@ export async function startup({ const doc = win.document; } - const scope = {}; - Services.scriptloader.loadSubScript( - `${rootURI}reference-network.js`, - scope - ); + // const scope = {}; + // Services.scriptloader.loadSubScript( + // `${rootURI}reference-network.js`, + // scope + // ); - const referenceNetwork = new scope.ReferenceNetwork(); + // const referenceNetwork = new scope.ReferenceNetwork(); + const referenceNetwork = new ReferenceNetwork(); await referenceNetwork.init({ id, version, rootURI }); log("Initialized Reference Network"); } catch (error) { From 3e4044c2d879c32321a10b69b3adf50271b421f3 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 11 Jun 2024 14:03:24 -0700 Subject: [PATCH 60/96] chore: declutter --- esbuild.js | 2 +- src/bootstrap.ts | 7 - src/helpers/orchestrator.ts | 290 ------------------------ src/{reference_network => }/ui/menus.ts | 0 src/{reference_network => }/ui/ui.ts | 0 5 files changed, 1 insertion(+), 298 deletions(-) delete mode 100644 src/helpers/orchestrator.ts rename src/{reference_network => }/ui/menus.ts (100%) rename src/{reference_network => }/ui/ui.ts (100%) diff --git a/esbuild.js b/esbuild.js index eaadb99..84b4e28 100644 --- a/esbuild.js +++ b/esbuild.js @@ -82,7 +82,7 @@ async function build() { "src/reference-network.ts", "src/environment/client.ts", "src/environment/os.ts", - "src/helpers/orchestrator.ts", + // "src/helpers/orchestrator.ts", ], outdir: "build", }); diff --git a/src/bootstrap.ts b/src/bootstrap.ts index c302423..d487ca7 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -48,13 +48,6 @@ export async function startup({ const doc = win.document; } - // const scope = {}; - // Services.scriptloader.loadSubScript( - // `${rootURI}reference-network.js`, - // scope - // ); - - // const referenceNetwork = new scope.ReferenceNetwork(); const referenceNetwork = new ReferenceNetwork(); await referenceNetwork.init({ id, version, rootURI }); log("Initialized Reference Network"); diff --git a/src/helpers/orchestrator.ts b/src/helpers/orchestrator.ts deleted file mode 100644 index 478a585..0000000 --- a/src/helpers/orchestrator.ts +++ /dev/null @@ -1,290 +0,0 @@ -import type { Reason } from "../bootstrap"; - -import { Shim } from "../environment/os"; -import { is7 } from "../environment/client"; - -export type Actor = - | "start" - | "done" - | "auto-export" - | "translators" - | "TeXstudio" - | "abbreviator" - | "keymanager" - | "serializer" - | "cache" - | "sqlite" - | "git-push" - | "citekeysearch"; -export type PhaseID = "startup" | "shutdown"; -const $OS = is7 ? Shim : OS; - -type Handler = ( - reason: Reason, - task?: Task -) => void | string | Promise; - -interface TaskOptions { - description?: string; - startup?: Handler; - shutdown?: Handler; - needs?: Actor[]; -} - -export type Task = { - id: Actor; - description: string; - action: Handler; - needs: Actor[]; - needed: boolean; - - started: number; - finished: number; - milestones: Map; -}; - -export type Progress = ( - phase: string, - name: string, - done: number, - total: number, - message?: string -) => void; - -type Phase = { - started: number; - promises: Partial>>; - tasks: Partial>; -}; - -export class Orchestrator { - public id: string = Zotero.Utilities.generateObjectKey(); - - public started: number = Date.now(); - - public start: Actor = "start"; - - public done: Actor = "done"; - - private resolved = false; - - private phase: Record = { - startup: { - started: 0, - promises: {}, - tasks: {}, - }, - shutdown: { - started: 0, - promises: {}, - tasks: {}, - }, - }; - - public add( - id: Actor, - { startup, shutdown, needs, description }: TaskOptions - ): void { - if (!startup && !shutdown) throw new Error(`${id}: no-op task`); - if (this.phase.startup.tasks[id]) throw new Error(`${id} exists`); - if (id === this.start && needs) - throw new Error("start task cannot have dependencies"); - if (id === this.done && needs) - throw new Error("done task has dependencies auto-assigned"); - - this.phase.startup.tasks[id] = { - id, - description: description || id, - action: startup, - needs: needs || [], - needed: false, - started: 0, - finished: 0, - milestones: new Map(), - }; - - this.phase.shutdown.tasks[id] = { - ...this.phase.startup.tasks[id], - action: shutdown, - needs: [], - milestones: new Map(), - }; - } - - private resolve() { - if (this.resolved) throw new Error("orchestrator: resolve ran twice"); - this.resolved = true; - - const tasks: Task[] = Object.values(this.phase.startup.tasks); - - const has = { - start: this.phase.startup.tasks[this.start], - done: this.phase.startup.tasks[this.done], - }; - - for (const task of tasks) { - if (has.start && task.id !== this.start && !task.needs.length) { - task.needs.push(this.start); - } - - for (const needed of task.needs) { - if (!this.phase.startup.tasks[needed]) { - throw new Error( - `orchestrator: ${task.id} needs non-existent ${needed}` - ); - } - this.phase.startup.tasks[needed].needed = true; - } - - task.needs = [...new Set(task.needs)].sort(); - } - - if (has.done) { - has.done.needs = tasks - .filter((task) => task.id !== this.done && !task.needed) - .map((task) => task.id); - has.done.needs = [...new Set(has.done.needs)].sort(); - } - - for (const task of tasks) { - for (const needed of task.needs) { - const shutdown = this.phase.shutdown.tasks[needed]; - shutdown.needs.push(task.id); - shutdown.needs = [...new Set(shutdown.needs)].sort(); - } - } - - for (const task of Object.values(this.phase.shutdown.tasks)) { - for (const needed of task.needs) { - this.phase.shutdown.tasks[needed].needed = true; - } - } - } - - private async run( - phase: PhaseID, - reason: Reason, - progress?: Progress - ): Promise { - if (this.phase[phase].started) - throw new Error(`orchestrator: re-run of ${phase}`); - this.phase[phase].started = Date.now(); - const taskmap: Partial> = this.phase[phase].tasks; - const tasks: Task[] = Object.values(taskmap); - const { promises } = this.phase[phase]; - - const circular = Promise.reject(new Error("circular dependency")); - circular.catch(() => { - /* ignore */ - }); // prevent unhandled rejection - - const running = (): string[] => - tasks.filter((t: Task) => t.started && !t.finished).map((t) => t.id); - const time = (ts) => new Date(ts).toISOString(); - const line = (name: string, event: string, timestamp: number) => - // eslint-disable-line arrow-body-style - `reference network orchestrator: [${name.padEnd(50, " ")}] ${phase.padEnd( - 10, - " " - )} ${event.padEnd(15, " ")} at ${time( - timestamp - )} running [${running().join(", ")}]`; - const report = (name: string) => { - const task = taskmap[name]; - - for (const timestamp of [...task.milestones.keys()].sort()) { - Zotero.log( - line( - `${this.id}.${name}.${task.milestones.get(timestamp)}`, - "finished", - timestamp - ) - ); - } - Zotero.log( - line( - `${this.id}.${name}`, - task.finished ? "finished" : "started", - task.finished || task.started - ) - ); - - progress?.( - phase, - name, - tasks.filter((t) => t.finished).length, - tasks.length, - (task.finished ? running()[0] : task.description) || task.description - ); - }; - - const run = (name) => { - const promise: Promise = promises[name]; - if (promise != null) return promise; - - const task = taskmap[name]; - const { action, needs } = task; - if (!action) { - promises[name] = Promise.resolve(); - return; - } - - const needed = async () => { - await Promise.all(needs.map(run)); - // for (const dep of needs) await run(dep) - }; - - const perform = async (): Promise => { - Zotero.log(`orchestrator: task ${phase}.${name} starting`); - try { - const res = (await action(reason, task)) as Promise; - Zotero.log(`orchestrator: task ${phase}.${name} finished`); - return await res; - } catch (err) { - Zotero.log( - `orchestrator: error: task ${phase}.${name} failed: ${err}\n${err.stack}` - ); - throw err; - } - }; - - promises[name] = circular; - - return (promises[name] = needed() - .then(async () => { - task.started = Date.now(); - report(name); - - try { - return await perform(); - } finally { - task.finished = Date.now(); - report(name); - } - }) - - .catch((err) => { - Zotero.log( - `orchestrator: ${name}.${phase} ${reason || ""} error: ${err}` - ); - throw err; - })); - }; - - await Promise.all(Object.keys(taskmap).map(run)); - } - - public async startup(reason: Reason, progress?: Progress): Promise { - this.resolve(); - await this.run("startup", reason, progress); - progress?.("startup", "ready", 100, 100, "ready"); - } - - public async shutdown(reason: Reason): Promise { - if (!this.resolved) - throw new Error("orchestrator: shutdown before startup"); - await this.run("shutdown", reason); - } -} - -export const orchestrator = new Orchestrator(); diff --git a/src/reference_network/ui/menus.ts b/src/ui/menus.ts similarity index 100% rename from src/reference_network/ui/menus.ts rename to src/ui/menus.ts diff --git a/src/reference_network/ui/ui.ts b/src/ui/ui.ts similarity index 100% rename from src/reference_network/ui/ui.ts rename to src/ui/ui.ts From 969f6d3f7d2b31d7ee67d531189008e6fa39f4cf Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 11 Jun 2024 18:51:04 -0700 Subject: [PATCH 61/96] feat: improve logging --- src/bootstrap.ts | 2 +- src/database/databaseManager.ts | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index d487ca7..a937365 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -14,7 +14,7 @@ type ReasonId = keyof typeof BOOTSTRAP_REASONS; export type Reason = (typeof BOOTSTRAP_REASONS)[ReasonId]; function log(msg: string): void { - Zotero.log(`Reference Network (bootstrap.ts): ${msg}`); + Zotero.log(msg, "warning", "Reference Network: bootstrap.ts"); } export function install(): void { diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts index ecacbd0..e9ce167 100644 --- a/src/database/databaseManager.ts +++ b/src/database/databaseManager.ts @@ -10,16 +10,20 @@ export class DatabaseManager { this.dbPath = Shim.Path.join(this.dir, "reference-network.sqlite"); } + private log(msg: string): void { + Zotero.log(msg, "warning", "Reference Network: databaseManager.ts"); + } + async initializeDatabase(): Promise { // Ensure directory exists Shim.File.makeDir(this.dir, { ignoreExisting: true }); - Zotero.log(`Directory created at ${this.dir}`); + this.log(`Directory created at ${this.dir}`); // Attach database await Zotero.DB.queryAsync("ATTACH DATABASE ? AS referencenetwork", [ this.dbPath, ]); - Zotero.log(`Database attached from ${this.dbPath}`); + this.log(`Database attached from ${this.dbPath}`); // Check for existing tables and create if necessary await this.forceCreateTable("referencenetwork", "graphs"); @@ -38,7 +42,7 @@ export class DatabaseManager { schemaName: string, tableName: string ): Promise { - Zotero.log(`Dropping ${tableName} table...`); + this.log(`Dropping ${tableName} table...`); await Zotero.DB.queryAsync(`DROP TABLE ${schemaName}.${tableName};`); } @@ -46,10 +50,9 @@ export class DatabaseManager { schemaName: string, tableName: string ): Promise { - Zotero.log(`Creating ${tableName} table...`); - Zotero.log(entities[tableName]); + this.log(`Creating ${tableName} table...`); await Zotero.DB.queryAsync(entities[tableName]); - Zotero.log(`${tableName} created`); + this.log(`${tableName} created`); } private async forceCreateTable( From e811c1aa82dacbce16ba7bb3438049c0d8d917de Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 11 Jun 2024 18:51:43 -0700 Subject: [PATCH 62/96] feat: grab cite/citedby lists from OpenAlex --- src/database/apiManager.ts | 35 +++++++++++++++++++++++--- src/reference-network.ts | 50 ++++++++++++++++++++++++++++++++------ 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/src/database/apiManager.ts b/src/database/apiManager.ts index 4e75b79..e6ac8ca 100644 --- a/src/database/apiManager.ts +++ b/src/database/apiManager.ts @@ -11,7 +11,15 @@ export class ApiManager { }; } - async fetchDOI(doi: string): Promise { + private log(msg: string): void { + Zotero.log(msg, "warning", "Reference Network: apiManager.ts"); + } + + private logError(error: Error): void { + Zotero.logError(error); + } + + async fetchReference(doi: string): Promise { const url = this.apiUrl + `works/https://doi.org/${encodeURIComponent(doi)}`; try { @@ -26,14 +34,17 @@ export class ApiManager { } } - async fetchDOIs(dois: string[], batchSize: number = 50): Promise { + async fetchReferences( + dois: string[], + batchSize: number = 25 + ): Promise { const data = []; let index = 0; while (index < dois.length) { const batchDOIs = dois.slice(index, index + batchSize); const url = this.apiUrl + `works?filter=doi:${batchDOIs.join("|")}`; - + this.log(`Fetching batch of DOIs: ${url}`); try { const response = await Zotero.HTTP.request("GET", url, { headers: this.headers, @@ -41,7 +52,7 @@ export class ApiManager { const dataBatch = JSON.parse(response.responseText); data.push(dataBatch); // Store the batch data } catch (error) { - Zotero.logError(error); + this.logError(error); throw error; // Re-throw the error after logging it } @@ -50,4 +61,20 @@ export class ApiManager { return data; } + + public async fetchCitedBy(id: string, url?: string): Promise { + if (!url) { + url = `https://api.openalex.org/works?filter=cites:${id}`; + } + try { + const response = await Zotero.HTTP.request("GET", url, { + headers: this.headers, + }); + const data = JSON.parse(response.responseText); + return data; + } catch (error) { + Zotero.logError(error); + throw error; // Re-throw to handle it in the calling function + } + } } diff --git a/src/reference-network.ts b/src/reference-network.ts index ce0f328..9571ed7 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -1,6 +1,6 @@ -import { queries } from "./database/queries"; import { DatabaseManager } from "./database/databaseManager"; import { ApiManager } from "./database/apiManager"; +import { queries } from "./database/queries"; // Define an interface for initialization options interface InitOptions { @@ -28,7 +28,7 @@ export class ReferenceNetwork { } private log(msg: string): void { - Zotero.log(`Reference Network (reference-network.ts): ${msg}`); + Zotero.log(msg, "warning", "Reference Network: reference-network.ts"); } private error(msg: string, e: Error): void { @@ -52,22 +52,58 @@ export class ReferenceNetwork { "DOI's grabbed: " + dois.length + "\nexamples: " + dois.slice(0, 5) ); + const referencedWorks: Record = {}; + const relatedWorks: Record = {}; + const citedByURL: Record = {}; + try { - const data = await this.apiManager.fetchDOIs(dois, 25); - this.log(`Data length is: ${Object.keys(data[0]).length}`); - this.log(`Data: ${JSON.stringify(data[0].meta)}`); + // test mode + const testDOIs = dois.slice(0, 5); + const fetchedData = await this.apiManager.fetchReferences(testDOIs, 25); + for (const batch of fetchedData) { + batch.results.forEach((result: any) => { + const id = this.getLastSlashComponent(result.id); + referencedWorks[id] = result.referenced_works.map((url: string) => + this.getLastSlashComponent(url) + ); + relatedWorks[id] = result.related_works.map((url: string) => + this.getLastSlashComponent(url) + ); + citedByURL[id] = result.cited_by_api_url; + }); + } + this.log(`References: ${JSON.stringify(referencedWorks)}`); } catch (e) { this.error("Failed to fetch DOIs", e); } + // This should only run when the user calls the "fetchCitedBy" method. + // Otherwise, it will make too many requests to OpenAlex. + // const citedBy: Record = {}; + // try { + // for (const [id, url] of Object.entries(citedByURL)) { + // const response = await this.apiManager.fetchCitedBy(id, url); + // citedBy[id] = response.results.map((result: { id: string }) => + // this.getLastSlashComponent(result.id) + // ); + // } + // this.log(`Cited by: ${JSON.stringify(citedBy)}`); + // } catch (e) { + // this.error("Failed to fetch cited by", e); + // } + this.initialized = true; } + + private getLastSlashComponent(url: string): string { + return url.substring(url.lastIndexOf("/") + 1); + } } function main() { const { host } = new URL("https://foo.com/path"); - this.log(`Host is ${host}`); - this.log( + Zotero.log(`Host is ${host}`); + Zotero.log( `Main called with ID: ${this.id}, Version: ${this.version}, Root URI: ${this.rootURI}` ); } From 7ac97ceb4cccbf1b4d205b85770dbaa6920958d3 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 11 Jun 2024 19:43:26 -0700 Subject: [PATCH 63/96] feat: declutter entities --- src/database/databaseManager.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts index e9ce167..b18ea43 100644 --- a/src/database/databaseManager.ts +++ b/src/database/databaseManager.ts @@ -1,4 +1,4 @@ -import { entities } from "./entities"; +import { SQL_CREATE_TABLES } from "./entities"; import { Shim } from "../environment/os"; export class DatabaseManager { @@ -20,13 +20,12 @@ export class DatabaseManager { this.log(`Directory created at ${this.dir}`); // Attach database - await Zotero.DB.queryAsync("ATTACH DATABASE ? AS referencenetwork", [ + await Zotero.DB.queryAsync("ATTACH DATABASE ? AS reference_network", [ this.dbPath, ]); this.log(`Database attached from ${this.dbPath}`); // Check for existing tables and create if necessary - await this.forceCreateTable("referencenetwork", "graphs"); } private async checkTableExists( From bd71ed51ee8641728525b891feab8e3b35c0e954 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 11 Jun 2024 20:16:00 -0700 Subject: [PATCH 64/96] feat: give structure to table creation --- src/database/databaseManager.ts | 56 ++++++------ src/database/entities.ts | 150 +++++++++++++------------------- 2 files changed, 92 insertions(+), 114 deletions(-) diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts index b18ea43..73a059d 100644 --- a/src/database/databaseManager.ts +++ b/src/database/databaseManager.ts @@ -1,4 +1,4 @@ -import { SQL_CREATE_TABLES } from "./entities"; +import { DATABASE_NAMES, TABLE_NAMES, DDL_QUERIES } from "./entities"; import { Shim } from "../environment/os"; export class DatabaseManager { @@ -20,48 +20,63 @@ export class DatabaseManager { this.log(`Directory created at ${this.dir}`); // Attach database - await Zotero.DB.queryAsync("ATTACH DATABASE ? AS reference_network", [ - this.dbPath, - ]); + await Zotero.DB.queryAsync( + `ATTACH DATABASE ? AS ${DATABASE_NAMES.REFERENCE_NETWORK}`, + [this.dbPath] + ); this.log(`Database attached from ${this.dbPath}`); // Check for existing tables and create if necessary } + private checkDDLExists(databaseName: string, tableName: string) { + // make sure databaseName and tablename are in databasenames and tablenames + if (!Object.values(DATABASE_NAMES).includes(databaseName)) { + throw new Error(`Invalid schema name: ${databaseName}`); + } + if (!Object.values(TABLE_NAMES).includes(tableName)) { + throw new Error(`Invalid table name: ${tableName}`); + } + } + private async checkTableExists( - schemaName: string, + databaseName: string, tableName: string ): Promise { + this.checkDDLExists(databaseName, tableName); return await Zotero.DB.valueQueryAsync( - `SELECT COUNT(*) FROM ${schemaName}.sqlite_master WHERE type='table' AND name='${tableName}'` + `SELECT COUNT(*) FROM ${databaseName}.sqlite_master WHERE type='table' AND name='${tableName}'` ); } private async dropTable( - schemaName: string, + databaseName: string, tableName: string ): Promise { + this.checkDDLExists(databaseName, tableName); this.log(`Dropping ${tableName} table...`); - await Zotero.DB.queryAsync(`DROP TABLE ${schemaName}.${tableName};`); + await Zotero.DB.queryAsync(`DROP TABLE ${databaseName}.${tableName};`); } private async createTable( - schemaName: string, + databaseName: string, tableName: string ): Promise { + this.checkDDLExists(databaseName, tableName); this.log(`Creating ${tableName} table...`); - await Zotero.DB.queryAsync(entities[tableName]); + await Zotero.DB.queryAsync(DDL_QUERIES[tableName]); this.log(`${tableName} created`); } - private async forceCreateTable( - schemaName: string, + private async recreateTable( + databaseName: string, tableName: string ): Promise { - if (await this.checkTableExists(schemaName, tableName)) { - await this.dropTable(schemaName, tableName); + this.checkDDLExists(databaseName, tableName); + if (await this.checkTableExists(databaseName, tableName)) { + await this.dropTable(databaseName, tableName); } - await this.createTable(schemaName, tableName); + await this.createTable(databaseName, tableName); } async addGraphRow( @@ -79,17 +94,6 @@ export class DatabaseManager { ); } - async getGraphRows(): Promise { - return await Zotero.DB.queryAsync("SELECT * FROM referencenetwork.graphs"); - } - - async getGraphRow(id: number): Promise { - return await Zotero.DB.queryAsync( - "SELECT * FROM referencenetwork.graphs WHERE id = ?", - [id] - ); - } - async updateGraphRow( id: number, source: string, diff --git a/src/database/entities.ts b/src/database/entities.ts index 0c76d1d..8c0660e 100644 --- a/src/database/entities.ts +++ b/src/database/entities.ts @@ -1,89 +1,63 @@ -export const entities = { - authors: ` - CREATE TABLE IF NOT EXISTS referencenetwork.authors ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ORCID TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - graphs: ` - CREATE TABLE IF NOT EXISTS referencenetwork.graphs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - source TEXT, - type TEXT, - target TEXT, - data_source TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - items: ` - CREATE TABLE IF NOT EXISTS referencenetwork.items ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - itemID TEXT, - title TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - itemAuthors: ` - CREATE TABLE IF NOT EXISTS referencenetwork.item_authors ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - itemID TEXT, - authorID TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - itemData: ` - CREATE TABLE IF NOT EXISTS referencenetwork.item_data ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - itemID TEXT, - fieldID TEXT, - valueID TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - itemDataValues: ` - CREATE TABLE IF NOT EXISTS referencenetwork.item_data_values ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - value TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - itemFields: ` - CREATE TABLE IF NOT EXISTS referencenetwork.item_fields ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - fieldName TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - itemTypes: ` - CREATE TABLE IF NOT EXISTS referencenetwork.item_types ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - typeName TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - libraries: ` - CREATE TABLE IF NOT EXISTS referencenetwork.libraries ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - libraryID TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - tags: ` - CREATE TABLE IF NOT EXISTS referencenetwork.tags ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - tagID TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, +export const DATABASE_NAMES = { + REFERENCE_NETWORK: "reference_network", +}; + +export const TABLE_NAMES = { + GRAPHS: `graphs`, + ITEMS: `items`, + REFERENCED_WORKS: `referenced_works`, + RELATED_WORKS: `related_works`, + CITED_BY: `cited_by`, +}; + +export const DDL_QUERIES = { + [TABLE_NAMES.GRAPHS]: ` + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.REFERENCE_NETWORK}.${TABLE_NAMES.GRAPHS} ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + source TEXT, + type TEXT, + target TEXT, + data_source TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + [TABLE_NAMES.ITEMS]: ` + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.REFERENCE_NETWORK}.${TABLE_NAMES.ITEMS} ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + zotero_item_id TEXT, + openalex_id TEXT, + doi TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + [TABLE_NAMES.REFERENCED_WORKS]: ` + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.REFERENCE_NETWORK}.${TABLE_NAMES.REFERENCED_WORKS} ( + id TEXT PRIMARY KEY, + item_id TEXT, + cites TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + [TABLE_NAMES.RELATED_WORKS]: ` + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.REFERENCE_NETWORK}.${TABLE_NAMES.RELATED_WORKS} ( + id TEXT PRIMARY KEY, + item_id TEXT, + is_related_to TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, + [TABLE_NAMES.CITED_BY]: ` + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.REFERENCE_NETWORK}.${TABLE_NAMES.CITED_BY} ( + id TEXT PRIMARY KEY, + item_id TEXT, + cited_by TEXT, + citer_in_library BOOLEAN, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, }; From d9c3dd5655ad68ae34c8dda83cd559620c443f69 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 11 Jun 2024 20:16:16 -0700 Subject: [PATCH 65/96] env: add dexie.js --- package-lock.json | 26 ++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 27 insertions(+) diff --git a/package-lock.json b/package-lock.json index 62853e9..76d5923 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "reference-network", "version": "0.0.1", "dependencies": { + "dexie": "^4.0.7", "esbuild": "^0.18.1", "eslint": "^8.42.0", "eslint-plugin-import": "^2.27.5", @@ -2389,6 +2390,11 @@ "node": ">=8" } }, + "node_modules/dexie": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.0.7.tgz", + "integrity": "sha512-M+Lo6rk4pekIfrc2T0o2tvVJwL6EAAM/B78DNfb8aaxFVoI1f8/rz5KTxuAnApkwqTSuxx7T5t0RKH7qprapGg==" + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -5017,6 +5023,26 @@ "node": "^16 || ^18 || >= 20" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "optional": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-gyp": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", diff --git a/package.json b/package.json index f425f8a..5fe20ce 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "homepage": "https://github.com/RaymondWJang/reference-network", "dependencies": { + "dexie": "^4.0.7", "esbuild": "^0.18.1", "eslint": "^8.42.0", "eslint-plugin-import": "^2.27.5", From 06f403f5bd42e575bf1298963b433f0adc8ac0d7 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Tue, 11 Jun 2024 20:25:51 -0700 Subject: [PATCH 66/96] test: dexie seems to work, but it does not connect to zotero db. --- src/database/databaseManager.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts index 73a059d..cd1faec 100644 --- a/src/database/databaseManager.ts +++ b/src/database/databaseManager.ts @@ -1,3 +1,4 @@ +import Dexie from "dexie"; import { DATABASE_NAMES, TABLE_NAMES, DDL_QUERIES } from "./entities"; import { Shim } from "../environment/os"; @@ -26,6 +27,14 @@ export class DatabaseManager { ); this.log(`Database attached from ${this.dbPath}`); + // Create a new instance of Dexie + const db = new Dexie("MyDatabase"); + + // Define a schema for the database + db.version(1).stores({ + friends: "++id, name, age", // `friends` is the name of the table + }); + // Check for existing tables and create if necessary } From 7cbe4d511874f9dde06ceed5e1fc7da735329a53 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 12 Jun 2024 15:07:10 -0700 Subject: [PATCH 67/96] chore: cleanup bundling --- esbuild.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/esbuild.js b/esbuild.js index 84b4e28..274c95d 100644 --- a/esbuild.js +++ b/esbuild.js @@ -77,15 +77,15 @@ async function build() { // banner: { js: "var Zotero;\n" }, }); - await bundle({ - entryPoints: [ - "src/reference-network.ts", - "src/environment/client.ts", - "src/environment/os.ts", - // "src/helpers/orchestrator.ts", - ], - outdir: "build", - }); + // await bundle({ + // entryPoints: [ + // "src/reference-network.ts", + // "src/environment/client.ts", + // "src/environment/os.ts", + // // "src/helpers/orchestrator.ts", + // ], + // outdir: "build", + // }); } build().catch((err) => { From 04f7c70f62ddb9e70cd0dfd7d3107c55ca861146 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 12 Jun 2024 15:07:52 -0700 Subject: [PATCH 68/96] feat: cleanup ref-net.ts, begin event-driven --- src/database/apiManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/apiManager.ts b/src/database/apiManager.ts index e6ac8ca..bdb0b8a 100644 --- a/src/database/apiManager.ts +++ b/src/database/apiManager.ts @@ -62,9 +62,9 @@ export class ApiManager { return data; } - public async fetchCitedBy(id: string, url?: string): Promise { + public async fetchCitedBy(openAlexID: string, url?: string): Promise { if (!url) { - url = `https://api.openalex.org/works?filter=cites:${id}`; + url = `https://api.openalex.org/works?filter=cites:${openAlexID}`; } try { const response = await Zotero.HTTP.request("GET", url, { From 00cf7df0a1396b79e8799609da8da905cabe16dc Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 12 Jun 2024 15:08:01 -0700 Subject: [PATCH 69/96] same as previous --- src/reference-network.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/reference-network.ts b/src/reference-network.ts index 9571ed7..05df278 100644 --- a/src/reference-network.ts +++ b/src/reference-network.ts @@ -44,6 +44,8 @@ export class ReferenceNetwork { this.version = options.version; this.rootURI = options.rootURI; + // from here, it must be initiated by a user action + await this.dbManager.initializeDatabase(); this.log("Grabbing all DOI's from Zotero..."); @@ -99,11 +101,3 @@ export class ReferenceNetwork { return url.substring(url.lastIndexOf("/") + 1); } } - -function main() { - const { host } = new URL("https://foo.com/path"); - Zotero.log(`Host is ${host}`); - Zotero.log( - `Main called with ID: ${this.id}, Version: ${this.version}, Root URI: ${this.rootURI}` - ); -} From 90bc41a5c1949b6455f372f7ad3f84fa82ff1096 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 12 Jun 2024 15:09:00 -0700 Subject: [PATCH 70/96] feat: remove dexie and other clutteres from dbmanager --- src/database/databaseManager.ts | 77 +++++++++------------------------ 1 file changed, 20 insertions(+), 57 deletions(-) diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts index cd1faec..8dc7ce7 100644 --- a/src/database/databaseManager.ts +++ b/src/database/databaseManager.ts @@ -1,4 +1,3 @@ -import Dexie from "dexie"; import { DATABASE_NAMES, TABLE_NAMES, DDL_QUERIES } from "./entities"; import { Shim } from "../environment/os"; @@ -27,24 +26,25 @@ export class DatabaseManager { ); this.log(`Database attached from ${this.dbPath}`); - // Create a new instance of Dexie - const db = new Dexie("MyDatabase"); - - // Define a schema for the database - db.version(1).stores({ - friends: "++id, name, age", // `friends` is the name of the table - }); - // Check for existing tables and create if necessary + for (const tableName of Object.values(TABLE_NAMES)) { + await this.createTable(DATABASE_NAMES.REFERENCE_NETWORK, tableName); + } } private checkDDLExists(databaseName: string, tableName: string) { // make sure databaseName and tablename are in databasenames and tablenames if (!Object.values(DATABASE_NAMES).includes(databaseName)) { - throw new Error(`Invalid schema name: ${databaseName}`); + throw new Error( + `database name must be one of ${Object.values( + DATABASE_NAMES + )}: ${databaseName}` + ); } if (!Object.values(TABLE_NAMES).includes(tableName)) { - throw new Error(`Invalid table name: ${tableName}`); + throw new Error( + `table name must be one of ${Object.values(TABLE_NAMES)}: ${tableName}` + ); } } @@ -72,62 +72,25 @@ export class DatabaseManager { tableName: string ): Promise { this.checkDDLExists(databaseName, tableName); + if (await this.checkTableExists(databaseName, tableName)) { + this.log(`${tableName} table already exists`); + return; + } + this.log(`Creating ${tableName} table...`); await Zotero.DB.queryAsync(DDL_QUERIES[tableName]); this.log(`${tableName} created`); } - private async recreateTable( + private async purgeTable( databaseName: string, tableName: string ): Promise { this.checkDDLExists(databaseName, tableName); - if (await this.checkTableExists(databaseName, tableName)) { - await this.dropTable(databaseName, tableName); + if (!(await this.checkTableExists(databaseName, tableName))) { + throw new Error(`${tableName} table does not exist`); } + await this.dropTable(databaseName, tableName); await this.createTable(databaseName, tableName); } - - async addGraphRow( - source: string, - type: string, - target: string, - dataSource: string - ): Promise { - await Zotero.DB.queryAsync( - ` - INSERT INTO referencenetwork.graphs (source, type, target, data_source) - VALUES (?, ?, ?, ?); - `, - [source, type, target, dataSource] - ); - } - - async updateGraphRow( - id: number, - source: string, - type: string, - target: string, - dataSource: string - ): Promise { - await Zotero.DB.queryAsync( - ` - UPDATE referencenetwork.graphs - SET source = ?, type = ?, target = ?, data_source = ? - WHERE id = ?; - `, - [source, type, target, dataSource, id] - ); - } - - async deleteGraphRow(id: number): Promise { - await Zotero.DB.queryAsync( - "DELETE FROM referencenetwork.graphs WHERE id = ?", - [id] - ); - } - - async deleteGraphRows(): Promise { - await Zotero.DB.queryAsync("DELETE FROM referencenetwork.graphs"); - } } From 12bddd28bb4312d86887e4cee0ea730f73d91821 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 12 Jun 2024 15:27:17 -0700 Subject: [PATCH 71/96] style: refactor esbuild.js --- esbuild.js | 108 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/esbuild.js b/esbuild.js index 274c95d..532b4f2 100644 --- a/esbuild.js +++ b/esbuild.js @@ -2,17 +2,23 @@ const path = require("path"); const fs = require("fs"); const esbuild = require("esbuild"); const rmrf = require("rimraf"); + +// Clear the 'gen' directory synchronously rmrf.sync("gen"); +// Load Zotero plugin tasks require("zotero-plugin/copy-assets"); require("zotero-plugin/rdf"); require("zotero-plugin/version"); -function js(src) { +// Helper function to replace TypeScript file extension with JavaScript +function replaceExtToJs(src) { return src.replace(/[.]ts$/, ".js"); } +// Bundles the files with the provided configuration async function bundle(config) { + // Default configuration enhanced with custom settings config = { bundle: true, format: "iife", @@ -23,71 +29,85 @@ async function bundle(config) { ...config, }; - let target; - if (config.outfile) { - target = config.outfile; - } else if (config.entryPoints.length === 1 && config.outdir) { - target = path.join(config.outdir, js(path.basename(config.entryPoints[0]))); - } else { - target = `${config.outdir} [${config.entryPoints.map(js).join(", ")}]`; - } + let target = determineTarget(config); const exportGlobals = config.exportGlobals; delete config.exportGlobals; + if (exportGlobals) { - const esm = await esbuild.build({ - ...config, - logLevel: "silent", - format: "esm", - metafile: true, - write: false, - }); - if (Object.values(esm.metafile.outputs).length !== 1) - throw new Error("exportGlobals not supported for multiple outputs"); - for (const output of Object.values(esm.metafile.outputs)) { - if (output.entryPoint) { - config.globalName = escape( - `{ ${output.exports.sort().join(", ")} }` - ).replace(/%/g, "$"); - // make these var, not const, so they get hoisted and are available in the global scope. - } - } + await handleExportGlobals(config); } console.log("* bundling", target); await esbuild.build(config); + if (exportGlobals) { - await fs.promises.writeFile( - target, - ( - await fs.promises.readFile(target, "utf-8") - ).replace( - config.globalName, - unescape(config.globalName.replace(/[$]/g, "%")) - ) + await rewriteGlobals(target, config); + } +} + +// Determine the target file or directory for output +function determineTarget(config) { + if (config.outfile) { + return config.outfile; + } else if (config.entryPoints.length === 1 && config.outdir) { + return path.join( + config.outdir, + replaceExtToJs(path.basename(config.entryPoints[0])) ); + } else { + return `${config.outdir} [${config.entryPoints + .map(replaceExtToJs) + .join(", ")}]`; } } +// Handle export of globals when required +async function handleExportGlobals(config) { + const esm = await esbuild.build({ + ...config, + logLevel: "silent", + format: "esm", + metafile: true, + write: false, + }); + if (Object.values(esm.metafile.outputs).length !== 1) { + throw new Error("exportGlobals not supported for multiple outputs"); + } + for (const output of Object.values(esm.metafile.outputs)) { + if (output.entryPoint) { + config.globalName = escape( + `{ ${output.exports.sort().join(", ")} }` + ).replace(/%/g, "$"); + } + } +} + +// Rewrite global variable names in the output files +async function rewriteGlobals(target, config) { + const originalContent = await fs.promises.readFile(target, "utf-8"); + const modifiedContent = originalContent.replace( + config.globalName, + unescape(config.globalName.replace(/[$]/g, "%")) + ); + await fs.promises.writeFile(target, modifiedContent); +} + +// Main build function async function build() { await bundle({ exportGlobals: true, entryPoints: ["src/bootstrap.ts"], outdir: "build", - // banner: { js: "var Zotero;\n" }, }); - // await bundle({ - // entryPoints: [ - // "src/reference-network.ts", - // "src/environment/client.ts", - // "src/environment/os.ts", - // // "src/helpers/orchestrator.ts", - // ], - // outdir: "build", - // }); + await bundle({ + entryPoints: ["src/prefs/prefs.ts"], + outdir: "build", + }); } +// Run build and handle any errors build().catch((err) => { console.log(err); process.exit(1); From a2a4b8c623ddec186439642b116fd3dce96c0966 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 12 Jun 2024 15:27:32 -0700 Subject: [PATCH 72/96] feat: add prefs.ts --- src/prefs/prefs.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/prefs/prefs.ts diff --git a/src/prefs/prefs.ts b/src/prefs/prefs.ts new file mode 100644 index 0000000..c76663f --- /dev/null +++ b/src/prefs/prefs.ts @@ -0,0 +1,24 @@ +document.addEventListener("DOMContentLoaded", () => { + // Example of setting and getting a preference + const prefElement = document.getElementById( + "example-pref" + ) as HTMLInputElement; + + // Load existing preference value + let prefValue = Zotero.Prefs.get( + "extensions.reference-network.examplePref", + true + ); + if (typeof prefValue !== "boolean") { + prefValue = !!prefValue; // Convert to boolean if it's not + } + prefElement.checked = prefValue; + + // Save preference value on change + prefElement.addEventListener("change", () => { + Zotero.Prefs.set( + "extensions.reference-network.examplePref", + prefElement.checked + ); + }); +}); From 7928a56c51a079f860f631f29611cc73ae9b5164 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Wed, 12 Jun 2024 16:13:01 -0700 Subject: [PATCH 73/96] feat: copy pref.xhtml during build --- esbuild.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/esbuild.js b/esbuild.js index 532b4f2..654d32f 100644 --- a/esbuild.js +++ b/esbuild.js @@ -16,6 +16,11 @@ function replaceExtToJs(src) { return src.replace(/[.]ts$/, ".js"); } +async function copyFile(source, destination) { + await fs.promises.mkdir(path.dirname(destination), { recursive: true }); + await fs.promises.copyFile(source, destination); +} + // Bundles the files with the provided configuration async function bundle(config) { // Default configuration enhanced with custom settings @@ -105,6 +110,8 @@ async function build() { entryPoints: ["src/prefs/prefs.ts"], outdir: "build", }); + + await copyFile("src/prefs/prefs.xhtml", "build/prefs.xhtml"); } // Run build and handle any errors From 4ce18db95db1cbaae9ec793d2e182cb8375d4632 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 13 Jun 2024 15:04:18 -0700 Subject: [PATCH 74/96] feat: change name to weaver --- docs/plugin/dev.md | 4 ++-- package-lock.json | 24 ++------------------ package.json | 21 +++++++++--------- src/bootstrap.ts | 29 +++++++++++-------------- src/database/apiManager.ts | 2 +- src/database/databaseManager.ts | 10 ++++----- src/database/entities.ts | 12 +++++----- src/environment/client.ts | 4 ++-- src/manifest.json | 10 ++++----- src/prefs/prefs.ts | 10 ++------- src/{reference-network.ts => weaver.ts} | 8 +++---- 11 files changed, 52 insertions(+), 82 deletions(-) rename src/{reference-network.ts => weaver.ts} (93%) diff --git a/docs/plugin/dev.md b/docs/plugin/dev.md index f869109..948a97e 100644 --- a/docs/plugin/dev.md +++ b/docs/plugin/dev.md @@ -2,7 +2,7 @@ Following: https://www.zotero.org/support/dev/client_coding/plugin_development -- set the location of the plugin in the `reference-network@example.com` file to `{repo}/build/` +- set the location of the plugin in the `weaver@example.com` file to `{repo}/build/` - (do the rest of the stuff in the above guide) - watch and rebuild the plugin with `npm run start` - you have to restart zotero between each change i think @@ -14,7 +14,7 @@ To run zotero with the plugin in debug mode, run the following command: ``` Weird things: -- the build step was generating the extension id as `reference-network@gmail.com` because [`zotero-plugin/rdf` just does that for some reason](https://github.com/retorquere/zotero-plugin/blob/ab40ae4ba59d2b6a3fcce3222d415d9b5d72b14b/rdf.ts#L16) +- the build step was generating the extension id as `weaver@gmail.com` because [`zotero-plugin/rdf` just does that for some reason](https://github.com/retorquere/zotero-plugin/blob/ab40ae4ba59d2b6a3fcce3222d415d9b5d72b14b/rdf.ts#L16) so the extension ID has to be updated in both the `package.json` file as well as the actual `manifest.json` file diff --git a/package-lock.json b/package-lock.json index 76d5923..24a709c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "reference-network", + "name": "weaver", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "reference-network", + "name": "weaver", "version": "0.0.1", "dependencies": { "dexie": "^4.0.7", @@ -25,7 +25,6 @@ "typeorm": "^0.3.20", "typescript": "^5.1.3", "zotero-plugin": "^1.4.22", - "zotero-plugin-toolkit": "^2.3.32", "zotero-types": "^1.0.15" }, "devDependencies": { @@ -7863,25 +7862,6 @@ "zotero-start": "bin/start.py" } }, - "node_modules/zotero-plugin-toolkit": { - "version": "2.3.32", - "resolved": "https://registry.npmjs.org/zotero-plugin-toolkit/-/zotero-plugin-toolkit-2.3.32.tgz", - "integrity": "sha512-KslBcCiIfEvAjmaU7DsMPYH9JOzwpZaF0psKmx+4FfD31BZfyTbO4VLXdZ7oP22Ezs0bLlEDMFgfjdIQFd2s7A==", - "dependencies": { - "zotero-types": "^2.0.0" - } - }, - "node_modules/zotero-plugin-toolkit/node_modules/zotero-types": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/zotero-types/-/zotero-types-2.0.1.tgz", - "integrity": "sha512-mqL9TeV5yEX1rDO2eI2qzg88Ow+FNJUkPhM9+0vfzL9FWJNtb5KcoPffa71LcOkGTBzCsK+WAVRnYs+c50QzYQ==", - "dependencies": { - "@types/bluebird": "^3.5.42", - "@types/react": "17.0.2", - "epubjs": "^0.3.93", - "pdfjs-dist": "~3.8.0" - } - }, "node_modules/zotero-plugin/node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", diff --git a/package.json b/package.json index 5fe20ce..eb46b11 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,29 @@ { - "name": "reference-network", - "id": "reference-network@example.com", + "name": "weaver", + "id": "weaver@example.com", "version": "0.0.1", - "description": "Reference Network", + "description": "Weaver", "scripts": { "lint": "eslint . --ext .ts --cache --cache-location .eslintcache/", "prebuild": "npm run lint", "build": "tsc --noEmit && node esbuild.js", "start": "tsc --noEmit && node --watch-path=./src esbuild.js", - "postbuild": "zotero-plugin-zipup build reference-network", + "postbuild": "zotero-plugin-zipup build weaver", "release": "zotero-plugin-release", "postversion": "git push --follow-tags" }, "repository": { "type": "git", - "url": "https://github.com/RaymondWJang/reference-network.git" + "url": "https://github.com/RaymondWJang/weaver.git" }, "author": { "name": "Raymond W. Chang", "email": "raymond.w.jang@gmail.com" }, "bugs": { - "url": "https://github.com/RaymondWJang/reference-network/issues" + "url": "https://github.com/RaymondWJang/weaver/issues" }, - "homepage": "https://github.com/RaymondWJang/reference-network", + "homepage": "https://github.com/RaymondWJang/weaver", "dependencies": { "dexie": "^4.0.7", "esbuild": "^0.18.1", @@ -42,13 +42,12 @@ "typeorm": "^0.3.20", "typescript": "^5.1.3", "zotero-plugin": "^1.4.22", - "zotero-plugin-toolkit": "^2.3.32", "zotero-types": "^1.0.15" }, "xpi": { - "name": "Reference Network for Zotero", - "updateLink": "https://github.com/RaymondWJang/reference-network/releases/download/v{version}/reference-network-{version}.xpi", - "releaseURL": "https://github.com/RaymondWJang/reference-network/releases/download/release/", + "name": "Weaver", + "updateLink": "https://github.com/RaymondWJang/weaver/releases/download/v{version}/weaver-{version}.xpi", + "releaseURL": "https://github.com/RaymondWJang/weaver/releases/download/release/", "bootstrapped": true }, "devDependencies": { diff --git a/src/bootstrap.ts b/src/bootstrap.ts index a937365..15ff2d7 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,4 +1,4 @@ -import { ReferenceNetwork } from "./reference-network"; +import { Weaver } from "./weaver"; const BOOTSTRAP_REASONS = { 1: "APP_STARTUP", @@ -14,7 +14,7 @@ type ReasonId = keyof typeof BOOTSTRAP_REASONS; export type Reason = (typeof BOOTSTRAP_REASONS)[ReasonId]; function log(msg: string): void { - Zotero.log(msg, "warning", "Reference Network: bootstrap.ts"); + Zotero.log(msg, "warning", "Weaver: bootstrap.ts"); } export function install(): void { @@ -27,6 +27,7 @@ export async function startup({ resourceURI, rootURI = resourceURI.spec, }) { + await Zotero.initializationPromise; log("Startup"); log(`ID: ${id}`); log(`Version: ${version}`); @@ -35,33 +36,29 @@ export async function startup({ try { await Zotero.PreferencePanes.register({ - pluginID: "reference-network@example.com", - src: `${rootURI}preferences.xhtml`, + // Generates a pane in Preference + pluginID: "weaver@example.com", + src: `${rootURI}prefs.xhtml`, scripts: [`${rootURI}prefs.js`], }); log("Registered preference pane"); - // Add DOM elements to the main Zotero pane - const win = Zotero.getMainWindow(); - if (win && win.ZoteroPane) { - const zp = win.ZoteroPane; - const doc = win.document; - } + // await Zotero. - const referenceNetwork = new ReferenceNetwork(); - await referenceNetwork.init({ id, version, rootURI }); - log("Initialized Reference Network"); + const weaver = new Weaver(); + await weaver.init({ id, version, rootURI }); + log("Initialized Weaver"); } catch (error) { log("Error during startup: " + error.message); } } export function shutdown() { - log("Reference Network: Shutdown"); + log("Weaver: Shutdown"); - Zotero.ReferenceNetwork = undefined; + Zotero.weaver = undefined; } export function uninstall() { - log("Reference Network: Uninstalled"); + log("Weaver: Uninstalled"); } diff --git a/src/database/apiManager.ts b/src/database/apiManager.ts index bdb0b8a..8d98bbb 100644 --- a/src/database/apiManager.ts +++ b/src/database/apiManager.ts @@ -12,7 +12,7 @@ export class ApiManager { } private log(msg: string): void { - Zotero.log(msg, "warning", "Reference Network: apiManager.ts"); + Zotero.log(msg, "warning", "Weaver: apiManager.ts"); } private logError(error: Error): void { diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts index 8dc7ce7..718ffc2 100644 --- a/src/database/databaseManager.ts +++ b/src/database/databaseManager.ts @@ -6,12 +6,12 @@ export class DatabaseManager { private dbPath: string; constructor(private rootDir: string) { - this.dir = Shim.Path.join(Zotero.DataDirectory.dir, "reference-network"); - this.dbPath = Shim.Path.join(this.dir, "reference-network.sqlite"); + this.dir = Shim.Path.join(Zotero.DataDirectory.dir, "weaver"); + this.dbPath = Shim.Path.join(this.dir, "weaver.sqlite"); } private log(msg: string): void { - Zotero.log(msg, "warning", "Reference Network: databaseManager.ts"); + Zotero.log(msg, "warning", "Weaver: databaseManager.ts"); } async initializeDatabase(): Promise { @@ -21,14 +21,14 @@ export class DatabaseManager { // Attach database await Zotero.DB.queryAsync( - `ATTACH DATABASE ? AS ${DATABASE_NAMES.REFERENCE_NETWORK}`, + `ATTACH DATABASE ? AS ${DATABASE_NAMES.WEAVER}`, [this.dbPath] ); this.log(`Database attached from ${this.dbPath}`); // Check for existing tables and create if necessary for (const tableName of Object.values(TABLE_NAMES)) { - await this.createTable(DATABASE_NAMES.REFERENCE_NETWORK, tableName); + await this.createTable(DATABASE_NAMES.WEAVER, tableName); } } diff --git a/src/database/entities.ts b/src/database/entities.ts index 8c0660e..9cfcb6d 100644 --- a/src/database/entities.ts +++ b/src/database/entities.ts @@ -1,5 +1,5 @@ export const DATABASE_NAMES = { - REFERENCE_NETWORK: "reference_network", + WEAVER: "weaver", }; export const TABLE_NAMES = { @@ -12,7 +12,7 @@ export const TABLE_NAMES = { export const DDL_QUERIES = { [TABLE_NAMES.GRAPHS]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.REFERENCE_NETWORK}.${TABLE_NAMES.GRAPHS} ( + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.GRAPHS} ( id INTEGER PRIMARY KEY AUTOINCREMENT, source TEXT, type TEXT, @@ -23,7 +23,7 @@ export const DDL_QUERIES = { ); `, [TABLE_NAMES.ITEMS]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.REFERENCE_NETWORK}.${TABLE_NAMES.ITEMS} ( + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.ITEMS} ( id INTEGER PRIMARY KEY AUTOINCREMENT, zotero_item_id TEXT, openalex_id TEXT, @@ -33,7 +33,7 @@ export const DDL_QUERIES = { ); `, [TABLE_NAMES.REFERENCED_WORKS]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.REFERENCE_NETWORK}.${TABLE_NAMES.REFERENCED_WORKS} ( + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.REFERENCED_WORKS} ( id TEXT PRIMARY KEY, item_id TEXT, cites TEXT, @@ -42,7 +42,7 @@ export const DDL_QUERIES = { ); `, [TABLE_NAMES.RELATED_WORKS]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.REFERENCE_NETWORK}.${TABLE_NAMES.RELATED_WORKS} ( + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.RELATED_WORKS} ( id TEXT PRIMARY KEY, item_id TEXT, is_related_to TEXT, @@ -51,7 +51,7 @@ export const DDL_QUERIES = { ); `, [TABLE_NAMES.CITED_BY]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.REFERENCE_NETWORK}.${TABLE_NAMES.CITED_BY} ( + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.CITED_BY} ( id TEXT PRIMARY KEY, item_id TEXT, cited_by TEXT, diff --git a/src/environment/client.ts b/src/environment/client.ts index d6d5370..743619e 100644 --- a/src/environment/client.ts +++ b/src/environment/client.ts @@ -12,8 +12,8 @@ function clientname(): string { } // if (process.versions.node) return 'Zotero' // testing if (Zotero.clientName) return Zotero.clientName as string; - if (Zotero.ReferenceNetwork?.clientName) { - return Zotero.ReferenceNetwork.clientName as string; + if (Zotero.Weaver?.clientName) { + return Zotero.Weaver.clientName as string; } throw new Error("Unable to detect clientName"); } diff --git a/src/manifest.json b/src/manifest.json index de249ad..1cb6ce1 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,13 +1,13 @@ { "manifest_version": 2, - "name": "Reference Network", + "name": "Weaver", "version": "0.0.1", - "description": "Reference Network", - "homepage_url": "https://github.com/RaymondWJang/reference-network", + "description": "Weaver", + "homepage_url": "https://github.com/RaymondWJang/weaver", "applications": { "zotero": { - "id": "reference-network@example.com", - "update_url": "https://github.com/RaymondWJang/reference-network/releases/download/release/update.rdf", + "id": "weaver@example.com", + "update_url": "https://github.com/RaymondWJang/weaver/releases/download/release/update.rdf", "strict_min_version": "6.999", "strict_max_version": "7.0.*" } diff --git a/src/prefs/prefs.ts b/src/prefs/prefs.ts index c76663f..2f529e6 100644 --- a/src/prefs/prefs.ts +++ b/src/prefs/prefs.ts @@ -5,10 +5,7 @@ document.addEventListener("DOMContentLoaded", () => { ) as HTMLInputElement; // Load existing preference value - let prefValue = Zotero.Prefs.get( - "extensions.reference-network.examplePref", - true - ); + let prefValue = Zotero.Prefs.get("extensions.weaver.examplePref", true); if (typeof prefValue !== "boolean") { prefValue = !!prefValue; // Convert to boolean if it's not } @@ -16,9 +13,6 @@ document.addEventListener("DOMContentLoaded", () => { // Save preference value on change prefElement.addEventListener("change", () => { - Zotero.Prefs.set( - "extensions.reference-network.examplePref", - prefElement.checked - ); + Zotero.Prefs.set("extensions.weaver.examplePref", prefElement.checked); }); }); diff --git a/src/reference-network.ts b/src/weaver.ts similarity index 93% rename from src/reference-network.ts rename to src/weaver.ts index 05df278..4197094 100644 --- a/src/reference-network.ts +++ b/src/weaver.ts @@ -9,7 +9,7 @@ interface InitOptions { rootURI: string; } -export class ReferenceNetwork { +export class Weaver { // I guess this is the .h of the __init__ method in Python 😭 private id: string | null = null; private version: string | null = null; @@ -28,7 +28,7 @@ export class ReferenceNetwork { } private log(msg: string): void { - Zotero.log(msg, "warning", "Reference Network: reference-network.ts"); + Zotero.log(msg, "warning", "Weaver: weaver.ts"); } private error(msg: string, e: Error): void { @@ -36,9 +36,9 @@ export class ReferenceNetwork { } async init(options: InitOptions): Promise { - this.log("Loading ReferenceNetwork: starting..."); + this.log("Loading Weaver: starting..."); if (this.initialized) { - throw new Error("ReferenceNetwork is already running"); + throw new Error("Weaver is already running"); } this.id = options.id; this.version = options.version; From 81d24f1fc82bea3fdcfd59b13be4391f9e36c3f1 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 13 Jun 2024 15:05:37 -0700 Subject: [PATCH 75/96] feat: start building UI interactions --- src/bootstrap.ts | 29 ++++--- src/locale/en-US/reference-network.ftl | 0 src/locale/en-US/weaver.ftl | 2 + src/types/global.d.ts | 12 ++- src/weaver.ts | 107 ++++++++++++++++++------- 5 files changed, 107 insertions(+), 43 deletions(-) delete mode 100644 src/locale/en-US/reference-network.ftl create mode 100644 src/locale/en-US/weaver.ftl diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 15ff2d7..7b478b2 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -27,30 +27,35 @@ export async function startup({ resourceURI, rootURI = resourceURI.spec, }) { - await Zotero.initializationPromise; - log("Startup"); + await Promise.all([ + Zotero.initializationPromise, + Zotero.unlockPromise, + Zotero.uiReadyPromise, + ]); + log(`ID: ${id}`); log(`Version: ${version}`); log(`Resource URI: ${resourceURI}`); log(`Root URI: ${rootURI}`); - try { - await Zotero.PreferencePanes.register({ - // Generates a pane in Preference - pluginID: "weaver@example.com", - src: `${rootURI}prefs.xhtml`, - scripts: [`${rootURI}prefs.js`], - }); - log("Registered preference pane"); + const weaver = new Weaver(); - // await Zotero. + try { + // await Zotero.PreferencePanes.register({ + // // Generates a pane in Preference + // pluginID: "weaver@example.com", + // src: `${rootURI}prefs.xhtml`, + // scripts: [`${rootURI}prefs.js`], + // }); + // log("Registered preference pane"); - const weaver = new Weaver(); await weaver.init({ id, version, rootURI }); log("Initialized Weaver"); } catch (error) { log("Error during startup: " + error.message); } + + weaver.addToAllWindows(); } export function shutdown() { diff --git a/src/locale/en-US/reference-network.ftl b/src/locale/en-US/reference-network.ftl deleted file mode 100644 index e69de29..0000000 diff --git a/src/locale/en-US/weaver.ftl b/src/locale/en-US/weaver.ftl new file mode 100644 index 0000000..4389d37 --- /dev/null +++ b/src/locale/en-US/weaver.ftl @@ -0,0 +1,2 @@ +weaver-test = + .label = !!Weaver Test!! diff --git a/src/types/global.d.ts b/src/types/global.d.ts index b219c52..1e097f4 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,8 +1,18 @@ import "zotero-types"; declare const Zotero: _ZoteroTypes.Zotero; +// declare const rootURI: string; declare global { interface Window { - ZoteroPane: _ZoteroTypes.ZoteroPane; + ZoteroPane?: _ZoteroTypes.ZoteroPane; + MozXULElement?: any; + } + interface Document { + createXULElement( + tagName: T + ): HTMLElementTagNameMap[T]; + } + interface HTMLElementTagNameMap { + menuitem: HTMLElement; } } diff --git a/src/weaver.ts b/src/weaver.ts index 4197094..9314ba4 100644 --- a/src/weaver.ts +++ b/src/weaver.ts @@ -46,38 +46,38 @@ export class Weaver { // from here, it must be initiated by a user action - await this.dbManager.initializeDatabase(); + // await this.dbManager.initializeDatabase(); - this.log("Grabbing all DOI's from Zotero..."); - const dois = await Zotero.DB.columnQueryAsync(queries.getDOIs); - this.log( - "DOI's grabbed: " + dois.length + "\nexamples: " + dois.slice(0, 5) - ); + // this.log("Grabbing all DOI's from Zotero..."); + // const dois = await Zotero.DB.columnQueryAsync(queries.getDOIs); + // this.log( + // "DOI's grabbed: " + dois.length + "\nexamples: " + dois.slice(0, 5) + // ); - const referencedWorks: Record = {}; - const relatedWorks: Record = {}; - const citedByURL: Record = {}; - - try { - // test mode - const testDOIs = dois.slice(0, 5); - const fetchedData = await this.apiManager.fetchReferences(testDOIs, 25); - for (const batch of fetchedData) { - batch.results.forEach((result: any) => { - const id = this.getLastSlashComponent(result.id); - referencedWorks[id] = result.referenced_works.map((url: string) => - this.getLastSlashComponent(url) - ); - relatedWorks[id] = result.related_works.map((url: string) => - this.getLastSlashComponent(url) - ); - citedByURL[id] = result.cited_by_api_url; - }); - } - this.log(`References: ${JSON.stringify(referencedWorks)}`); - } catch (e) { - this.error("Failed to fetch DOIs", e); - } + // const referencedWorks: Record = {}; + // const relatedWorks: Record = {}; + // const citedByURL: Record = {}; + + // try { + // // test mode + // const testDOIs = dois.slice(0, 5); + // const fetchedData = await this.apiManager.fetchReferences(testDOIs, 25); + // for (const batch of fetchedData) { + // batch.results.forEach((result: any) => { + // const id = this.getLastSlashComponent(result.id); + // referencedWorks[id] = result.referenced_works.map((url: string) => + // this.getLastSlashComponent(url) + // ); + // relatedWorks[id] = result.related_works.map((url: string) => + // this.getLastSlashComponent(url) + // ); + // citedByURL[id] = result.cited_by_api_url; + // }); + // } + // this.log(`References: ${JSON.stringify(referencedWorks)}`); + // } catch (e) { + // this.error("Failed to fetch DOIs", e); + // } // This should only run when the user calls the "fetchCitedBy" method. // Otherwise, it will make too many requests to OpenAlex. @@ -100,4 +100,51 @@ export class Weaver { private getLastSlashComponent(url: string): string { return url.substring(url.lastIndexOf("/") + 1); } + + static addToWindow(window: Window) { + const doc = window.document; + + // // Add a stylesheet to the main Zotero pane + // let link1 = doc.createElement('link'); + // link1.id = 'make-it-red-stylesheet'; + // link1.type = 'text/css'; + // link1.rel = 'stylesheet'; + // link1.href = this.rootURI + 'style.css'; + // doc.documentElement.appendChild(link1); + // this.storeAddedElement(link1); + + // Use Fluent for localization + window.MozXULElement.insertFTLIfNeeded( + rootURI + "/locale/en-US/weaver.ftl" + ); + + // Add menu option + if (window.MozXULElement && "createXULElement" in window.document) { + const menuitem = doc.createXULElement("menuitem"); + menuitem.id = "weaver-test-id"; + menuitem.setAttribute("type", "checkbox"); + menuitem.setAttribute("data-l10n-id", "weaver-test"); + menuitem.addEventListener("command", () => { + const inputElement = menuitem as HTMLInputElement; + this.menuToggleTest(window, inputElement.checked); + }); + const menuViewPopup = doc.getElementById("menu_viewPopup"); + if (menuViewPopup) { + menuViewPopup.appendChild(menuitem); + } + // this.storeAddedElement(menuitem); + } + } + + static menuToggleTest(a, b) { + Zotero.log("Menu has been checked!"); + } + + addToAllWindows(): void { + const windows: Window[] = Zotero.getMainWindows(); + for (const win of windows) { + if (!win.ZoteroPane) continue; + Weaver.addToWindow(win); + } + } } From 182d4865e63755ab234d119819ca2a2fbe2cc067 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 13 Jun 2024 15:06:11 -0700 Subject: [PATCH 76/96] TODO: figure out how the preferences pane works...:/ --- src/prefs/prefs.ts | 18 ------------------ src/prefs/prefs.xhtml | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 18 deletions(-) create mode 100644 src/prefs/prefs.xhtml diff --git a/src/prefs/prefs.ts b/src/prefs/prefs.ts index 2f529e6..e69de29 100644 --- a/src/prefs/prefs.ts +++ b/src/prefs/prefs.ts @@ -1,18 +0,0 @@ -document.addEventListener("DOMContentLoaded", () => { - // Example of setting and getting a preference - const prefElement = document.getElementById( - "example-pref" - ) as HTMLInputElement; - - // Load existing preference value - let prefValue = Zotero.Prefs.get("extensions.weaver.examplePref", true); - if (typeof prefValue !== "boolean") { - prefValue = !!prefValue; // Convert to boolean if it's not - } - prefElement.checked = prefValue; - - // Save preference value on change - prefElement.addEventListener("change", () => { - Zotero.Prefs.set("extensions.weaver.examplePref", prefElement.checked); - }); -}); diff --git a/src/prefs/prefs.xhtml b/src/prefs/prefs.xhtml new file mode 100644 index 0000000..6845eb8 --- /dev/null +++ b/src/prefs/prefs.xhtml @@ -0,0 +1,15 @@ + + + + + Preferences + + + +

Plugin Preferences

+ + + From 8d3a555b36d4dd79871871283dfdbfb228af217d Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Thu, 13 Jun 2024 15:06:25 -0700 Subject: [PATCH 77/96] chore: cleanup UI.ts --- src/ui/ui.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 0bc7db5..e69de29 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -1,16 +0,0 @@ -import { addContextMenu } from "./menus"; - -export const initUI = (window) => { - addContextMenu(window); -} - -// Remove any added items from the DOM -export const destroyUI = (zp) => { - // if (zp) { - // for (const id of addedElementIDs) { - // // ?. (null coalescing operator) not available in Zotero 6 - // const elem = zp.document.getElementById(id) - // if (elem) elem.remove() - // } - // } -} From 38b1b9b7fa15472fe580a0e1af278ba5bf091770 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 14 Jun 2024 13:44:24 -0700 Subject: [PATCH 78/96] feat: weaver loading with scriptloader than import --- esbuild.js | 2 +- src/bootstrap.ts | 24 +++++++++++++++++------- src/prefs/prefs.ts | 9 +++++++++ src/weaver.ts | 27 +++++++++------------------ 4 files changed, 36 insertions(+), 26 deletions(-) diff --git a/esbuild.js b/esbuild.js index 654d32f..9a0c3e8 100644 --- a/esbuild.js +++ b/esbuild.js @@ -107,7 +107,7 @@ async function build() { }); await bundle({ - entryPoints: ["src/prefs/prefs.ts"], + entryPoints: ["src/weaver.ts", "src/prefs/prefs.ts"], outdir: "build", }); diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 7b478b2..ae2af3f 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,5 +1,3 @@ -import { Weaver } from "./weaver"; - const BOOTSTRAP_REASONS = { 1: "APP_STARTUP", 2: "APP_SHUTDOWN", @@ -17,6 +15,10 @@ function log(msg: string): void { Zotero.log(msg, "warning", "Weaver: bootstrap.ts"); } +function logError(msg: string, error): void { + Zotero.log(`${msg} ${error}: ${error.stack}`, "error"); +} + export function install(): void { log("Installed"); } @@ -38,8 +40,11 @@ export async function startup({ log(`Resource URI: ${resourceURI}`); log(`Root URI: ${rootURI}`); - const weaver = new Weaver(); + Services.scriptloader.loadSubScript(`${rootURI}weaver.js`, { Zotero }); + + // const weaver = Zotero.Weaver; + log(`Startup: ${typeof Zotero}`); try { // await Zotero.PreferencePanes.register({ // // Generates a pane in Preference @@ -49,19 +54,24 @@ export async function startup({ // }); // log("Registered preference pane"); - await weaver.init({ id, version, rootURI }); + await Zotero.Weaver.init({ id, version, rootURI }); log("Initialized Weaver"); } catch (error) { - log("Error during startup: " + error.message); + log("Error during startup: " + error.stack); } - weaver.addToAllWindows(); + try { + log(typeof Zotero.Weaver); + Zotero.Weaver.addToAllWindows(); + } catch (error) { + logError("Error during a UI Test!", error); + } } export function shutdown() { log("Weaver: Shutdown"); - Zotero.weaver = undefined; + Zotero.Weaver = undefined; } export function uninstall() { diff --git a/src/prefs/prefs.ts b/src/prefs/prefs.ts index e69de29..b964d28 100644 --- a/src/prefs/prefs.ts +++ b/src/prefs/prefs.ts @@ -0,0 +1,9 @@ +// Default Preferences + +// In Zotero 6, default preferences could be set by creating .js files in your plugin's defaults/preferences/ folder: +// pref("extensions.make-it-red.intensity", 100); +// These default preferences are loaded automatically at startup. While this works fine for overlay plugins, +// which require a restart, bootstrapped plugins can be installed or enabled at any time, and their default +// preferences are not read until Zotero is restarted. +// In Zotero 7, default preferences should be placed in a prefs.js file in the plugin root, following the same +// format as above. These preferences will be read when plugins are installed or enabled and then on every startup. diff --git a/src/weaver.ts b/src/weaver.ts index 9314ba4..2ea233f 100644 --- a/src/weaver.ts +++ b/src/weaver.ts @@ -9,7 +9,7 @@ interface InitOptions { rootURI: string; } -export class Weaver { +Zotero.Weaver = class { // I guess this is the .h of the __init__ method in Python 😭 private id: string | null = null; private version: string | null = null; @@ -101,22 +101,13 @@ export class Weaver { return url.substring(url.lastIndexOf("/") + 1); } - static addToWindow(window: Window) { + public addToWindow(window: Window) { const doc = window.document; - // // Add a stylesheet to the main Zotero pane - // let link1 = doc.createElement('link'); - // link1.id = 'make-it-red-stylesheet'; - // link1.type = 'text/css'; - // link1.rel = 'stylesheet'; - // link1.href = this.rootURI + 'style.css'; - // doc.documentElement.appendChild(link1); - // this.storeAddedElement(link1); - // Use Fluent for localization - window.MozXULElement.insertFTLIfNeeded( - rootURI + "/locale/en-US/weaver.ftl" - ); + // window.MozXULElement.insertFTLIfNeeded( + // this.rootURI + "/locale/en-US/weaver.ftl" + // ); // Add menu option if (window.MozXULElement && "createXULElement" in window.document) { @@ -136,15 +127,15 @@ export class Weaver { } } - static menuToggleTest(a, b) { + private menuToggleTest(a, b) { Zotero.log("Menu has been checked!"); } - addToAllWindows(): void { + public addToAllWindows(): void { const windows: Window[] = Zotero.getMainWindows(); for (const win of windows) { if (!win.ZoteroPane) continue; - Weaver.addToWindow(win); + this.addToWindow(win); } } -} +}; From eeef3010045defc39d180463ae8fc7f6024d26d5 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 14 Jun 2024 17:15:04 -0700 Subject: [PATCH 79/96] feat: add copy directory in esbuild --- esbuild.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/esbuild.js b/esbuild.js index 9a0c3e8..6c455ac 100644 --- a/esbuild.js +++ b/esbuild.js @@ -21,6 +21,28 @@ async function copyFile(source, destination) { await fs.promises.copyFile(source, destination); } +async function copyDirectory(source, destination) { + // Ensure the destination directory exists + await fs.promises.mkdir(destination, { recursive: true }); + + // Read all items in the source directory + const items = await fs.promises.readdir(source, { withFileTypes: true }); + + // Iterate through each item in the source directory + for (const item of items) { + const sourcePath = path.join(source, item.name); + const destinationPath = path.join(destination, item.name); + + if (item.isDirectory()) { + // If it's a directory, recursively copy it + await copyDirectory(sourcePath, destinationPath); + } else { + // If it's a file, copy the file + await copyFile(sourcePath, destinationPath); + } + } +} + // Bundles the files with the provided configuration async function bundle(config) { // Default configuration enhanced with custom settings @@ -111,7 +133,8 @@ async function build() { outdir: "build", }); - await copyFile("src/prefs/prefs.xhtml", "build/prefs.xhtml"); + await copyFile("src/prefs/prefs.xhtml", "build//prefs/prefs.xhtml"); + await copyDirectory("src/locale/", "build/locale/"); } // Run build and handle any errors From a24c6596dc1cd32614e6add6bc85242d1bcfa294 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 14 Jun 2024 17:16:12 -0700 Subject: [PATCH 80/96] feat: switch to raw import in bootstrap --- src/bootstrap.ts | 39 ++++++++++++++------------------------- src/weaver.ts | 11 ++++++----- 2 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index ae2af3f..b842ebc 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,15 +1,4 @@ -const BOOTSTRAP_REASONS = { - 1: "APP_STARTUP", - 2: "APP_SHUTDOWN", - 3: "ADDON_ENABLE", - 4: "ADDON_DISABLE", - 5: "ADDON_INSTALL", - 6: "ADDON_UNINSTALL", - 7: "ADDON_UPGRADE", - 8: "ADDON_DOWNGRADE", -} as const; -type ReasonId = keyof typeof BOOTSTRAP_REASONS; -export type Reason = (typeof BOOTSTRAP_REASONS)[ReasonId]; +import { Weaver } from "./weaver.js"; function log(msg: string): void { Zotero.log(msg, "warning", "Weaver: bootstrap.ts"); @@ -40,29 +29,30 @@ export async function startup({ log(`Resource URI: ${resourceURI}`); log(`Root URI: ${rootURI}`); - Services.scriptloader.loadSubScript(`${rootURI}weaver.js`, { Zotero }); + // Services.scriptloader.loadSubScript(`${rootURI}weaver.js`, { Zotero }); - // const weaver = Zotero.Weaver; + const weaver = new Weaver(); + Zotero.Weaver = weaver; log(`Startup: ${typeof Zotero}`); try { - // await Zotero.PreferencePanes.register({ - // // Generates a pane in Preference - // pluginID: "weaver@example.com", - // src: `${rootURI}prefs.xhtml`, - // scripts: [`${rootURI}prefs.js`], - // }); - // log("Registered preference pane"); + await Zotero.PreferencePanes.register({ + // Generates a pane in Preference + pluginID: "weaver@example.com", + src: `${rootURI}prefs.xhtml`, + scripts: [`${rootURI}prefs/prefs.js`], + }); + log("Registered preference pane"); - await Zotero.Weaver.init({ id, version, rootURI }); + await weaver.init({ id, version, rootURI }); log("Initialized Weaver"); } catch (error) { log("Error during startup: " + error.stack); } try { - log(typeof Zotero.Weaver); - Zotero.Weaver.addToAllWindows(); + weaver.addToAllWindows(); + log("UI test complete!"); } catch (error) { logError("Error during a UI Test!", error); } @@ -70,7 +60,6 @@ export async function startup({ export function shutdown() { log("Weaver: Shutdown"); - Zotero.Weaver = undefined; } diff --git a/src/weaver.ts b/src/weaver.ts index 2ea233f..0b1dc24 100644 --- a/src/weaver.ts +++ b/src/weaver.ts @@ -9,7 +9,7 @@ interface InitOptions { rootURI: string; } -Zotero.Weaver = class { +export class Weaver { // I guess this is the .h of the __init__ method in Python 😭 private id: string | null = null; private version: string | null = null; @@ -105,9 +105,10 @@ Zotero.Weaver = class { const doc = window.document; // Use Fluent for localization - // window.MozXULElement.insertFTLIfNeeded( - // this.rootURI + "/locale/en-US/weaver.ftl" - // ); + window.MozXULElement.insertFTLIfNeeded( + this.rootURI + "locale/en-US/weaver.ftl" + ); + this.log(this.rootURI + "locale/en-US/weaver.ftl"); // Add menu option if (window.MozXULElement && "createXULElement" in window.document) { @@ -138,4 +139,4 @@ Zotero.Weaver = class { this.addToWindow(win); } } -}; +} From 89b747a825b83ce8f04435440781e8d22bff7f1a Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 14 Jun 2024 17:46:08 -0700 Subject: [PATCH 81/96] TODO: figure out preference pane... --- src/bootstrap.ts | 3 ++- src/prefs/prefs.ts | 17 +++++++++++++++++ src/prefs/prefs.xhtml | 30 ++++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index b842ebc..a57d318 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -39,8 +39,9 @@ export async function startup({ await Zotero.PreferencePanes.register({ // Generates a pane in Preference pluginID: "weaver@example.com", - src: `${rootURI}prefs.xhtml`, + src: `${rootURI}prefs/prefs.xhtml`, scripts: [`${rootURI}prefs/prefs.js`], + // defaultXUL: false, }); log("Registered preference pane"); diff --git a/src/prefs/prefs.ts b/src/prefs/prefs.ts index b964d28..37fecc5 100644 --- a/src/prefs/prefs.ts +++ b/src/prefs/prefs.ts @@ -7,3 +7,20 @@ // preferences are not read until Zotero is restarted. // In Zotero 7, default preferences should be placed in a prefs.js file in the plugin root, following the same // format as above. These preferences will be read when plugins are installed or enabled and then on every startup. + +// Ensure the DOM is fully loaded before executing the script +document.addEventListener("DOMContentLoaded", () => { + const checkbox = document.getElementById("example-pref") as HTMLInputElement; + + // Load the saved checkbox state from localStorage and apply it + const savedCheckboxState = localStorage.getItem("examplePrefChecked"); + if (savedCheckboxState) { + checkbox.checked = savedCheckboxState === "true"; + } + + // Add an event listener for changes to the checkbox + checkbox.addEventListener("change", () => { + // Save the new state of the checkbox to localStorage + localStorage.setItem("examplePrefChecked", checkbox.checked.toString()); + }); +}); diff --git a/src/prefs/prefs.xhtml b/src/prefs/prefs.xhtml index 6845eb8..df430fe 100644 --- a/src/prefs/prefs.xhtml +++ b/src/prefs/prefs.xhtml @@ -1,4 +1,4 @@ - + + + + + + + + + + + Zotero Preferences Pane + + From bc4d2ebd2c37968de9d4aa2abf4dd2468106718d Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 14 Jun 2024 17:59:01 -0700 Subject: [PATCH 82/96] debug: fix typo --- esbuild.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esbuild.js b/esbuild.js index 6c455ac..da87e9d 100644 --- a/esbuild.js +++ b/esbuild.js @@ -133,7 +133,7 @@ async function build() { outdir: "build", }); - await copyFile("src/prefs/prefs.xhtml", "build//prefs/prefs.xhtml"); + await copyFile("src/prefs/prefs.xhtml", "build/prefs/prefs.xhtml"); await copyDirectory("src/locale/", "build/locale/"); } From e64cb74f4e843de4c865f627681acd7c03baad98 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 14 Jun 2024 19:01:08 -0700 Subject: [PATCH 83/96] feat: condense data model --- src/database/entities.ts | 37 +++---------------------------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/src/database/entities.ts b/src/database/entities.ts index 9cfcb6d..fc614d6 100644 --- a/src/database/entities.ts +++ b/src/database/entities.ts @@ -5,18 +5,15 @@ export const DATABASE_NAMES = { export const TABLE_NAMES = { GRAPHS: `graphs`, ITEMS: `items`, - REFERENCED_WORKS: `referenced_works`, - RELATED_WORKS: `related_works`, - CITED_BY: `cited_by`, }; export const DDL_QUERIES = { [TABLE_NAMES.GRAPHS]: ` CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.GRAPHS} ( - id INTEGER PRIMARY KEY AUTOINCREMENT, + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, source TEXT, - type TEXT, target TEXT, + type TEXT check(type = "related_to" or type = "cites"), data_source TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP @@ -24,7 +21,7 @@ export const DDL_QUERIES = { `, [TABLE_NAMES.ITEMS]: ` CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.ITEMS} ( - id INTEGER PRIMARY KEY AUTOINCREMENT, + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, zotero_item_id TEXT, openalex_id TEXT, doi TEXT, @@ -32,32 +29,4 @@ export const DDL_QUERIES = { updated_at TEXT DEFAULT CURRENT_TIMESTAMP ); `, - [TABLE_NAMES.REFERENCED_WORKS]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.REFERENCED_WORKS} ( - id TEXT PRIMARY KEY, - item_id TEXT, - cites TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - [TABLE_NAMES.RELATED_WORKS]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.RELATED_WORKS} ( - id TEXT PRIMARY KEY, - item_id TEXT, - is_related_to TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - [TABLE_NAMES.CITED_BY]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.CITED_BY} ( - id TEXT PRIMARY KEY, - item_id TEXT, - cited_by TEXT, - citer_in_library BOOLEAN, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, }; From 3180b590a339ecaeaa0537bb968ebdff1e18b44c Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 14 Jun 2024 19:01:24 -0700 Subject: [PATCH 84/96] feat: parametrize library id in query --- src/database/queries.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/queries.ts b/src/database/queries.ts index 4e55367..c80d95b 100644 --- a/src/database/queries.ts +++ b/src/database/queries.ts @@ -1,11 +1,11 @@ export const queries = { - getDOIs: ` + getDOIs: (libraryID: number) => ` SELECT value -- iD.itemID, iDV.valueID, fieldName FROM itemDataValues AS iDV LEFT JOIN itemData AS iD ON iDV.valueID = iD.valueID LEFT JOIN fields AS f ON iD.fieldID = f.fieldID LEFT JOIN items AS i ON iD.itemID = i.itemID WHERE fieldName = 'DOI' - AND libraryID = 1; + AND libraryID = ${libraryID}; `, }; From 3edc186bd7e3d61e0d9f043861d1d7c20dcf822b Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 14 Jun 2024 19:01:46 -0700 Subject: [PATCH 85/96] chore: rename apimanager methods for clarity --- src/database/apiManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/apiManager.ts b/src/database/apiManager.ts index 8d98bbb..6ff51ad 100644 --- a/src/database/apiManager.ts +++ b/src/database/apiManager.ts @@ -19,7 +19,7 @@ export class ApiManager { Zotero.logError(error); } - async fetchReference(doi: string): Promise { + async fetchPublication(doi: string): Promise { const url = this.apiUrl + `works/https://doi.org/${encodeURIComponent(doi)}`; try { @@ -34,7 +34,7 @@ export class ApiManager { } } - async fetchReferences( + async fetchPublications( dois: string[], batchSize: number = 25 ): Promise { From b269aefab85d9e95e7cb180dd1a413b898be7fe8 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 14 Jun 2024 19:02:09 -0700 Subject: [PATCH 86/96] feat: begin modulizing weaver functionalities --- src/weaver.ts | 111 ++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/src/weaver.ts b/src/weaver.ts index 0b1dc24..1a64ff1 100644 --- a/src/weaver.ts +++ b/src/weaver.ts @@ -35,6 +35,10 @@ export class Weaver { Zotero.logError(e); } + async install(): Promise { + await this.dbManager.initializeDatabase(); + } + async init(options: InitOptions): Promise { this.log("Loading Weaver: starting..."); if (this.initialized) { @@ -45,58 +49,69 @@ export class Weaver { this.rootURI = options.rootURI; // from here, it must be initiated by a user action - - // await this.dbManager.initializeDatabase(); - - // this.log("Grabbing all DOI's from Zotero..."); - // const dois = await Zotero.DB.columnQueryAsync(queries.getDOIs); - // this.log( - // "DOI's grabbed: " + dois.length + "\nexamples: " + dois.slice(0, 5) - // ); - - // const referencedWorks: Record = {}; - // const relatedWorks: Record = {}; - // const citedByURL: Record = {}; - - // try { - // // test mode - // const testDOIs = dois.slice(0, 5); - // const fetchedData = await this.apiManager.fetchReferences(testDOIs, 25); - // for (const batch of fetchedData) { - // batch.results.forEach((result: any) => { - // const id = this.getLastSlashComponent(result.id); - // referencedWorks[id] = result.referenced_works.map((url: string) => - // this.getLastSlashComponent(url) - // ); - // relatedWorks[id] = result.related_works.map((url: string) => - // this.getLastSlashComponent(url) - // ); - // citedByURL[id] = result.cited_by_api_url; - // }); - // } - // this.log(`References: ${JSON.stringify(referencedWorks)}`); - // } catch (e) { - // this.error("Failed to fetch DOIs", e); - // } - - // This should only run when the user calls the "fetchCitedBy" method. - // Otherwise, it will make too many requests to OpenAlex. - // const citedBy: Record = {}; - // try { - // for (const [id, url] of Object.entries(citedByURL)) { - // const response = await this.apiManager.fetchCitedBy(id, url); - // citedBy[id] = response.results.map((result: { id: string }) => - // this.getLastSlashComponent(result.id) - // ); - // } - // this.log(`Cited by: ${JSON.stringify(citedBy)}`); - // } catch (e) { - // this.error("Failed to fetch cited by", e); - // } + try { + const doi = await this.grabAllDOIs(1); + await this.fetchAndParseCitations(doi); + // cited = await fetchCitedBy(fetchCitedBy); + } catch (error) { + this.error("Failed to grab all DOIs", error); + } this.initialized = true; } + private async grabAllDOIs(libraryID: number = 1): Promise { + this.log("Grabbing all DOI's from Zotero..."); + const dois = await Zotero.DB.columnQueryAsync(queries.getDOIs(libraryID)); + this.log( + "DOI's grabbed: " + dois.length + "\nexamples: " + dois.slice(0, 5) + ); + + return dois; + } + + private async fetchAndParseCitations(dois: string[]): Promise { + const referencedWorks: Record = {}; + const relatedWorks: Record = {}; + const citedByURL: Record = {}; + + try { + // test mode + const testDOIs = dois.slice(0, 5); + const fetchedData = await this.apiManager.fetchPublications(testDOIs, 25); + for (const batch of fetchedData) { + batch.results.forEach((result: any) => { + const id = this.getLastSlashComponent(result.id); + referencedWorks[id] = result.referenced_works.map((url: string) => + this.getLastSlashComponent(url) + ); + relatedWorks[id] = result.related_works.map((url: string) => + this.getLastSlashComponent(url) + ); + citedByURL[id] = result.cited_by_api_url; + }); + } + this.log(`References: ${JSON.stringify(referencedWorks)}`); + } catch (e) { + this.error("Failed to fetch DOIs", e); + } + } + + private async fetchCitedBy(citedByURL: string[]): Promise { + const citedBy: Record = {}; + try { + for (const [id, url] of Object.entries(citedByURL)) { + const response = await this.apiManager.fetchCitedBy(id, url); + citedBy[id] = response.results.map((result: { id: string }) => + this.getLastSlashComponent(result.id) + ); + } + this.log(`Cited by: ${JSON.stringify(citedBy)}`); + } catch (e) { + this.error("Failed to fetch cited by", e); + } + } + private getLastSlashComponent(url: string): string { return url.substring(url.lastIndexOf("/") + 1); } From 906f8e5314b72da76c1898cac1d3f5fa87c552e2 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Fri, 14 Jun 2024 19:02:44 -0700 Subject: [PATCH 87/96] feat: separate install step in bootstrap --- src/bootstrap.ts | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index a57d318..02cf55e 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -8,8 +8,16 @@ function logError(msg: string, error): void { Zotero.log(`${msg} ${error}: ${error.stack}`, "error"); } -export function install(): void { - log("Installed"); +export async function install(): Promise { + const weaver = new Weaver(); + Zotero.Weaver = weaver; + + try { + await weaver.install(); + log("Installed Weaver"); + } catch (error) { + logError("Error during installation", error); + } } export async function startup({ @@ -29,8 +37,6 @@ export async function startup({ log(`Resource URI: ${resourceURI}`); log(`Root URI: ${rootURI}`); - // Services.scriptloader.loadSubScript(`${rootURI}weaver.js`, { Zotero }); - const weaver = new Weaver(); Zotero.Weaver = weaver; @@ -44,7 +50,18 @@ export async function startup({ // defaultXUL: false, }); log("Registered preference pane"); + } catch (error) { + logError("Error registering preference pane", error); + } + try { + await weaver.install(); + log("Installed Weaver"); + } catch (error) { + logError("Error during installation", error); + } + + try { await weaver.init({ id, version, rootURI }); log("Initialized Weaver"); } catch (error) { From 59ffe5787f9c4b7d8728dc976e3cf9120ebd6ddd Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Sat, 15 Jun 2024 17:32:09 -0700 Subject: [PATCH 88/96] feat: modulize installation --- src/bootstrap.ts | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 02cf55e..64a5ad3 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -1,4 +1,4 @@ -import { Weaver } from "./weaver.js"; +import { weaver } from "./weaver.js"; function log(msg: string): void { Zotero.log(msg, "warning", "Weaver: bootstrap.ts"); @@ -9,7 +9,6 @@ function logError(msg: string, error): void { } export async function install(): Promise { - const weaver = new Weaver(); Zotero.Weaver = weaver; try { @@ -34,13 +33,14 @@ export async function startup({ log(`ID: ${id}`); log(`Version: ${version}`); - log(`Resource URI: ${resourceURI}`); + // log(`Resource URI: ${resourceURI}`); log(`Root URI: ${rootURI}`); - const weaver = new Weaver(); - Zotero.Weaver = weaver; + if (typeof Zotero.Weaver !== "undefined") { + Zotero.Weaver = weaver; + } - log(`Startup: ${typeof Zotero}`); + log(`Bootstrap startup`); try { await Zotero.PreferencePanes.register({ // Generates a pane in Preference @@ -54,13 +54,6 @@ export async function startup({ logError("Error registering preference pane", error); } - try { - await weaver.install(); - log("Installed Weaver"); - } catch (error) { - logError("Error during installation", error); - } - try { await weaver.init({ id, version, rootURI }); log("Initialized Weaver"); From 7f3ec3809bb81c4e0ad0af816beb17e339e3fc09 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Sat, 15 Jun 2024 17:32:36 -0700 Subject: [PATCH 89/96] feat: isud for dbmanager --- src/database/databaseManager.ts | 82 ++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 28 deletions(-) diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts index 718ffc2..d2b856a 100644 --- a/src/database/databaseManager.ts +++ b/src/database/databaseManager.ts @@ -1,4 +1,11 @@ -import { DATABASE_NAMES, TABLE_NAMES, DDL_QUERIES } from "./entities"; +import { + DATABASE_NAMES, + TABLE_NAMES, + DDL_QUERIES, + ISUD, + DatabaseNameKey, + TableNameKey, +} from "./entities"; import { Shim } from "../environment/os"; export class DatabaseManager { @@ -32,27 +39,10 @@ export class DatabaseManager { } } - private checkDDLExists(databaseName: string, tableName: string) { - // make sure databaseName and tablename are in databasenames and tablenames - if (!Object.values(DATABASE_NAMES).includes(databaseName)) { - throw new Error( - `database name must be one of ${Object.values( - DATABASE_NAMES - )}: ${databaseName}` - ); - } - if (!Object.values(TABLE_NAMES).includes(tableName)) { - throw new Error( - `table name must be one of ${Object.values(TABLE_NAMES)}: ${tableName}` - ); - } - } - private async checkTableExists( databaseName: string, tableName: string ): Promise { - this.checkDDLExists(databaseName, tableName); return await Zotero.DB.valueQueryAsync( `SELECT COUNT(*) FROM ${databaseName}.sqlite_master WHERE type='table' AND name='${tableName}'` ); @@ -62,7 +52,6 @@ export class DatabaseManager { databaseName: string, tableName: string ): Promise { - this.checkDDLExists(databaseName, tableName); this.log(`Dropping ${tableName} table...`); await Zotero.DB.queryAsync(`DROP TABLE ${databaseName}.${tableName};`); } @@ -71,7 +60,6 @@ export class DatabaseManager { databaseName: string, tableName: string ): Promise { - this.checkDDLExists(databaseName, tableName); if (await this.checkTableExists(databaseName, tableName)) { this.log(`${tableName} table already exists`); return; @@ -82,15 +70,53 @@ export class DatabaseManager { this.log(`${tableName} created`); } - private async purgeTable( - databaseName: string, - tableName: string + async insert( + tableName: TableNameKey, + columns: string, + values: string + ): Promise { + await Zotero.DB.queryAsync(ISUD.INSERT(tableName, columns, values)); + } + + async select( + tableName: TableNameKey, + columns: string, + condition: string + ): Promise { + return await Zotero.DB.queryAsync( + ISUD.SELECT(tableName, columns, condition) + ); + } + + async update( + tableName: TableNameKey, + columnValuePairs: string, + condition?: string + ): Promise { + await Zotero.DB.queryAsync( + ISUD.UPDATE(tableName, columnValuePairs, condition) + ); + } + + async delete(tableName: TableNameKey, condition: string): Promise { + await Zotero.DB.queryAsync(ISUD.DELETE(tableName, condition)); + } + + async upsert( + tableName: TableNameKey, + columns: string, + values: string, + condition: string ): Promise { - this.checkDDLExists(databaseName, tableName); - if (!(await this.checkTableExists(databaseName, tableName))) { - throw new Error(`${tableName} table does not exist`); + const rows = await this.select(tableName, columns, condition); + if (rows.length === 0) { + await this.insert(tableName, columns, values); + } else { + const columnValuePairs = columns + .split(", ") + .map((column, i) => `${column} = ${values.split(", ")[i]}`) + .join(", "); + await this.update(tableName, columnValuePairs, condition); } - await this.dropTable(databaseName, tableName); - await this.createTable(databaseName, tableName); } } From 485485d31888db8b141e1a338da95e52cefeceb8 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Sat, 15 Jun 2024 17:32:50 -0700 Subject: [PATCH 90/96] feat: isud for entities --- src/database/entities.ts | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/database/entities.ts b/src/database/entities.ts index fc614d6..67315c0 100644 --- a/src/database/entities.ts +++ b/src/database/entities.ts @@ -1,19 +1,22 @@ export const DATABASE_NAMES = { WEAVER: "weaver", -}; +} as const; export const TABLE_NAMES = { GRAPHS: `graphs`, ITEMS: `items`, -}; +} as const; + +export type DatabaseNameKey = keyof typeof DATABASE_NAMES; +export type TableNameKey = keyof typeof TABLE_NAMES; export const DDL_QUERIES = { [TABLE_NAMES.GRAPHS]: ` CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.GRAPHS} ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - source TEXT, - target TEXT, - type TEXT check(type = "related_to" or type = "cites"), + source TEXT NOT NULL, + target TEXT NOT NULL, + type TEXT check(type = "related_to" or type = "cites") NOT NULL, data_source TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP @@ -30,3 +33,23 @@ export const DDL_QUERIES = { ); `, }; + +export const ISUD = { + INSERT: (tableName: TableNameKey, columns: string, values: string) => + `INSERT INTO ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES[tableName]} (${columns}) VALUES (${values});`, + SELECT: (tableName: TableNameKey, columns: string, condition?: string) => { + if (condition) { + `SELECT ${columns} FROM ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES[tableName]} WHERE ${condition};`; + } else { + `SELECT ${columns} FROM ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES[tableName]}`; + } + }, + UPDATE: ( + tableName: TableNameKey, + columnValuePairs: string, + condition?: string + ) => + `UPDATE ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES[tableName]} SET ${columnValuePairs} WHERE ${condition};`, + DELETE: (tableName: TableNameKey, condition: string) => + `DELETE FROM ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES[tableName]} WHERE ${condition};`, +}; From 7f426f606f255c46538a9cf5776406aff310f38e Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 17 Jun 2024 15:54:30 -0700 Subject: [PATCH 91/96] feat: fix query --- src/database/queries.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/queries.ts b/src/database/queries.ts index c80d95b..c192fe1 100644 --- a/src/database/queries.ts +++ b/src/database/queries.ts @@ -1,6 +1,6 @@ export const queries = { getDOIs: (libraryID: number) => ` - SELECT value -- iD.itemID, iDV.valueID, fieldName + SELECT value as doi, iD.itemID -- iDV.valueID, fieldName FROM itemDataValues AS iDV LEFT JOIN itemData AS iD ON iDV.valueID = iD.valueID LEFT JOIN fields AS f ON iD.fieldID = f.fieldID From 28d9a3476b733b27d33bda80f0e7c1f68cd76a7d Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 17 Jun 2024 15:54:53 -0700 Subject: [PATCH 92/96] style: ISOD to CRUD --- src/database/databaseManager.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/database/databaseManager.ts b/src/database/databaseManager.ts index d2b856a..6d24110 100644 --- a/src/database/databaseManager.ts +++ b/src/database/databaseManager.ts @@ -2,7 +2,7 @@ import { DATABASE_NAMES, TABLE_NAMES, DDL_QUERIES, - ISUD, + CRUD, DatabaseNameKey, TableNameKey, } from "./entities"; @@ -75,7 +75,7 @@ export class DatabaseManager { columns: string, values: string ): Promise { - await Zotero.DB.queryAsync(ISUD.INSERT(tableName, columns, values)); + await Zotero.DB.queryAsync(CRUD.INSERT(tableName, columns, values)); } async select( @@ -84,7 +84,7 @@ export class DatabaseManager { condition: string ): Promise { return await Zotero.DB.queryAsync( - ISUD.SELECT(tableName, columns, condition) + CRUD.SELECT(tableName, columns, condition) ); } @@ -94,12 +94,12 @@ export class DatabaseManager { condition?: string ): Promise { await Zotero.DB.queryAsync( - ISUD.UPDATE(tableName, columnValuePairs, condition) + CRUD.UPDATE(tableName, columnValuePairs, condition) ); } async delete(tableName: TableNameKey, condition: string): Promise { - await Zotero.DB.queryAsync(ISUD.DELETE(tableName, condition)); + await Zotero.DB.queryAsync(CRUD.DELETE(tableName, condition)); } async upsert( From 4ee3f21f645823ec22afcb3de7d9053b24343209 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 17 Jun 2024 15:55:09 -0700 Subject: [PATCH 93/96] chore: readability --- src/database/entities.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/database/entities.ts b/src/database/entities.ts index 67315c0..42df139 100644 --- a/src/database/entities.ts +++ b/src/database/entities.ts @@ -11,6 +11,16 @@ export type DatabaseNameKey = keyof typeof DATABASE_NAMES; export type TableNameKey = keyof typeof TABLE_NAMES; export const DDL_QUERIES = { + [TABLE_NAMES.ITEMS]: ` + CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.ITEMS} ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + zotero_item_id TEXT, + openalex_id TEXT, + doi TEXT, + created_at TEXT DEFAULT CURRENT_TIMESTAMP, + updated_at TEXT DEFAULT CURRENT_TIMESTAMP + ); + `, [TABLE_NAMES.GRAPHS]: ` CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.GRAPHS} ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, @@ -22,19 +32,9 @@ export const DDL_QUERIES = { updated_at TEXT DEFAULT CURRENT_TIMESTAMP ); `, - [TABLE_NAMES.ITEMS]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.ITEMS} ( - id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, - zotero_item_id TEXT, - openalex_id TEXT, - doi TEXT, - created_at TEXT DEFAULT CURRENT_TIMESTAMP, - updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, }; -export const ISUD = { +export const CRUD = { INSERT: (tableName: TableNameKey, columns: string, values: string) => `INSERT INTO ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES[tableName]} (${columns}) VALUES (${values});`, SELECT: (tableName: TableNameKey, columns: string, condition?: string) => { From 282848b16809d39e6c640f3c9e3f2bb78ee8880d Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 17 Jun 2024 16:11:00 -0700 Subject: [PATCH 94/96] feat: modulize db populate TODO: why does await Zotero.DB.queryAsync(query); return nothing in runtime??? --- src/weaver.ts | 83 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/src/weaver.ts b/src/weaver.ts index 1a64ff1..f09b093 100644 --- a/src/weaver.ts +++ b/src/weaver.ts @@ -50,9 +50,18 @@ export class Weaver { // from here, it must be initiated by a user action try { - const doi = await this.grabAllDOIs(1); - await this.fetchAndParseCitations(doi); + const libraryID = 1; + this.log("point 1"); + const ZoteroIdDoi = await this.grabAllDOIs(libraryID); + this.log("point 2"); + + const dois = ZoteroIdDoi.map((row) => row.doi); + const data = await this.fetchAndParseCitations(dois); // cited = await fetchCitedBy(fetchCitedBy); + data["idDoiPair"] = ZoteroIdDoi; + this.log("point 3"); + + await this.populateItemsTable(data); } catch (error) { this.error("Failed to grab all DOIs", error); } @@ -60,17 +69,64 @@ export class Weaver { this.initialized = true; } - private async grabAllDOIs(libraryID: number = 1): Promise { - this.log("Grabbing all DOI's from Zotero..."); - const dois = await Zotero.DB.columnQueryAsync(queries.getDOIs(libraryID)); - this.log( - "DOI's grabbed: " + dois.length + "\nexamples: " + dois.slice(0, 5) - ); + private async populateItemsTable(data: object): Promise { + // upsert into items table + const referencesDOIs = this.getUniqueElements(data["referencedWorks"]); + const relatedDOIs = this.getUniqueElements(data["realatedWorks"]); + + // combine the sets + const allDOIs = new Set([...referencesDOIs, ...relatedDOIs]); + + for (const doi of allDOIs) { + await this.dbManager.upsert( + "ITEMS", + `zotero_item_id, openalex_id, doi`, + `${null}, ${null}, ${doi}`, + `doi = ${doi}` + ); + } + for (const idDoiPair of data["idDoiPair"]) { + await this.dbManager.upsert( + "ITEMS", + `zotero_item_id, openalex_id, doi`, + `${idDoiPair.itemID}, ${null}, ${idDoiPair.doi}`, + `doi = ${idDoiPair.doi}` + ); + } + } + + private getUniqueElements(record: Record): Set { + // Extract keys + const keys = Object.keys(record); + + // Flatten values + const values = Object.values(record).flat(); - return dois; + // Combine keys and values + const combined = keys.concat(values); + + // Create a Set to automatically remove duplicates + return new Set(combined); + } + + private async grabAllDOIs(libraryID: number = 1): Promise { + this.log("Grabbing all DOI's from Zotero..."); + const query = queries.getDOIs(libraryID); + this.log(`Query: ${query}`); + try { + const idDOI = await Zotero.DB.queryAsync(query); + this.log( + `DOI's grabbed: ${idDOI.length}\nexamples: ${idDOI.slice(0, 5)}` + ); + return idDOI; + } catch (e) { + this.error(`Failed to grab DOIs: `, e); + throw e; + } } - private async fetchAndParseCitations(dois: string[]): Promise { + private async fetchAndParseCitations(dois: string[]): Promise { + this.log("Fetching and parsing citations..."); const referencedWorks: Record = {}; const relatedWorks: Record = {}; const citedByURL: Record = {}; @@ -95,6 +151,11 @@ export class Weaver { } catch (e) { this.error("Failed to fetch DOIs", e); } + return { + referencedWorks: referencedWorks, + relatedWorks: relatedWorks, + citedByURL: citedByURL, + }; } private async fetchCitedBy(citedByURL: string[]): Promise { @@ -155,3 +216,5 @@ export class Weaver { } } } + +export const weaver = new Weaver(); From 5b965633e499ed9b8231fd4c94b737d44433a927 Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 17 Jun 2024 16:21:03 -0700 Subject: [PATCH 95/96] ARE YOU FUCKING KIDDING ME --- src/database/queries.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/database/queries.ts b/src/database/queries.ts index c192fe1..bf3b10b 100644 --- a/src/database/queries.ts +++ b/src/database/queries.ts @@ -1,11 +1,11 @@ export const queries = { - getDOIs: (libraryID: number) => ` - SELECT value as doi, iD.itemID -- iDV.valueID, fieldName + getDOIs: ( + libraryID: number + ) => `SELECT value as doi, iD.itemID -- iDV.valueID, fieldName FROM itemDataValues AS iDV LEFT JOIN itemData AS iD ON iDV.valueID = iD.valueID LEFT JOIN fields AS f ON iD.fieldID = f.fieldID LEFT JOIN items AS i ON iD.itemID = i.itemID WHERE fieldName = 'DOI' - AND libraryID = ${libraryID}; - `, + AND libraryID = ${libraryID};`, }; From 61ce455662da6c7138d00430787587d5d878b62a Mon Sep 17 00:00:00 2001 From: "Raymond W. Chang" Date: Mon, 17 Jun 2024 16:54:18 -0700 Subject: [PATCH 96/96] debug: cannot start queries with linebreak... --- src/database/entities.ts | 12 ++++-------- src/weaver.ts | 1 - 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/database/entities.ts b/src/database/entities.ts index 42df139..1fd102a 100644 --- a/src/database/entities.ts +++ b/src/database/entities.ts @@ -11,18 +11,15 @@ export type DatabaseNameKey = keyof typeof DATABASE_NAMES; export type TableNameKey = keyof typeof TABLE_NAMES; export const DDL_QUERIES = { - [TABLE_NAMES.ITEMS]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.ITEMS} ( + [TABLE_NAMES.ITEMS]: `CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.ITEMS} ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, zotero_item_id TEXT, openalex_id TEXT, doi TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, - [TABLE_NAMES.GRAPHS]: ` - CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.GRAPHS} ( + );`, + [TABLE_NAMES.GRAPHS]: `CREATE TABLE IF NOT EXISTS ${DATABASE_NAMES.WEAVER}.${TABLE_NAMES.GRAPHS} ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, source TEXT NOT NULL, target TEXT NOT NULL, @@ -30,8 +27,7 @@ export const DDL_QUERIES = { data_source TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP - ); - `, + );`, }; export const CRUD = { diff --git a/src/weaver.ts b/src/weaver.ts index f09b093..cd14c31 100644 --- a/src/weaver.ts +++ b/src/weaver.ts @@ -112,7 +112,6 @@ export class Weaver { private async grabAllDOIs(libraryID: number = 1): Promise { this.log("Grabbing all DOI's from Zotero..."); const query = queries.getDOIs(libraryID); - this.log(`Query: ${query}`); try { const idDOI = await Zotero.DB.queryAsync(query); this.log(