Skip to content

Instantly share code, notes, and snippets.

@oaustegard
Last active April 24, 2023 08:33
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oaustegard/aa821a4154f91881c26569da9b02e1d1 to your computer and use it in GitHub Desktop.
Save oaustegard/aa821a4154f91881c26569da9b02e1d1 to your computer and use it in GitHub Desktop.
CookieStoreShim -- a shim for the CookieStore API
class CookieStoreShim {
static isSupported() {
return typeof window.CookieStore !== 'undefined';
}
async get(nameOrOptions) {
if (CookieStoreShim.isSupported()) {
return cookieStore.get(nameOrOptions);
} else {
const name = typeof nameOrOptions === 'string' ? nameOrOptions : nameOrOptions.name;
const value = this._getCookie(name);
return value ? { name, value } : null;
}
}
async getAll(nameOrOptions) {
if (CookieStoreShim.isSupported()) {
return cookieStore.getAll(nameOrOptions);
} else {
const name = typeof nameOrOptions === 'string' ? nameOrOptions : nameOrOptions.name;
return this._getAllCookies().filter(cookie => cookie.name === name);
}
}
async set(nameOrOptions, value) {
if (CookieStoreShim.isSupported()) {
if (typeof nameOrOptions === 'string') {
return cookieStore.set({ name: nameOrOptions, value });
} else {
return cookieStore.set(nameOrOptions);
}
} else {
if (typeof nameOrOptions === 'string') {
this._setCookie(nameOrOptions, value);
} else {
this._setCookie(nameOrOptions.name, nameOrOptions.value, nameOrOptions);
}
return Promise.resolve();
}
}
async delete(nameOrOptions) {
if (CookieStoreShim.isSupported()) {
return cookieStore.delete(nameOrOptions);
} else {
const name = typeof nameOrOptions === 'string' ? nameOrOptions : nameOrOptions.name;
this._setCookie(name, '', { expires: -1 });
return Promise.resolve();
}
}
_getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) {
return parts.pop().split(';').shift();
}
return null;
}
_getAllCookies() {
const cookies = document.cookie.split('; ');
return cookies.map(cookie => {
const [name, value] = cookie.split('=');
return { name, value };
});
}
_setCookie(name, value, options = {}) {
let attributes = '';
if (options.expires) {
const date = new Date(options.expires);
attributes += `; expires=${date.toUTCString()}`;
}
if (options.domain) {
attributes += `; domain=${options.domain}`;
}
if (options.path) {
attributes += `; path=${options.path}`;
}
if (options.sameSite) {
attributes += `; samesite=${options.sameSite}`;
}
document.cookie = `${name}=${value}${attributes}`;
}
}
const cookieStoreShim = new CookieStoreShim();

CookieStoreShim -- a shim for the CookieStore API

Generated by GPT-4

How to do SW Development, April 2023

  1. Read blogpost about experimental Web standard: https://www.raymondcamden.com/2023/04/12/using-the-cookie-store-api
  2. Follow link to docs: https://developer.mozilla.org/en-US/docs/Web/API/Cookie_Store_API
  3. Follow link to specs: https://wicg.github.io/cookie-store/#CookieStore
  4. Copy specs into GPT-4 and ask it to generate a shim in JS, TS and add some tests: https://austegard.com/pv?e5b305cf008b86862389e20fa5a633d6
  5. Code: This gist
type CookieOptions = {
expires?: number;
domain?: string;
path?: string;
sameSite?: 'strict' | 'lax' | 'none';
};
type CookieItem = {
name: string;
value: string;
domain?: string;
path: string;
expires?: number;
secure?: boolean;
sameSite?: 'strict' | 'lax' | 'none';
};
class CookieStoreShim {
static isSupported(): boolean {
return typeof (window as any).CookieStore !== 'undefined';
}
async get(nameOrOptions: string | { name: string }): Promise<CookieItem | null> {
if (CookieStoreShim.isSupported()) {
return (window as any).cookieStore.get(nameOrOptions);
} else {
const name = typeof nameOrOptions === 'string' ? nameOrOptions : nameOrOptions.name;
const value = this._getCookie(name);
return value ? { name, value } : null;
}
}
async getAll(nameOrOptions: string | { name: string }): Promise<CookieItem[]> {
if (CookieStoreShim.isSupported()) {
return (window as any).cookieStore.getAll(nameOrOptions);
} else {
const name = typeof nameOrOptions === 'string' ? nameOrOptions : nameOrOptions.name;
return this._getAllCookies().filter((cookie: CookieItem) => cookie.name === name);
}
}
async set(nameOrOptions: string | CookieOptions & { name: string; value: string }, value?: string): Promise<void> {
if (CookieStoreShim.isSupported()) {
if (typeof nameOrOptions === 'string') {
return (window as any).cookieStore.set({ name: nameOrOptions, value });
} else {
return (window as any).cookieStore.set(nameOrOptions);
}
} else {
if (typeof nameOrOptions === 'string') {
this._setCookie(nameOrOptions, value!);
} else {
this._setCookie(nameOrOptions.name, nameOrOptions.value, nameOrOptions);
}
return Promise.resolve();
}
}
async delete(nameOrOptions: string | { name: string }): Promise<void> {
if (CookieStoreShim.isSupported()) {
return (window as any).cookieStore.delete(nameOrOptions);
} else {
const name = typeof nameOrOptions === 'string' ? nameOrOptions : nameOrOptions.name;
this._setCookie(name, '', { expires: -1 });
return Promise.resolve();
}
}
private _getCookie(name: string): string | null {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) {
return parts.pop().split(';').shift()!;
}
return null;
}
private _getAllCookies(): CookieItem[] {
const cookies = document.cookie.split('; ');
return cookies.map((cookie) => {
const [name, value] = cookie.split('=');
return { name, value };
});
}
private _setCookie(name: string, value: string, options: CookieOptions = {}): void {
let attributes = '';
if (options.expires) {
const date = new Date(options.expires);
attributes += `; expires=${date.toUTCString()}`;
}
if (options.domain) {
attributes += `; domain=${options.domain}`;
}
if (options.path) {
attributes += ; path=${options.path};
}
if (options.sameSite) {
attributes += ; samesite=${options.sameSite};
}
document.cookie = ${name}=${value}${attributes};
}
}
const cookieStoreShim = new CookieStoreShim();
async function runTests() {
console.log('Running tests...');
// Test 1: Set a cookie
await cookieStoreShim.set('testCookie', 'testValue');
const setCookieResult = await cookieStoreShim.get('testCookie');
console.assert(setCookieResult.value === 'testValue', 'Test 1: Set a cookie - FAILED');
// Test 2: Update a cookie
await cookieStoreShim.set('testCookie', 'updatedValue');
const updatedCookieResult = await cookieStoreShim.get('testCookie');
console.assert(updatedCookieResult.value === 'updatedValue', 'Test 2: Update a cookie - FAILED');
// Test 3: Delete a cookie
await cookieStoreShim.delete('testCookie');
const deletedCookieResult = await cookieStoreShim.get('testCookie');
console.assert(deletedCookieResult === null, 'Test 3: Delete a cookie - FAILED');
// Test 4: Get all cookies with a specific name
await cookieStoreShim.set('testCookie1', 'value1');
await cookieStoreShim.set('testCookie2', 'value2');
const allNamedCookies = await cookieStoreShim.getAll('testCookie1');
console.assert(allNamedCookies.length === 1 && allNamedCookies[0].value === 'value1', 'Test 4: Get all cookies with a specific name - FAILED');
// Test 5: Set a cookie with options
const cookieOptions = {
expires: Date.now() + 1000 * 60 * 60 * 24, // Expires in 1 day
domain: window.location.hostname,
path: '/',
sameSite: 'strict',
};
await cookieStoreShim.set({ ...cookieOptions, name: 'testCookieWithOptions', value: 'optionsValue' });
const cookieWithOptions = await cookieStoreShim.get('testCookieWithOptions');
console.assert(cookieWithOptions.value === 'optionsValue', 'Test 5: Set a cookie with options - FAILED');
// Test 6: Attempt to set a cookie without a name
try {
await cookieStoreShim.set('', 'noNameValue');
console.assert(false, 'Test 6: Attempt to set a cookie without a name - FAILED');
} catch (error) {
console.assert(true, 'Test 6: Attempt to set a cookie without a name - PASSED');
}
// Test 7: Attempt to delete a non-existent cookie
await cookieStoreShim.delete('nonExistentCookie');
const nonExistentCookie = await cookieStoreShim.get('nonExistentCookie');
console.assert(nonExistentCookie === null, 'Test 7: Attempt to delete a non-existent cookie - FAILED');
console.log('All tests completed.');
}
runTests();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment