@pyreon/solid-compat 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,43 @@
1
- import { ComponentFn, ErrorBoundary, For, LazyComponent, Match, Props, Show, Suspense, Switch, VNodeChild, createContext as pyreonCreateContext, useContext as pyreonUseContext } from "@pyreon/core";
1
+ import { ComponentFn, Context, ErrorBoundary, For, LazyComponent, Match, Props, Show, Suspense, Switch, VNodeChild } from "@pyreon/core";
2
2
  import { EffectScope, batch as pyreonBatch, runUntracked } from "@pyreon/reactivity";
3
3
 
4
4
  //#region src/index.d.ts
5
+ /** Solid-compatible read accessor type */
6
+ type Accessor<T> = () => T;
7
+ /** Solid-compatible setter type */
8
+ type Setter<T> = (v: T | ((prev: T) => T)) => void;
9
+ /** Solid-compatible signal tuple type */
10
+ type Signal<T> = [Accessor<T>, Setter<T>];
11
+ /** Solid-compatible owner type */
12
+ type Owner = EffectScope;
13
+ /** Solid-compatible component type */
14
+ type Component<P = object> = (props: P) => VNodeChild;
15
+ /** Solid-compatible parent component type (includes children) */
16
+ type ParentComponent<P = object> = (props: P & {
17
+ children?: VNodeChild;
18
+ }) => VNodeChild;
19
+ /** Solid-compatible flow component type */
20
+ type FlowComponent<P = object> = Component<P>;
21
+ /** Solid-compatible void component type (no children) */
22
+ type VoidComponent<P = object> = Component<P>;
5
23
  type SignalGetter<T> = () => T;
6
24
  type SignalSetter<T> = (v: T | ((prev: T) => T)) => void;
7
- declare function createSignal<T>(initialValue: T): [SignalGetter<T>, SignalSetter<T>];
25
+ interface CreateSignalOptions<T> {
26
+ equals?: false | ((prev: T, next: T) => boolean);
27
+ }
28
+ declare function createSignal<T>(initialValue: T, options?: CreateSignalOptions<T>): [SignalGetter<T>, SignalSetter<T>];
8
29
  /**
9
30
  * Solid-compatible `createEffect` — creates a reactive side effect.
10
31
  *
32
+ * Supports the `(prev) => next` signature with an optional initial value,
33
+ * matching Solid's `createEffect<T>(fn: (prev: T) => T, initialValue: T)`.
34
+ *
11
35
  * In component context: hook-indexed, only created on first render. The effect
12
36
  * uses Pyreon's native tracking so signal reads are automatically tracked.
13
37
  * A re-entrance guard prevents infinite loops from signal writes inside
14
38
  * the effect.
15
39
  */
16
- declare function createEffect(fn: () => void): void;
40
+ declare function createEffect<T>(fn: ((prev?: T) => T) | (() => void), initialValue?: T): void;
17
41
  /**
18
42
  * Solid-compatible `createRenderEffect` — same as createEffect.
19
43
  * In Solid, this runs during the render phase; here it runs as a Pyreon effect.
@@ -22,14 +46,19 @@ declare function createRenderEffect(fn: () => void): void;
22
46
  /**
23
47
  * Solid-compatible `createMemo` — derives a value from reactive sources.
24
48
  *
49
+ * Supports the `(prev) => next` signature with an optional initial value,
50
+ * matching Solid's `createMemo<T>(fn: (prev: T) => T, initialValue: T)`.
51
+ *
25
52
  * In component context: hook-indexed, only created on first render.
26
53
  * Uses Pyreon's native computed for auto-tracking.
27
54
  */
28
- declare function createMemo<T>(fn: () => T): () => T;
55
+ declare function createMemo<T>(fn: ((prev?: T) => T) | (() => T), initialValue?: T): () => T;
29
56
  declare function createRoot<T>(fn: (dispose: () => void) => T): T;
30
57
  type AccessorArray = readonly (() => unknown)[];
31
58
  type OnEffectFunction<D, V> = (input: D, prevInput: D | undefined, prev: V | undefined) => V;
32
- declare function on<S extends (() => unknown) | AccessorArray, V>(deps: S, fn: OnEffectFunction<S extends (() => infer R) ? R : S extends readonly (() => infer R)[] ? R[] : never, V>): () => V | undefined;
59
+ declare function on<S extends (() => unknown) | AccessorArray, V>(deps: S, fn: OnEffectFunction<S extends (() => infer R) ? R : S extends readonly (() => infer R)[] ? R[] : never, V>, options?: {
60
+ defer?: boolean;
61
+ }): () => V | undefined;
33
62
  /**
34
63
  * Solid-compatible `onMount` — runs once after the component's first render.
35
64
  */
@@ -52,8 +81,167 @@ declare function lazy<P extends Props>(loader: () => Promise<{
52
81
  default: ComponentFn<P>;
53
82
  }>;
54
83
  };
84
+ declare const SOLID_CTX: unique symbol;
85
+ /**
86
+ * Solid-compatible context with a Provider component that uses Pyreon's
87
+ * native tree-scoped context stack for proper nesting (inner Provider
88
+ * overrides outer for its subtree).
89
+ */
90
+ interface SolidContext<T> {
91
+ readonly [SOLID_CTX_BRAND]: true;
92
+ readonly id: symbol;
93
+ readonly defaultValue: T | undefined;
94
+ Provider: (props: Record<string, unknown>) => unknown;
95
+ }
96
+ declare const SOLID_CTX_BRAND: typeof SOLID_CTX;
97
+ /**
98
+ * Solid-compatible `createContext` — creates a context with a `.Provider`
99
+ * component. Uses Pyreon's native context stack for tree-scoped nesting.
100
+ */
101
+ declare function createContext<T>(defaultValue?: T): SolidContext<T>;
102
+ /**
103
+ * Solid-compatible `useContext` — reads the nearest provided value for a context.
104
+ * Works with both compat contexts (from this module's `createContext`) and
105
+ * Pyreon native contexts (from `@pyreon/core`).
106
+ */
107
+ declare function useContext<T>(context: SolidContext<T> | Context<T>): T;
55
108
  declare function getOwner(): EffectScope | null;
56
109
  declare function runWithOwner<T>(owner: EffectScope | null, fn: () => T): T;
110
+ /**
111
+ * Solid-compatible resource — async data fetching with reactive source tracking.
112
+ * Returns `[resource, { mutate, refetch }]` where `resource()` is the data accessor
113
+ * with `.loading`, `.error`, and `.latest` reactive properties.
114
+ *
115
+ * When the resource is loading and read inside a Suspense boundary, the accessor
116
+ * throws the fetch promise so Suspense can catch it. It also integrates with
117
+ * Pyreon's `__loading` protocol so `<Suspense>` can detect it.
118
+ */
119
+ interface Resource<T> {
120
+ (): T | undefined;
121
+ loading: boolean;
122
+ error: Error | undefined;
123
+ latest: T | undefined;
124
+ }
125
+ type ResourceReturn<T> = [Resource<T>, {
126
+ mutate: (v: T | ((prev: T | undefined) => T)) => void;
127
+ refetch: () => void;
128
+ }];
129
+ declare function createResource<T>(fetcher: (info: {
130
+ value: T | undefined;
131
+ }) => Promise<T> | T, options?: {
132
+ initialValue?: T;
133
+ }): ResourceReturn<T>;
134
+ declare function createResource<T, S = true>(source: (() => S) | true, fetcher: (source: S, info: {
135
+ value: T | undefined;
136
+ }) => Promise<T> | T, options?: {
137
+ initialValue?: T;
138
+ }): ResourceReturn<T>;
139
+ /**
140
+ * Solid-compatible `createStore` — creates a deeply reactive proxy-based store.
141
+ *
142
+ * Returns `[store, setStore]` where:
143
+ * - `store` is a recursive proxy that lazily creates per-path signals for fine-grained tracking
144
+ * - `setStore` supports Solid's path-based setter API:
145
+ * - `setStore('key', value)` — set a top-level key
146
+ * - `setStore('nested', 'key', value)` — set a nested path
147
+ * - `setStore('key', prev => next)` — functional update at a path
148
+ * - `setStore('todos', 0, 'done', true)` — numeric index into arrays
149
+ * - `setStore('todos', t => t.done, 'text', 'x')` — filter predicate on arrays
150
+ * - `setStore(fn)` — mutator function (receives a draft clone)
151
+ */
152
+ type SetStoreFunction<_T> = {
153
+ (...args: unknown[]): void;
154
+ };
155
+ declare function createStore<T extends object>(initialValue: T): [T, SetStoreFunction<T>];
156
+ /**
157
+ * Solid-compatible `reconcile` — replaces the entire store state with the given value.
158
+ * Used with setStore: `setStore(reconcile(newData))`
159
+ */
160
+ declare function reconcile<T extends object>(value: T): (state: T) => T;
161
+ /**
162
+ * Solid-compatible `unwrap` — returns a deep clone of the store's raw data,
163
+ * stripping the reactive proxy.
164
+ */
165
+ declare function unwrap<T>(value: T): T;
166
+ /**
167
+ * Solid-compatible `produce` — creates an Immer-like updater function for stores.
168
+ * Returns a function that clones the state, applies mutations, and returns the result.
169
+ */
170
+ declare function produce<T extends object>(fn: (state: T) => void): (state: T) => T;
171
+ /**
172
+ * Solid-compatible `startTransition` — runs a function as a transition.
173
+ * In Pyreon, this is a no-op wrapper that calls the function synchronously.
174
+ */
175
+ declare function startTransition(fn: () => void): void;
176
+ /**
177
+ * Solid-compatible `useTransition` — returns `[isPending, startTransition]`.
178
+ * In Pyreon, transitions are not deferred — isPending is always false.
179
+ */
180
+ declare function useTransition(): [() => boolean, (fn: () => void) => void];
181
+ interface Observer<T> {
182
+ next: (v: T) => void;
183
+ }
184
+ interface Subscription {
185
+ unsubscribe: () => void;
186
+ }
187
+ interface Observable<T> {
188
+ subscribe: (observer: Observer<T>) => Subscription;
189
+ }
190
+ /**
191
+ * Solid-compatible `observable` — converts a signal accessor to an observable.
192
+ * Returns an object with a `subscribe` method that tracks signal changes.
193
+ */
194
+ declare function observable<T>(input: () => T): Observable<T>;
195
+ /**
196
+ * Solid-compatible `from` — converts an observable or producer into a signal accessor.
197
+ * Accepts either a producer function `(setter) => cleanup` or an observable with `.subscribe()`.
198
+ */
199
+ declare function from<T>(producer: ((setter: (v: T) => void) => () => void) | Observable<T>): () => T | undefined;
200
+ /**
201
+ * Solid-compatible `mapArray` — maps a reactive list by item identity.
202
+ * Each item is a static value, while the index is a reactive accessor.
203
+ */
204
+ declare function mapArray<T, U>(list: () => readonly T[], mapFn: (item: T, index: () => number) => U): () => U[];
205
+ /**
206
+ * Solid-compatible `indexArray` — maps a reactive list by index position.
207
+ * Each item is a reactive accessor, while the index is a static number.
208
+ */
209
+ declare function indexArray<T, U>(list: () => readonly T[], mapFn: (item: () => T, index: number) => U): () => U[];
210
+ /**
211
+ * Solid-compatible `Index` — like `For` but keyed by index.
212
+ * Items are reactive accessors, indices are static numbers.
213
+ *
214
+ * In Solid, `<Index>` keeps DOM nodes stable per index position.
215
+ * Here we use a computed that maps items to `(item: () => T, index: number)`.
216
+ */
217
+ declare function Index<T>(props: {
218
+ each: readonly T[] | (() => readonly T[]);
219
+ children: (item: () => T, index: number) => VNodeChild;
220
+ }): VNodeChild;
221
+ /**
222
+ * Solid-compatible `createUniqueId` — returns a unique string identifier.
223
+ */
224
+ declare function createUniqueId(): string;
225
+ /**
226
+ * Solid-compatible `DEV` — an object in dev mode, `undefined` in production.
227
+ * Used for conditional dev-only code: `if (DEV) { ... }`
228
+ */
229
+ declare const DEV: {} | undefined;
230
+ /**
231
+ * Solid-compatible `catchError` — wraps a function and catches synchronous errors.
232
+ */
233
+ declare function catchError<T>(tryFn: () => T, onError: (err: Error) => void): T | undefined;
234
+ /**
235
+ * Solid-compatible `createDeferred` — creates a memo that updates on next idle frame.
236
+ * In Pyreon there is no concurrent scheduling, so this behaves the same as `createMemo`.
237
+ */
238
+ declare function createDeferred<T>(fn: () => T): () => T;
239
+ /**
240
+ * Solid-compatible `createReaction` — manual tracking primitive.
241
+ * Returns a function that accepts a tracking function. When any tracked
242
+ * dependency changes, `onInvalidate` fires (but only after the first run).
243
+ */
244
+ declare function createReaction(onInvalidate: () => void): (tracking: () => void) => void;
57
245
  //#endregion
58
- export { ErrorBoundary, For, Match, Show, SignalGetter, SignalSetter, Suspense, Switch, pyreonBatch as batch, children, createEffect as createComputed, createEffect, pyreonCreateContext as createContext, createMemo, createRenderEffect, createRoot, createSelector, createSignal, getOwner, lazy, mergeProps, on, onCleanup, onMount, runWithOwner, splitProps, runUntracked as untrack, pyreonUseContext as useContext };
246
+ export { Accessor, Component, CreateSignalOptions, DEV, ErrorBoundary, FlowComponent, For, Index, Match, Owner, ParentComponent, Resource, ResourceReturn, SetStoreFunction, Setter, Show, Signal, SignalGetter, SignalSetter, SolidContext, Suspense, Switch, VoidComponent, pyreonBatch as batch, catchError, children, createEffect as createComputed, createEffect, createContext, createDeferred, createMemo, createReaction, createRenderEffect, createResource, createRoot, createSelector, createSignal, createStore, createUniqueId, from, getOwner, indexArray, lazy, mapArray, mergeProps, observable, on, onCleanup, onMount, produce, reconcile, runWithOwner, splitProps, startTransition, runUntracked as untrack, unwrap, useContext, useTransition };
59
247
  //# sourceMappingURL=index2.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/solid-compat",
3
- "version": "0.13.1",
3
+ "version": "0.15.0",
4
4
  "description": "SolidJS-compatible API shim for Pyreon — write Solid-style code that runs on Pyreon's reactive engine",
5
5
  "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/solid-compat#readme",
6
6
  "bugs": {
@@ -14,6 +14,7 @@
14
14
  },
15
15
  "files": [
16
16
  "lib",
17
+ "!lib/**/*.map",
17
18
  "src",
18
19
  "README.md",
19
20
  "LICENSE"
@@ -47,17 +48,20 @@
47
48
  "build": "vl_rolldown_build",
48
49
  "dev": "vl_rolldown_build-watch",
49
50
  "test": "vitest run",
51
+ "test:browser": "vitest run --config ./vitest.browser.config.ts",
50
52
  "typecheck": "tsc --noEmit",
51
53
  "lint": "oxlint .",
52
54
  "prepublishOnly": "bun run build"
53
55
  },
54
56
  "dependencies": {
55
- "@pyreon/core": "^0.13.1",
56
- "@pyreon/reactivity": "^0.13.1",
57
- "@pyreon/runtime-dom": "^0.13.1"
57
+ "@pyreon/core": "^0.15.0",
58
+ "@pyreon/reactivity": "^0.15.0",
59
+ "@pyreon/runtime-dom": "^0.15.0"
58
60
  },
59
61
  "devDependencies": {
60
62
  "@happy-dom/global-registrator": "^20.8.9",
63
+ "@pyreon/test-utils": "^0.13.2",
64
+ "@vitest/browser-playwright": "^4.1.4",
61
65
  "happy-dom": "^20.8.3"
62
66
  }
63
67
  }
package/src/env.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Minimal process type — just enough for `process.env.NODE_ENV` checks.
3
+ * Avoids requiring @types/node in consumers that import pyreon source
4
+ * via the `"bun"` export condition.
5
+ */
6
+ declare var process: { env: { NODE_ENV?: string } }