encore.dev 1.54.2 → 1.55.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/config/secrets.ts +7 -1
- package/dist/config/secrets.js +4 -0
- package/dist/config/secrets.js.map +1 -1
- package/dist/internal/runtime/napi/napi.cjs +3 -1
- package/dist/internal/runtime/napi/napi.d.cts +114 -1
- package/dist/storage/cache/basic.d.ts +268 -0
- package/dist/storage/cache/basic.js +383 -0
- package/dist/storage/cache/basic.js.map +1 -0
- package/dist/storage/cache/cluster.d.ts +48 -0
- package/dist/storage/cache/cluster.js +40 -0
- package/dist/storage/cache/cluster.js.map +1 -0
- package/dist/storage/cache/errors.d.ts +19 -0
- package/dist/storage/cache/errors.js +59 -0
- package/dist/storage/cache/errors.js.map +1 -0
- package/dist/storage/cache/expiry.d.ts +55 -0
- package/dist/storage/cache/expiry.js +74 -0
- package/dist/storage/cache/expiry.js.map +1 -0
- package/dist/storage/cache/keyspace.d.ts +77 -0
- package/dist/storage/cache/keyspace.js +100 -0
- package/dist/storage/cache/keyspace.js.map +1 -0
- package/dist/storage/cache/list.d.ts +249 -0
- package/dist/storage/cache/list.js +376 -0
- package/dist/storage/cache/list.js.map +1 -0
- package/dist/storage/cache/mod.d.ts +10 -0
- package/dist/storage/cache/mod.js +13 -0
- package/dist/storage/cache/mod.js.map +1 -0
- package/dist/storage/cache/set.d.ts +258 -0
- package/dist/storage/cache/set.js +411 -0
- package/dist/storage/cache/set.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/internal/runtime/napi/napi.cjs +3 -1
- package/internal/runtime/napi/napi.d.cts +114 -1
- package/package.json +6 -1
- package/storage/cache/basic.ts +511 -0
- package/storage/cache/cluster.ts +67 -0
- package/storage/cache/errors.ts +66 -0
- package/storage/cache/expiry.ts +98 -0
- package/storage/cache/keyspace.ts +142 -0
- package/storage/cache/list.ts +496 -0
- package/storage/cache/mod.ts +36 -0
- package/storage/cache/set.ts +491 -0
|
@@ -74,6 +74,119 @@ export interface BuildMeta {
|
|
|
74
74
|
uncommittedChanges: boolean
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/** A cache cluster for storing cached data. */
|
|
78
|
+
export class CacheCluster {
|
|
79
|
+
/** Get a value by key. */
|
|
80
|
+
get(key: string, source?: Request | undefined | null): Promise<Buffer | null>
|
|
81
|
+
/** Set a value by key with optional TTL. */
|
|
82
|
+
set(key: string, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<void>
|
|
83
|
+
/** Set a value only if the key doesn't exist. */
|
|
84
|
+
setIfNotExists(key: string, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<boolean>
|
|
85
|
+
/** Replace a value only if the key exists. */
|
|
86
|
+
replace(key: string, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<boolean>
|
|
87
|
+
/** Get old value and set new value atomically. */
|
|
88
|
+
getAndSet(key: string, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<Buffer | null>
|
|
89
|
+
/** Get value and delete key atomically. */
|
|
90
|
+
getAndDelete(key: string, source?: Request | undefined | null): Promise<Buffer | null>
|
|
91
|
+
/** Delete one or more keys. */
|
|
92
|
+
delete(keys: Array<string>, source?: Request | undefined | null): Promise<number>
|
|
93
|
+
/** Get multiple values. */
|
|
94
|
+
mget(keys: Array<string>, source?: Request | undefined | null): Promise<Array<Buffer | undefined | null>>
|
|
95
|
+
/** Append to a string value. */
|
|
96
|
+
append(key: string, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
97
|
+
/** Get a substring of a string value. */
|
|
98
|
+
getRange(key: string, start: number, end: number, source?: Request | undefined | null): Promise<Buffer>
|
|
99
|
+
/** Set a substring at a specific offset. */
|
|
100
|
+
setRange(key: string, offset: number, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
101
|
+
/** Get string length. */
|
|
102
|
+
strlen(key: string, source?: Request | undefined | null): Promise<number>
|
|
103
|
+
/** Increment an integer value. */
|
|
104
|
+
incrBy(key: string, delta: number, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
105
|
+
/** Decrement an integer value. */
|
|
106
|
+
decrBy(key: string, delta: number, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
107
|
+
/** Increment a float value. */
|
|
108
|
+
incrByFloat(key: string, delta: number, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
109
|
+
/** Push values to the left (head) of a list. */
|
|
110
|
+
lpush(key: string, values: Array<Buffer>, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
111
|
+
/** Push values to the right (tail) of a list. */
|
|
112
|
+
rpush(key: string, values: Array<Buffer>, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
113
|
+
/**
|
|
114
|
+
* Pop a value from the left (head) of a list.
|
|
115
|
+
* Returns null if the list is empty or doesn't exist.
|
|
116
|
+
*/
|
|
117
|
+
lpop(key: string, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<Buffer | null>
|
|
118
|
+
/**
|
|
119
|
+
* Pop a value from the right (tail) of a list.
|
|
120
|
+
* Returns null if the list is empty or doesn't exist.
|
|
121
|
+
*/
|
|
122
|
+
rpop(key: string, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<Buffer | null>
|
|
123
|
+
/** Get element at index from a list. */
|
|
124
|
+
lindex(key: string, index: number, source?: Request | undefined | null): Promise<Buffer | null>
|
|
125
|
+
/** Get a range of elements from a list. */
|
|
126
|
+
lrange(key: string, start: number, stop: number, source?: Request | undefined | null): Promise<Array<Buffer>>
|
|
127
|
+
/** Get all elements of a list (traced as "items"). */
|
|
128
|
+
lrangeAll(key: string, source?: Request | undefined | null): Promise<Array<Buffer>>
|
|
129
|
+
/** Get list length. */
|
|
130
|
+
llen(key: string, source?: Request | undefined | null): Promise<number>
|
|
131
|
+
/** Trim a list to the specified range. */
|
|
132
|
+
ltrim(key: string, start: number, stop: number, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<void>
|
|
133
|
+
/** Set element at index in list. */
|
|
134
|
+
lset(key: string, index: number, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<void>
|
|
135
|
+
/** Insert value before pivot in list. */
|
|
136
|
+
linsertBefore(key: string, pivot: Buffer, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
137
|
+
/** Insert value after pivot in list. */
|
|
138
|
+
linsertAfter(key: string, pivot: Buffer, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
139
|
+
/** Remove elements from list. */
|
|
140
|
+
lremAll(key: string, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
141
|
+
/** Remove elements from list. */
|
|
142
|
+
lremFirst(key: string, count: number, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
143
|
+
/** Remove elements from list. */
|
|
144
|
+
lremLast(key: string, count: number, value: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
145
|
+
/** Move element between lists atomically. */
|
|
146
|
+
lmove(src: string, dst: string, srcDir: string, dstDir: string, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<Buffer | null>
|
|
147
|
+
/** Add members to a set. */
|
|
148
|
+
sadd(key: string, members: Array<Buffer>, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
149
|
+
/** Remove members from a set. */
|
|
150
|
+
srem(key: string, members: Array<Buffer>, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
151
|
+
/** Check if member exists in set. */
|
|
152
|
+
sismember(key: string, member: Buffer, source?: Request | undefined | null): Promise<boolean>
|
|
153
|
+
/** Get all members of a set. */
|
|
154
|
+
smembers(key: string, source?: Request | undefined | null): Promise<Array<Buffer>>
|
|
155
|
+
/** Get set cardinality. */
|
|
156
|
+
scard(key: string, source?: Request | undefined | null): Promise<number>
|
|
157
|
+
/**
|
|
158
|
+
* Pop a single random member from a set.
|
|
159
|
+
* Returns null if the set is empty or doesn't exist.
|
|
160
|
+
*/
|
|
161
|
+
spop(key: string, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<Buffer | null>
|
|
162
|
+
/** Pop random members from a set. */
|
|
163
|
+
spopN(key: string, count: number, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<Array<Buffer>>
|
|
164
|
+
/**
|
|
165
|
+
* Get a single random member from a set (without removing).
|
|
166
|
+
* Returns null if the set is empty or doesn't exist.
|
|
167
|
+
*/
|
|
168
|
+
srandmember(key: string, source?: Request | undefined | null): Promise<Buffer | null>
|
|
169
|
+
/**
|
|
170
|
+
* Get random members from a set (without removing).
|
|
171
|
+
* Positive count returns distinct elements, negative count may return duplicates.
|
|
172
|
+
*/
|
|
173
|
+
srandmemberN(key: string, count: number, source?: Request | undefined | null): Promise<Array<Buffer>>
|
|
174
|
+
/** Get the difference between sets. */
|
|
175
|
+
sdiff(keys: Array<string>, source?: Request | undefined | null): Promise<Array<Buffer>>
|
|
176
|
+
/** Store the difference between sets. */
|
|
177
|
+
sdiffstore(destination: string, keys: Array<string>, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
178
|
+
/** Get the intersection of sets. */
|
|
179
|
+
sinter(keys: Array<string>, source?: Request | undefined | null): Promise<Array<Buffer>>
|
|
180
|
+
/** Store the intersection of sets. */
|
|
181
|
+
sinterstore(destination: string, keys: Array<string>, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
182
|
+
/** Get the union of sets. */
|
|
183
|
+
sunion(keys: Array<string>, source?: Request | undefined | null): Promise<Array<Buffer>>
|
|
184
|
+
/** Store the union of sets. */
|
|
185
|
+
sunionstore(destination: string, keys: Array<string>, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<number>
|
|
186
|
+
/** Move member from one set to another. */
|
|
187
|
+
smove(src: string, dst: string, member: Buffer, ttlMs?: number | undefined | null, source?: Request | undefined | null): Promise<boolean>
|
|
188
|
+
}
|
|
189
|
+
|
|
77
190
|
/** CallOpts can be used to set options for API calls. */
|
|
78
191
|
export interface CallOpts {
|
|
79
192
|
authData?: PVals
|
|
@@ -279,6 +392,7 @@ export class Runtime {
|
|
|
279
392
|
sqlDatabase(encoreName: string): SQLDatabase
|
|
280
393
|
pubsubTopic(encoreName: string): PubSubTopic
|
|
281
394
|
bucket(encoreName: string): Bucket
|
|
395
|
+
cacheCluster(encoreName: string): CacheCluster
|
|
282
396
|
gateway(encoreName: string, cfg: GatewayConfig): Gateway
|
|
283
397
|
/** Gets the root logger from the runtime */
|
|
284
398
|
logger(): Logger
|
|
@@ -307,7 +421,6 @@ export class Runtime {
|
|
|
307
421
|
createMetricsRegistry(buffer: object): void
|
|
308
422
|
/** Get the shared metrics registry (all worker threads use this) */
|
|
309
423
|
getMetricsRegistry(): MetricsRegistry
|
|
310
|
-
|
|
311
424
|
}
|
|
312
425
|
|
|
313
426
|
export interface RuntimeConfig {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "encore.dev",
|
|
3
3
|
"description": "Encore's JavaScript/TypeScript SDK",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.55.0",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/encoredev/encore/issues"
|
|
@@ -69,6 +69,11 @@
|
|
|
69
69
|
"bun": "./storage/objects/mod.ts",
|
|
70
70
|
"default": "./dist/storage/objects/mod.js"
|
|
71
71
|
},
|
|
72
|
+
"./storage/cache": {
|
|
73
|
+
"types": "./storage/cache/mod.ts",
|
|
74
|
+
"bun": "./storage/cache/mod.ts",
|
|
75
|
+
"default": "./dist/storage/cache/mod.js"
|
|
76
|
+
},
|
|
72
77
|
"./validate": {
|
|
73
78
|
"types": "./validate/mod.ts",
|
|
74
79
|
"bun": "./validate/mod.ts",
|
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
import { getCurrentRequest } from "../../internal/reqtrack/mod";
|
|
2
|
+
import { CacheCluster } from "./cluster";
|
|
3
|
+
import { CacheMiss, CacheKeyExists } from "./errors";
|
|
4
|
+
import { Keyspace, KeyspaceConfig, WriteOptions } from "./keyspace";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Base class for basic (scalar value) keyspaces.
|
|
8
|
+
* Provides get/set/replace/etc operations.
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
abstract class BasicKeyspace<K, V> extends Keyspace<K> {
|
|
12
|
+
constructor(cluster: CacheCluster, config: KeyspaceConfig<K>) {
|
|
13
|
+
super(cluster, config);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Serializes a value to a Buffer for storage.
|
|
18
|
+
*/
|
|
19
|
+
protected abstract serialize(value: V): Buffer;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Deserializes a Buffer from storage to a value.
|
|
23
|
+
*/
|
|
24
|
+
protected abstract deserialize(data: Buffer): V;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Gets the value stored at key.
|
|
28
|
+
* If the key does not exist, it returns `undefined`.
|
|
29
|
+
*
|
|
30
|
+
* @returns The value, or `undefined` if the key does not exist.
|
|
31
|
+
* @see https://redis.io/commands/get/
|
|
32
|
+
*/
|
|
33
|
+
async get(key: K): Promise<V | undefined> {
|
|
34
|
+
const source = getCurrentRequest();
|
|
35
|
+
const mappedKey = this.mapKey(key);
|
|
36
|
+
const result = await this.cluster.impl.get(mappedKey, source);
|
|
37
|
+
|
|
38
|
+
if (result === null) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return this.deserialize(result);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Gets the values stored at multiple keys.
|
|
47
|
+
*
|
|
48
|
+
* @returns An array of values in the same order as the provided keys.
|
|
49
|
+
* Each element is the value or `undefined` if the key was not found.
|
|
50
|
+
* @see https://redis.io/commands/mget/
|
|
51
|
+
*/
|
|
52
|
+
async multiGet(...keys: K[]): Promise<(V | undefined)[]> {
|
|
53
|
+
const source = getCurrentRequest();
|
|
54
|
+
const mappedKeys = keys.map((k) => this.mapKey(k));
|
|
55
|
+
const results = await this.cluster.impl.mget(mappedKeys, source);
|
|
56
|
+
return results.map((r) =>
|
|
57
|
+
r === null || r === undefined ? undefined : this.deserialize(r)
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Updates the value stored at key to val.
|
|
63
|
+
*
|
|
64
|
+
* @see https://redis.io/commands/set/
|
|
65
|
+
*/
|
|
66
|
+
async set(key: K, value: V, options?: WriteOptions): Promise<void> {
|
|
67
|
+
const source = getCurrentRequest();
|
|
68
|
+
const mappedKey = this.mapKey(key);
|
|
69
|
+
const serialized = this.serialize(value);
|
|
70
|
+
const ttlMs = this.resolveTtl(options);
|
|
71
|
+
|
|
72
|
+
await this.cluster.impl.set(mappedKey, serialized, ttlMs, source);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Sets the value stored at key to val, but only if the key does not exist beforehand.
|
|
77
|
+
*
|
|
78
|
+
* @throws {CacheKeyExists} If the key already exists.
|
|
79
|
+
* @see https://redis.io/commands/setnx/
|
|
80
|
+
*/
|
|
81
|
+
async setIfNotExists(
|
|
82
|
+
key: K,
|
|
83
|
+
value: V,
|
|
84
|
+
options?: WriteOptions
|
|
85
|
+
): Promise<void> {
|
|
86
|
+
const source = getCurrentRequest();
|
|
87
|
+
const mappedKey = this.mapKey(key);
|
|
88
|
+
const serialized = this.serialize(value);
|
|
89
|
+
const ttlMs = this.resolveTtl(options);
|
|
90
|
+
|
|
91
|
+
const set = await this.cluster.impl.setIfNotExists(
|
|
92
|
+
mappedKey,
|
|
93
|
+
serialized,
|
|
94
|
+
ttlMs,
|
|
95
|
+
source
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (!set) {
|
|
99
|
+
throw new CacheKeyExists(mappedKey);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Replaces the existing value stored at key to val.
|
|
105
|
+
*
|
|
106
|
+
* @throws {CacheMiss} If the key does not already exist.
|
|
107
|
+
* @see https://redis.io/commands/set/
|
|
108
|
+
*/
|
|
109
|
+
async replace(key: K, value: V, options?: WriteOptions): Promise<void> {
|
|
110
|
+
const source = getCurrentRequest();
|
|
111
|
+
const mappedKey = this.mapKey(key);
|
|
112
|
+
const serialized = this.serialize(value);
|
|
113
|
+
const ttlMs = this.resolveTtl(options);
|
|
114
|
+
|
|
115
|
+
const replaced = await this.cluster.impl.replace(
|
|
116
|
+
mappedKey,
|
|
117
|
+
serialized,
|
|
118
|
+
ttlMs,
|
|
119
|
+
source
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (!replaced) {
|
|
123
|
+
throw new CacheMiss(mappedKey);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Updates the value of key to val and returns the previously stored value.
|
|
129
|
+
* If the key does not already exist, it sets it and returns `undefined`.
|
|
130
|
+
*
|
|
131
|
+
* @returns The previous value, or `undefined` if the key did not exist.
|
|
132
|
+
* @see https://redis.io/commands/getset/
|
|
133
|
+
*/
|
|
134
|
+
async getAndSet(
|
|
135
|
+
key: K,
|
|
136
|
+
value: V,
|
|
137
|
+
options?: WriteOptions
|
|
138
|
+
): Promise<V | undefined> {
|
|
139
|
+
const source = getCurrentRequest();
|
|
140
|
+
const mappedKey = this.mapKey(key);
|
|
141
|
+
const serialized = this.serialize(value);
|
|
142
|
+
const ttlMs = this.resolveTtl(options);
|
|
143
|
+
|
|
144
|
+
const oldValue = await this.cluster.impl.getAndSet(
|
|
145
|
+
mappedKey,
|
|
146
|
+
serialized,
|
|
147
|
+
ttlMs,
|
|
148
|
+
source
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
if (oldValue === null) {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return this.deserialize(oldValue);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Deletes the key and returns the previously stored value.
|
|
160
|
+
* If the key does not already exist, it returns `undefined`.
|
|
161
|
+
*
|
|
162
|
+
* @returns The previous value, or `undefined` if the key did not exist.
|
|
163
|
+
* @see https://redis.io/commands/getdel/
|
|
164
|
+
*/
|
|
165
|
+
async getAndDelete(key: K): Promise<V | undefined> {
|
|
166
|
+
const source = getCurrentRequest();
|
|
167
|
+
const mappedKey = this.mapKey(key);
|
|
168
|
+
|
|
169
|
+
const value = await this.cluster.impl.getAndDelete(mappedKey, source);
|
|
170
|
+
|
|
171
|
+
if (value === null) {
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return this.deserialize(value);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* StringKeyspace stores string values.
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* const tokens = new StringKeyspace<string>(cluster, {
|
|
185
|
+
* keyPattern: "token/:id",
|
|
186
|
+
* defaultExpiry: ExpireIn(3600000), // 1 hour
|
|
187
|
+
* });
|
|
188
|
+
*
|
|
189
|
+
* await tokens.set("abc123", "user-token-value");
|
|
190
|
+
* const token = await tokens.get("abc123");
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
export class StringKeyspace<K> extends BasicKeyspace<K, string> {
|
|
194
|
+
constructor(cluster: CacheCluster, config: KeyspaceConfig<K>) {
|
|
195
|
+
super(cluster, config);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
protected serialize(value: string): Buffer {
|
|
199
|
+
return Buffer.from(value, "utf-8");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
protected deserialize(data: Buffer): string {
|
|
203
|
+
return data.toString("utf-8");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Appends a string to the value stored at key.
|
|
208
|
+
*
|
|
209
|
+
* If the key does not exist it is first created and set as the empty string,
|
|
210
|
+
* causing append to behave like set.
|
|
211
|
+
*
|
|
212
|
+
* @returns The new string length.
|
|
213
|
+
* @see https://redis.io/commands/append/
|
|
214
|
+
*/
|
|
215
|
+
async append(key: K, value: string, options?: WriteOptions): Promise<number> {
|
|
216
|
+
const source = getCurrentRequest();
|
|
217
|
+
const mappedKey = this.mapKey(key);
|
|
218
|
+
const ttlMs = this.resolveTtl(options);
|
|
219
|
+
const result = await this.cluster.impl.append(
|
|
220
|
+
mappedKey,
|
|
221
|
+
Buffer.from(value, "utf-8"),
|
|
222
|
+
ttlMs,
|
|
223
|
+
source
|
|
224
|
+
);
|
|
225
|
+
return Number(result);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Returns a substring of the string value stored at key.
|
|
230
|
+
*
|
|
231
|
+
* The `start` and `end` values are zero-based indices, but unlike typical slicing
|
|
232
|
+
* the `end` value is inclusive.
|
|
233
|
+
*
|
|
234
|
+
* Negative values can be used in order to provide an offset starting
|
|
235
|
+
* from the end of the string, so -1 means the last character.
|
|
236
|
+
*
|
|
237
|
+
* If the string does not exist it returns the empty string.
|
|
238
|
+
*
|
|
239
|
+
* @param key - The cache key.
|
|
240
|
+
* @param start - Start index (inclusive, 0-based).
|
|
241
|
+
* @param end - End index (inclusive, 0-based). Use -1 for end of string.
|
|
242
|
+
* @returns The substring.
|
|
243
|
+
* @see https://redis.io/commands/getrange/
|
|
244
|
+
*/
|
|
245
|
+
async getRange(key: K, start: number, end: number): Promise<string> {
|
|
246
|
+
const source = getCurrentRequest();
|
|
247
|
+
const mappedKey = this.mapKey(key);
|
|
248
|
+
const result = await this.cluster.impl.getRange(
|
|
249
|
+
mappedKey,
|
|
250
|
+
start,
|
|
251
|
+
end,
|
|
252
|
+
source
|
|
253
|
+
);
|
|
254
|
+
return result.toString("utf-8");
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Overwrites part of the string stored at key, starting at
|
|
259
|
+
* the zero-based `offset` and for the entire length of `value`, extending
|
|
260
|
+
* the string if necessary.
|
|
261
|
+
*
|
|
262
|
+
* If the offset is larger than the current string length stored at key,
|
|
263
|
+
* the string is first padded with zero-bytes to make offset fit.
|
|
264
|
+
*
|
|
265
|
+
* Non-existing keys are considered as empty strings.
|
|
266
|
+
*
|
|
267
|
+
* @param key - The cache key.
|
|
268
|
+
* @param offset - Zero-based byte offset to start writing at.
|
|
269
|
+
* @param value - The string to write.
|
|
270
|
+
* @returns The length of the string after the operation.
|
|
271
|
+
* @see https://redis.io/commands/setrange/
|
|
272
|
+
*/
|
|
273
|
+
async setRange(
|
|
274
|
+
key: K,
|
|
275
|
+
offset: number,
|
|
276
|
+
value: string,
|
|
277
|
+
options?: WriteOptions
|
|
278
|
+
): Promise<number> {
|
|
279
|
+
const source = getCurrentRequest();
|
|
280
|
+
const mappedKey = this.mapKey(key);
|
|
281
|
+
const ttlMs = this.resolveTtl(options);
|
|
282
|
+
const result = await this.cluster.impl.setRange(
|
|
283
|
+
mappedKey,
|
|
284
|
+
offset,
|
|
285
|
+
Buffer.from(value, "utf-8"),
|
|
286
|
+
ttlMs,
|
|
287
|
+
source
|
|
288
|
+
);
|
|
289
|
+
return Number(result);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Returns the length of the string value stored at key.
|
|
294
|
+
*
|
|
295
|
+
* Non-existing keys are considered as empty strings.
|
|
296
|
+
*
|
|
297
|
+
* @returns The string length.
|
|
298
|
+
* @see https://redis.io/commands/strlen/
|
|
299
|
+
*/
|
|
300
|
+
async len(key: K): Promise<number> {
|
|
301
|
+
const source = getCurrentRequest();
|
|
302
|
+
const mappedKey = this.mapKey(key);
|
|
303
|
+
const result = await this.cluster.impl.strlen(mappedKey, source);
|
|
304
|
+
return Number(result);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* IntKeyspace stores 64-bit integer values.
|
|
310
|
+
* Values are floored to integers using `Math.floor`.
|
|
311
|
+
* For fractional values, use {@link FloatKeyspace} instead.
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```ts
|
|
315
|
+
* const counters = new IntKeyspace<string>(cluster, {
|
|
316
|
+
* keyPattern: "counter/:name",
|
|
317
|
+
* });
|
|
318
|
+
*
|
|
319
|
+
* await counters.set("page-views", 0);
|
|
320
|
+
* const newCount = await counters.increment("page-views", 1);
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
export class IntKeyspace<K> extends BasicKeyspace<K, number> {
|
|
324
|
+
constructor(cluster: CacheCluster, config: KeyspaceConfig<K>) {
|
|
325
|
+
super(cluster, config);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
protected serialize(value: number): Buffer {
|
|
329
|
+
return Buffer.from(String(Math.floor(value)), "utf-8");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
protected deserialize(data: Buffer): number {
|
|
333
|
+
return parseInt(data.toString("utf-8"), 10);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Increments the number stored at key by `delta`.
|
|
338
|
+
*
|
|
339
|
+
* If the key does not exist it is first created with a value of 0
|
|
340
|
+
* before incrementing.
|
|
341
|
+
*
|
|
342
|
+
* Negative values can be used to decrease the value,
|
|
343
|
+
* but typically you want to use {@link decrement} for that.
|
|
344
|
+
*
|
|
345
|
+
* @param key - The cache key.
|
|
346
|
+
* @param delta - The amount to increment by (default 1).
|
|
347
|
+
* @returns The new value after incrementing.
|
|
348
|
+
* @see https://redis.io/commands/incrby/
|
|
349
|
+
*/
|
|
350
|
+
async increment(
|
|
351
|
+
key: K,
|
|
352
|
+
delta: number = 1,
|
|
353
|
+
options?: WriteOptions
|
|
354
|
+
): Promise<number> {
|
|
355
|
+
const source = getCurrentRequest();
|
|
356
|
+
const mappedKey = this.mapKey(key);
|
|
357
|
+
const ttlMs = this.resolveTtl(options);
|
|
358
|
+
return await this.cluster.impl.incrBy(
|
|
359
|
+
mappedKey,
|
|
360
|
+
Math.floor(delta),
|
|
361
|
+
ttlMs,
|
|
362
|
+
source
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Decrements the number stored at key by `delta`.
|
|
368
|
+
*
|
|
369
|
+
* If the key does not exist it is first created with a value of 0
|
|
370
|
+
* before decrementing.
|
|
371
|
+
*
|
|
372
|
+
* Negative values can be used to increase the value,
|
|
373
|
+
* but typically you want to use {@link increment} for that.
|
|
374
|
+
*
|
|
375
|
+
* @param key - The cache key.
|
|
376
|
+
* @param delta - The amount to decrement by (default 1).
|
|
377
|
+
* @returns The new value after decrementing.
|
|
378
|
+
* @see https://redis.io/commands/decrby/
|
|
379
|
+
*/
|
|
380
|
+
async decrement(
|
|
381
|
+
key: K,
|
|
382
|
+
delta: number = 1,
|
|
383
|
+
options?: WriteOptions
|
|
384
|
+
): Promise<number> {
|
|
385
|
+
const source = getCurrentRequest();
|
|
386
|
+
const mappedKey = this.mapKey(key);
|
|
387
|
+
const ttlMs = this.resolveTtl(options);
|
|
388
|
+
return await this.cluster.impl.decrBy(
|
|
389
|
+
mappedKey,
|
|
390
|
+
Math.floor(delta),
|
|
391
|
+
ttlMs,
|
|
392
|
+
source
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* FloatKeyspace stores 64-bit floating point values.
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```ts
|
|
402
|
+
* const scores = new FloatKeyspace<string>(cluster, {
|
|
403
|
+
* keyPattern: "score/:playerId",
|
|
404
|
+
* });
|
|
405
|
+
*
|
|
406
|
+
* await scores.set("player1", 100.5);
|
|
407
|
+
* const newScore = await scores.increment("player1", 10.25);
|
|
408
|
+
* ```
|
|
409
|
+
*/
|
|
410
|
+
export class FloatKeyspace<K> extends BasicKeyspace<K, number> {
|
|
411
|
+
constructor(cluster: CacheCluster, config: KeyspaceConfig<K>) {
|
|
412
|
+
super(cluster, config);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
protected serialize(value: number): Buffer {
|
|
416
|
+
return Buffer.from(String(value), "utf-8");
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
protected deserialize(data: Buffer): number {
|
|
420
|
+
return parseFloat(data.toString("utf-8"));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Increments the number stored at key by `delta`.
|
|
425
|
+
*
|
|
426
|
+
* If the key does not exist it is first created with a value of 0
|
|
427
|
+
* before incrementing.
|
|
428
|
+
*
|
|
429
|
+
* Negative values can be used to decrease the value,
|
|
430
|
+
* but typically you want to use {@link decrement} for that.
|
|
431
|
+
*
|
|
432
|
+
* @param key - The cache key.
|
|
433
|
+
* @param delta - The amount to increment by (default 1).
|
|
434
|
+
* @returns The new value after incrementing.
|
|
435
|
+
* @see https://redis.io/commands/incrbyfloat/
|
|
436
|
+
*/
|
|
437
|
+
async increment(
|
|
438
|
+
key: K,
|
|
439
|
+
delta: number = 1,
|
|
440
|
+
options?: WriteOptions
|
|
441
|
+
): Promise<number> {
|
|
442
|
+
const source = getCurrentRequest();
|
|
443
|
+
const mappedKey = this.mapKey(key);
|
|
444
|
+
const ttlMs = this.resolveTtl(options);
|
|
445
|
+
return await this.cluster.impl.incrByFloat(mappedKey, delta, ttlMs, source);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Decrements the number stored at key by `delta`.
|
|
450
|
+
*
|
|
451
|
+
* If the key does not exist it is first created with a value of 0
|
|
452
|
+
* before decrementing.
|
|
453
|
+
*
|
|
454
|
+
* Negative values can be used to increase the value,
|
|
455
|
+
* but typically you want to use {@link increment} for that.
|
|
456
|
+
*
|
|
457
|
+
* @param key - The cache key.
|
|
458
|
+
* @param delta - The amount to decrement by (default 1).
|
|
459
|
+
* @returns The new value after decrementing.
|
|
460
|
+
* @see https://redis.io/commands/incrbyfloat/
|
|
461
|
+
*/
|
|
462
|
+
async decrement(
|
|
463
|
+
key: K,
|
|
464
|
+
delta: number = 1,
|
|
465
|
+
options?: WriteOptions
|
|
466
|
+
): Promise<number> {
|
|
467
|
+
const source = getCurrentRequest();
|
|
468
|
+
const mappedKey = this.mapKey(key);
|
|
469
|
+
const ttlMs = this.resolveTtl(options);
|
|
470
|
+
return await this.cluster.impl.incrByFloat(
|
|
471
|
+
mappedKey,
|
|
472
|
+
-delta,
|
|
473
|
+
ttlMs,
|
|
474
|
+
source
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* StructKeyspace stores arbitrary objects serialized as JSON.
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* ```ts
|
|
484
|
+
* interface User {
|
|
485
|
+
* id: string;
|
|
486
|
+
* name: string;
|
|
487
|
+
* email: string;
|
|
488
|
+
* }
|
|
489
|
+
*
|
|
490
|
+
* const users = new StructKeyspace<string, User>(cluster, {
|
|
491
|
+
* keyPattern: "user/:id",
|
|
492
|
+
* defaultExpiry: ExpireIn(3600000),
|
|
493
|
+
* });
|
|
494
|
+
*
|
|
495
|
+
* await users.set("user1", { id: "user1", name: "Alice", email: "alice@example.com" });
|
|
496
|
+
* const user = await users.get("user1");
|
|
497
|
+
* ```
|
|
498
|
+
*/
|
|
499
|
+
export class StructKeyspace<K, V> extends BasicKeyspace<K, V> {
|
|
500
|
+
constructor(cluster: CacheCluster, config: KeyspaceConfig<K>) {
|
|
501
|
+
super(cluster, config);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
protected serialize(value: V): Buffer {
|
|
505
|
+
return Buffer.from(JSON.stringify(value), "utf-8");
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
protected deserialize(data: Buffer): V {
|
|
509
|
+
return JSON.parse(data.toString("utf-8")) as V;
|
|
510
|
+
}
|
|
511
|
+
}
|