special links
authorJustin Wind <justin.wind+git@gmail.com>
Sat, 14 Jun 2025 17:32:09 +0000 (10:32 -0700)
committerJustin Wind <justin.wind+git@gmail.com>
Sat, 14 Jun 2025 17:32:09 +0000 (10:32 -0700)
package-lock.json
package.json
src/db/postgres/index.js
src/db/postgres/sql/schema/1.0.0/apply.sql
src/db/postgres/sql/schema/1.0.1/apply.sql [new file with mode: 0644]
src/db/postgres/sql/schema/1.0.1/revert.sql [new file with mode: 0644]
src/db/postgres/sql/schema/init.sql
src/db/sqlite/index.js
test/src/db/sqlite/index.js

index f4bd7b7d0d01bc994fdb9488111b762e44dbd559..54a7b554f69340fe7971042dfd24c7d30339611d 100644 (file)
@@ -15,7 +15,7 @@
         "better-sqlite3": "^9.4.3",
         "eslint-plugin-n": "^16.6.2",
         "pg-promise": "11.5.4",
-        "pm2": "^5.3.1",
+        "pm2": "^5.4.3",
         "uuid": "^9.0.1"
       },
       "devDependencies": {
         "node": ">= 8"
       }
     },
-    "node_modules/@opencensus/core": {
-      "version": "0.0.9",
-      "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.9.tgz",
-      "integrity": "sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q==",
-      "dependencies": {
-        "continuation-local-storage": "^3.2.1",
-        "log-driver": "^1.2.7",
-        "semver": "^5.5.0",
-        "shimmer": "^1.2.0",
-        "uuid": "^3.2.1"
-      },
-      "engines": {
-        "node": ">=6.0"
-      }
-    },
-    "node_modules/@opencensus/core/node_modules/semver": {
-      "version": "5.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
-      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
-      "bin": {
-        "semver": "bin/semver"
-      }
-    },
-    "node_modules/@opencensus/core/node_modules/uuid": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
-      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
-      "deprecated": "Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.",
-      "bin": {
-        "uuid": "bin/uuid"
-      }
-    },
-    "node_modules/@opencensus/propagation-b3": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz",
-      "integrity": "sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A==",
-      "dependencies": {
-        "@opencensus/core": "^0.0.8",
-        "uuid": "^3.2.1"
-      },
-      "engines": {
-        "node": ">=6.0"
-      }
-    },
-    "node_modules/@opencensus/propagation-b3/node_modules/@opencensus/core": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.8.tgz",
-      "integrity": "sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ==",
-      "dependencies": {
-        "continuation-local-storage": "^3.2.1",
-        "log-driver": "^1.2.7",
-        "semver": "^5.5.0",
-        "shimmer": "^1.2.0",
-        "uuid": "^3.2.1"
-      },
-      "engines": {
-        "node": ">=6.0"
-      }
-    },
-    "node_modules/@opencensus/propagation-b3/node_modules/semver": {
-      "version": "5.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
-      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
-      "bin": {
-        "semver": "bin/semver"
-      }
-    },
-    "node_modules/@opencensus/propagation-b3/node_modules/uuid": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
-      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
-      "deprecated": "Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.",
-      "bin": {
-        "uuid": "bin/uuid"
-      }
-    },
     "node_modules/@phc/format": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
       "integrity": "sha512-3VmRXEtw7RZKAf+4Tv1Ym9AGeo8r8+CjDi26x+7SYQil1UqtqdaokhzoEJohqlzt0m5kacJSDhJQkG/LWhpRBw=="
     },
     "node_modules/@pm2/io": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/@pm2/io/-/io-5.0.2.tgz",
-      "integrity": "sha512-XAvrNoQPKOyO/jJyCu8jPhLzlyp35MEf7w/carHXmWKddPzeNOFSEpSEqMzPDawsvpxbE+i918cNN+MwgVsStA==",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/@pm2/io/-/io-6.0.1.tgz",
+      "integrity": "sha512-KiA+shC6sULQAr9mGZ1pg+6KVW9MF8NpG99x26Lf/082/Qy8qsTCtnJy+HQReW1A9Rdf0C/404cz0RZGZro+IA==",
       "dependencies": {
-        "@opencensus/core": "0.0.9",
-        "@opencensus/propagation-b3": "0.0.8",
         "async": "~2.6.1",
         "debug": "~4.3.1",
         "eventemitter2": "^6.3.1",
       "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
       "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
     },
-    "node_modules/async-listener": {
-      "version": "0.6.10",
-      "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz",
-      "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==",
-      "dependencies": {
-        "semver": "^5.3.0",
-        "shimmer": "^1.1.0"
-      },
-      "engines": {
-        "node": "<=0.11.8 || >0.11.10"
-      }
-    },
-    "node_modules/async-listener/node_modules/semver": {
-      "version": "5.7.2",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
-      "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
-      "bin": {
-        "semver": "bin/semver"
-      }
-    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
       "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
       "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
     },
-    "node_modules/continuation-local-storage": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz",
-      "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==",
-      "dependencies": {
-        "async-listener": "^0.6.0",
-        "emitter-listener": "^1.1.1"
-      }
-    },
     "node_modules/convert-source-map": {
       "version": "1.9.0",
       "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
       "integrity": "sha512-4nToZ5jlPO14W82NkF32wyjhYqQByVaDmLy4J2/tYcAbJfgO2TKJC780Az1V13gzq4l73CJ0yuyalpXvxXXD9A==",
       "dev": true
     },
-    "node_modules/emitter-listener": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz",
-      "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==",
-      "dependencies": {
-        "shimmer": "^1.2.0"
-      }
-    },
     "node_modules/emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
     },
-    "node_modules/log-driver": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz",
-      "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg==",
-      "engines": {
-        "node": ">=0.8.6"
-      }
-    },
     "node_modules/log-symbols": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
       }
     },
     "node_modules/pm2": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.3.1.tgz",
-      "integrity": "sha512-DLVQHpSR1EegaTaRH3KbRXxpPVaqYwAp3uHSCtCsS++LSErvk07WSxuUnntFblBRqNU/w2KQyqs12mSq5wurkg==",
+      "version": "5.4.3",
+      "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.4.3.tgz",
+      "integrity": "sha512-4/I1htIHzZk1Y67UgOCo4F1cJtas1kSds31N8zN0PybO230id1nigyjGuGFzUnGmUFPmrJ0On22fO1ChFlp7VQ==",
       "dependencies": {
         "@pm2/agent": "~2.0.0",
-        "@pm2/io": "~5.0.0",
+        "@pm2/io": "~6.0.1",
         "@pm2/js-api": "~0.8.0",
         "@pm2/pm2-version-check": "latest",
         "async": "~3.2.0",
         "enquirer": "2.3.6",
         "eventemitter2": "5.0.1",
         "fclone": "1.0.11",
+        "js-yaml": "~4.1.0",
         "mkdirp": "1.0.4",
         "needle": "2.4.0",
         "pidusage": "~3.0",
         "semver": "^7.2",
         "source-map-support": "0.5.21",
         "sprintf-js": "1.1.2",
-        "vizion": "~2.2.1",
-        "yamljs": "0.3.0"
+        "vizion": "~2.2.1"
       },
       "bin": {
         "pm2": "bin/pm2",
         "pm2-runtime": "bin/pm2-runtime"
       },
       "engines": {
-        "node": ">=10.0.0"
+        "node": ">=12.0.0"
       },
       "optionalDependencies": {
         "pm2-sysmonit": "^1.2.8"
       "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
       "dev": true
     },
-    "node_modules/yamljs": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz",
-      "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==",
-      "dependencies": {
-        "argparse": "^1.0.7",
-        "glob": "^7.0.5"
-      },
-      "bin": {
-        "json2yaml": "bin/json2yaml",
-        "yaml2json": "bin/yaml2json"
-      }
-    },
-    "node_modules/yamljs/node_modules/argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "dependencies": {
-        "sprintf-js": "~1.0.2"
-      }
-    },
-    "node_modules/yamljs/node_modules/glob": {
-      "version": "7.2.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-      "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/yamljs/node_modules/sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
-    },
     "node_modules/yargs": {
       "version": "16.2.0",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
         "fastq": "^1.6.0"
       }
     },
-    "@opencensus/core": {
-      "version": "0.0.9",
-      "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.9.tgz",
-      "integrity": "sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q==",
-      "requires": {
-        "continuation-local-storage": "^3.2.1",
-        "log-driver": "^1.2.7",
-        "semver": "^5.5.0",
-        "shimmer": "^1.2.0",
-        "uuid": "^3.2.1"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.7.2",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
-          "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
-        },
-        "uuid": {
-          "version": "3.4.0",
-          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
-          "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
-        }
-      }
-    },
-    "@opencensus/propagation-b3": {
-      "version": "0.0.8",
-      "resolved": "https://registry.npmjs.org/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz",
-      "integrity": "sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A==",
-      "requires": {
-        "@opencensus/core": "^0.0.8",
-        "uuid": "^3.2.1"
-      },
-      "dependencies": {
-        "@opencensus/core": {
-          "version": "0.0.8",
-          "resolved": "https://registry.npmjs.org/@opencensus/core/-/core-0.0.8.tgz",
-          "integrity": "sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ==",
-          "requires": {
-            "continuation-local-storage": "^3.2.1",
-            "log-driver": "^1.2.7",
-            "semver": "^5.5.0",
-            "shimmer": "^1.2.0",
-            "uuid": "^3.2.1"
-          }
-        },
-        "semver": {
-          "version": "5.7.2",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
-          "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
-        },
-        "uuid": {
-          "version": "3.4.0",
-          "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
-          "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
-        }
-      }
-    },
     "@phc/format": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz",
       }
     },
     "@pm2/io": {
-      "version": "5.0.2",
-      "resolved": "https://registry.npmjs.org/@pm2/io/-/io-5.0.2.tgz",
-      "integrity": "sha512-XAvrNoQPKOyO/jJyCu8jPhLzlyp35MEf7w/carHXmWKddPzeNOFSEpSEqMzPDawsvpxbE+i918cNN+MwgVsStA==",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/@pm2/io/-/io-6.0.1.tgz",
+      "integrity": "sha512-KiA+shC6sULQAr9mGZ1pg+6KVW9MF8NpG99x26Lf/082/Qy8qsTCtnJy+HQReW1A9Rdf0C/404cz0RZGZro+IA==",
       "requires": {
-        "@opencensus/core": "0.0.9",
-        "@opencensus/propagation-b3": "0.0.8",
         "async": "~2.6.1",
         "debug": "~4.3.1",
         "eventemitter2": "^6.3.1",
       "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
       "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
     },
-    "async-listener": {
-      "version": "0.6.10",
-      "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz",
-      "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==",
-      "requires": {
-        "semver": "^5.3.0",
-        "shimmer": "^1.1.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.7.2",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
-          "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
-        }
-      }
-    },
     "balanced-match": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
       "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
       "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
     },
-    "continuation-local-storage": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz",
-      "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==",
-      "requires": {
-        "async-listener": "^0.6.0",
-        "emitter-listener": "^1.1.1"
-      }
-    },
     "convert-source-map": {
       "version": "1.9.0",
       "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
       "integrity": "sha512-4nToZ5jlPO14W82NkF32wyjhYqQByVaDmLy4J2/tYcAbJfgO2TKJC780Az1V13gzq4l73CJ0yuyalpXvxXXD9A==",
       "dev": true
     },
-    "emitter-listener": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz",
-      "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==",
-      "requires": {
-        "shimmer": "^1.2.0"
-      }
-    },
     "emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="
     },
-    "log-driver": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.7.tgz",
-      "integrity": "sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg=="
-    },
     "log-symbols": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
       }
     },
     "pm2": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.3.1.tgz",
-      "integrity": "sha512-DLVQHpSR1EegaTaRH3KbRXxpPVaqYwAp3uHSCtCsS++LSErvk07WSxuUnntFblBRqNU/w2KQyqs12mSq5wurkg==",
+      "version": "5.4.3",
+      "resolved": "https://registry.npmjs.org/pm2/-/pm2-5.4.3.tgz",
+      "integrity": "sha512-4/I1htIHzZk1Y67UgOCo4F1cJtas1kSds31N8zN0PybO230id1nigyjGuGFzUnGmUFPmrJ0On22fO1ChFlp7VQ==",
       "requires": {
         "@pm2/agent": "~2.0.0",
-        "@pm2/io": "~5.0.0",
+        "@pm2/io": "~6.0.1",
         "@pm2/js-api": "~0.8.0",
         "@pm2/pm2-version-check": "latest",
         "async": "~3.2.0",
         "enquirer": "2.3.6",
         "eventemitter2": "5.0.1",
         "fclone": "1.0.11",
+        "js-yaml": "~4.1.0",
         "mkdirp": "1.0.4",
         "needle": "2.4.0",
         "pidusage": "~3.0",
         "semver": "^7.2",
         "source-map-support": "0.5.21",
         "sprintf-js": "1.1.2",
-        "vizion": "~2.2.1",
-        "yamljs": "0.3.0"
+        "vizion": "~2.2.1"
       },
       "dependencies": {
         "chalk": {
       "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
       "dev": true
     },
-    "yamljs": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz",
-      "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==",
-      "requires": {
-        "argparse": "^1.0.7",
-        "glob": "^7.0.5"
-      },
-      "dependencies": {
-        "argparse": {
-          "version": "1.0.10",
-          "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-          "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-          "requires": {
-            "sprintf-js": "~1.0.2"
-          }
-        },
-        "glob": {
-          "version": "7.2.3",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
-          "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
-          "requires": {
-            "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"
-          }
-        },
-        "sprintf-js": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-          "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
-        }
-      }
-    },
     "yargs": {
       "version": "16.2.0",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
index e1fb1ca62a10c80a26288334c8c3d66d1742dd22..e3979a9aedefcea979589bdce89cb8007d119612 100644 (file)
@@ -24,7 +24,7 @@
     "better-sqlite3": "^9.4.3",
     "eslint-plugin-n": "^16.6.2",
     "pg-promise": "11.5.4",
-    "pm2": "^5.3.1",
+    "pm2": "^5.4.3",
     "uuid": "^9.0.1"
   },
   "devDependencies": {
index f848bea9f751c646a44baa93b874116949a889a4..f8827e6937c14fb9206d3409e22792f14d0829bf 100644 (file)
@@ -36,15 +36,15 @@ class PostgresDatabase extends BaseDatabase {
 
     if (this.queryLogLevel) {
       pgpInitOptions.query = (e) => {
-        this.logger[this.queryLogLevel](_fileScope('query'), e.query, { params: e.params });
+        this.logger[this.queryLogLevel](_fileScope('pgp:query'), e.query, { params: e.params });
       };
     }
 
-    pgpInitOptions.error = (err, e) => {
-      this.logger[this.queryLogLevel](_fileScope('pgp'), '', { err, e });
+    pgpInitOptions.error = (err, event) => {
+      this.logger.error(_fileScope('pgp:error'), '', { err, event });
     };
 
-    pgpInitOptions.receive = (data, result, e) => {
+    pgpInitOptions.receive = ({ data, result, ctx: event }) => {
       const exemplaryRow = data[0];
       for (const prop in exemplaryRow) {
         const camel = BaseDatabase._camelfy(prop);
@@ -59,7 +59,7 @@ class PostgresDatabase extends BaseDatabase {
       }
 
       if (this.queryLogLevel) {
-        this.logger[this.queryLogLevel](_fileScope('result'), e.query, PostgresDatabase._resultLog(result));
+        this.logger[this.queryLogLevel](_fileScope('pgp:result'), { query: event.query, ...PostgresDatabase._resultLog(result) });
       }
     };
     this._initStatements(_pgp);
@@ -67,9 +67,9 @@ class PostgresDatabase extends BaseDatabase {
 
   static _resultLog(result) {
     return {
-      command: result.commaand,
-      rowCount: result.rowCount,
-      duration: result.duration,
+      command: result?.commaand,
+      rowCount: result?.rowCount,
+      duration: result?.duration,
     };
   }
 
index d4d7a84a6c10b294fa3c9024a19f9e1d52d44ed4..ddfaaffbff9b305b682ac3925cbe64dc947bac36 100644 (file)
@@ -41,6 +41,9 @@ BEGIN;
                password TEXT
        );
 
+       -- system entries
+       INSERT INTO link (id, url) VALUES ('static', '/static/index.html'), ('favicon.ico', '/static/favicon.ico'), ('robots.txt', '/static/robots.txt');
+
        -- migration complete
        INSERT INTO _meta_schema_version (major, minor, patch) VALUES (1, 0, 0);
 COMMIT;
diff --git a/src/db/postgres/sql/schema/1.0.1/apply.sql b/src/db/postgres/sql/schema/1.0.1/apply.sql
new file mode 100644 (file)
index 0000000..94374ff
--- /dev/null
@@ -0,0 +1,14 @@
+BEGIN;
+
+       ALTER TABLE link
+               ADD COLUMN is_special BOOLEAN NOT NULL DEFAULT false
+       ;
+
+       UPDATE link
+               SET is_special = true
+       WHERE
+               url = ANY('/static/robots.txt', '/static/index.html',  '/static/favicon.ico');
+
+       INSERT INTO _meta_schema_version (major, minor, patch) VALUES (1, 0, 1);
+
+COMMIT;
diff --git a/src/db/postgres/sql/schema/1.0.1/revert.sql b/src/db/postgres/sql/schema/1.0.1/revert.sql
new file mode 100644 (file)
index 0000000..08fa1c4
--- /dev/null
@@ -0,0 +1,9 @@
+BEGIN;
+
+       ALTER TABLE link
+               DROP COLUMN is_special
+       ;
+
+       DELETE FROM _meta_schema_version WHERE major = 1 AND minor = 0 AND patch = 1;
+
+COMMIT;
index 8343f3dc696bd60a101451340ce8c1967a280143..27fdae3115f7bad395729658e35242a16259afa4 100644 (file)
@@ -1,8 +1,12 @@
-CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
-CREATE TABLE IF NOT EXISTS _meta_schema_version (
-       major BIGINT NOT NULL,
-       minor BIGINT NOT NULL,
-       patch BIGINT NOT NULL,
-       applied TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
-       PRIMARY KEY (major, minor, patch)
-);
+BEGIN;
+       CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
+
+       CREATE TABLE IF NOT EXISTS _meta_schema_version (
+               major BIGINT NOT NULL,
+               minor BIGINT NOT NULL,
+               patch BIGINT NOT NULL,
+               applied TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
+               PRIMARY KEY (major, minor, patch)
+       );
+       INSERT INTO _meta_schema_version (major, minor, patch) VALUES (0, 0, 0);
+COMMIT;
index 59711425b2b6fc896c99d68f38132c479286065d..67c1f788c0b9f0f7a81372e38ff1eeb91a6d4309 100644 (file)
@@ -57,9 +57,9 @@ class SQLiteDatabase extends BaseDatabase {
         name: 'auth',
         statements: [
           `CREATE TABLE IF NOT EXISTS auth (
-            id TEXT NOT NULL PRIMARY KEY,
-            secret TEXT NOT NULL,
-            password TEXT
+            id TEXT NOT NULL PRIMARY KEY CHECK (typeof(id) = 'text'),
+            secret TEXT NOT NULL CHECK (typeof(secret) = 'text'),
+            password TEXT CHECK (typeof(password) IN ('text', 'null'))
           )`,
           `INSERT INTO auth (id, secret, password)
             VALUES
@@ -70,13 +70,14 @@ class SQLiteDatabase extends BaseDatabase {
         name: 'link',
         statements: [
           `CREATE TABLE IF NOT EXISTS link (
-            id TEXT NOT NULL PRIMARY KEY,
-            url TEXT NOT NULL UNIQUE,
-            created INTEGER NOT NULL DEFAULT ${EPOCH_NOW},
-            last_access INTEGER NOT NULL DEFAULT 0,
-            accesses INTEGER NOT NULL DEFAULT 0,
-            expires INTEGER,
-            auth_token TEXT
+            id TEXT NOT NULL PRIMARY KEY CHECK (typeof(id) = 'text'),
+            url TEXT NOT NULL UNIQUE CHECK (typeof(url) = 'text'),
+            created INTEGER NOT NULL DEFAULT ${EPOCH_NOW} CHECK (typeof(created) = 'integer'),
+            last_access INTEGER NOT NULL DEFAULT 0 CHECK (typeof(last_access) = 'integer'),
+            accesses INTEGER NOT NULL DEFAULT 0 CHECK (typeof(accesses) = 'integer'),
+            expires INTEGER CHECK (typeof(expires) IN ('integer', 'null')),
+            auth_token TEXT CHECK (typeof(auth_token) IN ('text', 'null')),
+            is_special INTEGER NOT NULL DEFAULT 0 CHECK (is_special IN (0, 1))
           )`,
           'CREATE INDEX IF NOT EXISTS link_url_idx ON link(url)',
         ],
@@ -203,6 +204,7 @@ class SQLiteDatabase extends BaseDatabase {
       accesses: Number(link.accesses),
       expires: ('expires' in link) ? Number(link.expires) : undefined,
       authToken: link.authToken,
+      isSpecial: !! link.isSpecial,
     };
   }
 
index 0a1b8037832e6bd2c015b0cd64e1f60181927386..62f245a408f27697e72ad69b739d7ab84a813bc7 100644 (file)
@@ -144,6 +144,7 @@ describe('SQLiteDatabase', function () {
     it('stubbed success', async function () {
       const returns = {
         id: 'id',
+        isSpecial: false,
         url: 'url',
         created: 0,
         expires: 0,
@@ -153,6 +154,7 @@ describe('SQLiteDatabase', function () {
        };
        const expected = {
          id: 'id',
+         isSpecial: false,
          url: 'url',
          created: 0,
          expires: 0,
@@ -188,6 +190,7 @@ describe('SQLiteDatabase', function () {
     it('stubbed success', async function () {
       const returns = {
         id: 'id',
+        isSpecial: false,
         url: 'url',
         created: 0,
         expires: 123,
@@ -197,6 +200,7 @@ describe('SQLiteDatabase', function () {
       };
       const expected = {
         id: 'id',
+        isSpecial: false,
         url: 'url',
         created: 0,
         expires: 123,
@@ -233,6 +237,7 @@ describe('SQLiteDatabase', function () {
     it('stubbed exists success', async function () {
       const returns = {
         id: 'id',
+        isSpecial: false,
         url: 'url',
         created: 0,
         expires: 0,
@@ -242,6 +247,7 @@ describe('SQLiteDatabase', function () {
        };
        const expected = {
         id: 'id',
+        isSpecial: false,
         url: 'url',
         created: 0,
         expires: 0,
@@ -409,6 +415,7 @@ describe('SQLiteDatabase', function () {
       const returns = [
         {
           id: 'id',
+          isSpecial: false,
           url: 'url',
           created: 0,
           expires: 0,
@@ -420,6 +427,7 @@ describe('SQLiteDatabase', function () {
       const expected = [
         {
           id: 'id',
+          isSpecial: false,
           url: 'url',
           created: 0,
           expires: 0,