@sigrea/vue 0.1.0 → 0.2.1

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.
Files changed (2) hide show
  1. package/README.md +135 -51
  2. package/package.json +78 -68
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 to Vue 3s Composition API. It keeps lifecycle scopes aligned with component mounts, forwards deep reactivity, and ships composables that feel first-class inside `<script setup>` or traditional setup functions.
4
-
5
- ## Installation
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
- pnpm add @sigrea/vue @sigrea/core vue
32
+ npm install @sigrea/vue @sigrea/core vue
9
33
  ```
10
34
 
11
- Vue 3.4+ and Node.js 20+ are required. Equivalent npm or yarn commands work as expected.
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 signal
39
+ ### Consume a Signal
25
40
 
26
- ```ts
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
- <span>{{ value }}</span>
51
+ <span>{{ value }}</span>
37
52
  </template>
38
53
  ```
39
54
 
40
- ### Bridge framework-agnostic logic
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
- const count = signal(props.initialCount);
62
+ const count = signal(props.initialCount);
48
63
 
49
- const increment = () => {
50
- count.value += 1;
51
- };
64
+ const increment = () => {
65
+ count.value += 1;
66
+ };
52
67
 
53
- const reset = () => {
54
- count.value = props.initialCount;
55
- };
68
+ const reset = () => {
69
+ count.value = props.initialCount;
70
+ };
56
71
 
57
- return { count, increment, reset };
72
+ return { count, increment, reset };
58
73
  });
59
74
  ```
60
75
 
61
- ```ts
62
- // Counter.vue
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
- <div>
75
- <span>{{ value }}</span>
76
- <button @click="counter.increment">Increment</button>
77
- <button @click="counter.reset">Reset</button>
78
- </div>
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 writable primitive signals
96
+ ### Bind Writable Primitive Signals
83
97
 
84
- ```ts
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
- <label>
95
- Count
96
- <input type="number" v-model.number="model" />
97
- </label>
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 deep reactive objects
117
+ ### Bind Deep Reactive Objects
104
118
 
105
- ```ts
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
- <label>
116
- Name
117
- <input v-model="model.name" />
118
- </label>
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
- - `pnpm install` install dependencies
125
- - `pnpm test` – run the Vitest suite
126
- - `pnpm build` emit distributable artifacts
127
- - `pnpm dev` launch the playground counter demo
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 `LICENSE`.
215
+ MIT — see [LICENSE](./LICENSE).
package/package.json CHANGED
@@ -1,69 +1,79 @@
1
1
  {
2
- "name": "@sigrea/vue",
3
- "version": "0.1.0",
4
- "description": "Vue adapter bindings for Sigrea logic modules.",
5
- "license": "MIT",
6
- "type": "module",
7
- "publishConfig": {
8
- "access": "public"
9
- },
10
- "repository": {
11
- "type": "git",
12
- "url": "git+https://github.com/sigrea/vue.git"
13
- },
14
- "homepage": "https://github.com/sigrea/vue#readme",
15
- "bugs": {
16
- "url": "https://github.com/sigrea/vue/issues"
17
- },
18
- "engines": {
19
- "node": ">=20"
20
- },
21
- "sideEffects": false,
22
- "exports": {
23
- ".": {
24
- "types": "./dist/index.d.ts",
25
- "import": "./dist/index.mjs",
26
- "require": "./dist/index.cjs"
27
- }
28
- },
29
- "main": "./dist/index.cjs",
30
- "types": "./dist/index.d.ts",
31
- "files": [
32
- "dist"
33
- ],
34
- "keywords": [
35
- "signals",
36
- "reactivity",
37
- "vue",
38
- "logic",
39
- "typescript"
40
- ],
41
- "peerDependencies": {
42
- "@sigrea/core": "^0.1.0",
43
- "vue": "^3.4.0"
44
- },
45
- "devDependencies": {
46
- "@biomejs/biome": "1.9.4",
47
- "@changesets/cli": "^2.29.6",
48
- "@vitejs/plugin-vue": "^5.1.4",
49
- "@vue/test-utils": "^2.4.0",
50
- "@vitest/coverage-v8": "^3.2.4",
51
- "lefthook": "1.13.6",
52
- "tsx": "^4.20.5",
53
- "typescript": "5.9.3",
54
- "unbuild": "3.6.1",
55
- "vite": "^5.4.6",
56
- "vitest": "^3.2.4",
57
- "vue": "^3.4.0",
58
- "jsdom": "^24.1.3"
59
- },
60
- "scripts": {
61
- "dev": "vite --config playground/vite.config.ts",
62
- "build": "unbuild",
63
- "test": "vitest run",
64
- "test:coverage": "vitest --coverage",
65
- "typecheck": "tsc -p tsconfig.json --noEmit",
66
- "format": "biome check .",
67
- "format:fix": "biome check --write ."
68
- }
69
- }
2
+ "name": "@sigrea/vue",
3
+ "version": "0.2.1",
4
+ "description": "Vue adapter bindings for Sigrea logic modules.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "packageManager": "pnpm@10.0.0",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/sigrea/vue.git"
14
+ },
15
+ "homepage": "https://github.com/sigrea/vue#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/sigrea/vue/issues"
18
+ },
19
+ "engines": {
20
+ "node": ">=20"
21
+ },
22
+ "sideEffects": false,
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.mjs",
27
+ "require": "./dist/index.cjs"
28
+ }
29
+ },
30
+ "main": "./dist/index.cjs",
31
+ "types": "./dist/index.d.ts",
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "keywords": [
36
+ "signals",
37
+ "reactivity",
38
+ "vue",
39
+ "logic",
40
+ "typescript"
41
+ ],
42
+ "scripts": {
43
+ "dev": "vite --config playground/vite.config.ts",
44
+ "build": "unbuild",
45
+ "prepack": "unbuild",
46
+ "changelog": "changelogen",
47
+ "release": "pnpm test && pnpm build && changelogen --release",
48
+ "test": "vitest run",
49
+ "test:coverage": "vitest --coverage",
50
+ "typecheck": "tsc -p tsconfig.json --noEmit",
51
+ "format": "biome check .",
52
+ "format:fix": "biome check --write ."
53
+ },
54
+ "peerDependencies": {
55
+ "@sigrea/core": "^0.3.1",
56
+ "vue": "^3.4.0"
57
+ },
58
+ "devDependencies": {
59
+ "@biomejs/biome": "1.9.4",
60
+ "@vitejs/plugin-vue": "^5.1.4",
61
+ "@vue/test-utils": "^2.4.0",
62
+ "@vitest/coverage-v8": "^3.2.4",
63
+ "changelogen": "^0.6.2",
64
+ "lefthook": "1.13.6",
65
+ "tsx": "^4.20.5",
66
+ "typescript": "5.9.3",
67
+ "unbuild": "3.6.1",
68
+ "vite": "^5.4.6",
69
+ "vitest": "^3.2.4",
70
+ "vue": "^3.4.0",
71
+ "jsdom": "^24.1.3"
72
+ },
73
+ "pnpm": {
74
+ "onlyBuiltDependencies": [
75
+ "lefthook",
76
+ "@biomejs/biome"
77
+ ]
78
+ }
79
+ }