@flaghoist/adapter-redis 0.1.1 → 0.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
@@ -33,6 +33,9 @@ createFlagServer((env) => ({
33
33
  Pick this over Cloudflare KV when you want changes to be visible immediately everywhere, rather than
34
34
  a few seconds later. Pick KV when you would rather not run anything.
35
35
 
36
+ Webhook endpoints (if you use them) live in their own hash, `flaghoist:webhooks` by default. Change
37
+ it with `{ webhookHashKey: '...' }`.
38
+
36
39
  Validated by the same conformance suite as the other adapters.
37
40
 
38
41
  ## Status
package/dist/index.cjs CHANGED
@@ -36,8 +36,37 @@ function toFlag(raw) {
36
36
  if (typeof raw === "object") return (0, import_core.parseFlag)(raw);
37
37
  return null;
38
38
  }
39
+ function toWebhook(raw) {
40
+ if (raw == null) return null;
41
+ if (typeof raw === "string") {
42
+ try {
43
+ return JSON.parse(raw);
44
+ } catch {
45
+ return null;
46
+ }
47
+ }
48
+ if (typeof raw === "object") return raw;
49
+ return null;
50
+ }
51
+ var wrapRecord = (value) => JSON.stringify({ flaghoistRecord: 1, value });
52
+ function unwrapRecord(raw) {
53
+ let parsed = raw;
54
+ if (typeof raw === "string") {
55
+ try {
56
+ parsed = JSON.parse(raw);
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+ if (parsed === null || typeof parsed !== "object") return null;
62
+ const envelope = parsed;
63
+ if (envelope.flaghoistRecord !== 1) return null;
64
+ return envelope.value ?? null;
65
+ }
39
66
  function redisAdapter(client, options = {}) {
40
67
  const hashKey = options.hashKey ?? "flaghoist:flags";
68
+ const whKey = options.webhookHashKey ?? "flaghoist:webhooks";
69
+ const recPrefix = options.recordHashPrefix ?? "flaghoist:records:";
41
70
  return {
42
71
  async get(key) {
43
72
  return toFlag(await client.hget(hashKey, key));
@@ -57,6 +86,48 @@ function redisAdapter(client, options = {}) {
57
86
  if (flag) flags.push(flag);
58
87
  }
59
88
  return flags;
89
+ },
90
+ async putWebhook(id, webhook) {
91
+ await client.hset(whKey, id, JSON.stringify(webhook));
92
+ },
93
+ async getWebhook(id) {
94
+ return toWebhook(await client.hget(whKey, id));
95
+ },
96
+ async deleteWebhook(id) {
97
+ await client.hdel(whKey, id);
98
+ },
99
+ async listWebhooks() {
100
+ const all = await client.hgetall(whKey);
101
+ if (!all) return [];
102
+ const hooks = [];
103
+ for (const raw of Object.values(all)) {
104
+ const hook = toWebhook(raw);
105
+ if (hook) hooks.push(hook);
106
+ }
107
+ return hooks;
108
+ },
109
+ async getRecord(collection, id) {
110
+ (0, import_core.assertRecordAddress)(collection, id);
111
+ return unwrapRecord(await client.hget(recPrefix + collection, id));
112
+ },
113
+ async putRecord(collection, id, value) {
114
+ (0, import_core.assertRecordAddress)(collection, id);
115
+ await client.hset(recPrefix + collection, id, wrapRecord(value));
116
+ },
117
+ async deleteRecord(collection, id) {
118
+ (0, import_core.assertRecordAddress)(collection, id);
119
+ await client.hdel(recPrefix + collection, id);
120
+ },
121
+ async listRecords(collection) {
122
+ (0, import_core.assertRecordAddress)(collection);
123
+ const all = await client.hgetall(recPrefix + collection);
124
+ if (!all) return [];
125
+ const entries = [];
126
+ for (const [id, raw] of Object.entries(all)) {
127
+ const value = unwrapRecord(raw);
128
+ if (value !== null) entries.push({ id, value });
129
+ }
130
+ return entries;
60
131
  }
61
132
  };
62
133
  }
package/dist/index.d.cts CHANGED
@@ -15,6 +15,13 @@ interface RedisClientLike {
15
15
  interface RedisAdapterOptions {
16
16
  /** Redis hash key under which all flags are stored. Default: `"flaghoist:flags"`. */
17
17
  hashKey?: string;
18
+ /** Redis hash key for webhook endpoints. Default: `"flaghoist:webhooks"`. */
19
+ webhookHashKey?: string;
20
+ /**
21
+ * Prefix for record-store hashes. Each collection gets its own hash, `<prefix><collection>`.
22
+ * Default: `"flaghoist:records:"`.
23
+ */
24
+ recordHashPrefix?: string;
18
25
  }
19
26
  /**
20
27
  * A StorageAdapter backed by Redis. All flags live in a single hash, so reads and writes are
package/dist/index.d.ts CHANGED
@@ -15,6 +15,13 @@ interface RedisClientLike {
15
15
  interface RedisAdapterOptions {
16
16
  /** Redis hash key under which all flags are stored. Default: `"flaghoist:flags"`. */
17
17
  hashKey?: string;
18
+ /** Redis hash key for webhook endpoints. Default: `"flaghoist:webhooks"`. */
19
+ webhookHashKey?: string;
20
+ /**
21
+ * Prefix for record-store hashes. Each collection gets its own hash, `<prefix><collection>`.
22
+ * Default: `"flaghoist:records:"`.
23
+ */
24
+ recordHashPrefix?: string;
18
25
  }
19
26
  /**
20
27
  * A StorageAdapter backed by Redis. All flags live in a single hash, so reads and writes are
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  // src/index.ts
2
- import { parseFlag } from "@flaghoist/core";
2
+ import {
3
+ assertRecordAddress,
4
+ parseFlag
5
+ } from "@flaghoist/core";
3
6
  function toFlag(raw) {
4
7
  if (raw == null) return null;
5
8
  if (typeof raw === "string") {
@@ -12,8 +15,37 @@ function toFlag(raw) {
12
15
  if (typeof raw === "object") return parseFlag(raw);
13
16
  return null;
14
17
  }
18
+ function toWebhook(raw) {
19
+ if (raw == null) return null;
20
+ if (typeof raw === "string") {
21
+ try {
22
+ return JSON.parse(raw);
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+ if (typeof raw === "object") return raw;
28
+ return null;
29
+ }
30
+ var wrapRecord = (value) => JSON.stringify({ flaghoistRecord: 1, value });
31
+ function unwrapRecord(raw) {
32
+ let parsed = raw;
33
+ if (typeof raw === "string") {
34
+ try {
35
+ parsed = JSON.parse(raw);
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+ if (parsed === null || typeof parsed !== "object") return null;
41
+ const envelope = parsed;
42
+ if (envelope.flaghoistRecord !== 1) return null;
43
+ return envelope.value ?? null;
44
+ }
15
45
  function redisAdapter(client, options = {}) {
16
46
  const hashKey = options.hashKey ?? "flaghoist:flags";
47
+ const whKey = options.webhookHashKey ?? "flaghoist:webhooks";
48
+ const recPrefix = options.recordHashPrefix ?? "flaghoist:records:";
17
49
  return {
18
50
  async get(key) {
19
51
  return toFlag(await client.hget(hashKey, key));
@@ -33,6 +65,48 @@ function redisAdapter(client, options = {}) {
33
65
  if (flag) flags.push(flag);
34
66
  }
35
67
  return flags;
68
+ },
69
+ async putWebhook(id, webhook) {
70
+ await client.hset(whKey, id, JSON.stringify(webhook));
71
+ },
72
+ async getWebhook(id) {
73
+ return toWebhook(await client.hget(whKey, id));
74
+ },
75
+ async deleteWebhook(id) {
76
+ await client.hdel(whKey, id);
77
+ },
78
+ async listWebhooks() {
79
+ const all = await client.hgetall(whKey);
80
+ if (!all) return [];
81
+ const hooks = [];
82
+ for (const raw of Object.values(all)) {
83
+ const hook = toWebhook(raw);
84
+ if (hook) hooks.push(hook);
85
+ }
86
+ return hooks;
87
+ },
88
+ async getRecord(collection, id) {
89
+ assertRecordAddress(collection, id);
90
+ return unwrapRecord(await client.hget(recPrefix + collection, id));
91
+ },
92
+ async putRecord(collection, id, value) {
93
+ assertRecordAddress(collection, id);
94
+ await client.hset(recPrefix + collection, id, wrapRecord(value));
95
+ },
96
+ async deleteRecord(collection, id) {
97
+ assertRecordAddress(collection, id);
98
+ await client.hdel(recPrefix + collection, id);
99
+ },
100
+ async listRecords(collection) {
101
+ assertRecordAddress(collection);
102
+ const all = await client.hgetall(recPrefix + collection);
103
+ if (!all) return [];
104
+ const entries = [];
105
+ for (const [id, raw] of Object.entries(all)) {
106
+ const value = unwrapRecord(raw);
107
+ if (value !== null) entries.push({ id, value });
108
+ }
109
+ return entries;
36
110
  }
37
111
  };
38
112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flaghoist/adapter-redis",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Redis StorageAdapter for Flaghoist — works with ioredis (Node) and Upstash (edge).",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -27,7 +27,7 @@
27
27
  "node": ">=20"
28
28
  },
29
29
  "dependencies": {
30
- "@flaghoist/core": "0.1.1"
30
+ "@flaghoist/core": "0.2.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@flaghoist/adapter-conformance": "0.0.0"