@procedures/collections 0.27.8 → 0.28.1
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/CHANGELOG.md +22 -0
- package/README.md +3 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/async/async-queue.d.ts +4 -0
- package/dist/async/async-queue.d.ts.map +1 -1
- package/dist/async/async-queue.js +36 -4
- package/dist/async/async-queue.js.map +1 -1
- package/dist/async/channels.d.ts.map +1 -1
- package/dist/async/channels.js +26 -10
- package/dist/async/channels.js.map +1 -1
- package/dist/behaviors/bounded.d.ts.map +1 -1
- package/dist/behaviors/bounded.js +20 -11
- package/dist/behaviors/bounded.js.map +1 -1
- package/dist/behaviors/readonly.d.ts.map +1 -1
- package/dist/behaviors/readonly.js +14 -2
- package/dist/behaviors/readonly.js.map +1 -1
- package/package.json +2 -2
- package/src/async/async-queue.ts +38 -4
- package/src/async/channels.ts +22 -10
- package/src/behaviors/bounded.ts +19 -11
- package/src/behaviors/readonly.ts +14 -2
package/src/behaviors/bounded.ts
CHANGED
|
@@ -69,7 +69,8 @@ export function boundedList<T>(
|
|
|
69
69
|
const { capacity, policy = "throw", onOverflow } = options;
|
|
70
70
|
|
|
71
71
|
return (next: List<T>): List<T> & BoundedCollection => {
|
|
72
|
-
|
|
72
|
+
// An unshift puts the element at the front, so drop-oldest drops the last element for it
|
|
73
|
+
const handleOverflow = (element: T, dropFrom: "first" | "last" = "first"): void => {
|
|
73
74
|
// Call overflow handler if provided
|
|
74
75
|
if (onOverflow) {
|
|
75
76
|
const context: OverflowContext<T> = {
|
|
@@ -87,9 +88,10 @@ export function boundedList<T>(
|
|
|
87
88
|
throw new Error(`List is full (capacity: ${capacity})`);
|
|
88
89
|
|
|
89
90
|
case "drop-oldest":
|
|
90
|
-
// Remove first
|
|
91
|
+
// Remove one element: the first, or the last for an unshift
|
|
91
92
|
if (!next.isEmpty) {
|
|
92
|
-
next.
|
|
93
|
+
if (dropFrom === "last") next.pop();
|
|
94
|
+
else next.shift();
|
|
93
95
|
}
|
|
94
96
|
break;
|
|
95
97
|
|
|
@@ -113,6 +115,9 @@ export function boundedList<T>(
|
|
|
113
115
|
}
|
|
114
116
|
};
|
|
115
117
|
|
|
118
|
+
// After drop-oldest removed the first element, an index of the caller is one place lower
|
|
119
|
+
const shifted = (index: number): number => (policy === "drop-oldest" ? Math.max(0, index - 1) : index);
|
|
120
|
+
|
|
116
121
|
return new Proxy(next, {
|
|
117
122
|
get(target, prop, receiver) {
|
|
118
123
|
// Capacity property
|
|
@@ -160,27 +165,26 @@ export function boundedList<T>(
|
|
|
160
165
|
case "unshift":
|
|
161
166
|
return function (element: T): void {
|
|
162
167
|
if (target.size >= capacity && policy !== "grow") {
|
|
163
|
-
|
|
168
|
+
// One element goes: the last one (before, the first and the last went: audit pass 2)
|
|
169
|
+
handleOverflow(element, "last");
|
|
164
170
|
if (policy === "reject" || policy === "drop-newest") {
|
|
165
171
|
return;
|
|
166
172
|
}
|
|
167
|
-
// For unshift with drop-oldest, we should drop from the end
|
|
168
|
-
if (policy === "drop-oldest" && !target.isEmpty) {
|
|
169
|
-
target.pop();
|
|
170
|
-
}
|
|
171
173
|
}
|
|
172
174
|
(value as Function).call(target, element);
|
|
173
175
|
};
|
|
174
176
|
|
|
175
177
|
case "insert":
|
|
176
178
|
return function (index: number, element: T): void {
|
|
179
|
+
let at = index;
|
|
177
180
|
if (target.size >= capacity && policy !== "grow") {
|
|
178
181
|
handleOverflow(element);
|
|
179
182
|
if (policy === "reject" || policy === "drop-newest") {
|
|
180
183
|
return;
|
|
181
184
|
}
|
|
185
|
+
at = shifted(at);
|
|
182
186
|
}
|
|
183
|
-
(value as Function).call(target,
|
|
187
|
+
(value as Function).call(target, at, element);
|
|
184
188
|
};
|
|
185
189
|
|
|
186
190
|
case "addAll":
|
|
@@ -191,19 +195,23 @@ export function boundedList<T>(
|
|
|
191
195
|
|
|
192
196
|
// Check if adding all elements would exceed capacity
|
|
193
197
|
if (target.size + elementsArray.length > capacity && policy !== "grow") {
|
|
194
|
-
// Add elements one by one with overflow handling
|
|
198
|
+
// Add elements one by one with overflow handling. An insert keeps the order of
|
|
199
|
+
// the elements, at an index that follows each drop.
|
|
195
200
|
let added = false;
|
|
201
|
+
let at = args[0] as number;
|
|
196
202
|
for (const element of elementsArray) {
|
|
197
203
|
if (target.size >= capacity) {
|
|
198
204
|
handleOverflow(element);
|
|
199
205
|
if (policy === "reject" || policy === "drop-newest") {
|
|
200
206
|
break;
|
|
201
207
|
}
|
|
208
|
+
at = shifted(at);
|
|
202
209
|
}
|
|
203
210
|
if (prop === "addAll") {
|
|
204
211
|
target.add(element);
|
|
205
212
|
} else {
|
|
206
|
-
target.insert(
|
|
213
|
+
target.insert(at, element);
|
|
214
|
+
at++;
|
|
207
215
|
}
|
|
208
216
|
added = true;
|
|
209
217
|
}
|
|
@@ -107,9 +107,13 @@ function createReadonly<C extends object>(
|
|
|
107
107
|
errorMessage: string = "Cannot modify readonly collection"
|
|
108
108
|
): Middleware<C> {
|
|
109
109
|
return (next: C): C => {
|
|
110
|
+
// The read methods, bound to the collection: one function for each name
|
|
111
|
+
const bound = new Map<PropertyKey, { value: Function; method: Function }>();
|
|
110
112
|
return new Proxy(next, {
|
|
111
|
-
get(target, prop
|
|
112
|
-
|
|
113
|
+
get(target, prop) {
|
|
114
|
+
// A read runs on the collection itself, not on the proxy: a read that changes internal
|
|
115
|
+
// state (a LinkedHashMap with accessOrder moves the entry) does not hit the set trap
|
|
116
|
+
const value = Reflect.get(target, prop, target);
|
|
113
117
|
|
|
114
118
|
// Block mutating methods
|
|
115
119
|
if (typeof prop === "string" && MUTATING_METHODS.has(prop)) {
|
|
@@ -131,6 +135,14 @@ function createReadonly<C extends object>(
|
|
|
131
135
|
};
|
|
132
136
|
}
|
|
133
137
|
|
|
138
|
+
if (typeof value === "function") {
|
|
139
|
+
const known = bound.get(prop);
|
|
140
|
+
if (known !== undefined && known.value === value) return known.method;
|
|
141
|
+
const method = (value as Function).bind(target) as Function;
|
|
142
|
+
bound.set(prop, { value, method });
|
|
143
|
+
return method;
|
|
144
|
+
}
|
|
145
|
+
|
|
134
146
|
return value;
|
|
135
147
|
},
|
|
136
148
|
|