@nexart/ui-renderer 0.8.3 → 0.8.5
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/README.md +30 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/preview/code-renderer.d.ts.map +1 -1
- package/dist/preview/code-renderer.js +27 -21
- package/dist/preview/preview-engine.d.ts.map +1 -1
- package/dist/preview/preview-engine.js +24 -19
- package/dist/preview/unified-renderer.d.ts.map +1 -1
- package/dist/preview/unified-renderer.js +21 -16
- package/dist/types.d.ts +2 -2
- package/dist/types.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @nexart/ui-renderer
|
|
2
2
|
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.5
|
|
4
4
|
|
|
5
5
|
**Lightweight Preview Runtime for NexArt Protocol**
|
|
6
6
|
|
|
@@ -19,6 +19,34 @@ Version: 0.8.3
|
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
+
## v0.8.5 — Correct Animation Loop Semantics
|
|
23
|
+
|
|
24
|
+
Fixed animation loop to use proper soft-gating without resets.
|
|
25
|
+
|
|
26
|
+
- **No resets inside loop**: Budget and frameCount are never reset mid-animation
|
|
27
|
+
- **Soft budget gating**: Budget exhaustion skips draw(), preserving last frame
|
|
28
|
+
- **Modulo-based looping**: `t = (frameCount % totalFrames) / totalFrames` for natural animation cycles
|
|
29
|
+
- **Canvas preservation**: Skipped frames keep current pixels, no black flashes
|
|
30
|
+
|
|
31
|
+
**Correct Pattern:**
|
|
32
|
+
```javascript
|
|
33
|
+
function loop() {
|
|
34
|
+
requestAnimationFrame(loop);
|
|
35
|
+
|
|
36
|
+
if (!budget.canRenderFrame()) return; // Preserve pixels
|
|
37
|
+
|
|
38
|
+
runtime.draw(); // Only changes canvas here
|
|
39
|
+
budget.recordFrame();
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Behavior:**
|
|
44
|
+
- Renders up to 30 frames (budget limit)
|
|
45
|
+
- After budget exhausted: RAF continues, draw() skipped, last frame stays visible
|
|
46
|
+
- Restart via `stop()` → `start()` to reset and continue
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
22
50
|
## v0.8.3 — Animation Loop Fix
|
|
23
51
|
|
|
24
52
|
Fixed critical bug where preview only rendered a single frame.
|
|
@@ -241,7 +269,7 @@ import { getCapabilities } from '@nexart/ui-renderer';
|
|
|
241
269
|
|
|
242
270
|
const caps = getCapabilities();
|
|
243
271
|
// {
|
|
244
|
-
// version: '0.8.
|
|
272
|
+
// version: '0.8.5',
|
|
245
273
|
// isCanonical: false,
|
|
246
274
|
// isArchival: false,
|
|
247
275
|
// previewBudget: { MAX_FRAMES: 30, MAX_TOTAL_TIME_MS: 500, FRAME_STRIDE: 3 },
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @nexart/ui-renderer
|
|
3
|
-
* Version: 0.8.
|
|
3
|
+
* Version: 0.8.5
|
|
4
4
|
*
|
|
5
5
|
* Lightweight Preview Runtime for NexArt Protocol
|
|
6
6
|
*
|
|
@@ -46,7 +46,7 @@ export { calculateScaledDimensions, applyScaledDimensions, type ScaledDimensions
|
|
|
46
46
|
export type { NexArtSystemInput, NexArtSystem, DeclarativeSystemInput, DeclarativeSystem, CodeSystem, NexArtCodeSystem, UnifiedSystemInput, UnifiedSystem, UnifiedElement, BackgroundElement, PrimitiveElement, SketchElement, BackgroundPreset, PrimitiveName, ColorPalette, MotionSpeed, StrokeWeightAuto, LoopConfig, DeclarativeElement, SystemElement, DotsElement, LinesElement, WavesElement, GridElement, FlowFieldElement, OrbitsElement, BackgroundConfig, MotionConfig, PreviewOptions, ValidationResult, } from './types';
|
|
47
47
|
export { AESTHETIC_DEFAULTS, SDK_VERSION as TYPE_SDK_VERSION } from './types';
|
|
48
48
|
export type { Capabilities, PrimitiveCapability, ParameterSpec, } from './capabilities';
|
|
49
|
-
export declare const SDK_VERSION = "0.8.
|
|
49
|
+
export declare const SDK_VERSION = "0.8.5";
|
|
50
50
|
export declare const PROTOCOL_VERSION = "0.8";
|
|
51
51
|
export declare const IS_CANONICAL = false;
|
|
52
52
|
export declare const IS_ARCHIVAL = false;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @nexart/ui-renderer
|
|
3
|
-
* Version: 0.8.
|
|
3
|
+
* Version: 0.8.5
|
|
4
4
|
*
|
|
5
5
|
* Lightweight Preview Runtime for NexArt Protocol
|
|
6
6
|
*
|
|
@@ -44,7 +44,7 @@ export { PREVIEW_BUDGET, CANVAS_LIMITS, } from './preview/preview-types';
|
|
|
44
44
|
export { createFrameBudget, canRenderFrame, recordFrame, resetBudget, shouldSkipFrame, } from './preview/frame-budget';
|
|
45
45
|
export { calculateScaledDimensions, applyScaledDimensions, } from './preview/canvas-scaler';
|
|
46
46
|
export { AESTHETIC_DEFAULTS, SDK_VERSION as TYPE_SDK_VERSION } from './types';
|
|
47
|
-
export const SDK_VERSION = '0.8.
|
|
47
|
+
export const SDK_VERSION = '0.8.5';
|
|
48
48
|
export const PROTOCOL_VERSION = '0.8';
|
|
49
49
|
export const IS_CANONICAL = false;
|
|
50
50
|
export const IS_ARCHIVAL = false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-renderer.d.ts","sourceRoot":"","sources":["../../src/preview/code-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AA0BjE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;IACnB,UAAU,EAAE,KAAK,CAAC;CACnB;AAwBD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,cAAmB,GAC3B,YAAY,
|
|
1
|
+
{"version":3,"file":"code-renderer.d.ts","sourceRoot":"","sources":["../../src/preview/code-renderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AA0BjE,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;IACnB,UAAU,EAAE,KAAK,CAAC;CACnB;AAwBD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,cAAmB,GAC3B,YAAY,CAyQd"}
|
|
@@ -177,8 +177,10 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
|
|
|
177
177
|
// ║ ANIMATION LOOP INVARIANT — DO NOT CHANGE ║
|
|
178
178
|
// ║ ║
|
|
179
179
|
// ║ requestAnimationFrame(loop) MUST be called on EVERY tick. ║
|
|
180
|
-
// ║ Budget gates ONLY whether draw() executes, NOT
|
|
181
|
-
// ║ Never
|
|
180
|
+
// ║ Budget gates ONLY whether draw() executes, NOT loop continuation. ║
|
|
181
|
+
// ║ Never reset frameCount or budget inside the loop. ║
|
|
182
|
+
// ║ Canvas only changes when draw() runs — skipped frames keep pixels. ║
|
|
183
|
+
// ║ Looping uses modulo math (t = frame % total), not counter resets. ║
|
|
182
184
|
// ╚═══════════════════════════════════════════════════════════════════════╝
|
|
183
185
|
const loop = () => {
|
|
184
186
|
// ALWAYS schedule next frame first — loop never terminates on its own
|
|
@@ -186,28 +188,32 @@ export function renderCodeModeSystem(system, canvas, options = {}) {
|
|
|
186
188
|
// Early exit checks (after RAF is scheduled)
|
|
187
189
|
if (!isRunning || isDestroyed)
|
|
188
190
|
return;
|
|
189
|
-
// Budget check — skip draw but keep
|
|
191
|
+
// Budget check — if exhausted, skip draw but keep last frame visible
|
|
190
192
|
if (!canRenderFrame(budget)) {
|
|
191
|
-
return; //
|
|
193
|
+
return; // Preserve current canvas, no clear, no reset
|
|
192
194
|
}
|
|
193
195
|
frameCount++;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
196
|
+
// Frame stride check — skip some frames for performance
|
|
197
|
+
if (shouldSkipFrame(frameCount)) {
|
|
198
|
+
return; // Skip but don't clear — preserve last drawn frame
|
|
199
|
+
}
|
|
200
|
+
// Update runtime timing using modulo for natural looping
|
|
201
|
+
if (runtime) {
|
|
202
|
+
runtime.frameCount = frameCount;
|
|
203
|
+
runtime.t = (frameCount % totalFrames) / totalFrames;
|
|
204
|
+
runtime.time = runtime.t;
|
|
205
|
+
runtime.tGlobal = runtime.t;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
// Clear canvas and draw (only when actually rendering)
|
|
209
|
+
clearCanvasIgnoringTransform(ctx, canvas);
|
|
210
|
+
if (drawFn)
|
|
211
|
+
drawFn();
|
|
212
|
+
drawBadge();
|
|
213
|
+
recordFrame(budget);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
console.warn('[UIRenderer] Draw error:', error);
|
|
211
217
|
}
|
|
212
218
|
};
|
|
213
219
|
animationId = requestAnimationFrame(loop);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview-engine.d.ts","sourceRoot":"","sources":["../../src/preview/preview-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EAErB,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"preview-engine.d.ts","sourceRoot":"","sources":["../../src/preview/preview-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EAErB,MAAM,iBAAiB,CAAC;AA2NzB;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,eAAe,CAEhF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB,CAKpF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAKxC"}
|
|
@@ -136,8 +136,10 @@ class PreviewEngine {
|
|
|
136
136
|
// ║ ANIMATION LOOP INVARIANT — DO NOT CHANGE ║
|
|
137
137
|
// ║ ║
|
|
138
138
|
// ║ requestAnimationFrame MUST be called on EVERY tick. ║
|
|
139
|
-
// ║ Budget gates ONLY whether draw() executes, NOT
|
|
140
|
-
// ║ Never
|
|
139
|
+
// ║ Budget gates ONLY whether draw() executes, NOT loop continuation. ║
|
|
140
|
+
// ║ Never reset frameCount or budget inside the loop. ║
|
|
141
|
+
// ║ Canvas only changes when draw() runs — skipped frames keep pixels. ║
|
|
142
|
+
// ║ Looping uses modulo math (t = frame % total), not counter resets. ║
|
|
141
143
|
// ╚═══════════════════════════════════════════════════════════════════════╝
|
|
142
144
|
scheduleNextFrame() {
|
|
143
145
|
// ALWAYS schedule next frame first — loop never terminates on its own
|
|
@@ -146,28 +148,31 @@ class PreviewEngine {
|
|
|
146
148
|
// Early exit checks (after RAF is scheduled)
|
|
147
149
|
if (!this.running)
|
|
148
150
|
return;
|
|
149
|
-
// Budget check — skip draw but keep
|
|
151
|
+
// Budget check — if exhausted, skip draw but keep last frame visible
|
|
150
152
|
if (!canRenderFrame(this.budget)) {
|
|
151
|
-
return; //
|
|
153
|
+
return; // Preserve current canvas, no clear, no reset
|
|
152
154
|
}
|
|
153
155
|
this.internalFrameCount++;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
recordFrame(this.budget);
|
|
156
|
+
// Frame stride check — skip some frames for performance
|
|
157
|
+
if (shouldSkipFrame(this.internalFrameCount)) {
|
|
158
|
+
return; // Skip but don't clear — preserve last drawn frame
|
|
159
|
+
}
|
|
160
|
+
// Update runtime timing using modulo for natural looping
|
|
161
|
+
try {
|
|
162
|
+
if (this.runtime) {
|
|
163
|
+
this.runtime.frameCount = this.internalFrameCount;
|
|
164
|
+
const totalFrames = this.config.totalFrames ?? 120;
|
|
165
|
+
this.runtime.t = (this.internalFrameCount % totalFrames) / totalFrames;
|
|
166
|
+
this.runtime.time = this.runtime.t;
|
|
167
|
+
this.runtime.tGlobal = this.runtime.t;
|
|
167
168
|
}
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
if (this.drawFn) {
|
|
170
|
+
this.drawFn();
|
|
170
171
|
}
|
|
172
|
+
recordFrame(this.budget);
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
console.warn('[PreviewEngine] Draw error:', error);
|
|
171
176
|
}
|
|
172
177
|
});
|
|
173
178
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unified-renderer.d.ts","sourceRoot":"","sources":["../../src/preview/unified-renderer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAkB,cAAc,EAAoE,MAAM,UAAU,CAAC;AAmBhJ,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;IACnB,UAAU,EAAE,KAAK,CAAC;CACnB;AA6ED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,cAAmB,GAC3B,eAAe,
|
|
1
|
+
{"version":3,"file":"unified-renderer.d.ts","sourceRoot":"","sources":["../../src/preview/unified-renderer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAkB,cAAc,EAAoE,MAAM,UAAU,CAAC;AAmBhJ,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;IACnB,UAAU,EAAE,KAAK,CAAC;CACnB;AA6ED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,cAAmB,GAC3B,eAAe,CAmRjB"}
|
|
@@ -241,8 +241,10 @@ export function renderUnifiedSystem(system, canvas, options = {}) {
|
|
|
241
241
|
// ║ ANIMATION LOOP INVARIANT — DO NOT CHANGE ║
|
|
242
242
|
// ║ ║
|
|
243
243
|
// ║ requestAnimationFrame(loop) MUST be called on EVERY tick. ║
|
|
244
|
-
// ║ Budget gates ONLY whether draw() executes, NOT
|
|
245
|
-
// ║ Never
|
|
244
|
+
// ║ Budget gates ONLY whether draw() executes, NOT loop continuation. ║
|
|
245
|
+
// ║ Never reset frameCount or budget inside the loop. ║
|
|
246
|
+
// ║ Canvas only changes when draw() runs — skipped frames keep pixels. ║
|
|
247
|
+
// ║ Looping uses modulo math (t = frame % total), not counter resets. ║
|
|
246
248
|
// ╚═══════════════════════════════════════════════════════════════════════╝
|
|
247
249
|
const loop = () => {
|
|
248
250
|
// ALWAYS schedule next frame first — loop never terminates on its own
|
|
@@ -250,23 +252,26 @@ export function renderUnifiedSystem(system, canvas, options = {}) {
|
|
|
250
252
|
// Early exit checks (after RAF is scheduled)
|
|
251
253
|
if (!isRunning || isDestroyed)
|
|
252
254
|
return;
|
|
253
|
-
// Budget check — skip draw but keep
|
|
255
|
+
// Budget check — if exhausted, skip draw but keep last frame visible
|
|
254
256
|
if (!canRenderFrame(budget)) {
|
|
255
|
-
return; //
|
|
257
|
+
return; // Preserve current canvas, no clear, no reset
|
|
256
258
|
}
|
|
257
259
|
frameCount++;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
260
|
+
// Frame stride check — skip some frames for performance
|
|
261
|
+
if (shouldSkipFrame(frameCount)) {
|
|
262
|
+
return; // Skip but don't clear — preserve last drawn frame
|
|
263
|
+
}
|
|
264
|
+
// Calculate t using modulo for natural looping
|
|
265
|
+
const t = (frameCount % totalFrames) / totalFrames;
|
|
266
|
+
p.randomSeed(system.seed);
|
|
267
|
+
p.noiseSeed(system.seed);
|
|
268
|
+
try {
|
|
269
|
+
runDraw(p, frameCount, t);
|
|
270
|
+
drawBadge();
|
|
271
|
+
recordFrame(budget);
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
console.warn('[UIRenderer] Draw error:', error);
|
|
270
275
|
}
|
|
271
276
|
};
|
|
272
277
|
animationId = requestAnimationFrame(loop);
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @nexart/ui-renderer v0.8.
|
|
2
|
+
* @nexart/ui-renderer v0.8.5 - Type Definitions
|
|
3
3
|
*
|
|
4
4
|
* Lightweight Preview Runtime for NexArt Protocol.
|
|
5
5
|
* This SDK is non-canonical and for preview only.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Max total time: 500ms
|
|
10
10
|
* - Max canvas dimension: 900px
|
|
11
11
|
*/
|
|
12
|
-
export declare const SDK_VERSION = "0.8.
|
|
12
|
+
export declare const SDK_VERSION = "0.8.5";
|
|
13
13
|
export declare const AESTHETIC_DEFAULTS: {
|
|
14
14
|
readonly background: {
|
|
15
15
|
readonly r: 246;
|
package/dist/types.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @nexart/ui-renderer v0.8.
|
|
2
|
+
* @nexart/ui-renderer v0.8.5 - Type Definitions
|
|
3
3
|
*
|
|
4
4
|
* Lightweight Preview Runtime for NexArt Protocol.
|
|
5
5
|
* This SDK is non-canonical and for preview only.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Max total time: 500ms
|
|
10
10
|
* - Max canvas dimension: 900px
|
|
11
11
|
*/
|
|
12
|
-
export const SDK_VERSION = '0.8.
|
|
12
|
+
export const SDK_VERSION = '0.8.5';
|
|
13
13
|
export const AESTHETIC_DEFAULTS = {
|
|
14
14
|
background: { r: 246, g: 245, b: 242 },
|
|
15
15
|
foreground: { r: 45, g: 45, b: 45 },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexart/ui-renderer",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "Lightweight Preview Runtime for NexArt Protocol. Non-canonical, performance-optimized with budget limits (max 30 frames, 500ms, 900px canvas).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|