Skip to content

Commit

Permalink
Merge pull request #9 from smithki/development
Browse files Browse the repository at this point in the history
Release v0.11.0
  • Loading branch information
smithki authored May 14, 2019
2 parents d5945b2 + b43ee61 commit 52bdd7c
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 26 deletions.
50 changes: 40 additions & 10 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
# ---------------------------------------------------------------------------- #
# Environment variables required in CircleCI: #
# SETUP INSTRUCTIONS #
# ---------------------------------------------------------------------------- #
# #
# 1. Environment variables required in CircleCI: #
# #
# $NPM_TOKEN -- NPM publishing auth token. #
# $GITHUB_REPO_TOKEN -- GitHub repo-scoped auth token (for use with GREN). #
# #
# $NPM_TOKEN -- NPM publishing auth token. #
# $GITHUB_REPO_TOKEN -- GitHub repo-scoped auth token (for use with GREN). #
# ---------------------------------------------------------------------------- #
# The following branches should be created & protected on GitHub: #
# ^^^^^^^^^ #
# master -- Production code (currently published NPM version). #
# next -- Pre-release code (published under the `next` tag on NPM). #
# development -- Work-in-progress code (not published). #
# #
# 2. The following branches should be created & protected on GitHub: #
# ^^^^^^^^^ #
# master -- Production code (currently published NPM version). #
# next -- Pre-release code (published under the `next` tag on NPM). #
# development -- Work-in-progress code (not published). This should be set #
# as the default branch! #
# #
# ---------------------------------------------------------------------------- #
# #
# 3. The following scripts should be created in `package.json`: #
# #
# lint -- Run a linter against source files. #
# build -- Build output required for publishing to NPM. #
# test -- Run unit tests. #
# #
# ---------------------------------------------------------------------------- #


version: 2.1

# --- Alias anchor definitions ----------------------------------------------- #
Expand Down Expand Up @@ -223,17 +239,24 @@ workflows:
requires:
- lint

- create-release:
- confirm-release:
type: approval
requires:
- build
- test

- create-release:
requires:
- confirm-release

- tag-release:
requires:
- confirm-release
- create-release

- create-release-notes:
requires:
- confirm-release
- tag-release

# Builds modules, verifies code with the linter, runs unit tests, and
Expand All @@ -254,15 +277,22 @@ workflows:
requires:
- lint

- create-prerelease:
- confirm-prerelease:
type: approval
requires:
- build
- test

- create-prerelease:
requires:
- confirm-prerelease

- tag-release:
requires:
- confirm-prerelease
- create-prerelease

- create-prerelease-notes:
requires:
- confirm-prerelease
- tag-release
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ npm install storage-proxy
```ts
import { StorageProxy } from 'storage-proxy';

// Optionally use a namespace to prefix storage keys like `[{namespace}]:{key}`.
const myLocalStorage = StorageProxy.createLocalStorage('my-namespace');
const mySessionStorage = StorageProxy.createSessionStorage('my-namespace');

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "storage-proxy",
"version": "0.10.2-rc.1",
"version": "0.11.0",
"description": "Use web storage (localStorage/sessionStorage) just like plain objects using ES6 Proxies.",
"author": "Ian K Smith <[email protected]>",
"license": "MIT",
Expand Down
25 changes: 20 additions & 5 deletions src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ function getDataFromStorage(namespace: string, storageTarget: StorageTarget) {
function createProxy<TStorageDefinitions extends any>(
storageTarget: StorageTarget,
namespace: string,
defaults?: Partial<TStorageDefinitions>,
): StorageProxy<TStorageDefinitions> {
if (!namespace) throw new Error('[storage-proxy] Namespace cannot be an empty `string`, `undefined`, or `null`.');

Expand All @@ -77,12 +78,20 @@ function createProxy<TStorageDefinitions extends any>(
window[storageTarget].setItem(namespace, JSON.stringify(proxyData));
});

return new Proxy(proxyData, {
const storageProxy = new Proxy(proxyData, {
get: (target, prop, receiver) => {
if (typeof proxyData[prop as any] === 'undefined') return null;
return proxyData[prop as any];
},
});

if (defaults) {
for (const [key, value] of Object.entries(defaults)) {
storageProxy[key] = value;
}
}

return storageProxy;
}

// --- Proxy factory -------------------------------------------------------- //
Expand All @@ -99,8 +108,11 @@ export const StorageProxy = {
*
* @return a `StorageProxy` object targeting `localStorage`.
*/
createLocalStorage<TStorageDefinitions extends any>(namespace: string): StorageProxy<TStorageDefinitions> {
return createProxy<TStorageDefinitions>(StorageTarget.Local, namespace);
createLocalStorage<TStorageDefinitions extends any>(
namespace: string,
defaults?: Partial<TStorageDefinitions>,
): StorageProxy<TStorageDefinitions> {
return createProxy<TStorageDefinitions>(StorageTarget.Local, namespace, defaults);
},

/**
Expand All @@ -110,8 +122,11 @@ export const StorageProxy = {
*
* @return a `StorageProxy` object targeting `sessionStorage`.
*/
createSessionStorage<TStorageDefinitions extends any>(namespace: string): StorageProxy<TStorageDefinitions> {
return createProxy<TStorageDefinitions>(StorageTarget.Session, namespace);
createSessionStorage<TStorageDefinitions extends any>(
namespace: string,
defaults?: Partial<TStorageDefinitions>,
): StorageProxy<TStorageDefinitions> {
return createProxy<TStorageDefinitions>(StorageTarget.Session, namespace, defaults);
},

/**
Expand Down
33 changes: 24 additions & 9 deletions test/src/storage-proxy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ interface TestStorage {
bar: string;
baz: typeof testObj;
fizz: number;
defaults: {
example: string;
};
}

function getItem(storageTarget: StorageTarget, path: string) {
Expand Down Expand Up @@ -45,32 +48,44 @@ export class StorageProxyTestFixture {
localStorage.setItem(testNamespace, JSON.stringify({ bar: testStr, test: 999, baz: testObj }));
sessionStorage.setItem(testNamespace, JSON.stringify({ bar: testStr, test: 999, baz: testObj }));

this.lStore = StorageProxy.createLocalStorage<TestStorage>(testNamespace);
this.lStore = StorageProxy.createLocalStorage<TestStorage>(testNamespace, {
defaults: { example: testStr },
});
this.sStore = StorageProxy.createSessionStorage<TestStorage>(testNamespace);
}

@Test('Set a namespaced `localStorage` key')
public setNamespacedLocalStorageKeyTest() {
@Test('Sets default items in storage')
public defaultsMergeTest() {
Expect(getItem(StorageTarget.Local, 'defaults.example')).toEqual(testStr);
}

@Test('Defaults are available on the `StorageProxy` object')
public defaultsAreGettableTest() {
Expect(this.lStore.defaults!.example).toEqual(testStr);
}

@Test('Set a `localStorage` key')
public setLocalStorageKeyTest() {
this.lStore.fizz = 123;

Expect(getItem(StorageTarget.Local, 'fizz')).toEqual(123);
}

@Test('Set a namespaced `sessionStorage` key')
public setNamespacedSessionStorageKeyTest() {
@Test('Set a `sessionStorage` key')
public setSessionStorageKeyTest() {
this.sStore.fizz = 123;

Expect(getItem(StorageTarget.Session, 'fizz')).toEqual(123);
}

@Test('Get namespaced `localStorage` key')
public getNamespacedLocalStorageKeyTest() {
@Test('Get `localStorage` key')
public getLocalStorageKeyTest() {
const data = this.lStore.bar;
Expect(data).toEqual(testStr);
}

@Test('Get namespaced `sessionStorage` key')
public getNamespacedSessionStorageKeyTest() {
@Test('Get `sessionStorage` key')
public getSessionStorageKeyTest() {
const data = this.sStore.bar;
Expect(data).toEqual(testStr);
}
Expand Down

0 comments on commit 52bdd7c

Please sign in to comment.