-
Notifications
You must be signed in to change notification settings - Fork 152
upgrade: storage package upgrade for Solid 2.0 #851
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from all commits
9404090
ef953e9
4a2b0ae
6a2a17e
383da78
0ceb475
25d10f8
c85f0ac
3e602a0
d72c4c3
2cb49ea
c0f20d7
a96bfb2
3d132af
31247bb
9e5eaf5
e00599f
aa218ec
ecb0763
37d3d4f
417e029
224d8fa
871aabf
153c169
19b9ee8
98846a5
8d1ef6f
aea1e1f
2df91dc
61baf95
ba4f617
7719d6f
aff1087
524e2f0
1c35ccf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@solid-primitives/storage": major | ||
| --- | ||
|
|
||
| **`makePersisted`** - simplify setter, Solid 2.0 adaption, simpler types using function overloads |
This file was deleted.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,8 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||||||
| import type { Accessor, Setter, Signal } from "solid-js"; | ||||||||||||||||||||||||||||||||||||||||||
| import { createUniqueId, untrack } from "solid-js"; | ||||||||||||||||||||||||||||||||||||||||||
| import { isServer, isDev } from "solid-js/web"; | ||||||||||||||||||||||||||||||||||||||||||
| import type { SetStoreFunction, Store } from "solid-js/store"; | ||||||||||||||||||||||||||||||||||||||||||
| import { reconcile } from "solid-js/store"; | ||||||||||||||||||||||||||||||||||||||||||
| import type { Signal, StoreSetter, Store } from "solid-js"; | ||||||||||||||||||||||||||||||||||||||||||
| import { action, createUniqueId, latest, untrack, reconcile, snapshot, DEV } from "solid-js"; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| export type SyncStorage = { | ||||||||||||||||||||||||||||||||||||||||||
| getItem: (key: string) => string | null; | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -58,70 +55,81 @@ export type PersistenceSyncAPI = [ | |||||||||||||||||||||||||||||||||||||||||
| update: (key: string, value: string | null | undefined) => void, | ||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| export type PersistenceOptions<T, O extends Record<string, any> | undefined> = { | ||||||||||||||||||||||||||||||||||||||||||
| export type PersistenceOptions< | ||||||||||||||||||||||||||||||||||||||||||
| S extends Signal<any> | [Store<any>, StoreSetter<any>] | readonly [Store<any>, StoreSetter<any>], | ||||||||||||||||||||||||||||||||||||||||||
| O extends Record<string, any> | undefined, | ||||||||||||||||||||||||||||||||||||||||||
| T = S extends Signal<infer T> ? T : S extends [Store<infer T>, StoreSetter<infer T>] ? T : never | ||||||||||||||||||||||||||||||||||||||||||
| > = { | ||||||||||||||||||||||||||||||||||||||||||
| name?: string; | ||||||||||||||||||||||||||||||||||||||||||
| serialize?: (data: T) => string; | ||||||||||||||||||||||||||||||||||||||||||
| deserialize?: (data: string) => T; | ||||||||||||||||||||||||||||||||||||||||||
| sync?: PersistenceSyncAPI; | ||||||||||||||||||||||||||||||||||||||||||
| action?: (signal: S) => Parameters<typeof action>[0]; | ||||||||||||||||||||||||||||||||||||||||||
| } & (undefined extends O | ||||||||||||||||||||||||||||||||||||||||||
| ? { storage?: SyncStorage | AsyncStorage } | ||||||||||||||||||||||||||||||||||||||||||
| : { | ||||||||||||||||||||||||||||||||||||||||||
| storage: SyncStorageWithOptions<O> | AsyncStorageWithOptions<O>; | ||||||||||||||||||||||||||||||||||||||||||
| storageOptions?: O; | ||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| export type SignalInput = Signal<any> | [Store<any>, SetStoreFunction<any>]; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| export type SignalType<S extends SignalInput> = | ||||||||||||||||||||||||||||||||||||||||||
| S extends Signal<infer T> ? T : S extends [Store<infer T>, SetStoreFunction<infer T>] ? T : never; | ||||||||||||||||||||||||||||||||||||||||||
| export type PersistedState<S> = S extends [any, any] ? [...S, Promise<string> | string | null] : never; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| export type PersistedState<S extends SignalInput> = | ||||||||||||||||||||||||||||||||||||||||||
| S extends Signal<infer T> | ||||||||||||||||||||||||||||||||||||||||||
| ? [get: Accessor<T>, set: Setter<T>, init: Promise<string> | string | null] | ||||||||||||||||||||||||||||||||||||||||||
| : S extends [Store<infer T>, SetStoreFunction<infer T>] | ||||||||||||||||||||||||||||||||||||||||||
| ? [get: Store<T>, set: SetStoreFunction<T>, init: Promise<string> | string | null] | ||||||||||||||||||||||||||||||||||||||||||
| : never; | ||||||||||||||||||||||||||||||||||||||||||
| export type StoreTuple<T> = [Store<T>, StoreSetter<T>]; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||
| * Persists a signal, store or similar API | ||||||||||||||||||||||||||||||||||||||||||
| * ```ts | ||||||||||||||||||||||||||||||||||||||||||
| * const [getter, setter] = makePersisted(createSignal("data"), options); | ||||||||||||||||||||||||||||||||||||||||||
| * const options = { | ||||||||||||||||||||||||||||||||||||||||||
| * storage: cookieStorage, // can be any synchronous or asynchronous storage | ||||||||||||||||||||||||||||||||||||||||||
| * storageOptions: { ... }, // for storages with options, otherwise not needed | ||||||||||||||||||||||||||||||||||||||||||
| * name: "solid-data", // optional | ||||||||||||||||||||||||||||||||||||||||||
| * serialize: (value: string) => value, // optional | ||||||||||||||||||||||||||||||||||||||||||
| * deserialize: (data: string) => data, // optional | ||||||||||||||||||||||||||||||||||||||||||
| * }; | ||||||||||||||||||||||||||||||||||||||||||
| * ``` | ||||||||||||||||||||||||||||||||||||||||||
| * Can be used with `createSignal` or `createStore`. The initial value from the storage will overwrite the initial | ||||||||||||||||||||||||||||||||||||||||||
| * value of the signal or store unless overwritten. Overwriting a signal with `null` or `undefined` will remove the | ||||||||||||||||||||||||||||||||||||||||||
| * item from the storage. | ||||||||||||||||||||||||||||||||||||||||||
| * ```ts | ||||||||||||||||||||||||||||||||||||||||||
| * const [getter, setter] = makePersisted(createSignal("data"), options); | ||||||||||||||||||||||||||||||||||||||||||
| * const options = { | ||||||||||||||||||||||||||||||||||||||||||
| * storage: cookieStorage, // can be any synchronous or asynchronous storage | ||||||||||||||||||||||||||||||||||||||||||
| * storageOptions: { ... }, // for storages with options, otherwise not needed | ||||||||||||||||||||||||||||||||||||||||||
| * name: "solid-data", // optional | ||||||||||||||||||||||||||||||||||||||||||
| * serialize: (value: string) => value, // optional | ||||||||||||||||||||||||||||||||||||||||||
| * deserialize: (data: string) => data, // optional | ||||||||||||||||||||||||||||||||||||||||||
| * action: (setter: Setter<T>) => Setter<T> // optional, to be put inside action | ||||||||||||||||||||||||||||||||||||||||||
| * }; | ||||||||||||||||||||||||||||||||||||||||||
| * ``` | ||||||||||||||||||||||||||||||||||||||||||
| * Can be used with `createSignal`, `createStore`, `createOptimistic`, or `createOptimisticStore` (for the latter | ||||||||||||||||||||||||||||||||||||||||||
| * two, use options.action to wrap the setter). The initial value from the storage will overwrite the initial | ||||||||||||||||||||||||||||||||||||||||||
| * value of the signal or store unless overwritten. Overwriting a signal with `null` or `undefined` will remove the | ||||||||||||||||||||||||||||||||||||||||||
| * item from the storage. | ||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||
| * @param {Signal<T> | [get: Store<T>, set: SetStoreFunction<T>]} signal - The signal or store to be persisted. | ||||||||||||||||||||||||||||||||||||||||||
| * @param {PersistenceOptions<T, O>} options - The options for persistence. | ||||||||||||||||||||||||||||||||||||||||||
| * @param {Signal<T> | [get: Store<T>, set: StoreSetter<T>]} signal - The signal or store to be persisted. | ||||||||||||||||||||||||||||||||||||||||||
| * @param {PersistenceOptions<Signal<T> | [get: Store<T>, set StoreSetter<T>], O>} options - The options for persistence. | ||||||||||||||||||||||||||||||||||||||||||
| * @returns {PersistedState<T>} - The persisted signal or store. | ||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||
| export function makePersisted<S extends SignalInput>( | ||||||||||||||||||||||||||||||||||||||||||
| signal: S, | ||||||||||||||||||||||||||||||||||||||||||
| options?: PersistenceOptions<SignalType<S>, undefined>, | ||||||||||||||||||||||||||||||||||||||||||
| ): PersistedState<S>; | ||||||||||||||||||||||||||||||||||||||||||
| export function makePersisted<S extends SignalInput, O extends Record<string, any>>( | ||||||||||||||||||||||||||||||||||||||||||
| signal: S, | ||||||||||||||||||||||||||||||||||||||||||
| options: PersistenceOptions<SignalType<S>, O>, | ||||||||||||||||||||||||||||||||||||||||||
| ): PersistedState<S>; | ||||||||||||||||||||||||||||||||||||||||||
| export function makePersisted<T>( | ||||||||||||||||||||||||||||||||||||||||||
| signal: Signal<T>, | ||||||||||||||||||||||||||||||||||||||||||
| options?: PersistenceOptions<Signal<T>, undefined>, | ||||||||||||||||||||||||||||||||||||||||||
| ): PersistedState<Signal<T>>; | ||||||||||||||||||||||||||||||||||||||||||
| export function makePersisted<T>( | ||||||||||||||||||||||||||||||||||||||||||
| signal: StoreTuple<T>, | ||||||||||||||||||||||||||||||||||||||||||
| options?: PersistenceOptions<StoreTuple<T>, undefined>, | ||||||||||||||||||||||||||||||||||||||||||
| ): PersistedState<StoreTuple<T>>; | ||||||||||||||||||||||||||||||||||||||||||
| export function makePersisted< | ||||||||||||||||||||||||||||||||||||||||||
| T, | ||||||||||||||||||||||||||||||||||||||||||
| O extends Record<string, any> | ||||||||||||||||||||||||||||||||||||||||||
| >(signal: Signal<T>, options: PersistenceOptions<Signal<T>, O>): PersistedState<Signal<T>>; | ||||||||||||||||||||||||||||||||||||||||||
| export function makePersisted< | ||||||||||||||||||||||||||||||||||||||||||
| S extends SignalInput, | ||||||||||||||||||||||||||||||||||||||||||
| T, | ||||||||||||||||||||||||||||||||||||||||||
| O extends Record<string, any> | ||||||||||||||||||||||||||||||||||||||||||
| >(signal: StoreTuple<T>, options: PersistenceOptions<StoreTuple<T>, O>): PersistedState<StoreTuple<T>>; | ||||||||||||||||||||||||||||||||||||||||||
| export function makePersisted< | ||||||||||||||||||||||||||||||||||||||||||
| T, | ||||||||||||||||||||||||||||||||||||||||||
| O extends Record<string, any> | undefined, | ||||||||||||||||||||||||||||||||||||||||||
| T = SignalType<S>, | ||||||||||||||||||||||||||||||||||||||||||
| S extends Signal<T> | StoreTuple<T>, | ||||||||||||||||||||||||||||||||||||||||||
| >( | ||||||||||||||||||||||||||||||||||||||||||
| signal: S, | ||||||||||||||||||||||||||||||||||||||||||
| options: PersistenceOptions<T, O> = {} as PersistenceOptions<T, O>, | ||||||||||||||||||||||||||||||||||||||||||
| options: PersistenceOptions<S, O> = {} as PersistenceOptions<S, O>, | ||||||||||||||||||||||||||||||||||||||||||
| ): PersistedState<S> { | ||||||||||||||||||||||||||||||||||||||||||
| const storage = options.storage || (globalThis.localStorage as Storage | undefined); | ||||||||||||||||||||||||||||||||||||||||||
| const name = options.name || `storage-${createUniqueId()}`; | ||||||||||||||||||||||||||||||||||||||||||
| const actionFn = options.action && options.action(signal); | ||||||||||||||||||||||||||||||||||||||||||
| if (actionFn) { | ||||||||||||||||||||||||||||||||||||||||||
| (signal as any)[1] = action(actionFn) as unknown as S[1]; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| if (!storage) { | ||||||||||||||||||||||||||||||||||||||||||
| return [signal[0], signal[1], null] as PersistedState<S>; | ||||||||||||||||||||||||||||||||||||||||||
| return [signal[0], signal[1], null] as unknown as PersistedState<S>; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| const storageOptions = (options as unknown as { storageOptions: O }).storageOptions; | ||||||||||||||||||||||||||||||||||||||||||
| const serialize: (data: T) => string = options.serialize || JSON.stringify.bind(JSON); | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was confused by the bind, neither method uses this...so isn't this just allocating a pointless closure? Maybe I missed something? lol |
||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -135,16 +143,16 @@ export function makePersisted< | |||||||||||||||||||||||||||||||||||||||||
| (signal[1] as any)(() => value); | ||||||||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line no-console | ||||||||||||||||||||||||||||||||||||||||||
| if (isDev) console.warn(e); | ||||||||||||||||||||||||||||||||||||||||||
| if (DEV) console.warn(e); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| : (data: string) => { | ||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||
| const value = deserialize(data); | ||||||||||||||||||||||||||||||||||||||||||
| (signal[1] as any)(reconcile(value)); | ||||||||||||||||||||||||||||||||||||||||||
| (signal[1] as any)(reconcile(value, () => undefined)); | ||||||||||||||||||||||||||||||||||||||||||
| } catch (e) { | ||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line no-console | ||||||||||||||||||||||||||||||||||||||||||
| if (isDev) console.warn(e); | ||||||||||||||||||||||||||||||||||||||||||
| if (DEV) console.warn(e); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
149
to
157
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: In SolidJS 2.0, the reconcile function accepts a second, optional argument that is an options object [1]. The signature for this options object is as follows [1]: { key?: string | null; merge?: boolean; } The key property specifies the property name used to match items in arrays during reconciliation (e.g., "id"), while the merge property determines whether reconciliation pushes updates deeper into the structure (true) or prefers replacing non-matching branches (false) [1]. Citations: 🏁 Script executed: #!/bin/bash
set -euo pipefail
sed -n '120,190p' packages/storage/src/persisted.ts | nl -baRepository: solidjs-community/solid-primitives Length of output: 121 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== persisted.ts (approx lines 130-180) ==="
sed -n '130,180p' packages/storage/src/persisted.ts | awk '{printf "%5d %s\n", NR+129, $0}'
echo
echo "=== reconcile usages in persisted.ts ==="
rg -n "reconcile\\(" packages/storage/src/persisted.ts
echo
echo "=== reconcile imports/typing in persisted.ts ==="
rg -n "import .*reconcile|from .*reconcile|type .*Reconcile|ReconcileOptions" packages/storage/src/persisted.tsRepository: solidjs-community/solid-primitives Length of output: 2573 🌐 Web query:
💡 Result: In SolidJS, the reconcile store utility officially expects the options argument to be an object with the properties key (string | null) and merge (boolean) [1]. It does not support receiving a function as the options argument [1]. If you pass a function where an options object is expected, the implementation will attempt to access the key and merge properties directly on that function (e.g., function.key, function.merge). Since these properties are typically undefined on a function object, they will be treated as their default values, effectively ignoring the function and proceeding with default reconciliation behavior [1]. It is worth noting that some third-party libraries, such as TanStack Query, have introduced their own custom reconcile options or patterns that allow providing a function to define custom reconciliation logic (e.g., (oldData, newData) => reconciledData) [2]. However, this is specific to those libraries' implementations and is distinct from the official solid-js/store reconcile utility [1][2]. Citations:
Fix
Proposed change- (signal[1] as any)(reconcile(value, () => undefined));
+ (signal[1] as any)(reconcile(value));📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| let unchanged = true; | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -158,7 +166,7 @@ export function makePersisted< | |||||||||||||||||||||||||||||||||||||||||
| options.sync[0]((data: PersistenceSyncData) => { | ||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||
| data.key !== name || | ||||||||||||||||||||||||||||||||||||||||||
| (!isServer && (data.url || globalThis.location.href) !== globalThis.location.href) || | ||||||||||||||||||||||||||||||||||||||||||
| (!globalThis.window && (data.url || globalThis.location.href) !== globalThis.location.href) || | ||||||||||||||||||||||||||||||||||||||||||
| data.newValue === serialize(untrack(get)) | ||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -167,28 +175,28 @@ export function makePersisted< | |||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| const getter = typeof signal[0] === "function" ? signal[0] as () => T : () => snapshot(signal[0] as T); | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're going a few of these evaluations on line 140, 166 and 179. Maybe just have |
||||||||||||||||||||||||||||||||||||||||||
| const persist = () => { | ||||||||||||||||||||||||||||||||||||||||||
| const next = untrack(() => latest(getter)); | ||||||||||||||||||||||||||||||||||||||||||
| if (next == null) { | ||||||||||||||||||||||||||||||||||||||||||
| storage.removeItem(name, storageOptions); | ||||||||||||||||||||||||||||||||||||||||||
| options.sync?.[1](name, null); | ||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||
| const serialized = serialize(next); | ||||||||||||||||||||||||||||||||||||||||||
| storage.setItem(name, serialized, storageOptions); | ||||||||||||||||||||||||||||||||||||||||||
| options.sync?.[1](name, serialized); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||
| return [ | ||||||||||||||||||||||||||||||||||||||||||
| signal[0], | ||||||||||||||||||||||||||||||||||||||||||
| typeof signal[0] === "function" | ||||||||||||||||||||||||||||||||||||||||||
| ? (value?: T | ((prev: T) => T)) => { | ||||||||||||||||||||||||||||||||||||||||||
| const output = (signal[1] as Setter<T>)(value as any); | ||||||||||||||||||||||||||||||||||||||||||
| const serialized: string | null | undefined = | ||||||||||||||||||||||||||||||||||||||||||
| value != null ? serialize(output) : (value as null | undefined); | ||||||||||||||||||||||||||||||||||||||||||
| options.sync?.[1](name, serialized); | ||||||||||||||||||||||||||||||||||||||||||
| if (serialized != null) storage.setItem(name, serialized, storageOptions); | ||||||||||||||||||||||||||||||||||||||||||
| else storage.removeItem(name, storageOptions); | ||||||||||||||||||||||||||||||||||||||||||
| unchanged = false; | ||||||||||||||||||||||||||||||||||||||||||
| return output; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| : (...args: any[]) => { | ||||||||||||||||||||||||||||||||||||||||||
| (signal[1] as any)(...args); | ||||||||||||||||||||||||||||||||||||||||||
| const value = serialize(untrack(() => signal[0])); | ||||||||||||||||||||||||||||||||||||||||||
| options.sync?.[1](name, value); | ||||||||||||||||||||||||||||||||||||||||||
| storage.setItem(name, value, storageOptions); | ||||||||||||||||||||||||||||||||||||||||||
| unchanged = false; | ||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||
| signal[0], | ||||||||||||||||||||||||||||||||||||||||||
| (value: any) => untrack(() => { | ||||||||||||||||||||||||||||||||||||||||||
| const output = signal[1](value); | ||||||||||||||||||||||||||||||||||||||||||
| persist(); | ||||||||||||||||||||||||||||||||||||||||||
| unchanged = false; | ||||||||||||||||||||||||||||||||||||||||||
| return output instanceof Promise ? output.then((result) => (persist(), result)) : output; | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. persist() is called synchronously to write the optimistic value, then again in .then() on line 197. There is no catch so if the action rejects, Solid rolls back the in-memory signal, but storage is never re-synced. It permanently holds the pre-rejection optimistic value. On the next page load the signal hydrates with stale data? |
||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||
| init, | ||||||||||||||||||||||||||||||||||||||||||
| ] as PersistedState<S>; | ||||||||||||||||||||||||||||||||||||||||||
| ] as unknown as PersistedState<S>; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -222,7 +230,7 @@ export const messageSync = (channel: Window | BroadcastChannel = window): Persis | |||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||
| * wsSync - syncronize persisted storage via web socket | ||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||
| export const wsSync = (ws: WebSocket, warnOnError: boolean = isDev): PersistenceSyncAPI => [ | ||||||||||||||||||||||||||||||||||||||||||
| export const wsSync = (ws: WebSocket, warnOnError: boolean = !!DEV): PersistenceSyncAPI => [ | ||||||||||||||||||||||||||||||||||||||||||
| (subscriber: PersistenceSyncCallback) => | ||||||||||||||||||||||||||||||||||||||||||
| ws.addEventListener("message", (ev: MessageEvent) => { | ||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -241,7 +249,7 @@ export const wsSync = (ws: WebSocket, warnOnError: boolean = isDev): Persistence | |||||||||||||||||||||||||||||||||||||||||
| key, | ||||||||||||||||||||||||||||||||||||||||||
| newValue, | ||||||||||||||||||||||||||||||||||||||||||
| timeStamp: +new Date(), | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't Date.now() more the idiomatic form because it avoids creating an unnecessary date object? |
||||||||||||||||||||||||||||||||||||||||||
| ...(isServer ? {} : { url: location.href }), | ||||||||||||||||||||||||||||||||||||||||||
| ...(globalThis.window ? { url: location.href } : {}), | ||||||||||||||||||||||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I still am not sure why we wouldn't just use isServer? |
||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Does solid-js 2.0.0-beta.14 export reconcile and snapshot from the root "solid-js" package entry?💡 Result:
No, solid-js version 2.0.0-beta.14 does not export reconcile or snapshot from the root solid-js package entry [1][2][3]. In SolidJS, reconcile and snapshot are utilities associated with the store system and are exported from the solid-js/store module [2][3]. While Solid 2.0 involves significant changes to the package structure—including the removal of explicit solid-js/web and solid-js/store subpaths in favor of a consolidated approach in some contexts—these specific store-related utilities continue to reside within the store-specific API surface, not the root export [1][2]. Developers should continue to import these from solid-js/store [2][3].
Citations:
Fix incorrect imports of
reconcile/snapshotfrom the Solid rootpackages/storage/src/persisted.tsimportsreconcileandsnapshotfrom"solid-js"(lines 1-2), butsolid-js@2.0.0-beta.14does not exportreconcile/snapshotfrom the package root—these are store-related APIs exposed fromsolid-js/store. Update the imports to use the correct module to avoid build-time resolution errors.🤖 Prompt for AI Agents