safe-session-storage 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +47 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# safe-session-storage
|
|
2
|
+
|
|
3
|
+
A secure, lightweight wrapper for `localStorage`, `sessionStorage`, and `AsyncStorage` (React Native) with automatic encryption. Prevent casual inspection of your application's stored data across all platforms.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔐 **Automatic Encryption**: Data is encrypted before being stored.
|
|
8
|
+
- ⚛️ **React & React Native Support**: Hooks for both synchronous and asynchronous storage.
|
|
9
|
+
- 🌐 **SSR Friendly**: Works perfectly with Next.js and other SSR frameworks.
|
|
10
|
+
- 📦 **Zero External Dependencies**: Lightweight and fast.
|
|
11
|
+
- 🛡️ **Type Safe**: Full TypeScript support.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install safe-session-storage
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Platform Support
|
|
20
|
+
|
|
21
|
+
### 1. Web (React, Vite, etc.)
|
|
22
|
+
Use `useSafeStorage` for synchronous `localStorage` or `sessionStorage`.
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
import { useSafeStorage } from 'safe-session-storage';
|
|
26
|
+
|
|
27
|
+
function App() {
|
|
28
|
+
const [theme, setTheme] = useSafeStorage('theme', 'light');
|
|
29
|
+
// ...
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Next.js (SSR)
|
|
34
|
+
`useSafeStorage` is SSR-safe. It detects the environment and returns the initial value during server-side rendering, then hydrates correctly on the client.
|
|
35
|
+
|
|
36
|
+
### 3. React Native
|
|
37
|
+
Use `useAsyncSafeStorage` with `@react-native-async-storage/async-storage`.
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
41
|
+
import { useAsyncSafeStorage } from 'safe-session-storage';
|
|
42
|
+
|
|
43
|
+
function Profile() {
|
|
44
|
+
const [user, setUser, loading] = useAsyncSafeStorage('user', null, {
|
|
45
|
+
customStorage: AsyncStorage,
|
|
46
|
+
secretKey: 'my-mobile-secret'
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (loading) return <Text>Loading...</Text>;
|
|
50
|
+
// ...
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
### Vanilla JavaScript / TypeScript
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { safeLocal, SafeStorage, AsyncSafeStorage } from 'safe-session-storage';
|
|
60
|
+
|
|
61
|
+
// Web (Sync)
|
|
62
|
+
safeLocal.setItem('key', 'value');
|
|
63
|
+
|
|
64
|
+
// React Native / Custom (Async)
|
|
65
|
+
const asyncStore = new AsyncSafeStorage({
|
|
66
|
+
customStorage: AsyncStorage,
|
|
67
|
+
secretKey: 'key'
|
|
68
|
+
});
|
|
69
|
+
await asyncStore.setItem('key', 'value');
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Why use this?
|
|
73
|
+
|
|
74
|
+
When you store data in storage engines, it is usually stored in plain text. Anyone with access to the device or a malicious script can easily read this data.
|
|
75
|
+
|
|
76
|
+
`safe-session-storage` obfuscates this data using XOR encryption and Base64 encoding. It works across:
|
|
77
|
+
- **Browsers**: Chrome, Firefox, Safari, Edge.
|
|
78
|
+
- **Server**: Node.js (SSR safe).
|
|
79
|
+
- **Mobile**: React Native (iOS/Android).
|
|
80
|
+
|
|
81
|
+
## API
|
|
82
|
+
|
|
83
|
+
### Web (Sync)
|
|
84
|
+
- `useSafeStorage(key, initialValue, options)`
|
|
85
|
+
- `safeLocal` / `safeSession` instances.
|
|
86
|
+
- `SafeStorage` class.
|
|
87
|
+
|
|
88
|
+
### Mobile/Custom (Async)
|
|
89
|
+
- `useAsyncSafeStorage(key, initialValue, options)`
|
|
90
|
+
- `AsyncSafeStorage` class.
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var I=Object.create;var y=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,O=Object.prototype.hasOwnProperty;var x=(r,t)=>{for(var e in t)y(r,e,{get:t[e],enumerable:!0})},h=(r,t,e,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of V(t))!O.call(r,o)&&o!==e&&y(r,o,{get:()=>t[o],enumerable:!(s=v(t,o))||s.enumerable});return r};var E=(r,t,e)=>(e=r!=null?I(K(r)):{},h(t||!r||!r.__esModule?y(e,"default",{value:r,enumerable:!0}):e,r)),J=r=>h(y({},"__esModule",{value:!0}),r);var b={};x(b,{AsyncSafeStorage:()=>f,SafeStorage:()=>u,safeLocal:()=>T,safeSession:()=>w,simpleDecrypt:()=>S,simpleEncrypt:()=>m,useAsyncSafeStorage:()=>P,useSafeStorage:()=>N});module.exports=J(b);var i=require("react"),p=E(require("crypto-js"),1),m=(r,t)=>{try{return p.default.AES.encrypt(r,t).toString()}catch(e){return console.error("Encryption failed:",e),r}},S=(r,t)=>{try{return p.default.AES.decrypt(r,t).toString(p.default.enc.Utf8)}catch(e){return console.error("Decryption failed:",e),""}},u=class{storage=null;secretKey;constructor(t={}){let{secretKey:e="safe-storage-default-key",storageType:s="local"}=t;this.secretKey=e,typeof window<"u"&&(this.storage=s==="local"?window.localStorage:window.sessionStorage)}setItem(t,e){if(!this.storage)return;let s=JSON.stringify(e),o=m(s,this.secretKey);this.storage.setItem(t,o)}getItem(t){if(!this.storage)return null;let e=this.storage.getItem(t);if(!e)return null;let s=S(e,this.secretKey);try{return JSON.parse(s)}catch{return null}}removeItem(t){this.storage&&this.storage.removeItem(t)}clear(){this.storage&&this.storage.clear()}},f=class{storage;secretKey;constructor(t={}){let{secretKey:e="safe-storage-default-key",customStorage:s}=t;this.secretKey=e,this.storage=s}async setItem(t,e){if(!this.storage)return;let s=JSON.stringify(e),o=m(s,this.secretKey);await this.storage.setItem(t,o)}async getItem(t){if(!this.storage)return null;let e=await this.storage.getItem(t);if(!e)return null;let s=S(e,this.secretKey);try{return JSON.parse(s)}catch{return null}}async removeItem(t){this.storage&&await this.storage.removeItem(t)}async clear(){this.storage&&await this.storage.clear()}},T=new u({storageType:"local"}),w=new u({storageType:"session"});function N(r,t,e={}){let[s,o]=(0,i.useState)(!1),c=e.storageType==="session"?w:T,[g,l]=(0,i.useState)(()=>{if(typeof window>"u")return t;try{let a=c.getItem(r);return a!==null?a:t}catch{return t}});(0,i.useEffect)(()=>{o(!0)},[]);let d=(0,i.useCallback)(a=>{try{let n=a instanceof Function?a(g):a;l(n),c.setItem(r,n)}catch(n){console.error(n)}},[r,c,g]);return[s?g:t,d]}function P(r,t,e){let[s,o]=(0,i.useState)(t),[c,g]=(0,i.useState)(!0),l=new f(e);(0,i.useEffect)(()=>{(async()=>{try{let n=await l.getItem(r);n!==null&&o(n)}catch(n){console.error(n)}finally{g(!1)}})()},[r]);let d=(0,i.useCallback)(async a=>{try{let n=a instanceof Function?a(s):a;o(n),await l.setItem(r,n)}catch(n){console.error(n)}},[r,l,s]);return[s,d,c]}0&&(module.exports={AsyncSafeStorage,SafeStorage,safeLocal,safeSession,simpleDecrypt,simpleEncrypt,useAsyncSafeStorage,useSafeStorage});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
type StorageType = 'local' | 'session' | 'async';
|
|
2
|
+
interface SafeStorageOptions {
|
|
3
|
+
secretKey?: string;
|
|
4
|
+
storageType?: StorageType;
|
|
5
|
+
customStorage?: any;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Military-grade AES encryption for storage values.
|
|
9
|
+
*/
|
|
10
|
+
declare const simpleEncrypt: (text: string, key: string) => string;
|
|
11
|
+
declare const simpleDecrypt: (encoded: string, key: string) => string;
|
|
12
|
+
/**
|
|
13
|
+
* Synchronous Storage Wrapper (Web)
|
|
14
|
+
*/
|
|
15
|
+
declare class SafeStorage {
|
|
16
|
+
private storage;
|
|
17
|
+
private secretKey;
|
|
18
|
+
constructor(options?: SafeStorageOptions);
|
|
19
|
+
setItem(key: string, value: any): void;
|
|
20
|
+
getItem<T = any>(key: string): T | null;
|
|
21
|
+
removeItem(key: string): void;
|
|
22
|
+
clear(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Asynchronous Storage Wrapper (React Native / Custom)
|
|
26
|
+
*/
|
|
27
|
+
declare class AsyncSafeStorage {
|
|
28
|
+
private storage;
|
|
29
|
+
private secretKey;
|
|
30
|
+
constructor(options?: SafeStorageOptions);
|
|
31
|
+
setItem(key: string, value: any): Promise<void>;
|
|
32
|
+
getItem<T = any>(key: string): Promise<T | null>;
|
|
33
|
+
removeItem(key: string): Promise<void>;
|
|
34
|
+
clear(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
declare const safeLocal: SafeStorage;
|
|
37
|
+
declare const safeSession: SafeStorage;
|
|
38
|
+
/**
|
|
39
|
+
* React Hook for Synchronous Storage (Web/Next.js)
|
|
40
|
+
*/
|
|
41
|
+
declare function useSafeStorage<T>(key: string, initialValue: T, options?: SafeStorageOptions): readonly [T, (value: T | ((val: T) => T)) => void];
|
|
42
|
+
/**
|
|
43
|
+
* React Hook for Asynchronous Storage (React Native)
|
|
44
|
+
*/
|
|
45
|
+
declare function useAsyncSafeStorage<T>(key: string, initialValue: T, options: SafeStorageOptions): readonly [T, (value: T | ((val: T) => T)) => Promise<void>, boolean];
|
|
46
|
+
|
|
47
|
+
export { AsyncSafeStorage, SafeStorage, safeLocal, safeSession, simpleDecrypt, simpleEncrypt, useAsyncSafeStorage, useSafeStorage };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
type StorageType = 'local' | 'session' | 'async';
|
|
2
|
+
interface SafeStorageOptions {
|
|
3
|
+
secretKey?: string;
|
|
4
|
+
storageType?: StorageType;
|
|
5
|
+
customStorage?: any;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Military-grade AES encryption for storage values.
|
|
9
|
+
*/
|
|
10
|
+
declare const simpleEncrypt: (text: string, key: string) => string;
|
|
11
|
+
declare const simpleDecrypt: (encoded: string, key: string) => string;
|
|
12
|
+
/**
|
|
13
|
+
* Synchronous Storage Wrapper (Web)
|
|
14
|
+
*/
|
|
15
|
+
declare class SafeStorage {
|
|
16
|
+
private storage;
|
|
17
|
+
private secretKey;
|
|
18
|
+
constructor(options?: SafeStorageOptions);
|
|
19
|
+
setItem(key: string, value: any): void;
|
|
20
|
+
getItem<T = any>(key: string): T | null;
|
|
21
|
+
removeItem(key: string): void;
|
|
22
|
+
clear(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Asynchronous Storage Wrapper (React Native / Custom)
|
|
26
|
+
*/
|
|
27
|
+
declare class AsyncSafeStorage {
|
|
28
|
+
private storage;
|
|
29
|
+
private secretKey;
|
|
30
|
+
constructor(options?: SafeStorageOptions);
|
|
31
|
+
setItem(key: string, value: any): Promise<void>;
|
|
32
|
+
getItem<T = any>(key: string): Promise<T | null>;
|
|
33
|
+
removeItem(key: string): Promise<void>;
|
|
34
|
+
clear(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
declare const safeLocal: SafeStorage;
|
|
37
|
+
declare const safeSession: SafeStorage;
|
|
38
|
+
/**
|
|
39
|
+
* React Hook for Synchronous Storage (Web/Next.js)
|
|
40
|
+
*/
|
|
41
|
+
declare function useSafeStorage<T>(key: string, initialValue: T, options?: SafeStorageOptions): readonly [T, (value: T | ((val: T) => T)) => void];
|
|
42
|
+
/**
|
|
43
|
+
* React Hook for Asynchronous Storage (React Native)
|
|
44
|
+
*/
|
|
45
|
+
declare function useAsyncSafeStorage<T>(key: string, initialValue: T, options: SafeStorageOptions): readonly [T, (value: T | ((val: T) => T)) => Promise<void>, boolean];
|
|
46
|
+
|
|
47
|
+
export { AsyncSafeStorage, SafeStorage, safeLocal, safeSession, simpleDecrypt, simpleEncrypt, useAsyncSafeStorage, useSafeStorage };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{useState as l,useEffect as d,useCallback as m}from"react";import p from"crypto-js";var S=(s,t)=>{try{return p.AES.encrypt(s,t).toString()}catch(e){return console.error("Encryption failed:",e),s}},h=(s,t)=>{try{return p.AES.decrypt(s,t).toString(p.enc.Utf8)}catch(e){return console.error("Decryption failed:",e),""}},u=class{storage=null;secretKey;constructor(t={}){let{secretKey:e="safe-storage-default-key",storageType:r="local"}=t;this.secretKey=e,typeof window<"u"&&(this.storage=r==="local"?window.localStorage:window.sessionStorage)}setItem(t,e){if(!this.storage)return;let r=JSON.stringify(e),a=S(r,this.secretKey);this.storage.setItem(t,a)}getItem(t){if(!this.storage)return null;let e=this.storage.getItem(t);if(!e)return null;let r=h(e,this.secretKey);try{return JSON.parse(r)}catch{return null}}removeItem(t){this.storage&&this.storage.removeItem(t)}clear(){this.storage&&this.storage.clear()}},f=class{storage;secretKey;constructor(t={}){let{secretKey:e="safe-storage-default-key",customStorage:r}=t;this.secretKey=e,this.storage=r}async setItem(t,e){if(!this.storage)return;let r=JSON.stringify(e),a=S(r,this.secretKey);await this.storage.setItem(t,a)}async getItem(t){if(!this.storage)return null;let e=await this.storage.getItem(t);if(!e)return null;let r=h(e,this.secretKey);try{return JSON.parse(r)}catch{return null}}async removeItem(t){this.storage&&await this.storage.removeItem(t)}async clear(){this.storage&&await this.storage.clear()}},T=new u({storageType:"local"}),w=new u({storageType:"session"});function V(s,t,e={}){let[r,a]=l(!1),i=e.storageType==="session"?w:T,[c,g]=l(()=>{if(typeof window>"u")return t;try{let n=i.getItem(s);return n!==null?n:t}catch{return t}});d(()=>{a(!0)},[]);let y=m(n=>{try{let o=n instanceof Function?n(c):n;g(o),i.setItem(s,o)}catch(o){console.error(o)}},[s,i,c]);return[r?c:t,y]}function K(s,t,e){let[r,a]=l(t),[i,c]=l(!0),g=new f(e);d(()=>{(async()=>{try{let o=await g.getItem(s);o!==null&&a(o)}catch(o){console.error(o)}finally{c(!1)}})()},[s]);let y=m(async n=>{try{let o=n instanceof Function?n(r):n;a(o),await g.setItem(s,o)}catch(o){console.error(o)}},[s,g,r]);return[r,y,i]}export{f as AsyncSafeStorage,u as SafeStorage,T as safeLocal,w as safeSession,h as simpleDecrypt,S as simpleEncrypt,K as useAsyncSafeStorage,V as useSafeStorage};
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "safe-session-storage",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A secure wrapper for localStorage and sessionStorage with automatic encryption.",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --minify --clean",
|
|
22
|
+
"dev": "tsup src/index.ts --format cjs,esm --watch --dts",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"lint": "tsc"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"storage",
|
|
28
|
+
"security",
|
|
29
|
+
"encryption",
|
|
30
|
+
"localstorage",
|
|
31
|
+
"sessionstorage",
|
|
32
|
+
"react",
|
|
33
|
+
"hook"
|
|
34
|
+
],
|
|
35
|
+
"author": "",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": ">=16.8.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependenciesMeta": {
|
|
41
|
+
"react": {
|
|
42
|
+
"optional": true
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@testing-library/react": "^16.3.2",
|
|
47
|
+
"@types/crypto-js": "^4.2.2",
|
|
48
|
+
"@types/node": "^20.0.0",
|
|
49
|
+
"@types/react": "^18.0.0",
|
|
50
|
+
"jsdom": "^29.0.2",
|
|
51
|
+
"react": "^18.0.0",
|
|
52
|
+
"tsup": "^8.0.0",
|
|
53
|
+
"typescript": "^5.0.0",
|
|
54
|
+
"vitest": "^4.1.5"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"crypto-js": "^4.2.0"
|
|
58
|
+
}
|
|
59
|
+
}
|