@vaadin/hilla-react-signals 24.5.0 → 24.6.0-alpha2

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,14 @@
1
+ import { FullStackSignal } from './FullStackSignal.js';
2
+ /**
3
+ * A {@link FullStackSignal} that represents a collection of values.
4
+ *
5
+ * @typeParam T - The type of the values in the collection.
6
+ */
7
+ export declare abstract class CollectionSignal<T> extends FullStackSignal<T> {
8
+ get value(): T;
9
+ /**
10
+ * @readonly
11
+ */
12
+ set value(_: never);
13
+ }
14
+ //# sourceMappingURL=CollectionSignal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CollectionSignal.d.ts","sourceRoot":"","sources":["src/CollectionSignal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;GAIG;AACH,8BAAsB,gBAAgB,CAAC,CAAC,CAAE,SAAQ,eAAe,CAAC,CAAC,CAAC;IAClE,IAAa,KAAK,IAAI,CAAC,CAEtB;IAED;;OAEG;IACH,IAAa,KAAK,CAAC,CAAC,EAAE,KAAK,EAE1B;CACF"}
@@ -0,0 +1,16 @@
1
+ import { FullStackSignal } from "./FullStackSignal.js";
2
+ class CollectionSignal extends FullStackSignal {
3
+ get value() {
4
+ return super.value;
5
+ }
6
+ /**
7
+ * @readonly
8
+ */
9
+ set value(_) {
10
+ throw new Error("Value of the collection signals cannot be set.");
11
+ }
12
+ }
13
+ export {
14
+ CollectionSignal
15
+ };
16
+ //# sourceMappingURL=CollectionSignal.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/CollectionSignal.ts"],
4
+ "sourcesContent": ["import { FullStackSignal } from './FullStackSignal.js';\n\n/**\n * A {@link FullStackSignal} that represents a collection of values.\n *\n * @typeParam T - The type of the values in the collection.\n */\nexport abstract class CollectionSignal<T> extends FullStackSignal<T> {\n override get value(): T {\n return super.value;\n }\n\n /**\n * @readonly\n */\n override set value(_: never) {\n throw new Error('Value of the collection signals cannot be set.');\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,uBAAuB;AAOzB,MAAe,yBAA4B,gBAAmB;AAAA,EACnE,IAAa,QAAW;AACtB,WAAO,MAAM;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA,IAAa,MAAM,GAAU;AAC3B,UAAM,IAAI,MAAM,gDAAgD;AAAA,EAClE;AACF;",
6
+ "names": []
7
+ }
@@ -35,20 +35,38 @@ export type ServerConnectionConfig = Readonly<{
35
35
  * method that provides the signal when subscribing to it.
36
36
  */
37
37
  params?: Record<string, unknown>;
38
+ /**
39
+ * The unique identifier of the parent signal in the client.
40
+ */
41
+ parentClientSignalId?: string;
38
42
  }>;
39
43
  /**
40
44
  * A server connection manager.
41
45
  */
42
- declare class ServerConnection<T> {
46
+ declare class ServerConnection {
43
47
  #private;
48
+ readonly config: ServerConnectionConfig;
44
49
  constructor(id: string, config: ServerConnectionConfig);
45
- get subscription(): Subscription<StateEvent<T>> | undefined;
46
- connect(): Subscription<StateEvent<T>>;
47
- update(event: StateEvent<T>): Promise<void>;
50
+ get subscription(): Subscription<Readonly<{
51
+ id: string;
52
+ type: string;
53
+ value: unknown;
54
+ accepted: boolean;
55
+ parentSignalId?: string;
56
+ }>> | undefined;
57
+ connect(): Subscription<Readonly<{
58
+ id: string;
59
+ type: string;
60
+ value: unknown;
61
+ accepted: boolean;
62
+ parentSignalId?: string;
63
+ }>>;
64
+ update(event: StateEvent): Promise<void>;
48
65
  disconnect(): void;
49
66
  }
50
67
  export declare const $update: unique symbol;
51
68
  export declare const $processServerResponse: unique symbol;
69
+ export declare const $setValueQuietly: unique symbol;
52
70
  /**
53
71
  * A signal that holds a shared value. Each change to the value is propagated to
54
72
  * the server-side signal provider. At the same time, each change received from
@@ -67,7 +85,7 @@ export declare abstract class FullStackSignal<T> extends DependencyTrackingSigna
67
85
  /**
68
86
  * The server connection manager.
69
87
  */
70
- readonly server: ServerConnection<T>;
88
+ readonly server: ServerConnection;
71
89
  /**
72
90
  * Defines whether the signal is currently awaits a server-side response.
73
91
  */
@@ -76,26 +94,26 @@ export declare abstract class FullStackSignal<T> extends DependencyTrackingSigna
76
94
  * Defines whether the signal has an error.
77
95
  */
78
96
  readonly error: import("@preact/signals-core").ReadonlySignal<Error | undefined>;
79
- constructor(value: T | undefined, config: ServerConnectionConfig);
97
+ constructor(value: T | undefined, config: ServerConnectionConfig, id?: string);
80
98
  /**
81
99
  * Sets the local value of the signal without sending any events to the server
82
100
  * @param value - The new value.
83
101
  * @internal
84
102
  */
85
- protected setValueLocal(value: T): void;
103
+ protected [$setValueQuietly](value: T): void;
86
104
  /**
87
105
  * A method to update the server with the new value.
88
106
  *
89
107
  * @param event - The event to update the server with.
90
108
  */
91
- protected [$update](event: StateEvent<T>): void;
109
+ protected [$update](event: StateEvent): void;
92
110
  /**
93
111
  * A method with to process the server response. The implementation is
94
112
  * specific for each signal type.
95
113
  *
96
114
  * @param event - The server response event.
97
115
  */
98
- protected abstract [$processServerResponse](event: StateEvent<T>): void;
116
+ protected abstract [$processServerResponse](event: StateEvent): void;
99
117
  }
100
118
  export {};
101
119
  //# sourceMappingURL=FullStackSignal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FullStackSignal.d.ts","sourceRoot":"","sources":["src/FullStackSignal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEpG,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;cAapF,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;CAmBxE"}
1
+ {"version":3,"file":"FullStackSignal.d.ts","sourceRoot":"","sources":["src/FullStackSignal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEpG,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;cAapF,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;IAEjC;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC,CAAC;AAEH;;GAEG;AACH,cAAM,gBAAgB;;IAEpB,QAAQ,CAAC,MAAM,EAAE,sBAAsB,CAAC;gBAG5B,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,sBAAsB;IAKtD,IAAI,YAAY;;;;;;oBAEf;IAED,OAAO;;;;;;;IAcD,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9C,UAAU;CAIX;AAED,eAAO,MAAM,OAAO,eAAmB,CAAC;AACxC,eAAO,MAAM,sBAAsB,eAAkC,CAAC;AACtE,eAAO,MAAM,gBAAgB,eAA4B,CAAC;AAE1D;;;;;;;GAOG;AACH,8BAAsB,eAAe,CAAC,CAAC,CAAE,SAAQ,wBAAwB,CAAC,CAAC,CAAC;;IAC1E;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAElC;;OAEG;IACH,QAAQ,CAAC,OAAO,yDAAuC;IAEvD;;OAEG;IACH,QAAQ,CAAC,KAAK,mEAAqC;gBASvC,KAAK,EAAE,CAAC,GAAG,SAAS,EAAE,MAAM,EAAE,sBAAsB,EAAE,EAAE,CAAC,EAAE,MAAM;IA2B7E;;;;OAIG;IACH,SAAS,CAAC,CAAC,gBAAgB,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAM5C;;;;OAIG;IACH,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAW5C;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,CAAC,sBAAsB,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAmBrE"}
@@ -35,27 +35,28 @@ class DependencyTrackingSignal extends Signal {
35
35
  }
36
36
  class ServerConnection {
37
37
  #id;
38
- #config;
38
+ config;
39
39
  #subscription;
40
40
  constructor(id, config) {
41
- this.#config = config;
41
+ this.config = config;
42
42
  this.#id = id;
43
43
  }
44
44
  get subscription() {
45
45
  return this.#subscription;
46
46
  }
47
47
  connect() {
48
- const { client, endpoint, method, params } = this.#config;
48
+ const { client, endpoint, method, params, parentClientSignalId } = this.config;
49
49
  this.#subscription ??= client.subscribe(ENDPOINT, "subscribe", {
50
50
  providerEndpoint: endpoint,
51
51
  providerMethod: method,
52
52
  clientSignalId: this.#id,
53
- params
53
+ params,
54
+ parentClientSignalId
54
55
  });
55
56
  return this.#subscription;
56
57
  }
57
58
  async update(event) {
58
- await this.#config.client.call(ENDPOINT, "update", {
59
+ await this.config.client.call(ENDPOINT, "update", {
59
60
  clientSignalId: this.#id,
60
61
  event
61
62
  });
@@ -67,12 +68,13 @@ class ServerConnection {
67
68
  }
68
69
  const $update = Symbol("update");
69
70
  const $processServerResponse = Symbol("processServerResponse");
71
+ const $setValueQuietly = Symbol("setValueQuietly");
70
72
  class FullStackSignal extends DependencyTrackingSignal {
71
73
  /**
72
74
  * The unique identifier of the signal necessary to communicate with the
73
75
  * server.
74
76
  */
75
- id = nanoid();
77
+ id;
76
78
  /**
77
79
  * The server connection manager.
78
80
  */
@@ -90,18 +92,20 @@ class FullStackSignal extends DependencyTrackingSignal {
90
92
  // Paused at the very start to prevent the signal from sending the initial
91
93
  // value to the server.
92
94
  #paused = true;
93
- constructor(value, config) {
95
+ constructor(value, config, id) {
94
96
  super(
95
97
  value,
96
98
  () => this.#connect(),
97
99
  () => this.#disconnect()
98
100
  );
101
+ this.id = id ?? nanoid();
99
102
  this.server = new ServerConnection(this.id, config);
100
103
  this.subscribe((v) => {
101
104
  if (!this.#paused) {
102
105
  this.#pending.value = true;
103
106
  this.#error.value = void 0;
104
- this[$update](createSetStateEvent(v));
107
+ const signalId = config.parentClientSignalId !== void 0 ? this.id : void 0;
108
+ this[$update](createSetStateEvent(v, signalId, config.parentClientSignalId));
105
109
  }
106
110
  });
107
111
  this.#paused = false;
@@ -111,9 +115,9 @@ class FullStackSignal extends DependencyTrackingSignal {
111
115
  * @param value - The new value.
112
116
  * @internal
113
117
  */
114
- setValueLocal(value) {
118
+ [$setValueQuietly](value) {
115
119
  this.#paused = true;
116
- this.value = value;
120
+ super.value = value;
117
121
  this.#paused = false;
118
122
  }
119
123
  /**
@@ -144,6 +148,7 @@ class FullStackSignal extends DependencyTrackingSignal {
144
148
  }
145
149
  export {
146
150
  $processServerResponse,
151
+ $setValueQuietly,
147
152
  $update,
148
153
  DependencyTrackingSignal,
149
154
  FullStackSignal
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["src/FullStackSignal.ts"],
4
- "sourcesContent": ["import type { ActionOnLostSubscription, 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (!(window as any).Vaadin?.featureFlags?.fullstackSignals) {\n // Remove when removing feature flag\n throw new Error(\n `The Hilla Fullstack Signals API is currently considered experimental and may change in the future. To use it you need to explicitly enable it in Copilot or by adding com.vaadin.experimental.fullstackSignals=true to vaadin-featureflags.properties`,\n );\n }\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\n .connect()\n .onSubscriptionLost(() => 'resubscribe' as ActionOnLostSubscription)\n .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": "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",
4
+ "sourcesContent": ["import type { ActionOnLostSubscription, 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 // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n if (!(window as any).Vaadin?.featureFlags?.fullstackSignals) {\n // Remove when removing feature flag\n throw new Error(\n `The Hilla Fullstack Signals API is currently considered experimental and may change in the future. To use it you need to explicitly enable it in Copilot or by adding com.vaadin.experimental.fullstackSignals=true to vaadin-featureflags.properties`,\n );\n }\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 * The unique identifier of the parent signal in the client.\n */\n parentClientSignalId?: string;\n}>;\n\n/**\n * A server connection manager.\n */\nclass ServerConnection {\n readonly #id: string;\n readonly config: ServerConnectionConfig;\n #subscription?: Subscription<StateEvent>;\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, parentClientSignalId } = this.config;\n\n this.#subscription ??= client.subscribe(ENDPOINT, 'subscribe', {\n providerEndpoint: endpoint,\n providerMethod: method,\n clientSignalId: this.#id,\n params,\n parentClientSignalId,\n });\n\n return this.#subscription;\n }\n\n async update(event: StateEvent): 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');\nexport const $setValueQuietly = Symbol('setValueQuietly');\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: string;\n\n /**\n * The server connection manager.\n */\n readonly server: ServerConnection;\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, id?: string) {\n super(\n value,\n () => this.#connect(),\n () => this.#disconnect(),\n );\n this.id = id ?? nanoid();\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 // For internal signals, the provided non-null to the constructor should\n // be used along with the parent client side signal id when sending the\n // set event to the server. For internal signals this combination is\n // needed for addressing the correct parent/child signal instances on\n // the server. For a standalone signal, both of them should be passed in\n // as undefined:\n const signalId = config.parentClientSignalId !== undefined ? this.id : undefined;\n this[$update](createSetStateEvent(v, signalId, config.parentClientSignalId));\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 [$setValueQuietly](value: T): void {\n this.#paused = true;\n super.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): 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): void;\n\n #connect() {\n this.server\n .connect()\n .onSubscriptionLost(() => 'resubscribe' as ActionOnLostSubscription)\n .onNext((event: StateEvent) => {\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": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,31 @@
1
+ import { CollectionSignal } from './CollectionSignal.js';
2
+ import { type StateEvent } from './events.js';
3
+ import { $processServerResponse, type ServerConnectionConfig } from './FullStackSignal.js';
4
+ import { ValueSignal } from './ValueSignal.js';
5
+ /**
6
+ * A {@link FullStackSignal} that represents a shared list of values, where each
7
+ * value is represented by a {@link ValueSignal}.
8
+ * The list can be modified by calling the defined methods to insert or remove
9
+ * items, but the `value` property of a `ListSignal` instance is read-only and
10
+ * cannot be assigned directly.
11
+ * The value of each item in the list can be manipulated similar to a regular
12
+ * {@link ValueSignal}.
13
+ *
14
+ * @typeParam T - The type of the values in the list.
15
+ */
16
+ export declare class ListSignal<T> extends CollectionSignal<ReadonlyArray<ValueSignal<T>>> {
17
+ #private;
18
+ constructor(config: ServerConnectionConfig);
19
+ protected [$processServerResponse](event: StateEvent): void;
20
+ /**
21
+ * Inserts a new value at the end of the list.
22
+ * @param value - The value to insert.
23
+ */
24
+ insertLast(value: T): void;
25
+ /**
26
+ * Removes the given item from the list.
27
+ * @param item - The item to remove.
28
+ */
29
+ remove(item: ValueSignal<T>): void;
30
+ }
31
+ //# sourceMappingURL=ListSignal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ListSignal.d.ts","sourceRoot":"","sources":["src/ListSignal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EASL,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,sBAAsB,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACtH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAU/C;;;;;;;;;;GAUG;AACH,qBAAa,UAAU,CAAC,CAAC,CAAE,SAAQ,gBAAgB,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;;gBAMpE,MAAM,EAAE,sBAAsB;cAgBvB,CAAC,sBAAsB,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAoFpE;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAK1B;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI;CAQnC"}
package/ListSignal.js ADDED
@@ -0,0 +1,132 @@
1
+ import { CollectionSignal } from "./CollectionSignal.js";
2
+ import {
3
+ createInsertLastStateEvent,
4
+ createRemoveStateEvent,
5
+ isInsertLastStateEvent,
6
+ isListSnapshotStateEvent,
7
+ isRemoveStateEvent
8
+ } from "./events.js";
9
+ import { $processServerResponse, $setValueQuietly, $update } from "./FullStackSignal.js";
10
+ import { ValueSignal } from "./ValueSignal.js";
11
+ class ListSignal extends CollectionSignal {
12
+ #head;
13
+ #tail;
14
+ #values = /* @__PURE__ */ new Map();
15
+ constructor(config) {
16
+ const initialValue = [];
17
+ super(initialValue, config);
18
+ }
19
+ #computeItems() {
20
+ let current = this.#head;
21
+ const result = [];
22
+ while (current !== void 0) {
23
+ const entry = this.#values.get(current);
24
+ result.push(entry.value);
25
+ current = entry.next;
26
+ }
27
+ return result;
28
+ }
29
+ [$processServerResponse](event) {
30
+ if (!event.accepted) {
31
+ return;
32
+ }
33
+ if (isListSnapshotStateEvent(event)) {
34
+ this.#handleSnapshotEvent(event);
35
+ } else if (isInsertLastStateEvent(event)) {
36
+ this.#handleInsertLastUpdate(event);
37
+ } else if (isRemoveStateEvent(event)) {
38
+ this.#handleRemoveUpdate(event);
39
+ }
40
+ }
41
+ #handleInsertLastUpdate(event) {
42
+ if (event.entryId === void 0) {
43
+ throw new Error("Unexpected state: Entry id should be defined when insert last event is accepted");
44
+ }
45
+ const valueSignal = new ValueSignal(
46
+ event.value,
47
+ { ...this.server.config, parentClientSignalId: this.id },
48
+ event.entryId
49
+ );
50
+ const newEntry = { id: valueSignal.id, value: valueSignal };
51
+ if (this.#head === void 0) {
52
+ this.#head = newEntry.id;
53
+ this.#tail = this.#head;
54
+ } else {
55
+ const tailEntry = this.#values.get(this.#tail);
56
+ tailEntry.next = newEntry.id;
57
+ newEntry.prev = this.#tail;
58
+ this.#tail = newEntry.id;
59
+ }
60
+ this.#values.set(valueSignal.id, newEntry);
61
+ this[$setValueQuietly](this.#computeItems());
62
+ }
63
+ #handleRemoveUpdate(event) {
64
+ const entryToRemove = this.#values.get(event.entryId);
65
+ if (entryToRemove === void 0) {
66
+ return;
67
+ }
68
+ this.#values.delete(event.id);
69
+ if (this.#head === entryToRemove.id) {
70
+ if (entryToRemove.next === void 0) {
71
+ this.#head = void 0;
72
+ this.#tail = void 0;
73
+ } else {
74
+ const newHead = this.#values.get(entryToRemove.next);
75
+ this.#head = newHead.id;
76
+ newHead.prev = void 0;
77
+ }
78
+ } else {
79
+ const prevEntry = this.#values.get(entryToRemove.prev);
80
+ const nextEntry = entryToRemove.next !== void 0 ? this.#values.get(entryToRemove.next) : void 0;
81
+ if (nextEntry === void 0) {
82
+ this.#tail = prevEntry.id;
83
+ prevEntry.next = void 0;
84
+ } else {
85
+ prevEntry.next = nextEntry.id;
86
+ nextEntry.prev = prevEntry.id;
87
+ }
88
+ }
89
+ this[$setValueQuietly](this.#computeItems());
90
+ }
91
+ #handleSnapshotEvent(event) {
92
+ event.entries.forEach((entry) => {
93
+ this.#values.set(entry.id, {
94
+ id: entry.id,
95
+ prev: entry.prev,
96
+ next: entry.next,
97
+ value: new ValueSignal(entry.value, { ...this.server.config, parentClientSignalId: this.id }, entry.id)
98
+ });
99
+ if (entry.prev === void 0) {
100
+ this.#head = entry.id;
101
+ }
102
+ if (entry.next === void 0) {
103
+ this.#tail = entry.id;
104
+ }
105
+ });
106
+ this[$setValueQuietly](this.#computeItems());
107
+ }
108
+ /**
109
+ * Inserts a new value at the end of the list.
110
+ * @param value - The value to insert.
111
+ */
112
+ insertLast(value) {
113
+ const event = createInsertLastStateEvent(value);
114
+ this[$update](event);
115
+ }
116
+ /**
117
+ * Removes the given item from the list.
118
+ * @param item - The item to remove.
119
+ */
120
+ remove(item) {
121
+ const entryToRemove = this.#values.get(item.id);
122
+ if (entryToRemove === void 0) {
123
+ return;
124
+ }
125
+ const removeEvent = createRemoveStateEvent(entryToRemove.value.id);
126
+ this[$update](removeEvent);
127
+ }
128
+ }
129
+ export {
130
+ ListSignal
131
+ };
132
+ //# sourceMappingURL=ListSignal.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["src/ListSignal.ts"],
4
+ "sourcesContent": ["import { CollectionSignal } from './CollectionSignal.js';\nimport {\n createInsertLastStateEvent,\n createRemoveStateEvent,\n type InsertLastStateEvent,\n isInsertLastStateEvent,\n isListSnapshotStateEvent,\n isRemoveStateEvent,\n type ListSnapshotStateEvent,\n type RemoveStateEvent,\n type StateEvent,\n} from './events.js';\nimport { $processServerResponse, $setValueQuietly, $update, type ServerConnectionConfig } from './FullStackSignal.js';\nimport { ValueSignal } from './ValueSignal.js';\n\ntype EntryId = string;\ntype Entry<T> = {\n id: EntryId;\n value: ValueSignal<T>;\n next?: EntryId;\n prev?: EntryId;\n};\n\n/**\n * A {@link FullStackSignal} that represents a shared list of values, where each\n * value is represented by a {@link ValueSignal}.\n * The list can be modified by calling the defined methods to insert or remove\n * items, but the `value` property of a `ListSignal` instance is read-only and\n * cannot be assigned directly.\n * The value of each item in the list can be manipulated similar to a regular\n * {@link ValueSignal}.\n *\n * @typeParam T - The type of the values in the list.\n */\nexport class ListSignal<T> extends CollectionSignal<ReadonlyArray<ValueSignal<T>>> {\n #head?: EntryId;\n #tail?: EntryId;\n\n readonly #values = new Map<string, Entry<T>>();\n\n constructor(config: ServerConnectionConfig) {\n const initialValue: Array<ValueSignal<T>> = [];\n super(initialValue, config);\n }\n\n #computeItems(): ReadonlyArray<ValueSignal<T>> {\n let current = this.#head;\n const result: Array<ValueSignal<T>> = [];\n while (current !== undefined) {\n const entry = this.#values.get(current)!;\n result.push(entry.value);\n current = entry.next;\n }\n return result;\n }\n\n protected override [$processServerResponse](event: StateEvent): void {\n if (!event.accepted) {\n return;\n }\n if (isListSnapshotStateEvent<T>(event)) {\n this.#handleSnapshotEvent(event);\n } else if (isInsertLastStateEvent<T>(event)) {\n this.#handleInsertLastUpdate(event);\n } else if (isRemoveStateEvent(event)) {\n this.#handleRemoveUpdate(event);\n }\n }\n\n #handleInsertLastUpdate(event: InsertLastStateEvent<T>): void {\n if (event.entryId === undefined) {\n throw new Error('Unexpected state: Entry id should be defined when insert last event is accepted');\n }\n const valueSignal = new ValueSignal<T>(\n event.value,\n { ...this.server.config, parentClientSignalId: this.id },\n event.entryId,\n );\n const newEntry: Entry<T> = { id: valueSignal.id, value: valueSignal };\n\n if (this.#head === undefined) {\n this.#head = newEntry.id;\n this.#tail = this.#head;\n } else {\n const tailEntry = this.#values.get(this.#tail!)!;\n tailEntry.next = newEntry.id;\n newEntry.prev = this.#tail;\n this.#tail = newEntry.id;\n }\n this.#values.set(valueSignal.id, newEntry);\n this[$setValueQuietly](this.#computeItems());\n }\n\n #handleRemoveUpdate(event: RemoveStateEvent): void {\n const entryToRemove = this.#values.get(event.entryId);\n if (entryToRemove === undefined) {\n return;\n }\n this.#values.delete(event.id);\n if (this.#head === entryToRemove.id) {\n if (entryToRemove.next === undefined) {\n this.#head = undefined;\n this.#tail = undefined;\n } else {\n const newHead = this.#values.get(entryToRemove.next)!;\n this.#head = newHead.id;\n newHead.prev = undefined;\n }\n } else {\n const prevEntry = this.#values.get(entryToRemove.prev!)!;\n const nextEntry = entryToRemove.next !== undefined ? this.#values.get(entryToRemove.next) : undefined;\n if (nextEntry === undefined) {\n this.#tail = prevEntry.id;\n prevEntry.next = undefined;\n } else {\n prevEntry.next = nextEntry.id;\n nextEntry.prev = prevEntry.id;\n }\n }\n this[$setValueQuietly](this.#computeItems());\n }\n\n #handleSnapshotEvent(event: ListSnapshotStateEvent<T>): void {\n event.entries.forEach((entry) => {\n this.#values.set(entry.id, {\n id: entry.id,\n prev: entry.prev,\n next: entry.next,\n value: new ValueSignal(entry.value, { ...this.server.config, parentClientSignalId: this.id }, entry.id),\n });\n if (entry.prev === undefined) {\n this.#head = entry.id;\n }\n if (entry.next === undefined) {\n this.#tail = entry.id;\n }\n });\n this[$setValueQuietly](this.#computeItems());\n }\n\n /**\n * Inserts a new value at the end of the list.\n * @param value - The value to insert.\n */\n insertLast(value: T): void {\n const event = createInsertLastStateEvent(value);\n this[$update](event);\n }\n\n /**\n * Removes the given item from the list.\n * @param item - The item to remove.\n */\n remove(item: ValueSignal<T>): void {\n const entryToRemove = this.#values.get(item.id);\n if (entryToRemove === undefined) {\n return;\n }\n const removeEvent = createRemoveStateEvent(entryToRemove.value.id);\n this[$update](removeEvent);\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/NumberSignal.d.ts CHANGED
@@ -14,7 +14,7 @@ import { ValueSignal } from './ValueSignal.js';
14
14
  *
15
15
  * @example
16
16
  * ```tsx
17
- * const counter = CounterService.counter();
17
+ * const counter = CounterService.counter();
18
18
  *
19
19
  * return (
20
20
  * <Button onClick={() => counter.incrementBy(1)}>
@@ -39,6 +39,6 @@ export declare class NumberSignal extends ValueSignal<number> {
39
39
  * negative.
40
40
  */
41
41
  incrementBy(delta: number): void;
42
- protected [$processServerResponse](event: StateEvent<number>): void;
42
+ protected [$processServerResponse](event: StateEvent): void;
43
43
  }
44
44
  //# sourceMappingURL=NumberSignal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NumberSignal.d.ts","sourceRoot":"","sources":["src/NumberSignal.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAW,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,YAAa,SAAQ,WAAW,CAAC,MAAM,CAAC;;IAEnD;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;cAUb,CAAC,sBAAsB,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,GAAG,IAAI;CAW7E"}
1
+ {"version":3,"file":"NumberSignal.d.ts","sourceRoot":"","sources":["src/NumberSignal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAChG,OAAO,EAAE,sBAAsB,EAA6B,MAAM,sBAAsB,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,YAAa,SAAQ,WAAW,CAAC,MAAM,CAAC;;IAEnD;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;cAUb,CAAC,sBAAsB,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAWrE"}
package/NumberSignal.js CHANGED
@@ -1,5 +1,5 @@
1
- import { createIncrementStateEvent } from "./events.js";
2
- import { $processServerResponse, $update } from "./FullStackSignal.js";
1
+ import { createIncrementStateEvent, isIncrementStateEvent } from "./events.js";
2
+ import { $processServerResponse, $setValueQuietly, $update } from "./FullStackSignal.js";
3
3
  import { ValueSignal } from "./ValueSignal.js";
4
4
  class NumberSignal extends ValueSignal {
5
5
  #sentIncrementEvents = /* @__PURE__ */ new Map();
@@ -19,18 +19,18 @@ class NumberSignal extends ValueSignal {
19
19
  if (delta === 0) {
20
20
  return;
21
21
  }
22
- this.setValueLocal(this.value + delta);
22
+ this[$setValueQuietly](this.value + delta);
23
23
  const event = createIncrementStateEvent(delta);
24
24
  this.#sentIncrementEvents.set(event.id, event);
25
25
  this[$update](event);
26
26
  }
27
27
  [$processServerResponse](event) {
28
- if (event.accepted && event.type === "increment") {
28
+ if (event.accepted && isIncrementStateEvent(event)) {
29
29
  if (this.#sentIncrementEvents.has(event.id)) {
30
30
  this.#sentIncrementEvents.delete(event.id);
31
31
  return;
32
32
  }
33
- this.setValueLocal(this.value + event.value);
33
+ this[$setValueQuietly](this.value + event.value);
34
34
  } else {
35
35
  super[$processServerResponse](event);
36
36
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["src/NumberSignal.ts"],
4
- "sourcesContent": ["import { createIncrementStateEvent, type StateEvent } from './events.js';\nimport { $processServerResponse, $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 readonly #sentIncrementEvents = new Map<string, StateEvent<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.#sentIncrementEvents.set(event.id, event);\n this[$update](event);\n }\n\n protected override [$processServerResponse](event: StateEvent<number>): void {\n if (event.accepted && event.type === 'increment') {\n if (this.#sentIncrementEvents.has(event.id)) {\n this.#sentIncrementEvents.delete(event.id);\n return;\n }\n this.setValueLocal(this.value + event.value);\n } else {\n super[$processServerResponse](event);\n }\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,iCAAkD;AAC3D,SAAS,wBAAwB,eAAe;AAChD,SAAS,mBAAmB;AAyBrB,MAAM,qBAAqB,YAAoB;AAAA,EAC3C,uBAAuB,oBAAI,IAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAapE,YAAY,OAAqB;AAC/B,QAAI,UAAU,GAAG;AACf;AAAA,IACF;AACA,SAAK,cAAc,KAAK,QAAQ,KAAK;AACrC,UAAM,QAAQ,0BAA0B,KAAK;AAC7C,SAAK,qBAAqB,IAAI,MAAM,IAAI,KAAK;AAC7C,SAAK,OAAO,EAAE,KAAK;AAAA,EACrB;AAAA,EAEA,CAAoB,sBAAsB,EAAE,OAAiC;AAC3E,QAAI,MAAM,YAAY,MAAM,SAAS,aAAa;AAChD,UAAI,KAAK,qBAAqB,IAAI,MAAM,EAAE,GAAG;AAC3C,aAAK,qBAAqB,OAAO,MAAM,EAAE;AACzC;AAAA,MACF;AACA,WAAK,cAAc,KAAK,QAAQ,MAAM,KAAK;AAAA,IAC7C,OAAO;AACL,YAAM,sBAAsB,EAAE,KAAK;AAAA,IACrC;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { createIncrementStateEvent, isIncrementStateEvent, type StateEvent } from './events.js';\nimport { $processServerResponse, $setValueQuietly, $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 readonly #sentIncrementEvents = new Map<string, StateEvent>();\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[$setValueQuietly](this.value + delta);\n const event = createIncrementStateEvent(delta);\n this.#sentIncrementEvents.set(event.id, event);\n this[$update](event);\n }\n\n protected override [$processServerResponse](event: StateEvent): void {\n if (event.accepted && isIncrementStateEvent(event)) {\n if (this.#sentIncrementEvents.has(event.id)) {\n this.#sentIncrementEvents.delete(event.id);\n return;\n }\n this[$setValueQuietly](this.value + event.value);\n } else {\n super[$processServerResponse](event);\n }\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,2BAA2B,6BAA8C;AAClF,SAAS,wBAAwB,kBAAkB,eAAe;AAClE,SAAS,mBAAmB;AAyBrB,MAAM,qBAAqB,YAAoB;AAAA,EAC3C,uBAAuB,oBAAI,IAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAa5D,YAAY,OAAqB;AAC/B,QAAI,UAAU,GAAG;AACf;AAAA,IACF;AACA,SAAK,gBAAgB,EAAE,KAAK,QAAQ,KAAK;AACzC,UAAM,QAAQ,0BAA0B,KAAK;AAC7C,SAAK,qBAAqB,IAAI,MAAM,IAAI,KAAK;AAC7C,SAAK,OAAO,EAAE,KAAK;AAAA,EACrB;AAAA,EAEA,CAAoB,sBAAsB,EAAE,OAAyB;AACnE,QAAI,MAAM,YAAY,sBAAsB,KAAK,GAAG;AAClD,UAAI,KAAK,qBAAqB,IAAI,MAAM,EAAE,GAAG;AAC3C,aAAK,qBAAqB,OAAO,MAAM,EAAE;AACzC;AAAA,MACF;AACA,WAAK,gBAAgB,EAAE,KAAK,QAAQ,MAAM,KAAK;AAAA,IACjD,OAAO;AACL,YAAM,sBAAsB,EAAE,KAAK;AAAA,IACrC;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
package/ValueSignal.d.ts CHANGED
@@ -44,6 +44,6 @@ export declare class ValueSignal<T> extends FullStackSignal<T> {
44
44
  * @returns An operation subscription that can be canceled.
45
45
  */
46
46
  update(callback: (value: T) => T): OperationSubscription;
47
- protected [$processServerResponse](event: StateEvent<T>): void;
47
+ protected [$processServerResponse](event: StateEvent): void;
48
48
  }
49
49
  //# sourceMappingURL=ValueSignal.d.ts.map
@@ -1 +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;CA8BxE"}
1
+ {"version":3,"file":"ValueSignal.d.ts","sourceRoot":"","sources":["src/ValueSignal.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AACrB,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;IAMvC;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,qBAAqB;cAerC,CAAC,sBAAsB,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CA8BrE"}
package/ValueSignal.js CHANGED
@@ -1,4 +1,9 @@
1
- import { createReplaceStateEvent } from "./events.js";
1
+ import {
2
+ createReplaceStateEvent,
3
+ isReplaceStateEvent,
4
+ isSetStateEvent,
5
+ isSnapshotStateEvent
6
+ } from "./events.js";
2
7
  import { $processServerResponse, $update, FullStackSignal } from "./FullStackSignal.js";
3
8
  class ValueSignal extends FullStackSignal {
4
9
  #pendingRequests = /* @__PURE__ */ new Map();
@@ -23,7 +28,9 @@ class ValueSignal extends FullStackSignal {
23
28
  * @param newValue - The new value.
24
29
  */
25
30
  replace(expected, newValue) {
26
- this[$update](createReplaceStateEvent(expected, newValue));
31
+ const { parentClientSignalId } = this.server.config;
32
+ const signalId = parentClientSignalId !== void 0 ? this.id : void 0;
33
+ this[$update](createReplaceStateEvent(expected, newValue, signalId, parentClientSignalId));
27
34
  }
28
35
  /**
29
36
  * Tries to update the value by applying the callback function to the current
@@ -62,7 +69,7 @@ class ValueSignal extends FullStackSignal {
62
69
  this.update(record.callback);
63
70
  }
64
71
  }
65
- if (event.accepted || event.type === "snapshot") {
72
+ if (event.accepted || isSnapshotStateEvent(event)) {
66
73
  if (record) {
67
74
  record.waiter.resolve();
68
75
  }
@@ -70,9 +77,9 @@ class ValueSignal extends FullStackSignal {
70
77
  }
71
78
  }
72
79
  #applyAcceptedEvent(event) {
73
- if (event.type === "set" || event.type === "snapshot") {
80
+ if (isSetStateEvent(event) || isSnapshotStateEvent(event)) {
74
81
  this.value = event.value;
75
- } else if (event.type === "replace") {
82
+ } else if (isReplaceStateEvent(event)) {
76
83
  if (JSON.stringify(this.value) === JSON.stringify(event.expected)) {
77
84
  this.value = event.value;
78
85
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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.accepted && record) {\n if (!record.canceled) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.update(record.callback);\n }\n }\n\n if (event.accepted || event.type === 'snapshot') {\n if (record) {\n record.waiter.resolve();\n }\n this.#applyAcceptedEvent(event);\n }\n }\n\n #applyAcceptedEvent(event: StateEvent<T>): void {\n if (event.type === 'set' || event.type === 'snapshot') {\n this.value = event.value;\n } else if (event.type === 'replace') {\n if (JSON.stringify(this.value) === JSON.stringify(event.expected)) {\n this.value = event.value;\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,CAAC,MAAM,YAAY,QAAQ;AAC7B,UAAI,CAAC,OAAO,UAAU;AAEpB,aAAK,OAAO,OAAO,QAAQ;AAAA,MAC7B;AAAA,IACF;AAEA,QAAI,MAAM,YAAY,MAAM,SAAS,YAAY;AAC/C,UAAI,QAAQ;AACV,eAAO,OAAO,QAAQ;AAAA,MACxB;AACA,WAAK,oBAAoB,KAAK;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,oBAAoB,OAA4B;AAC9C,QAAI,MAAM,SAAS,SAAS,MAAM,SAAS,YAAY;AACrD,WAAK,QAAQ,MAAM;AAAA,IACrB,WAAW,MAAM,SAAS,WAAW;AACnC,UAAI,KAAK,UAAU,KAAK,KAAK,MAAM,KAAK,UAAU,MAAM,QAAQ,GAAG;AACjE,aAAK,QAAQ,MAAM;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import {\n createReplaceStateEvent,\n isReplaceStateEvent,\n isSetStateEvent,\n isSnapshotStateEvent,\n type StateEvent,\n} 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 const { parentClientSignalId } = this.server.config;\n const signalId = parentClientSignalId !== undefined ? this.id : undefined;\n this[$update](createReplaceStateEvent(expected, newValue, signalId, parentClientSignalId));\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): void {\n const record = this.#pendingRequests.get(event.id);\n if (record) {\n this.#pendingRequests.delete(event.id);\n }\n\n if (!event.accepted && record) {\n if (!record.canceled) {\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n this.update(record.callback);\n }\n }\n\n if (event.accepted || isSnapshotStateEvent<T>(event)) {\n if (record) {\n record.waiter.resolve();\n }\n this.#applyAcceptedEvent(event);\n }\n }\n\n #applyAcceptedEvent(event: StateEvent): void {\n if (isSetStateEvent<T>(event) || isSnapshotStateEvent<T>(event)) {\n this.value = event.value;\n } else if (isReplaceStateEvent<T>(event)) {\n if (JSON.stringify(this.value) === JSON.stringify(event.expected)) {\n this.value = event.value;\n }\n }\n }\n}\n"],
5
+ "mappings": "AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,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,UAAM,EAAE,qBAAqB,IAAI,KAAK,OAAO;AAC7C,UAAM,WAAW,yBAAyB,SAAY,KAAK,KAAK;AAChE,SAAK,OAAO,EAAE,wBAAwB,UAAU,UAAU,UAAU,oBAAoB,CAAC;AAAA,EAC3F;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,OAAyB;AACnE,UAAM,SAAS,KAAK,iBAAiB,IAAI,MAAM,EAAE;AACjD,QAAI,QAAQ;AACV,WAAK,iBAAiB,OAAO,MAAM,EAAE;AAAA,IACvC;AAEA,QAAI,CAAC,MAAM,YAAY,QAAQ;AAC7B,UAAI,CAAC,OAAO,UAAU;AAEpB,aAAK,OAAO,OAAO,QAAQ;AAAA,MAC7B;AAAA,IACF;AAEA,QAAI,MAAM,YAAY,qBAAwB,KAAK,GAAG;AACpD,UAAI,QAAQ;AACV,eAAO,OAAO,QAAQ;AAAA,MACxB;AACA,WAAK,oBAAoB,KAAK;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,oBAAoB,OAAyB;AAC3C,QAAI,gBAAmB,KAAK,KAAK,qBAAwB,KAAK,GAAG;AAC/D,WAAK,QAAQ,MAAM;AAAA,IACrB,WAAW,oBAAuB,KAAK,GAAG;AACxC,UAAI,KAAK,UAAU,KAAK,KAAK,MAAM,KAAK,UAAU,MAAM,QAAQ,GAAG;AACjE,aAAK,QAAQ,MAAM;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
package/events.d.ts CHANGED
@@ -1,13 +1,17 @@
1
- import type { Simplify } from 'type-fest';
1
+ export type StateEvent = Readonly<{
2
+ id: string;
3
+ type: string;
4
+ value: unknown;
5
+ accepted: boolean;
6
+ parentSignalId?: string;
7
+ }>;
2
8
  /**
3
9
  * Creates a new state event type.
4
10
  */
5
- type CreateStateEventType<V, T extends string, C extends Record<string, unknown> = Record<never, never>> = Simplify<Readonly<{
6
- id: string;
11
+ type CreateStateEventType<V, T extends string, C extends Record<string, unknown> = Record<never, never>> = Readonly<{
7
12
  type: T;
8
13
  value: V;
9
- accepted: boolean;
10
- }> & Readonly<C>>;
14
+ }> & Readonly<C> & StateEvent;
11
15
  /**
12
16
  * A state event received from the server describing the current state of the
13
17
  * signal.
@@ -17,16 +21,37 @@ export type SnapshotStateEvent<T> = CreateStateEventType<T, 'snapshot'>;
17
21
  * A state event defines a new value of the signal shared with the server. The
18
22
  */
19
23
  export type SetStateEvent<T> = CreateStateEventType<T, 'set'>;
20
- export declare function createSetStateEvent<T>(value: T): SetStateEvent<T>;
24
+ export declare function createSetStateEvent<T>(value: T, signalId?: string, parentSignalId?: string): SetStateEvent<T>;
21
25
  export type ReplaceStateEvent<T> = CreateStateEventType<T, 'replace', {
22
26
  expected: T;
23
27
  }>;
24
- export declare function createReplaceStateEvent<T>(expected: T, value: T): ReplaceStateEvent<T>;
28
+ export declare function createReplaceStateEvent<T>(expected: T, value: T, signalId?: string, parentSignalId?: string): ReplaceStateEvent<T>;
25
29
  export type IncrementStateEvent = CreateStateEventType<number, 'increment'>;
26
30
  export declare function createIncrementStateEvent(delta: number): IncrementStateEvent;
27
- /**
28
- * An object that describes the change of the signal state.
29
- */
30
- export type StateEvent<T> = IncrementStateEvent | ReplaceStateEvent<T> | SetStateEvent<T> | SnapshotStateEvent<T>;
31
+ export type ListEntry<T> = Readonly<{
32
+ id: string;
33
+ prev?: string;
34
+ next?: string;
35
+ value: T;
36
+ }>;
37
+ export type ListSnapshotStateEvent<T> = CreateStateEventType<never, 'snapshot', {
38
+ entries: Array<ListEntry<T>>;
39
+ }>;
40
+ export type InsertLastStateEvent<T> = CreateStateEventType<T, 'insert', {
41
+ position: 'last';
42
+ entryId?: string;
43
+ }>;
44
+ export declare function createInsertLastStateEvent<T>(value: T): InsertLastStateEvent<T>;
45
+ export type RemoveStateEvent = CreateStateEventType<never, 'remove', {
46
+ entryId: string;
47
+ }>;
48
+ export declare function createRemoveStateEvent(entryId: string): RemoveStateEvent;
49
+ export declare function isSnapshotStateEvent<T>(event: unknown): event is SnapshotStateEvent<T>;
50
+ export declare function isSetStateEvent<T>(event: unknown): event is SetStateEvent<T>;
51
+ export declare function isReplaceStateEvent<T>(event: unknown): event is ReplaceStateEvent<T>;
52
+ export declare function isIncrementStateEvent(event: unknown): event is IncrementStateEvent;
53
+ export declare function isListSnapshotStateEvent<T>(event: unknown): event is ListSnapshotStateEvent<T>;
54
+ export declare function isInsertLastStateEvent<T>(event: unknown): event is InsertLastStateEvent<T>;
55
+ export declare function isRemoveStateEvent(event: unknown): event is RemoveStateEvent;
31
56
  export {};
32
57
  //# sourceMappingURL=events.d.ts.map
package/events.d.ts.map CHANGED
@@ -1 +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;IACT,QAAQ,EAAE,OAAO,CAAC;CACnB,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;;GAEG;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,CAOjE;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,CAQtF;AAED,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE5E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,CAO5E;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,mBAAmB,GAAG,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["src/events.ts"],"names":[],"mappings":"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"}
package/events.js CHANGED
@@ -1,19 +1,21 @@
1
1
  import { nanoid } from "nanoid";
2
- function createSetStateEvent(value) {
2
+ function createSetStateEvent(value, signalId, parentSignalId) {
3
3
  return {
4
- id: nanoid(),
4
+ id: signalId ?? nanoid(),
5
5
  type: "set",
6
6
  value,
7
- accepted: false
7
+ accepted: false,
8
+ ...parentSignalId !== void 0 ? { parentSignalId } : {}
8
9
  };
9
10
  }
10
- function createReplaceStateEvent(expected, value) {
11
+ function createReplaceStateEvent(expected, value, signalId, parentSignalId) {
11
12
  return {
12
- id: nanoid(),
13
+ id: signalId ?? nanoid(),
13
14
  type: "replace",
14
15
  value,
15
16
  expected,
16
- accepted: false
17
+ accepted: false,
18
+ ...parentSignalId !== void 0 ? { parentSignalId } : {}
17
19
  };
18
20
  }
19
21
  function createIncrementStateEvent(delta) {
@@ -24,9 +26,60 @@ function createIncrementStateEvent(delta) {
24
26
  accepted: false
25
27
  };
26
28
  }
29
+ function createInsertLastStateEvent(value) {
30
+ return {
31
+ id: nanoid(),
32
+ type: "insert",
33
+ value,
34
+ position: "last",
35
+ accepted: false
36
+ };
37
+ }
38
+ function createRemoveStateEvent(entryId) {
39
+ return {
40
+ id: nanoid(),
41
+ type: "remove",
42
+ entryId,
43
+ value: void 0,
44
+ accepted: false
45
+ };
46
+ }
47
+ function isStateEvent(event) {
48
+ return typeof event === "object" && event !== null && typeof event.id === "string" && typeof event.type === "string" && typeof event.value !== "undefined" && typeof event.accepted === "boolean";
49
+ }
50
+ function isSnapshotStateEvent(event) {
51
+ return isStateEvent(event) && event.type === "snapshot";
52
+ }
53
+ function isSetStateEvent(event) {
54
+ return isStateEvent(event) && event.type === "set";
55
+ }
56
+ function isReplaceStateEvent(event) {
57
+ return isStateEvent(event) && typeof event.expected !== "undefined" && event.type === "replace";
58
+ }
59
+ function isIncrementStateEvent(event) {
60
+ return isStateEvent(event) && event.type === "increment";
61
+ }
62
+ function isListSnapshotStateEvent(event) {
63
+ return typeof event === "object" && event !== null && typeof event.id === "string" && event.type === "snapshot" && event.entries instanceof Array && typeof event.accepted !== "undefined";
64
+ }
65
+ function isInsertLastStateEvent(event) {
66
+ return isStateEvent(event) && event.type === "insert" && event.position === "last";
67
+ }
68
+ function isRemoveStateEvent(event) {
69
+ return typeof event === "object" && event !== null && typeof event.id === "string" && event.type === "remove" && typeof event.entryId === "string" && typeof event.value === "undefined";
70
+ }
27
71
  export {
28
72
  createIncrementStateEvent,
73
+ createInsertLastStateEvent,
74
+ createRemoveStateEvent,
29
75
  createReplaceStateEvent,
30
- createSetStateEvent
76
+ createSetStateEvent,
77
+ isIncrementStateEvent,
78
+ isInsertLastStateEvent,
79
+ isListSnapshotStateEvent,
80
+ isRemoveStateEvent,
81
+ isReplaceStateEvent,
82
+ isSetStateEvent,
83
+ isSnapshotStateEvent
31
84
  };
32
85
  //# sourceMappingURL=events.js.map
package/events.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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 accepted: boolean;\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\n/**\n * A state event defines a new value of the signal shared with the server. The\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 accepted: false,\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 accepted: false,\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 accepted: false,\n };\n}\n\n/**\n * An object that describes the change of the signal state.\n */\nexport type StateEvent<T> = IncrementStateEvent | ReplaceStateEvent<T> | SetStateEvent<T> | SnapshotStateEvent<T>;\n"],
5
- "mappings": "AAAA,SAAS,cAAc;AA2BhB,SAAS,oBAAuB,OAA4B;AACjE,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX,MAAM;AAAA,IACN;AAAA,IACA,UAAU;AAAA,EACZ;AACF;AAIO,SAAS,wBAA2B,UAAa,OAAgC;AACtF,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,UAAU;AAAA,EACZ;AACF;AAIO,SAAS,0BAA0B,OAAoC;AAC5E,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,EACZ;AACF;",
4
+ "sourcesContent": ["import { nanoid } from 'nanoid';\n\nexport type StateEvent = Readonly<{\n id: string;\n type: string;\n value: unknown;\n accepted: boolean;\n parentSignalId?: string;\n}>;\n\n/**\n * Creates a new state event type.\n */\ntype CreateStateEventType<V, T extends string, C extends Record<string, unknown> = Record<never, never>> = Readonly<{\n type: T;\n value: V;\n}> &\n Readonly<C> &\n StateEvent;\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\n/**\n * A state event defines a new value of the signal shared with the server. The\n */\nexport type SetStateEvent<T> = CreateStateEventType<T, 'set'>;\n\nexport function createSetStateEvent<T>(value: T, signalId?: string, parentSignalId?: string): SetStateEvent<T> {\n return {\n id: signalId ?? nanoid(),\n type: 'set',\n value,\n accepted: false,\n ...(parentSignalId !== undefined ? { parentSignalId } : {}),\n };\n}\n\nexport type ReplaceStateEvent<T> = CreateStateEventType<T, 'replace', { expected: T }>;\n\nexport function createReplaceStateEvent<T>(\n expected: T,\n value: T,\n signalId?: string,\n parentSignalId?: string,\n): ReplaceStateEvent<T> {\n return {\n id: signalId ?? nanoid(),\n type: 'replace',\n value,\n expected,\n accepted: false,\n ...(parentSignalId !== undefined ? { parentSignalId } : {}),\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 accepted: false,\n };\n}\n\nexport type ListEntry<T> = Readonly<{\n id: string;\n prev?: string;\n next?: string;\n value: T;\n}>;\n\nexport type ListSnapshotStateEvent<T> = CreateStateEventType<never, 'snapshot', { entries: Array<ListEntry<T>> }>;\n\nexport type InsertLastStateEvent<T> = CreateStateEventType<T, 'insert', { position: 'last'; entryId?: string }>;\n\nexport function createInsertLastStateEvent<T>(value: T): InsertLastStateEvent<T> {\n return {\n id: nanoid(),\n type: 'insert',\n value,\n position: 'last',\n accepted: false,\n };\n}\n\nexport type RemoveStateEvent = CreateStateEventType<never, 'remove', { entryId: string }>;\n\nexport function createRemoveStateEvent(entryId: string): RemoveStateEvent {\n return {\n id: nanoid(),\n type: 'remove',\n entryId,\n value: undefined as never,\n accepted: false,\n };\n}\n\nfunction isStateEvent(event: unknown): event is StateEvent {\n return (\n typeof event === 'object' &&\n event !== null &&\n typeof (event as { id?: unknown }).id === 'string' &&\n typeof (event as { type?: unknown }).type === 'string' &&\n typeof (event as { value?: unknown }).value !== 'undefined' &&\n typeof (event as { accepted?: unknown }).accepted === 'boolean'\n );\n}\n\nexport function isSnapshotStateEvent<T>(event: unknown): event is SnapshotStateEvent<T> {\n return isStateEvent(event) && event.type === 'snapshot';\n}\n\nexport function isSetStateEvent<T>(event: unknown): event is SetStateEvent<T> {\n return isStateEvent(event) && event.type === 'set';\n}\n\nexport function isReplaceStateEvent<T>(event: unknown): event is ReplaceStateEvent<T> {\n return (\n isStateEvent(event) && typeof (event as { expected?: unknown }).expected !== 'undefined' && event.type === 'replace'\n );\n}\n\nexport function isIncrementStateEvent(event: unknown): event is IncrementStateEvent {\n return isStateEvent(event) && event.type === 'increment';\n}\n\nexport function isListSnapshotStateEvent<T>(event: unknown): event is ListSnapshotStateEvent<T> {\n return (\n typeof event === 'object' &&\n event !== null &&\n typeof (event as { id?: unknown }).id === 'string' &&\n (event as { type?: unknown }).type === 'snapshot' &&\n (event as { entries?: unknown }).entries instanceof Array &&\n typeof (event as { accepted?: unknown }).accepted !== 'undefined'\n );\n}\n\nexport function isInsertLastStateEvent<T>(event: unknown): event is InsertLastStateEvent<T> {\n return isStateEvent(event) && event.type === 'insert' && (event as { position?: unknown }).position === 'last';\n}\n\nexport function isRemoveStateEvent(event: unknown): event is RemoveStateEvent {\n return (\n typeof event === 'object' &&\n event !== null &&\n typeof (event as { id?: unknown }).id === 'string' &&\n (event as { type?: unknown }).type === 'remove' &&\n typeof (event as { entryId?: unknown }).entryId === 'string' &&\n typeof (event as { value?: unknown }).value === 'undefined'\n );\n}\n"],
5
+ "mappings": "AAAA,SAAS,cAAc;AA+BhB,SAAS,oBAAuB,OAAU,UAAmB,gBAA2C;AAC7G,SAAO;AAAA,IACL,IAAI,YAAY,OAAO;AAAA,IACvB,MAAM;AAAA,IACN;AAAA,IACA,UAAU;AAAA,IACV,GAAI,mBAAmB,SAAY,EAAE,eAAe,IAAI,CAAC;AAAA,EAC3D;AACF;AAIO,SAAS,wBACd,UACA,OACA,UACA,gBACsB;AACtB,SAAO;AAAA,IACL,IAAI,YAAY,OAAO;AAAA,IACvB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV,GAAI,mBAAmB,SAAY,EAAE,eAAe,IAAI,CAAC;AAAA,EAC3D;AACF;AAIO,SAAS,0BAA0B,OAAoC;AAC5E,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,EACZ;AACF;AAaO,SAAS,2BAA8B,OAAmC;AAC/E,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX,MAAM;AAAA,IACN;AAAA,IACA,UAAU;AAAA,IACV,UAAU;AAAA,EACZ;AACF;AAIO,SAAS,uBAAuB,SAAmC;AACxE,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX,MAAM;AAAA,IACN;AAAA,IACA,OAAO;AAAA,IACP,UAAU;AAAA,EACZ;AACF;AAEA,SAAS,aAAa,OAAqC;AACzD,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA2B,OAAO,YAC1C,OAAQ,MAA6B,SAAS,YAC9C,OAAQ,MAA8B,UAAU,eAChD,OAAQ,MAAiC,aAAa;AAE1D;AAEO,SAAS,qBAAwB,OAAgD;AACtF,SAAO,aAAa,KAAK,KAAK,MAAM,SAAS;AAC/C;AAEO,SAAS,gBAAmB,OAA2C;AAC5E,SAAO,aAAa,KAAK,KAAK,MAAM,SAAS;AAC/C;AAEO,SAAS,oBAAuB,OAA+C;AACpF,SACE,aAAa,KAAK,KAAK,OAAQ,MAAiC,aAAa,eAAe,MAAM,SAAS;AAE/G;AAEO,SAAS,sBAAsB,OAA8C;AAClF,SAAO,aAAa,KAAK,KAAK,MAAM,SAAS;AAC/C;AAEO,SAAS,yBAA4B,OAAoD;AAC9F,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA2B,OAAO,YACzC,MAA6B,SAAS,cACtC,MAAgC,mBAAmB,SACpD,OAAQ,MAAiC,aAAa;AAE1D;AAEO,SAAS,uBAA0B,OAAkD;AAC1F,SAAO,aAAa,KAAK,KAAK,MAAM,SAAS,YAAa,MAAiC,aAAa;AAC1G;AAEO,SAAS,mBAAmB,OAA2C;AAC5E,SACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA2B,OAAO,YACzC,MAA6B,SAAS,YACvC,OAAQ,MAAgC,YAAY,YACpD,OAAQ,MAA8B,UAAU;AAEpD;",
6
6
  "names": []
7
7
  }
package/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import './polyfills.js';
2
2
  export * from './core.js';
3
3
  export { NumberSignal } from './NumberSignal.js';
4
4
  export { ValueSignal } from './ValueSignal.js';
5
+ export { ListSignal } from './ListSignal.js';
5
6
  export type { OperationSubscription } from './ValueSignal.js';
6
7
  export { FullStackSignal } from './FullStackSignal.js';
7
8
  //# 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,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"}
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,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
package/index.js CHANGED
@@ -2,9 +2,11 @@ import "./polyfills.js";
2
2
  export * from "./core.js";
3
3
  import { NumberSignal } from "./NumberSignal.js";
4
4
  import { ValueSignal } from "./ValueSignal.js";
5
+ import { ListSignal } from "./ListSignal.js";
5
6
  import { FullStackSignal } from "./FullStackSignal.js";
6
7
  export {
7
8
  FullStackSignal,
9
+ ListSignal,
8
10
  NumberSignal,
9
11
  ValueSignal
10
12
  };
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/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;",
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 { ListSignal } from './ListSignal.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;AAC5B,SAAS,kBAAkB;AAE3B,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",
3
+ "version": "24.6.0-alpha2",
4
4
  "description": "Signals for Hilla React",
5
5
  "main": "index.js",
6
6
  "module": "index.js",
@@ -47,7 +47,7 @@
47
47
  },
48
48
  "dependencies": {
49
49
  "@preact/signals-react": "^2.0.0",
50
- "@vaadin/hilla-frontend": "24.5.0",
50
+ "@vaadin/hilla-frontend": "24.6.0-alpha2",
51
51
  "nanoid": "^5.0.7"
52
52
  },
53
53
  "peerDependencies": {