lunibee 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +3 -3
  2. package/dist/builders/attachment.d.ts +8 -4
  3. package/dist/builders/commands.d.ts +122 -149
  4. package/dist/builders/components.d.ts +10 -150
  5. package/dist/builders/embed.d.ts +66 -37
  6. package/dist/builders/index.d.ts +0 -4
  7. package/dist/builders/index.js +112 -221
  8. package/dist/builders/index.js.map +10 -9
  9. package/dist/collection/cache.d.ts +15 -6
  10. package/dist/collection/index.d.ts +92 -17
  11. package/dist/collection/index.js +471 -279
  12. package/dist/collection/index.js.map +4 -4
  13. package/dist/core/api.d.ts +130 -0
  14. package/dist/core/channel-create.d.ts +72 -0
  15. package/dist/core/commands.d.ts +232 -0
  16. package/dist/core/events.d.ts +24 -7
  17. package/dist/core/handles.d.ts +157 -0
  18. package/dist/core/index.d.ts +68 -4
  19. package/dist/core/index.js +2639 -493
  20. package/dist/core/index.js.map +47 -37
  21. package/dist/core/permissions.d.ts +7 -7
  22. package/dist/formatters/index.js +9 -5
  23. package/dist/formatters/index.js.map +3 -3
  24. package/dist/handlers/index.js +5 -10
  25. package/dist/handlers/index.js.map +3 -3
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.js +9527 -7809
  28. package/dist/index.js.map +63 -53
  29. package/dist/managers/advanced.d.ts +29 -1
  30. package/dist/managers/base.d.ts +22 -14
  31. package/dist/managers/emoji.d.ts +0 -2
  32. package/dist/managers/guild-resources.d.ts +13 -5
  33. package/dist/managers/guild.d.ts +40 -4
  34. package/dist/managers/index.d.ts +21 -13
  35. package/dist/managers/index.js +969 -386
  36. package/dist/managers/index.js.map +30 -25
  37. package/dist/managers/member.d.ts +9 -2
  38. package/dist/managers/message.d.ts +10 -15
  39. package/dist/managers/role.d.ts +18 -10
  40. package/dist/managers/thread.d.ts +68 -0
  41. package/dist/managers/user.d.ts +18 -0
  42. package/dist/rest/errors.d.ts +14 -7
  43. package/dist/rest/index.d.ts +24 -12
  44. package/dist/rest/index.js +70 -20
  45. package/dist/rest/index.js.map +9 -9
  46. package/dist/rest/redis.d.ts +1 -1
  47. package/dist/rest/route.d.ts +8 -4
  48. package/dist/rest/routes.d.ts +122 -48
  49. package/dist/rest/transport.d.ts +10 -5
  50. package/dist/rest/webhook.d.ts +9 -5
  51. package/dist/sharding/bus.d.ts +47 -19
  52. package/dist/sharding/cluster.d.ts +11 -0
  53. package/dist/sharding/gateway-bot.d.ts +18 -0
  54. package/dist/sharding/index.d.ts +64 -25
  55. package/dist/sharding/index.js +439 -124
  56. package/dist/sharding/index.js.map +19 -14
  57. package/dist/sharding/supervisor.d.ts +32 -0
  58. package/dist/sharding/transport.d.ts +45 -0
  59. package/dist/structures/audit-log.d.ts +99 -2
  60. package/dist/structures/base.d.ts +40 -23
  61. package/dist/structures/channels.d.ts +45 -14
  62. package/dist/structures/index.d.ts +8 -12
  63. package/dist/structures/index.js +280 -132
  64. package/dist/structures/index.js.map +21 -15
  65. package/dist/structures/interactions.d.ts +91 -63
  66. package/dist/structures/resources.d.ts +99 -52
  67. package/dist/structures/sticker.d.ts +33 -0
  68. package/dist/types/channel-kind.d.ts +26 -0
  69. package/dist/types/gateway.d.ts +24 -12
  70. package/dist/types/index.d.ts +115 -113
  71. package/dist/types/index.js +53 -48
  72. package/dist/types/index.js.map +6 -5
  73. package/dist/voice/index.d.ts +12 -4
  74. package/dist/voice/index.js +14 -18
  75. package/dist/voice/index.js.map +3 -3
  76. package/dist/ws/decoder.d.ts +6 -2
  77. package/dist/ws/dispatcher.d.ts +19 -0
  78. package/dist/ws/index.d.ts +61 -15
  79. package/dist/ws/index.js +194 -77
  80. package/dist/ws/index.js.map +13 -11
  81. package/dist/ws/protocol.d.ts +10 -2
  82. package/dist/ws/reconnect.d.ts +10 -0
  83. package/dist/ws/send-budget.d.ts +6 -2
  84. package/dist/ws/session.d.ts +14 -4
  85. package/dist/ws/state.d.ts +22 -11
  86. package/package.json +20 -16
@@ -1,64 +1,39 @@
1
1
  // @bun
2
2
  // packages/collection/src/cache.ts
3
3
  class Cache {
4
- #entries = new Collection;
5
- #maxSize;
6
- #ttl;
7
- #timer;
4
+ #entries;
8
5
  constructor(options = {}) {
9
- this.#maxSize = options.maxSize ?? Infinity;
10
- this.#ttl = options.ttl ?? 0;
11
- if (!Number.isInteger(this.#maxSize) && this.#maxSize !== Infinity || this.#maxSize < 1)
6
+ const maxSize = options.maxSize ?? Infinity;
7
+ const ttl = options.ttl ?? 0;
8
+ if (!Number.isInteger(maxSize) && maxSize !== Infinity || maxSize < 1)
12
9
  throw new RangeError("Cache maxSize must be a positive integer.");
13
- if (!Number.isFinite(this.#ttl) || this.#ttl < 0)
10
+ if (!Number.isFinite(ttl) || ttl < 0)
14
11
  throw new RangeError("Cache ttl must be a non-negative finite number.");
15
- if (this.#ttl > 0) {
16
- const interval = options.sweepInterval ?? Math.min(this.#ttl, 60000);
12
+ if (!((options.jitter ?? 0) >= 0 && (options.jitter ?? 0) <= 1))
13
+ throw new RangeError("Cache jitter must be between 0 and 1.");
14
+ if (ttl > 0) {
15
+ const interval = options.sweepInterval ?? Math.min(ttl, 60000);
17
16
  if (!Number.isFinite(interval) || interval < 1)
18
17
  throw new RangeError("Cache sweepInterval must be positive.");
19
- this.#timer = setInterval(() => this.sweep(), interval);
20
- this.#timer.unref?.();
21
18
  }
19
+ this.#entries = new Collection(null, {
20
+ ttl: ttl > 0 ? ttl : undefined,
21
+ jitter: options.jitter,
22
+ maxSize: maxSize === Infinity ? undefined : maxSize,
23
+ slide: false
24
+ });
22
25
  }
23
26
  get size() {
24
- this.#sweepExpired();
25
27
  return this.#entries.size;
26
28
  }
27
29
  get(key) {
28
- const entry = this.#entries.get(key);
29
- if (!entry)
30
- return;
31
- if (entry.expiresAt > 0 && entry.expiresAt <= Date.now()) {
32
- this.#entries.delete(key);
33
- return;
34
- }
35
- this.#entries.delete(key);
36
- this.#entries.set(key, entry);
37
- return entry.value;
30
+ return this.#entries.get(key);
38
31
  }
39
32
  has(key) {
40
- const entry = this.#entries.get(key);
41
- if (!entry)
42
- return false;
43
- if (entry.expiresAt > 0 && entry.expiresAt <= Date.now()) {
44
- this.#entries.delete(key);
45
- return false;
46
- }
47
- return true;
33
+ return this.#entries.has(key);
48
34
  }
49
35
  set(key, value) {
50
- const expiresAt = this.#ttl > 0 ? Date.now() + this.#ttl : 0;
51
- this.#entries.delete(key);
52
- this.#entries.set(key, { value, expiresAt });
53
- if (this.#maxSize !== Infinity) {
54
- while (this.#entries.size > this.#maxSize) {
55
- const oldest = this.#entries.firstKey();
56
- if (oldest !== undefined)
57
- this.#entries.delete(oldest);
58
- else
59
- break;
60
- }
61
- }
36
+ this.#entries.set(key, value);
62
37
  return this;
63
38
  }
64
39
  delete(key) {
@@ -68,95 +43,53 @@ class Cache {
68
43
  this.#entries.clear();
69
44
  }
70
45
  invalidate(predicate) {
71
- return this.#entries.sweep((entry, key) => predicate(entry.value, key));
46
+ return this.#entries.sweep(predicate);
72
47
  }
73
48
  sweep() {
74
- return this.#entries.sweep((entry) => this.#expired(entry));
75
- }
76
- dispose() {
77
- if (this.#timer)
78
- clearInterval(this.#timer);
79
- this.#timer = undefined;
49
+ return this.#entries.purge();
80
50
  }
51
+ dispose() {}
81
52
  values() {
82
- this.#sweepExpired();
83
- return this.#entries.map((entry) => entry.value);
53
+ return [...this.#entries.values()];
84
54
  }
85
55
  entries() {
86
- this.#sweepExpired();
87
- return this.#entries.map((entry, key) => [key, entry.value]);
88
- }
89
- #expired(entry) {
90
- return entry.expiresAt > 0 && entry.expiresAt <= Date.now();
91
- }
92
- #sweepExpired() {
93
- if (this.#ttl > 0)
94
- this.sweep();
56
+ return [...this.#entries.entries()];
95
57
  }
96
58
  }
97
59
 
98
60
  // packages/collection/src/index.ts
99
- class RecencyList {
100
- #nodes = new Map;
101
- #oldest;
102
- #newest;
103
- get size() {
104
- return this.#nodes.size;
105
- }
106
- oldest() {
107
- return this.#oldest?.key;
108
- }
109
- touch(key) {
110
- const node = this.#nodes.get(key);
111
- if (node)
112
- return this.#moveNewest(node);
113
- const created = { key, older: this.#newest };
114
- if (this.#newest)
115
- this.#newest.newer = created;
116
- else
117
- this.#oldest = created;
118
- this.#newest = created;
119
- this.#nodes.set(key, created);
120
- }
121
- promote(key) {
122
- const node = this.#nodes.get(key);
123
- if (node)
124
- this.#moveNewest(node);
125
- }
126
- delete(key) {
127
- const node = this.#nodes.get(key);
128
- if (!node)
129
- return false;
130
- this.#unlink(node);
131
- this.#nodes.delete(key);
132
- return true;
133
- }
134
- clear() {
135
- this.#nodes.clear();
136
- this.#oldest = this.#newest = undefined;
137
- }
138
- #unlink(node) {
139
- if (node.older)
140
- node.older.newer = node.newer;
141
- else
142
- this.#oldest = node.newer;
143
- if (node.newer)
144
- node.newer.older = node.older;
145
- else
146
- this.#newest = node.older;
147
- node.older = node.newer = undefined;
148
- }
149
- #moveNewest(node) {
150
- if (node === this.#newest)
151
- return;
152
- this.#unlink(node);
153
- node.older = this.#newest;
154
- if (this.#newest)
155
- this.#newest.newer = node;
156
- else
157
- this.#oldest = node;
158
- this.#newest = node;
159
- }
61
+ function newPolicy(options = {}) {
62
+ return {
63
+ ttl: options.ttl,
64
+ jitter: options.jitter ?? 0,
65
+ maxSize: options.maxSize,
66
+ slide: options.slide ?? true,
67
+ onEvict: options.onEvict,
68
+ expired: 0,
69
+ evicted: 0,
70
+ entries: new Map,
71
+ oldest: undefined,
72
+ newest: undefined,
73
+ lruSize: 0,
74
+ heap: [],
75
+ armedFor: Infinity
76
+ };
77
+ }
78
+ function peekOf(collection, key) {
79
+ return collection instanceof Map ? Map.prototype.get.call(collection, key) : collection.get(key);
80
+ }
81
+ var mapGet = Map.prototype.get;
82
+ var mapHas = Map.prototype.has;
83
+ function sample(items, amount) {
84
+ if (!Number.isInteger(amount) || amount < 0)
85
+ throw new RangeError("amount must be a non-negative integer.");
86
+ const count = Math.min(amount, items.length);
87
+ for (let i = 0;i < count; i++) {
88
+ const j = i + Math.floor(Math.random() * (items.length - i));
89
+ [items[i], items[j]] = [items[j], items[i]];
90
+ }
91
+ items.length = count;
92
+ return items;
160
93
  }
161
94
  function assertTTL(ttl) {
162
95
  if (ttl !== undefined && (!Number.isFinite(ttl) || ttl <= 0))
@@ -164,81 +97,95 @@ function assertTTL(ttl) {
164
97
  }
165
98
 
166
99
  class Collection extends Map {
167
- #ttl;
168
- #maxSize;
169
- #onEvict;
170
- #expiry = new Map;
171
- #lru = new RecencyList;
172
- #stats = {
173
- hits: 0,
174
- misses: 0,
175
- expired: 0,
176
- evicted: 0
177
- };
178
- #heap = [];
179
- #timer;
180
- #armedFor = Infinity;
100
+ #policy;
101
+ #hits = 0;
102
+ #misses = 0;
181
103
  constructor(entries, options = {}) {
182
104
  super();
183
105
  assertTTL(options.ttl);
106
+ if (options.jitter !== undefined && !(options.jitter >= 0 && options.jitter <= 1))
107
+ throw new RangeError("jitter must be between 0 and 1.");
184
108
  if (options.maxSize !== undefined && (!Number.isInteger(options.maxSize) || options.maxSize <= 0))
185
109
  throw new RangeError("maxSize must be a positive integer.");
186
- this.#ttl = options.ttl;
187
- this.#maxSize = options.maxSize;
188
- this.#onEvict = options.onEvict;
110
+ if (options.ttl !== undefined || options.jitter !== undefined || options.maxSize !== undefined || options.slide !== undefined || options.onEvict !== undefined)
111
+ this.#policy = newPolicy(options);
189
112
  if (entries)
190
113
  for (const [key, value] of entries)
191
114
  this.set(key, value);
192
115
  }
193
116
  set(key, value, ttl) {
117
+ if (ttl === undefined && this.#policy === undefined)
118
+ return super.set(key, value);
194
119
  assertTTL(ttl);
195
- const window = ttl ?? this.#ttl;
196
- if (this.#maxSize !== undefined) {
197
- this.#lru.touch(key);
198
- }
199
- super.set(key, value);
120
+ let window = ttl ?? this.#policy?.ttl;
121
+ const jitter = this.#policy?.jitter ?? 0;
122
+ if (window !== undefined && jitter > 0)
123
+ window = Math.max(1, window * (1 - jitter * Math.random()));
200
124
  if (window !== undefined)
201
- this.#schedule(key, window);
202
- else if (this.#expiry.size > 0)
203
- this.#expiry.delete(key);
125
+ this.#policy ??= newPolicy();
126
+ if (!this.#policy)
127
+ return super.set(key, value);
128
+ const before = super.size;
129
+ super.set(key, value);
130
+ this.#track(key, value, window, super.size !== before);
204
131
  this.#enforceCap();
205
132
  return this;
206
133
  }
207
134
  setWithoutTTL(key, value) {
208
- if (this.#expiry.size > 0)
209
- this.#expiry.delete(key);
210
- if (this.#lru.size > 0)
211
- this.#lru.delete(key);
135
+ if (this.#policy)
136
+ this.#forget(key);
212
137
  super.set(key, value);
213
138
  return this;
214
139
  }
215
140
  get(key) {
216
- if (this.#expireIfDue(key)) {
217
- this.#stats.misses++;
141
+ const policy = this.#policy;
142
+ const entry = policy?.entries.get(key);
143
+ if (entry === undefined) {
144
+ const value = mapGet.call(this, key);
145
+ if (value !== undefined || mapHas.call(this, key)) {
146
+ this.#hits++;
147
+ return value;
148
+ }
149
+ this.#misses++;
218
150
  return;
219
151
  }
220
- const value = super.get(key);
221
- if (value === undefined && !super.has(key)) {
222
- this.#stats.misses++;
152
+ const timed = entry.window !== 0;
153
+ const now = timed ? Date.now() : 0;
154
+ if (timed && entry.deadline <= now) {
155
+ this.#expire(entry);
156
+ this.#misses++;
223
157
  return;
224
158
  }
225
- this.#stats.hits++;
226
- const expiry = this.#expiry.get(key);
227
- if (expiry)
228
- expiry.deadline = Date.now() + expiry.window;
229
- this.#lru.promote(key);
230
- return value;
159
+ this.#hits++;
160
+ if (timed && policy.slide)
161
+ entry.deadline = now + entry.window;
162
+ if (entry.inLru)
163
+ this.#promote(policy, entry);
164
+ return entry.value;
165
+ }
166
+ ensure(key, factory, ttl) {
167
+ const existing = this.get(key);
168
+ if (existing !== undefined || super.has(key))
169
+ return existing;
170
+ const created = factory(key, this);
171
+ this.set(key, created, ttl);
172
+ return created;
231
173
  }
232
174
  peek(key) {
233
175
  if (!this.#expireIfDue(key) && super.has(key)) {
234
- this.#stats.hits++;
176
+ this.#hits++;
235
177
  return super.get(key);
236
178
  }
237
- this.#stats.misses++;
179
+ this.#misses++;
238
180
  return;
239
181
  }
240
182
  get stats() {
241
- return { ...this.#stats };
183
+ return {
184
+ hits: this.#hits,
185
+ misses: this.#misses,
186
+ expired: this.#policy?.expired ?? 0,
187
+ evicted: this.#policy?.evicted ?? 0
188
+ };
242
189
  }
243
190
  has(key) {
244
191
  return !this.#expireIfDue(key) && super.has(key);
@@ -246,19 +193,23 @@ class Collection extends Map {
246
193
  delete(key) {
247
194
  if (this.#expireIfDue(key))
248
195
  return false;
249
- this.#expiry.delete(key);
250
- this.#lru.delete(key);
196
+ if (this.#policy)
197
+ this.#forget(key);
251
198
  return super.delete(key);
252
199
  }
253
200
  clear() {
254
201
  super.clear();
255
- this.#expiry.clear();
256
- this.#lru.clear();
257
- this.#heap.length = 0;
258
- if (this.#timer)
259
- clearTimeout(this.#timer);
260
- this.#timer = undefined;
261
- this.#armedFor = Infinity;
202
+ const policy = this.#policy;
203
+ if (!policy)
204
+ return;
205
+ policy.entries.clear();
206
+ policy.oldest = policy.newest = undefined;
207
+ policy.lruSize = 0;
208
+ policy.heap.length = 0;
209
+ if (policy.timer)
210
+ clearTimeout(policy.timer);
211
+ policy.timer = undefined;
212
+ policy.armedFor = Infinity;
262
213
  }
263
214
  get size() {
264
215
  this.purge();
@@ -286,11 +237,11 @@ class Collection extends Map {
286
237
  ttlRemaining(key) {
287
238
  if (this.#expireIfDue(key))
288
239
  return;
289
- const expiry = this.#expiry.get(key);
290
- return expiry && Math.max(0, expiry.deadline - Date.now());
240
+ const entry = this.#policy?.entries.get(key);
241
+ return entry && entry.window !== 0 ? Math.max(0, entry.deadline - Date.now()) : undefined;
291
242
  }
292
243
  purge() {
293
- if (this.#expiry.size === 0)
244
+ if (!this.#policy || this.#policy.heap.length === 0)
294
245
  return 0;
295
246
  const now = Date.now();
296
247
  let removed = 0;
@@ -301,133 +252,255 @@ class Collection extends Map {
301
252
  this.#remove(due.key);
302
253
  removed++;
303
254
  }
304
- this.#stats.expired += removed;
255
+ this.#policy.expired += removed;
305
256
  this.#rearm();
306
257
  for (const [key, value] of dropped)
307
258
  this.#notify(key, value, "expired");
308
259
  return removed;
309
260
  }
310
261
  #expireIfDue(key) {
311
- const expiry = this.#expiry.get(key);
312
- if (!expiry || expiry.deadline > Date.now())
262
+ const entry = this.#policy?.entries.get(key);
263
+ if (!entry || entry.window === 0 || entry.deadline > Date.now())
313
264
  return false;
314
- const value = super.get(key);
315
- this.#remove(key);
316
- this.#stats.expired++;
317
- this.#notify(key, value, "expired");
265
+ this.#expire(entry);
318
266
  return true;
319
267
  }
268
+ #expire(entry) {
269
+ const value = super.get(entry.key);
270
+ this.#remove(entry.key);
271
+ this.#policy.expired++;
272
+ this.#notify(entry.key, value, "expired");
273
+ }
320
274
  #remove(key) {
321
- this.#expiry.delete(key);
322
- this.#lru.delete(key);
275
+ this.#forget(key);
323
276
  super.delete(key);
324
277
  }
278
+ #forget(key) {
279
+ const policy = this.#policy;
280
+ const entry = policy.entries.get(key);
281
+ if (!entry)
282
+ return;
283
+ if (entry.inLru)
284
+ this.#unlink(policy, entry);
285
+ policy.entries.delete(key);
286
+ }
287
+ #track(key, value, window, isNew) {
288
+ const policy = this.#policy;
289
+ const lru = policy.maxSize !== undefined;
290
+ let entry = isNew ? undefined : policy.entries.get(key);
291
+ if (entry)
292
+ entry.value = value;
293
+ if (window === undefined) {
294
+ if (entry && entry.window !== 0) {
295
+ if (lru)
296
+ entry = this.#replace(policy, entry, 0, 0);
297
+ else {
298
+ this.#forget(key);
299
+ return;
300
+ }
301
+ }
302
+ if (!entry && lru) {
303
+ entry = this.#create(key, value, 0, 0);
304
+ policy.entries.set(key, entry);
305
+ }
306
+ if (entry)
307
+ this.#promote(policy, entry);
308
+ return;
309
+ }
310
+ const deadline = Date.now() + window;
311
+ if (entry && entry.window !== 0) {
312
+ entry.deadline = deadline;
313
+ entry.window = window;
314
+ if (deadline < entry.filed)
315
+ entry = this.#replace(policy, entry, window, deadline);
316
+ else {
317
+ if (entry.inLru)
318
+ this.#promote(policy, entry);
319
+ return;
320
+ }
321
+ } else if (entry) {
322
+ entry.window = window;
323
+ entry.deadline = entry.filed = deadline;
324
+ } else {
325
+ entry = this.#create(key, value, window, deadline);
326
+ policy.entries.set(key, entry);
327
+ }
328
+ if (lru)
329
+ this.#promote(policy, entry);
330
+ this.#heapPush(entry);
331
+ if (policy.heap.length > 2 * policy.entries.size + 8)
332
+ this.#compact();
333
+ if (policy.timer === undefined || deadline < policy.armedFor)
334
+ this.#rearm();
335
+ }
336
+ #create(key, value, window, deadline) {
337
+ return {
338
+ key,
339
+ value,
340
+ older: undefined,
341
+ newer: undefined,
342
+ inLru: false,
343
+ window,
344
+ deadline,
345
+ filed: deadline
346
+ };
347
+ }
348
+ #replace(policy, old, window, deadline) {
349
+ const entry = {
350
+ ...old,
351
+ window,
352
+ deadline,
353
+ filed: deadline
354
+ };
355
+ if (old.inLru) {
356
+ if (old.older)
357
+ old.older.newer = entry;
358
+ else
359
+ policy.oldest = entry;
360
+ if (old.newer)
361
+ old.newer.older = entry;
362
+ else
363
+ policy.newest = entry;
364
+ }
365
+ policy.entries.set(old.key, entry);
366
+ return entry;
367
+ }
368
+ #promote(policy, entry) {
369
+ if (entry === policy.newest)
370
+ return;
371
+ if (entry.inLru)
372
+ this.#unlink(policy, entry, true);
373
+ else {
374
+ entry.inLru = true;
375
+ policy.lruSize++;
376
+ }
377
+ entry.older = policy.newest;
378
+ entry.newer = undefined;
379
+ if (policy.newest)
380
+ policy.newest.newer = entry;
381
+ else
382
+ policy.oldest = entry;
383
+ policy.newest = entry;
384
+ }
385
+ #unlink(policy, entry, keepCount = false) {
386
+ if (entry.older)
387
+ entry.older.newer = entry.newer;
388
+ else
389
+ policy.oldest = entry.newer;
390
+ if (entry.newer)
391
+ entry.newer.older = entry.older;
392
+ else
393
+ policy.newest = entry.older;
394
+ entry.older = entry.newer = undefined;
395
+ if (!keepCount) {
396
+ entry.inLru = false;
397
+ policy.lruSize--;
398
+ }
399
+ }
400
+ #derived() {
401
+ const Species = this.constructor[Symbol.species] ?? Collection;
402
+ return new Species;
403
+ }
325
404
  #notify(key, value, reason) {
326
- if (!this.#onEvict)
405
+ const onEvict = this.#policy?.onEvict;
406
+ if (!onEvict)
327
407
  return;
328
408
  try {
329
- this.#onEvict(key, value, reason);
409
+ onEvict(key, value, reason);
330
410
  } catch {}
331
411
  }
332
412
  #enforceCap() {
333
- if (this.#maxSize === undefined)
413
+ const policy = this.#policy;
414
+ const max = policy.maxSize;
415
+ if (max === undefined)
334
416
  return;
335
- while (this.#lru.size > this.#maxSize) {
336
- const oldest = this.#lru.oldest();
337
- const value = super.get(oldest);
338
- this.#remove(oldest);
339
- this.#stats.evicted++;
340
- this.#notify(oldest, value, "evicted");
417
+ if (policy.lruSize > max && policy.heap.length > 0 && policy.heap[0].filed <= Date.now())
418
+ this.purge();
419
+ while (policy.lruSize > max) {
420
+ const oldest = policy.oldest;
421
+ const value = policy.onEvict ? super.get(oldest.key) : undefined;
422
+ this.#unlink(policy, oldest);
423
+ policy.entries.delete(oldest.key);
424
+ super.delete(oldest.key);
425
+ policy.evicted++;
426
+ this.#notify(oldest.key, value, "evicted");
341
427
  }
342
428
  }
343
- #schedule(key, window) {
344
- const deadline = Date.now() + window;
345
- const expiry = this.#expiry.get(key);
346
- if (expiry) {
347
- expiry.deadline = deadline;
348
- expiry.window = window;
349
- if (deadline >= expiry.filed)
350
- return;
351
- expiry.filed = deadline;
352
- } else
353
- this.#expiry.set(key, { deadline, window, filed: deadline });
354
- this.#heapPush({ key, deadline });
355
- if (this.#heap.length > 2 * this.#expiry.size + 8)
356
- this.#compact();
357
- this.#rearm();
358
- }
359
429
  #nextDeadline() {
430
+ const policy = this.#policy;
360
431
  for (;; ) {
361
- const top = this.#heap[0];
432
+ const top = policy.heap[0];
362
433
  if (top === undefined)
363
434
  return Infinity;
364
- const expiry = this.#expiry.get(top.key);
365
- if (!expiry || expiry.filed !== top.deadline) {
435
+ if (policy.entries.get(top.key) !== top) {
366
436
  this.#heapPop();
367
437
  continue;
368
438
  }
369
- if (expiry.deadline === top.deadline)
370
- return top.deadline;
439
+ if (top.deadline === top.filed)
440
+ return top.filed;
371
441
  this.#heapPop();
372
- expiry.filed = expiry.deadline;
373
- this.#heapPush({ key: top.key, deadline: expiry.deadline });
442
+ top.filed = top.deadline;
443
+ this.#heapPush(top);
374
444
  }
375
445
  }
376
446
  #compact() {
377
- const live = this.#heap.filter((record) => this.#expiry.get(record.key)?.filed === record.deadline);
378
- this.#heap.length = 0;
379
- for (const record of live)
380
- this.#heapPush(record);
447
+ const policy = this.#policy;
448
+ const live = policy.heap.filter((entry) => policy.entries.get(entry.key) === entry);
449
+ policy.heap.length = 0;
450
+ for (const entry of live)
451
+ this.#heapPush(entry);
381
452
  }
382
453
  #rearm() {
454
+ const p = this.#policy;
383
455
  const deadline = this.#nextDeadline();
384
- if (this.#timer !== undefined && deadline >= this.#armedFor)
456
+ if (p.timer !== undefined && deadline >= p.armedFor)
385
457
  return;
386
- if (this.#timer)
387
- clearTimeout(this.#timer);
388
- this.#timer = undefined;
389
- this.#armedFor = deadline;
458
+ if (p.timer)
459
+ clearTimeout(p.timer);
460
+ p.timer = undefined;
461
+ p.armedFor = deadline;
390
462
  if (deadline === Infinity)
391
463
  return;
392
- this.#timer = setTimeout(() => {
393
- this.#timer = undefined;
394
- this.#armedFor = Infinity;
464
+ p.timer = setTimeout(() => {
465
+ p.timer = undefined;
466
+ p.armedFor = Infinity;
395
467
  this.purge();
396
468
  }, Math.max(0, deadline - Date.now()));
397
- this.#timer.unref?.();
469
+ p.timer.unref?.();
398
470
  }
399
- #heapPush(record) {
400
- const heap = this.#heap;
401
- heap.push(record);
402
- let i = heap.length - 1;
471
+ #heapPush(entry) {
472
+ const heap = this.#policy.heap;
473
+ let i = heap.length;
474
+ heap.push(entry);
403
475
  while (i > 0) {
404
476
  const parent = i - 1 >> 1;
405
- if (heap[parent].deadline <= heap[i].deadline)
477
+ const above = heap[parent];
478
+ if (above.filed <= entry.filed)
406
479
  break;
407
- [heap[parent], heap[i]] = [heap[i], heap[parent]];
480
+ heap[i] = above;
408
481
  i = parent;
409
482
  }
483
+ heap[i] = entry;
410
484
  }
411
485
  #heapPop() {
412
- const heap = this.#heap;
486
+ const heap = this.#policy.heap;
413
487
  const top = heap[0];
414
488
  const last = heap.pop();
415
489
  if (top === undefined || heap.length === 0)
416
490
  return top;
417
- heap[0] = last;
418
- for (let i = 0;; ) {
419
- const left = 2 * i + 1;
420
- const right = left + 1;
421
- let smallest = i;
422
- if (left < heap.length && heap[left].deadline < heap[smallest].deadline)
423
- smallest = left;
424
- if (right < heap.length && heap[right].deadline < heap[smallest].deadline)
425
- smallest = right;
426
- if (smallest === i)
491
+ let i = 0;
492
+ for (;; ) {
493
+ let child = 2 * i + 1;
494
+ if (child >= heap.length)
427
495
  break;
428
- [heap[smallest], heap[i]] = [heap[i], heap[smallest]];
429
- i = smallest;
496
+ if (child + 1 < heap.length && heap[child + 1].filed < heap[child].filed)
497
+ child++;
498
+ if (heap[child].filed >= last.filed)
499
+ break;
500
+ heap[i] = heap[child];
501
+ i = child;
430
502
  }
503
+ heap[i] = last;
431
504
  return top;
432
505
  }
433
506
  first() {
@@ -459,19 +532,35 @@ class Collection extends Map {
459
532
  }
460
533
  return;
461
534
  }
535
+ findLast(predicate) {
536
+ const keys = this.keyArray();
537
+ const values = this.array();
538
+ for (let i = values.length - 1;i >= 0; i--)
539
+ if (predicate(values[i], keys[i], this))
540
+ return values[i];
541
+ return;
542
+ }
543
+ findLastKey(predicate) {
544
+ const keys = this.keyArray();
545
+ const values = this.array();
546
+ for (let i = values.length - 1;i >= 0; i--)
547
+ if (predicate(values[i], keys[i], this))
548
+ return keys[i];
549
+ return;
550
+ }
462
551
  filter(predicate) {
463
- const result = new Collection;
464
- for (const [key, value] of this) {
552
+ const result = this.#derived();
553
+ this.forEach((value, key) => {
465
554
  if (predicate(value, key, this))
466
555
  result.set(key, value);
467
- }
556
+ });
468
557
  return result;
469
558
  }
470
559
  map(transform) {
471
560
  const result = [];
472
- for (const [key, value] of this) {
561
+ this.forEach((value, key) => {
473
562
  result.push(transform(value, key, this));
474
- }
563
+ });
475
564
  return result;
476
565
  }
477
566
  some(predicate) {
@@ -489,9 +578,9 @@ class Collection extends Map {
489
578
  return true;
490
579
  }
491
580
  each(callback) {
492
- for (const [key, value] of this) {
581
+ this.forEach((value, key) => {
493
582
  callback(value, key, this);
494
- }
583
+ });
495
584
  return this;
496
585
  }
497
586
  array() {
@@ -504,9 +593,10 @@ class Collection extends Map {
504
593
  return [...this.entries()];
505
594
  }
506
595
  clone() {
507
- const copy = new Collection;
508
- for (const [key, value] of this)
596
+ const copy = this.#derived();
597
+ this.forEach((value, key) => {
509
598
  copy.set(key, value);
599
+ });
510
600
  return copy;
511
601
  }
512
602
  hasAll(...keys) {
@@ -559,7 +649,7 @@ class Collection extends Map {
559
649
  return result;
560
650
  }
561
651
  intersection(other) {
562
- const result = new Collection;
652
+ const result = this.#derived();
563
653
  for (const [key, value] of this) {
564
654
  if (other.has(key))
565
655
  result.set(key, value);
@@ -567,7 +657,7 @@ class Collection extends Map {
567
657
  return result;
568
658
  }
569
659
  difference(other) {
570
- const result = new Collection;
660
+ const result = this.#derived();
571
661
  for (const [key, value] of this) {
572
662
  if (!other.has(key))
573
663
  result.set(key, value);
@@ -575,8 +665,8 @@ class Collection extends Map {
575
665
  return result;
576
666
  }
577
667
  partition(predicate) {
578
- const pass = new Collection;
579
- const fail = new Collection;
668
+ const pass = this.#derived();
669
+ const fail = this.#derived();
580
670
  for (const [key, value] of this) {
581
671
  if (predicate(value, key, this))
582
672
  pass.set(key, value);
@@ -585,6 +675,81 @@ class Collection extends Map {
585
675
  }
586
676
  return [pass, fail];
587
677
  }
678
+ symmetricDifference(other) {
679
+ const result = this.#derived();
680
+ for (const [key, value] of this)
681
+ if (!other.has(key))
682
+ result.set(key, value);
683
+ for (const [key, value] of other)
684
+ if (!this.has(key))
685
+ result.set(key, value);
686
+ return result;
687
+ }
688
+ concat(...collections) {
689
+ const result = this.clone();
690
+ for (const collection of collections)
691
+ for (const [key, value] of collection)
692
+ result.set(key, value);
693
+ return result;
694
+ }
695
+ merge(other, whenInSelf, whenInOther, whenInBoth) {
696
+ const result = this.#derived();
697
+ const decide = (key, kept) => {
698
+ if (kept.keep)
699
+ result.set(key, kept.value);
700
+ };
701
+ for (const [key, value] of this) {
702
+ decide(key, other.has(key) ? whenInBoth(value, peekOf(other, key), key) : whenInSelf(value, key));
703
+ }
704
+ for (const [key, value] of other)
705
+ if (!this.has(key))
706
+ decide(key, whenInOther(value, key));
707
+ return result;
708
+ }
709
+ equals(other) {
710
+ if (!other)
711
+ return false;
712
+ if (other === this)
713
+ return true;
714
+ if (this.size !== other.size)
715
+ return false;
716
+ for (const [key, value] of this) {
717
+ if (!other.has(key))
718
+ return false;
719
+ if (value !== peekOf(other, key))
720
+ return false;
721
+ }
722
+ return true;
723
+ }
724
+ mapValues(transform) {
725
+ const result = this.#derived();
726
+ for (const [key, value] of this)
727
+ result.set(key, transform(value, key, this));
728
+ return result;
729
+ }
730
+ toReversed() {
731
+ const result = this.#derived();
732
+ const entries = [...this];
733
+ for (let i = entries.length - 1;i >= 0; i--)
734
+ result.set(entries[i][0], entries[i][1]);
735
+ return result;
736
+ }
737
+ toSorted(comparator) {
738
+ return this.sorted(comparator);
739
+ }
740
+ static groupBy(items, keySelector) {
741
+ const groups = new Collection;
742
+ let index = 0;
743
+ for (const item of items)
744
+ groups.ensure(keySelector(item, index++), () => []).push(item);
745
+ return groups;
746
+ }
747
+ static combineEntries(entries, combine) {
748
+ const result = new Collection;
749
+ for (const [key, value] of entries)
750
+ result.set(key, result.has(key) ? combine(result.get(key), value, key) : value);
751
+ return result;
752
+ }
588
753
  tap(fn) {
589
754
  fn(this);
590
755
  return this;
@@ -603,6 +768,13 @@ class Collection extends Map {
603
768
  acc = fn(acc, value, key, this);
604
769
  return acc;
605
770
  }
771
+ reduceRight(fn, initialValue) {
772
+ const entries = [...this];
773
+ let acc = initialValue;
774
+ for (let i = entries.length - 1;i >= 0; i--)
775
+ acc = fn(acc, entries[i][1], entries[i][0], this);
776
+ return acc;
777
+ }
606
778
  sorted(comparator) {
607
779
  const decorated = [...this.entries()].map((entry, index) => ({
608
780
  entry,
@@ -613,32 +785,52 @@ class Collection extends Map {
613
785
  const comparison = comparator(x.entry[1], y.entry[1], x.entry[0], y.entry[0]);
614
786
  return comparison !== 0 ? comparison : x.index - y.index;
615
787
  });
616
- const result = new Collection;
788
+ const result = this.#derived();
617
789
  for (const { entry } of decorated)
618
790
  result.set(entry[0], entry[1]);
619
791
  return result;
620
792
  }
621
- random() {
622
- const arr = [...this.values()];
623
- if (!arr.length)
793
+ random(amount) {
794
+ if (amount !== undefined)
795
+ return sample([...this.values()], amount);
796
+ const size = this.size;
797
+ if (size === 0)
624
798
  return;
625
- return arr[Math.floor(Math.random() * arr.length)];
799
+ let index = Math.floor(Math.random() * size);
800
+ for (const value of this.values())
801
+ if (index-- === 0)
802
+ return value;
803
+ return;
626
804
  }
627
- randomKey() {
628
- const arr = [...this.keys()];
629
- if (!arr.length)
805
+ randomKey(amount) {
806
+ if (amount !== undefined)
807
+ return sample([...this.keys()], amount);
808
+ const size = this.size;
809
+ if (size === 0)
630
810
  return;
631
- return arr[Math.floor(Math.random() * arr.length)];
811
+ let index = Math.floor(Math.random() * size);
812
+ for (const key of this.keys())
813
+ if (index-- === 0)
814
+ return key;
815
+ return;
632
816
  }
633
817
  at(index) {
634
- const arr = [...this.values()];
635
- const i = index < 0 ? arr.length + index : index;
636
- return arr[i];
818
+ let remaining = index < 0 ? this.size + index : index;
819
+ if (remaining < 0)
820
+ return;
821
+ for (const value of this.values())
822
+ if (remaining-- === 0)
823
+ return value;
824
+ return;
637
825
  }
638
826
  keyAt(index) {
639
- const arr = [...this.keys()];
640
- const i = index < 0 ? arr.length + index : index;
641
- return arr[i];
827
+ let remaining = index < 0 ? this.size + index : index;
828
+ if (remaining < 0)
829
+ return;
830
+ for (const key of this.keys())
831
+ if (remaining-- === 0)
832
+ return key;
833
+ return;
642
834
  }
643
835
  toJSON() {
644
836
  return [...this.entries()];
@@ -649,4 +841,4 @@ export {
649
841
  Collection
650
842
  };
651
843
 
652
- //# debugId=4109A00736CB03B064756E2164756E21
844
+ //# debugId=44902D8F098C340964756E2164756E21