@xyn-html/xyn-signal 0.1.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
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,88 @@
1
+ # xyn-signal
2
+
3
+ Lightweight, reactive signal-based state management for JavaScript applications.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install xyn-signal
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```javascript
14
+ import { createSignal, watch, timing } from 'xyn-signal';
15
+
16
+ // Create a signal
17
+ const count = createSignal(0);
18
+
19
+ // Subscribe to changes
20
+ count.subscribe((value) => {
21
+ console.log('Count changed:', value);
22
+ });
23
+
24
+ // Update the value
25
+ count.value = 1;
26
+ count.value = 2;
27
+
28
+ // Object signals with reactive properties
29
+ const user = createSignal({ name: 'John', age: 30 });
30
+ user.value.name = 'Jane'; // Triggers subscribers
31
+
32
+ // Array signals with reactive methods
33
+ const items = createSignal([1, 2, 3]);
34
+ items.value.push(4); // Triggers subscribers
35
+
36
+ // Watch multiple signals
37
+ const firstName = createSignal('John');
38
+ const lastName = createSignal('Doe');
39
+
40
+ watch(firstName)
41
+ .watch(lastName)
42
+ .effect(() => {
43
+ console.log(`Full name: ${firstName.value} ${lastName.value}`);
44
+ });
45
+
46
+ // Derived signals
47
+ const { signal: fullName } = watch(firstName)
48
+ .watch(lastName)
49
+ .derived(() => `${firstName.value} ${lastName.value}`);
50
+
51
+ // Timing functions
52
+ const debouncedEffect = timing(300).debounce(() => {
53
+ console.log('Debounced!');
54
+ });
55
+
56
+ watch(firstName).effect(debouncedEffect);
57
+ ```
58
+
59
+ ## API
60
+
61
+ ### createSignal(initialValue)
62
+
63
+ Creates a reactive signal that can hold any value type.
64
+
65
+ - **Primitives**: Simple value storage with change detection
66
+ - **Objects**: Reactive property assignment and deletion
67
+ - **Arrays**: Reactive array methods (push, pop, shift, unshift, splice)
68
+ - **Map/Set**: Reactive collection operations
69
+
70
+ ### watch(signal)
71
+
72
+ Creates a watcher for observing multiple signals.
73
+
74
+ - `.watch(signal)` - Chain additional signals
75
+ - `.effect(fn)` - Subscribe to all watched signals
76
+ - `.derived(fn, wrappingFn?)` - Create a derived signal
77
+
78
+ ### timing(delay)
79
+
80
+ Creates timing-controlled function wrappers.
81
+
82
+ - `.debounce(fn)` - Execute after inactivity period
83
+ - `.throttle(fn)` - Limit execution rate
84
+ - `.delay(fn)` - Execute after fixed delay
85
+
86
+ ## License
87
+
88
+ MIT
package/dist/.gitkeep ADDED
File without changes
@@ -0,0 +1,581 @@
1
+ /**
2
+ * @fileoverview XynSignal is a library for creating reactive signals, it is
3
+ * used in XynHTML to render the data to HTML elements. It may also be used
4
+ * independently of XynHTML.
5
+ *
6
+ * @license MIT
7
+ * Copyright (c) 2024 XynHTML
8
+ *
9
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ * of this software and associated documentation files (the "Software"), to deal
11
+ * in the Software without restriction, including without limitation the rights
12
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ * copies of the Software, and to permit persons to whom the Software is
14
+ * furnished to do so, subject to the following conditions:
15
+ *
16
+ * The above copyright notice and this permission notice shall be included in all
17
+ * copies or substantial portions of the Software.
18
+ *
19
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ * SOFTWARE.
26
+ */
27
+
28
+ /**
29
+ * @typedef {Object} XynChange
30
+ * @template T
31
+ * @property {T} value
32
+ * @property {T} previousValue
33
+ * @description XynChange is a class for storing the values of a change.
34
+ * @example
35
+ * const change = XynChange.create(1, 0);
36
+ * console.log(change.value); // 1
37
+ * console.log(change.previousValue); // 0
38
+ * console.log(change.values); // { value: 1, previousValue: 0 }
39
+ */
40
+ class XynChange {
41
+ #value;
42
+ #previousValue;
43
+
44
+ constructor(value, previousValue) {
45
+ this.#value = value;
46
+ this.#previousValue = previousValue;
47
+ }
48
+
49
+ static create(value, previousValue) {
50
+ return new XynChange(value, previousValue);
51
+ }
52
+
53
+ get value() {
54
+ return this.#value;
55
+ }
56
+
57
+ get previousValue() {
58
+ return this.#previousValue;
59
+ }
60
+
61
+ get values() {
62
+ return {
63
+ value: this.#value,
64
+ previousValue: this.#previousValue,
65
+ };
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @typedef {Object} XynCollectionChange
71
+ * @template T
72
+ * @property {int} index
73
+ * @property {T} value
74
+ * @property {T} previousValue
75
+ * @description XynListChange is a class for storing the values of a collection change.
76
+ * @example
77
+ * const change = XynListChange.create(0, 1, 0);
78
+ * console.log(change.index); // 0
79
+ * console.log(change.value); // 1
80
+ * console.log(change.previousValue); // 0
81
+ * console.log(change.values); // { index: 0, value: 1, previousValue: 0 }
82
+ */
83
+ class XynCollectionChange {
84
+ #index;
85
+ #value;
86
+ #previousValue;
87
+
88
+ constructor(index, value, previousValue) {
89
+ this.#index = index;
90
+ this.#value = value;
91
+ this.#previousValue = previousValue;
92
+ }
93
+
94
+ static create(index, value, previousValue) {
95
+ return new XynCollectionChange(index, value, previousValue);
96
+ }
97
+
98
+ get index() {
99
+ return this.#index;
100
+ }
101
+
102
+ get value() {
103
+ return this.#value;
104
+ }
105
+
106
+ get previousValue() {
107
+ return this.#previousValue;
108
+ }
109
+
110
+ get values() {
111
+ return {
112
+ value: this.#value,
113
+ previousValue: this.#previousValue,
114
+ index: this.#index,
115
+ };
116
+ }
117
+ }
118
+
119
+ /**
120
+ * @class None
121
+ * @description None is a class for storing a value that doesn't exist.
122
+ * @example
123
+ * const none = new None("none");
124
+ * console.log(none.is(new None("none"))); // true
125
+ * console.log(none.is(new None("other"))); // false
126
+ * console.log(none.value); // "none"
127
+ */
128
+ class None {
129
+ #value;
130
+
131
+ constructor(value) {
132
+ this.#value = value;
133
+ }
134
+
135
+ is(none) {
136
+ return none instanceof None && this.#value === none.value;
137
+ }
138
+
139
+ get value() {
140
+ return this.#value;
141
+ }
142
+ }
143
+
144
+ /**
145
+ * @enum {None}
146
+ * @readonly
147
+ * @description CollectionValue is an enum of sentinals for the values of a collection change that don't or didn't exist.
148
+ */
149
+ export const CollectionValue = Object.freeze({
150
+ INSERT: new None("insert"),
151
+ DELETE: new None("delete"),
152
+ });
153
+
154
+ /**
155
+ * @template T
156
+ * @param {T} value
157
+ * @returns {{ value: T, subscribe: (subscriber: Function) => void }}
158
+ * @description Creates a signal with the given value.
159
+ * The signal can be subscribed to and will notify subscribers when the value changes.
160
+ * @example
161
+ * const signal = createSignal(0);
162
+ * signal.subscribe(({value, previousValue}) =>
163
+ * console.log(`Value changed from ${previousValue} to ${value}`)
164
+ * );
165
+ */
166
+ export function createSignal(value) {
167
+ if (typeof value === "function") {
168
+ value = value();
169
+ }
170
+
171
+ if (value != null) {
172
+ if (value instanceof Map || value instanceof Set) {
173
+ return createCollectionSignal(value);
174
+ }
175
+ if (Array.isArray(value)) {
176
+ return createListSignal(value);
177
+ }
178
+ if (typeof value === "object" && value !== null) {
179
+ return createObjectSignal(value);
180
+ }
181
+ }
182
+ const subscribers = new Set();
183
+
184
+ /**
185
+ * @type {Object}
186
+ * @property {T} value
187
+ * @property {function(function({value: any, previousValue: any}): void): function(): void} subscribe
188
+ * @description Subscribes to the signal and returns a function to unsubscribe.
189
+ * The subscriber function is called with the current value and the previous value.
190
+ * The subscriber function is called immediately with the current value.
191
+ * @example
192
+ * const signal = createSignal(0);
193
+ * const unsubscribe = signal.subscribe(({value, previousValue}) =>
194
+ * console.log(`Value changed from ${previousValue} to ${value}`)
195
+ * );
196
+ */
197
+ const signalProxy = new Proxy(
198
+ {
199
+ value,
200
+ subscribe(subscriber) {
201
+ subscribers.add(subscriber);
202
+ return () => subscribers.delete(subscriber);
203
+ },
204
+ },
205
+ {
206
+ get(target, prop) {
207
+ return Reflect.get(target, prop);
208
+ },
209
+ set(target, prop, newValue) {
210
+ if (prop === "value") {
211
+ const previousValue = Reflect.get(target, prop);
212
+ Reflect.set(target, prop, newValue);
213
+ if (previousValue === newValue) {
214
+ return true;
215
+ }
216
+ subscribers.forEach((subscriber) =>
217
+ subscriber(XynChange.create(newValue, previousValue)),
218
+ );
219
+ return true;
220
+ }
221
+ return Reflect.set(target, prop, newValue);
222
+ },
223
+ apply(target, thisArg, args) {
224
+ return Reflect.apply(target, thisArg, args);
225
+ },
226
+ },
227
+ );
228
+
229
+ return signalProxy;
230
+ }
231
+
232
+ /**
233
+ * @function proxyFactory
234
+ * @param {Object} obj
235
+ * @param {Set<Function>} subscribers
236
+ * @returns {Proxy | Number | String | Boolean | null | undefined | Symbol | BigInt }
237
+ * @description Creates a proxy for the given object that notifies subscribers when the object changes.
238
+ * @example
239
+ * const obj = { a: 1, b: 2 };
240
+ * const subscribers = new Set();
241
+ * const proxy = proxyFactory(obj, subscribers);
242
+ * proxy.a = 3; // Notifies subscribers
243
+ * proxy.b = 4; // Notifies subscribers
244
+ * proxy.c = 5; // Notifies subscribers
245
+ * delete proxy.a; // Notifies subscribers
246
+ * proxy.a = 6; // Notifies subscribers
247
+ * proxy.a = 7; // Notifies subscribers
248
+ */
249
+ function proxyFactory(value, subscribers) {
250
+ if (value instanceof Map || value instanceof Set) {
251
+ return createCollectionProxy(value, subscribers);
252
+ } else if (Array.isArray(value)) {
253
+ return createListProxy(value, subscribers);
254
+ } else if (typeof value === "object" && value != null) {
255
+ return createObjectProxy(value, subscribers);
256
+ } else if (typeof value === "symbol") {
257
+ return String(value);
258
+ }
259
+ return value;
260
+ }
261
+
262
+ function createCollectionProxy(collection, subscribers) {
263
+ return new Proxy(collection, {
264
+ get(target, prop) {
265
+ const value = Reflect.get(target, prop);
266
+
267
+ if (typeof value === "function") {
268
+ return (...args) => {
269
+ const prevDeleteValue =
270
+ prop === "delete" ? Reflect.get(target, args[0]) : null;
271
+
272
+ const result = value.apply(target, args);
273
+ if (prop === "get") {
274
+ return proxyFactory(result, subscribers);
275
+ }
276
+ if (prop === "set" || prop === "add") {
277
+ if (prop === "set" && Reflect.has(target, args[0])) {
278
+ return result;
279
+ }
280
+ subscribers.forEach((subscriber) => {
281
+ subscriber(
282
+ XynCollectionChange.create(
283
+ args[0],
284
+ args[1],
285
+ CollectionValue.INSERT,
286
+ ),
287
+ );
288
+ });
289
+ }
290
+ if (prop === "delete") {
291
+ subscribers.forEach((subscriber) => {
292
+ subscriber(
293
+ XynCollectionChange.create(
294
+ args[0],
295
+ CollectionValue.DELETE,
296
+ prevDeleteValue,
297
+ ),
298
+ );
299
+ });
300
+ }
301
+ if (prop === "clear") {
302
+ subscribers.forEach((subscriber) => {
303
+ subscriber(
304
+ XynCollectionChange.create(
305
+ CollectionValue.DELETE,
306
+ CollectionValue.DELETE,
307
+ CollectionValue.DELETE,
308
+ ),
309
+ );
310
+ });
311
+ }
312
+ return result;
313
+ };
314
+ }
315
+
316
+ if (typeof value === "object" && value !== null) {
317
+ return proxyFactory(value, subscribers);
318
+ }
319
+
320
+ return value;
321
+ },
322
+ getPrototypeOf(target) {
323
+ return Reflect.getPrototypeOf(target);
324
+ },
325
+ });
326
+ }
327
+
328
+ function createListProxy(list, subscribers) {
329
+ return new Proxy(list, {
330
+ get(target, prop) {
331
+ const value = Reflect.get(target, prop);
332
+ if (typeof value === "function") {
333
+ return (...args) => {
334
+ const result = value.apply(target, args);
335
+ const LAST_INDEX = Reflect.get(target, "length") - 1;
336
+
337
+ if (prop === "push" || prop === "unshift") {
338
+ subscribers.forEach((subscriber) =>
339
+ subscriber(
340
+ XynCollectionChange.create(
341
+ prop === "push" ? LAST_INDEX : 0,
342
+ args[0],
343
+ CollectionValue.INSERT,
344
+ ),
345
+ ),
346
+ );
347
+ }
348
+
349
+ if (prop === "pop" || prop === "shift") {
350
+ subscribers.forEach((subscriber) =>
351
+ subscriber(
352
+ XynCollectionChange.create(
353
+ prop === "pop" ? LAST_INDEX : 0,
354
+ CollectionValue.DELETE,
355
+ result,
356
+ ),
357
+ ),
358
+ );
359
+ }
360
+
361
+ if (prop === "splice") {
362
+ const [start, deleteCount, ...items] = args;
363
+ subscribers.forEach((subscriber) =>
364
+ subscriber(
365
+ XynCollectionChange.create([start, deleteCount], items, result),
366
+ ),
367
+ );
368
+ }
369
+
370
+ return result;
371
+ };
372
+ }
373
+
374
+ if (typeof value === "object" && value !== null) {
375
+ return proxyFactory(value, subscribers);
376
+ }
377
+
378
+ return value;
379
+ },
380
+ getPrototypeOf(target) {
381
+ return Reflect.getPrototypeOf(target);
382
+ },
383
+ });
384
+ }
385
+
386
+ function createObjectProxy(obj, subscribers) {
387
+ return new Proxy(obj, {
388
+ get(target, prop, receiver) {
389
+ const value = Reflect.get(target, prop, receiver);
390
+ if (typeof value === "function") {
391
+ value = value();
392
+ }
393
+
394
+ if (typeof value === "object" && value !== null) {
395
+ return proxyFactory(value, subscribers);
396
+ }
397
+
398
+ return value;
399
+ },
400
+ set(target, prop, newValue) {
401
+ const previousValue = Reflect.get(target, prop);
402
+ Reflect.set(target, prop, newValue);
403
+ subscribers.forEach((subscriber) =>
404
+ subscriber(XynCollectionChange.create(prop, newValue, previousValue)),
405
+ );
406
+ return true;
407
+ },
408
+ deleteProperty(target, prop) {
409
+ const previousValue = Reflect.get(target, prop);
410
+ if (typeof prop === "symbol") {
411
+ prop = prop.description || prop.toString();
412
+ }
413
+ Reflect.deleteProperty(target, prop);
414
+ subscribers.forEach((subscriber) =>
415
+ subscriber(
416
+ XynCollectionChange.create(
417
+ prop,
418
+ CollectionValue.DELETE,
419
+ previousValue,
420
+ ),
421
+ ),
422
+ );
423
+ return true;
424
+ },
425
+ getPrototypeOf(target) {
426
+ return Reflect.getPrototypeOf(target);
427
+ },
428
+ });
429
+ }
430
+
431
+ function createObjectSignal(obj) {
432
+ const subscribers = new Set();
433
+
434
+ return {
435
+ value: createObjectProxy(obj, subscribers),
436
+ subscribe(subscriber) {
437
+ subscribers.add(subscriber);
438
+ return () => subscribers.delete(subscriber);
439
+ },
440
+ };
441
+ }
442
+
443
+ function createListSignal(list) {
444
+ const subscribers = new Set();
445
+
446
+ return {
447
+ value: createListProxy(list, subscribers),
448
+ subscribe(subscriber) {
449
+ subscribers.add(subscriber);
450
+ return () => subscribers.delete(subscriber);
451
+ },
452
+ };
453
+ }
454
+
455
+ function createCollectionSignal(collection) {
456
+ const subscribers = new Set();
457
+
458
+ return {
459
+ value: createCollectionProxy(collection, subscribers),
460
+ subscribe(subscriber) {
461
+ subscribers.add(subscriber);
462
+ return () => subscribers.delete(subscriber);
463
+ },
464
+ };
465
+ }
466
+
467
+ /**
468
+ * @typedef {Object} Watch
469
+ * @property {function(Object): Watch} watch
470
+ * @property {function(function({value: any, previousValue: any}): void): function(): void} effect
471
+ * @property {function(function(...any): any): {signal: Object, unsubscribe: function(): void}} derived
472
+ */
473
+
474
+ /**
475
+ * @function watch
476
+ * @param {Object} signal
477
+ * @returns {Watch}
478
+ * @description Watches the given signal and returns an object with methods to watch other signals and create effects.
479
+ * The effect method takes a subscriber function and returns a function to unsubscribe.
480
+ * The derived method takes a function and returns a signal and a function to unsubscribe.
481
+ * @example
482
+ * const signal = createSignal(0);
483
+ * const signal2 = createSignal(1);
484
+ * const unsubscribe = watch(signal)
485
+ * .watch(signal2)
486
+ * .effect(({value, previousValue}) => console.log(`Value changed from ${previousValue} to ${value}`));
487
+ */
488
+ export function watch(signal) {
489
+ const signals = new Set();
490
+
491
+ const watchers = (sig) => {
492
+ if (sig && typeof sig.subscribe === "function") {
493
+ signals.add(sig);
494
+ }
495
+ return {
496
+ watch(s) {
497
+ return watchers(s);
498
+ },
499
+ effect(subscriber) {
500
+ const unsubscribers = Array.from(signals.keys()).map((s) =>
501
+ s.subscribe(subscriber),
502
+ );
503
+
504
+ return () => {
505
+ unsubscribers.forEach((unsubscribe) => unsubscribe());
506
+ };
507
+ },
508
+ derived(fn, wrappingFn = (fn) => (change) => fn(change)) {
509
+ const derivedSignal = createSignal(fn());
510
+ const unsubscribers = Array.from(signals.keys()).map((s) =>
511
+ s.subscribe(
512
+ wrappingFn((change) => {
513
+ derivedSignal.value = fn(change);
514
+ }),
515
+ ),
516
+ );
517
+
518
+ return {
519
+ signal: derivedSignal,
520
+ unsubscribe: () =>
521
+ unsubscribers.forEach((unsubscribe) => unsubscribe()),
522
+ };
523
+ },
524
+ };
525
+ };
526
+
527
+ return watchers(signal);
528
+ }
529
+
530
+ /**
531
+ * @typedef {Object} Timing
532
+ * @property {function(function(...any): void): function(...any): void} debounce
533
+ * @property {function(function(...any): void): function(...any): void} throttle
534
+ * @property {function(function(...any): void): function(...any): void} delay
535
+ * @description Timing is an object with methods to debounce, throttle, and delay functions.
536
+ */
537
+
538
+ /**
539
+ * @function timing
540
+ * @param {int} delay
541
+ * @returns {Timing}
542
+ * @description Returns an object with methods to debounce, throttle, and delay functions.
543
+ * The debounce method takes a function and returns a function that will only call the given function after the given delay.
544
+ * The throttle method takes a function and returns a function that will only call the given function once per delay.
545
+ * The delay method takes a function and returns a function that will call the given function after the given delay.
546
+ * @example
547
+ * const debounced = timing(100).debounce(() => console.log("debounced"));
548
+ * const throttled = timing(100).throttle(() => console.log("throttled"));
549
+ * const delayed = timing(100).delay(() => console.log("delayed"));
550
+ * debounced(); // Will log "debounced" after 100ms
551
+ * throttled(); // Will log "throttled" immediately
552
+ * throttled(); // Will not log "throttled" again until 100ms have passed
553
+ * delayed(); // Will log "delayed" after 100ms
554
+ * debounced(); // Will not log "debounced" again until 100ms have passed
555
+ */
556
+ export function timing(delay) {
557
+ return {
558
+ debounce(fn) {
559
+ let timeoutId;
560
+ return (...args) => {
561
+ clearTimeout(timeoutId);
562
+ timeoutId = setTimeout(fn, delay, ...args);
563
+ };
564
+ },
565
+ throttle(fn) {
566
+ let lastCall = 0;
567
+ return (...args) => {
568
+ const now = Date.now();
569
+ if (now - lastCall >= delay) {
570
+ lastCall = now;
571
+ fn(...args);
572
+ }
573
+ };
574
+ },
575
+ delay(fn) {
576
+ return (...args) => {
577
+ setTimeout(fn, delay, ...args);
578
+ };
579
+ },
580
+ };
581
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@xyn-html/xyn-signal",
3
+ "version": "0.1.0",
4
+ "description": "Lightweight, reactive signal-based state management for JavaScript applications",
5
+ "type": "module",
6
+ "main": "dist/xyn_signal.js",
7
+ "module": "dist/xyn_signal.js",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/xyn_signal.js",
11
+ "default": "./dist/xyn_signal.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "build": "cp ../../src/xyn_signal.js dist/xyn_signal.js",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "signal",
25
+ "reactive",
26
+ "state-management",
27
+ "javascript",
28
+ "frontend",
29
+ "xyn"
30
+ ],
31
+ "author": "Jeffrey Tackett",
32
+ "license": "MIT",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "https://github/jfftck/XynHTML"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/jfftck/XynHTML"
39
+ },
40
+ "homepage": "https://github.com/jfftck/XynHTML"
41
+ }