@muloka/figma-console-mcp 0.4.0 → 0.5.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.
Files changed (109) hide show
  1. package/README.md +7 -3
  2. package/dist/core/websocket-connector.d.ts +2 -1
  3. package/dist/core/websocket-connector.d.ts.map +1 -1
  4. package/dist/core/websocket-connector.js +17 -2
  5. package/dist/core/websocket-connector.js.map +1 -1
  6. package/dist/core/websocket-server.d.ts +8 -1
  7. package/dist/core/websocket-server.d.ts.map +1 -1
  8. package/dist/core/websocket-server.js +19 -3
  9. package/dist/core/websocket-server.js.map +1 -1
  10. package/dist/core/write-tools.d.ts +3 -1
  11. package/dist/core/write-tools.d.ts.map +1 -1
  12. package/dist/core/write-tools.js +98 -23
  13. package/dist/core/write-tools.js.map +1 -1
  14. package/dist/local.d.ts +2 -1
  15. package/dist/local.d.ts.map +1 -1
  16. package/dist/local.js +5 -4
  17. package/dist/local.js.map +1 -1
  18. package/figma-desktop-bridge/code.js +57 -27
  19. package/figma-desktop-bridge/ui.html +10 -2
  20. package/package.json +1 -1
  21. package/dist/apps/design-system-dashboard/mcp-app.html +0 -521
  22. package/dist/apps/token-browser/mcp-app.html +0 -410
  23. package/dist/cloudflare/apps/design-system-dashboard/scoring/accessibility.js +0 -277
  24. package/dist/cloudflare/apps/design-system-dashboard/scoring/component-metadata.js +0 -357
  25. package/dist/cloudflare/apps/design-system-dashboard/scoring/consistency.js +0 -341
  26. package/dist/cloudflare/apps/design-system-dashboard/scoring/coverage.js +0 -230
  27. package/dist/cloudflare/apps/design-system-dashboard/scoring/engine.js +0 -92
  28. package/dist/cloudflare/apps/design-system-dashboard/scoring/naming-semantics.js +0 -308
  29. package/dist/cloudflare/apps/design-system-dashboard/scoring/token-architecture.js +0 -349
  30. package/dist/cloudflare/apps/design-system-dashboard/scoring/types.js +0 -40
  31. package/dist/cloudflare/apps/design-system-dashboard/server.js +0 -159
  32. package/dist/cloudflare/apps/token-browser/server.js +0 -136
  33. package/dist/cloudflare/browser-manager.js +0 -157
  34. package/dist/cloudflare/core/accessibility-tools.js +0 -361
  35. package/dist/cloudflare/core/annotation-tools.js +0 -230
  36. package/dist/cloudflare/core/cloud-websocket-connector.js +0 -419
  37. package/dist/cloudflare/core/cloud-websocket-relay.js +0 -198
  38. package/dist/cloudflare/core/comment-tools.js +0 -292
  39. package/dist/cloudflare/core/config.js +0 -153
  40. package/dist/cloudflare/core/console-monitor.js +0 -427
  41. package/dist/cloudflare/core/deep-component-tools.js +0 -128
  42. package/dist/cloudflare/core/design-code-tools.js +0 -2793
  43. package/dist/cloudflare/core/design-system-manifest.js +0 -265
  44. package/dist/cloudflare/core/design-system-tools.js +0 -882
  45. package/dist/cloudflare/core/diagnose-tool.js +0 -100
  46. package/dist/cloudflare/core/diff/changelog-formatter.js +0 -275
  47. package/dist/cloudflare/core/diff/diff-engine.js +0 -334
  48. package/dist/cloudflare/core/diff/property-compare.js +0 -36
  49. package/dist/cloudflare/core/diff/version-cache.js +0 -74
  50. package/dist/cloudflare/core/enrichment/enrichment-service.js +0 -374
  51. package/dist/cloudflare/core/enrichment/index.js +0 -7
  52. package/dist/cloudflare/core/enrichment/relationship-mapper.js +0 -351
  53. package/dist/cloudflare/core/enrichment/style-resolver.js +0 -346
  54. package/dist/cloudflare/core/figjam-tools.js +0 -547
  55. package/dist/cloudflare/core/figma-api.js +0 -509
  56. package/dist/cloudflare/core/figma-connector.js +0 -7
  57. package/dist/cloudflare/core/figma-reconstruction-spec.js +0 -402
  58. package/dist/cloudflare/core/figma-style-extractor.js +0 -311
  59. package/dist/cloudflare/core/figma-tools.js +0 -3353
  60. package/dist/cloudflare/core/identity.js +0 -96
  61. package/dist/cloudflare/core/library-tools.js +0 -580
  62. package/dist/cloudflare/core/logger.js +0 -53
  63. package/dist/cloudflare/core/port-discovery.js +0 -935
  64. package/dist/cloudflare/core/resolve-package-root.js +0 -11
  65. package/dist/cloudflare/core/slides-tools.js +0 -714
  66. package/dist/cloudflare/core/slot-tools.js +0 -334
  67. package/dist/cloudflare/core/snippet-injector.js +0 -96
  68. package/dist/cloudflare/core/tokens/alias-resolver.js +0 -205
  69. package/dist/cloudflare/core/tokens/config.js +0 -294
  70. package/dist/cloudflare/core/tokens/dialect.js +0 -232
  71. package/dist/cloudflare/core/tokens/figma-converter.js +0 -323
  72. package/dist/cloudflare/core/tokens/formatters/css-vars.js +0 -338
  73. package/dist/cloudflare/core/tokens/formatters/dtcg.js +0 -376
  74. package/dist/cloudflare/core/tokens/formatters/index.js +0 -45
  75. package/dist/cloudflare/core/tokens/formatters/json.js +0 -205
  76. package/dist/cloudflare/core/tokens/formatters/less.js +0 -4
  77. package/dist/cloudflare/core/tokens/formatters/scss.js +0 -258
  78. package/dist/cloudflare/core/tokens/formatters/stubs.js +0 -13
  79. package/dist/cloudflare/core/tokens/formatters/style-dictionary-v3.js +0 -213
  80. package/dist/cloudflare/core/tokens/formatters/tailwind-v3.js +0 -237
  81. package/dist/cloudflare/core/tokens/formatters/tailwind-v4.js +0 -335
  82. package/dist/cloudflare/core/tokens/formatters/tokens-studio.js +0 -256
  83. package/dist/cloudflare/core/tokens/formatters/ts-module.js +0 -198
  84. package/dist/cloudflare/core/tokens/index.js +0 -16
  85. package/dist/cloudflare/core/tokens/parsers/css-vars.js +0 -4
  86. package/dist/cloudflare/core/tokens/parsers/dtcg.js +0 -280
  87. package/dist/cloudflare/core/tokens/parsers/index.js +0 -138
  88. package/dist/cloudflare/core/tokens/parsers/json.js +0 -7
  89. package/dist/cloudflare/core/tokens/parsers/scss.js +0 -4
  90. package/dist/cloudflare/core/tokens/parsers/stubs.js +0 -20
  91. package/dist/cloudflare/core/tokens/parsers/style-dictionary-v3.js +0 -4
  92. package/dist/cloudflare/core/tokens/parsers/tailwind-v3.js +0 -4
  93. package/dist/cloudflare/core/tokens/parsers/tailwind-v4.js +0 -4
  94. package/dist/cloudflare/core/tokens/parsers/tokens-studio.js +0 -4
  95. package/dist/cloudflare/core/tokens/schemas.js +0 -152
  96. package/dist/cloudflare/core/tokens/transforms/color.js +0 -12
  97. package/dist/cloudflare/core/tokens/transforms/index.js +0 -29
  98. package/dist/cloudflare/core/tokens/transforms/size.js +0 -7
  99. package/dist/cloudflare/core/tokens/types.js +0 -18
  100. package/dist/cloudflare/core/tokens-tools.js +0 -1788
  101. package/dist/cloudflare/core/types/design-code.js +0 -4
  102. package/dist/cloudflare/core/types/enriched.js +0 -5
  103. package/dist/cloudflare/core/types/index.js +0 -4
  104. package/dist/cloudflare/core/variable-resolver.js +0 -85
  105. package/dist/cloudflare/core/version-tools.js +0 -1199
  106. package/dist/cloudflare/core/websocket-connector.js +0 -436
  107. package/dist/cloudflare/core/websocket-server.js +0 -1009
  108. package/dist/cloudflare/core/write-tools.js +0 -2506
  109. package/dist/cloudflare/index.js +0 -3010
@@ -1,334 +0,0 @@
1
- /**
2
- * Diff engine — pure functions for comparing Figma file/node snapshots
3
- * across two versions.
4
- *
5
- * Scope (v1):
6
- * - Page-structure diff (added/removed/renamed pages) from depth=1 file fetches
7
- * - Per-node diff for scoped fetches: name, description, children added/removed,
8
- * componentPropertyDefinitions changes (for COMPONENT_SET), boundVariables changes
9
- *
10
- * NOT in scope (deferred):
11
- * - Variable VALUE history (Figma REST does not expose this; planned forward
12
- * ledger will fill the gap going forward, never retroactively)
13
- * - Style-content diffs (only style add/remove via reachable styles map)
14
- * - Inside-fills/strokes binding diffs (top-level boundVariables only for v1)
15
- */
16
- /**
17
- * Compare two file documents (from /v1/files/:key?version=X with depth=1 or higher).
18
- * Both arguments should be the `document` field of a Figma file response.
19
- */
20
- export function diffPageStructure(fromDoc, toDoc) {
21
- const fromPages = extractPages(fromDoc);
22
- const toPages = extractPages(toDoc);
23
- const fromById = new Map(fromPages.map((p) => [p.id, p]));
24
- const toById = new Map(toPages.map((p) => [p.id, p]));
25
- const added = [];
26
- const removed = [];
27
- const renamed = [];
28
- for (const p of toPages) {
29
- if (!fromById.has(p.id)) {
30
- added.push(p);
31
- }
32
- else {
33
- const old = fromById.get(p.id);
34
- if (old.name !== p.name) {
35
- renamed.push({ id: p.id, old_name: old.name, new_name: p.name });
36
- }
37
- }
38
- }
39
- for (const p of fromPages) {
40
- if (!toById.has(p.id))
41
- removed.push(p);
42
- }
43
- return {
44
- pages_added: added,
45
- pages_removed: removed,
46
- pages_renamed: renamed,
47
- summary: {
48
- added: added.length,
49
- removed: removed.length,
50
- renamed: renamed.length,
51
- },
52
- };
53
- }
54
- function extractPages(doc) {
55
- const children = doc?.children;
56
- if (!Array.isArray(children))
57
- return [];
58
- return children
59
- .filter((c) => c && typeof c.id === "string" && typeof c.name === "string")
60
- .map((c) => ({ id: c.id, name: c.name }));
61
- }
62
- /**
63
- * Diff a COMPONENT_SET's componentPropertyDefinitions map.
64
- * Each definition is keyed by `propName#nodeId` in Figma's response.
65
- */
66
- export function diffComponentPropertyDefinitions(fromDefs, toDefs) {
67
- const from = fromDefs ?? {};
68
- const to = toDefs ?? {};
69
- const added = [];
70
- const removed = [];
71
- const typeChanged = [];
72
- const defaultChanged = [];
73
- for (const key of Object.keys(to)) {
74
- const toDef = to[key];
75
- if (!(key in from)) {
76
- added.push({ name: key, type: toDef.type, default_value: toDef.defaultValue });
77
- }
78
- else {
79
- const fromDef = from[key];
80
- if (fromDef.type !== toDef.type) {
81
- typeChanged.push({ name: key, from_type: fromDef.type, to_type: toDef.type });
82
- }
83
- if (!deepEqual(fromDef.defaultValue, toDef.defaultValue)) {
84
- defaultChanged.push({
85
- name: key,
86
- from_default: fromDef.defaultValue,
87
- to_default: toDef.defaultValue,
88
- });
89
- }
90
- }
91
- }
92
- for (const key of Object.keys(from)) {
93
- if (!(key in to)) {
94
- const fromDef = from[key];
95
- removed.push({ name: key, type: fromDef.type, default_value: fromDef.defaultValue });
96
- }
97
- }
98
- return {
99
- added,
100
- removed,
101
- type_changed: typeChanged,
102
- default_changed: defaultChanged,
103
- summary: {
104
- added: added.length,
105
- removed: removed.length,
106
- type_changed: typeChanged.length,
107
- default_changed: defaultChanged.length,
108
- },
109
- };
110
- }
111
- /**
112
- * Walk both node trees in parallel (matched by node id), comparing top-level
113
- * boundVariables. Inside-fills/strokes binding diffs are explicitly NOT included
114
- * in v1 — they add complexity for marginal additional value over node-level deltas.
115
- */
116
- export function collectBindingChanges(fromNode, toNode) {
117
- const changes = [];
118
- walkBoth(fromNode, toNode, (a, b) => {
119
- const aBindings = extractTopLevelBindings(a);
120
- const bBindings = extractTopLevelBindings(b);
121
- const allKeys = new Set([...Object.keys(aBindings), ...Object.keys(bBindings)]);
122
- for (const key of allKeys) {
123
- const fromId = aBindings[key] ?? null;
124
- const toId = bBindings[key] ?? null;
125
- if (fromId === toId)
126
- continue;
127
- let kind;
128
- if (fromId === null)
129
- kind = "added";
130
- else if (toId === null)
131
- kind = "removed";
132
- else
133
- kind = "rebound";
134
- changes.push({
135
- node_id: (b ?? a).id,
136
- node_name: (b ?? a).name ?? "",
137
- property: key,
138
- from_variable_id: fromId,
139
- to_variable_id: toId,
140
- change_kind: kind,
141
- });
142
- }
143
- });
144
- return changes;
145
- }
146
- function extractTopLevelBindings(node) {
147
- const out = {};
148
- const bv = node?.boundVariables;
149
- if (!bv || typeof bv !== "object")
150
- return out;
151
- for (const [prop, ref] of Object.entries(bv)) {
152
- if (ref && typeof ref === "object") {
153
- // Single VARIABLE_ALIAS reference
154
- if (ref.type === "VARIABLE_ALIAS" && typeof ref.id === "string") {
155
- out[prop] = ref.id;
156
- continue;
157
- }
158
- // Array of refs (e.g., fills binding can be an array — surface first match for v1)
159
- if (Array.isArray(ref)) {
160
- const first = ref.find((r) => r && r.type === "VARIABLE_ALIAS" && typeof r.id === "string");
161
- if (first)
162
- out[`${prop}[0]`] = first.id;
163
- }
164
- }
165
- }
166
- return out;
167
- }
168
- function walkBoth(fromNode, toNode, visit) {
169
- if (!fromNode && !toNode)
170
- return;
171
- visit(fromNode, toNode);
172
- const fromChildren = Array.isArray(fromNode?.children) ? fromNode.children : [];
173
- const toChildren = Array.isArray(toNode?.children) ? toNode.children : [];
174
- const byIdFrom = new Map();
175
- for (const c of fromChildren)
176
- if (c?.id)
177
- byIdFrom.set(c.id, c);
178
- const byIdTo = new Map();
179
- for (const c of toChildren)
180
- if (c?.id)
181
- byIdTo.set(c.id, c);
182
- const allIds = new Set([...byIdFrom.keys(), ...byIdTo.keys()]);
183
- for (const id of allIds) {
184
- walkBoth(byIdFrom.get(id), byIdTo.get(id), visit);
185
- }
186
- }
187
- /**
188
- * Diff a single node (typically a COMPONENT_SET) at depth=2. Either side may be
189
- * null/undefined to represent "newly added" or "removed entirely."
190
- */
191
- export function diffNode(fromNode, toNode, mode = "standard") {
192
- const id = (toNode ?? fromNode)?.id ?? "";
193
- const name = (toNode ?? fromNode)?.name ?? "";
194
- const type = (toNode ?? fromNode)?.type ?? "";
195
- const out = {
196
- node_id: id,
197
- node_name: name,
198
- node_type: type,
199
- name_changed: null,
200
- description_changed: null,
201
- children_added: [],
202
- children_removed: [],
203
- component_properties: null,
204
- binding_changes: [],
205
- change_count: 0,
206
- notes: [],
207
- };
208
- if (!fromNode && toNode) {
209
- out.notes.push("Node was added in the target version (no prior state to compare).");
210
- out.change_count = 1;
211
- return out;
212
- }
213
- if (fromNode && !toNode) {
214
- out.notes.push("Node was removed in the target version (no later state to compare).");
215
- out.change_count = 1;
216
- return out;
217
- }
218
- if (!fromNode && !toNode) {
219
- out.notes.push("Node not found in either version.");
220
- return out;
221
- }
222
- if (fromNode.name !== toNode.name) {
223
- out.name_changed = { from: fromNode.name, to: toNode.name };
224
- }
225
- if ((fromNode.description ?? "") !== (toNode.description ?? "")) {
226
- out.description_changed = {
227
- from: fromNode.description ?? "",
228
- to: toNode.description ?? "",
229
- };
230
- }
231
- const fromChildIds = new Set((fromNode.children ?? []).filter((c) => c?.id).map((c) => c.id));
232
- const toChildIds = new Set((toNode.children ?? []).filter((c) => c?.id).map((c) => c.id));
233
- for (const c of toNode.children ?? []) {
234
- if (c?.id && !fromChildIds.has(c.id)) {
235
- out.children_added.push({ id: c.id, name: c.name ?? "", type: c.type ?? "" });
236
- }
237
- }
238
- for (const c of fromNode.children ?? []) {
239
- if (c?.id && !toChildIds.has(c.id)) {
240
- out.children_removed.push({ id: c.id, name: c.name ?? "", type: c.type ?? "" });
241
- }
242
- }
243
- if (fromNode.type === "COMPONENT_SET" || toNode.type === "COMPONENT_SET") {
244
- const propDiff = diffComponentPropertyDefinitions(fromNode.componentPropertyDefinitions, toNode.componentPropertyDefinitions);
245
- const hasAny = propDiff.summary.added > 0 ||
246
- propDiff.summary.removed > 0 ||
247
- propDiff.summary.type_changed > 0 ||
248
- propDiff.summary.default_changed > 0;
249
- if (hasAny)
250
- out.component_properties = propDiff;
251
- }
252
- out.binding_changes = collectBindingChanges(fromNode, toNode);
253
- out.change_count =
254
- (out.name_changed ? 1 : 0) +
255
- (out.description_changed ? 1 : 0) +
256
- out.children_added.length +
257
- out.children_removed.length +
258
- (out.component_properties
259
- ? out.component_properties.summary.added +
260
- out.component_properties.summary.removed +
261
- out.component_properties.summary.type_changed +
262
- out.component_properties.summary.default_changed
263
- : 0) +
264
- out.binding_changes.length;
265
- if (mode === "summary") {
266
- // Strip detail arrays, keep counts only
267
- out.children_added = [];
268
- out.children_removed = [];
269
- if (out.component_properties) {
270
- out.component_properties = {
271
- added: [],
272
- removed: [],
273
- type_changed: [],
274
- default_changed: [],
275
- summary: out.component_properties.summary,
276
- };
277
- }
278
- out.binding_changes = [];
279
- }
280
- else if (mode === "standard") {
281
- // Keep the lightweight summaries (children_added, children_removed,
282
- // component_properties.summary). Strip the heavy detail arrays.
283
- if (out.component_properties) {
284
- out.component_properties = {
285
- added: out.component_properties.added.map((p) => ({ ...p, default_value: undefined })),
286
- removed: out.component_properties.removed.map((p) => ({
287
- ...p,
288
- default_value: undefined,
289
- })),
290
- type_changed: out.component_properties.type_changed,
291
- default_changed: out.component_properties.default_changed.map((d) => ({
292
- name: d.name,
293
- from_default: undefined,
294
- to_default: undefined,
295
- })),
296
- summary: out.component_properties.summary,
297
- };
298
- }
299
- // Bindings: keep as-is, they're already compact.
300
- }
301
- // mode === "detailed" → keep everything
302
- return out;
303
- }
304
- // ============================================================================
305
- // Helpers
306
- // ============================================================================
307
- function deepEqual(a, b) {
308
- if (a === b)
309
- return true;
310
- if (a === null || b === null)
311
- return false;
312
- if (typeof a !== typeof b)
313
- return false;
314
- if (typeof a !== "object")
315
- return false;
316
- if (Array.isArray(a) !== Array.isArray(b))
317
- return false;
318
- if (Array.isArray(a)) {
319
- if (a.length !== b.length)
320
- return false;
321
- for (let i = 0; i < a.length; i++)
322
- if (!deepEqual(a[i], b[i]))
323
- return false;
324
- return true;
325
- }
326
- const aKeys = Object.keys(a);
327
- const bKeys = Object.keys(b);
328
- if (aKeys.length !== bKeys.length)
329
- return false;
330
- for (const k of aKeys)
331
- if (!deepEqual(a[k], b[k]))
332
- return false;
333
- return true;
334
- }
@@ -1,36 +0,0 @@
1
- /**
2
- * Property comparison primitives.
3
- *
4
- * Pure helper functions used by both the design-code parity tools and
5
- * the version diff engine. Moved here from src/core/design-code-tools.ts
6
- * so multiple consumers can share without circular dependencies.
7
- */
8
- /** Convert Figma RGBA (0-1 floats) to hex string */
9
- export function figmaRGBAToHex(color) {
10
- const r = Math.round(color.r * 255);
11
- const g = Math.round(color.g * 255);
12
- const b = Math.round(color.b * 255);
13
- const hex = `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`.toUpperCase();
14
- if (color.a !== undefined && color.a < 1) {
15
- const a = Math.round(color.a * 255);
16
- return `${hex}${a.toString(16).padStart(2, "0")}`;
17
- }
18
- return hex;
19
- }
20
- /** Normalize a color string for comparison (uppercase hex without alpha if fully opaque) */
21
- export function normalizeColor(color) {
22
- let c = color.trim().toUpperCase();
23
- // Strip alpha if fully opaque (FF)
24
- if (c.length === 9 && c.endsWith("FF")) {
25
- c = c.slice(0, 7);
26
- }
27
- // Expand shorthand (#RGB -> #RRGGBB)
28
- if (/^#[0-9A-F]{3}$/.test(c)) {
29
- c = `#${c[1]}${c[1]}${c[2]}${c[2]}${c[3]}${c[3]}`;
30
- }
31
- return c;
32
- }
33
- /** Compare numeric values with a tolerance */
34
- export function numericClose(a, b, tolerance = 1) {
35
- return Math.abs(a - b) <= tolerance;
36
- }
@@ -1,74 +0,0 @@
1
- /**
2
- * In-memory LRU cache for Figma file snapshots at past versions.
3
- *
4
- * Past versions are immutable, so a cached snapshot for `${fileKey}:${versionId}`
5
- * never goes stale. The cache is process-scoped (no persistence) and bounded by
6
- * a max-entries cap rather than a byte budget — keeps the implementation simple
7
- * and avoids assumptions about JSON serialization size.
8
- *
9
- * HEAD ("current") is intentionally NOT cached because it changes on every save.
10
- * Callers should pass `null` as the versionId for HEAD requests; the cache
11
- * methods short-circuit on null.
12
- *
13
- * Usage:
14
- * const cache = new VersionSnapshotCache();
15
- * const key = cache.makeKey(fileKey, versionId, depth, nodeIds);
16
- * let snapshot = cache.get(key);
17
- * if (!snapshot) {
18
- * snapshot = await api.getFile(fileKey, { version: versionId, depth });
19
- * cache.set(key, snapshot);
20
- * }
21
- */
22
- export class VersionSnapshotCache {
23
- constructor(options = {}) {
24
- // Map preserves insertion order — first key is the LRU.
25
- this.store = new Map();
26
- this.maxEntries = options.maxEntries ?? 50;
27
- }
28
- /**
29
- * Build a stable cache key from the request parameters.
30
- * Returns null for HEAD requests (versionId is null/undefined/empty).
31
- */
32
- makeKey(fileKey, versionId, depth, nodeIds) {
33
- if (!versionId)
34
- return null;
35
- const idsHash = nodeIds && nodeIds.length > 0
36
- ? [...nodeIds].sort().join(",")
37
- : "";
38
- const depthPart = depth === undefined ? "full" : String(depth);
39
- return `${fileKey}:${versionId}:${depthPart}:${idsHash}`;
40
- }
41
- get(key) {
42
- if (key === null)
43
- return undefined;
44
- if (!this.store.has(key))
45
- return undefined;
46
- // Refresh recency: delete + re-insert moves the key to the end.
47
- const value = this.store.get(key);
48
- this.store.delete(key);
49
- this.store.set(key, value);
50
- return value;
51
- }
52
- set(key, value) {
53
- if (key === null)
54
- return;
55
- if (this.store.has(key))
56
- this.store.delete(key);
57
- this.store.set(key, value);
58
- while (this.store.size > this.maxEntries) {
59
- const oldestKey = this.store.keys().next().value;
60
- if (oldestKey === undefined)
61
- break;
62
- this.store.delete(oldestKey);
63
- }
64
- }
65
- has(key) {
66
- return key !== null && this.store.has(key);
67
- }
68
- get size() {
69
- return this.store.size;
70
- }
71
- clear() {
72
- this.store.clear();
73
- }
74
- }