@vaadin/hilla-react-signals 24.7.0-alpha9 → 24.7.0-beta3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CollectionSignal.d.ts +0 -9
- package/CollectionSignal.js +9 -15
- package/CollectionSignal.js.map +1 -7
- package/FullStackSignal.d.ts +0 -88
- package/FullStackSignal.js +151 -195
- package/FullStackSignal.js.map +1 -7
- package/ListSignal.d.ts +0 -22
- package/ListSignal.js +112 -138
- package/ListSignal.js.map +1 -7
- package/NumberSignal.d.ts +0 -37
- package/NumberSignal.js +29 -49
- package/NumberSignal.js.map +1 -7
- package/ValueSignal.d.ts +0 -40
- package/ValueSignal.js +58 -105
- package/ValueSignal.js.map +1 -7
- package/core.d.ts +0 -1
- package/core.js +2 -2
- package/core.js.map +1 -7
- package/events.d.ts +0 -11
- package/events.js +72 -71
- package/events.js.map +1 -7
- package/index.d.ts +0 -1
- package/index.js +7 -13
- package/index.js.map +1 -7
- package/package.json +9 -34
- package/polyfills.d.ts +0 -1
- package/polyfills.js +15 -14
- package/polyfills.js.map +1 -7
- package/types.d.ts +4 -7
- package/types.js +2 -0
- package/types.js.map +1 -0
- package/utils.d.ts +0 -1
- package/utils.js +10 -13
- package/utils.js.map +1 -7
- package/CollectionSignal.d.ts.map +0 -1
- package/FullStackSignal.d.ts.map +0 -1
- package/ListSignal.d.ts.map +0 -1
- package/NumberSignal.d.ts.map +0 -1
- package/ValueSignal.d.ts.map +0 -1
- package/core.d.ts.map +0 -1
- package/events.d.ts.map +0 -1
- package/index.d.ts.map +0 -1
- package/polyfills.d.ts.map +0 -1
- package/utils.d.ts.map +0 -1
package/ListSignal.js
CHANGED
|
@@ -1,147 +1,121 @@
|
|
|
1
|
-
import { CollectionSignal } from
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
$resolveOperation,
|
|
13
|
-
$setValueQuietly,
|
|
14
|
-
$update
|
|
15
|
-
} from "./FullStackSignal.js";
|
|
16
|
-
import { ValueSignal } from "./ValueSignal.js";
|
|
17
|
-
class ListSignal extends CollectionSignal {
|
|
18
|
-
#head;
|
|
19
|
-
#tail;
|
|
20
|
-
#values = /* @__PURE__ */ new Map();
|
|
21
|
-
constructor(config) {
|
|
22
|
-
const initialValue = [];
|
|
23
|
-
super(initialValue, config);
|
|
24
|
-
}
|
|
25
|
-
#computeItems() {
|
|
26
|
-
let current = this.#head;
|
|
27
|
-
const result = [];
|
|
28
|
-
while (current !== void 0) {
|
|
29
|
-
const entry = this.#values.get(current);
|
|
30
|
-
result.push(entry.value);
|
|
31
|
-
current = entry.next;
|
|
1
|
+
import { CollectionSignal } from './CollectionSignal.js';
|
|
2
|
+
import { createInsertLastStateEvent, createRemoveStateEvent, isInsertLastStateEvent, isListSnapshotStateEvent, isRemoveStateEvent, } from './events.js';
|
|
3
|
+
import { $createOperation, $processServerResponse, $resolveOperation, $setValueQuietly, $update, } from './FullStackSignal.js';
|
|
4
|
+
import { ValueSignal } from './ValueSignal.js';
|
|
5
|
+
export class ListSignal extends CollectionSignal {
|
|
6
|
+
#head;
|
|
7
|
+
#tail;
|
|
8
|
+
#values = new Map();
|
|
9
|
+
constructor(config) {
|
|
10
|
+
const initialValue = [];
|
|
11
|
+
super(initialValue, config);
|
|
32
12
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
13
|
+
#computeItems() {
|
|
14
|
+
let current = this.#head;
|
|
15
|
+
const result = [];
|
|
16
|
+
while (current !== undefined) {
|
|
17
|
+
const entry = this.#values.get(current);
|
|
18
|
+
result.push(entry.value);
|
|
19
|
+
current = entry.next;
|
|
20
|
+
}
|
|
21
|
+
return result;
|
|
42
22
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
23
|
+
[$processServerResponse](event) {
|
|
24
|
+
if (!event.accepted) {
|
|
25
|
+
this[$resolveOperation](event.id, `Server rejected the operation with id '${event.id}'. See the server log for more details.`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (isListSnapshotStateEvent(event)) {
|
|
29
|
+
this.#handleSnapshotEvent(event);
|
|
30
|
+
}
|
|
31
|
+
else if (isInsertLastStateEvent(event)) {
|
|
32
|
+
this.#handleInsertLastUpdate(event);
|
|
33
|
+
}
|
|
34
|
+
else if (isRemoveStateEvent(event)) {
|
|
35
|
+
this.#handleRemoveUpdate(event);
|
|
36
|
+
}
|
|
37
|
+
this[$resolveOperation](event.id);
|
|
49
38
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
39
|
+
#handleInsertLastUpdate(event) {
|
|
40
|
+
if (event.entryId === undefined) {
|
|
41
|
+
throw new Error('Unexpected state: Entry id should be defined when insert last event is accepted');
|
|
42
|
+
}
|
|
43
|
+
const valueSignal = new ValueSignal(event.value, { ...this.server.config, parentClientSignalId: this.id }, event.entryId);
|
|
44
|
+
const newEntry = { id: valueSignal.id, value: valueSignal };
|
|
45
|
+
if (this.#head === undefined) {
|
|
46
|
+
this.#head = newEntry.id;
|
|
47
|
+
this.#tail = this.#head;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const tailEntry = this.#values.get(this.#tail);
|
|
51
|
+
tailEntry.next = newEntry.id;
|
|
52
|
+
newEntry.prev = this.#tail;
|
|
53
|
+
this.#tail = newEntry.id;
|
|
54
|
+
}
|
|
55
|
+
this.#values.set(valueSignal.id, newEntry);
|
|
56
|
+
this[$setValueQuietly](this.#computeItems());
|
|
55
57
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
#handleRemoveUpdate(event) {
|
|
59
|
+
const entryToRemove = this.#values.get(event.entryId);
|
|
60
|
+
if (entryToRemove === undefined) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
this.#values.delete(event.id);
|
|
64
|
+
if (this.#head === entryToRemove.id) {
|
|
65
|
+
if (entryToRemove.next === undefined) {
|
|
66
|
+
this.#head = undefined;
|
|
67
|
+
this.#tail = undefined;
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
const newHead = this.#values.get(entryToRemove.next);
|
|
71
|
+
this.#head = newHead.id;
|
|
72
|
+
newHead.prev = undefined;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const prevEntry = this.#values.get(entryToRemove.prev);
|
|
77
|
+
const nextEntry = entryToRemove.next !== undefined ? this.#values.get(entryToRemove.next) : undefined;
|
|
78
|
+
if (nextEntry === undefined) {
|
|
79
|
+
this.#tail = prevEntry.id;
|
|
80
|
+
prevEntry.next = undefined;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
prevEntry.next = nextEntry.id;
|
|
84
|
+
nextEntry.prev = prevEntry.id;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
this[$setValueQuietly](this.#computeItems());
|
|
70
88
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
#handleSnapshotEvent(event) {
|
|
90
|
+
event.entries.forEach((entry) => {
|
|
91
|
+
this.#values.set(entry.id, {
|
|
92
|
+
id: entry.id,
|
|
93
|
+
prev: entry.prev,
|
|
94
|
+
next: entry.next,
|
|
95
|
+
value: new ValueSignal(entry.value, { ...this.server.config, parentClientSignalId: this.id }, entry.id),
|
|
96
|
+
});
|
|
97
|
+
if (entry.prev === undefined) {
|
|
98
|
+
this.#head = entry.id;
|
|
99
|
+
}
|
|
100
|
+
if (entry.next === undefined) {
|
|
101
|
+
this.#tail = entry.id;
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
this[$setValueQuietly](this.#computeItems());
|
|
78
105
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
this
|
|
83
|
-
this.#tail = void 0;
|
|
84
|
-
} else {
|
|
85
|
-
const newHead = this.#values.get(entryToRemove.next);
|
|
86
|
-
this.#head = newHead.id;
|
|
87
|
-
newHead.prev = void 0;
|
|
88
|
-
}
|
|
89
|
-
} else {
|
|
90
|
-
const prevEntry = this.#values.get(entryToRemove.prev);
|
|
91
|
-
const nextEntry = entryToRemove.next !== void 0 ? this.#values.get(entryToRemove.next) : void 0;
|
|
92
|
-
if (nextEntry === void 0) {
|
|
93
|
-
this.#tail = prevEntry.id;
|
|
94
|
-
prevEntry.next = void 0;
|
|
95
|
-
} else {
|
|
96
|
-
prevEntry.next = nextEntry.id;
|
|
97
|
-
nextEntry.prev = prevEntry.id;
|
|
98
|
-
}
|
|
106
|
+
insertLast(value) {
|
|
107
|
+
const event = createInsertLastStateEvent(value);
|
|
108
|
+
const promise = this[$update](event);
|
|
109
|
+
return this[$createOperation]({ id: event.id, promise });
|
|
99
110
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
value: new ValueSignal(entry.value, { ...this.server.config, parentClientSignalId: this.id }, entry.id)
|
|
109
|
-
});
|
|
110
|
-
if (entry.prev === void 0) {
|
|
111
|
-
this.#head = entry.id;
|
|
112
|
-
}
|
|
113
|
-
if (entry.next === void 0) {
|
|
114
|
-
this.#tail = entry.id;
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
this[$setValueQuietly](this.#computeItems());
|
|
118
|
-
}
|
|
119
|
-
/**
|
|
120
|
-
* Inserts a new value at the end of the list.
|
|
121
|
-
* @param value - The value to insert.
|
|
122
|
-
* @returns An operation object that allows to perform additional actions.
|
|
123
|
-
*/
|
|
124
|
-
insertLast(value) {
|
|
125
|
-
const event = createInsertLastStateEvent(value);
|
|
126
|
-
const promise = this[$update](event);
|
|
127
|
-
return this[$createOperation]({ id: event.id, promise });
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Removes the given item from the list.
|
|
131
|
-
* @param item - The item to remove.
|
|
132
|
-
* @returns An operation object that allows to perform additional actions.
|
|
133
|
-
*/
|
|
134
|
-
remove(item) {
|
|
135
|
-
const entryToRemove = this.#values.get(item.id);
|
|
136
|
-
if (entryToRemove === void 0) {
|
|
137
|
-
return { result: Promise.resolve() };
|
|
111
|
+
remove(item) {
|
|
112
|
+
const entryToRemove = this.#values.get(item.id);
|
|
113
|
+
if (entryToRemove === undefined) {
|
|
114
|
+
return { result: Promise.resolve() };
|
|
115
|
+
}
|
|
116
|
+
const removeEvent = createRemoveStateEvent(entryToRemove.value.id);
|
|
117
|
+
const promise = this[$update](removeEvent);
|
|
118
|
+
return this[$createOperation]({ id: removeEvent.id, promise });
|
|
138
119
|
}
|
|
139
|
-
const removeEvent = createRemoveStateEvent(entryToRemove.value.id);
|
|
140
|
-
const promise = this[$update](removeEvent);
|
|
141
|
-
return this[$createOperation]({ id: removeEvent.id, promise });
|
|
142
|
-
}
|
|
143
120
|
}
|
|
144
|
-
|
|
145
|
-
ListSignal
|
|
146
|
-
};
|
|
147
|
-
//# sourceMappingURL=ListSignal.js.map
|
|
121
|
+
//# sourceMappingURL=ListSignal.js.map
|
package/ListSignal.js.map
CHANGED
|
@@ -1,7 +1 @@
|
|
|
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 {\n $createOperation,\n $processServerResponse,\n $resolveOperation,\n $setValueQuietly,\n $update,\n type Operation,\n type ServerConnectionConfig,\n} 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 this[$resolveOperation](\n event.id,\n `Server rejected the operation with id '${event.id}'. See the server log for more details.`,\n );\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 this[$resolveOperation](event.id);\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 * @returns An operation object that allows to perform additional actions.\n */\n insertLast(value: T): Operation {\n const event = createInsertLastStateEvent(value);\n const promise = this[$update](event);\n return this[$createOperation]({ id: event.id, promise });\n }\n\n /**\n * Removes the given item from the list.\n * @param item - The item to remove.\n * @returns An operation object that allows to perform additional actions.\n */\n remove(item: ValueSignal<T>): Operation {\n const entryToRemove = this.#values.get(item.id);\n if (entryToRemove === undefined) {\n return { result: Promise.resolve() };\n }\n const removeEvent = createRemoveStateEvent(entryToRemove.value.id);\n const promise = this[$update](removeEvent);\n return this[$createOperation]({ id: removeEvent.id, promise });\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
1
|
+
{"version":3,"file":"ListSignal.js","sourceRoot":"","sources":["src/ListSignal.ts"],"names":[],"mappings":"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","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 {\n $createOperation,\n $processServerResponse,\n $resolveOperation,\n $setValueQuietly,\n $update,\n type Operation,\n type ServerConnectionConfig,\n} 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 this[$resolveOperation](\n event.id,\n `Server rejected the operation with id '${event.id}'. See the server log for more details.`,\n );\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 this[$resolveOperation](event.id);\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 * @returns An operation object that allows to perform additional actions.\n */\n insertLast(value: T): Operation {\n const event = createInsertLastStateEvent(value);\n const promise = this[$update](event);\n return this[$createOperation]({ id: event.id, promise });\n }\n\n /**\n * Removes the given item from the list.\n * @param item - The item to remove.\n * @returns An operation object that allows to perform additional actions.\n */\n remove(item: ValueSignal<T>): Operation {\n const entryToRemove = this.#values.get(item.id);\n if (entryToRemove === undefined) {\n return { result: Promise.resolve() };\n }\n const removeEvent = createRemoveStateEvent(entryToRemove.value.id);\n const promise = this[$update](removeEvent);\n return this[$createOperation]({ id: removeEvent.id, promise });\n }\n}\n"]}
|
package/NumberSignal.d.ts
CHANGED
|
@@ -1,45 +1,8 @@
|
|
|
1
1
|
import { type StateEvent } from './events.js';
|
|
2
2
|
import { $processServerResponse, type Operation } from './FullStackSignal.js';
|
|
3
3
|
import { ValueSignal } from './ValueSignal.js';
|
|
4
|
-
/**
|
|
5
|
-
* A signal that holds a number value. The underlying
|
|
6
|
-
* value of this signal is stored and updated as a
|
|
7
|
-
* shared value on the server.
|
|
8
|
-
*
|
|
9
|
-
* After obtaining the NumberSignal instance from
|
|
10
|
-
* a server-side service that returns one, the value
|
|
11
|
-
* can be updated using the `value` property,
|
|
12
|
-
* and it can be read with or without the
|
|
13
|
-
* `value` property (similar to a normal signal):
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```tsx
|
|
17
|
-
* const counter = CounterService.counter();
|
|
18
|
-
*
|
|
19
|
-
* return (
|
|
20
|
-
* <Button onClick={() => counter.incrementBy(1)}>
|
|
21
|
-
* Click count: { counter }
|
|
22
|
-
* </Button>
|
|
23
|
-
* <Button onClick={() => counter.value = 0}>Reset</Button>
|
|
24
|
-
* );
|
|
25
|
-
* ```
|
|
26
|
-
*/
|
|
27
4
|
export declare class NumberSignal extends ValueSignal<number> {
|
|
28
5
|
#private;
|
|
29
|
-
/**
|
|
30
|
-
* Increments the value by the specified delta. The delta can be negative to
|
|
31
|
-
* decrease the value.
|
|
32
|
-
*
|
|
33
|
-
* This method differs from using the `++` or `+=` operators directly on the
|
|
34
|
-
* signal value. It performs an atomic operation to prevent conflicts from
|
|
35
|
-
* concurrent changes, ensuring that other users' modifications are not
|
|
36
|
-
* accidentally overwritten.
|
|
37
|
-
*
|
|
38
|
-
* @param delta - The delta to increment the value by. The delta can be
|
|
39
|
-
* negative.
|
|
40
|
-
* @returns An operation object that allows to perform additional actions.
|
|
41
|
-
*/
|
|
42
6
|
incrementBy(delta: number): Operation;
|
|
43
7
|
protected [$processServerResponse](event: StateEvent): void;
|
|
44
8
|
}
|
|
45
|
-
//# sourceMappingURL=NumberSignal.d.ts.map
|
package/NumberSignal.js
CHANGED
|
@@ -1,52 +1,32 @@
|
|
|
1
|
-
import { createIncrementStateEvent, isIncrementStateEvent } from
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
*
|
|
16
|
-
* This method differs from using the `++` or `+=` operators directly on the
|
|
17
|
-
* signal value. It performs an atomic operation to prevent conflicts from
|
|
18
|
-
* concurrent changes, ensuring that other users' modifications are not
|
|
19
|
-
* accidentally overwritten.
|
|
20
|
-
*
|
|
21
|
-
* @param delta - The delta to increment the value by. The delta can be
|
|
22
|
-
* negative.
|
|
23
|
-
* @returns An operation object that allows to perform additional actions.
|
|
24
|
-
*/
|
|
25
|
-
incrementBy(delta) {
|
|
26
|
-
if (delta === 0) {
|
|
27
|
-
return { result: Promise.resolve() };
|
|
1
|
+
import { createIncrementStateEvent, isIncrementStateEvent } from './events.js';
|
|
2
|
+
import { $createOperation, $processServerResponse, $resolveOperation, $setValueQuietly, $update, } from './FullStackSignal.js';
|
|
3
|
+
import { ValueSignal } from './ValueSignal.js';
|
|
4
|
+
export class NumberSignal extends ValueSignal {
|
|
5
|
+
#sentIncrementEvents = new Map();
|
|
6
|
+
incrementBy(delta) {
|
|
7
|
+
if (delta === 0) {
|
|
8
|
+
return { result: Promise.resolve() };
|
|
9
|
+
}
|
|
10
|
+
this[$setValueQuietly](this.value + delta);
|
|
11
|
+
const event = createIncrementStateEvent(delta);
|
|
12
|
+
this.#sentIncrementEvents.set(event.id, event);
|
|
13
|
+
const promise = this[$update](event);
|
|
14
|
+
return this[$createOperation]({ id: event.id, promise });
|
|
28
15
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this[$resolveOperation](event.id);
|
|
44
|
-
} else {
|
|
45
|
-
super[$processServerResponse](event);
|
|
16
|
+
[$processServerResponse](event) {
|
|
17
|
+
if (event.accepted && isIncrementStateEvent(event)) {
|
|
18
|
+
const sentEvent = this.#sentIncrementEvents.get(event.id);
|
|
19
|
+
if (sentEvent) {
|
|
20
|
+
this.#sentIncrementEvents.delete(event.id);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
this[$setValueQuietly](this.value + event.value);
|
|
24
|
+
}
|
|
25
|
+
this[$resolveOperation](event.id);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
super[$processServerResponse](event);
|
|
29
|
+
}
|
|
46
30
|
}
|
|
47
|
-
}
|
|
48
31
|
}
|
|
49
|
-
|
|
50
|
-
NumberSignal
|
|
51
|
-
};
|
|
52
|
-
//# sourceMappingURL=NumberSignal.js.map
|
|
32
|
+
//# sourceMappingURL=NumberSignal.js.map
|
package/NumberSignal.js.map
CHANGED
|
@@ -1,7 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["src/NumberSignal.ts"],
|
|
4
|
-
"sourcesContent": ["import { createIncrementStateEvent, isIncrementStateEvent, type StateEvent } from './events.js';\nimport {\n $createOperation,\n $processServerResponse,\n $resolveOperation,\n $setValueQuietly,\n $update,\n type Operation,\n} 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 * @returns An operation object that allows to perform additional actions.\n */\n incrementBy(delta: number): Operation {\n if (delta === 0) {\n return { result: Promise.resolve() };\n }\n this[$setValueQuietly](this.value + delta);\n const event = createIncrementStateEvent(delta);\n this.#sentIncrementEvents.set(event.id, event);\n const promise = this[$update](event);\n return this[$createOperation]({ id: event.id, promise });\n }\n\n protected override [$processServerResponse](event: StateEvent): void {\n if (event.accepted && isIncrementStateEvent(event)) {\n const sentEvent = this.#sentIncrementEvents.get(event.id);\n if (sentEvent) {\n this.#sentIncrementEvents.delete(event.id);\n } else {\n this[$setValueQuietly](this.value + event.value);\n }\n this[$resolveOperation](event.id);\n } else {\n super[$processServerResponse](event);\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA,SAAS,2BAA2B,6BAA8C;AAClF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,mBAAmB;AAyBrB,MAAM,qBAAqB,YAAoB;AAAA,EAC3C,uBAAuB,oBAAI,IAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAc5D,YAAY,OAA0B;AACpC,QAAI,UAAU,GAAG;AACf,aAAO,EAAE,QAAQ,QAAQ,QAAQ,EAAE;AAAA,IACrC;AACA,SAAK,gBAAgB,EAAE,KAAK,QAAQ,KAAK;AACzC,UAAM,QAAQ,0BAA0B,KAAK;AAC7C,SAAK,qBAAqB,IAAI,MAAM,IAAI,KAAK;AAC7C,UAAM,UAAU,KAAK,OAAO,EAAE,KAAK;AACnC,WAAO,KAAK,gBAAgB,EAAE,EAAE,IAAI,MAAM,IAAI,QAAQ,CAAC;AAAA,EACzD;AAAA,EAEA,CAAoB,sBAAsB,EAAE,OAAyB;AACnE,QAAI,MAAM,YAAY,sBAAsB,KAAK,GAAG;AAClD,YAAM,YAAY,KAAK,qBAAqB,IAAI,MAAM,EAAE;AACxD,UAAI,WAAW;AACb,aAAK,qBAAqB,OAAO,MAAM,EAAE;AAAA,MAC3C,OAAO;AACL,aAAK,gBAAgB,EAAE,KAAK,QAAQ,MAAM,KAAK;AAAA,MACjD;AACA,WAAK,iBAAiB,EAAE,MAAM,EAAE;AAAA,IAClC,OAAO;AACL,YAAM,sBAAsB,EAAE,KAAK;AAAA,IACrC;AAAA,EACF;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
1
|
+
{"version":3,"file":"NumberSignal.js","sourceRoot":"","sources":["src/NumberSignal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,qBAAqB,EAAmB,MAAM,aAAa,CAAC;AAChG,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,gBAAgB,EAChB,OAAO,GAER,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAyB/C,MAAM,OAAO,YAAa,SAAQ,WAAmB;IAC1C,oBAAoB,GAAG,IAAI,GAAG,EAAsB,CAAC;IAc9D,WAAW,CAAC,KAAa;QACvB,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACvC,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,yBAAyB,CAAC,KAAK,CAAC,CAAC;QAC/C,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;QACrC,OAAO,IAAI,CAAC,gBAAgB,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3D,CAAC;IAEkB,CAAC,sBAAsB,CAAC,CAAC,KAAiB;QAC3D,IAAI,KAAK,CAAC,QAAQ,IAAI,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC;YACnD,MAAM,SAAS,GAAG,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC1D,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;YACnD,CAAC;YACD,IAAI,CAAC,iBAAiB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,sBAAsB,CAAC,CAAC,KAAK,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;CACF","sourcesContent":["import { createIncrementStateEvent, isIncrementStateEvent, type StateEvent } from './events.js';\nimport {\n $createOperation,\n $processServerResponse,\n $resolveOperation,\n $setValueQuietly,\n $update,\n type Operation,\n} 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 * @returns An operation object that allows to perform additional actions.\n */\n incrementBy(delta: number): Operation {\n if (delta === 0) {\n return { result: Promise.resolve() };\n }\n this[$setValueQuietly](this.value + delta);\n const event = createIncrementStateEvent(delta);\n this.#sentIncrementEvents.set(event.id, event);\n const promise = this[$update](event);\n return this[$createOperation]({ id: event.id, promise });\n }\n\n protected override [$processServerResponse](event: StateEvent): void {\n if (event.accepted && isIncrementStateEvent(event)) {\n const sentEvent = this.#sentIncrementEvents.get(event.id);\n if (sentEvent) {\n this.#sentIncrementEvents.delete(event.id);\n } else {\n this[$setValueQuietly](this.value + event.value);\n }\n this[$resolveOperation](event.id);\n } else {\n super[$processServerResponse](event);\n }\n }\n}\n"]}
|
package/ValueSignal.d.ts
CHANGED
|
@@ -1,52 +1,12 @@
|
|
|
1
1
|
import { type StateEvent } from './events.js';
|
|
2
2
|
import { $processServerResponse, FullStackSignal, type Operation } from './FullStackSignal.js';
|
|
3
|
-
/**
|
|
4
|
-
* An operation subscription that can be canceled.
|
|
5
|
-
*/
|
|
6
3
|
export interface OperationSubscription extends Operation {
|
|
7
4
|
cancel(): void;
|
|
8
5
|
}
|
|
9
|
-
/**
|
|
10
|
-
* A full-stack signal that holds an arbitrary value.
|
|
11
|
-
*/
|
|
12
6
|
export declare class ValueSignal<T> extends FullStackSignal<T> {
|
|
13
7
|
#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
|
-
* @returns An operation object that allows to perform additional actions.
|
|
24
|
-
*/
|
|
25
8
|
set(value: T): Operation;
|
|
26
|
-
/**
|
|
27
|
-
* Replaces the value with a new one only if the current value is equal to the
|
|
28
|
-
* expected value.
|
|
29
|
-
*
|
|
30
|
-
* @param expected - The expected value.
|
|
31
|
-
* @param newValue - The new value.
|
|
32
|
-
* @returns An operation object that allows to perform additional actions.
|
|
33
|
-
*/
|
|
34
9
|
replace(expected: T, newValue: T): Operation;
|
|
35
|
-
/**
|
|
36
|
-
* Tries to update the value by applying the callback function to the current
|
|
37
|
-
* value. In case of a concurrent change, the callback is run again with an
|
|
38
|
-
* updated input value. This is repeated until the result can be applied
|
|
39
|
-
* without concurrent changes, or the operation is canceled.
|
|
40
|
-
*
|
|
41
|
-
* Note that there is no guarantee that cancel() will be effective always,
|
|
42
|
-
* since a succeeding operation might already be on its way to the server.
|
|
43
|
-
*
|
|
44
|
-
* @param callback - The function that is applied on the current value to
|
|
45
|
-
* produce the new value.
|
|
46
|
-
* @returns An operation object that allows to perform additional actions,
|
|
47
|
-
* including cancellation.
|
|
48
|
-
*/
|
|
49
10
|
update(callback: (value: T) => T): OperationSubscription;
|
|
50
11
|
protected [$processServerResponse](event: StateEvent): void;
|
|
51
12
|
}
|
|
52
|
-
//# sourceMappingURL=ValueSignal.d.ts.map
|