@sigrea/vue 0.1.0 → 0.2.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.
- package/README.md +135 -51
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -1,29 +1,44 @@
|
|
|
1
1
|
# @sigrea/vue
|
|
2
2
|
|
|
3
|
-
`@sigrea/vue` adapts [@sigrea/core](https://www.npmjs.com/package/@sigrea/core) logic modules and signals
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
`@sigrea/vue` adapts [@sigrea/core](https://www.npmjs.com/package/@sigrea/core) logic modules and signals for Vue 3's Composition API. It aligns lifecycle scopes with component lifecycles, preserves deep reactivity, and provides composables for `<script setup>` and traditional setup functions.
|
|
4
|
+
|
|
5
|
+
- **Signal subscriptions.** `useSignal` subscribes to signals and computed values, returning a readonly ref that updates when they change.
|
|
6
|
+
- **Computed subscriptions.** `useComputed` subscribes to computed values, mirroring Vue's `computed` while tracking through Sigrea scopes.
|
|
7
|
+
- **Deep signal subscriptions.** `useDeepSignal` subscribes to deep signal objects and exposes them as mutable refs with automatic cleanup.
|
|
8
|
+
- **Two-way bindings.** `useMutableSignal` wraps primitive signals as `WritableComputedRef` for two-way bindings like `v-model`.
|
|
9
|
+
- **Logic lifecycles.** `useLogic` mounts logic factories and binds their lifecycles to Vue components.
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Install](#install)
|
|
14
|
+
- [Quick Start](#quick-start)
|
|
15
|
+
- [Consume a Signal](#consume-a-signal)
|
|
16
|
+
- [Bridge Framework-Agnostic Logic](#bridge-framework-agnostic-logic)
|
|
17
|
+
- [Bind Writable Primitive Signals](#bind-writable-primitive-signals)
|
|
18
|
+
- [Bind Deep Reactive Objects](#bind-deep-reactive-objects)
|
|
19
|
+
- [API Reference](#api-reference)
|
|
20
|
+
- [useSignal](#usesignal)
|
|
21
|
+
- [useComputed](#usecomputed)
|
|
22
|
+
- [useDeepSignal](#usedeepsignal)
|
|
23
|
+
- [useMutableSignal](#usemutablesignal)
|
|
24
|
+
- [useLogic](#uselogic)
|
|
25
|
+
- [Testing](#testing)
|
|
26
|
+
- [Development](#development)
|
|
27
|
+
- [License](#license)
|
|
28
|
+
|
|
29
|
+
## Install
|
|
6
30
|
|
|
7
31
|
```bash
|
|
8
|
-
|
|
32
|
+
npm install @sigrea/vue @sigrea/core vue
|
|
9
33
|
```
|
|
10
34
|
|
|
11
|
-
Vue 3.4+ and Node.js 20
|
|
12
|
-
|
|
13
|
-
## What This Adapter Provides
|
|
14
|
-
|
|
15
|
-
- **Signal readers** – `useSignal` consumes shallow signals and computed values inside Vue components.
|
|
16
|
-
- **Deep signal access** – `useDeepSignal` exposes deeply reactive objects with automatic cleanup.
|
|
17
|
-
- **Derived state** – `useComputed` mirrors Vue’s `computed`, but tracks through Sigrea scopes.
|
|
18
|
-
- **Logic lifecycles** – `useLogic` mounts `defineLogic` factories while honoring `onMount` / `onUnmount`.
|
|
19
|
-
- **Snapshots** – `useSnapshot` grants direct access to low-level signal handlers when needed.
|
|
20
|
-
- **Writable bridge** – `useMutableSignal` exposes primitive `signal()` values as `WritableComputedRef`s for two-way bindings.
|
|
35
|
+
Requires Vue 3.4+ and Node.js 20 or later.
|
|
21
36
|
|
|
22
37
|
## Quick Start
|
|
23
38
|
|
|
24
|
-
### Consume a
|
|
39
|
+
### Consume a Signal
|
|
25
40
|
|
|
26
|
-
```
|
|
41
|
+
```vue
|
|
27
42
|
<script setup lang="ts">
|
|
28
43
|
import { signal } from "@sigrea/core";
|
|
29
44
|
import { useSignal } from "@sigrea/vue";
|
|
@@ -33,36 +48,35 @@ const value = useSignal(count);
|
|
|
33
48
|
</script>
|
|
34
49
|
|
|
35
50
|
<template>
|
|
36
|
-
|
|
51
|
+
<span>{{ value }}</span>
|
|
37
52
|
</template>
|
|
38
53
|
```
|
|
39
54
|
|
|
40
|
-
### Bridge
|
|
55
|
+
### Bridge Framework-Agnostic Logic
|
|
41
56
|
|
|
42
57
|
```ts
|
|
43
58
|
// CounterLogic.ts
|
|
44
59
|
import { defineLogic, signal } from "@sigrea/core";
|
|
45
60
|
|
|
46
61
|
export const CounterLogic = defineLogic<{ initialCount: number }>()((props) => {
|
|
47
|
-
|
|
62
|
+
const count = signal(props.initialCount);
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
const increment = () => {
|
|
65
|
+
count.value += 1;
|
|
66
|
+
};
|
|
52
67
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
const reset = () => {
|
|
69
|
+
count.value = props.initialCount;
|
|
70
|
+
};
|
|
56
71
|
|
|
57
|
-
|
|
72
|
+
return { count, increment, reset };
|
|
58
73
|
});
|
|
59
74
|
```
|
|
60
75
|
|
|
61
|
-
```
|
|
62
|
-
|
|
76
|
+
```vue
|
|
77
|
+
<!-- Counter.vue -->
|
|
63
78
|
<script setup lang="ts">
|
|
64
79
|
import { useLogic, useSignal } from "@sigrea/vue";
|
|
65
|
-
|
|
66
80
|
import { CounterLogic } from "./CounterLogic";
|
|
67
81
|
|
|
68
82
|
const props = defineProps<{ initialCount: number }>();
|
|
@@ -71,17 +85,17 @@ const value = useSignal(counter.count);
|
|
|
71
85
|
</script>
|
|
72
86
|
|
|
73
87
|
<template>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
88
|
+
<div>
|
|
89
|
+
<span>{{ value }}</span>
|
|
90
|
+
<button @click="counter.increment">Increment</button>
|
|
91
|
+
<button @click="counter.reset">Reset</button>
|
|
92
|
+
</div>
|
|
79
93
|
</template>
|
|
80
94
|
```
|
|
81
95
|
|
|
82
|
-
### Bind
|
|
96
|
+
### Bind Writable Primitive Signals
|
|
83
97
|
|
|
84
|
-
```
|
|
98
|
+
```vue
|
|
85
99
|
<script setup lang="ts">
|
|
86
100
|
import { signal } from "@sigrea/core";
|
|
87
101
|
import { useMutableSignal } from "@sigrea/vue";
|
|
@@ -91,18 +105,18 @@ const model = useMutableSignal(count);
|
|
|
91
105
|
</script>
|
|
92
106
|
|
|
93
107
|
<template>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
108
|
+
<label>
|
|
109
|
+
Count
|
|
110
|
+
<input type="number" v-model.number="model" />
|
|
111
|
+
</label>
|
|
98
112
|
</template>
|
|
99
113
|
```
|
|
100
114
|
|
|
101
115
|
`useMutableSignal` expects a writable signal produced by `signal()`. Passing a readonly signal throws at runtime so incorrect bindings fail fast.
|
|
102
116
|
|
|
103
|
-
### Bind
|
|
117
|
+
### Bind Deep Reactive Objects
|
|
104
118
|
|
|
105
|
-
```
|
|
119
|
+
```vue
|
|
106
120
|
<script setup lang="ts">
|
|
107
121
|
import { deepSignal } from "@sigrea/core";
|
|
108
122
|
import { useDeepSignal } from "@sigrea/vue";
|
|
@@ -112,20 +126,90 @@ const model = useDeepSignal(profile);
|
|
|
112
126
|
</script>
|
|
113
127
|
|
|
114
128
|
<template>
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
129
|
+
<label>
|
|
130
|
+
Name
|
|
131
|
+
<input v-model="model.name" />
|
|
132
|
+
</label>
|
|
119
133
|
</template>
|
|
120
134
|
```
|
|
121
135
|
|
|
136
|
+
## API Reference
|
|
137
|
+
|
|
138
|
+
### useSignal
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
function useSignal<T>(
|
|
142
|
+
signal: Signal<T> | ReadonlySignal<T>
|
|
143
|
+
): DeepReadonly<ShallowRef<T>>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Subscribes to a signal or computed value and returns a readonly Vue ref that updates when the signal changes. The subscription is cleaned up when the component unmounts.
|
|
147
|
+
|
|
148
|
+
### useComputed
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
function useComputed<T>(source: Computed<T>): DeepReadonly<ShallowRef<T>>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Subscribes to a computed value and returns a readonly Vue ref that updates when the computed value changes. The subscription is cleaned up when the component unmounts.
|
|
155
|
+
|
|
156
|
+
### useDeepSignal
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
function useDeepSignal<T extends object>(signal: DeepSignal<T>): ShallowRef<T>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Subscribes to a deep signal and returns a mutable Vue ref. Updates to the deep signal trigger reactivity, and the subscription is cleaned up when the component unmounts. Templates unwrap the ref automatically, so accessing nested properties requires no `.value`. In script blocks, use `state.value` to access the underlying object.
|
|
163
|
+
|
|
164
|
+
### useMutableSignal
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
function useMutableSignal<T>(signal: Signal<T>): WritableComputedRef<T>
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Wraps a Sigrea signal as a Vue `WritableComputedRef` for two-way bindings like `v-model`. Expects a writable signal created by `signal()`. Passing a readonly signal throws at runtime.
|
|
171
|
+
|
|
172
|
+
### useLogic
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
function useLogic<TReturn extends object, TProps = void>(
|
|
176
|
+
logic: LogicFunction<TReturn, TProps>,
|
|
177
|
+
...args: LogicArgs<TProps>
|
|
178
|
+
): LogicInstance<TReturn>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Mounts a logic factory and returns its LogicInstance. Sigrea augments the logic with lifecycle metadata: `onMount` callbacks run after the component mounts, and `onUnmount` callbacks run before it unmounts.
|
|
182
|
+
|
|
183
|
+
## Testing
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
// tests/Counter.test.ts
|
|
187
|
+
import { mount } from "@vue/test-utils";
|
|
188
|
+
import { cleanupLogics } from "@sigrea/core";
|
|
189
|
+
import Counter from "../components/Counter.vue";
|
|
190
|
+
|
|
191
|
+
afterEach(() => cleanupLogics());
|
|
192
|
+
|
|
193
|
+
it("increments and displays the updated count", async () => {
|
|
194
|
+
const wrapper = mount(Counter, {
|
|
195
|
+
props: { initialCount: 10 },
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
await wrapper.find("button").trigger("click");
|
|
199
|
+
|
|
200
|
+
expect(wrapper.text()).toContain("11");
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
122
204
|
## Development
|
|
123
205
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
- `pnpm
|
|
127
|
-
- `pnpm
|
|
206
|
+
Development scripts prefer pnpm. npm or yarn work too, but pnpm keeps dependency resolution identical to CI.
|
|
207
|
+
|
|
208
|
+
- `pnpm install` — install dependencies.
|
|
209
|
+
- `pnpm test` — run the Vitest suite once (no watch).
|
|
210
|
+
- `pnpm build` — compile via unbuild to produce dual CJS/ESM bundles.
|
|
211
|
+
- `pnpm dev` — launch the playground counter demo.
|
|
128
212
|
|
|
129
213
|
## License
|
|
130
214
|
|
|
131
|
-
MIT — see
|
|
215
|
+
MIT — see [LICENSE](./LICENSE).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigrea/vue",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Vue adapter bindings for Sigrea logic modules.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,15 +39,15 @@
|
|
|
39
39
|
"typescript"
|
|
40
40
|
],
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@sigrea/core": "^0.1
|
|
42
|
+
"@sigrea/core": "^0.3.1",
|
|
43
43
|
"vue": "^3.4.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@biomejs/biome": "1.9.4",
|
|
47
|
-
"@changesets/cli": "^2.29.6",
|
|
48
47
|
"@vitejs/plugin-vue": "^5.1.4",
|
|
49
48
|
"@vue/test-utils": "^2.4.0",
|
|
50
49
|
"@vitest/coverage-v8": "^3.2.4",
|
|
50
|
+
"changelogen": "^0.6.2",
|
|
51
51
|
"lefthook": "1.13.6",
|
|
52
52
|
"tsx": "^4.20.5",
|
|
53
53
|
"typescript": "5.9.3",
|
|
@@ -60,6 +60,8 @@
|
|
|
60
60
|
"scripts": {
|
|
61
61
|
"dev": "vite --config playground/vite.config.ts",
|
|
62
62
|
"build": "unbuild",
|
|
63
|
+
"changelog": "changelogen",
|
|
64
|
+
"release": "pnpm test && pnpm build && changelogen --release",
|
|
63
65
|
"test": "vitest run",
|
|
64
66
|
"test:coverage": "vitest --coverage",
|
|
65
67
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|