-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
be374df
commit c4237d0
Showing
13 changed files
with
224 additions
and
158 deletions.
There are no files selected for viewing
Binary file not shown.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { strEqualsIgnoreCase, expiresInHours } from 'live-connect-common' | ||
import { WrappedStorageHandler } from './handlers/storage-handler' | ||
import { StorageStrategies, StorageStrategy } from './model/storage-strategy' | ||
|
||
export type CacheRecord = { | ||
data: string | ||
expiresAt?: Date | ||
} | ||
|
||
export interface DurableCache { | ||
get: (key: string) => CacheRecord | null // null is used to signal missing value | ||
set: (key: string, value: string, expiration?: Date) => void | ||
} | ||
|
||
export type StorageHandlerBackedCacheOpts = { | ||
strategy: 'cookie' | 'ls', | ||
storageHandler: WrappedStorageHandler, | ||
domain: string, | ||
defaultExpirationHours?: number | ||
} | ||
|
||
export type MakeCacheOpts = StorageHandlerBackedCacheOpts & { | ||
strategy: StorageStrategy, | ||
} | ||
|
||
export function makeCache(opts: MakeCacheOpts): DurableCache { | ||
if (!strEqualsIgnoreCase(opts.strategy, StorageStrategies.cookie) && strEqualsIgnoreCase(opts.strategy, StorageStrategies.none)) { | ||
return NoOpCache | ||
} else { | ||
// TODO: Remove once we validate config properly | ||
const strategyWithDefault = opts.strategy ?? StorageStrategies.cookie | ||
return new StorageHandlerBackedCache({ ...opts, strategy: strategyWithDefault }) | ||
} | ||
} | ||
|
||
export class StorageHandlerBackedCache implements DurableCache { | ||
private handler | ||
private storageStrategy | ||
private defaultExpirationHours? | ||
private domain | ||
|
||
constructor (opts: StorageHandlerBackedCacheOpts) { | ||
this.handler = opts.storageHandler | ||
this.storageStrategy = opts.strategy | ||
this.defaultExpirationHours = opts.defaultExpirationHours | ||
this.domain = opts.domain | ||
} | ||
|
||
private getCookieRecord(key: string): CacheRecord | null { | ||
let expiresAt: Date | undefined | ||
|
||
const cookieExpirationEntry = this.handler.getCookie(expirationKey(key)) | ||
if (cookieExpirationEntry && cookieExpirationEntry.length > 0) { | ||
expiresAt = new Date(cookieExpirationEntry) | ||
if (expiresAt <= new Date()) { | ||
return null | ||
} | ||
} | ||
|
||
const data = this.handler.getCookie(key) | ||
if (data) { | ||
return { data, expiresAt } | ||
} else { | ||
return null | ||
} | ||
} | ||
|
||
private getLSRecord(key: string): CacheRecord | null { | ||
let expiresAt: Date | undefined | ||
const oldLsExpirationEntry = this.handler.getDataFromLocalStorage(expirationKey(key)) | ||
|
||
if (oldLsExpirationEntry) { | ||
expiresAt = new Date(oldLsExpirationEntry) | ||
if (expiresAt <= new Date()) { | ||
this.handler.removeDataFromLocalStorage(key) | ||
this.handler.removeDataFromLocalStorage(expirationKey(key)) | ||
return null | ||
} | ||
} | ||
|
||
const data = this.handler.getDataFromLocalStorage(key) | ||
if (data) { | ||
return { data, expiresAt } | ||
} else { | ||
return null | ||
} | ||
} | ||
|
||
get(key: string): CacheRecord | null { | ||
if (strEqualsIgnoreCase(this.storageStrategy, StorageStrategies.localStorage) && this.handler.localStorageIsEnabled()) { | ||
return this.getLSRecord(key) | ||
} else { | ||
return this.getCookieRecord(key) | ||
} | ||
} | ||
|
||
set(key: string, value: string, expires?: Date): void { | ||
if (!expires && this.defaultExpirationHours) { | ||
expires = expiresInHours(this.defaultExpirationHours) | ||
} | ||
|
||
if (strEqualsIgnoreCase(this.storageStrategy, StorageStrategies.localStorage) && this.handler.localStorageIsEnabled()) { | ||
this.handler.setDataInLocalStorage(key, value) | ||
if (expires) { | ||
this.handler.setDataInLocalStorage(expirationKey(key), `${expires}`) | ||
} else { | ||
this.handler.removeDataFromLocalStorage(expirationKey(key)) | ||
} | ||
} else { | ||
this.handler.setCookie(key, value, expires, 'Lax', this.domain) | ||
if (expires) { | ||
this.handler.setCookie(expirationKey(key), `${expires}`, expires, 'Lax', this.domain) | ||
} else { | ||
// sentinel value to indicate no expiration | ||
this.handler.setCookie(expirationKey(key), '', undefined, 'Lax', this.domain) | ||
} | ||
} | ||
} | ||
} | ||
|
||
export const NoOpCache: DurableCache = { | ||
get: () => null, | ||
set: () => undefined | ||
} | ||
|
||
function expirationKey(baseKey: string): string { | ||
return `${baseKey}_exp` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.