@@ -109,10 +109,19 @@ export class NodeCache<T> extends Hookified {
109109
110110 /**
111111 * Sets a key value pair. It is possible to define a ttl (in seconds). Returns true on success.
112+ *
113+ * TTL behavior:
114+ * - `ttl > 0`: cache expires after the given number of seconds
115+ * - `ttl === 0`: cache indefinitely (overrides stdTTL)
116+ * - `ttl < 0`: store the value but it expires immediately on next access (matches original node-cache behavior)
117+ * - `ttl` omitted/undefined: use `stdTTL` from options (0 = unlimited if stdTTL is 0 or not set)
118+ * - `ttl` as string: shorthand format like '1h', '30s', '5m', '2d'
119+ *
112120 * @param {string | number } key - it will convert the key to a string
113121 * @param {T } value
114- * @param {number | string } [ttl] - this is in seconds and undefined will use the default ttl
115- * @returns {boolean }
122+ * @param {number | string } [ttl] - TTL in seconds. 0 = unlimited, negative = expires immediately, string = shorthand format
123+ * @returns {boolean } true on success
124+ * @throws {Error } If the `key` or `ttl` is of an invalid type.
116125 */
117126 public set ( key : string | number , value : T , ttl ?: number | string ) : boolean {
118127 // Check on key type
@@ -131,15 +140,20 @@ export class NodeCache<T> extends Hookified {
131140 throw this . createError ( NodeCacheErrors . ETTLTYPE , this . formatKey ( key ) ) ;
132141 }
133142
134- // Reject negative TTL values (numeric or numeric string)
135- if ( this . isNegativeTtl ( ttl ) ) {
136- return false ;
137- }
138-
139143 const keyValue = this . formatKey ( key ) ;
140144 let expirationTimestamp = 0 ; // 0 = never delete
141145
142- if ( ttl !== undefined && ( typeof ttl === "string" || ttl > 0 ) ) {
146+ if ( this . isNegativeTtl ( ttl ) ) {
147+ // Negative TTL: store with a past timestamp so it expires immediately on next access.
148+ // Math.max(1, ...) ensures the timestamp is always > 0, since the expiration
149+ // check only triggers for ttl > 0 (0 means "unlimited").
150+ expirationTimestamp = Math . max (
151+ 1 ,
152+ this . getExpirationTimestamp (
153+ typeof ttl === "string" ? Number ( ttl ) : ( ttl as number ) ,
154+ ) ,
155+ ) ;
156+ } else if ( ttl !== undefined && ( typeof ttl === "string" || ttl > 0 ) ) {
143157 // Explicit positive TTL or string shorthand overrides stdTTL
144158 expirationTimestamp = this . resolveExpiration ( ttl ) ;
145159 } else if (
@@ -179,8 +193,16 @@ export class NodeCache<T> extends Hookified {
179193
180194 /**
181195 * Sets multiple key val pairs. It is possible to define a ttl (seconds). Returns true on success.
196+ *
197+ * Each item follows the same TTL behavior as `set()`:
198+ * - Positive TTL: expires after the given seconds
199+ * - `0`: cache indefinitely
200+ * - Negative TTL: stored but expires immediately on next access
201+ * - Omitted: uses `stdTTL` from options
202+ *
182203 * @param {PartialNodeCacheItem<T>[] } data an array of key value pairs with optional ttl
183- * @returns {boolean }
204+ * @returns {boolean } true on success
205+ * @throws {Error } If `data` is not an array, or if any item has an invalid key or ttl type.
184206 */
185207 public mset ( data : Array < PartialNodeCacheItem < T > > ) : boolean {
186208 // Check on keys type
@@ -189,14 +211,11 @@ export class NodeCache<T> extends Hookified {
189211 throw this . createError ( NodeCacheErrors . EKEYSTYPE ) ;
190212 }
191213
192- let success = true ;
193214 for ( const item of data ) {
194- if ( ! this . set ( item . key , item . value , item . ttl ) ) {
195- success = false ;
196- }
215+ this . set ( item . key , item . value , item . ttl ) ;
197216 }
198217
199- return success ;
218+ return true ;
200219 }
201220
202221 /**
@@ -325,19 +344,43 @@ export class NodeCache<T> extends Hookified {
325344 /**
326345 * Redefine the ttl of a key. Returns true if the key has been found and changed.
327346 * Otherwise returns false. If the ttl-argument isn't passed the default-TTL will be used.
347+ *
348+ * TTL behavior:
349+ * - `ttl > 0`: key expires after the given number of seconds
350+ * - `ttl === 0`: key lives indefinitely (overrides stdTTL)
351+ * - `ttl < 0`: key expires immediately on next access (matches original node-cache behavior)
352+ * - `ttl` omitted/undefined: use `stdTTL` from options (0 = unlimited if stdTTL is 0 or not set)
353+ * - `ttl` as string: shorthand format like '1h', '30s', '5m', '2d'
354+ *
328355 * @param {string | number } key if the key is a number it will convert it to a string
329- * @param {number | string } [ttl] the ttl in seconds if number, or a shorthand string like '1h' for 1 hour
356+ * @param {number | string } [ttl] TTL in seconds. 0 = unlimited, negative = expires immediately, string = shorthand format
330357 * @returns {boolean } true if the key has been found and changed. Otherwise returns false.
358+ * @throws {Error } If the `ttl` is of an invalid type (must be a number or string).
331359 */
332360 public ttl ( key : string | number , ttl ?: number | string ) : boolean {
333- // Reject negative TTL values (numeric or numeric string)
334- if ( this . isNegativeTtl ( ttl ) ) {
335- return false ;
361+ // Check on ttl type
362+ /* v8 ignore next -- @preserve */
363+ if (
364+ ttl !== undefined &&
365+ typeof ttl !== "number" &&
366+ typeof ttl !== "string"
367+ ) {
368+ throw this . createError ( NodeCacheErrors . ETTLTYPE , this . formatKey ( key ) ) ;
336369 }
337370
338371 const result = this . store . get ( this . formatKey ( key ) ) ;
339372 if ( result ) {
340- if ( ttl !== undefined && ( typeof ttl === "string" || ttl > 0 ) ) {
373+ if ( this . isNegativeTtl ( ttl ) ) {
374+ // Negative TTL: set past timestamp so it expires immediately on next access.
375+ // Math.max(1, ...) ensures the timestamp is always > 0, since the expiration
376+ // check only triggers for ttl > 0 (0 means "unlimited").
377+ result . ttl = Math . max (
378+ 1 ,
379+ this . getExpirationTimestamp (
380+ typeof ttl === "string" ? Number ( ttl ) : ( ttl as number ) ,
381+ ) ,
382+ ) ;
383+ } else if ( ttl !== undefined && ( typeof ttl === "string" || ttl > 0 ) ) {
341384 // Explicit positive TTL or string shorthand
342385 result . ttl = this . resolveExpiration ( ttl ) ;
343386 } else if ( ttl === 0 ) {
0 commit comments