@pithyjs/signals 0.1.0-beta.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PithyJS
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,162 @@
1
+ # @pithyjs/signals
2
+
3
+ Fine-grained reactive signal system for the PithyJS framework. Signals provide O(1) reactive state with automatic dependency tracking — no virtual DOM, no re-renders, only the exact DOM nodes that depend on a signal update.
4
+
5
+ ## Installation
6
+
7
+ <!-- @codex:auto install="@pithyjs/signals" -->
8
+ ```bash
9
+ npm install @pithyjs/signals
10
+ ```
11
+
12
+ ```bash
13
+ pnpm add @pithyjs/signals
14
+ ```
15
+
16
+ ```bash
17
+ yarn add @pithyjs/signals
18
+ ```
19
+ <!-- @codex:end -->
20
+
21
+ ```typescript
22
+ import { signal, computed, effect, batch, onCleanup, isSignal } from '@pithyjs/signals';
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ <!-- @codex:auto examples="pithy.feature.signals" limit="1" -->
28
+ **creates a reactive signal with initial value**
29
+
30
+ ```typescript
31
+ const count = signal(0);
32
+ count(); // → 0
33
+ count.set(1);
34
+ count(); // → 1
35
+ ```
36
+ <!-- @codex:end -->
37
+
38
+ ## API Reference
39
+
40
+ <!-- @codex:auto api="pithy.feature.signals" format="table" -->
41
+ | API | Signature | Stability | Description |
42
+ | --- | --- | --- | --- |
43
+ | isSignal | `(value: unknown) => value is Signal<any>` | stable | - |
44
+ | onCleanup | `(fn: () => void) => void` | stable | - |
45
+ | effect | `(fn: () => void) => () => void` | stable | - |
46
+ | batch | `(fn: () => void) => void` | stable | - |
47
+ | signal | `<T>(initial: T) => Signal<T>` | stable | - |
48
+ | computed | `<T>(compute: () => T) => () => T` | stable | - |
49
+ <!-- @codex:end -->
50
+
51
+ ## Examples
52
+
53
+ <!-- @codex:auto examples="pithy.feature.signals" -->
54
+ **creates a reactive signal with initial value**
55
+
56
+ ```typescript
57
+ const count = signal(0);
58
+ count(); // → 0
59
+ count.set(1);
60
+ count(); // → 1
61
+ ```
62
+
63
+ **supports functional updates via .set()**
64
+
65
+ ```typescript
66
+ const count = signal(0);
67
+ count.set(n => n + 1);
68
+ count(); // → 1
69
+ count.set(n => n * 10);
70
+ count(); // → 10
71
+ ```
72
+
73
+ **tracks signal dependencies and re-runs**
74
+
75
+ ```typescript
76
+ const count = signal(0);
77
+ let observed = -1;
78
+ effect(() => {
79
+ observed = count();
80
+ });
81
+ observed; // → 0
82
+ count.set(5);
83
+ observed; // → 5
84
+ ```
85
+
86
+ **derives a value from other signals**
87
+
88
+ ```typescript
89
+ const a = signal(2);
90
+ const b = signal(3);
91
+ const sum = computed(() => a() + b());
92
+ sum(); // → 5
93
+ a.set(10);
94
+ sum(); // → 13
95
+ ```
96
+
97
+ **defers effect execution until batch completes**
98
+
99
+ ```typescript
100
+ const a = signal(1);
101
+ const b = signal(2);
102
+ let runCount = 0;
103
+ effect(() => {
104
+ a();
105
+ b();
106
+ runCount++;
107
+ });
108
+ runCount; // → 1
109
+
110
+ batch(() => {
111
+ a.set(10);
112
+ b.set(20);
113
+ });
114
+ // Effect should have run only once after batch, not twice
115
+ runCount; // → 2
116
+ ```
117
+
118
+ **runs cleanup on disposal**
119
+
120
+ ```typescript
121
+ const count = signal(0);
122
+ let cleaned = false;
123
+
124
+ const dispose = effect(() => {
125
+ count();
126
+ onCleanup(() => {
127
+ cleaned = true;
128
+ });
129
+ });
130
+
131
+ cleaned; // → false
132
+ dispose();
133
+ cleaned; // → true
134
+ ```
135
+
136
+ **detects signal values at runtime**
137
+
138
+ ```typescript
139
+ const count = signal(0);
140
+ isSignal(count); // → true
141
+ isSignal(42); // → false
142
+ isSignal(() => {}); // → false
143
+ isSignal(null); // → false
144
+ ```
145
+ <!-- @codex:end -->
146
+
147
+ ## Testing
148
+
149
+ <!-- @codex:auto testing="pithy.feature.signals" format="table" -->
150
+ | Feature | Unit | Integration | E2E | Status |
151
+ | --- | --- | --- | --- | --- |
152
+ | isSignal | ✓ | - | - | Missing integration |
153
+ | onCleanup | ✓ | - | - | Missing integration |
154
+ | effect | ✓ | - | - | Missing integration |
155
+ | batch | ✓ | - | - | Missing integration |
156
+ | signal | ✓ | - | - | Missing integration |
157
+ | computed | ✓ | - | - | Missing integration |
158
+ <!-- @codex:end -->
159
+
160
+ ## License
161
+
162
+ MIT
@@ -0,0 +1,3 @@
1
+ export * from './signal';
2
+ export * from './types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from './signal';
2
+ export * from './types';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC"}
@@ -0,0 +1,58 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.feature.signals",
4
+ "title": "Reactive Signal System",
5
+ "category": "feature"
6
+ }
7
+ */
8
+ import { Signal } from './types';
9
+ /**
10
+ * Checks if a value is a Signal at runtime.
11
+ * @param value - The value to check.
12
+ * @returns True if the value is a Signal.
13
+ */
14
+ /** @codexApi {"parent":"pithy.feature.signals","name":"isSignal","stability":"stable","signature":"(value: unknown) => value is Signal<any>"} */
15
+ export declare function isSignal(value: unknown): value is Signal<unknown>;
16
+ /**
17
+ * Registers a cleanup callback that will be executed
18
+ * before the current effect is re-run.
19
+ *
20
+ * @param fn - The cleanup function.
21
+ */
22
+ /** @codexApi {"parent":"pithy.feature.signals","name":"onCleanup","stability":"stable","signature":"(fn: () => void) => void"} */
23
+ export declare function onCleanup(fn: () => void): void;
24
+ /**
25
+ * Creates a reactive effect that automatically tracks
26
+ * signals it reads and re-runs when any of them change.
27
+ *
28
+ * @param fn - The effect function to run reactively.
29
+ * @returns A disposer function to stop tracking this effect.
30
+ */
31
+ /** @codexApi {"parent":"pithy.feature.signals","name":"effect","stability":"stable","signature":"(fn: () => void) => () => void"} */
32
+ export declare function effect(fn: () => void): () => void;
33
+ /**
34
+ * Batches multiple signal updates together.
35
+ * Effects triggered during the batch are deferred and executed once after the batch.
36
+ *
37
+ * @param fn - The function performing multiple signal updates.
38
+ */
39
+ /** @codexApi {"parent":"pithy.feature.signals","name":"batch","stability":"stable","signature":"(fn: () => void) => void"} */
40
+ export declare function batch(fn: () => void): void;
41
+ /**
42
+ * Creates a signal: a reactive function holding a value and supporting subscriptions.
43
+ *
44
+ * @param initial - Initial value of the signal.
45
+ * @returns A getter function with `.set()` and `.subscribe()` attached.
46
+ */
47
+ /** @codexApi {"parent":"pithy.feature.signals","name":"signal","stability":"stable","signature":"<T>(initial: T) => Signal<T>"} */
48
+ export declare function signal<T>(initial: T): Signal<T>;
49
+ /**
50
+ * Creates a derived signal (computed value) that automatically
51
+ * recalculates when its dependencies change.
52
+ *
53
+ * @param compute - A function returning the computed value.
54
+ * @returns A readonly function returning the latest computed value.
55
+ */
56
+ /** @codexApi {"parent":"pithy.feature.signals","name":"computed","stability":"stable","signature":"<T>(compute: () => T) => () => T"} */
57
+ export declare function computed<T>(compute: () => T): () => T;
58
+ //# sourceMappingURL=signal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../src/signal.ts"],"names":[],"mappings":"AAAA;;;;;;EAME;AAEF,OAAO,EAAE,MAAM,EAAiB,MAAM,SAAS,CAAC;AAmChD;;;;GAIG;AACH,iJAAiJ;AACjJ,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,CAKjE;AAiBD;;;;;GAKG;AACH,kIAAkI;AAClI,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAK9C;AAED;;;;;;GAMG;AACH,qIAAqI;AACrI,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAajD;AAED;;;;;GAKG;AACH,8HAA8H;AAC9H,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAa1C;AAED;;;;;GAKG;AACH,mIAAmI;AACnI,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAoE/C;AAED;;;;;;GAMG;AACH,yIAAyI;AACzI,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,CAmBrD"}
package/dist/signal.js ADDED
@@ -0,0 +1,230 @@
1
+ /** @codex
2
+ {
3
+ "id": "pithy.feature.signals",
4
+ "title": "Reactive Signal System",
5
+ "category": "feature"
6
+ }
7
+ */
8
+ import { SIGNAL_SYMBOL } from './types';
9
+ /**
10
+ * Tracks the current reactive effect being registered.
11
+ * Used to record dependencies when a signal is read.
12
+ */
13
+ let currentEffect = null;
14
+ /**
15
+ * Internal cleanup registry to track cleanup functions for each registered effect.
16
+ * Each effect can have multiple cleanups that are called before re-execution.
17
+ */
18
+ const cleanupMap = new WeakMap();
19
+ /**
20
+ * Flag to indicate if we are currently inside a batched update.
21
+ */
22
+ let isBatching = false;
23
+ /**
24
+ * Set of effect wrappers scheduled to run after a batch ends.
25
+ */
26
+ const pendingEffects = new Set();
27
+ /**
28
+ * Indicates whether we are currently inside a tracking context (effect or computed).
29
+ * Used to suppress signal read warnings in appropriate scopes.
30
+ */
31
+ let isTrackingContext = false;
32
+ /**
33
+ * Tracks suppression depth for temporarily suppressing signal read warnings.
34
+ */
35
+ let suppressionDepth = 0;
36
+ /**
37
+ * Checks if a value is a Signal at runtime.
38
+ * @param value - The value to check.
39
+ * @returns True if the value is a Signal.
40
+ */
41
+ /** @codexApi {"parent":"pithy.feature.signals","name":"isSignal","stability":"stable","signature":"(value: unknown) => value is Signal<any>"} */
42
+ export function isSignal(value) {
43
+ return (typeof value === 'function' &&
44
+ value[SIGNAL_SYMBOL] === true);
45
+ }
46
+ /**
47
+ * Temporarily suppresses signal read warnings inside the given function.
48
+ * Handles nested suppression correctly.
49
+ * @param fn - Function during which warnings should be suppressed.
50
+ * @returns The return value of the function.
51
+ */
52
+ function suppressWarnings(fn) {
53
+ suppressionDepth++;
54
+ try {
55
+ return fn();
56
+ }
57
+ finally {
58
+ suppressionDepth--;
59
+ }
60
+ }
61
+ /**
62
+ * Registers a cleanup callback that will be executed
63
+ * before the current effect is re-run.
64
+ *
65
+ * @param fn - The cleanup function.
66
+ */
67
+ /** @codexApi {"parent":"pithy.feature.signals","name":"onCleanup","stability":"stable","signature":"(fn: () => void) => void"} */
68
+ export function onCleanup(fn) {
69
+ if (currentEffect) {
70
+ const stack = cleanupMap.get(currentEffect);
71
+ if (stack)
72
+ stack.push(fn);
73
+ }
74
+ }
75
+ /**
76
+ * Creates a reactive effect that automatically tracks
77
+ * signals it reads and re-runs when any of them change.
78
+ *
79
+ * @param fn - The effect function to run reactively.
80
+ * @returns A disposer function to stop tracking this effect.
81
+ */
82
+ /** @codexApi {"parent":"pithy.feature.signals","name":"effect","stability":"stable","signature":"(fn: () => void) => () => void"} */
83
+ export function effect(fn) {
84
+ const wrapped = () => runEffect(wrapped, fn);
85
+ // Run immediately to establish dependencies
86
+ wrapped();
87
+ // Return disposer to manually stop the effect
88
+ const disposer = () => {
89
+ cleanupMap.get(wrapped)?.forEach(fn => fn());
90
+ cleanupMap.delete(wrapped);
91
+ };
92
+ return disposer;
93
+ }
94
+ /**
95
+ * Batches multiple signal updates together.
96
+ * Effects triggered during the batch are deferred and executed once after the batch.
97
+ *
98
+ * @param fn - The function performing multiple signal updates.
99
+ */
100
+ /** @codexApi {"parent":"pithy.feature.signals","name":"batch","stability":"stable","signature":"(fn: () => void) => void"} */
101
+ export function batch(fn) {
102
+ isBatching = true;
103
+ try {
104
+ fn();
105
+ }
106
+ finally {
107
+ isBatching = false;
108
+ // Execute all pending effects once
109
+ const effects = Array.from(pendingEffects);
110
+ pendingEffects.clear();
111
+ effects.forEach(run => run());
112
+ }
113
+ }
114
+ /**
115
+ * Creates a signal: a reactive function holding a value and supporting subscriptions.
116
+ *
117
+ * @param initial - Initial value of the signal.
118
+ * @returns A getter function with `.set()` and `.subscribe()` attached.
119
+ */
120
+ /** @codexApi {"parent":"pithy.feature.signals","name":"signal","stability":"stable","signature":"<T>(initial: T) => Signal<T>"} */
121
+ export function signal(initial) {
122
+ let value = initial;
123
+ const subscribers = new Set();
124
+ const listeners = new Set();
125
+ const read = () => suppressWarnings(() => {
126
+ if (currentEffect) {
127
+ // Register currentEffect as a subscriber
128
+ subscribers.add(currentEffect);
129
+ // IMPORTANT: Capture currentEffect NOW, not when cleanup runs!
130
+ // Otherwise, if cleanup runs while a different effect is active,
131
+ // we'd delete the wrong effect from subscribers.
132
+ const effectToRemove = currentEffect;
133
+ onCleanup(() => {
134
+ subscribers.delete(effectToRemove);
135
+ });
136
+ }
137
+ else {
138
+ // Warn if a signal is read outside of reactive context (in dev mode)
139
+ if (!isTrackingContext &&
140
+ suppressionDepth === 0 &&
141
+ import.meta.env?.DEV &&
142
+ !import.meta.env?.TEST) {
143
+ const error = new Error();
144
+ const stack = error.stack ?? '';
145
+ console.warn(`[PithyJS] ❗ Signal (value: ${value}) was read outside of a reactive context.\n` +
146
+ ` • This might lead to missed updates in the UI.\n\n` +
147
+ `🔎 Where it happened:\n${stack.split('\n').slice(2).join('\n')}`);
148
+ }
149
+ }
150
+ return value;
151
+ });
152
+ read.set = (next) => {
153
+ const newValue = suppressWarnings(() => typeof next === 'function' ? next(value) : next);
154
+ if (value !== newValue) {
155
+ value = newValue;
156
+ // Notify all reactive subscribers
157
+ subscribers.forEach(fn => {
158
+ if (isBatching) {
159
+ pendingEffects.add(fn);
160
+ }
161
+ else {
162
+ fn();
163
+ }
164
+ });
165
+ // Notify all external listeners (subscriptions)
166
+ listeners.forEach(fn => fn(value));
167
+ }
168
+ };
169
+ read.subscribe = (fn) => {
170
+ listeners.add(fn);
171
+ fn(value); // Immediately call with current value
172
+ return () => listeners.delete(fn);
173
+ };
174
+ read[SIGNAL_SYMBOL] =
175
+ true;
176
+ return read;
177
+ }
178
+ /**
179
+ * Creates a derived signal (computed value) that automatically
180
+ * recalculates when its dependencies change.
181
+ *
182
+ * @param compute - A function returning the computed value.
183
+ * @returns A readonly function returning the latest computed value.
184
+ */
185
+ /** @codexApi {"parent":"pithy.feature.signals","name":"computed","stability":"stable","signature":"<T>(compute: () => T) => () => T"} */
186
+ export function computed(compute) {
187
+ isTrackingContext = true; // Allow first compute() call to access signals without warnings
188
+ let initial;
189
+ try {
190
+ initial = compute();
191
+ }
192
+ finally {
193
+ isTrackingContext = false;
194
+ }
195
+ const result = signal(initial);
196
+ const owner = currentEffect;
197
+ const dispose = effect(() => {
198
+ result.set(compute());
199
+ });
200
+ if (owner) {
201
+ cleanupMap.get(owner)?.push(dispose);
202
+ }
203
+ return result;
204
+ }
205
+ /**
206
+ * Runs the provided effect function, setting up the current tracking context,
207
+ * managing cleanups, and allowing signal dependencies to be re-collected.
208
+ *
209
+ * @param wrapped - The wrapper function associated with the effect.
210
+ * @param fn - The effect body function.
211
+ */
212
+ function runEffect(wrapped, fn) {
213
+ isTrackingContext = true;
214
+ if (isBatching) {
215
+ // Defer running effect until after batch completes
216
+ pendingEffects.add(() => runEffect(wrapped, fn));
217
+ return;
218
+ }
219
+ // Prepare new cleanup stack
220
+ cleanupMap.set(wrapped, []);
221
+ currentEffect = wrapped;
222
+ try {
223
+ fn(); // Run the effect
224
+ }
225
+ finally {
226
+ currentEffect = null;
227
+ isTrackingContext = false;
228
+ }
229
+ }
230
+ //# sourceMappingURL=signal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal.js","sourceRoot":"","sources":["../src/signal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type EffectFn = () => void;
2
+ export type Signal<T> = (() => T) & {
3
+ set: (value: T | ((prev: T) => T)) => void;
4
+ subscribe: (fn: (value: T) => void) => () => void;
5
+ };
6
+ /** Read-only accessor for a signal's value. Compatible with effects for dependency tracking. */
7
+ export type ReadonlySignal<T> = () => T;
8
+ /** The signature of the `effect()` function from @pithyjs/signals. */
9
+ export type EffectRunner = (fn: EffectFn) => () => void;
10
+ export declare const SIGNAL_SYMBOL: unique symbol;
11
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC;AAElC,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG;IAClC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,CAAC;IAC3C,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;CACnD,CAAC;AAEF,gGAAgG;AAChG,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAExC,sEAAsE;AACtE,MAAM,MAAM,YAAY,GAAG,CAAC,EAAE,EAAE,QAAQ,KAAK,MAAM,IAAI,CAAC;AAExD,eAAO,MAAM,aAAa,eAAyB,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export const SIGNAL_SYMBOL = Symbol('pithy-signal');
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@pithyjs/signals",
3
+ "version": "0.1.0-beta.0",
4
+ "description": "Reactive signal system for PithyJS framework",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "devDependencies": {
15
+ "typescript": "^5.0.0",
16
+ "vitest": "^1.0.0"
17
+ },
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "license": "MIT",
22
+ "publishConfig": {
23
+ "access": "public",
24
+ "tag": "beta"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "dev": "tsc --watch",
29
+ "test": "vitest"
30
+ }
31
+ }