keyv-github 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,50 @@ Every `set` costs **2 API calls** (read SHA + write), every `delete` costs **2**
15
15
 
16
16
  Use a [GitHub App](https://docs.github.com/en/developers/apps) token or a fine-grained PAT with `contents: write` permission to maximise your quota.
17
17
 
18
+ ### Best Practice: Use cache layers
19
+
20
+ To reduce API calls, chain memory and file caches before keyv-github using [keyv-nest](https://github.com/snomiao/keyv-nest) and [keyv-dir-store](https://github.com/snomiao/keyv-dir-store):
21
+
22
+ ```ts
23
+ import Keyv from "keyv";
24
+ import KeyvNest from "keyv-nest";
25
+ import { KeyvDirStore } from "keyv-dir-store";
26
+ import KeyvGithub from "keyv-github";
27
+
28
+ // Simple memory store (avoids Keyv namespace prefix issues)
29
+ const memoryStore = {
30
+ cache: new Map<string, any>(),
31
+ opts: { url: "", dialect: "map" },
32
+ get(key: string) { return this.cache.get(key); },
33
+ set(key: string, value: any) { this.cache.set(key, value); },
34
+ delete(key: string) { return this.cache.delete(key); },
35
+ clear() { this.cache.clear(); },
36
+ };
37
+
38
+ // Use same prefix/suffix so local cache mirrors GitHub paths
39
+ const prefix = "data/";
40
+ const suffix = ".json";
41
+
42
+ const store = KeyvNest(
43
+ memoryStore, // L1: Memory (fastest)
44
+ new KeyvDirStore("./cache", { // L2: Local files (fast)
45
+ prefix,
46
+ suffix,
47
+ filename: (k) => k, // use key as-is, no hashing
48
+ }),
49
+ new KeyvGithub("owner/repo/tree/main", { client, prefix, suffix }) // L3: GitHub
50
+ );
51
+
52
+ // Wrap with Keyv (use empty namespace to preserve keys)
53
+ (store as any).opts = { url: "", dialect: "keyv-nest" };
54
+ const kv = new Keyv({ store, namespace: "" });
55
+ // key "foo" -> ./cache/data/foo.json (local) and data/foo.json (GitHub)
56
+ ```
57
+
58
+ See [demo-best-practice.ts](./demo-best-practice.ts) for a runnable example.
59
+
60
+ Reads check L1 → L2 → L3, with automatic backfill to faster layers on cache miss.
61
+
18
62
  ## Install
19
63
 
20
64
  ```sh
@@ -99,6 +143,8 @@ const store = new KeyvGithub("owner/repo", {
99
143
 
100
144
  ## Key rules
101
145
 
146
+ ⚠️ **Keys are validated but NOT sanitized.** You must sanitize keys yourself before passing them to this adapter. Invalid keys will throw an error.
147
+
102
148
  Keys must be valid relative file paths:
103
149
 
104
150
  - Non-empty
@@ -107,8 +153,22 @@ Keys must be valid relative file paths:
107
153
  - No `.` or `..` segments
108
154
  - No null bytes
109
155
 
156
+ **OS-specific characters** like `<>:"|?*\` (invalid on Windows) are NOT validated — you must sanitize these yourself if your keys might contain them.
157
+
110
158
  Invalid keys throw synchronously before any API request.
111
159
 
160
+ ```ts
161
+ // ✗ These will throw errors
162
+ await store.set("/absolute/path", "value"); // leading slash
163
+ await store.set("path/", "value"); // trailing slash
164
+ await store.set("path/../escape", "value"); // directory traversal
165
+ await store.set("path//double", "value"); // double slashes
166
+
167
+ // ✓ Valid keys
168
+ await store.set("data/file.txt", "value");
169
+ await store.set("nested/path/key.json", "value");
170
+ ```
171
+
112
172
  ## See Also
113
173
 
114
174
  Other Keyv storage adapters by the same author: