keyv-github 1.1.0 → 1.2.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
@@ -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/dist/index.d.mts CHANGED
@@ -46,7 +46,7 @@ declare class KeyvGithub extends EventEmitter implements KeyvStoreAdapter {
46
46
  private static isHttpError;
47
47
  private validatePath;
48
48
  get<Value>(key: string): Promise<StoredData<Value> | undefined>;
49
- set(key: string, value: any, _ttl?: number): Promise<void>;
49
+ set(key: string, value: any, ttl?: number): Promise<void>;
50
50
  delete(key: string): Promise<boolean>;
51
51
  has(key: string): Promise<boolean>;
52
52
  /**
@@ -59,7 +59,6 @@ declare class KeyvGithub extends EventEmitter implements KeyvStoreAdapter {
59
59
  setMany(values: Array<{
60
60
  key: string;
61
61
  value: any;
62
- ttl?: number;
63
62
  }>): Promise<void>;
64
63
  /**
65
64
  * Keyv batch-delete: deletes multiple keys in a single commit (7 API calls total).
package/dist/index.mjs CHANGED
@@ -80,7 +80,9 @@ var KeyvGithub = class KeyvGithub extends EventEmitter {
80
80
  throw e;
81
81
  }
82
82
  }
83
- async set(key, value, _ttl) {
83
+ async set(key, value, ttl) {
84
+ 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.");
85
+ if (typeof value !== "string") throw new Error("keyv-github only supports string values natively. Use new Keyv(store) which serializes values automatically.");
84
86
  this.validatePath(this.toPath(key));
85
87
  const path = this.toPath(key);
86
88
  let sha;
@@ -197,7 +199,10 @@ var KeyvGithub = class KeyvGithub extends EventEmitter {
197
199
  /** Keyv batch-set: writes multiple keys in a single commit (5 API calls total). */
198
200
  async setMany(values) {
199
201
  if (values.length === 0) return;
200
- for (const { key } of values) this.validatePath(this.toPath(key));
202
+ for (const { key, value } of values) {
203
+ if (typeof value !== "string") throw new Error("keyv-github only supports string values natively. Use new Keyv(store) which serializes values automatically.");
204
+ this.validatePath(this.toPath(key));
205
+ }
201
206
  const entries = values.map(({ key, value }) => [this.toPath(key), String(value)]);
202
207
  const message = entries.length === 1 ? this.msg(entries[0][0], entries[0][1]) : `batch update ${entries.length} files`;
203
208
  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.2.0",
4
4
  "module": "src/index.ts",
5
5
  "main": "dist/index.mjs",
6
6
  "types": "dist/index.d.mts",
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
@@ -124,7 +124,19 @@ export default class KeyvGithub
124
124
  }
125
125
  }
126
126
 
127
- async set(key: string, value: any, _ttl?: number): Promise<void> {
127
+ async set(key: string, value: any, ttl?: number): Promise<void> {
128
+ if (ttl !== undefined) {
129
+ throw new Error(
130
+ "TTL is not supported natively by keyv-github. " +
131
+ "Use new Keyv(store) which handles TTL via value expiration metadata.",
132
+ );
133
+ }
134
+ if (typeof value !== "string") {
135
+ throw new Error(
136
+ "keyv-github only supports string values natively. " +
137
+ "Use new Keyv(store) which serializes values automatically.",
138
+ );
139
+ }
128
140
  this.validatePath(this.toPath(key));
129
141
  const path = this.toPath(key);
130
142
  let sha: string | undefined;
@@ -257,11 +269,17 @@ export default class KeyvGithub
257
269
  }
258
270
 
259
271
  /** 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> {
272
+ async setMany(values: Array<{ key: string; value: any }>): Promise<void> {
263
273
  if (values.length === 0) return;
264
- for (const { key } of values) this.validatePath(this.toPath(key));
274
+ for (const { key, value } of values) {
275
+ if (typeof value !== "string") {
276
+ throw new Error(
277
+ "keyv-github only supports string values natively. " +
278
+ "Use new Keyv(store) which serializes values automatically.",
279
+ );
280
+ }
281
+ this.validatePath(this.toPath(key));
282
+ }
265
283
  const entries: [string, string][] = values.map(({ key, value }) => [
266
284
  this.toPath(key),
267
285
  String(value),