Repository: KID-joker/proxy-web-storage
Branch: main
Commit: 62f6ee4db5f3
Files: 36
Total size: 91.6 KB
Directory structure:
gitextract_0wk62674/
├── .eslintignore
├── .gitignore
├── LICENSE
├── README.ja.md
├── README.md
├── README.zh.md
├── eslint.config.js
├── package.json
├── playground/
│ └── template.js
├── playwright.config.ts
├── rollup.config.js
├── src/
│ ├── extends/
│ │ ├── disposable.ts
│ │ ├── expires.ts
│ │ ├── options.ts
│ │ └── watch.ts
│ ├── index.ts
│ ├── proxy/
│ │ ├── broadcast.ts
│ │ ├── object.ts
│ │ ├── storage.ts
│ │ └── transform.ts
│ ├── shared.ts
│ ├── types.ts
│ └── utils.ts
├── tests/
│ ├── base.spec.ts
│ ├── disposable.spec.ts
│ ├── equal.spec.ts
│ ├── expired.spec.ts
│ ├── global.d.ts
│ ├── localforage.spec.ts
│ ├── serializer.spec.ts
│ ├── storage.spec.ts
│ ├── storageOptions.spec.ts
│ └── subscribe.spec.ts
├── tsconfig.json
├── v2.md
└── v2.zh.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .eslintignore
================================================
.DS_Store
node_modules
dist
playground/stokado.js
================================================
FILE: .gitignore
================================================
.DS_Store
node_modules
dist
playground/index.html
playground/stokado.js
test-results
.vscode
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2022 KID-joker
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.ja.md
================================================
```shell
__ __ __ __
____ /\ \__ ___ /\ \/ \ __ /\ \ ___
/ ,__\ \ \ ,_\ / __`\ \ \ < /'__`\ \_\ \ / __`\
/\__, `\ \ \ \/ /\ \_\ \ \ \ ^ \ /\ \_\.\_ /\ ,. \ /\ \_\ \
\/\____/ \ \ \_ \ \____/ \ \_\ \_\\ \__/.\_\\ \____\\ \____/
\/___/ \ \__\ \/___/ \/_/\/_/ \/__/\/_/ \/___ / \/___/
\/__/
```
**[English](./README.md) | [中文](./README.zh.md) | 日本語**
[v2 ドキュメント](./v2.md)
> *Stokado*(/stəˈkɑːdoʊ/) は *storage* の[エスペラント語](https://ja.wikipedia.org/wiki/%E3%82%A8%E3%82%B9%E3%83%9A%E3%83%A9%E3%83%B3%E3%83%88)(国際補助語)であり、*Stokado* は *storage* の補助エージェントでもあります。
`stokado` は、任意の `storage` ライクなオブジェクトをプロキシし、ゲッター/セッターのシンタックスシュガー、シリアライゼーション、サブスクリプションリスニング、期限設定、一度だけの値の取得を提供します。
## 使用方法
### インストール
```shell
npm install stokado
```
### プロキシ
```js
import { createProxyStorage } from 'stokado'
const storage = createProxyStorage(localStorage)
storage.getItem('test')
```
#### createProxyStorage(storage[, name])
`createProxyStorage` は2つのパラメータを取ります: `storage` ライクなオブジェクトとオプションの `name`。`name` は他のページと `storage` の変更を同期するために使用されます。デフォルトでは、`localStorage` は同じ `name` を持ちますが、`sessionStorage` は持ちません。他のオブジェクトの場合は手動で渡す必要があります。
### 機能
#### 1. シンタックスシュガー
オブジェクト指向のアプローチで直接 `storage` を操作します
もちろん、`localStorage` と `sessionStorage` はネイティブにサポートされています
```js
const storage = createProxyStorage(localStorage)
storage.test = 'hello stokado'
storage.test // 'hello stokado'
delete storage.test
```
`storage` には同じメソッドとプロパティもあります: `key()`, `getItem()`, `setItem()`, `removeItem()`, `clear()`, `length`。
#### 2. シリアライザー
ストレージ値の型を変更せずに保持します
```js
// number
storage.test = 0
storage.test === 0
// boolean
storage.test = false
storage.test === false
// undefined
storage.test = undefined
storage.test === undefined
// null
storage.test = null
storage.test === null
// object
storage.test = { hello: 'world' }
storage.test.hello === 'stokado'
// array
storage.test = ['hello']
storage.test.push('stokado')
storage.test.length // 2
// Date
storage.test = new Date('2000-01-01T00:00:00.000Z')
storage.test.getTime() === 946684800000
// RegExp
storage.test = /d(b+)d/g
storage.test.test('cdbbdbsbz')
// function
storage.test = function () {
return 'hello stokado!'
}
storage.test() === 'hello stokado!'
```
#### 3. サブスクライブ
値の変更をサブスクライブします
```js
storage.on(key, callback)
storage.once(key, callback)
storage.off([[key], callback])
```
- `key`: サブスクライブするアイテムの名前。`Object` の `obj.a` や `Array` の `list[0]`、および `Array` の長さをサポートします。
- `callback`: アイテムが変更されたときに呼び出される関数。`newValue` と `oldValue` を含みます。
**ヒント:** `off` の場合、`callback` が存在する場合は指定されたコールバックのトリガーを削除します。存在しない場合は、`key` にバインドされたすべてのコールバックを削除します。`key` が空の場合は、すべてのリスニングコールバックを削除します。
#### 4. 期限
アイテムの期限を設定します
```js
storage.setExpires(key, expires)
storage.getExpires(key)
storage.removeExpires(key)
```
- `key`: 期限を設定するアイテムの名前。
- `expires`: `string`、`number`、`Date` を受け入れます。
#### 5. 一度だけ
一度だけ値を取得します。これは `storage` を介して通信するために使用できます。
```js
storage.setDisposable(key)
```
- `key`:一度だけの値を設定するアイテムの名前。
#### 6. オプション
指定されたアイテムの `expires` と `disposable` の設定情報を取得します
```js
storage.getOptions(key)
```
`setItem` を使用して `expires` と `disposable` を設定します
```js
storage.setItem(key, value, { expires, disposable })
```
## localForage と一緒に使う
`localForage` は `localStorage` と同じ API を提供しているため、`stokado` と一緒に使用できます。
```js
import localForage from 'localforage'
import { createProxyStorage } from 'stokado'
const local = createProxyStorage(localForage, 'localForage')
```
ただし、`localForage` は非同期 API を使用しているため、`Promise` を使用して呼び出す必要があります。
```js
await (local.test = 'hello localForage')
// または
await local.setItem('test', 'hello localForage')
```
#### 複数のインスタンス
`createInstance` を使用して、異なるストアを指す `localForage` の複数のインスタンスを作成できます。
```js
const store = localforage.createInstance({
name: 'nameHere'
})
const proxyStore = createProxyStorage(store, 'store')
const otherStore = localforage.createInstance({
name: 'otherName'
})
const proxyOtherStore = createProxyStorage(otherStore, 'otherStore')
```
================================================
FILE: README.md
================================================
```shell
__ __ __ __
____ /\ \__ ___ /\ \/ \ __ /\ \ ___
/ ,__\ \ \ ,_\ / __`\ \ \ < /'__`\ \_\ \ / __`\
/\__, `\ \ \ \/ /\ \_\ \ \ \ ^ \ /\ \_\.\_ /\ ,. \ /\ \_\ \
\/\____/ \ \ \_ \ \____/ \ \_\ \_\\ \__/.\_\\ \____\\ \____/
\/___/ \ \__\ \/___/ \/_/\/_/ \/__/\/_/ \/___ / \/___/
\/__/
```
**English | [中文](./README.zh.md) | [日本語](./README.ja.md)**
[v2 document](./v2.md)
> *Stokado*(/stəˈkɑːdoʊ/) is the [Esperanto](https://en.wikipedia.org/wiki/Esperanto)(an international auxiliary language) for *storage*, meaning that *Stokado* is also an auxiliary agent for *storage*.
`stokado` can proxy objects of any `storage`-like, providing getter/setter syntax sugars, serialization, subscription listening, expiration setting, one-time value retrieval.
## Usage
### Install
```shell
npm install stokado
```
### Proxy
```js
import { createProxyStorage } from 'stokado'
const storage = createProxyStorage(localStorage)
storage.getItem('test')
```
#### createProxyStorage(storage[, name])
`createProxyStorage` takes two parameters: an object of `storage`-like and an optional `name`. The `name` is used to synchronize `storage` modifications with other pages. By default, `localStorage` has the same `name`, whereas `sessionStorage` does not; for other objects, it needs to be passed in manually.
### Features
#### 1. Syntax sugar
Operate `storage` directly through object-oriented approach
Of course, `localStorage` and `sessionStorage` are supported natively
```js
const storage = createProxyStorage(localStorage)
storage.test = 'hello stokado'
storage.test // 'hello stokado'
delete storage.test
```
The `storage` also have the same methods and properties: `key()`, `getItem()`, `setItem()`, `removeItem()`, `clear()` and `length`.
#### 2. Serializer
Keep the type of storage value unchanged
```js
// number
storage.test = 0
storage.test === 0
// boolean
storage.test = false
storage.test === false
// undefined
storage.test = undefined
storage.test === undefined
// null
storage.test = null
storage.test === null
// object
storage.test = { hello: 'world' }
storage.test.hello === 'stokado'
// array
storage.test = ['hello']
storage.test.push('stokado')
storage.test.length // 2
// Date
storage.test = new Date('2000-01-01T00:00:00.000Z')
storage.test.getTime() === 946684800000
// RegExp
storage.test = /d(b+)d/g
storage.test.test('cdbbdbsbz')
// function
storage.test = function () {
return 'hello stokado!'
}
storage.test() === 'hello stokado!'
```
#### 3. Subscribe
Subscribe to value changes
```js
storage.on(key, callback)
storage.once(key, callback)
storage.off([[key], callback])
```
- `key`: the name of the item to subscribe to. Support `obj.a` for `Object` and `list[0]` for `Array`, and also `Array` length.
- `callback`: the function to call when the item is changed. Includes `newValue` and `oldValue`.
**Tips:** For `off`, if a `callback` exists, it removes the trigger of the specified callback; otherwise, it removes all callbacks bound to the `key`; if the `key` is empty, it removes all listening callbacks.
#### 4. Expired
Set expires for items
```js
storage.setExpires(key, expires)
storage.getExpires(key)
storage.removeExpires(key)
```
- `key`: the name of the item to set expires.
- `expires`: accept `string`、`number` and `Date`.
#### 5. Disposable
Get the value once, which can be used for communication through `storage`.
```js
storage.setDisposable(key)
```
- `key`:the name of the item to set disposable.
#### 6. Options
Get `expires` and `disposable` configuration information for the specified item
```js
storage.getOptions(key)
```
Set `expires` and `disposable` using `setItem`
```js
storage.setItem(key, value, { expires, disposable })
```
## Work with localForage
`localForage` provides the same API as `localStorage`, it can be used in conjunction with `stokado`.
```js
import localForage from 'localforage'
import { createProxyStorage } from 'stokado'
const local = createProxyStorage(localForage, 'localForage')
```
However, `localForage` uses an async API, it needs to be called using `Promise`.
```js
await (local.test = 'hello localForage')
// or
await local.setItem('test', 'hello localForage')
```
#### Multiple instances
You can create multiple instances of `localForage` that point to different stores using `createInstance`.
```js
const store = localforage.createInstance({
name: 'nameHere'
})
const proxyStore = createProxyStorage(store, 'store')
const otherStore = localforage.createInstance({
name: 'otherName'
})
const proxyOtherStore = createProxyStorage(otherStore, 'otherStore')
```
================================================
FILE: README.zh.md
================================================
```shell
__ __ __ __
____ /\ \__ ___ /\ \/ \ __ /\ \ ___
/ ,__\ \ \ ,_\ / __`\ \ \ < /'__`\ \_\ \ / __`\
/\__, `\ \ \ \/ /\ \_\ \ \ \ ^ \ /\ \_\.\_ /\ ,. \ /\ \_\ \
\/\____/ \ \ \_ \ \____/ \ \_\ \_\\ \__/.\_\\ \____\\ \____/
\/___/ \ \__\ \/___/ \/_/\/_/ \/__/\/_/ \/___ / \/___/
\/__/
```
**[English](./README.md) | 中文 | [日本語](./README.ja.md)**
[v2 文档](./v2.zh.md)
> *stokado*(/stəˈkɑːdoʊ/) 是 *storage* 的[世界语](https://zh.wikipedia.org/wiki/%E4%B8%96%E7%95%8C%E8%AF%AD)(一种国际辅助语言),喻意为 *stokado* 也是 *storage* 的辅助代理。
`stokado` 可以代理任何类 `storage` 的对象,实现简洁的 `getter`,`setter` 等语法糖,序列化,监听订阅,设置过期,一次性取值等功能。
## Usage
### Install
```shell
npm install stokado
```
### Proxy
```js
import { createProxyStorage } from 'stokado'
const storage = createProxyStorage(localStorage)
storage.getItem('test')
```
#### createProxyStorage(storage[, name])
`createProxyStorage` 接收两个参数:类 `storage` 对象和可选的 `name`。`name` 用于同步其他页面的 `storage` 修改。`localStorage` 默认存在同名的 `name`,`sessionStorage` 则没有,其他对象需自行传入。
### Features
#### 1. Syntax sugar
通过对象方式直接操作 `storage`
当然,`localStorage` 和 `sessionStorage` 本身也是支持的
```js
const storage = createProxyStorage(localStorage)
storage.test = 'hello stokado'
storage.test // 'hello stokado'
delete storage.test
```
同时也支持 `storage` 的原生方法和属性:`key()`,`getItem()`,`setItem()`,`removeItem()`,`clear()` 和 `length`。
#### 2. Serializer
保持值类型不变
```js
// number
storage.test = 0
storage.test === 0
// boolean
storage.test = false
storage.test === false
// undefined
storage.test = undefined
storage.test === undefined
// null
storage.test = null
storage.test === null
// object
storage.test = { hello: 'world' }
storage.test.hello === 'stokado'
// array
storage.test = ['hello']
storage.test.push('stokado')
storage.test.length // 2
// Date
storage.test = new Date('2000-01-01T00:00:00.000Z')
storage.test.getTime() === 946684800000
// RegExp
storage.test = /d(b+)d/g
storage.test.test('cdbbdbsbz')
// function
storage.test = function () {
return 'hello stokado!'
}
storage.test() === 'hello stokado!'
```
#### 3. Subscribe
监听储值的变化
```js
storage.on(key, callback)
storage.once(key, callback)
storage.off([[key], callback])
```
- `key`:监听指定项的名字。支持对象的二级监听,例如:`obj.a` 对于 `Object` 和 `list[0]` 对于 `Array`,还支持数组长度的监听。
- `callback`:指定项的值发生变化时,触发的回调函数。参数包括`newValue` 和 `oldValue`。
**Tips:** 对于 `off`,如果 `callback` 存在,则移除指定回调的触发;否则,移除对于 `key` 绑定的所有回调;如果 `key` 为空,移除所有监听回调。
#### 4. Expired
为指定项设置过期时间
```js
storage.setExpires(key, expires)
storage.getExpires(key)
storage.removeExpires(key)
```
- `key`:设置过期的指定项名字。
- `expires`:过期时间。接受`string`、`number` 和 `Date`类型。
#### 5. Disposable
一次性取值,可用于借助 `storage` 进行通信
```js
storage.setDisposable(key)
```
- `key`:设置一次性的指定项名字。
#### 6. Options
获取指定项的过期、一次性等配置信息
```js
storage.getOptions(key)
```
通过 `setItem` 设置过期及一次性
```js
storage.setItem(key, value, { expires, disposable })
```
## Work with localForage
因为 `localForage` 提供了跟 `localStorage` 一样的 API,它是类 `storage` 对象,可以跟 `stokado` 配合使用。
```js
import localForage from 'localforage'
import { createProxyStorage } from 'stokado'
const local = createProxyStorage(localForage, 'localForage')
```
但是因为 `localForage` 采用异步的 API,所以需要使用 `Promise` 来调用它。
```js
await (local.test = 'hello localForage')
// or
await local.setItem('test', 'hello localForage')
```
#### Multiple instances
通过 `createInstance` 可以创建多个 `localForage` 实例,也是类 `storage` 对象。
```js
const store = localforage.createInstance({
name: 'nameHere'
})
const proxyStore = createProxyStorage(store, 'store')
const otherStore = localforage.createInstance({
name: 'otherName'
})
const proxyOtherStore = createProxyStorage(otherStore, 'otherStore')
```
================================================
FILE: eslint.config.js
================================================
import antfu from '@antfu/eslint-config'
export default antfu({
ignores: ['.DS_Store', '**/.DS_Store/**', 'node_modules', 'dist', 'playground/stokado.js'],
}, {
rules: {
'no-new-wrappers': 'off',
'prefer-regex-literals': 'off',
'ts/no-unsafe-function-type': 'off',
'unicorn/new-for-builtins': 'off',
},
})
================================================
FILE: package.json
================================================
{
"name": "stokado",
"type": "module",
"version": "3.0.1",
"description": "stokado can proxy objects of any `storage`-like, providing getter/setter syntax sugars, serialization, subscription listening, expiration setting, one-time value retrieval.",
"author": "KID-joker",
"license": "MIT",
"homepage": "https://github.com/KID-joker/stokado#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/KID-joker/stokado.git"
},
"bugs": {
"url": "https://github.com/KID-joker/stokado/issues"
},
"keywords": [
"localStorage",
"sessionStorage",
"storage",
"browser",
"proxy",
"serializer",
"subscribe",
"expires",
"once",
"disposable"
],
"exports": {
".": {
"types": "./dist/stokado.d.ts",
"import": "./dist/stokado.mjs",
"require": "./dist/stokado.cjs"
}
},
"main": "dist/stokado.cjs",
"module": "dist/stokado.mjs",
"unpkg": "dist/stokado.min.js",
"jsdelivr": "dist/stokado.min.js",
"types": "dist/stokado.d.ts",
"files": [
"dist"
],
"engines": {
"node": ">=16.20.0"
},
"scripts": {
"build": "rimraf dist && rollup -c --environment BUILD:prod",
"dev": "rollup -c -w --environment BUILD:dev",
"test": "rollup -c --environment BUILD:test && npx playwright test",
"lint": "eslint . --fix",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@antfu/eslint-config": "^5.2.2",
"@playwright/test": "^1.55.0",
"@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-html": "^2.0.0",
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-typescript": "^12.1.4",
"@types/node": "^24.3.1",
"esbuild": "^0.25.9",
"eslint": "^9.35.0",
"http-server": "^14.1.1",
"lint-staged": "^16.1.6",
"rimraf": "^6.0.1",
"rollup": "^4.50.0",
"rollup-plugin-dts": "^6.2.3",
"rollup-plugin-esbuild": "^6.2.1",
"rollup-plugin-serve": "^3.0.0",
"simple-git-hooks": "^2.13.1",
"tslib": "^2.8.1",
"typescript": "^5.9.2"
},
"simple-git-hooks": {
"pre-commit": "npx lint-staged"
},
"lint-staged": {
"*.{js,ts,md}": [
"eslint --fix"
]
}
}
================================================
FILE: playground/template.js
================================================
function template(options) {
return `
${options.title}
`
}
export default template
================================================
FILE: playwright.config.ts
================================================
import type { PlaywrightTestConfig } from '@playwright/test'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const root = path.resolve(__dirname, './playground')
const port = 8080
const config: PlaywrightTestConfig = {
testDir: 'tests',
webServer: {
command: `npx http-server ${root} -p ${port} --cors`,
port,
reuseExistingServer: !process.env.CI,
},
use: {
baseURL: `http://localhost:${port}`,
},
projects: [{
name: 'chromium',
use: {
browserName: 'chromium',
},
}],
}
export default config
================================================
FILE: rollup.config.js
================================================
import { readFileSync } from 'node:fs'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
import alias from '@rollup/plugin-alias'
import html from '@rollup/plugin-html'
import { nodeResolve } from '@rollup/plugin-node-resolve'
import typescript from '@rollup/plugin-typescript'
import dts from 'rollup-plugin-dts'
import esbuild from 'rollup-plugin-esbuild'
import serve from 'rollup-plugin-serve'
import template from './playground/template.js'
const pkg = JSON.parse(readFileSync('./package.json', { encoding: 'utf8' }))
const __dirname = fileURLToPath(new URL('.', import.meta.url))
const configs = []
const srcDir = path.resolve(__dirname, 'src')
const input = path.resolve(__dirname, 'src/index.ts')
const pkgName = pkg.name
const output = [{
file: `${process.env.BUILD === 'prod' ? 'dist' : 'playground'}/${pkgName}.js`,
format: 'iife',
name: pkgName,
extend: true,
}]
const pluginEsbuild = process.env.BUILD === 'prod' ? esbuild({ drop: ['console'] }) : esbuild()
const pluginAlias = alias({ entries: [{ find: '@', replacement: srcDir }] })
const plugins = [pluginEsbuild, typescript({ declaration: false }), pluginAlias, nodeResolve({ browser: true })]
if (process.env.BUILD === 'prod') {
output.push({
file: `dist/${pkgName}.mjs`,
format: 'es',
}, {
file: `dist/${pkgName}.cjs`,
format: 'cjs',
}, {
file: `dist/${pkgName}.min.js`,
format: 'iife',
name: pkgName,
extend: true,
plugins: [
esbuild({
minify: true,
}),
],
})
configs.push({
input,
output: {
file: `dist/${pkgName}.d.ts`,
format: 'es',
},
plugins: [
dts(),
pluginAlias,
],
})
}
if (process.env.BUILD !== 'prod') {
plugins.push(html({
title: pkgName,
template,
}))
}
if (process.env.BUILD === 'dev')
plugins.push(serve('playground'))
configs.push({
input,
output,
plugins,
})
export default configs
================================================
FILE: src/extends/disposable.ts
================================================
import type { StorageObject } from '@/types'
import { encode } from '@/proxy/transform'
import { deleteProxyStorageProperty, getProxyStorageProperty } from '@/shared'
import { isObject, pThen } from '@/utils'
export function setDisposable(
storage: Record,
property: string,
) {
pThen(() => getProxyStorageProperty(storage, property), (res: StorageObject | string | null) => {
if (isObject(res)) {
const options = Object.assign({}, res?.options, { disposable: true })
const encodeValue = encode({ data: res.value, storage, property, options })
storage.setItem(property, encodeValue)
}
})
}
export function checkDisposable({
data,
storage,
property,
}: {
data: StorageObject | string | null
storage: Record
property: string
}) {
if (!isObject(data) || !data.options)
return data
const { disposable } = data.options
if (disposable) {
deleteProxyStorageProperty(storage, property)
}
return data
}
================================================
FILE: src/extends/expires.ts
================================================
import type { ExpiresType, StorageObject, StorageOptions } from '@/types'
import { encode } from '@/proxy/transform'
import { deleteProxyStorageProperty, getProxyStorageProperty } from '@/shared'
import { formatTime, isObject, pThen } from '@/utils'
import { getOptions } from './options'
export function setExpires(
storage: Record,
property: string,
expires: ExpiresType,
) {
const time = formatTime(expires)
if (time <= Date.now()) {
deleteProxyStorageProperty(storage, property)
return undefined
}
pThen(() => getProxyStorageProperty(storage, property), (res: StorageObject | string | null) => {
if (isObject(res)) {
const options = Object.assign({}, res?.options, { expires: time })
const encodeValue = encode({ data: res.value, storage, property, options })
storage.setItem(property, encodeValue)
}
})
}
export function getExpires(
storage: Record,
property: string,
) {
return pThen(() => getOptions(storage, property), (res: StorageOptions) => {
if (!res?.expires || +res.expires <= Date.now())
return undefined
return new Date(+res.expires)
})
}
export function removeExpires(
storage: Record,
property: string,
) {
pThen(() => getProxyStorageProperty(storage, property), (res: StorageObject | string | null) => {
if (isObject(res) && res.options) {
delete res.options.expires
const encodeValue = encode({ data: res.value, storage, property, options: res.options })
storage.setItem(property, encodeValue)
}
})
}
export function checkExpired({
data,
storage,
property,
}: {
data: StorageObject | string | null
storage: Record
property: string
}) {
if (!isObject(data) || !data.options)
return data
const { expires } = data.options
if (expires && new Date(+expires).getTime() <= Date.now()) {
deleteProxyStorageProperty(storage, property)
data.value = undefined
}
return data
}
================================================
FILE: src/extends/options.ts
================================================
import type { StorageObject } from '@/types'
import { getProxyStorageProperty } from '@/shared'
import { isObject, pThen } from '@/utils'
export function getOptions(
storage: Record,
property: string,
) {
return pThen(() => getProxyStorageProperty(storage, property), (res: StorageObject | string | null) => {
if (isObject(res) && res.options)
return res.options
return {}
})
}
================================================
FILE: src/extends/watch.ts
================================================
import type { Effect, EffectFn, EffectMap } from '@/types'
import { postMessage } from '@/proxy/broadcast'
import { hasChanged } from '@/utils'
const storageEffectMap = new WeakMap