diff --git a/CHANGELOG.md b/CHANGELOG.md index a679780281..6730c12791 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ #### Changes +* Node: Added EXPIRETIME and PEXPIRETIME commands ([#2063](https://github.com/valkey-io/valkey-glide/pull/2063)) * Node: Added SORT commands ([#2028](https://github.com/valkey-io/valkey-glide/pull/2028)) * Node: Added LASTSAVE command ([#2059](https://github.com/valkey-io/valkey-glide/pull/2059)) * Node: Added LCS command ([#2049](https://github.com/valkey-io/valkey-glide/pull/2049)) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 97cfcce3dc..12761b85e6 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -62,6 +62,7 @@ import { createExists, createExpire, createExpireAt, + createExpireTime, createFCall, createFCallReadOnly, createGeoAdd, @@ -110,6 +111,7 @@ import { createObjectRefcount, createPExpire, createPExpireAt, + createPExpireTime, createPTTL, createPersist, createPfAdd, @@ -2429,6 +2431,35 @@ export class BaseClient { ); } + /** + * Returns the absolute Unix timestamp (since January 1, 1970) at which the given `key` will expire, in seconds. + * To get the expiration with millisecond precision, use {@link pexpiretime}. + * + * See https://valkey.io/commands/expiretime/ for details. + * + * @param key - The `key` to determine the expiration value of. + * @returns The expiration Unix timestamp in seconds, `-2` if `key` does not exist or `-1` if `key` exists but has no associated expire. + * + * since Valkey version 7.0.0. + * + * @example + * ```typescript + * const result1 = await client.expiretime("myKey"); + * console.log(result1); // Output: -2 - myKey doesn't exist. + * + * const result2 = await client.set(myKey, "value"); + * const result3 = await client.expireTime(myKey); + * console.log(result2); // Output: -1 - myKey has no associated expiration. + * + * client.expire(myKey, 60); + * const result3 = await client.expireTime(myKey); + * console.log(result3); // Output: 123456 - the Unix timestamp (in seconds) when "myKey" will expire. + * ``` + */ + public async expiretime(key: string): Promise { + return this.createWritePromise(createExpireTime(key)); + } + /** Sets a timeout on `key` in milliseconds. After the timeout has expired, the key will automatically be deleted. * If `key` already has an existing expire set, the time to live is updated to the new value. * If `milliseconds` is non-positive number, the key will be deleted rather than expired. @@ -2487,6 +2518,34 @@ export class BaseClient { ); } + /** + * Returns the absolute Unix timestamp (since January 1, 1970) at which the given `key` will expire, in milliseconds. + * + * See https://valkey.io/commands/pexpiretime/ for details. + * + * @param key - The `key` to determine the expiration value of. + * @returns The expiration Unix timestamp in seconds, `-2` if `key` does not exist or `-1` if `key` exists but has no associated expire. + * + * since Valkey version 7.0.0. + * + * @example + * ```typescript + * const result1 = client.pexpiretime("myKey"); + * console.log(result1); // Output: -2 - myKey doesn't exist. + * + * const result2 = client.set(myKey, "value"); + * const result3 = client.pexpireTime(myKey); + * console.log(result2); // Output: -1 - myKey has no associated expiration. + * + * client.expire(myKey, 60); + * const result3 = client.pexpireTime(myKey); + * console.log(result3); // Output: 123456789 - the Unix timestamp (in milliseconds) when "myKey" will expire. + * ``` + */ + public async pexpiretime(key: string): Promise { + return this.createWritePromise(createPExpireTime(key)); + } + /** Returns the remaining time to live of `key` that has a timeout. * See https://valkey.io/commands/ttl/ for details. * diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 8fb9d53b24..81573afe55 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -1265,6 +1265,13 @@ export function createExpireAt( return createCommand(RequestType.ExpireAt, args); } +/** + * @internal + */ +export function createExpireTime(key: string): command_request.Command { + return createCommand(RequestType.ExpireTime, [key]); +} + /** * @internal */ @@ -1295,6 +1302,13 @@ export function createPExpireAt( return createCommand(RequestType.PExpireAt, args); } +/** + * @internal + */ +export function createPExpireTime(key: string): command_request.Command { + return createCommand(RequestType.PExpireTime, [key]); +} + /** * @internal */ diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index 61f03233cf..3d41ed5e39 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -76,6 +76,7 @@ import { createExists, createExpire, createExpireAt, + createExpireTime, createFCall, createFCallReadOnly, createFlushAll, @@ -133,6 +134,7 @@ import { createObjectRefcount, createPExpire, createPExpireAt, + createPExpireTime, createPTTL, createPersist, createPfAdd, @@ -1333,6 +1335,22 @@ export class BaseTransaction> { return this.addAndReturn(createExpireAt(key, unixSeconds, option)); } + /** + * Returns the absolute Unix timestamp (since January 1, 1970) at which the given `key` will expire, in seconds. + * To get the expiration with millisecond precision, use {@link pexpiretime}. + * + * See https://valkey.io/commands/expiretime/ for details. + * + * @param key - The `key` to determine the expiration value of. + * + * Command Response - The expiration Unix timestamp in seconds, `-2` if `key` does not exist or `-1` if `key` exists but has no associated expire. + * + * since Valkey version 7.0.0. + */ + public expireTime(key: string): T { + return this.addAndReturn(createExpireTime(key)); + } + /** Sets a timeout on `key` in milliseconds. After the timeout has expired, the key will automatically be deleted. * If `key` already has an existing expire set, the time to live is updated to the new value. * If `milliseconds` is non-positive number, the key will be deleted rather than expired. @@ -1377,6 +1395,21 @@ export class BaseTransaction> { ); } + /** + * Returns the absolute Unix timestamp (since January 1, 1970) at which the given `key` will expire, in milliseconds. + * + * See https://valkey.io/commands/pexpiretime/ for details. + * + * @param key - The `key` to determine the expiration value of. + * + * Command Response - The expiration Unix timestamp in seconds, `-2` if `key` does not exist or `-1` if `key` exists but has no associated expire. + * + * since Valkey version 7.0.0. + */ + public pexpireTime(key: string): T { + return this.addAndReturn(createPExpireTime(key)); + } + /** Returns the remaining time to live of `key` that has a timeout. * See https://valkey.io/commands/ttl/ for details. * diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index 87c96f32d8..dd47041824 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -2688,6 +2688,12 @@ export function runBaseTests(config: { ExpireOptions.HasExistingExpiry, ), ).toEqual(true); + expect(await client.expiretime(key)).toBeGreaterThan( + Math.floor(Date.now() / 1000), + ); + expect(await client.pexpiretime(key)).toBeGreaterThan( + Date.now(), + ); } expect(await client.ttl(key)).toBeLessThanOrEqual(15); @@ -2751,7 +2757,7 @@ export function runBaseTests(config: { it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( `expire, pexpire, expireAt and pexpireAt with timestamp in the past or negative timeout_%p`, async (protocol) => { - await runTest(async (client: BaseClient) => { + await runTest(async (client: BaseClient, cluster) => { const key = uuidv4(); expect(await client.set(key, "foo")).toEqual("OK"); expect(await client.ttl(key)).toEqual(-1); @@ -2769,6 +2775,13 @@ export function runBaseTests(config: { ).toEqual(true); expect(await client.ttl(key)).toEqual(-2); expect(await client.set(key, "foo")).toEqual("OK"); + + // no timeout set yet + if (!cluster.checkIfServerVersionLessThan("7.0.0")) { + expect(await client.expiretime(key)).toEqual(-1); + expect(await client.pexpiretime(key)).toEqual(-1); + } + expect( await client.pexpireAt( key, @@ -2784,7 +2797,7 @@ export function runBaseTests(config: { it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( `expire, pexpire, expireAt, pexpireAt and ttl with non-existing key_%p`, async (protocol) => { - await runTest(async (client: BaseClient) => { + await runTest(async (client: BaseClient, cluster) => { const key = uuidv4(); expect(await client.expire(key, 10)).toEqual(false); expect(await client.pexpire(key, 10000)).toEqual(false); @@ -2801,6 +2814,11 @@ export function runBaseTests(config: { ), ).toEqual(false); expect(await client.ttl(key)).toEqual(-2); + + if (!cluster.checkIfServerVersionLessThan("7.0.0")) { + expect(await client.expiretime(key)).toEqual(-2); + expect(await client.pexpiretime(key)).toEqual(-2); + } }, protocol); }, config.timeout, diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index 34d79f3f05..ea87b86daf 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -506,6 +506,15 @@ export async function transactionTest( responseData.push(["echo(value)", value]); baseTransaction.persist(key1); responseData.push(["persist(key1)", false]); + + if (gte(version, "7.0.0")) { + baseTransaction.expireTime(key1); + responseData.push(["expiretime(key1)", -1]); + + baseTransaction.pexpireTime(key1); + responseData.push(["pexpiretime(key1)", -1]); + } + baseTransaction.set(key2, "baz", { returnOldValue: true }); responseData.push(['set(key2, "baz", { returnOldValue: true })', null]); baseTransaction.customCommand(["MGET", key1, key2]);