keyv-github 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ {
2
+ "plugins": [
3
+ "@semantic-release/commit-analyzer",
4
+ "@semantic-release/release-notes-generator",
5
+ "@semantic-release/npm",
6
+ [
7
+ "@semantic-release/git",
8
+ {
9
+ "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
10
+ }
11
+ ],
12
+ "@semantic-release/github"
13
+ ]
14
+ }
package/README.md CHANGED
@@ -45,11 +45,38 @@ await kv.delete("data/hello.txt");
45
45
  new KeyvGithub(repoUrl, options?)
46
46
  ```
47
47
 
48
- | Option | Type | Default | Description |
49
- |---|---|---|---|
50
- | `branch` | `string` | parsed from URL or `"main"` | Target branch |
51
- | `client` | `Octokit` | `new Octokit()` | Authenticated Octokit instance |
52
- | `msg` | `(key, value) => string` | `"update <key>"` / `"delete <key>"` | Customize commit messages; `value` is `null` for deletes |
48
+ | Option | Type | Default | Description |
49
+ | -------- | ------------------------ | ----------------------------------- | -------------------------------------------------------- |
50
+ | `branch` | `string` | parsed from URL or `"main"` | Target branch |
51
+ | `client` | `Octokit` | `new Octokit()` | Authenticated Octokit instance |
52
+ | `msg` | `(key, value) => string` | `"update <key>"` / `"delete <key>"` | Customize commit messages; `value` is `null` for deletes |
53
+ | `prefix` | `string` | `""` | Path prefix prepended to every key (e.g. `"data/"`) |
54
+ | `suffix` | `string` | `""` | Path suffix appended to every key (e.g. `".json"`) |
55
+
56
+ ### Store limitations
57
+
58
+ When using `KeyvGithub` directly (without wrapping in `Keyv`):
59
+
60
+ - **Values must be strings** — objects, arrays, and numbers will throw an error
61
+ - **TTL is not supported** — passing a TTL parameter throws an error
62
+
63
+ To store non-string values or use TTL, wrap the store with `new Keyv(store)`:
64
+
65
+ ```ts
66
+ // Direct usage: strings only, no TTL
67
+ await store.set("key", "string value"); // ✓
68
+ await store.set("key", { obj: true }); // ✗ throws error
69
+ await store.set("key", "value", 1000); // ✗ throws error
70
+
71
+ // With Keyv wrapper: any serializable value, TTL supported
72
+ const kv = new Keyv({ store });
73
+ await kv.set("key", { obj: true }); // ✓ serialized automatically
74
+ await kv.set("key", "value", 1000); // ✓ TTL handled by Keyv
75
+ ```
76
+
77
+ ### TTL
78
+
79
+ TTL is **not enforced at the adapter level** — GitHub has no native file expiry. If you pass a `ttl` to `new Keyv({ store, ttl })`, Keyv handles it by wrapping values as `{"value":…,"expires":…}` and filtering on read. Expired files remain in the repo as inert files until overwritten or deleted. This adapter is best suited for long-lived or permanent storage.
53
80
 
54
81
  ### URL formats accepted
55
82
 
@@ -66,9 +93,7 @@ owner/repo/tree/my-branch
66
93
  ```ts
67
94
  const store = new KeyvGithub("owner/repo", {
68
95
  msg: (key, value) =>
69
- value === null
70
- ? `chore: delete ${key}`
71
- : `chore: update ${key} → ${value.slice(0, 40)}`,
96
+ value === null ? `chore: delete ${key}` : `chore: update ${key} → ${value.slice(0, 40)}`,
72
97
  });
73
98
  ```
74
99
 
@@ -84,6 +109,17 @@ Keys must be valid relative file paths:
84
109
 
85
110
  Invalid keys throw synchronously before any API request.
86
111
 
112
+ ## See Also
113
+
114
+ Other Keyv storage adapters by the same author:
115
+
116
+ - [keyv-sqlite](https://github.com/snomiao/keyv-sqlite) — SQLite storage adapter
117
+ - [keyv-mongodb-store](https://github.com/snomiao/keyv-mongodb-store) — MongoDB storage adapter
118
+ - [keyv-nedb-store](https://github.com/snomiao/keyv-nedb-store) — NeDB embedded file-based adapter
119
+ - [keyv-dir-store](https://github.com/snomiao/keyv-dir-store) — file-per-key directory adapter with TTL via mtime
120
+ - [keyv-cache-proxy](https://github.com/snomiao/keyv-cache-proxy) — transparent caching proxy that wraps any object
121
+ - [keyv-nest](https://github.com/snomiao/keyv-nest) — hierarchical multi-layer caching adapter
122
+
87
123
  ## License
88
124
 
89
125
  MIT
package/bun.lock CHANGED
@@ -9,6 +9,7 @@
9
9
  "octokit": "^5.0.5",
10
10
  },
11
11
  "devDependencies": {
12
+ "@semantic-release/git": "^10.0.1",
12
13
  "@types/bun": "latest",
13
14
  "semantic-release": "^25.0.3",
14
15
  "tsdown": "^0.20.3",
@@ -149,7 +150,9 @@
149
150
 
150
151
  "@semantic-release/commit-analyzer": ["@semantic-release/commit-analyzer@13.0.1", "", { "dependencies": { "conventional-changelog-angular": "^8.0.0", "conventional-changelog-writer": "^8.0.0", "conventional-commits-filter": "^5.0.0", "conventional-commits-parser": "^6.0.0", "debug": "^4.0.0", "import-from-esm": "^2.0.0", "lodash-es": "^4.17.21", "micromatch": "^4.0.2" }, "peerDependencies": { "semantic-release": ">=20.1.0" } }, "sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ=="],
151
152
 
152
- "@semantic-release/error": ["@semantic-release/error@4.0.0", "", {}, "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ=="],
153
+ "@semantic-release/error": ["@semantic-release/error@3.0.0", "", {}, "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw=="],
154
+
155
+ "@semantic-release/git": ["@semantic-release/git@10.0.1", "", { "dependencies": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", "debug": "^4.0.0", "dir-glob": "^3.0.0", "execa": "^5.0.0", "lodash": "^4.17.4", "micromatch": "^4.0.0", "p-reduce": "^2.0.0" }, "peerDependencies": { "semantic-release": ">=18.0.0" } }, "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w=="],
153
156
 
154
157
  "@semantic-release/github": ["@semantic-release/github@12.0.6", "", { "dependencies": { "@octokit/core": "^7.0.0", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-retry": "^8.0.0", "@octokit/plugin-throttling": "^11.0.0", "@semantic-release/error": "^4.0.0", "aggregate-error": "^5.0.0", "debug": "^4.3.4", "dir-glob": "^3.0.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "issue-parser": "^7.0.0", "lodash-es": "^4.17.21", "mime": "^4.0.0", "p-filter": "^4.0.0", "tinyglobby": "^0.2.14", "undici": "^7.0.0", "url-join": "^5.0.0" }, "peerDependencies": { "semantic-release": ">=24.1.0" } }, "sha512-aYYFkwHW3c6YtHwQF0t0+lAjlU+87NFOZuH2CvWFD0Ylivc7MwhZMiHOJ0FMpIgPpCVib/VUAcOwvrW0KnxQtA=="],
155
158
 
@@ -193,7 +196,7 @@
193
196
 
194
197
  "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
195
198
 
196
- "aggregate-error": ["aggregate-error@5.0.0", "", { "dependencies": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" } }, "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw=="],
199
+ "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
197
200
 
198
201
  "ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="],
199
202
 
@@ -231,7 +234,7 @@
231
234
 
232
235
  "char-regex": ["char-regex@1.0.2", "", {}, "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="],
233
236
 
234
- "clean-stack": ["clean-stack@5.3.0", "", { "dependencies": { "escape-string-regexp": "5.0.0" } }, "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg=="],
237
+ "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="],
235
238
 
236
239
  "cli-highlight": ["cli-highlight@2.1.11", "", { "dependencies": { "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" } }, "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg=="],
237
240
 
@@ -299,7 +302,7 @@
299
302
 
300
303
  "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
301
304
 
302
- "execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],
305
+ "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
303
306
 
304
307
  "fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="],
305
308
 
@@ -349,7 +352,7 @@
349
352
 
350
353
  "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
351
354
 
352
- "human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
355
+ "human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
353
356
 
354
357
  "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
355
358
 
@@ -359,7 +362,7 @@
359
362
 
360
363
  "import-without-cache": ["import-without-cache@0.2.5", "", {}, "sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A=="],
361
364
 
362
- "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
365
+ "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
363
366
 
364
367
  "index-to-position": ["index-to-position@1.2.0", "", {}, "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw=="],
365
368
 
@@ -379,7 +382,7 @@
379
382
 
380
383
  "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
381
384
 
382
- "is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
385
+ "is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
383
386
 
384
387
  "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
385
388
 
@@ -411,6 +414,8 @@
411
414
 
412
415
  "locate-path": ["locate-path@2.0.0", "", { "dependencies": { "p-locate": "^2.0.0", "path-exists": "^3.0.0" } }, "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA=="],
413
416
 
417
+ "lodash": ["lodash@4.17.23", "", {}, "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w=="],
418
+
414
419
  "lodash-es": ["lodash-es@4.17.23", "", {}, "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg=="],
415
420
 
416
421
  "lodash.capitalize": ["lodash.capitalize@4.2.1", "", {}, "sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw=="],
@@ -439,7 +444,7 @@
439
444
 
440
445
  "mime": ["mime@4.1.0", "", { "bin": { "mime": "bin/cli.js" } }, "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw=="],
441
446
 
442
- "mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
447
+ "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
443
448
 
444
449
  "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
445
450
 
@@ -459,7 +464,7 @@
459
464
 
460
465
  "npm": ["npm@11.10.0", "", { "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", "@npmcli/arborist": "^9.3.0", "@npmcli/config": "^10.7.0", "@npmcli/fs": "^5.0.0", "@npmcli/map-workspaces": "^5.0.3", "@npmcli/metavuln-calculator": "^9.0.3", "@npmcli/package-json": "^7.0.4", "@npmcli/promise-spawn": "^9.0.1", "@npmcli/redact": "^4.0.0", "@npmcli/run-script": "^10.0.3", "@sigstore/tuf": "^4.0.1", "abbrev": "^4.0.0", "archy": "~1.0.0", "cacache": "^20.0.3", "chalk": "^5.6.2", "ci-info": "^4.4.0", "cli-columns": "^4.0.0", "fastest-levenshtein": "^1.0.16", "fs-minipass": "^3.0.3", "glob": "^13.0.2", "graceful-fs": "^4.2.11", "hosted-git-info": "^9.0.2", "ini": "^6.0.0", "init-package-json": "^8.2.4", "is-cidr": "^6.0.3", "json-parse-even-better-errors": "^5.0.0", "libnpmaccess": "^10.0.3", "libnpmdiff": "^8.1.1", "libnpmexec": "^10.2.1", "libnpmfund": "^7.0.15", "libnpmorg": "^8.0.1", "libnpmpack": "^9.1.1", "libnpmpublish": "^11.1.3", "libnpmsearch": "^9.0.1", "libnpmteam": "^8.0.2", "libnpmversion": "^8.0.3", "make-fetch-happen": "^15.0.3", "minimatch": "^10.1.1", "minipass": "^7.1.1", "minipass-pipeline": "^1.2.4", "ms": "^2.1.2", "node-gyp": "^12.2.0", "nopt": "^9.0.0", "npm-audit-report": "^7.0.0", "npm-install-checks": "^8.0.0", "npm-package-arg": "^13.0.2", "npm-pick-manifest": "^11.0.3", "npm-profile": "^12.0.1", "npm-registry-fetch": "^19.1.1", "npm-user-validate": "^4.0.0", "p-map": "^7.0.4", "pacote": "^21.3.1", "parse-conflict-json": "^5.0.1", "proc-log": "^6.1.0", "qrcode-terminal": "^0.12.0", "read": "^5.0.1", "semver": "^7.7.4", "spdx-expression-parse": "^4.0.0", "ssri": "^13.0.1", "supports-color": "^10.2.2", "tar": "^7.5.7", "text-table": "~0.2.0", "tiny-relative-date": "^2.0.2", "treeverse": "^3.0.0", "validate-npm-package-name": "^7.0.2", "which": "^6.0.1" }, "bin": { "npm": "bin/npm-cli.js", "npx": "bin/npx-cli.js" } }, "sha512-i8hE43iSIAMFuYVi8TxsEISdELM4fIza600aLjJ0ankGPLqd0oTPKMJqAcO/QWm307MbSlWGzJcNZ0lGMQgHPA=="],
461
466
 
462
- "npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="],
467
+ "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
463
468
 
464
469
  "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
465
470
 
@@ -467,7 +472,7 @@
467
472
 
468
473
  "octokit": ["octokit@5.0.5", "", { "dependencies": { "@octokit/app": "^16.1.2", "@octokit/core": "^7.0.6", "@octokit/oauth-app": "^8.0.3", "@octokit/plugin-paginate-graphql": "^6.0.0", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-rest-endpoint-methods": "^17.0.0", "@octokit/plugin-retry": "^8.0.3", "@octokit/plugin-throttling": "^11.0.3", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "@octokit/webhooks": "^14.0.0" } }, "sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw=="],
469
474
 
470
- "onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
475
+ "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
471
476
 
472
477
  "p-each-series": ["p-each-series@3.0.0", "", {}, "sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw=="],
473
478
 
@@ -483,7 +488,7 @@
483
488
 
484
489
  "p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="],
485
490
 
486
- "p-reduce": ["p-reduce@3.0.0", "", {}, "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q=="],
491
+ "p-reduce": ["p-reduce@2.1.0", "", {}, "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw=="],
487
492
 
488
493
  "p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="],
489
494
 
@@ -555,7 +560,7 @@
555
560
 
556
561
  "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
557
562
 
558
- "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
563
+ "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="],
559
564
 
560
565
  "signale": ["signale@1.4.0", "", { "dependencies": { "chalk": "^2.3.2", "figures": "^2.0.0", "pkg-conf": "^2.1.0" } }, "sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w=="],
561
566
 
@@ -585,7 +590,7 @@
585
590
 
586
591
  "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
587
592
 
588
- "strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
593
+ "strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
589
594
 
590
595
  "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
591
596
 
@@ -683,6 +688,16 @@
683
688
 
684
689
  "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="],
685
690
 
691
+ "@semantic-release/github/@semantic-release/error": ["@semantic-release/error@4.0.0", "", {}, "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ=="],
692
+
693
+ "@semantic-release/github/aggregate-error": ["aggregate-error@5.0.0", "", { "dependencies": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" } }, "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw=="],
694
+
695
+ "@semantic-release/npm/@semantic-release/error": ["@semantic-release/error@4.0.0", "", {}, "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ=="],
696
+
697
+ "@semantic-release/npm/aggregate-error": ["aggregate-error@5.0.0", "", { "dependencies": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" } }, "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw=="],
698
+
699
+ "@semantic-release/npm/execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],
700
+
686
701
  "@semantic-release/release-notes-generator/get-stream": ["get-stream@7.0.1", "", {}, "sha512-3M8C1EOFN6r8AMUhwUAACIoXZJEOufDU5+0gFFN5uNs6XYOralD2Pqkl7m046va6x77FwposWXbAhPPIOus7mQ=="],
687
702
 
688
703
  "@semantic-release/release-notes-generator/read-package-up": ["read-package-up@11.0.0", "", { "dependencies": { "find-up-simple": "^1.0.0", "read-pkg": "^9.0.0", "type-fest": "^4.6.0" } }, "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ=="],
@@ -697,8 +712,6 @@
697
712
 
698
713
  "env-ci/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="],
699
714
 
700
- "execa/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
701
-
702
715
  "import-fresh/resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
703
716
 
704
717
  "load-json-file/parse-json": ["parse-json@4.0.0", "", { "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" } }, "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw=="],
@@ -1015,14 +1028,18 @@
1015
1028
 
1016
1029
  "npm/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
1017
1030
 
1018
- "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
1019
-
1020
- "npm-run-path/unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="],
1021
-
1022
1031
  "parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="],
1023
1032
 
1024
1033
  "read-pkg/parse-json": ["parse-json@8.3.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "index-to-position": "^1.1.0", "type-fest": "^4.39.1" } }, "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ=="],
1025
1034
 
1035
+ "semantic-release/@semantic-release/error": ["@semantic-release/error@4.0.0", "", {}, "sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ=="],
1036
+
1037
+ "semantic-release/aggregate-error": ["aggregate-error@5.0.0", "", { "dependencies": { "clean-stack": "^5.2.0", "indent-string": "^5.0.0" } }, "sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw=="],
1038
+
1039
+ "semantic-release/execa": ["execa@9.6.1", "", { "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", "figures": "^6.1.0", "get-stream": "^9.0.0", "human-signals": "^8.0.1", "is-plain-obj": "^4.1.0", "is-stream": "^4.0.1", "npm-run-path": "^6.0.0", "pretty-ms": "^9.2.0", "signal-exit": "^4.1.0", "strip-final-newline": "^4.0.0", "yoctocolors": "^2.1.1" } }, "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA=="],
1040
+
1041
+ "semantic-release/p-reduce": ["p-reduce@3.0.0", "", {}, "sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q=="],
1042
+
1026
1043
  "signale/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
1027
1044
 
1028
1045
  "signale/figures": ["figures@2.0.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA=="],
@@ -1033,6 +1050,26 @@
1033
1050
 
1034
1051
  "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
1035
1052
 
1053
+ "@semantic-release/github/aggregate-error/clean-stack": ["clean-stack@5.3.0", "", { "dependencies": { "escape-string-regexp": "5.0.0" } }, "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg=="],
1054
+
1055
+ "@semantic-release/github/aggregate-error/indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
1056
+
1057
+ "@semantic-release/npm/aggregate-error/clean-stack": ["clean-stack@5.3.0", "", { "dependencies": { "escape-string-regexp": "5.0.0" } }, "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg=="],
1058
+
1059
+ "@semantic-release/npm/aggregate-error/indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
1060
+
1061
+ "@semantic-release/npm/execa/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
1062
+
1063
+ "@semantic-release/npm/execa/human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
1064
+
1065
+ "@semantic-release/npm/execa/is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
1066
+
1067
+ "@semantic-release/npm/execa/npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="],
1068
+
1069
+ "@semantic-release/npm/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
1070
+
1071
+ "@semantic-release/npm/execa/strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
1072
+
1036
1073
  "@semantic-release/release-notes-generator/read-package-up/read-pkg": ["read-pkg@9.0.1", "", { "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", "parse-json": "^8.0.0", "type-fest": "^4.6.0", "unicorn-magic": "^0.1.0" } }, "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA=="],
1037
1074
 
1038
1075
  "@semantic-release/release-notes-generator/read-package-up/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
@@ -1057,6 +1094,10 @@
1057
1094
 
1058
1095
  "env-ci/execa/npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="],
1059
1096
 
1097
+ "env-ci/execa/onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="],
1098
+
1099
+ "env-ci/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
1100
+
1060
1101
  "env-ci/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="],
1061
1102
 
1062
1103
  "npm/minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="],
@@ -1069,12 +1110,32 @@
1069
1110
 
1070
1111
  "read-pkg/parse-json/type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
1071
1112
 
1113
+ "semantic-release/aggregate-error/clean-stack": ["clean-stack@5.3.0", "", { "dependencies": { "escape-string-regexp": "5.0.0" } }, "sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg=="],
1114
+
1115
+ "semantic-release/aggregate-error/indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
1116
+
1117
+ "semantic-release/execa/get-stream": ["get-stream@9.0.1", "", { "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" } }, "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA=="],
1118
+
1119
+ "semantic-release/execa/human-signals": ["human-signals@8.0.1", "", {}, "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ=="],
1120
+
1121
+ "semantic-release/execa/is-stream": ["is-stream@4.0.1", "", {}, "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A=="],
1122
+
1123
+ "semantic-release/execa/npm-run-path": ["npm-run-path@6.0.0", "", { "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" } }, "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA=="],
1124
+
1125
+ "semantic-release/execa/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
1126
+
1127
+ "semantic-release/execa/strip-final-newline": ["strip-final-newline@4.0.0", "", {}, "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw=="],
1128
+
1072
1129
  "signale/chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
1073
1130
 
1074
1131
  "signale/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
1075
1132
 
1076
1133
  "signale/figures/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
1077
1134
 
1135
+ "@semantic-release/npm/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
1136
+
1137
+ "@semantic-release/npm/execa/npm-run-path/unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="],
1138
+
1078
1139
  "@semantic-release/release-notes-generator/read-package-up/read-pkg/normalize-package-data": ["normalize-package-data@6.0.2", "", { "dependencies": { "hosted-git-info": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g=="],
1079
1140
 
1080
1141
  "@semantic-release/release-notes-generator/read-package-up/read-pkg/parse-json": ["parse-json@8.3.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "index-to-position": "^1.1.0", "type-fest": "^4.39.1" } }, "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ=="],
@@ -1095,10 +1156,16 @@
1095
1156
 
1096
1157
  "env-ci/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
1097
1158
 
1159
+ "env-ci/execa/onetime/mimic-fn": ["mimic-fn@4.0.0", "", {}, "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw=="],
1160
+
1098
1161
  "npm/minipass-flush/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
1099
1162
 
1100
1163
  "npm/minipass-pipeline/minipass/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
1101
1164
 
1165
+ "semantic-release/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="],
1166
+
1167
+ "semantic-release/execa/npm-run-path/unicorn-magic": ["unicorn-magic@0.3.0", "", {}, "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA=="],
1168
+
1102
1169
  "signale/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="],
1103
1170
 
1104
1171
  "@semantic-release/release-notes-generator/read-package-up/read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="],
package/dist/index.d.mts CHANGED
@@ -3,6 +3,11 @@ import { Octokit } from "octokit";
3
3
  import { KeyvStoreAdapter, StoredData } from "keyv";
4
4
 
5
5
  //#region src/index.d.ts
6
+ /** Minimal Map-like interface for SHA caching. */
7
+ interface ShaMap {
8
+ get(key: string): string | null | undefined;
9
+ set(key: string, value: string | null): void;
10
+ }
6
11
  interface KeyvGithubOptions {
7
12
  url: string;
8
13
  branch?: string;
@@ -15,6 +20,8 @@ interface KeyvGithubOptions {
15
20
  prefix?: string;
16
21
  /** Path suffix appended to every key (e.g. '.json'). Defaults to ''. */
17
22
  suffix?: string;
23
+ /** SHA cache map. Defaults to new Map(). Pass any keyv-like object with get/set. */
24
+ shaMap?: ShaMap;
18
25
  }
19
26
  /**
20
27
  * Keyv storage adapter backed by a GitHub repository.
@@ -35,6 +42,8 @@ declare class KeyvGithub extends EventEmitter implements KeyvStoreAdapter {
35
42
  readonly enableClear: boolean;
36
43
  readonly prefix: string;
37
44
  readonly suffix: string;
45
+ /** SHA cache: key → sha (string), null (file doesn't exist), undefined (unknown). */
46
+ readonly shaMap: ShaMap;
38
47
  constructor(url: string, options?: Omit<KeyvGithubOptions, "url">);
39
48
  /** Converts a user key to the GitHub file path. */
40
49
  private toPath;
@@ -46,7 +55,7 @@ declare class KeyvGithub extends EventEmitter implements KeyvStoreAdapter {
46
55
  private static isHttpError;
47
56
  private validatePath;
48
57
  get<Value>(key: string): Promise<StoredData<Value> | undefined>;
49
- set(key: string, value: any, _ttl?: number): Promise<void>;
58
+ set(key: string, value: any, ttl?: number): Promise<void>;
50
59
  delete(key: string): Promise<boolean>;
51
60
  has(key: string): Promise<boolean>;
52
61
  /**
@@ -59,7 +68,6 @@ declare class KeyvGithub extends EventEmitter implements KeyvStoreAdapter {
59
68
  setMany(values: Array<{
60
69
  key: string;
61
70
  value: any;
62
- ttl?: number;
63
71
  }>): Promise<void>;
64
72
  /**
65
73
  * Keyv batch-delete: deletes multiple keys in a single commit (7 API calls total).
@@ -70,4 +78,4 @@ declare class KeyvGithub extends EventEmitter implements KeyvStoreAdapter {
70
78
  iterator<Value>(prefix?: string): AsyncGenerator<[string, Value | undefined]>;
71
79
  }
72
80
  //#endregion
73
- export { KeyvGithubOptions, KeyvGithub as default };
81
+ export { KeyvGithubOptions, ShaMap, KeyvGithub as default };
package/dist/index.mjs CHANGED
@@ -23,6 +23,8 @@ var KeyvGithub = class KeyvGithub extends EventEmitter {
23
23
  enableClear;
24
24
  prefix;
25
25
  suffix;
26
+ /** SHA cache: key → sha (string), null (file doesn't exist), undefined (unknown). */
27
+ shaMap;
26
28
  constructor(url, options = {}) {
27
29
  super();
28
30
  const match = url.match(/(?:.*github\.com[/:])?([^/:]+)\/([^/]+?)(?:\.git)?(?:\/tree\/([^?#]+))?(?:[?#].*)?$/);
@@ -39,6 +41,7 @@ var KeyvGithub = class KeyvGithub extends EventEmitter {
39
41
  this.enableClear = options.enableClear ?? false;
40
42
  this.prefix = options.prefix ?? "";
41
43
  this.suffix = options.suffix ?? "";
44
+ this.shaMap = options.shaMap ?? /* @__PURE__ */ new Map();
42
45
  }
43
46
  /** Converts a user key to the GitHub file path. */
44
47
  toPath(key) {
@@ -65,37 +68,41 @@ var KeyvGithub = class KeyvGithub extends EventEmitter {
65
68
  if (path.split("/").some((seg) => seg === ".." || seg === ".")) throw new Error(`Path must not contain '.' or '..' segments: ${path}`);
66
69
  }
67
70
  async get(key) {
68
- this.validatePath(this.toPath(key));
71
+ const path = this.toPath(key);
72
+ this.validatePath(path);
69
73
  try {
70
74
  const { data } = await this.rest.repos.getContent({
71
75
  owner: this.owner,
72
76
  repo: this.repo,
73
- path: this.toPath(key),
77
+ path,
74
78
  ref: this.ref
75
79
  });
76
- if (Array.isArray(data) || data.type !== "file") return void 0;
80
+ if (Array.isArray(data) || data.type !== "file") {
81
+ this.shaMap.set(path, null);
82
+ return;
83
+ }
84
+ this.shaMap.set(path, data.sha);
77
85
  return Buffer.from(data.content, "base64").toString("utf-8");
78
86
  } catch (e) {
79
- if (KeyvGithub.isHttpError(e) && e.status === 404) return void 0;
87
+ if (KeyvGithub.isHttpError(e) && e.status === 404) {
88
+ this.shaMap.set(path, null);
89
+ return;
90
+ }
80
91
  throw e;
81
92
  }
82
93
  }
83
- async set(key, value, _ttl) {
84
- this.validatePath(this.toPath(key));
94
+ async set(key, value, ttl) {
95
+ if (ttl !== void 0) throw new Error("TTL is not supported natively by keyv-github. Use new Keyv(store) which handles TTL via value expiration metadata.");
96
+ if (typeof value !== "string") throw new Error("keyv-github only supports string values natively. Use new Keyv(store) which serializes values automatically.");
85
97
  const path = this.toPath(key);
86
- let sha;
87
- try {
88
- const { data } = await this.rest.repos.getContent({
89
- owner: this.owner,
90
- repo: this.repo,
91
- path,
92
- ref: this.ref
93
- });
94
- if (!Array.isArray(data) && data.type === "file") sha = data.sha;
95
- } catch (e) {
96
- if (!KeyvGithub.isHttpError(e) || e.status !== 404) throw e;
98
+ this.validatePath(path);
99
+ let cachedSha = this.shaMap.get(path);
100
+ if (cachedSha === void 0) {
101
+ await this.get(key);
102
+ cachedSha = this.shaMap.get(path);
97
103
  }
98
- await this.rest.repos.createOrUpdateFileContents({
104
+ const sha = cachedSha ?? void 0;
105
+ const { data } = await this.rest.repos.createOrUpdateFileContents({
99
106
  owner: this.owner,
100
107
  repo: this.repo,
101
108
  path,
@@ -104,44 +111,58 @@ var KeyvGithub = class KeyvGithub extends EventEmitter {
104
111
  sha,
105
112
  branch: this.ref
106
113
  });
114
+ this.shaMap.set(path, data.content?.sha ?? null);
107
115
  }
108
116
  async delete(key) {
109
- this.validatePath(this.toPath(key));
110
117
  const path = this.toPath(key);
118
+ this.validatePath(path);
119
+ let cachedSha = this.shaMap.get(path);
120
+ if (cachedSha === void 0) {
121
+ await this.get(key);
122
+ cachedSha = this.shaMap.get(path);
123
+ }
124
+ if (!cachedSha) return false;
125
+ const sha = cachedSha;
111
126
  try {
112
- const { data } = await this.rest.repos.getContent({
113
- owner: this.owner,
114
- repo: this.repo,
115
- path,
116
- ref: this.ref
117
- });
118
- if (Array.isArray(data) || data.type !== "file") return false;
119
127
  await this.rest.repos.deleteFile({
120
128
  owner: this.owner,
121
129
  repo: this.repo,
122
130
  path,
123
131
  message: this.msg(path, null),
124
- sha: data.sha,
132
+ sha,
125
133
  branch: this.ref
126
134
  });
135
+ this.shaMap.set(path, null);
127
136
  return true;
128
137
  } catch (e) {
129
- if (KeyvGithub.isHttpError(e) && e.status === 404) return false;
138
+ if (KeyvGithub.isHttpError(e) && e.status === 404) {
139
+ this.shaMap.set(path, null);
140
+ return false;
141
+ }
130
142
  throw e;
131
143
  }
132
144
  }
133
145
  async has(key) {
134
- this.validatePath(this.toPath(key));
146
+ const path = this.toPath(key);
147
+ this.validatePath(path);
135
148
  try {
136
149
  const { data } = await this.rest.repos.getContent({
137
150
  owner: this.owner,
138
151
  repo: this.repo,
139
- path: this.toPath(key),
152
+ path,
140
153
  ref: this.ref
141
154
  });
142
- return !Array.isArray(data) && data.type === "file";
155
+ if (Array.isArray(data) || data.type !== "file") {
156
+ this.shaMap.set(path, null);
157
+ return false;
158
+ }
159
+ this.shaMap.set(path, data.sha);
160
+ return true;
143
161
  } catch (e) {
144
- if (KeyvGithub.isHttpError(e) && e.status === 404) return false;
162
+ if (KeyvGithub.isHttpError(e) && e.status === 404) {
163
+ this.shaMap.set(path, null);
164
+ return false;
165
+ }
145
166
  throw e;
146
167
  }
147
168
  }
@@ -197,7 +218,10 @@ var KeyvGithub = class KeyvGithub extends EventEmitter {
197
218
  /** Keyv batch-set: writes multiple keys in a single commit (5 API calls total). */
198
219
  async setMany(values) {
199
220
  if (values.length === 0) return;
200
- for (const { key } of values) this.validatePath(this.toPath(key));
221
+ for (const { key, value } of values) {
222
+ if (typeof value !== "string") throw new Error("keyv-github only supports string values natively. Use new Keyv(store) which serializes values automatically.");
223
+ this.validatePath(this.toPath(key));
224
+ }
201
225
  const entries = values.map(({ key, value }) => [this.toPath(key), String(value)]);
202
226
  const message = entries.length === 1 ? this.msg(entries[0][0], entries[0][1]) : `batch update ${entries.length} files`;
203
227
  await this._batchCommit({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keyv-github",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "module": "src/index.ts",
5
5
  "main": "dist/index.mjs",
6
6
  "types": "dist/index.d.mts",
@@ -20,6 +20,7 @@
20
20
  "octokit": "^5.0.5"
21
21
  },
22
22
  "devDependencies": {
23
+ "@semantic-release/git": "^10.0.1",
23
24
  "@types/bun": "latest",
24
25
  "semantic-release": "^25.0.3",
25
26
  "tsdown": "^0.20.3",
package/src/index.test.ts CHANGED
@@ -204,7 +204,9 @@ describe("KeyvGithub constructor", () => {
204
204
  const client = makeMockClient();
205
205
  const store = new KeyvGithub(
206
206
  "https://github.com/owner/repo/tree/feature/my-branch",
207
- { client: client.rest as unknown as Octokit["rest"] },
207
+ {
208
+ client: client.rest as unknown as Octokit["rest"],
209
+ },
208
210
  );
209
211
  expect(store.branch).toBe("feature/my-branch");
210
212
  });
@@ -294,6 +296,26 @@ describe("set", () => {
294
296
  await store.set("unicode", "日本語テスト 🎉");
295
297
  expect(mockFiles.get("unicode")?.content).toBe("日本語テスト 🎉");
296
298
  });
299
+
300
+ test("throws when TTL is provided (not supported natively)", async () => {
301
+ const { store } = makeStore();
302
+ expect(store.set("key", "value", 1000)).rejects.toThrow(
303
+ "TTL is not supported natively",
304
+ );
305
+ });
306
+
307
+ test("throws when value is not a string", async () => {
308
+ const { store } = makeStore();
309
+ expect(store.set("key", { obj: true })).rejects.toThrow(
310
+ "only supports string values",
311
+ );
312
+ expect(store.set("key", 123)).rejects.toThrow(
313
+ "only supports string values",
314
+ );
315
+ expect(store.set("key", ["array"])).rejects.toThrow(
316
+ "only supports string values",
317
+ );
318
+ });
297
319
  });
298
320
 
299
321
  describe("delete", () => {
@@ -391,6 +413,17 @@ describe("setMany", () => {
391
413
  ).rejects.toThrow();
392
414
  expect(mockFiles.size).toBe(0);
393
415
  });
416
+
417
+ test("throws when any value is not a string", async () => {
418
+ const { store, mockFiles } = makeStore();
419
+ expect(
420
+ store.setMany([
421
+ { key: "a", value: "ok" },
422
+ { key: "b", value: { obj: true } },
423
+ ]),
424
+ ).rejects.toThrow("only supports string values");
425
+ expect(mockFiles.size).toBe(0);
426
+ });
394
427
  });
395
428
 
396
429
  describe("deleteMany", () => {
package/src/index.ts CHANGED
@@ -2,6 +2,12 @@ import { EventEmitter } from "events";
2
2
  import type { KeyvStoreAdapter, StoredData } from "keyv";
3
3
  import { Octokit } from "octokit";
4
4
 
5
+ /** Minimal Map-like interface for SHA caching. */
6
+ export interface ShaMap {
7
+ get(key: string): string | null | undefined;
8
+ set(key: string, value: string | null): void;
9
+ }
10
+
5
11
  export interface KeyvGithubOptions {
6
12
  url: string;
7
13
  branch?: string;
@@ -14,6 +20,8 @@ export interface KeyvGithubOptions {
14
20
  prefix?: string;
15
21
  /** Path suffix appended to every key (e.g. '.json'). Defaults to ''. */
16
22
  suffix?: string;
23
+ /** SHA cache map. Defaults to new Map(). Pass any keyv-like object with get/set. */
24
+ shaMap?: ShaMap;
17
25
  }
18
26
 
19
27
  /**
@@ -41,6 +49,8 @@ export default class KeyvGithub
41
49
  readonly enableClear: boolean;
42
50
  readonly prefix: string;
43
51
  readonly suffix: string;
52
+ /** SHA cache: key → sha (string), null (file doesn't exist), undefined (unknown). */
53
+ readonly shaMap: ShaMap;
44
54
 
45
55
  constructor(url: string, options: Omit<KeyvGithubOptions, "url"> = {}) {
46
56
  super();
@@ -64,6 +74,7 @@ export default class KeyvGithub
64
74
  this.enableClear = options.enableClear ?? false;
65
75
  this.prefix = options.prefix ?? "";
66
76
  this.suffix = options.suffix ?? "";
77
+ this.shaMap = options.shaMap ?? new Map<string, string | null>();
67
78
  }
68
79
 
69
80
  /** Converts a user key to the GitHub file path. */
@@ -106,41 +117,58 @@ export default class KeyvGithub
106
117
  }
107
118
 
108
119
  async get<Value>(key: string): Promise<StoredData<Value> | undefined> {
109
- this.validatePath(this.toPath(key));
120
+ const path = this.toPath(key);
121
+ this.validatePath(path);
110
122
  try {
111
123
  const { data } = await this.rest.repos.getContent({
112
124
  owner: this.owner,
113
125
  repo: this.repo,
114
- path: this.toPath(key),
126
+ path,
115
127
  ref: this.ref,
116
128
  });
117
- if (Array.isArray(data) || data.type !== "file") return undefined;
129
+ if (Array.isArray(data) || data.type !== "file") {
130
+ this.shaMap.set(path, null);
131
+ return undefined;
132
+ }
133
+ this.shaMap.set(path, data.sha);
118
134
  return Buffer.from(data.content, "base64").toString(
119
135
  "utf-8",
120
136
  ) as StoredData<Value>;
121
137
  } catch (e: unknown) {
122
- if (KeyvGithub.isHttpError(e) && e.status === 404) return undefined;
138
+ if (KeyvGithub.isHttpError(e) && e.status === 404) {
139
+ this.shaMap.set(path, null);
140
+ return undefined;
141
+ }
123
142
  throw e;
124
143
  }
125
144
  }
126
145
 
127
- async set(key: string, value: any, _ttl?: number): Promise<void> {
128
- this.validatePath(this.toPath(key));
146
+ async set(key: string, value: any, ttl?: number): Promise<void> {
147
+ if (ttl !== undefined) {
148
+ throw new Error(
149
+ "TTL is not supported natively by keyv-github. " +
150
+ "Use new Keyv(store) which handles TTL via value expiration metadata.",
151
+ );
152
+ }
153
+ if (typeof value !== "string") {
154
+ throw new Error(
155
+ "keyv-github only supports string values natively. " +
156
+ "Use new Keyv(store) which serializes values automatically.",
157
+ );
158
+ }
129
159
  const path = this.toPath(key);
130
- let sha: string | undefined;
131
- try {
132
- const { data } = await this.rest.repos.getContent({
133
- owner: this.owner,
134
- repo: this.repo,
135
- path,
136
- ref: this.ref,
137
- });
138
- if (!Array.isArray(data) && data.type === "file") sha = data.sha;
139
- } catch (e: unknown) {
140
- if (!KeyvGithub.isHttpError(e) || e.status !== 404) throw e;
160
+ this.validatePath(path);
161
+
162
+ // Check shaMap first; if unknown (undefined), fetch to populate it
163
+ let cachedSha = this.shaMap.get(path);
164
+ if (cachedSha === undefined) {
165
+ await this.get(key); // populates shaMap
166
+ cachedSha = this.shaMap.get(path);
141
167
  }
168
+ // cachedSha is now string (existing file) or null (doesn't exist)
169
+ const sha = cachedSha ?? undefined;
142
170
 
143
- await this.rest.repos.createOrUpdateFileContents({
171
+ const { data } = await this.rest.repos.createOrUpdateFileContents({
144
172
  owner: this.owner,
145
173
  repo: this.repo,
146
174
  path,
@@ -149,46 +177,65 @@ export default class KeyvGithub
149
177
  sha,
150
178
  branch: this.ref,
151
179
  });
180
+ // Update shaMap with new sha from response
181
+ this.shaMap.set(path, data.content?.sha ?? null);
152
182
  }
153
183
 
154
184
  async delete(key: string): Promise<boolean> {
155
- this.validatePath(this.toPath(key));
156
185
  const path = this.toPath(key);
186
+ this.validatePath(path);
187
+
188
+ // Check shaMap first; if unknown (undefined), fetch to populate it
189
+ let cachedSha = this.shaMap.get(path);
190
+ if (cachedSha === undefined) {
191
+ await this.get(key); // populates shaMap
192
+ cachedSha = this.shaMap.get(path);
193
+ }
194
+ // If null or still undefined, file doesn't exist
195
+ if (!cachedSha) return false;
196
+
197
+ const sha = cachedSha; // narrow to string for TypeScript
157
198
  try {
158
- const { data } = await this.rest.repos.getContent({
159
- owner: this.owner,
160
- repo: this.repo,
161
- path,
162
- ref: this.ref,
163
- });
164
- if (Array.isArray(data) || data.type !== "file") return false;
165
199
  await this.rest.repos.deleteFile({
166
200
  owner: this.owner,
167
201
  repo: this.repo,
168
202
  path,
169
203
  message: this.msg(path, null),
170
- sha: data.sha,
204
+ sha,
171
205
  branch: this.ref,
172
206
  });
207
+ this.shaMap.set(path, null);
173
208
  return true;
174
209
  } catch (e: unknown) {
175
- if (KeyvGithub.isHttpError(e) && e.status === 404) return false;
210
+ if (KeyvGithub.isHttpError(e) && e.status === 404) {
211
+ this.shaMap.set(path, null);
212
+ return false;
213
+ }
176
214
  throw e;
177
215
  }
178
216
  }
179
217
 
180
218
  async has(key: string): Promise<boolean> {
181
- this.validatePath(this.toPath(key));
219
+ const path = this.toPath(key);
220
+ this.validatePath(path);
182
221
  try {
183
222
  const { data } = await this.rest.repos.getContent({
184
223
  owner: this.owner,
185
224
  repo: this.repo,
186
- path: this.toPath(key),
225
+ path,
187
226
  ref: this.ref,
188
227
  });
189
- return !Array.isArray(data) && data.type === "file";
228
+ if (Array.isArray(data) || data.type !== "file") {
229
+ this.shaMap.set(path, null);
230
+ return false;
231
+ }
232
+ this.shaMap.set(path, data.sha);
233
+ return true;
190
234
  } catch (e: unknown) {
191
- if (KeyvGithub.isHttpError(e) && e.status === 404) return false;
235
+ if (KeyvGithub.isHttpError(e) && e.status === 404) {
236
+ this.shaMap.set(path, null);
237
+ return false;
238
+ }
192
239
  throw e;
193
240
  }
194
241
  }
@@ -257,11 +304,17 @@ export default class KeyvGithub
257
304
  }
258
305
 
259
306
  /** Keyv batch-set: writes multiple keys in a single commit (5 API calls total). */
260
- async setMany(
261
- values: Array<{ key: string; value: any; ttl?: number }>,
262
- ): Promise<void> {
307
+ async setMany(values: Array<{ key: string; value: any }>): Promise<void> {
263
308
  if (values.length === 0) return;
264
- for (const { key } of values) this.validatePath(this.toPath(key));
309
+ for (const { key, value } of values) {
310
+ if (typeof value !== "string") {
311
+ throw new Error(
312
+ "keyv-github only supports string values natively. " +
313
+ "Use new Keyv(store) which serializes values automatically.",
314
+ );
315
+ }
316
+ this.validatePath(this.toPath(key));
317
+ }
265
318
  const entries: [string, string][] = values.map(({ key, value }) => [
266
319
  this.toPath(key),
267
320
  String(value),