first commit

This commit is contained in:
2026-01-30 14:25:12 +08:00
commit 8dd8d2668a
899 changed files with 90844 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
{
"name": "@sa/utils",
"version": "1.3.8",
"exports": {
".": "./src/index.ts"
},
"typesVersions": {
"*": {
"*": ["./src/*"]
}
},
"dependencies": {
"colord": "2.9.3",
"crypto-js": "4.2.0",
"klona": "2.0.6",
"localforage": "1.10.0",
"nanoid": "5.0.7"
},
"devDependencies": {
"@types/crypto-js": "4.2.2"
}
}

View File

@@ -0,0 +1,27 @@
import CryptoJS from 'crypto-js';
export class Crypto<T extends object> {
/** Secret */
secret: string;
constructor(secret: string) {
this.secret = secret;
}
encrypt(data: T): string {
const dataString = JSON.stringify(data);
const encrypted = CryptoJS.AES.encrypt(dataString, this.secret);
return encrypted.toString();
}
decrypt(encrypted: string) {
const decrypted = CryptoJS.AES.decrypt(encrypted, this.secret);
const dataString = decrypted.toString(CryptoJS.enc.Utf8);
try {
return JSON.parse(dataString) as T;
} catch {
// avoid parse error
return null;
}
}
}

View File

@@ -0,0 +1,4 @@
export * from './crypto';
export * from './storage';
export * from './nanoid';
export * from './klona';

View File

@@ -0,0 +1,3 @@
import { klona as jsonClone } from 'klona/json';
export { jsonClone };

View File

@@ -0,0 +1,3 @@
import { nanoid } from 'nanoid';
export { nanoid };

View File

@@ -0,0 +1,76 @@
import localforage from 'localforage';
/** The storage type */
export type StorageType = 'local' | 'session';
export function createStorage<T extends object>(type: StorageType, storagePrefix: string) {
const stg = type === 'session' ? window.sessionStorage : window.localStorage;
const storage = {
/**
* Set session
*
* @param key Session key
* @param value Session value
*/
set<K extends keyof T>(key: K, value: T[K]) {
const json = JSON.stringify(value);
stg.setItem(`${storagePrefix}${key as string}`, json);
},
/**
* Get session
*
* @param key Session key
*/
get<K extends keyof T>(key: K): T[K] | null {
const json = stg.getItem(`${storagePrefix}${key as string}`);
if (json) {
let storageData: T[K] | null = null;
try {
storageData = JSON.parse(json);
} catch {}
if (storageData) {
return storageData as T[K];
}
}
stg.removeItem(`${storagePrefix}${key as string}`);
return null;
},
remove(key: keyof T) {
stg.removeItem(`${storagePrefix}${key as string}`);
},
clear() {
stg.clear();
}
};
return storage;
}
type LocalForage<T extends object> = Omit<typeof localforage, 'getItem' | 'setItem' | 'removeItem'> & {
getItem<K extends keyof T>(key: K, callback?: (err: any, value: T[K] | null) => void): Promise<T[K] | null>;
setItem<K extends keyof T>(key: K, value: T[K], callback?: (err: any, value: T[K]) => void): Promise<T[K]>;
removeItem(key: keyof T, callback?: (err: any) => void): Promise<void>;
};
type LocalforageDriver = 'local' | 'indexedDB' | 'webSQL';
export function createLocalforage<T extends object>(driver: LocalforageDriver) {
const driverMap: Record<LocalforageDriver, string> = {
local: localforage.LOCALSTORAGE,
indexedDB: localforage.INDEXEDDB,
webSQL: localforage.WEBSQL
};
localforage.config({
driver: driverMap[driver]
});
return localforage as LocalForage<T>;
}

View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ESNext",
"jsx": "preserve",
"lib": ["DOM", "ESNext"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "node",
"resolveJsonModule": true,
"types": ["node"],
"strict": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}