@sigx/lynx-runtime 0.2.4
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.
- package/LICENSE +21 -0
- package/README.md +42 -0
- package/dist/animated/animated-value.d.ts +16 -0
- package/dist/animated/shared-value.d.ts +67 -0
- package/dist/animated/use-animated-style.d.ts +37 -0
- package/dist/animated-bridge.d.ts +45 -0
- package/dist/bg-bridge.d.ts +29 -0
- package/dist/event-registry.d.ts +37 -0
- package/dist/flush.d.ts +8 -0
- package/dist/hmr.d.ts +35 -0
- package/dist/hmr.js +38 -0
- package/dist/hmr.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +849 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx.d.ts +340 -0
- package/dist/main-thread-ref.d.ts +88 -0
- package/dist/model-processor.d.ts +24 -0
- package/dist/mt-hmr-bridge.d.ts +47 -0
- package/dist/mt-hmr-bridge.js +58 -0
- package/dist/mt-hmr-bridge.js.map +1 -0
- package/dist/native/gesture-detector.d.ts +151 -0
- package/dist/native/index.d.ts +2 -0
- package/dist/nodeOps.d.ts +14 -0
- package/dist/op-queue.d.ts +55 -0
- package/dist/render.d.ts +22 -0
- package/dist/run-on-background.d.ts +37 -0
- package/dist/shadow-element.d.ts +30 -0
- package/dist/threading.d.ts +41 -0
- package/dist/types.d.ts +17 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Andreas Ekdahl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @sigx/lynx-runtime
|
|
2
|
+
|
|
3
|
+
Background-thread renderer for [SignalX](https://github.com/signalxjs) on Lynx. Translates sigx component output into the BG → MT op stream that drives the native render tree.
|
|
4
|
+
|
|
5
|
+
> Most apps should depend on [`@sigx/lynx`](../lynx) instead, which re-exports this package's public surface alongside `@sigx/reactivity` and `@sigx/runtime-core` for a single import path.
|
|
6
|
+
|
|
7
|
+
## Responsibilities
|
|
8
|
+
|
|
9
|
+
- **`render` / `lynxMount`** — boot the BG renderer against a `lynx.getJSContext()`-style host.
|
|
10
|
+
- **`nodeOps`** — sigx `RuntimeRenderer` adapter that turns vnode operations into op-queue entries.
|
|
11
|
+
- **Op queue** — `pushOp`, `scheduleFlush`, `takeOps`, `flushNow` — the wire protocol carrying renders from BG to MT.
|
|
12
|
+
- **Main-thread refs** — `MainThreadRef`, `useMainThreadRef` — the BG-side handle whose `.current` value lives on the main thread; the build pipeline serializes these into worklet captures via their `_wvid`.
|
|
13
|
+
- **Cross-thread bridges** — `runOnMainThread` (BG→MT one-shot), `runOnBackground` (MT→BG dispatch handle), `transformToWorklet` (handle → JsFn marshal).
|
|
14
|
+
- **AnimatedValue BG sink** — `registerBgSink`, `unregisterBgSink`, `ingestAvPublishes` — receive MT-published `AnimatedValue` writes into a `signal`-backed mirror so `effect(() => av.value)` re-runs reactively. The producer side lives in [`@sigx/gestures`](../gestures); the MT side lives in [`@sigx/lynx-runtime-main`](../lynx-runtime-main).
|
|
15
|
+
- **JSX types** — `MainThread`, `Define`, `ViewAttributes`, etc.
|
|
16
|
+
|
|
17
|
+
## Wire protocol
|
|
18
|
+
|
|
19
|
+
Ops are flat-array tuples produced on BG and consumed on MT. The op codes (`CREATE`, `INSERT`, `SET_STYLE`, `SET_WORKLET_EVENT`, `INIT_MT_REF`, `REGISTER_AV_BRIDGE`, ...) are defined in [`@sigx/lynx-runtime-internal`](../lynx-runtime-internal) so both sides stay in sync.
|
|
20
|
+
|
|
21
|
+
A typical batch:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
[OP.CREATE, 1, 'view',
|
|
25
|
+
OP.SET_STYLE, 1, { width: '100px', height: '100px' },
|
|
26
|
+
OP.INSERT, 0, 1, -1,
|
|
27
|
+
OP.INIT_MT_REF, 7, null,
|
|
28
|
+
OP.SET_MT_REF, 1, 7]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Serialized to JSON, shipped via `lynx.getNativeApp().callLepusMethod('sigxPatchUpdate', { data })`, applied by the MT runtime in `@sigx/lynx-runtime-main`.
|
|
32
|
+
|
|
33
|
+
## Background event bridge
|
|
34
|
+
|
|
35
|
+
`bg-bridge.ts` listens on `lynx.getCoreContext()` for two MT-originated event types:
|
|
36
|
+
|
|
37
|
+
- `Lynx.Sigx.PublishEvent` — hybrid worklet → BG event handler dispatch (`<view bindtap={…}>` style).
|
|
38
|
+
- `Lynx.Sigx.AvPublish` — coalesced `AnimatedValue` write batches; routed into `ingestAvPublishes`.
|
|
39
|
+
|
|
40
|
+
## License
|
|
41
|
+
|
|
42
|
+
MIT
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated since Phase 2.8 — renamed to `SharedValue` to reflect that the
|
|
3
|
+
* primitive is a general MT-writeable, BG-observable cross-thread value, not
|
|
4
|
+
* an animation-specific construct. Animation is one customer; scroll, sensors,
|
|
5
|
+
* and gestures are equally first-class consumers.
|
|
6
|
+
*
|
|
7
|
+
* Import from `@sigx/lynx` directly:
|
|
8
|
+
*
|
|
9
|
+
* - `useAnimatedValue` → `useSharedValue`
|
|
10
|
+
* - `AnimatedValue` → `SharedValue`
|
|
11
|
+
* - `AnimatedValueState` → `SharedValueState`
|
|
12
|
+
*
|
|
13
|
+
* The old names continue to work via these re-exports for one minor cycle.
|
|
14
|
+
*/
|
|
15
|
+
export { SharedValue as AnimatedValue, useSharedValue as useAnimatedValue, } from './shared-value.js';
|
|
16
|
+
export type { SharedValueState as AnimatedValueState } from './shared-value.js';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { PrimitiveSignal } from '@sigx/reactivity';
|
|
2
|
+
import { MainThreadRef } from '../main-thread-ref.js';
|
|
3
|
+
/**
|
|
4
|
+
* Internal envelope shape stored under `MainThreadRef.current`. Wrapping the
|
|
5
|
+
* value in `{ value: T }` (rather than a bare `T`) lets MT worklets mutate
|
|
6
|
+
* the value without breaking ref identity, and leaves room to extend with
|
|
7
|
+
* derived state later (e.g. velocity) without changing the public type.
|
|
8
|
+
*/
|
|
9
|
+
export interface SharedValueState<T> {
|
|
10
|
+
value: T;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* MT-writeable, BG-readable cross-thread value with sigx reactive tracking.
|
|
14
|
+
*
|
|
15
|
+
* **Not animation-specific.** Animation is one customer (see `@sigx/motion`),
|
|
16
|
+
* gestures is another (`<Draggable translateX={sv} />`), scroll is another
|
|
17
|
+
* (`useScrollViewOffset`). Anywhere fast-or-frequent state lives natively on
|
|
18
|
+
* MT and you want BG to observe it reactively, this is the primitive.
|
|
19
|
+
*
|
|
20
|
+
* **MT side** — inside `'main thread'` worklets, mutate via `sv.current.value
|
|
21
|
+
* = newValue`. The MainThreadRef machinery makes the mutation stick across
|
|
22
|
+
* worklet invocations on the same wvid; the bridge picks up the new value on
|
|
23
|
+
* the next `__FlushElementTree` boundary.
|
|
24
|
+
*
|
|
25
|
+
* **BG side** — read via `sv.value` to get the latest published snapshot.
|
|
26
|
+
* Sigx tracking applies, so an `effect(() => console.log(sv.value))` re-runs
|
|
27
|
+
* whenever an MT publish ingests a new value. Writes from BG are read-only;
|
|
28
|
+
* dev-mode warns to point users back at the MT path.
|
|
29
|
+
*
|
|
30
|
+
* The class extends `MainThreadRef<{value:T}>` so that BG-side serialization
|
|
31
|
+
* (`nodeOps.ts:sanitizeCaptured`) recognizes it as a worklet ref and ships
|
|
32
|
+
* `{_wvid, _initValue}` over the wire to the MT bundle.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* const tx = useSharedValue(0);
|
|
37
|
+
*
|
|
38
|
+
* <Draggable translateX={tx} />
|
|
39
|
+
* <text>x = {tx.value}</text> // BG-reactive, updates per drag frame
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare class SharedValue<T = number> extends MainThreadRef<SharedValueState<T>> {
|
|
43
|
+
/** @internal — populated by `useSharedValue`. Reads here back the BG signal. */
|
|
44
|
+
private _bgSignal;
|
|
45
|
+
constructor(initial: T);
|
|
46
|
+
/** @internal */
|
|
47
|
+
_bind(sig: PrimitiveSignal<T>): void;
|
|
48
|
+
/**
|
|
49
|
+
* BG-side reactive read. Returns the latest snapshot published by the MT
|
|
50
|
+
* bridge (or the initial value before any publish has occurred).
|
|
51
|
+
*/
|
|
52
|
+
get value(): T;
|
|
53
|
+
set value(_v: T);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Allocate a `SharedValue<T>` whose MT-side mutations are observable on the
|
|
57
|
+
* BG thread via sigx reactivity. Bind it to a component prop or read its
|
|
58
|
+
* `.value` from a BG `effect`.
|
|
59
|
+
*
|
|
60
|
+
* Lifecycle:
|
|
61
|
+
* - On allocate: pushes `INIT_MT_REF` (creates the MT-side refMap holder)
|
|
62
|
+
* and `REGISTER_AV_BRIDGE` (tells MT to track this wvid in its diff/
|
|
63
|
+
* publish pass). Allocates a BG-side signal mirror keyed by wvid.
|
|
64
|
+
* - On the owning component's unmount: pushes `UNREGISTER_AV_BRIDGE` and
|
|
65
|
+
* `RELEASE_MT_REF`, drops the BG signal.
|
|
66
|
+
*/
|
|
67
|
+
export declare function useSharedValue<T = number>(initial: T): SharedValue<T>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { MainThreadRef } from '../main-thread-ref.js';
|
|
2
|
+
import type { MainThread } from '../jsx.js';
|
|
3
|
+
import type { BuiltinMapperName, MapperParams } from '@sigx/lynx-runtime-internal';
|
|
4
|
+
import type { SharedValue } from './shared-value.js';
|
|
5
|
+
export type { BuiltinMapperName, MapperParams };
|
|
6
|
+
/** Reset hook for tests. */
|
|
7
|
+
export declare function resetAnimatedStyleBindingIds(): void;
|
|
8
|
+
/**
|
|
9
|
+
* Bind a `MainThreadRef`'s element style to a `SharedValue` via a named
|
|
10
|
+
* mapper. The MT-side runtime applies the mapper's output via
|
|
11
|
+
* `setStyleProperties` on every flush boundary where the SharedValue's value changed.
|
|
12
|
+
*
|
|
13
|
+
* The mapper runs on the **Main Thread** with no thread crossing per frame —
|
|
14
|
+
* the only inputs are the SharedValue's value (already on MT) and the `params` shipped
|
|
15
|
+
* once in the registration op. Because mappers are looked up by *name*, the
|
|
16
|
+
* SWC worklet transform doesn't have to capture a function reference (which
|
|
17
|
+
* it can't), so this fits the existing worklet pipeline cleanly.
|
|
18
|
+
*
|
|
19
|
+
* Multiple bindings on the same element compose into a single
|
|
20
|
+
* `setStyleProperties` call per flush. `transform` outputs concatenate in
|
|
21
|
+
* registration order (e.g. `translateX(50px) translateY(20px)`); other style
|
|
22
|
+
* keys merge by last-write-wins. When ANY binding on an element is dirty,
|
|
23
|
+
* ALL of that element's bindings re-run so partial outputs don't drop the
|
|
24
|
+
* unchanged-axis contribution.
|
|
25
|
+
*
|
|
26
|
+
* @example Ghost follower at 0.5×
|
|
27
|
+
* ```tsx
|
|
28
|
+
* const tx = useSharedValue(0);
|
|
29
|
+
* const ghostRef = useMainThreadRef<MainThread.Element | null>(null);
|
|
30
|
+
*
|
|
31
|
+
* useAnimatedStyle(ghostRef, tx, 'translateX', { factor: 0.5 });
|
|
32
|
+
*
|
|
33
|
+
* <Draggable translateX={tx} />
|
|
34
|
+
* <view main-thread:ref={ghostRef} style={...} />
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function useAnimatedStyle<N extends BuiltinMapperName>(elRef: MainThreadRef<MainThread.Element | null>, sv: SharedValue<unknown>, mapperName: N, params?: MapperParams[N]): void;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BG-side SharedValue bridge — receives MT-thread publishes.
|
|
3
|
+
*
|
|
4
|
+
* Maintains a `wvid → signal<T>` registry. The MT-side bridge in
|
|
5
|
+
* `@sigx/lynx-runtime-main/animated-bridge-mt.ts` dispatches batched
|
|
6
|
+
* `Lynx.Sigx.AvPublish` events with `[wvid, value]` tuples; `bg-bridge.ts`
|
|
7
|
+
* routes them through `ingestAvPublishes()` here, which writes each new
|
|
8
|
+
* value into the corresponding signal. Sigx reactivity tracking then re-runs
|
|
9
|
+
* any `effect` that read `sv.value` on BG.
|
|
10
|
+
*
|
|
11
|
+
* Producer: the `useSharedValue` hook in `@sigx/lynx` allocates the
|
|
12
|
+
* BG-side signal via `registerBgSink(wvid, initial)` and tears it down via
|
|
13
|
+
* `unregisterBgSink(wvid)` on component unmount.
|
|
14
|
+
*
|
|
15
|
+
* Naming note: the wire-format ops (`REGISTER_AV_BRIDGE`, `UNREGISTER_AV_BRIDGE`,
|
|
16
|
+
* `Lynx.Sigx.AvPublish`) keep their original `Av` prefix as infrastructure
|
|
17
|
+
* constants. The user-facing primitive renamed from `AnimatedValue` to
|
|
18
|
+
* `SharedValue` in Phase 2.8.
|
|
19
|
+
*/
|
|
20
|
+
import { type PrimitiveSignal } from '@sigx/reactivity';
|
|
21
|
+
/**
|
|
22
|
+
* Allocate a BG-side signal mirror for the given wvid. Returns the signal so
|
|
23
|
+
* the caller can read it via `.value` — sigx tracking applies as usual.
|
|
24
|
+
*
|
|
25
|
+
* Idempotent on the wvid: if a signal is already registered, returns the
|
|
26
|
+
* existing one without resetting its value (avoids losing in-flight publishes
|
|
27
|
+
* during HMR-triggered re-registration).
|
|
28
|
+
*/
|
|
29
|
+
export declare function registerBgSink<T>(wvid: number, initial: T): PrimitiveSignal<T>;
|
|
30
|
+
/**
|
|
31
|
+
* Drop the BG-side signal for this wvid. Called on component unmount.
|
|
32
|
+
* Subsequent `ingestPublish` entries for this wvid become no-ops.
|
|
33
|
+
*/
|
|
34
|
+
export declare function unregisterBgSink(wvid: number): void;
|
|
35
|
+
/**
|
|
36
|
+
* Ingest a batch of `[wvid, value]` tuples from the MT bridge. Writes each
|
|
37
|
+
* into the corresponding signal so any `effect` that read it re-runs on the
|
|
38
|
+
* sigx scheduler's next tick. Tuples for unregistered wvids (race with
|
|
39
|
+
* unmount) are silently dropped.
|
|
40
|
+
*/
|
|
41
|
+
export declare function ingestAvPublishes(updates: Array<[number, unknown]>): void;
|
|
42
|
+
/** Reset hook — for testing. Drops every registered sink. */
|
|
43
|
+
export declare function resetBgAvBridge(): void;
|
|
44
|
+
/** Test hook — number of currently registered sinks. */
|
|
45
|
+
export declare function bgAvSinkCount(): number;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BG-side listener for the MT→BG `Lynx.Sigx.PublishEvent` channel.
|
|
3
|
+
*
|
|
4
|
+
* The MT-side hybrid worklet (`lynx-runtime-main/src/hybrid-worklet.ts`) calls
|
|
5
|
+
* `lynx.getJSContext().dispatchEvent({ type: 'Lynx.Sigx.PublishEvent', data })`
|
|
6
|
+
* to fire the BG handler whose sign is captured in the hybrid ctx. We listen
|
|
7
|
+
* for that event here and route through the existing event-registry's
|
|
8
|
+
* `publishEvent` — the same dispatcher Lynx native calls when a normal
|
|
9
|
+
* `bindtap` fires on BG. The user's BG handler runs in the same call-stack
|
|
10
|
+
* shape it always has, so signal updates / `count.value++` etc. work without
|
|
11
|
+
* any awareness that the trigger came from MT.
|
|
12
|
+
*
|
|
13
|
+
* Side-effect import from `index.ts` so the listener is wired before any
|
|
14
|
+
* user code runs.
|
|
15
|
+
*
|
|
16
|
+
* CROSS-THREAD ASYMMETRY (per @lynx-js/react/runtime/lib/worklet/call/runOnBackground.js):
|
|
17
|
+
* - MT → BG dispatch: MT calls `lynx.getJSContext().dispatchEvent(...)`,
|
|
18
|
+
* BG listens via `lynx.getCoreContext().addEventListener(...)`.
|
|
19
|
+
* - BG → MT dispatch: BG calls `lynx.getCoreContext().dispatchEvent(...)`,
|
|
20
|
+
* MT listens via `lynx.getJSContext().addEventListener(...)`.
|
|
21
|
+
* Each side calls a DIFFERENT method to reach the other thread — they're not
|
|
22
|
+
* symmetric. Listening on `lynx.getJSContext()` from BG just listens on BG's
|
|
23
|
+
* own context (no cross-thread events arrive).
|
|
24
|
+
*
|
|
25
|
+
* `lynx` is closure-injected by RuntimeWrapperWebpackPlugin (declared in
|
|
26
|
+
* shims.d.ts). It is NOT available as `globalThis.lynx` — use the free
|
|
27
|
+
* identifier directly.
|
|
28
|
+
*/
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign-based event handler registry for the Background Thread.
|
|
3
|
+
*
|
|
4
|
+
* When patchProp registers an event handler, it gets a unique sign string.
|
|
5
|
+
* The Main Thread stores this sign with __AddEvent(). When Native fires an
|
|
6
|
+
* event it calls publishEvent(sign, data) on the BG Thread, which looks up
|
|
7
|
+
* the handler and executes it directly — no cross-thread round-trip.
|
|
8
|
+
*
|
|
9
|
+
* sigx uses signal closures, so event handlers are plain functions captured by the component's
|
|
10
|
+
* render closure.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Register a handler and return a unique sign string.
|
|
14
|
+
* The sign is passed to __AddEvent so that the MTS can route events back.
|
|
15
|
+
*/
|
|
16
|
+
export declare function register(handler: (data: unknown) => void): string;
|
|
17
|
+
/**
|
|
18
|
+
* Update the handler for an existing sign without changing the sign.
|
|
19
|
+
* Used on re-renders: keeps the same sign registered on the Main Thread
|
|
20
|
+
* while pointing it to the freshest handler closure.
|
|
21
|
+
*/
|
|
22
|
+
export declare function updateHandler(sign: string, handler: (data: unknown) => void): void;
|
|
23
|
+
/**
|
|
24
|
+
* Get the current handler for a sign.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getHandler(sign: string): ((data: unknown) => void) | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Unregister a handler by its sign.
|
|
29
|
+
*/
|
|
30
|
+
export declare function unregister(sign: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Called by Lynx Native when an event fires on the BG Thread.
|
|
33
|
+
* Looks up the handler by sign and invokes it with the event data.
|
|
34
|
+
*/
|
|
35
|
+
export declare function publishEvent(sign: string, data: unknown): void;
|
|
36
|
+
/** Reset all state — for testing only. */
|
|
37
|
+
export declare function resetRegistry(): void;
|
package/dist/flush.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flush scheduler — re-exports from op-queue.ts.
|
|
3
|
+
*
|
|
4
|
+
* In the new architecture, the BG thread no longer calls __FlushElementTree
|
|
5
|
+
* directly. Instead, ops are batched in a queue and flushed to the Main Thread
|
|
6
|
+
* via sigxPatchUpdate. This file exists for backwards-compatible imports.
|
|
7
|
+
*/
|
|
8
|
+
export { scheduleFlush, flushNow, resetOpQueue as resetFlushState } from './op-queue.js';
|
package/dist/hmr.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR runtime for sigx-lynx (rspack/rsbuild).
|
|
3
|
+
*
|
|
4
|
+
* The `@sigx/lynx` meta package inlines runtime-core into a single bundle,
|
|
5
|
+
* so `@sigx/runtime-core/internals` would resolve to a *different* copy of
|
|
6
|
+
* the plugin registry than the one used by `component()`. To work around
|
|
7
|
+
* this the HMR loader injects a call to `initHMR(registerComponentPlugin)`
|
|
8
|
+
* where `registerComponentPlugin` is imported from `@sigx/lynx` (the same
|
|
9
|
+
* bundle the app uses), ensuring a single shared plugin array.
|
|
10
|
+
*
|
|
11
|
+
* Flow:
|
|
12
|
+
* 1. Loader prepends:
|
|
13
|
+
* import { __registerComponentPlugin } from '@sigx/lynx';
|
|
14
|
+
* import { initHMR, registerHMRModule } from '@sigx/lynx-runtime/hmr';
|
|
15
|
+
* initHMR(__registerComponentPlugin);
|
|
16
|
+
* registerHMRModule('<moduleId>');
|
|
17
|
+
* 2. On first call, `initHMR` installs the onDefine plugin.
|
|
18
|
+
* 3. On HMR update the module re-executes, `registerHMRModule` resets
|
|
19
|
+
* the per-module index, `component()` fires `onDefine`, and existing
|
|
20
|
+
* instances are patched in-place (property-ops only — no crash).
|
|
21
|
+
*/
|
|
22
|
+
type RegisterFn = (plugin: {
|
|
23
|
+
onDefine?: (name: string | undefined, factory: any, setup: Function) => void;
|
|
24
|
+
}) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Initialise the HMR plugin using the *app-side* registerComponentPlugin.
|
|
27
|
+
* Called once by the loader-injected preamble. Idempotent.
|
|
28
|
+
*/
|
|
29
|
+
export declare function initHMR(registerComponentPlugin: RegisterFn): void;
|
|
30
|
+
/**
|
|
31
|
+
* Register the current module for HMR tracking.
|
|
32
|
+
* Called at the top of each transformed module by the HMR loader.
|
|
33
|
+
*/
|
|
34
|
+
export declare function registerHMRModule(moduleId: string): void;
|
|
35
|
+
export {};
|
package/dist/hmr.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/hmr.ts
|
|
2
|
+
var e = /* @__PURE__ */ new Map(), t = /* @__PURE__ */ new Map(), n = null, r = !1;
|
|
3
|
+
function i(t) {
|
|
4
|
+
r || (r = !0, t({ onDefine(t, n, r) {
|
|
5
|
+
let i = o();
|
|
6
|
+
if (!i) return;
|
|
7
|
+
n.__hmrId = i;
|
|
8
|
+
let a = e.get(i);
|
|
9
|
+
a && a.size > 0 && a.forEach((e) => {
|
|
10
|
+
try {
|
|
11
|
+
let t = r(e.ctx);
|
|
12
|
+
e.ctx.renderFn = t, e.ctx.update();
|
|
13
|
+
} catch (e) {
|
|
14
|
+
console.error(`[sigx-hmr] Failed to update ${t || "component"}:`, e);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
let s = r;
|
|
18
|
+
n.__setup = (t) => {
|
|
19
|
+
let n = s(t), r = { ctx: t }, a = e.get(i);
|
|
20
|
+
return a || (a = /* @__PURE__ */ new Set(), e.set(i, a)), a.add(r), t.onUnmounted(() => {
|
|
21
|
+
let t = e.get(i);
|
|
22
|
+
t && t.delete(r);
|
|
23
|
+
}), n;
|
|
24
|
+
};
|
|
25
|
+
} }));
|
|
26
|
+
}
|
|
27
|
+
function a(e) {
|
|
28
|
+
n = e, t.set(e, 0);
|
|
29
|
+
}
|
|
30
|
+
function o() {
|
|
31
|
+
if (!n) return null;
|
|
32
|
+
let e = t.get(n) || 0;
|
|
33
|
+
return t.set(n, e + 1), `${n}:${e}`;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { i as initHMR, a as registerHMRModule };
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=hmr.js.map
|
package/dist/hmr.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hmr.js","names":[],"sources":["../src/hmr.ts"],"sourcesContent":["/**\n * HMR runtime for sigx-lynx (rspack/rsbuild).\n *\n * The `@sigx/lynx` meta package inlines runtime-core into a single bundle,\n * so `@sigx/runtime-core/internals` would resolve to a *different* copy of\n * the plugin registry than the one used by `component()`. To work around\n * this the HMR loader injects a call to `initHMR(registerComponentPlugin)`\n * where `registerComponentPlugin` is imported from `@sigx/lynx` (the same\n * bundle the app uses), ensuring a single shared plugin array.\n *\n * Flow:\n * 1. Loader prepends:\n * import { __registerComponentPlugin } from '@sigx/lynx';\n * import { initHMR, registerHMRModule } from '@sigx/lynx-runtime/hmr';\n * initHMR(__registerComponentPlugin);\n * registerHMRModule('<moduleId>');\n * 2. On first call, `initHMR` installs the onDefine plugin.\n * 3. On HMR update the module re-executes, `registerHMRModule` resets\n * the per-module index, `component()` fires `onDefine`, and existing\n * instances are patched in-place (property-ops only — no crash).\n */\n\n// Type-only import — NOT a runtime import from runtime-core\nimport type { ComponentSetupContext } from '@sigx/runtime-core/internals';\n\ntype RegisterFn = (plugin: { onDefine?: (name: string | undefined, factory: any, setup: Function) => void }) => void;\n\ninterface InstanceEntry {\n ctx: ComponentSetupContext;\n}\n\n// Track instances by component ID (moduleId:index)\nconst instancesByComponentId = new Map<string, Set<InstanceEntry>>();\n\n// Track component definition order within each module\nconst moduleComponentIndex = new Map<string, number>();\n\n// Current module being registered\nlet currentModuleId: string | null = null;\n\nlet installed = false;\n\n/**\n * Initialise the HMR plugin using the *app-side* registerComponentPlugin.\n * Called once by the loader-injected preamble. Idempotent.\n */\nexport function initHMR(registerComponentPlugin: RegisterFn): void {\n if (installed) return;\n installed = true;\n\n registerComponentPlugin({\n onDefine(name: string | undefined, factory: any, setup: Function) {\n const componentId = getNextComponentId();\n if (!componentId) return;\n\n factory.__hmrId = componentId;\n\n const existingInstances = instancesByComponentId.get(componentId);\n\n if (existingInstances && existingInstances.size > 0) {\n // HMR update: patch all existing instances with the new setup\n existingInstances.forEach(instance => {\n try {\n const newRenderFn = setup(instance.ctx);\n instance.ctx.renderFn = newRenderFn;\n instance.ctx.update();\n } catch (e) {\n console.error(`[sigx-hmr] Failed to update ${name || 'component'}:`, e);\n }\n });\n }\n\n // Wrap setup to track future instances\n const originalSetup = setup;\n\n factory.__setup = (ctx: ComponentSetupContext) => {\n const renderFn = originalSetup(ctx);\n\n const instance: InstanceEntry = { ctx };\n\n let instances = instancesByComponentId.get(componentId);\n if (!instances) {\n instances = new Set();\n instancesByComponentId.set(componentId, instances);\n }\n instances.add(instance);\n\n ctx.onUnmounted(() => {\n const instances = instancesByComponentId.get(componentId);\n if (instances) instances.delete(instance);\n });\n\n return renderFn;\n };\n }\n });\n}\n\n/**\n * Register the current module for HMR tracking.\n * Called at the top of each transformed module by the HMR loader.\n */\nexport function registerHMRModule(moduleId: string): void {\n currentModuleId = moduleId;\n moduleComponentIndex.set(moduleId, 0);\n}\n\n/**\n * Get the next component ID for the current module.\n */\nfunction getNextComponentId(): string | null {\n if (!currentModuleId) return null;\n\n const index = moduleComponentIndex.get(currentModuleId) || 0;\n moduleComponentIndex.set(currentModuleId, index + 1);\n\n return `${currentModuleId}:${index}`;\n}\n"],"mappings":";AAgCA,IAAM,oBAAyB,IAAI,KAAiC,EAG9D,oBAAuB,IAAI,KAAqB,EAGlD,IAAiC,MAEjC,IAAY;AAMhB,SAAgB,EAAQ,GAA2C;CAC3D,MACJ,IAAY,IAEZ,EAAwB,EACpB,SAAS,GAA0B,GAAc,GAAiB;EAC9D,IAAM,IAAc,GAAoB;EACxC,IAAI,CAAC,GAAa;EAElB,EAAQ,UAAU;EAElB,IAAM,IAAoB,EAAuB,IAAI,EAAY;EAEjE,AAAI,KAAqB,EAAkB,OAAO,KAE9C,EAAkB,SAAQ,MAAY;GAClC,IAAI;IACA,IAAM,IAAc,EAAM,EAAS,IAAI;IAEvC,AADA,EAAS,IAAI,WAAW,GACxB,EAAS,IAAI,QAAQ;YAChB,GAAG;IACR,QAAQ,MAAM,+BAA+B,KAAQ,YAAY,IAAI,EAAE;;IAE7E;EAIN,IAAM,IAAgB;EAEtB,EAAQ,WAAW,MAA+B;GAC9C,IAAM,IAAW,EAAc,EAAI,EAE7B,IAA0B,EAAE,QAAK,EAEnC,IAAY,EAAuB,IAAI,EAAY;GAYvD,OAXK,MACD,oBAAY,IAAI,KAAK,EACrB,EAAuB,IAAI,GAAa,EAAU,GAEtD,EAAU,IAAI,EAAS,EAEvB,EAAI,kBAAkB;IAClB,IAAM,IAAY,EAAuB,IAAI,EAAY;IACzD,AAAI,KAAW,EAAU,OAAO,EAAS;KAC3C,EAEK;;IAGlB,CAAC;;AAON,SAAgB,EAAkB,GAAwB;CAEtD,AADA,IAAkB,GAClB,EAAqB,IAAI,GAAU,EAAE;;AAMzC,SAAS,IAAoC;CACzC,IAAI,CAAC,GAAiB,OAAO;CAE7B,IAAM,IAAQ,EAAqB,IAAI,EAAgB,IAAI;CAG3D,OAFA,EAAqB,IAAI,GAAiB,IAAQ,EAAE,EAE7C,GAAG,EAAgB,GAAG"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import './jsx.js';
|
|
2
|
+
import './types.js';
|
|
3
|
+
import './model-processor.js';
|
|
4
|
+
import './bg-bridge.js';
|
|
5
|
+
import './run-on-background.js';
|
|
6
|
+
export { render, lynxMount } from './render.js';
|
|
7
|
+
export { nodeOps } from './nodeOps.js';
|
|
8
|
+
export type { LynxNode, LynxElement } from './nodeOps.js';
|
|
9
|
+
export { ShadowElement, createPageRoot, resetShadowState } from './shadow-element.js';
|
|
10
|
+
export { pushOp, takeOps, scheduleFlush, flushNow, resetOpQueue } from './op-queue.js';
|
|
11
|
+
export { OP } from '@sigx/lynx-runtime-internal';
|
|
12
|
+
export type { OpCode, MapperParams, RangeParams, BuiltinMapperName, AnimatedStyleMapper, } from '@sigx/lynx-runtime-internal';
|
|
13
|
+
export { register, updateHandler, unregister, getHandler, publishEvent, resetRegistry, } from './event-registry.js';
|
|
14
|
+
export { MainThreadRef, useMainThreadRef, resetWvidCounter, } from './main-thread-ref.js';
|
|
15
|
+
export { registerBgSink, unregisterBgSink, ingestAvPublishes, resetBgAvBridge, bgAvSinkCount, } from './animated-bridge.js';
|
|
16
|
+
export { useSharedValue, SharedValue, } from './animated/shared-value.js';
|
|
17
|
+
export type { SharedValueState } from './animated/shared-value.js';
|
|
18
|
+
export { useAnimatedStyle, resetAnimatedStyleBindingIds, } from './animated/use-animated-style.js';
|
|
19
|
+
export { useAnimatedValue, AnimatedValue, } from './animated/animated-value.js';
|
|
20
|
+
export type { AnimatedValueState } from './animated/animated-value.js';
|
|
21
|
+
export { runOnMainThread, runOnBackground, resetThreading, transformToWorklet, resetRunOnBackgroundState, } from './threading.js';
|
|
22
|
+
export { Gesture, GestureType, useGestureDetector, resetGestureIdCounter, } from './native/index.js';
|
|
23
|
+
export type { GestureTypeValue, GestureWorklet, GestureCallback, BaseGesture, ComposedGesture, AnyGesture, } from './native/index.js';
|
|
24
|
+
export type { LynxEventHandler, LynxCommonAttributes, ViewAttributes, TextAttributes, ImageAttributes, ScrollViewAttributes, ListAttributes, ListItemAttributes, InputAttributes, TextAreaAttributes, PageAttributes, SvgAttributes, FilterImageAttributes, MainThread, } from './jsx.js';
|