Skip to content

Commit

Permalink
add set
Browse files Browse the repository at this point in the history
  • Loading branch information
farwayer committed Nov 10, 2024
1 parent fbfae52 commit a8949cd
Show file tree
Hide file tree
Showing 10 changed files with 1,079 additions and 31 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ _Lightweight proxy-based state manager, simple, fast and built with ❤️_
<img src="docs/logo.webp" height="192" align="right">

**Fast**: hand-crafted and optimized to be as fast as possible
🤏 **Tiny**: *514 bytes* in minimal config, *712 bytes* with React support
🤏 **Tiny**: *515 bytes* in minimal config, *707 bytes* with React support
🥧 **Simple**: *~140 lines* of sparse code + *~70 lines* for React support
🍳 **Easy to use**: just modify data, magic will take care of the rest!
⚛️ **React & React Native**: *hooks* and *selectors*, uses modern React 18 API
Expand Down
22 changes: 11 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,42 +52,42 @@
"import": {
"src/index.js": "{create, onWrite, obj}"
},
"limit": "514b"
"limit": "515b"
},
{
"name": "Store obj full",
"import": {
"src/index.js": "{create, obj, onWrite, onRead, lock, unlock}"
},
"limit": "534b"
"limit": "533b"
},
{
"name": "Store map full",
"import": {
"src/index.js": "{create, map, onWrite, onRead, lock, unlock}"
},
"limit": "930b"
"limit": "904b"
},
{
"name": "Store objIgnoreSpecials full",
"import": {
"src/index.js": "{create, objIgnoreSpecials, onWrite, onRead, lock, unlock}"
},
"limit": "626b"
"limit": "624b"
},
{
"name": "Store objMap full",
"import": {
"src/index.js": "{create, objMap, onWrite, onRead, lock, unlock}"
},
"limit": "1127b"
"limit": "1104b"
},
{
"name": "Store objMapIgnoreSpecialsRef zero",
"import": {
"src/index.js": "{create, objMapIgnoreSpecialsRef}"
},
"limit": "1124b"
"limit": "1104b"
},
{
"name": "React obj",
Expand All @@ -98,7 +98,7 @@
"ignore": [
"react"
],
"limit": "712b"
"limit": "707b"
},
{
"name": "React objIgnoreSpecials",
Expand All @@ -109,18 +109,18 @@
"ignore": [
"react"
],
"limit": "807b"
"limit": "805b"
},
{
"name": "React objMapIgnoreSpecialsRef",
"name": "React objMapSetIgnoreSpecialsRef",
"import": {
"src/index.js": "{create, objMapIgnoreSpecialsRef}",
"src/index.js": "{create, objMapSetIgnoreSpecialsRef}",
"src/react/index.js": "{StoreProvider, useStore}"
},
"ignore": [
"react"
],
"limit": "1413b"
"limit": "1598b"
},
{
"name": "Utils",
Expand Down
1 change: 1 addition & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ export let map: Proxify<any>
export let objMap: Proxify<any>
export let objMapIgnoreSpecials: Proxify<any>
export let objMapIgnoreSpecialsRef: Proxify<any>
export let objMapSetIgnoreSpecialsRef: Proxify<any>
38 changes: 38 additions & 0 deletions src/proxify/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {proxifyObj} from './obj.js'
import {proxifyMap} from './map.js'
import {proxifySet} from './set.js'
import {RefSym} from './ref.js'


Expand Down Expand Up @@ -103,4 +104,41 @@ export let objMapIgnoreSpecialsRef = ($, val) => {
: proxifyObj($, val)
}

export let objMapSetIgnoreSpecialsRef = ($, val) => {
if (
typeof val !== 'object' ||
!val ||
val instanceof Date ||
val instanceof Error ||
val instanceof RegExp ||
val instanceof WeakMap ||
val instanceof WeakSet ||
val instanceof ArrayBuffer ||
val instanceof Number ||
val instanceof String ||
val instanceof Promise ||
val instanceof File ||
isTypedArray(val) ||
(typeof WeakRef !== 'undefined' && val instanceof WeakRef) ||
(typeof Node !== 'undefined' && val instanceof Node)
) {
return val
}

let refs = $[RefSym]
if (refs && refs.has(val)) {
return val
}

if (val instanceof Map) {
return proxifyMap($, val)
}

if (val instanceof Set) {
return proxifySet($, val)
}

return proxifyObj($, val)
}

let isTypedArray = ArrayBuffer.isView
235 changes: 235 additions & 0 deletions src/proxify/set.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
import {$Sym, NakedSym} from '../store.js'


let ReflectGet = Reflect.get
let ReflectDefineProperty = Reflect.defineProperty
let SymbolIterator = Symbol.iterator
let SymbolFor = Symbol.for
let ArrayFrom = Array.from


export let SizeSym = SymbolFor('size')

export let proxifySet = ($, set) => {
let [proxify, cache, writeSubs, readSubs] = $

let proxy = cache.get(set)
if (proxy) return proxy

proxy = new Proxy(set, {
get(set, prop, receiver) {
let val, objProp, iterateEntry

switch (prop) {
case $Sym: {
return $
}

case NakedSym: {
return set
}

case 'size': {
val = set.size
}
break

case 'has': val = function (val) {
let target = this === receiver ? set : this
let has = target.has(val)

for (let cb of readSubs) {
cb(set, val)
}

return has
}
break

case 'entries':
iterateEntry = true
case 'keys':
case 'values':
case SymbolIterator: val = function () {
let target = this === receiver ? set : this
let valuesIt = target.values()

for (let cb of readSubs) {
cb(set, SizeSym)
}

return {
[SymbolIterator]() {
return this
},
next() {
let next = valuesIt.next()

if (!next.done) {
let nextVal = proxify($, next.value)
next.value = iterateEntry ? [nextVal, nextVal] : nextVal
}

return next
},
}
}
break

case 'forEach': val = function (cb, thisArg) {
let target = this === receiver ? set : this

for (let cb of readSubs) {
cb(set, SizeSym)
}

target.forEach(val => {
val = proxify($, val)
cb(val, val, this)
}, thisArg)
}
break

case 'delete': val = function (val) {
$[4] && "store locked!"()

let target = this === receiver ? set : this
let has = target.has(val)
if (!has) return false

target.delete(val)

for (let cb of writeSubs) {
cb(set, SizeSym)
cb(set, val)
}

return true
}
break

case 'clear': val = function () {
$[4] && "store locked!"()

let target = this === receiver ? set : this
if (!target.size) return

// because values() returns iterator
// it will be empty after clear
// so we need to save all values first
// may be slow and takes memory (depending on set size and values)
// but anyway clear() should not be often operation
let vals = ArrayFrom(target.values())

target.clear()

for (let cb of writeSubs) {
cb(set, SizeSym)

for (let val of vals) {
cb(set, val)
}
}
}
break

case 'add': val = function (val) {
$[4] && "store locked!"()

let target = this === receiver ? set : this
let has = target.has(val)
if (has) return this

target.add(val)

for (let cb of writeSubs) {
cb(set, SizeSym)
cb(set, val)
}

return this
}
break

// Set is js object so it's possible to get some props
default: {
val = ReflectGet(set, prop, receiver)
objProp = true
}
}

// to differ set values and set object props (set.add('x') vs set.x)
if (typeof prop !== 'symbol') {
prop = SymbolFor(prop)
}

for (let cb of readSubs) {
cb(set, prop)
}

return objProp
? proxify($, val)
: val
},

// Set is js object so it's possible to define some props
defineProperty(set, prop, desc) {
$[4] && "store locked!"()

// in theory prop getter (prev or next) can modify object
// so we need to use Reflect with the proxy as receiver
// to catch this changes

let has = prop in set
let prev = has && ReflectGet(set, prop, proxy)

// unwrap value if it was proxied with current $
let value = desc.value
if (value != null && value[$Sym] === $) {
desc.value = value[NakedSym]
}

ReflectDefineProperty(set, prop, desc)

let next = has && ReflectGet(set, prop, proxy)

if (!has || next !== prev) {
// to differ set keys and set object props (set.add('x') vs set.x)
if (typeof prop !== 'symbol') {
prop = SymbolFor(prop)
}

for (let cb of writeSubs) {
cb(set, prop)
}
}

return true
},

// Set is js object so it's possible to delete some props
deleteProperty(set, prop) {
$[4] && "store locked!"()

let has = prop in set
if (!has) return true

delete set[prop]

// to differ set keys and set object props (set.add('x') vs set.x)
if (typeof prop !== 'symbol') {
prop = SymbolFor(prop)
}

for (let cb of writeSubs) {
cb(set, prop)
}

return true
},
})

cache.set(set, proxy)

return proxy
}
Loading

0 comments on commit a8949cd

Please sign in to comment.