@vaadin/hilla-react-signals 24.5.0-alpha8 → 24.5.0-beta1

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.
@@ -0,0 +1,101 @@
1
+ import type { ConnectClient, Subscription } from '@vaadin/hilla-frontend';
2
+ import { Signal } from './core.js';
3
+ import { type StateEvent } from './events.js';
4
+ /**
5
+ * An abstraction of a signal that tracks the number of subscribers, and calls
6
+ * the provided `onSubscribe` and `onUnsubscribe` callbacks for the first
7
+ * subscription and the last unsubscription, respectively.
8
+ * @internal
9
+ */
10
+ export declare abstract class DependencyTrackingSignal<T> extends Signal<T> {
11
+ #private;
12
+ protected constructor(value: T | undefined, onFirstSubscribe: () => void, onLastUnsubscribe: () => void);
13
+ protected S(node: unknown): void;
14
+ protected U(node: unknown): void;
15
+ }
16
+ /**
17
+ * An object that describes a data object to connect to the signal provider
18
+ * service.
19
+ */
20
+ export type ServerConnectionConfig = Readonly<{
21
+ /**
22
+ * The client instance to be used for communication.
23
+ */
24
+ client: ConnectClient;
25
+ /**
26
+ * The name of the signal provider service endpoint.
27
+ */
28
+ endpoint: string;
29
+ /**
30
+ * The name of the signal provider service method.
31
+ */
32
+ method: string;
33
+ /**
34
+ * Optional object with method call arguments to be sent to the endpoint
35
+ * method that provides the signal when subscribing to it.
36
+ */
37
+ params?: Record<string, unknown>;
38
+ }>;
39
+ /**
40
+ * A server connection manager.
41
+ */
42
+ declare class ServerConnection<T> {
43
+ #private;
44
+ constructor(id: string, config: ServerConnectionConfig);
45
+ get subscription(): Subscription<StateEvent<T>> | undefined;
46
+ connect(): Subscription<StateEvent<T>>;
47
+ update(event: StateEvent<T>): Promise<void>;
48
+ disconnect(): void;
49
+ }
50
+ export declare const $update: unique symbol;
51
+ export declare const $processServerResponse: unique symbol;
52
+ /**
53
+ * A signal that holds a shared value. Each change to the value is propagated to
54
+ * the server-side signal provider. At the same time, each change received from
55
+ * the server-side signal provider is propagated to the local signal and it's
56
+ * subscribers.
57
+ *
58
+ * @internal
59
+ */
60
+ export declare abstract class FullStackSignal<T> extends DependencyTrackingSignal<T> {
61
+ #private;
62
+ /**
63
+ * The unique identifier of the signal necessary to communicate with the
64
+ * server.
65
+ */
66
+ readonly id: string;
67
+ /**
68
+ * The server connection manager.
69
+ */
70
+ readonly server: ServerConnection<T>;
71
+ /**
72
+ * Defines whether the signal is currently awaits a server-side response.
73
+ */
74
+ readonly pending: import("@preact/signals-core").ReadonlySignal<boolean>;
75
+ /**
76
+ * Defines whether the signal has an error.
77
+ */
78
+ readonly error: import("@preact/signals-core").ReadonlySignal<Error | undefined>;
79
+ constructor(value: T | undefined, config: ServerConnectionConfig);
80
+ /**
81
+ * Sets the local value of the signal without sending any events to the server
82
+ * @param value - The new value.
83
+ * @internal
84
+ */
85
+ protected setValueLocal(value: T): void;
86
+ /**
87
+ * A method to update the server with the new value.
88
+ *
89
+ * @param event - The event to update the server with.
90
+ */
91
+ protected [$update](event: StateEvent<T>): void;
92
+ /**
93
+ * A method with to process the server response. The implementation is
94
+ * specific for each signal type.
95
+ *
96
+ * @param event - The server response event.
97
+ */
98
+ protected abstract [$processServerResponse](event: StateEvent<T>): void;
99
+ }
100
+ export {};
101
+ //# sourceMappingURL=FullStackSignal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FullStackSignal.d.ts","sourceRoot":"","sources":["src/FullStackSignal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE1E,OAAO,EAAoB,MAAM,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAuB,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAInE;;;;;GAKG;AACH,8BAAsB,wBAAwB,CAAC,CAAC,CAAE,SAAQ,MAAM,CAAC,CAAC,CAAC;;IAQjE,SAAS,aAAa,KAAK,EAAE,CAAC,GAAG,SAAS,EAAE,gBAAgB,EAAE,MAAM,IAAI,EAAE,iBAAiB,EAAE,MAAM,IAAI;cAMpF,CAAC,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;cAQtB,CAAC,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;CAO1C;AAED;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,QAAQ,CAAC;IAC5C;;OAEG;IACH,MAAM,EAAE,aAAa,CAAC;IAEtB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC,CAAC,CAAC;AAEH;;GAEG;AACH,cAAM,gBAAgB,CAAC,CAAC;;gBAKV,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB;IAKtD,IAAI,YAAY,4CAEf;IAED,OAAO;IAaD,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOjD,UAAU;CAIX;AAED,eAAO,MAAM,OAAO,eAAmB,CAAC;AACxC,eAAO,MAAM,sBAAsB,eAAkC,CAAC;AAEtE;;;;;;;GAOG;AACH,8BAAsB,eAAe,CAAC,CAAC,CAAE,SAAQ,wBAAwB,CAAC,CAAC,CAAC;;IAC1E;;;OAGG;IACH,QAAQ,CAAC,EAAE,SAAY;IAEvB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAErC;;OAEG;IACH,QAAQ,CAAC,OAAO,yDAAuC;IAEvD;;OAEG;IACH,QAAQ,CAAC,KAAK,mEAAqC;gBASvC,KAAK,EAAE,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,sBAAsB;IAmBhE;;;;OAIG;IACH,SAAS,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAMvC;;;;OAIG;IACH,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;IAW/C;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,CAAC,sBAAsB,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;CAgBxE"}
@@ -0,0 +1,146 @@
1
+ import { nanoid } from "nanoid";
2
+ import { computed, signal, Signal } from "./core.js";
3
+ import { createSetStateEvent } from "./events.js";
4
+ const ENDPOINT = "SignalsHandler";
5
+ class DependencyTrackingSignal extends Signal {
6
+ #onFirstSubscribe;
7
+ #onLastUnsubscribe;
8
+ // -1 means to ignore the first subscription that is created internally in the
9
+ // FullStackSignal constructor.
10
+ #subscribeCount = -1;
11
+ constructor(value, onFirstSubscribe, onLastUnsubscribe) {
12
+ super(value);
13
+ this.#onFirstSubscribe = onFirstSubscribe;
14
+ this.#onLastUnsubscribe = onLastUnsubscribe;
15
+ }
16
+ S(node) {
17
+ super.S(node);
18
+ if (this.#subscribeCount === 0) {
19
+ this.#onFirstSubscribe();
20
+ }
21
+ this.#subscribeCount += 1;
22
+ }
23
+ U(node) {
24
+ super.U(node);
25
+ this.#subscribeCount -= 1;
26
+ if (this.#subscribeCount === 0) {
27
+ this.#onLastUnsubscribe();
28
+ }
29
+ }
30
+ }
31
+ class ServerConnection {
32
+ #id;
33
+ #config;
34
+ #subscription;
35
+ constructor(id, config) {
36
+ this.#config = config;
37
+ this.#id = id;
38
+ }
39
+ get subscription() {
40
+ return this.#subscription;
41
+ }
42
+ connect() {
43
+ const { client, endpoint, method, params } = this.#config;
44
+ this.#subscription ??= client.subscribe(ENDPOINT, "subscribe", {
45
+ providerEndpoint: endpoint,
46
+ providerMethod: method,
47
+ clientSignalId: this.#id,
48
+ params
49
+ });
50
+ return this.#subscription;
51
+ }
52
+ async update(event) {
53
+ await this.#config.client.call(ENDPOINT, "update", {
54
+ clientSignalId: this.#id,
55
+ event
56
+ });
57
+ }
58
+ disconnect() {
59
+ this.#subscription?.cancel();
60
+ this.#subscription = void 0;
61
+ }
62
+ }
63
+ const $update = Symbol("update");
64
+ const $processServerResponse = Symbol("processServerResponse");
65
+ class FullStackSignal extends DependencyTrackingSignal {
66
+ /**
67
+ * The unique identifier of the signal necessary to communicate with the
68
+ * server.
69
+ */
70
+ id = nanoid();
71
+ /**
72
+ * The server connection manager.
73
+ */
74
+ server;
75
+ /**
76
+ * Defines whether the signal is currently awaits a server-side response.
77
+ */
78
+ pending = computed(() => this.#pending.value);
79
+ /**
80
+ * Defines whether the signal has an error.
81
+ */
82
+ error = computed(() => this.#error.value);
83
+ #pending = signal(false);
84
+ #error = signal(void 0);
85
+ // Paused at the very start to prevent the signal from sending the initial
86
+ // value to the server.
87
+ #paused = true;
88
+ constructor(value, config) {
89
+ super(
90
+ value,
91
+ () => this.#connect(),
92
+ () => this.#disconnect()
93
+ );
94
+ this.server = new ServerConnection(this.id, config);
95
+ this.subscribe((v) => {
96
+ if (!this.#paused) {
97
+ this.#pending.value = true;
98
+ this.#error.value = void 0;
99
+ this[$update](createSetStateEvent(v));
100
+ }
101
+ });
102
+ this.#paused = false;
103
+ }
104
+ /**
105
+ * Sets the local value of the signal without sending any events to the server
106
+ * @param value - The new value.
107
+ * @internal
108
+ */
109
+ setValueLocal(value) {
110
+ this.#paused = true;
111
+ this.value = value;
112
+ this.#paused = false;
113
+ }
114
+ /**
115
+ * A method to update the server with the new value.
116
+ *
117
+ * @param event - The event to update the server with.
118
+ */
119
+ [$update](event) {
120
+ this.server.update(event).catch((error) => {
121
+ this.#error.value = error instanceof Error ? error : new Error(String(error));
122
+ }).finally(() => {
123
+ this.#pending.value = false;
124
+ });
125
+ }
126
+ #connect() {
127
+ this.server.connect().onNext((event) => {
128
+ this.#paused = true;
129
+ this[$processServerResponse](event);
130
+ this.#paused = false;
131
+ });
132
+ }
133
+ #disconnect() {
134
+ if (this.server.subscription === void 0) {
135
+ return;
136
+ }
137
+ this.server.disconnect();
138
+ }
139
+ }
140
+ export {
141
+ $processServerResponse,
142
+ $update,
143
+ DependencyTrackingSignal,
144
+ FullStackSignal
145
+ };
146
+ //# sourceMappingURL=FullStackSignal.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/FullStackSignal.ts"],
4
+ "sourcesContent": ["import type { ConnectClient, Subscription } from '@vaadin/hilla-frontend';\nimport { nanoid } from 'nanoid';\nimport { computed, signal, Signal } from './core.js';\nimport { createSetStateEvent, type StateEvent } from './events.js';\n\nconst ENDPOINT = 'SignalsHandler';\n\n/**\n * An abstraction of a signal that tracks the number of subscribers, and calls\n * the provided `onSubscribe` and `onUnsubscribe` callbacks for the first\n * subscription and the last unsubscription, respectively.\n * @internal\n */\nexport abstract class DependencyTrackingSignal<T> extends Signal<T> {\n readonly #onFirstSubscribe: () => void;\n readonly #onLastUnsubscribe: () => void;\n\n // -1 means to ignore the first subscription that is created internally in the\n // FullStackSignal constructor.\n #subscribeCount = -1;\n\n protected constructor(value: T | undefined, onFirstSubscribe: () => void, onLastUnsubscribe: () => void) {\n super(value);\n this.#onFirstSubscribe = onFirstSubscribe;\n this.#onLastUnsubscribe = onLastUnsubscribe;\n }\n\n protected override S(node: unknown): void {\n super.S(node);\n if (this.#subscribeCount === 0) {\n this.#onFirstSubscribe();\n }\n this.#subscribeCount += 1;\n }\n\n protected override U(node: unknown): void {\n super.U(node);\n this.#subscribeCount -= 1;\n if (this.#subscribeCount === 0) {\n this.#onLastUnsubscribe();\n }\n }\n}\n\n/**\n * An object that describes a data object to connect to the signal provider\n * service.\n */\nexport type ServerConnectionConfig = Readonly<{\n /**\n * The client instance to be used for communication.\n */\n client: ConnectClient;\n\n /**\n * The name of the signal provider service endpoint.\n */\n endpoint: string;\n\n /**\n * The name of the signal provider service method.\n */\n method: string;\n\n /**\n * Optional object with method call arguments to be sent to the endpoint\n * method that provides the signal when subscribing to it.\n */\n params?: Record<string, unknown>;\n}>;\n\n/**\n * A server connection manager.\n */\nclass ServerConnection<T> {\n readonly #id: string;\n readonly #config: ServerConnectionConfig;\n #subscription?: Subscription<StateEvent<T>>;\n\n constructor(id: string, config: ServerConnectionConfig) {\n this.#config = config;\n this.#id = id;\n }\n\n get subscription() {\n return this.#subscription;\n }\n\n connect() {\n const { client, endpoint, method, params } = this.#config;\n\n this.#subscription ??= client.subscribe(ENDPOINT, 'subscribe', {\n providerEndpoint: endpoint,\n providerMethod: method,\n clientSignalId: this.#id,\n params,\n });\n\n return this.#subscription;\n }\n\n async update(event: StateEvent<T>): Promise<void> {\n await this.#config.client.call(ENDPOINT, 'update', {\n clientSignalId: this.#id,\n event,\n });\n }\n\n disconnect() {\n this.#subscription?.cancel();\n this.#subscription = undefined;\n }\n}\n\nexport const $update = Symbol('update');\nexport const $processServerResponse = Symbol('processServerResponse');\n\n/**\n * A signal that holds a shared value. Each change to the value is propagated to\n * the server-side signal provider. At the same time, each change received from\n * the server-side signal provider is propagated to the local signal and it's\n * subscribers.\n *\n * @internal\n */\nexport abstract class FullStackSignal<T> extends DependencyTrackingSignal<T> {\n /**\n * The unique identifier of the signal necessary to communicate with the\n * server.\n */\n readonly id = nanoid();\n\n /**\n * The server connection manager.\n */\n readonly server: ServerConnection<T>;\n\n /**\n * Defines whether the signal is currently awaits a server-side response.\n */\n readonly pending = computed(() => this.#pending.value);\n\n /**\n * Defines whether the signal has an error.\n */\n readonly error = computed(() => this.#error.value);\n\n readonly #pending = signal(false);\n readonly #error = signal<Error | undefined>(undefined);\n\n // Paused at the very start to prevent the signal from sending the initial\n // value to the server.\n #paused = true;\n\n constructor(value: T | undefined, config: ServerConnectionConfig) {\n super(\n value,\n () => this.#connect(),\n () => this.#disconnect(),\n );\n this.server = new ServerConnection(this.id, config);\n\n this.subscribe((v) => {\n if (!this.#paused) {\n this.#pending.value = true;\n this.#error.value = undefined;\n this[$update](createSetStateEvent(v));\n }\n });\n\n this.#paused = false;\n }\n\n /**\n * Sets the local value of the signal without sending any events to the server\n * @param value - The new value.\n * @internal\n */\n protected setValueLocal(value: T): void {\n this.#paused = true;\n this.value = value;\n this.#paused = false;\n }\n\n /**\n * A method to update the server with the new value.\n *\n * @param event - The event to update the server with.\n */\n protected [$update](event: StateEvent<T>): void {\n this.server\n .update(event)\n .catch((error: unknown) => {\n this.#error.value = error instanceof Error ? error : new Error(String(error));\n })\n .finally(() => {\n this.#pending.value = false;\n });\n }\n\n /**\n * A method with to process the server response. The implementation is\n * specific for each signal type.\n *\n * @param event - The server response event.\n */\n protected abstract [$processServerResponse](event: StateEvent<T>): void;\n\n #connect() {\n this.server.connect().onNext((event: StateEvent<T>) => {\n this.#paused = true;\n this[$processServerResponse](event);\n this.#paused = false;\n });\n }\n\n #disconnect() {\n if (this.server.subscription === undefined) {\n return;\n }\n this.server.disconnect();\n }\n}\n"],
5
+ "mappings": "AACA,SAAS,cAAc;AACvB,SAAS,UAAU,QAAQ,cAAc;AACzC,SAAS,2BAA4C;AAErD,MAAM,WAAW;AAQV,MAAe,iCAAoC,OAAU;AAAA,EACzD;AAAA,EACA;AAAA;AAAA;AAAA,EAIT,kBAAkB;AAAA,EAER,YAAY,OAAsB,kBAA8B,mBAA+B;AACvG,UAAM,KAAK;AACX,SAAK,oBAAoB;AACzB,SAAK,qBAAqB;AAAA,EAC5B;AAAA,EAEmB,EAAE,MAAqB;AACxC,UAAM,EAAE,IAAI;AACZ,QAAI,KAAK,oBAAoB,GAAG;AAC9B,WAAK,kBAAkB;AAAA,IACzB;AACA,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEmB,EAAE,MAAqB;AACxC,UAAM,EAAE,IAAI;AACZ,SAAK,mBAAmB;AACxB,QAAI,KAAK,oBAAoB,GAAG;AAC9B,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AACF;AAgCA,MAAM,iBAAoB;AAAA,EACf;AAAA,EACA;AAAA,EACT;AAAA,EAEA,YAAY,IAAY,QAAgC;AACtD,SAAK,UAAU;AACf,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,IAAI,eAAe;AACjB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAU;AACR,UAAM,EAAE,QAAQ,UAAU,QAAQ,OAAO,IAAI,KAAK;AAElD,SAAK,kBAAkB,OAAO,UAAU,UAAU,aAAa;AAAA,MAC7D,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB,gBAAgB,KAAK;AAAA,MACrB;AAAA,IACF,CAAC;AAED,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,OAAO,OAAqC;AAChD,UAAM,KAAK,QAAQ,OAAO,KAAK,UAAU,UAAU;AAAA,MACjD,gBAAgB,KAAK;AAAA,MACrB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,aAAa;AACX,SAAK,eAAe,OAAO;AAC3B,SAAK,gBAAgB;AAAA,EACvB;AACF;AAEO,MAAM,UAAU,OAAO,QAAQ;AAC/B,MAAM,yBAAyB,OAAO,uBAAuB;AAU7D,MAAe,wBAA2B,yBAA4B;AAAA;AAAA;AAAA;AAAA;AAAA,EAKlE,KAAK,OAAO;AAAA;AAAA;AAAA;AAAA,EAKZ;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,SAAS,MAAM,KAAK,SAAS,KAAK;AAAA;AAAA;AAAA;AAAA,EAK5C,QAAQ,SAAS,MAAM,KAAK,OAAO,KAAK;AAAA,EAExC,WAAW,OAAO,KAAK;AAAA,EACvB,SAAS,OAA0B,MAAS;AAAA;AAAA;AAAA,EAIrD,UAAU;AAAA,EAEV,YAAY,OAAsB,QAAgC;AAChE;AAAA,MACE;AAAA,MACA,MAAM,KAAK,SAAS;AAAA,MACpB,MAAM,KAAK,YAAY;AAAA,IACzB;AACA,SAAK,SAAS,IAAI,iBAAiB,KAAK,IAAI,MAAM;AAElD,SAAK,UAAU,CAAC,MAAM;AACpB,UAAI,CAAC,KAAK,SAAS;AACjB,aAAK,SAAS,QAAQ;AACtB,aAAK,OAAO,QAAQ;AACpB,aAAK,OAAO,EAAE,oBAAoB,CAAC,CAAC;AAAA,MACtC;AAAA,IACF,CAAC;AAED,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOU,cAAc,OAAgB;AACtC,SAAK,UAAU;AACf,SAAK,QAAQ;AACb,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,CAAW,OAAO,EAAE,OAA4B;AAC9C,SAAK,OACF,OAAO,KAAK,EACZ,MAAM,CAAC,UAAmB;AACzB,WAAK,OAAO,QAAQ,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,IAC9E,CAAC,EACA,QAAQ,MAAM;AACb,WAAK,SAAS,QAAQ;AAAA,IACxB,CAAC;AAAA,EACL;AAAA,EAUA,WAAW;AACT,SAAK,OAAO,QAAQ,EAAE,OAAO,CAAC,UAAyB;AACrD,WAAK,UAAU;AACf,WAAK,sBAAsB,EAAE,KAAK;AAClC,WAAK,UAAU;AAAA,IACjB,CAAC;AAAA,EACH;AAAA,EAEA,cAAc;AACZ,QAAI,KAAK,OAAO,iBAAiB,QAAW;AAC1C;AAAA,IACF;AACA,SAAK,OAAO,WAAW;AAAA,EACzB;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,40 @@
1
+ import { ValueSignal } from './ValueSignal.js';
2
+ /**
3
+ * A signal that holds a number value. The underlying
4
+ * value of this signal is stored and updated as a
5
+ * shared value on the server.
6
+ *
7
+ * After obtaining the NumberSignal instance from
8
+ * a server-side service that returns one, the value
9
+ * can be updated using the `value` property,
10
+ * and it can be read with or without the
11
+ * `value` property (similar to a normal signal):
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * const counter = CounterService.counter();
16
+ *
17
+ * return (
18
+ * <Button onClick={() => counter.incrementBy(1)}>
19
+ * Click count: { counter }
20
+ * </Button>
21
+ * <Button onClick={() => counter.value = 0}>Reset</Button>
22
+ * );
23
+ * ```
24
+ */
25
+ export declare class NumberSignal extends ValueSignal<number> {
26
+ /**
27
+ * Increments the value by the specified delta. The delta can be negative to
28
+ * decrease the value.
29
+ *
30
+ * This method differs from using the `++` or `+=` operators directly on the
31
+ * signal value. It performs an atomic operation to prevent conflicts from
32
+ * concurrent changes, ensuring that other users' modifications are not
33
+ * accidentally overwritten.
34
+ *
35
+ * @param delta - The delta to increment the value by. The delta can be
36
+ * negative.
37
+ */
38
+ incrementBy(delta: number): void;
39
+ }
40
+ //# sourceMappingURL=NumberSignal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NumberSignal.d.ts","sourceRoot":"","sources":["src/NumberSignal.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,YAAa,SAAQ,WAAW,CAAC,MAAM,CAAC;IACnD;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAQjC"}
@@ -0,0 +1,29 @@
1
+ import { createIncrementStateEvent } from "./events.js";
2
+ import { $update } from "./FullStackSignal.js";
3
+ import { ValueSignal } from "./ValueSignal.js";
4
+ class NumberSignal extends ValueSignal {
5
+ /**
6
+ * Increments the value by the specified delta. The delta can be negative to
7
+ * decrease the value.
8
+ *
9
+ * This method differs from using the `++` or `+=` operators directly on the
10
+ * signal value. It performs an atomic operation to prevent conflicts from
11
+ * concurrent changes, ensuring that other users' modifications are not
12
+ * accidentally overwritten.
13
+ *
14
+ * @param delta - The delta to increment the value by. The delta can be
15
+ * negative.
16
+ */
17
+ incrementBy(delta) {
18
+ if (delta === 0) {
19
+ return;
20
+ }
21
+ this.setValueLocal(this.value + delta);
22
+ const event = createIncrementStateEvent(delta);
23
+ this[$update](event);
24
+ }
25
+ }
26
+ export {
27
+ NumberSignal
28
+ };
29
+ //# sourceMappingURL=NumberSignal.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/NumberSignal.ts"],
4
+ "sourcesContent": ["import { createIncrementStateEvent } from './events.js';\nimport { $update } from './FullStackSignal.js';\nimport { ValueSignal } from './ValueSignal.js';\n\n/**\n * A signal that holds a number value. The underlying\n * value of this signal is stored and updated as a\n * shared value on the server.\n *\n * After obtaining the NumberSignal instance from\n * a server-side service that returns one, the value\n * can be updated using the `value` property,\n * and it can be read with or without the\n * `value` property (similar to a normal signal):\n *\n * @example\n * ```tsx\n * const counter = CounterService.counter();\n *\n * return (\n * <Button onClick={() => counter.incrementBy(1)}>\n * Click count: { counter }\n * </Button>\n * <Button onClick={() => counter.value = 0}>Reset</Button>\n * );\n * ```\n */\nexport class NumberSignal extends ValueSignal<number> {\n /**\n * Increments the value by the specified delta. The delta can be negative to\n * decrease the value.\n *\n * This method differs from using the `++` or `+=` operators directly on the\n * signal value. It performs an atomic operation to prevent conflicts from\n * concurrent changes, ensuring that other users' modifications are not\n * accidentally overwritten.\n *\n * @param delta - The delta to increment the value by. The delta can be\n * negative.\n */\n incrementBy(delta: number): void {\n if (delta === 0) {\n return;\n }\n this.setValueLocal(this.value + delta);\n const event = createIncrementStateEvent(delta);\n this[$update](event);\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,iCAAiC;AAC1C,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAyBrB,MAAM,qBAAqB,YAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAapD,YAAY,OAAqB;AAC/B,QAAI,UAAU,GAAG;AACf;AAAA,IACF;AACA,SAAK,cAAc,KAAK,QAAQ,KAAK;AACrC,UAAM,QAAQ,0BAA0B,KAAK;AAC7C,SAAK,OAAO,EAAE,KAAK;AAAA,EACrB;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,49 @@
1
+ import { type StateEvent } from './events.js';
2
+ import { $processServerResponse, FullStackSignal } from './FullStackSignal.js';
3
+ /**
4
+ * An operation subscription that can be canceled.
5
+ */
6
+ export interface OperationSubscription {
7
+ cancel(): void;
8
+ }
9
+ /**
10
+ * A full-stack signal that holds an arbitrary value.
11
+ */
12
+ export declare class ValueSignal<T> extends FullStackSignal<T> {
13
+ #private;
14
+ /**
15
+ * Sets the value.
16
+ * Note that the value change event that is propagated to the server as the
17
+ * result of this operation is not taking the last seen value into account and
18
+ * will overwrite the shared value on the server unconditionally (AKA: "Last
19
+ * Write Wins"). If you need to perform a conditional update, use the
20
+ * `replace` method instead.
21
+ *
22
+ * @param value - The new value.
23
+ */
24
+ set(value: T): void;
25
+ /**
26
+ * Replaces the value with a new one only if the current value is equal to the
27
+ * expected value.
28
+ *
29
+ * @param expected - The expected value.
30
+ * @param newValue - The new value.
31
+ */
32
+ replace(expected: T, newValue: T): void;
33
+ /**
34
+ * Tries to update the value by applying the callback function to the current
35
+ * value. In case of a concurrent change, the callback is run again with an
36
+ * updated input value. This is repeated until the result can be applied
37
+ * without concurrent changes, or the operation is canceled.
38
+ *
39
+ * Note that there is no guarantee that cancel() will be effective always,
40
+ * since a succeeding operation might already be on its way to the server.
41
+ *
42
+ * @param callback - The function that is applied on the current value to
43
+ * produce the new value.
44
+ * @returns An operation subscription that can be canceled.
45
+ */
46
+ update(callback: (value: T) => T): OperationSubscription;
47
+ protected [$processServerResponse](event: StateEvent<T>): void;
48
+ }
49
+ //# sourceMappingURL=ValueSignal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ValueSignal.d.ts","sourceRoot":"","sources":["src/ValueSignal.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAW,eAAe,EAAE,MAAM,sBAAsB,CAAC;AASxF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,IAAI,IAAI,CAAC;CAChB;AAED;;GAEG;AACH,qBAAa,WAAW,CAAC,CAAC,CAAE,SAAQ,eAAe,CAAC,CAAC,CAAC;;IAGpD;;;;;;;;;OASG;IACH,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAInB;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI;IAIvC;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,qBAAqB;cAerC,CAAC,sBAAsB,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI;CAoBxE"}
package/ValueSignal.js ADDED
@@ -0,0 +1,76 @@
1
+ import { createReplaceStateEvent } from "./events.js";
2
+ import { $processServerResponse, $update, FullStackSignal } from "./FullStackSignal.js";
3
+ class ValueSignal extends FullStackSignal {
4
+ #pendingRequests = /* @__PURE__ */ new Map();
5
+ /**
6
+ * Sets the value.
7
+ * Note that the value change event that is propagated to the server as the
8
+ * result of this operation is not taking the last seen value into account and
9
+ * will overwrite the shared value on the server unconditionally (AKA: "Last
10
+ * Write Wins"). If you need to perform a conditional update, use the
11
+ * `replace` method instead.
12
+ *
13
+ * @param value - The new value.
14
+ */
15
+ set(value) {
16
+ this.value = value;
17
+ }
18
+ /**
19
+ * Replaces the value with a new one only if the current value is equal to the
20
+ * expected value.
21
+ *
22
+ * @param expected - The expected value.
23
+ * @param newValue - The new value.
24
+ */
25
+ replace(expected, newValue) {
26
+ this[$update](createReplaceStateEvent(expected, newValue));
27
+ }
28
+ /**
29
+ * Tries to update the value by applying the callback function to the current
30
+ * value. In case of a concurrent change, the callback is run again with an
31
+ * updated input value. This is repeated until the result can be applied
32
+ * without concurrent changes, or the operation is canceled.
33
+ *
34
+ * Note that there is no guarantee that cancel() will be effective always,
35
+ * since a succeeding operation might already be on its way to the server.
36
+ *
37
+ * @param callback - The function that is applied on the current value to
38
+ * produce the new value.
39
+ * @returns An operation subscription that can be canceled.
40
+ */
41
+ update(callback) {
42
+ const newValue = callback(this.value);
43
+ const event = createReplaceStateEvent(this.value, newValue);
44
+ this[$update](event);
45
+ const waiter = Promise.withResolvers();
46
+ const pendingRequest = { callback, waiter, canceled: false };
47
+ this.#pendingRequests.set(event.id, pendingRequest);
48
+ return {
49
+ cancel: () => {
50
+ pendingRequest.canceled = true;
51
+ pendingRequest.waiter.resolve();
52
+ }
53
+ };
54
+ }
55
+ [$processServerResponse](event) {
56
+ const record = this.#pendingRequests.get(event.id);
57
+ if (record) {
58
+ this.#pendingRequests.delete(event.id);
59
+ }
60
+ if (event.type === "snapshot") {
61
+ if (record) {
62
+ record.waiter.resolve();
63
+ }
64
+ this.value = event.value;
65
+ }
66
+ if (event.type === "reject" && record) {
67
+ if (!record.canceled) {
68
+ this.update(record.callback);
69
+ }
70
+ }
71
+ }
72
+ }
73
+ export {
74
+ ValueSignal
75
+ };
76
+ //# sourceMappingURL=ValueSignal.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/ValueSignal.ts"],
4
+ "sourcesContent": ["import { createReplaceStateEvent, type StateEvent } from './events.js';\nimport { $processServerResponse, $update, FullStackSignal } from './FullStackSignal.js';\n\ntype PromiseWithResolvers = ReturnType<typeof Promise.withResolvers<void>>;\ntype PendingRequestsRecord<T> = Readonly<{\n waiter: PromiseWithResolvers;\n callback(value: T): T;\n canceled: boolean;\n}>;\n\n/**\n * An operation subscription that can be canceled.\n */\nexport interface OperationSubscription {\n cancel(): void;\n}\n\n/**\n * A full-stack signal that holds an arbitrary value.\n */\nexport class ValueSignal<T> extends FullStackSignal<T> {\n readonly #pendingRequests = new Map<string, PendingRequestsRecord<T>>();\n\n /**\n * Sets the value.\n * Note that the value change event that is propagated to the server as the\n * result of this operation is not taking the last seen value into account and\n * will overwrite the shared value on the server unconditionally (AKA: \"Last\n * Write Wins\"). If you need to perform a conditional update, use the\n * `replace` method instead.\n *\n * @param value - The new value.\n */\n set(value: T): void {\n this.value = value;\n }\n\n /**\n * Replaces the value with a new one only if the current value is equal to the\n * expected value.\n *\n * @param expected - The expected value.\n * @param newValue - The new value.\n */\n replace(expected: T, newValue: T): void {\n this[$update](createReplaceStateEvent(expected, newValue));\n }\n\n /**\n * Tries to update the value by applying the callback function to the current\n * value. In case of a concurrent change, the callback is run again with an\n * updated input value. This is repeated until the result can be applied\n * without concurrent changes, or the operation is canceled.\n *\n * Note that there is no guarantee that cancel() will be effective always,\n * since a succeeding operation might already be on its way to the server.\n *\n * @param callback - The function that is applied on the current value to\n * produce the new value.\n * @returns An operation subscription that can be canceled.\n */\n update(callback: (value: T) => T): OperationSubscription {\n const newValue = callback(this.value);\n const event = createReplaceStateEvent(this.value, newValue);\n this[$update](event);\n const waiter = Promise.withResolvers<void>();\n const pendingRequest = { callback, waiter, canceled: false };\n this.#pendingRequests.set(event.id, pendingRequest);\n return {\n cancel: () => {\n pendingRequest.canceled = true;\n pendingRequest.waiter.resolve();\n },\n };\n }\n\n protected override [$processServerResponse](event: StateEvent<T>): void {\n const record = this.#pendingRequests.get(event.id);\n if (record) {\n this.#pendingRequests.delete(event.id);\n }\n\n if (event.type === 'snapshot') {\n if (record) {\n record.waiter.resolve();\n }\n this.value = event.value;\n }\n\n if (event.type === 'reject' && record) {\n if (!record.canceled) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.update(record.callback);\n }\n }\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,+BAAgD;AACzD,SAAS,wBAAwB,SAAS,uBAAuB;AAmB1D,MAAM,oBAAuB,gBAAmB;AAAA,EAC5C,mBAAmB,oBAAI,IAAsC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYtE,IAAI,OAAgB;AAClB,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,QAAQ,UAAa,UAAmB;AACtC,SAAK,OAAO,EAAE,wBAAwB,UAAU,QAAQ,CAAC;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,OAAO,UAAkD;AACvD,UAAM,WAAW,SAAS,KAAK,KAAK;AACpC,UAAM,QAAQ,wBAAwB,KAAK,OAAO,QAAQ;AAC1D,SAAK,OAAO,EAAE,KAAK;AACnB,UAAM,SAAS,QAAQ,cAAoB;AAC3C,UAAM,iBAAiB,EAAE,UAAU,QAAQ,UAAU,MAAM;AAC3D,SAAK,iBAAiB,IAAI,MAAM,IAAI,cAAc;AAClD,WAAO;AAAA,MACL,QAAQ,MAAM;AACZ,uBAAe,WAAW;AAC1B,uBAAe,OAAO,QAAQ;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AAAA,EAEA,CAAoB,sBAAsB,EAAE,OAA4B;AACtE,UAAM,SAAS,KAAK,iBAAiB,IAAI,MAAM,EAAE;AACjD,QAAI,QAAQ;AACV,WAAK,iBAAiB,OAAO,MAAM,EAAE;AAAA,IACvC;AAEA,QAAI,MAAM,SAAS,YAAY;AAC7B,UAAI,QAAQ;AACV,eAAO,OAAO,QAAQ;AAAA,MACxB;AACA,WAAK,QAAQ,MAAM;AAAA,IACrB;AAEA,QAAI,MAAM,SAAS,YAAY,QAAQ;AACrC,UAAI,CAAC,OAAO,UAAU;AAEpB,aAAK,OAAO,OAAO,QAAQ;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
package/events.d.ts ADDED
@@ -0,0 +1,33 @@
1
+ import type { Simplify } from 'type-fest';
2
+ /**
3
+ * Creates a new state event type.
4
+ */
5
+ type CreateStateEventType<V, T extends string, C extends Record<string, unknown> = Record<never, never>> = Simplify<Readonly<{
6
+ id: string;
7
+ type: T;
8
+ value: V;
9
+ }> & Readonly<C>>;
10
+ /**
11
+ * A state event received from the server describing the current state of the
12
+ * signal.
13
+ */
14
+ export type SnapshotStateEvent<T> = CreateStateEventType<T, 'snapshot'>;
15
+ export type RejectStateEvent = CreateStateEventType<never, 'reject'>;
16
+ /**
17
+ * A state event defines a new value of the signal shared with the server. The
18
+ *
19
+ */
20
+ export type SetStateEvent<T> = CreateStateEventType<T, 'set'>;
21
+ export declare function createSetStateEvent<T>(value: T): SetStateEvent<T>;
22
+ export type ReplaceStateEvent<T> = CreateStateEventType<T, 'replace', {
23
+ expected: T;
24
+ }>;
25
+ export declare function createReplaceStateEvent<T>(expected: T, value: T): ReplaceStateEvent<T>;
26
+ export type IncrementStateEvent = CreateStateEventType<number, 'increment'>;
27
+ export declare function createIncrementStateEvent(delta: number): IncrementStateEvent;
28
+ /**
29
+ * An object that describes the change of the signal state.
30
+ */
31
+ export type StateEvent<T> = IncrementStateEvent | RejectStateEvent | ReplaceStateEvent<T> | SetStateEvent<T> | SnapshotStateEvent<T>;
32
+ export {};
33
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["src/events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C;;GAEG;AACH,KAAK,oBAAoB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,QAAQ,CACjH,QAAQ,CAAC;IACP,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,CAAC,CAAC;IACR,KAAK,EAAE,CAAC,CAAC;CACV,CAAC,GACA,QAAQ,CAAC,CAAC,CAAC,CACd,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,IAAI,oBAAoB,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;AAExE,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAErE;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,CAAC,IAAI,oBAAoB,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAE9D,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAMjE;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAAI,oBAAoB,CAAC,CAAC,EAAE,SAAS,EAAE;IAAE,QAAQ,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAEvF,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAOtF;AAED,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE5E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAM5E;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IACpB,mBAAmB,GACnB,gBAAgB,GAChB,iBAAiB,CAAC,CAAC,CAAC,GACpB,aAAa,CAAC,CAAC,CAAC,GAChB,kBAAkB,CAAC,CAAC,CAAC,CAAC"}
package/events.js ADDED
@@ -0,0 +1,29 @@
1
+ import { nanoid } from "nanoid";
2
+ function createSetStateEvent(value) {
3
+ return {
4
+ id: nanoid(),
5
+ type: "set",
6
+ value
7
+ };
8
+ }
9
+ function createReplaceStateEvent(expected, value) {
10
+ return {
11
+ id: nanoid(),
12
+ type: "replace",
13
+ value,
14
+ expected
15
+ };
16
+ }
17
+ function createIncrementStateEvent(delta) {
18
+ return {
19
+ id: nanoid(),
20
+ type: "increment",
21
+ value: delta
22
+ };
23
+ }
24
+ export {
25
+ createIncrementStateEvent,
26
+ createReplaceStateEvent,
27
+ createSetStateEvent
28
+ };
29
+ //# sourceMappingURL=events.js.map
package/events.js.map ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/events.ts"],
4
+ "sourcesContent": ["import { nanoid } from 'nanoid';\nimport type { Simplify } from 'type-fest';\n\n/**\n * Creates a new state event type.\n */\ntype CreateStateEventType<V, T extends string, C extends Record<string, unknown> = Record<never, never>> = Simplify<\n Readonly<{\n id: string;\n type: T;\n value: V;\n }> &\n Readonly<C>\n>;\n\n/**\n * A state event received from the server describing the current state of the\n * signal.\n */\nexport type SnapshotStateEvent<T> = CreateStateEventType<T, 'snapshot'>;\n\nexport type RejectStateEvent = CreateStateEventType<never, 'reject'>;\n\n/**\n * A state event defines a new value of the signal shared with the server. The\n *\n */\nexport type SetStateEvent<T> = CreateStateEventType<T, 'set'>;\n\nexport function createSetStateEvent<T>(value: T): SetStateEvent<T> {\n return {\n id: nanoid(),\n type: 'set',\n value,\n };\n}\n\nexport type ReplaceStateEvent<T> = CreateStateEventType<T, 'replace', { expected: T }>;\n\nexport function createReplaceStateEvent<T>(expected: T, value: T): ReplaceStateEvent<T> {\n return {\n id: nanoid(),\n type: 'replace',\n value,\n expected,\n };\n}\n\nexport type IncrementStateEvent = CreateStateEventType<number, 'increment'>;\n\nexport function createIncrementStateEvent(delta: number): IncrementStateEvent {\n return {\n id: nanoid(),\n type: 'increment',\n value: delta,\n };\n}\n\n/**\n * An object that describes the change of the signal state.\n */\nexport type StateEvent<T> =\n | IncrementStateEvent\n | RejectStateEvent\n | ReplaceStateEvent<T>\n | SetStateEvent<T>\n | SnapshotStateEvent<T>;\n"],
5
+ "mappings": "AAAA,SAAS,cAAc;AA6BhB,SAAS,oBAAuB,OAA4B;AACjE,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX,MAAM;AAAA,IACN;AAAA,EACF;AACF;AAIO,SAAS,wBAA2B,UAAa,OAAgC;AACtF,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX,MAAM;AAAA,IACN;AAAA,IACA;AAAA,EACF;AACF;AAIO,SAAS,0BAA0B,OAAoC;AAC5E,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX,MAAM;AAAA,IACN,OAAO;AAAA,EACT;AACF;",
6
+ "names": []
7
+ }
package/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import './polyfills.js';
1
2
  export * from './core.js';
2
- export { NumberSignalChannel } from './EventChannel.js';
3
- export { NumberSignal, ValueSignal } from './Signals.js';
4
- export * from './types.js';
3
+ export { NumberSignal } from './NumberSignal.js';
4
+ export { ValueSignal } from './ValueSignal.js';
5
+ export type { OperationSubscription } from './ValueSignal.js';
6
+ export { FullStackSignal } from './FullStackSignal.js';
5
7
  //# sourceMappingURL=index.d.ts.map
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AACA,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACzD,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["src/index.ts"],"names":[],"mappings":"AACA,OAAO,gBAAgB,CAAC;AAGxB,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
package/index.js CHANGED
@@ -1,10 +1,11 @@
1
+ import "./polyfills.js";
1
2
  export * from "./core.js";
2
- import { NumberSignalChannel } from "./EventChannel.js";
3
- import { NumberSignal, ValueSignal } from "./Signals.js";
4
- export * from "./types.js";
3
+ import { NumberSignal } from "./NumberSignal.js";
4
+ import { ValueSignal } from "./ValueSignal.js";
5
+ import { FullStackSignal } from "./FullStackSignal.js";
5
6
  export {
7
+ FullStackSignal,
6
8
  NumberSignal,
7
- NumberSignalChannel,
8
9
  ValueSignal
9
10
  };
10
11
  //# sourceMappingURL=index.js.map
package/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["src/index.ts"],
4
- "sourcesContent": ["// eslint-disable-next-line import/export\nexport * from './core.js';\nexport { NumberSignalChannel } from './EventChannel.js';\nexport { NumberSignal, ValueSignal } from './Signals.js';\nexport * from './types.js';\n"],
5
- "mappings": "AACA,cAAc;AACd,SAAS,2BAA2B;AACpC,SAAS,cAAc,mBAAmB;AAC1C,cAAc;",
4
+ "sourcesContent": ["// eslint-disable-next-line import/no-unassigned-import\nimport './polyfills.js';\n\n// eslint-disable-next-line import/export\nexport * from './core.js';\nexport { NumberSignal } from './NumberSignal.js';\nexport { ValueSignal } from './ValueSignal.js';\nexport type { OperationSubscription } from './ValueSignal.js';\nexport { FullStackSignal } from './FullStackSignal.js';\n"],
5
+ "mappings": "AACA,OAAO;AAGP,cAAc;AACd,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAE5B,SAAS,uBAAuB;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vaadin/hilla-react-signals",
3
- "version": "24.5.0-alpha8",
3
+ "version": "24.5.0-beta1",
4
4
  "description": "Signals for Hilla React",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
@@ -47,12 +47,12 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@preact/signals-react": "^2.0.0",
50
- "@vaadin/hilla-frontend": "24.5.0-alpha8",
50
+ "@vaadin/hilla-frontend": "24.5.0-beta1",
51
51
  "nanoid": "^5.0.7"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "react": "^18",
55
- "react-router-dom": "^6"
55
+ "react-router-dom": "^6.26.2"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@esm-bundle/chai": "^4.3.4-fix.0",
@@ -74,6 +74,6 @@
74
74
  "karma-viewport": "^1.0.9",
75
75
  "sinon": "^16.0.0",
76
76
  "sinon-chai": "^3.7.0",
77
- "typescript": "5.5.2"
77
+ "typescript": "5.6.2"
78
78
  }
79
79
  }
package/polyfills.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ declare global {
2
+ interface PromiseConstructor {
3
+ withResolvers<T>(): {
4
+ resolve(value: T): void;
5
+ reject(reason?: unknown): void;
6
+ promise: Promise<T>;
7
+ };
8
+ }
9
+ }
10
+ export {};
11
+ //# sourceMappingURL=polyfills.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polyfills.d.ts","sourceRoot":"","sources":["src/polyfills.ts"],"names":[],"mappings":"AACA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,kBAAkB;QAC1B,aAAa,CAAC,CAAC,KAAK;YAClB,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;YACxB,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;YAC/B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;SACrB,CAAC;KACH;CACF;AAkBD,OAAO,EAAE,CAAC"}
package/polyfills.js ADDED
@@ -0,0 +1,15 @@
1
+ if (!("withResolvers" in Promise)) {
2
+ Object.defineProperty(Promise, "withResolvers", {
3
+ configurable: true,
4
+ value() {
5
+ let resolve;
6
+ let reject;
7
+ const promise = new Promise((_resolve, _reject) => {
8
+ resolve = _resolve;
9
+ reject = _reject;
10
+ });
11
+ return { resolve, reject, promise };
12
+ }
13
+ });
14
+ }
15
+ //# sourceMappingURL=polyfills.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/polyfills.ts"],
4
+ "sourcesContent": ["// TODO: Remove this polyfill when we move to ECMA2024\ndeclare global {\n interface PromiseConstructor {\n withResolvers<T>(): {\n resolve(value: T): void;\n reject(reason?: unknown): void;\n promise: Promise<T>;\n };\n }\n}\n\nif (!('withResolvers' in Promise)) {\n // eslint-disable-next-line no-extend-native\n Object.defineProperty(Promise, 'withResolvers', {\n configurable: true,\n value<T>() {\n let resolve: (value: T) => void;\n let reject: (reason?: unknown) => void;\n const promise = new Promise<T>((_resolve, _reject) => {\n resolve = _resolve;\n reject = _reject;\n });\n return { resolve: resolve!, reject: reject!, promise };\n },\n });\n}\n\nexport {};\n"],
5
+ "mappings": "AAWA,IAAI,EAAE,mBAAmB,UAAU;AAEjC,SAAO,eAAe,SAAS,iBAAiB;AAAA,IAC9C,cAAc;AAAA,IACd,QAAW;AACT,UAAI;AACJ,UAAI;AACJ,YAAM,UAAU,IAAI,QAAW,CAAC,UAAU,YAAY;AACpD,kBAAU;AACV,iBAAS;AAAA,MACX,CAAC;AACD,aAAO,EAAE,SAAmB,QAAiB,QAAQ;AAAA,IACvD;AAAA,EACF,CAAC;AACH;",
6
+ "names": []
7
+ }
package/types.d.ts CHANGED
@@ -1,16 +1,10 @@
1
- /**
2
- * Types of events that can be produced or processed by a signal.
3
- */
4
- export declare enum StateEventType {
5
- SET = "set",
6
- SNAPSHOT = "snapshot"
1
+ import { Signal as _Signal } from '@preact/signals-react';
2
+
3
+ declare module './core.js' {
4
+ export declare class Signal<T> extends _Signal<T> {
5
+ protected S(node: unknown): void;
6
+ protected U(node: unknown): void;
7
+ }
7
8
  }
8
- /**
9
- * Event that describes the state of a signal.
10
- */
11
- export type StateEvent = {
12
- id: string;
13
- type: StateEventType;
14
- value: any;
15
- };
16
- //# sourceMappingURL=types.d.ts.map
9
+
10
+ export {};
package/utils.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { ReadonlySignal } from '@preact/signals-react';
2
+ export declare function createPromiseFromSignal<T, U, E>(signal: ReadonlySignal<T>, callback: (value: T, resolve: (value: PromiseLike<U> | U) => void, reject: (reason?: E) => void) => void): Promise<U>;
3
+ //# sourceMappingURL=utils.d.ts.map
package/utils.d.ts.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,wBAAsB,uBAAuB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACnD,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,EACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,IAAI,KAAK,IAAI,GACvG,OAAO,CAAC,CAAC,CAAC,CAWZ"}
package/utils.js ADDED
@@ -0,0 +1,15 @@
1
+ async function createPromiseFromSignal(signal, callback) {
2
+ return new Promise((resolve, reject) => {
3
+ const unsubscribe = signal.subscribe((value) => {
4
+ if (!value) {
5
+ return;
6
+ }
7
+ unsubscribe();
8
+ callback(value, resolve, reject);
9
+ });
10
+ });
11
+ }
12
+ export {
13
+ createPromiseFromSignal
14
+ };
15
+ //# sourceMappingURL=utils.js.map
package/utils.js.map ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/utils.ts"],
4
+ "sourcesContent": ["import type { ReadonlySignal } from '@preact/signals-react';\n\nexport async function createPromiseFromSignal<T, U, E>(\n signal: ReadonlySignal<T>,\n callback: (value: T, resolve: (value: PromiseLike<U> | U) => void, reject: (reason?: E) => void) => void,\n): Promise<U> {\n return new Promise((resolve, reject) => {\n const unsubscribe = signal.subscribe((value) => {\n if (!value) {\n return;\n }\n\n unsubscribe();\n callback(value, resolve, reject);\n });\n });\n}\n"],
5
+ "mappings": "AAEA,eAAsB,wBACpB,QACA,UACY;AACZ,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,cAAc,OAAO,UAAU,CAAC,UAAU;AAC9C,UAAI,CAAC,OAAO;AACV;AAAA,MACF;AAEA,kBAAY;AACZ,eAAS,OAAO,SAAS,MAAM;AAAA,IACjC,CAAC;AAAA,EACH,CAAC;AACH;",
6
+ "names": []
7
+ }
package/EventChannel.d.ts DELETED
@@ -1,38 +0,0 @@
1
- import type { ConnectClient } from '@vaadin/hilla-frontend';
2
- import { NumberSignal, type ValueSignal } from './Signals.js';
3
- import { type StateEvent } from './types.js';
4
- /**
5
- * A generic class that represents a signal channel
6
- * that can be used to communicate with a server-side
7
- * signal instance.
8
- *
9
- * The signal channel is responsible for subscribing to
10
- * the server-side signal and updating the local signal
11
- * based on the received events.
12
- *
13
- * @typeParam T - The type of the signal value.
14
- * @typeParam S - The type of the signal instance.
15
- */
16
- declare abstract class SignalChannel<T, S extends ValueSignal<T>> {
17
- #private;
18
- constructor(signalProviderServiceMethod: string, connectClient: ConnectClient);
19
- publish(event: StateEvent): Promise<boolean>;
20
- /**
21
- * Returns the signal instance to be used in components.
22
- */
23
- get signal(): S;
24
- /**
25
- * Returns the id of the signal channel.
26
- */
27
- get id(): string;
28
- abstract createInternalSignal(publish: (event: StateEvent) => Promise<boolean>, initialValue?: T): S;
29
- }
30
- /**
31
- * A signal channel that is used to communicate with a
32
- * server-side signal instance that holds a number value.
33
- */
34
- export declare class NumberSignalChannel extends SignalChannel<number, NumberSignal> {
35
- createInternalSignal(publish: (event: StateEvent) => Promise<boolean>, initialValue?: number): NumberSignal;
36
- }
37
- export {};
38
- //# sourceMappingURL=EventChannel.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EventChannel.d.ts","sourceRoot":"","sources":["src/EventChannel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,wBAAwB,CAAC;AAE1E,OAAO,EAAE,YAAY,EAAoB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhF,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,YAAY,CAAC;AAY7D;;;;;;;;;;;GAWG;AACH,uBAAe,aAAa,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,CAAC,CAAC,CAAC;;gBAO1C,2BAA2B,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa;IA8BvE,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlD;;OAEG;IACH,IAAI,MAAM,IAAI,CAAC,CAEd;IAED;;OAEG;IACH,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,QAAQ,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC;CACrG;AAED;;;GAGG;AACH,qBAAa,mBAAoB,SAAQ,aAAa,CAAC,MAAM,EAAE,YAAY,CAAC;IACjE,oBAAoB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY;CAGrH"}
package/EventChannel.js DELETED
@@ -1,56 +0,0 @@
1
- import { nanoid } from "nanoid";
2
- import { NumberSignal, setInternalValue } from "./Signals.js";
3
- import SignalsHandler from "./SignalsHandler";
4
- import { StateEventType } from "./types.js";
5
- class SignalChannel {
6
- #channelDescriptor;
7
- #signalsHandler;
8
- #id;
9
- #internalSignal;
10
- constructor(signalProviderServiceMethod, connectClient) {
11
- this.#id = nanoid();
12
- this.#signalsHandler = new SignalsHandler(connectClient);
13
- this.#channelDescriptor = {
14
- signalProviderEndpointMethod: signalProviderServiceMethod,
15
- subscribe: (signalProviderEndpointMethod, signalId) => this.#signalsHandler.subscribe(signalProviderEndpointMethod, signalId),
16
- publish: async (signalId, event) => this.#signalsHandler.update(signalId, event)
17
- };
18
- this.#internalSignal = this.createInternalSignal(async (event) => this.publish(event));
19
- this.#connect();
20
- }
21
- #connect() {
22
- this.#channelDescriptor.subscribe(this.#channelDescriptor.signalProviderEndpointMethod, this.#id).onNext((stateEvent) => {
23
- this.#updateSignals(stateEvent);
24
- });
25
- }
26
- #updateSignals(stateEvent) {
27
- if (stateEvent.type === StateEventType.SNAPSHOT) {
28
- setInternalValue(this.#internalSignal, stateEvent.value);
29
- }
30
- }
31
- async publish(event) {
32
- await this.#channelDescriptor.publish(this.#id, event);
33
- return true;
34
- }
35
- /**
36
- * Returns the signal instance to be used in components.
37
- */
38
- get signal() {
39
- return this.#internalSignal;
40
- }
41
- /**
42
- * Returns the id of the signal channel.
43
- */
44
- get id() {
45
- return this.#id;
46
- }
47
- }
48
- class NumberSignalChannel extends SignalChannel {
49
- createInternalSignal(publish, initialValue) {
50
- return new NumberSignal(publish, initialValue);
51
- }
52
- }
53
- export {
54
- NumberSignalChannel
55
- };
56
- //# sourceMappingURL=EventChannel.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["src/EventChannel.ts"],
4
- "sourcesContent": ["import type { ConnectClient, Subscription } from '@vaadin/hilla-frontend';\nimport { nanoid } from 'nanoid';\nimport { NumberSignal, setInternalValue, type ValueSignal } from './Signals.js';\nimport SignalsHandler from './SignalsHandler';\nimport { type StateEvent, StateEventType } from './types.js';\n\n/**\n * The type that describes the needed information to\n * subscribe and publish to a server-side signal instance.\n */\ntype SignalChannelDescriptor<T> = Readonly<{\n signalProviderEndpointMethod: string;\n subscribe(signalProviderEndpointMethod: string, clientSignalId: string): Subscription<T>;\n publish(clientSignalId: string, event: T): Promise<void>;\n}>;\n\n/**\n * A generic class that represents a signal channel\n * that can be used to communicate with a server-side\n * signal instance.\n *\n * The signal channel is responsible for subscribing to\n * the server-side signal and updating the local signal\n * based on the received events.\n *\n * @typeParam T - The type of the signal value.\n * @typeParam S - The type of the signal instance.\n */\nabstract class SignalChannel<T, S extends ValueSignal<T>> {\n readonly #channelDescriptor: SignalChannelDescriptor<StateEvent>;\n readonly #signalsHandler: SignalsHandler;\n readonly #id: string;\n\n readonly #internalSignal: S;\n\n constructor(signalProviderServiceMethod: string, connectClient: ConnectClient) {\n this.#id = nanoid();\n this.#signalsHandler = new SignalsHandler(connectClient);\n this.#channelDescriptor = {\n signalProviderEndpointMethod: signalProviderServiceMethod,\n subscribe: (signalProviderEndpointMethod: string, signalId: string) =>\n this.#signalsHandler.subscribe(signalProviderEndpointMethod, signalId),\n publish: async (signalId: string, event: StateEvent) => this.#signalsHandler.update(signalId, event),\n };\n\n this.#internalSignal = this.createInternalSignal(async (event: StateEvent) => this.publish(event));\n\n this.#connect();\n }\n\n #connect() {\n this.#channelDescriptor\n .subscribe(this.#channelDescriptor.signalProviderEndpointMethod, this.#id)\n .onNext((stateEvent) => {\n // Update signals based on the new value from the event:\n this.#updateSignals(stateEvent);\n });\n }\n\n #updateSignals(stateEvent: StateEvent): void {\n if (stateEvent.type === StateEventType.SNAPSHOT) {\n setInternalValue(this.#internalSignal, stateEvent.value);\n }\n }\n\n async publish(event: StateEvent): Promise<boolean> {\n await this.#channelDescriptor.publish(this.#id, event);\n return true;\n }\n\n /**\n * Returns the signal instance to be used in components.\n */\n get signal(): S {\n return this.#internalSignal;\n }\n\n /**\n * Returns the id of the signal channel.\n */\n get id(): string {\n return this.#id;\n }\n\n abstract createInternalSignal(publish: (event: StateEvent) => Promise<boolean>, initialValue?: T): S;\n}\n\n/**\n * A signal channel that is used to communicate with a\n * server-side signal instance that holds a number value.\n */\nexport class NumberSignalChannel extends SignalChannel<number, NumberSignal> {\n override createInternalSignal(publish: (event: StateEvent) => Promise<boolean>, initialValue?: number): NumberSignal {\n return new NumberSignal(publish, initialValue);\n }\n}\n"],
5
- "mappings": "AACA,SAAS,cAAc;AACvB,SAAS,cAAc,wBAA0C;AACjE,OAAO,oBAAoB;AAC3B,SAA0B,sBAAsB;AAwBhD,MAAe,cAA2C;AAAA,EAC/C;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EAET,YAAY,6BAAqC,eAA8B;AAC7E,SAAK,MAAM,OAAO;AAClB,SAAK,kBAAkB,IAAI,eAAe,aAAa;AACvD,SAAK,qBAAqB;AAAA,MACxB,8BAA8B;AAAA,MAC9B,WAAW,CAAC,8BAAsC,aAChD,KAAK,gBAAgB,UAAU,8BAA8B,QAAQ;AAAA,MACvE,SAAS,OAAO,UAAkB,UAAsB,KAAK,gBAAgB,OAAO,UAAU,KAAK;AAAA,IACrG;AAEA,SAAK,kBAAkB,KAAK,qBAAqB,OAAO,UAAsB,KAAK,QAAQ,KAAK,CAAC;AAEjG,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,WAAW;AACT,SAAK,mBACF,UAAU,KAAK,mBAAmB,8BAA8B,KAAK,GAAG,EACxE,OAAO,CAAC,eAAe;AAEtB,WAAK,eAAe,UAAU;AAAA,IAChC,CAAC;AAAA,EACL;AAAA,EAEA,eAAe,YAA8B;AAC3C,QAAI,WAAW,SAAS,eAAe,UAAU;AAC/C,uBAAiB,KAAK,iBAAiB,WAAW,KAAK;AAAA,IACzD;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,OAAqC;AACjD,UAAM,KAAK,mBAAmB,QAAQ,KAAK,KAAK,KAAK;AACrD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,SAAY;AACd,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,KAAa;AACf,WAAO,KAAK;AAAA,EACd;AAGF;AAMO,MAAM,4BAA4B,cAAoC;AAAA,EAClE,qBAAqB,SAAkD,cAAqC;AACnH,WAAO,IAAI,aAAa,SAAS,YAAY;AAAA,EAC/C;AACF;",
6
- "names": []
7
- }
package/Signals.d.ts DELETED
@@ -1,60 +0,0 @@
1
- import { Signal } from './core.js';
2
- import { type StateEvent } from './types';
3
- export declare let setInternalValue: <T>(signal: ValueSignal<T>, value: T) => void;
4
- /**
5
- * A signal that holds a value. The underlying
6
- * value of this signal is stored and updated as a
7
- * shared value on the server.
8
- *
9
- * @internal
10
- */
11
- export declare abstract class ValueSignal<T> extends Signal<T> {
12
- #private;
13
- /**
14
- * Creates a new ValueSignal instance.
15
- * @param publish - The function that publishes the
16
- * value of the signal to the server.
17
- * @param value - The initial value of the signal
18
- * @defaultValue undefined
19
- */
20
- constructor(publish: (event: StateEvent) => Promise<boolean>, value?: T);
21
- /**
22
- * Returns the value of the signal.
23
- */
24
- get value(): T;
25
- /**
26
- * Publishes the new value to the server.
27
- * Note that this method is not setting
28
- * the signal's value.
29
- *
30
- * @param value - The new value of the signal
31
- * to be published to the server.
32
- */
33
- set value(value: T);
34
- }
35
- /**
36
- * A signal that holds a number value. The underlying
37
- * value of this signal is stored and updated as a
38
- * shared value on the server.
39
- *
40
- * After obtaining the NumberSignal instance from
41
- * a server-side service that returns one, the value
42
- * can be updated using the `value` property,
43
- * and it can be read with or without the
44
- * `value` property (similar to a normal signal):
45
- *
46
- * @example
47
- * ```tsx
48
- * const counter = CounterService.counter();
49
- *
50
- * return (
51
- * <Button onClick={() => counter++)}>
52
- * Click count: { counter }
53
- * </Button>
54
- * <Button onClick={() => counter.value = 0}>Reset</Button>
55
- * );
56
- * ```
57
- */
58
- export declare class NumberSignal extends ValueSignal<number> {
59
- }
60
- //# sourceMappingURL=Signals.d.ts.map
package/Signals.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"Signals.d.ts","sourceRoot":"","sources":["src/Signals.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,SAAS,CAAC;AAG1D,eAAO,IAAI,gBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAE3E;;;;;;GAMG;AACH,8BAAsB,WAAW,CAAC,CAAC,CAAE,SAAQ,MAAM,CAAC,CAAC,CAAC;;IAOpD;;;;;;OAMG;gBACS,OAAO,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;IAKvE;;OAEG;IACH,IAAa,KAAK,IAAI,CAAC,CAEtB;IAED;;;;;;;OAOG;IACH,IAAa,KAAK,CAAC,KAAK,EAAE,CAAC,EAQ1B;CAUF;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,YAAa,SAAQ,WAAW,CAAC,MAAM,CAAC;CAAG"}
package/Signals.js DELETED
@@ -1,58 +0,0 @@
1
- import { nanoid } from "nanoid";
2
- import { Signal } from "./core.js";
3
- import { StateEventType } from "./types";
4
- let setInternalValue;
5
- class ValueSignal extends Signal {
6
- static {
7
- setInternalValue = (signal, value) => signal.#setInternalValue(value);
8
- }
9
- #publish;
10
- /**
11
- * Creates a new ValueSignal instance.
12
- * @param publish - The function that publishes the
13
- * value of the signal to the server.
14
- * @param value - The initial value of the signal
15
- * @defaultValue undefined
16
- */
17
- constructor(publish, value) {
18
- super(value);
19
- this.#publish = publish;
20
- }
21
- /**
22
- * Returns the value of the signal.
23
- */
24
- get value() {
25
- return super.value;
26
- }
27
- /**
28
- * Publishes the new value to the server.
29
- * Note that this method is not setting
30
- * the signal's value.
31
- *
32
- * @param value - The new value of the signal
33
- * to be published to the server.
34
- */
35
- set value(value) {
36
- const id = nanoid();
37
- this.#setInternalValue(value);
38
- this.#publish({ id, type: StateEventType.SET, value }).catch((error) => {
39
- throw error;
40
- });
41
- }
42
- /**
43
- * Sets the value of the signal.
44
- * @param value - The new value of the signal.
45
- * @internal
46
- */
47
- #setInternalValue(value) {
48
- super.value = value;
49
- }
50
- }
51
- class NumberSignal extends ValueSignal {
52
- }
53
- export {
54
- NumberSignal,
55
- ValueSignal,
56
- setInternalValue
57
- };
58
- //# sourceMappingURL=Signals.js.map
package/Signals.js.map DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["src/Signals.ts"],
4
- "sourcesContent": ["import { nanoid } from 'nanoid';\nimport { Signal } from './core.js';\nimport { type StateEvent, StateEventType } from './types';\n\n// eslint-disable-next-line import/no-mutable-exports\nexport let setInternalValue: <T>(signal: ValueSignal<T>, value: T) => void;\n\n/**\n * A signal that holds a value. The underlying\n * value of this signal is stored and updated as a\n * shared value on the server.\n *\n * @internal\n */\nexport abstract class ValueSignal<T> extends Signal<T> {\n static {\n setInternalValue = (signal: ValueSignal<unknown>, value: unknown): void => signal.#setInternalValue(value);\n }\n\n readonly #publish: (event: StateEvent) => Promise<boolean>;\n\n /**\n * Creates a new ValueSignal instance.\n * @param publish - The function that publishes the\n * value of the signal to the server.\n * @param value - The initial value of the signal\n * @defaultValue undefined\n */\n constructor(publish: (event: StateEvent) => Promise<boolean>, value?: T) {\n super(value);\n this.#publish = publish;\n }\n\n /**\n * Returns the value of the signal.\n */\n override get value(): T {\n return super.value;\n }\n\n /**\n * Publishes the new value to the server.\n * Note that this method is not setting\n * the signal's value.\n *\n * @param value - The new value of the signal\n * to be published to the server.\n */\n override set value(value: T) {\n const id = nanoid();\n // set the local value to be used for latency compensation and offline support:\n this.#setInternalValue(value);\n // publish the update to the server:\n this.#publish({ id, type: StateEventType.SET, value }).catch((error) => {\n throw error;\n });\n }\n\n /**\n * Sets the value of the signal.\n * @param value - The new value of the signal.\n * @internal\n */\n #setInternalValue(value: T): void {\n super.value = value;\n }\n}\n\n/**\n * A signal that holds a number value. The underlying\n * value of this signal is stored and updated as a\n * shared value on the server.\n *\n * After obtaining the NumberSignal instance from\n * a server-side service that returns one, the value\n * can be updated using the `value` property,\n * and it can be read with or without the\n * `value` property (similar to a normal signal):\n *\n * @example\n * ```tsx\n * const counter = CounterService.counter();\n *\n * return (\n * <Button onClick={() => counter++)}>\n * Click count: { counter }\n * </Button>\n * <Button onClick={() => counter.value = 0}>Reset</Button>\n * );\n * ```\n */\nexport class NumberSignal extends ValueSignal<number> {}\n"],
5
- "mappings": "AAAA,SAAS,cAAc;AACvB,SAAS,cAAc;AACvB,SAA0B,sBAAsB;AAGzC,IAAI;AASJ,MAAe,oBAAuB,OAAU;AAAA,EACrD,OAAO;AACL,uBAAmB,CAAC,QAA8B,UAAyB,OAAO,kBAAkB,KAAK;AAAA,EAC3G;AAAA,EAES;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,YAAY,SAAkD,OAAW;AACvE,UAAM,KAAK;AACX,SAAK,WAAW;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAa,QAAW;AACtB,WAAO,MAAM;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,IAAa,MAAM,OAAU;AAC3B,UAAM,KAAK,OAAO;AAElB,SAAK,kBAAkB,KAAK;AAE5B,SAAK,SAAS,EAAE,IAAI,MAAM,eAAe,KAAK,MAAM,CAAC,EAAE,MAAM,CAAC,UAAU;AACtE,YAAM;AAAA,IACR,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,kBAAkB,OAAgB;AAChC,UAAM,QAAQ;AAAA,EAChB;AACF;AAyBO,MAAM,qBAAqB,YAAoB;AAAC;",
6
- "names": []
7
- }
@@ -1,15 +0,0 @@
1
- import type { ConnectClient, EndpointRequestInit, Subscription } from '@vaadin/hilla-frontend';
2
- import type { StateEvent } from './types';
3
- /**
4
- * SignalsHandler is a helper class for handling the
5
- * communication of the full-stack signal instances
6
- * and their server-side counterparts they are
7
- * subscribed and publish their updates to.
8
- */
9
- export default class SignalsHandler {
10
- #private;
11
- constructor(client: ConnectClient);
12
- subscribe(signalProviderEndpointMethod: string, clientSignalId: string): Subscription<StateEvent>;
13
- update(clientSignalId: string, event: StateEvent, init?: EndpointRequestInit): Promise<void>;
14
- }
15
- //# sourceMappingURL=SignalsHandler.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SignalsHandler.d.ts","sourceRoot":"","sources":["src/SignalsHandler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC/F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,CAAC,OAAO,OAAO,cAAc;;gBAGrB,MAAM,EAAE,aAAa;IAIjC,SAAS,CAAC,4BAA4B,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC;IAI3F,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;CAGnG"}
package/SignalsHandler.js DELETED
@@ -1,16 +0,0 @@
1
- class SignalsHandler {
2
- #client;
3
- constructor(client) {
4
- this.#client = client;
5
- }
6
- subscribe(signalProviderEndpointMethod, clientSignalId) {
7
- return this.#client.subscribe("SignalsHandler", "subscribe", { signalProviderEndpointMethod, clientSignalId });
8
- }
9
- async update(clientSignalId, event, init) {
10
- return this.#client.call("SignalsHandler", "update", { clientSignalId, event }, init);
11
- }
12
- }
13
- export {
14
- SignalsHandler as default
15
- };
16
- //# sourceMappingURL=SignalsHandler.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["src/SignalsHandler.ts"],
4
- "sourcesContent": ["import type { ConnectClient, EndpointRequestInit, Subscription } from '@vaadin/hilla-frontend';\nimport type { StateEvent } from './types';\n\n/**\n * SignalsHandler is a helper class for handling the\n * communication of the full-stack signal instances\n * and their server-side counterparts they are\n * subscribed and publish their updates to.\n */\nexport default class SignalsHandler {\n readonly #client: ConnectClient;\n\n constructor(client: ConnectClient) {\n this.#client = client;\n }\n\n subscribe(signalProviderEndpointMethod: string, clientSignalId: string): Subscription<StateEvent> {\n return this.#client.subscribe('SignalsHandler', 'subscribe', { signalProviderEndpointMethod, clientSignalId });\n }\n\n async update(clientSignalId: string, event: StateEvent, init?: EndpointRequestInit): Promise<void> {\n return this.#client.call('SignalsHandler', 'update', { clientSignalId, event }, init);\n }\n}\n"],
5
- "mappings": "AASA,MAAO,eAA6B;AAAA,EACzB;AAAA,EAET,YAAY,QAAuB;AACjC,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,UAAU,8BAAsC,gBAAkD;AAChG,WAAO,KAAK,QAAQ,UAAU,kBAAkB,aAAa,EAAE,8BAA8B,eAAe,CAAC;AAAA,EAC/G;AAAA,EAEA,MAAM,OAAO,gBAAwB,OAAmB,MAA2C;AACjG,WAAO,KAAK,QAAQ,KAAK,kBAAkB,UAAU,EAAE,gBAAgB,MAAM,GAAG,IAAI;AAAA,EACtF;AACF;",
6
- "names": []
7
- }
package/types.d.ts.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,cAAc;IACxB,GAAG,QAAQ;IACX,QAAQ,aAAa;CACtB;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,GAAG,CAAC;CACZ,CAAC"}
package/types.js DELETED
@@ -1,9 +0,0 @@
1
- var StateEventType = /* @__PURE__ */ ((StateEventType2) => {
2
- StateEventType2["SET"] = "set";
3
- StateEventType2["SNAPSHOT"] = "snapshot";
4
- return StateEventType2;
5
- })(StateEventType || {});
6
- export {
7
- StateEventType
8
- };
9
- //# sourceMappingURL=types.js.map
package/types.js.map DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["src/types.ts"],
4
- "sourcesContent": ["/**\n * Types of events that can be produced or processed by a signal.\n */\nexport enum StateEventType {\n SET = 'set',\n SNAPSHOT = 'snapshot',\n}\n\n/**\n * Event that describes the state of a signal.\n */\nexport type StateEvent = {\n id: string;\n type: StateEventType;\n value: any;\n};\n"],
5
- "mappings": "AAGO,IAAK,iBAAL,kBAAKA,oBAAL;AACL,EAAAA,gBAAA,SAAM;AACN,EAAAA,gBAAA,cAAW;AAFD,SAAAA;AAAA,GAAA;",
6
- "names": ["StateEventType"]
7
- }