mirror of
https://github.com/littensy/charm.git
synced 2026-10-22 12:24:08 +00:00
chore: prepare release candidate
This commit is contained in:
parent
29d8add265
commit
ed9566bfff
14 changed files with 67 additions and 59 deletions
64
README.md
64
README.md
|
|
@ -52,8 +52,9 @@
|
||||||
- [`mapped(getter, mapper)`](#mappedgetter-mapper)
|
- [`mapped(getter, mapper)`](#mappedgetter-mapper)
|
||||||
- [`onCleanup(callback, failSilently?)`](#oncleanupcallback-failsilently)
|
- [`onCleanup(callback, failSilently?)`](#oncleanupcallback-failsilently)
|
||||||
- [`atom(initialValue, equals?)`](#atominitialvalue-equals)
|
- [`atom(initialValue, equals?)`](#atominitialvalue-equals)
|
||||||
- [`trigger(callback)`](#triggercallback)
|
- [`recursive()`](#recursive)
|
||||||
- [`flags`](#flags)
|
- [`trigger(callback)`](#triggercallback)
|
||||||
|
- [`flags`](#flags)
|
||||||
- [Client-Server Sync](#client-server-sync)
|
- [Client-Server Sync](#client-server-sync)
|
||||||
- [Installation](#installation-1)
|
- [Installation](#installation-1)
|
||||||
- [Quick Start](#quick-start)
|
- [Quick Start](#quick-start)
|
||||||
|
|
@ -229,9 +230,10 @@ Effects are fundamental to reactivity, allowing you to react to signal updates.
|
||||||
```luau
|
```luau
|
||||||
local getCounter, setCounter = signal(0)
|
local getCounter, setCounter = signal(0)
|
||||||
|
|
||||||
|
-- Count is 0
|
||||||
effect(function()
|
effect(function()
|
||||||
print(`Count is {getCounter()}`)
|
print(`Count is {getCounter()}`)
|
||||||
end) -- Count is 0
|
end)
|
||||||
|
|
||||||
setCounter(1) -- Count is 1
|
setCounter(1) -- Count is 1
|
||||||
```
|
```
|
||||||
|
|
@ -269,7 +271,7 @@ setCounter(2) -- Outer: 2, Inner: 2
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> To run code that is "detached" from the parent effect or scope, use `untracked()` or a detached effect scope. If you suspect that the new nested effect behavior is causing issues with migration, try disabling the `globals.trackInnerEffects` flag to assist with debugging.
|
> To run code that is "detached" from the parent effect or scope, use `untracked()` or a detached effect scope. If you suspect that the new nested effect behavior is causing issues with migration, try disabling the `flags.trackInnerEffects` flag to assist with debugging.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -278,18 +280,17 @@ setCounter(2) -- Outer: 2, Inner: 2
|
||||||
In case you want to read signals but don't want to subscribe to them, you can use `untracked()` to essentially run code _outside_ the current effect, preventing signals and effects in the callback from being tracked.
|
In case you want to read signals but don't want to subscribe to them, you can use `untracked()` to essentially run code _outside_ the current effect, preventing signals and effects in the callback from being tracked.
|
||||||
|
|
||||||
```luau
|
```luau
|
||||||
local getCounter, setCounter = signal(0)
|
local getTracked, setTracked = signal(0)
|
||||||
local getRuns, setRuns = signal(0)
|
local getUntracked, setUntracked = signal(0)
|
||||||
|
|
||||||
|
-- Tracked: 0, Untracked: 0
|
||||||
effect(function()
|
effect(function()
|
||||||
local runs = untracked(getRuns) + 1
|
print(`Tracked: {getTracked()}, Untracked: {untracked(getUntracked)}`)
|
||||||
setRuns(runs)
|
end)
|
||||||
print(`Count is {getCounter()}, ran {runs} times`)
|
|
||||||
end) -- Count is 0, ran 1 time
|
|
||||||
|
|
||||||
setCounter(10) -- Count is 10, ran 2 times
|
setTracked(1) -- Tracked: 1, Untracked: 0
|
||||||
setRuns(10) -- No output
|
setUntracked(1) -- No output
|
||||||
setCounter(20) -- Count is 20, ran 11 times
|
setTracked(2) -- Tracked: 2, Untracked: 1
|
||||||
```
|
```
|
||||||
|
|
||||||
Because `untracked()` executes the callback outside the current effect, nested effects created in the callback do not get tracked by the parent effect:
|
Because `untracked()` executes the callback outside the current effect, nested effects created in the callback do not get tracked by the parent effect:
|
||||||
|
|
@ -319,9 +320,10 @@ Similar to `untracked()`, but does not prevent the parent effect from tracking n
|
||||||
```luau
|
```luau
|
||||||
local getCounter, setCounter = signal(0)
|
local getCounter, setCounter = signal(0)
|
||||||
|
|
||||||
|
-- Count is 0
|
||||||
local disposeOuter = effect(function()
|
local disposeOuter = effect(function()
|
||||||
print(`Count is {peek(getCounter)}`)
|
print(`Count is {peek(getCounter)}`)
|
||||||
end) -- Count is 0
|
end)
|
||||||
|
|
||||||
setCounter(1) -- No output; count was accessed in peek()
|
setCounter(1) -- No output; count was accessed in peek()
|
||||||
```
|
```
|
||||||
|
|
@ -384,9 +386,10 @@ The callback also receives the previous value, or `nil` when running for the fir
|
||||||
```luau
|
```luau
|
||||||
local getCounter, setCounter = signal(0)
|
local getCounter, setCounter = signal(0)
|
||||||
|
|
||||||
|
-- Count is 0 (was nil)
|
||||||
listen(getCounter, function(count, prevCount)
|
listen(getCounter, function(count, prevCount)
|
||||||
print(`Count is {count} (was {prevCount})`)
|
print(`Count is {count} (was {prevCount})`)
|
||||||
end) -- Count is 0 (was nil)
|
end)
|
||||||
|
|
||||||
setCounter(1) -- Count is 1 (was 0)
|
setCounter(1) -- Count is 1 (was 0)
|
||||||
```
|
```
|
||||||
|
|
@ -421,12 +424,13 @@ Note that nested effects can be created inside `subscribe`, and they will clean
|
||||||
```luau
|
```luau
|
||||||
local getItems, setItems = signal({ a = 0, b = 0 })
|
local getItems, setItems = signal({ a = 0, b = 0 })
|
||||||
|
|
||||||
|
-- Added a, Added b
|
||||||
observe(getItems, function(value, key)
|
observe(getItems, function(value, key)
|
||||||
print(`Added {key}`)
|
print(`Added {key}`)
|
||||||
return function()
|
return function()
|
||||||
print(`Removed {key}`)
|
print(`Removed {key}`)
|
||||||
end
|
end
|
||||||
end) -- Added a, Added b
|
end)
|
||||||
|
|
||||||
setItems({ a = 0, c = 0 }) -- Removed b, Added c
|
setItems({ a = 0, c = 0 }) -- Removed b, Added c
|
||||||
```
|
```
|
||||||
|
|
@ -547,7 +551,9 @@ effect(function()
|
||||||
setCounter(function(count)
|
setCounter(function(count)
|
||||||
return math.min(count + 1, 3)
|
return math.min(count + 1, 3)
|
||||||
end)
|
end)
|
||||||
end) -- 0, 1, 2, 3
|
end)
|
||||||
|
|
||||||
|
-- Output: 0, 1, 2, 3
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -675,7 +681,7 @@ end)
|
||||||
|
|
||||||
To sync the client with the server's state, call `client.addSignals` with a table of writable signals (setter functions or atoms) whose keys match their server counterparts.
|
To sync the client with the server's state, call `client.addSignals` with a table of writable signals (setter functions or atoms) whose keys match their server counterparts.
|
||||||
|
|
||||||
After the client receives updates from the server, call `client.hydrate` to patch the client's signals with the incoming state updates.
|
After the client receives updates from the server, call `client.patch` to patch the client's signals with the incoming state updates.
|
||||||
|
|
||||||
```luau
|
```luau
|
||||||
-- Add signal setters or atoms
|
-- Add signal setters or atoms
|
||||||
|
|
@ -686,7 +692,7 @@ client.addSignals({
|
||||||
|
|
||||||
-- Update client signals when receiving updates from the server
|
-- Update client signals when receiving updates from the server
|
||||||
syncEvent.OnClientEvent:Connect(function(updates)
|
syncEvent.OnClientEvent:Connect(function(updates)
|
||||||
client.hydrate(updates)
|
client.patch(updates)
|
||||||
end)
|
end)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -798,7 +804,7 @@ server.flush()
|
||||||
|
|
||||||
Subscribes the given writable signals to the states with the corresponding keys on the server. When the server sends updates, the functions associated with each key in the state will be called with the patched values.
|
Subscribes the given writable signals to the states with the corresponding keys on the server. When the server sends updates, the functions associated with each key in the state will be called with the patched values.
|
||||||
|
|
||||||
You should pass either pass signal setter functions or atoms to this function:
|
You can pass either writable signals or atoms to this function:
|
||||||
|
|
||||||
```luau
|
```luau
|
||||||
client.addSignals({
|
client.addSignals({
|
||||||
|
|
@ -823,11 +829,11 @@ client.removeSignals("name")
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### `client.hydrate(updates)`
|
### `client.patch(updates)`
|
||||||
|
|
||||||
The `hydrate` function patches the client's state with the updates sent from the server. The initial update sent by the server will be the full state, and later updates will only include the values that changed.
|
The `patch` function patches the client's state with the updates sent from the server. The initial update sent by the server will be the full state, and later updates will only include the values that changed.
|
||||||
|
|
||||||
You should call `hydrate` when receiving updates from the server from a remote event:
|
You should call `patch` when receiving updates from the server from a remote event:
|
||||||
|
|
||||||
```luau
|
```luau
|
||||||
client.addSignals({
|
client.addSignals({
|
||||||
|
|
@ -836,7 +842,7 @@ client.addSignals({
|
||||||
})
|
})
|
||||||
|
|
||||||
syncEvent.OnClientEvent:Connect(function(updates)
|
syncEvent.OnClientEvent:Connect(function(updates)
|
||||||
client.hydrate(updates)
|
client.patch(updates)
|
||||||
end)
|
end)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -885,22 +891,22 @@ Charm v0.11 introduces _a lot_ of breaking changes, so below are some tips that
|
||||||
4. Nested effects automatically clean up when the parent effect re-runs or gets disposed. In other words, all effects created during the execution of another effect will be added as a "child" and clean up with the parent effect. This might cause issues in code that relied on the old behavior, where effects were detached from the parent.
|
4. Nested effects automatically clean up when the parent effect re-runs or gets disposed. In other words, all effects created during the execution of another effect will be added as a "child" and clean up with the parent effect. This might cause issues in code that relied on the old behavior, where effects were detached from the parent.
|
||||||
- This feature applies to all reaction APIs, including the listener function in `subscribe()` and the observer function in `observe()`.
|
- This feature applies to all reaction APIs, including the listener function in `subscribe()` and the observer function in `observe()`.
|
||||||
- Effects that should not be tracked by a parent effect/scope should be wrapped in [`untracked()`](#untrackedcallback).
|
- Effects that should not be tracked by a parent effect/scope should be wrapped in [`untracked()`](#untrackedcallback).
|
||||||
- This feature can introduce runtime bugs in migrated code. If you suspect this to be the cause, to help identify the issue, you can disable this feature by setting [`globals.trackInnerEffects`](#flags) to `false`.
|
- This feature can introduce runtime bugs in migrated code. If you suspect this to be the cause, to help identify the issue, you can disable this feature by setting [`flags.trackInnerEffects`](#flags) to `false`.
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> An example of nested effects causing a bug is an old implementation of [`VideCharm.useAtom`](./packages/vide-charm/src/init.luau) that did not wrap the source update in `untracked()`. Because Vide effects run immediately after a source updates, Vide will notify components during the execution of the Charm effect in `useAtom`.
|
> An example of nested effects causing a bug is an old implementation of [`VideCharm.useAtom`](./packages/vide-charm/src/init.luau) that did not wrap the source update in `untracked()`. Because Vide effects run immediately after a source updates, Vide will notify components during the execution of the Charm effect in `useAtom`.
|
||||||
>
|
>
|
||||||
> This meant effects created as a side effect of a source update would implicitly get added as a child of the `useAtom` effect, and they could get disposed at the wrong time and desync UI.
|
> This meant effects created as a side effect of a source update would implicitly get added as a child of the `useAtom` effect, and they could get disposed at the wrong time and desync UI.
|
||||||
|
|
||||||
5. Recursion is disallowed in effects and computed signals now that Charm uses the [alien-signals algorithm](https://github.com/stackblitz/alien-signals). This change may introduce bugs in code relying on the old behavior.
|
5. Recursion is now disallowed in effects and computed signals by default. This change may introduce bugs in code relying on the old behavior.
|
||||||
- Recursive checks are opt-out. You can allow recursion for a specific effect by calling [`recursive()`](#recursive) at the top of the effect callback.
|
- Recursive checks are opt-out. You can allow recursion for a specific effect by calling [`recursive()`](#recursive) at the top of the effect callback.
|
||||||
|
|
||||||
6. Consider refactoring your code to use some new quality-of-life features. Many of these are made possible thanks to [alien-signals](https://github.com/stackblitz/alien-signals)!
|
6. Consider refactoring your code to use some new quality-of-life features. Many of these are made possible thanks to [alien-signals](https://github.com/stackblitz/alien-signals)!
|
||||||
- Added [`signal()`](#signalinitialvalue-equals) to make reads and writes more explicit
|
- Added [`signal()`](#signalinitialvalue-equals) to make reads and writes more explicit
|
||||||
- Added [`listen()`](#listengetter-callback) for running a subscription once immediately
|
- Added [`listen()`](#listengetter-callback) to create a subscription that runs once immediately
|
||||||
- Added [`effectScope()`](#effectscopecallback) for collecting and cleaning up multiple effects at once
|
- Added [`effectScope()`](#effectscopecallback) for collecting and cleaning up multiple effects at once
|
||||||
- Added [`onCleanup()`](#oncleanupcallback-failsilently) for running code when the currently-running effect, subscription, or observer cleans up
|
- Added [`onCleanup()`](#oncleanupcallback-failsilently) to bind a cleanup function to the active effect
|
||||||
- Added [`trigger()`](#triggercallback) for triggering updates for table mutations
|
- Added [`trigger()`](#triggercallback) to trigger updates for table mutations
|
||||||
- The [`computed()`](#computedgetter) callback now gets called with the previous computed value
|
- The [`computed()`](#computedgetter) callback now gets called with the previous computed value
|
||||||
- Optimized `computed()` to use lazy evaluation instead of eager updates
|
- Optimized `computed()` to use lazy evaluation instead of eager updates
|
||||||
- Optimized `mapped()` to only map values that changed in the original table
|
- Optimized `mapped()` to only map values that changed in the original table
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@rbxts/charm-sync",
|
"name": "@rbxts/charm-sync",
|
||||||
"version": "0.3.0",
|
"version": "0.4.0-rc.1",
|
||||||
"description": "Simple server-to-client sync for Charm",
|
"description": "Simple server-to-client sync for Charm",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "src/init.luau",
|
"main": "src/init.luau",
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ test("should hydrate atom", function()
|
||||||
local a = Charm.atom(1)
|
local a = Charm.atom(1)
|
||||||
|
|
||||||
client.addSignals({ a = a })
|
client.addSignals({ a = a })
|
||||||
client.hydrate({
|
client.patch({
|
||||||
{ type = "init", data = { a = 2 } },
|
{ type = "init", data = { a = 2 } },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -25,13 +25,13 @@ test("should merge patch", function()
|
||||||
local previous = a()
|
local previous = a()
|
||||||
|
|
||||||
client.addSignals({ a = a })
|
client.addSignals({ a = a })
|
||||||
client.hydrate({
|
client.patch({
|
||||||
{ type = "init", data = { a = a() } },
|
{ type = "init", data = { a = a() } },
|
||||||
})
|
})
|
||||||
|
|
||||||
assert(a() == previous, "should set a to the initial value")
|
assert(a() == previous, "should set a to the initial value")
|
||||||
|
|
||||||
client.hydrate({
|
client.patch({
|
||||||
{ type = "patch", data = { a = { b = 3 } } },
|
{ type = "patch", data = { a = { b = 3 } } },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -44,14 +44,14 @@ test("should fix sparse array patches", function()
|
||||||
local a = Charm.atom({ 1 })
|
local a = Charm.atom({ 1 })
|
||||||
|
|
||||||
client.addSignals({ a = a })
|
client.addSignals({ a = a })
|
||||||
client.hydrate({
|
client.patch({
|
||||||
{ type = "init", data = { a = a() } },
|
{ type = "init", data = { a = a() } },
|
||||||
})
|
})
|
||||||
|
|
||||||
-- This patch would be the result of a diff between { 1 } and { 1, 2 } that
|
-- This patch would be the result of a diff between { 1 } and { 1, 2 } that
|
||||||
-- was JSON-serialized and deserialized. The client should be able to catch
|
-- was JSON-serialized and deserialized. The client should be able to catch
|
||||||
-- this and convert the string key back to a number.
|
-- this and convert the string key back to a number.
|
||||||
client.hydrate({
|
client.patch({
|
||||||
{ type = "patch", data = { a = { ["2"] = 2 } } },
|
{ type = "patch", data = { a = { ["2"] = 2 } } },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -69,11 +69,11 @@ test("should merge multiple patches", function()
|
||||||
table.insert(calls, value)
|
table.insert(calls, value)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
client.hydrate({
|
client.patch({
|
||||||
{ type = "init", data = { a = a() } },
|
{ type = "init", data = { a = a() } },
|
||||||
})
|
})
|
||||||
|
|
||||||
client.hydrate({
|
client.patch({
|
||||||
{ type = "patch", data = { a = 2 } },
|
{ type = "patch", data = { a = 2 } },
|
||||||
{ type = "patch", data = { a = 3 } },
|
{ type = "patch", data = { a = 3 } },
|
||||||
})
|
})
|
||||||
|
|
@ -97,11 +97,11 @@ test("should batch patches", function()
|
||||||
states.a = a()
|
states.a = a()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
client.hydrate({
|
client.patch({
|
||||||
{ type = "init", data = { a = a(), b = b() } },
|
{ type = "init", data = { a = a(), b = b() } },
|
||||||
})
|
})
|
||||||
|
|
||||||
client.hydrate({
|
client.patch({
|
||||||
{ type = "patch", data = { a = 1, b = 2 } },
|
{ type = "patch", data = { a = 1, b = 2 } },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ test("should replicate server state to client", function()
|
||||||
server.addSignalsToClient(target, { a = serverA, b = serverB })
|
server.addSignalsToClient(target, { a = serverA, b = serverB })
|
||||||
server.connect(function(syncTarget, payloads)
|
server.connect(function(syncTarget, payloads)
|
||||||
if syncTarget == target then
|
if syncTarget == target then
|
||||||
client.hydrate(payloads)
|
client.patch(payloads)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ test("should replicate historical server state to client", function()
|
||||||
server.addSignalsToClient(target, { a = serverA, b = serverB })
|
server.addSignalsToClient(target, { a = serverA, b = serverB })
|
||||||
server.connect(function(syncTarget, payloads)
|
server.connect(function(syncTarget, payloads)
|
||||||
if syncTarget == target then
|
if syncTarget == target then
|
||||||
client.hydrate(payloads)
|
client.patch(payloads)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,8 @@ end
|
||||||
--- initializing it with the full state or merging a partial patch.
|
--- initializing it with the full state or merging a partial patch.
|
||||||
---
|
---
|
||||||
--- @param payloads The state updates received from the server.
|
--- @param payloads The state updates received from the server.
|
||||||
local function hydrate(payloads: { SyncPayload })
|
local function patch(payloads: { SyncPayload })
|
||||||
|
debug.profilebegin("CharmSync")
|
||||||
for _, payload in payloads do
|
for _, payload in payloads do
|
||||||
if payload.type == "init" then
|
if payload.type == "init" then
|
||||||
Charm.batch(applyInitPayload, payload.data)
|
Charm.batch(applyInitPayload, payload.data)
|
||||||
|
|
@ -70,11 +71,12 @@ local function hydrate(payloads: { SyncPayload })
|
||||||
Charm.batch(applyPatchPayload, payload.data)
|
Charm.batch(applyPatchPayload, payload.data)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
debug.profileend()
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
addSignals = addSignals,
|
addSignals = addSignals,
|
||||||
removeSignals = removeSignals,
|
removeSignals = removeSignals,
|
||||||
removeAllSignals = removeAllSignals,
|
removeAllSignals = removeAllSignals,
|
||||||
hydrate = hydrate,
|
patch = patch,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
packages/charm-sync/src/index.d.ts
vendored
6
packages/charm-sync/src/index.d.ts
vendored
|
|
@ -1,4 +1,4 @@
|
||||||
import { Atom, Getter, Setter } from "@rbxts/charm";
|
import { Atom, Setter } from "@rbxts/charm";
|
||||||
|
|
||||||
type Key = string | number | symbol;
|
type Key = string | number | symbol;
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@ export interface None {
|
||||||
|
|
||||||
export type MaybeNone<T> = undefined extends T ? None : never;
|
export type MaybeNone<T> = undefined extends T ? None : never;
|
||||||
|
|
||||||
type GetterMap = Record<string, Getter<any>>;
|
type GetterMap = Record<string, () => any>;
|
||||||
type SetterMap = Record<string, Setter<any>>;
|
type SetterMap = Record<string, Setter<any>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -134,7 +134,7 @@ export namespace client {
|
||||||
*
|
*
|
||||||
* @param payloads The state updates received from the server.
|
* @param payloads The state updates received from the server.
|
||||||
*/
|
*/
|
||||||
export function hydrate<Getters extends GetterMap = GetterMap, FixArrays extends boolean = true>(
|
export function patch<Getters extends GetterMap = GetterMap, FixArrays extends boolean = true>(
|
||||||
payloads: SyncPayload<Getters, FixArrays>[],
|
payloads: SyncPayload<Getters, FixArrays>[],
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "littensy/charm-sync"
|
name = "littensy/charm-sync"
|
||||||
version = "0.3.0"
|
version = "0.4.0-rc.1"
|
||||||
registry = "https://github.com/UpliftGames/wally-index"
|
registry = "https://github.com/UpliftGames/wally-index"
|
||||||
realm = "shared"
|
realm = "shared"
|
||||||
exclude = [
|
exclude = [
|
||||||
|
|
@ -12,4 +12,4 @@ exclude = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
Charm = "littensy/charm@^0.10"
|
Charm = "littensy/charm@0.11.0-rc.1"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@rbxts/charm",
|
"name": "@rbxts/charm",
|
||||||
"version": "0.10.0",
|
"version": "0.11.0-rc.1",
|
||||||
"description": "Atomic state management based on alien-signals",
|
"description": "Atomic state management based on alien-signals",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "src/init.luau",
|
"main": "src/init.luau",
|
||||||
|
|
|
||||||
|
|
@ -367,10 +367,10 @@ end
|
||||||
--- @see https://github.com/littensy/charm?tab=readme-ov-file#atominitialValue-equals
|
--- @see https://github.com/littensy/charm?tab=readme-ov-file#atominitialValue-equals
|
||||||
local function atom<T>(initialValue: T, equals: ((current: T, incoming: T) -> boolean)?): Atom<T>
|
local function atom<T>(initialValue: T, equals: ((current: T, incoming: T) -> boolean)?): Atom<T>
|
||||||
local getter, setter = signal(initialValue, equals)
|
local getter, setter = signal(initialValue, equals)
|
||||||
local function atomGetterSetter(...)
|
local function atomOper(...)
|
||||||
return if select("#", ...) == 0 then getter() else setter((...))
|
return if select("#", ...) == 0 then getter() else setter((...))
|
||||||
end
|
end
|
||||||
return atomGetterSetter
|
return atomOper
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creates a new read-only signal that is computed based on the values of
|
--- Creates a new read-only signal that is computed based on the values of
|
||||||
|
|
@ -565,7 +565,7 @@ end
|
||||||
--- @return A computed signal that tracks the value returned by the getter.
|
--- @return A computed signal that tracks the value returned by the getter.
|
||||||
local function toSignal<T>(fn: () -> T): () -> T
|
local function toSignal<T>(fn: () -> T): () -> T
|
||||||
local name = debug.info(fn, "n")
|
local name = debug.info(fn, "n")
|
||||||
return if name == "signalGetter" or name == "computedGetter" or name == "atomGetterSetter" then fn else computed(fn)
|
return if name == "signalGetter" or name == "computedGetter" or name == "atomOper" then fn else computed(fn)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Creates an effect that runs the callback one immediately, and then again
|
--- Creates an effect that runs the callback one immediately, and then again
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "littensy/charm"
|
name = "littensy/charm"
|
||||||
version = "0.10.0"
|
version = "0.11.0-rc.1"
|
||||||
registry = "https://github.com/UpliftGames/wally-index"
|
registry = "https://github.com/UpliftGames/wally-index"
|
||||||
realm = "shared"
|
realm = "shared"
|
||||||
exclude = ["node_modules", "src/__tests__", "src/index.d.ts", "package.json"]
|
exclude = ["node_modules", "src/__tests__", "src/index.d.ts", "package.json"]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@rbxts/react-charm",
|
"name": "@rbxts/react-charm",
|
||||||
"version": "0.3.0",
|
"version": "0.4.0-rc.1",
|
||||||
"description": "React bindings for Charm state management",
|
"description": "React bindings for Charm state management",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "src/init.luau",
|
"main": "src/init.luau",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "littensy/react-charm"
|
name = "littensy/react-charm"
|
||||||
version = "0.3.0"
|
version = "0.4.0-rc.1"
|
||||||
registry = "https://github.com/UpliftGames/wally-index"
|
registry = "https://github.com/UpliftGames/wally-index"
|
||||||
realm = "shared"
|
realm = "shared"
|
||||||
exclude = [
|
exclude = [
|
||||||
|
|
@ -13,5 +13,5 @@ exclude = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
Charm = "littensy/charm@^0.10"
|
Charm = "littensy/charm@0.11.0-rc.1"
|
||||||
React = "jsdotlua/react@^17"
|
React = "jsdotlua/react@^17"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@rbxts/vide-charm",
|
"name": "@rbxts/vide-charm",
|
||||||
"version": "0.3.0",
|
"version": "0.4.0-rc.1",
|
||||||
"description": "Vide bindings for Charm state management",
|
"description": "Vide bindings for Charm state management",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "src/init.luau",
|
"main": "src/init.luau",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "littensy/vide-charm"
|
name = "littensy/vide-charm"
|
||||||
version = "0.3.0"
|
version = "0.4.0-rc.1"
|
||||||
registry = "https://github.com/UpliftGames/wally-index"
|
registry = "https://github.com/UpliftGames/wally-index"
|
||||||
realm = "shared"
|
realm = "shared"
|
||||||
exclude = [
|
exclude = [
|
||||||
|
|
@ -13,5 +13,5 @@ exclude = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
Charm = "littensy/charm@^0.10"
|
Charm = "littensy/charm@0.11.0-rc.1"
|
||||||
Vide = "centau/vide@^0.3"
|
Vide = "centau/vide@^0.3"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue