@reteps/dockerfmt 0.2.0-alpha → 0.2.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.
@@ -0,0 +1,8 @@
1
+ import './wasm_exec.cjs';
2
+ interface FormatOptions {
3
+ indent: number;
4
+ trailingNewline: boolean;
5
+ }
6
+ declare const formatDockerfile: (fileName: string, options: FormatOptions) => Promise<string>;
7
+ declare const formatDockerfileContents: (fileContents: string, options: FormatOptions) => Promise<string>;
8
+ export { formatDockerfile, formatDockerfileContents };
package/dist/format.js ADDED
@@ -0,0 +1,36 @@
1
+ import fs from 'node:fs/promises';
2
+ import './wasm_exec.cjs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'url';
5
+ const formatDockerfile = async (fileName, options) => {
6
+ const fileBuffer = await fs.readFile(fileName);
7
+ const fileContents = fileBuffer.toString();
8
+ return formatDockerfileContents(fileContents, options);
9
+ };
10
+ const formatDockerfileContents = async (fileContents, options) => {
11
+ const go = new Go(); // Defined in wasm_exec.js
12
+ const encoder = new TextEncoder();
13
+ const decoder = new TextDecoder();
14
+ // get current working directory
15
+ const wasmBuffer = await fs.readFile(path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'format.wasm'));
16
+ const wasm = await WebAssembly.instantiate(wasmBuffer, go.importObject);
17
+ /**
18
+ * Do not await this promise, because it only resolves once the go main()
19
+ * function has exited. But we need the main function to stay alive to be
20
+ * able to call the `parse` and `print` function.
21
+ */
22
+ go.run(wasm.instance);
23
+ const { memory, malloc, free, formatBytes } = wasm.instance.exports;
24
+ const fileBufferBytes = encoder.encode(fileContents);
25
+ const filePointer = malloc(fileBufferBytes.byteLength);
26
+ new Uint8Array(memory.buffer).set(fileBufferBytes, filePointer);
27
+ // Call formatBytes function from WebAssembly
28
+ const resultPointer = formatBytes(filePointer, fileBufferBytes.byteLength, options.indent, options.trailingNewline);
29
+ // Decode the result
30
+ const resultBytes = new Uint8Array(memory.buffer).subarray(resultPointer);
31
+ const end = resultBytes.indexOf(0);
32
+ const result = decoder.decode(resultBytes.subarray(0, end));
33
+ free(filePointer);
34
+ return result;
35
+ };
36
+ export { formatDockerfile, formatDockerfileContents };
Binary file
@@ -0,0 +1,554 @@
1
+ // https://github.com/tinygo-org/tinygo/blob/64d8a043084cb9a56763192000e40ddc5dce733f/targets/wasm_exec.js
2
+ // Copyright 2018 The Go Authors. All rights reserved.
3
+ // Use of this source code is governed by a BSD-style
4
+ // license that can be found in the LICENSE file.
5
+ //
6
+ // This file has been modified for use by the TinyGo compiler.
7
+
8
+ (() => {
9
+ // Map multiple JavaScript environments to a single common API,
10
+ // preferring web standards over Node.js API.
11
+ //
12
+ // Environments considered:
13
+ // - Browsers
14
+ // - Node.js
15
+ // - Electron
16
+ // - Parcel
17
+
18
+ if (typeof global !== "undefined") {
19
+ // global already exists
20
+ } else if (typeof window !== "undefined") {
21
+ window.global = window;
22
+ } else if (typeof self !== "undefined") {
23
+ self.global = self;
24
+ } else {
25
+ throw new Error("cannot export Go (neither global, window nor self is defined)");
26
+ }
27
+
28
+ if (!global.require && typeof require !== "undefined") {
29
+ global.require = require;
30
+ }
31
+
32
+ if (!global.fs && global.require) {
33
+ global.fs = require("node:fs");
34
+ }
35
+
36
+ const enosys = () => {
37
+ const err = new Error("not implemented");
38
+ err.code = "ENOSYS";
39
+ return err;
40
+ };
41
+
42
+ if (!global.fs) {
43
+ let outputBuf = "";
44
+ global.fs = {
45
+ constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused
46
+ writeSync(fd, buf) {
47
+ outputBuf += decoder.decode(buf);
48
+ const nl = outputBuf.lastIndexOf("\n");
49
+ if (nl != -1) {
50
+ console.log(outputBuf.substr(0, nl));
51
+ outputBuf = outputBuf.substr(nl + 1);
52
+ }
53
+ return buf.length;
54
+ },
55
+ write(fd, buf, offset, length, position, callback) {
56
+ if (offset !== 0 || length !== buf.length || position !== null) {
57
+ callback(enosys());
58
+ return;
59
+ }
60
+ const n = this.writeSync(fd, buf);
61
+ callback(null, n);
62
+ },
63
+ chmod(path, mode, callback) { callback(enosys()); },
64
+ chown(path, uid, gid, callback) { callback(enosys()); },
65
+ close(fd, callback) { callback(enosys()); },
66
+ fchmod(fd, mode, callback) { callback(enosys()); },
67
+ fchown(fd, uid, gid, callback) { callback(enosys()); },
68
+ fstat(fd, callback) { callback(enosys()); },
69
+ fsync(fd, callback) { callback(null); },
70
+ ftruncate(fd, length, callback) { callback(enosys()); },
71
+ lchown(path, uid, gid, callback) { callback(enosys()); },
72
+ link(path, link, callback) { callback(enosys()); },
73
+ lstat(path, callback) { callback(enosys()); },
74
+ mkdir(path, perm, callback) { callback(enosys()); },
75
+ open(path, flags, mode, callback) { callback(enosys()); },
76
+ read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
77
+ readdir(path, callback) { callback(enosys()); },
78
+ readlink(path, callback) { callback(enosys()); },
79
+ rename(from, to, callback) { callback(enosys()); },
80
+ rmdir(path, callback) { callback(enosys()); },
81
+ stat(path, callback) { callback(enosys()); },
82
+ symlink(path, link, callback) { callback(enosys()); },
83
+ truncate(path, length, callback) { callback(enosys()); },
84
+ unlink(path, callback) { callback(enosys()); },
85
+ utimes(path, atime, mtime, callback) { callback(enosys()); },
86
+ };
87
+ }
88
+
89
+ if (!global.process) {
90
+ global.process = {
91
+ getuid() { return -1; },
92
+ getgid() { return -1; },
93
+ geteuid() { return -1; },
94
+ getegid() { return -1; },
95
+ getgroups() { throw enosys(); },
96
+ pid: -1,
97
+ ppid: -1,
98
+ umask() { throw enosys(); },
99
+ cwd() { throw enosys(); },
100
+ chdir() { throw enosys(); },
101
+ }
102
+ }
103
+
104
+ if (!global.crypto) {
105
+ const nodeCrypto = require("node:crypto");
106
+ global.crypto = {
107
+ getRandomValues(b) {
108
+ nodeCrypto.randomFillSync(b);
109
+ },
110
+ };
111
+ }
112
+
113
+ if (!global.performance) {
114
+ global.performance = {
115
+ now() {
116
+ const [sec, nsec] = process.hrtime();
117
+ return sec * 1000 + nsec / 1000000;
118
+ },
119
+ };
120
+ }
121
+
122
+ if (!global.TextEncoder) {
123
+ global.TextEncoder = require("node:util").TextEncoder;
124
+ }
125
+
126
+ if (!global.TextDecoder) {
127
+ global.TextDecoder = require("node:util").TextDecoder;
128
+ }
129
+
130
+ // End of polyfills for common API.
131
+
132
+ const encoder = new TextEncoder("utf-8");
133
+ const decoder = new TextDecoder("utf-8");
134
+ let reinterpretBuf = new DataView(new ArrayBuffer(8));
135
+ var logLine = [];
136
+ const wasmExit = {}; // thrown to exit via proc_exit (not an error)
137
+
138
+ global.Go = class {
139
+ constructor() {
140
+ this._callbackTimeouts = new Map();
141
+ this._nextCallbackTimeoutID = 1;
142
+
143
+ const mem = () => {
144
+ // The buffer may change when requesting more memory.
145
+ return new DataView(this._inst.exports.memory.buffer);
146
+ }
147
+
148
+ const unboxValue = (v_ref) => {
149
+ reinterpretBuf.setBigInt64(0, v_ref, true);
150
+ const f = reinterpretBuf.getFloat64(0, true);
151
+ if (f === 0) {
152
+ return undefined;
153
+ }
154
+ if (!isNaN(f)) {
155
+ return f;
156
+ }
157
+
158
+ const id = v_ref & 0xffffffffn;
159
+ return this._values[id];
160
+ }
161
+
162
+
163
+ const loadValue = (addr) => {
164
+ let v_ref = mem().getBigUint64(addr, true);
165
+ return unboxValue(v_ref);
166
+ }
167
+
168
+ const boxValue = (v) => {
169
+ const nanHead = 0x7FF80000n;
170
+
171
+ if (typeof v === "number") {
172
+ if (isNaN(v)) {
173
+ return nanHead << 32n;
174
+ }
175
+ if (v === 0) {
176
+ return (nanHead << 32n) | 1n;
177
+ }
178
+ reinterpretBuf.setFloat64(0, v, true);
179
+ return reinterpretBuf.getBigInt64(0, true);
180
+ }
181
+
182
+ switch (v) {
183
+ case undefined:
184
+ return 0n;
185
+ case null:
186
+ return (nanHead << 32n) | 2n;
187
+ case true:
188
+ return (nanHead << 32n) | 3n;
189
+ case false:
190
+ return (nanHead << 32n) | 4n;
191
+ }
192
+
193
+ let id = this._ids.get(v);
194
+ if (id === undefined) {
195
+ id = this._idPool.pop();
196
+ if (id === undefined) {
197
+ id = BigInt(this._values.length);
198
+ }
199
+ this._values[id] = v;
200
+ this._goRefCounts[id] = 0;
201
+ this._ids.set(v, id);
202
+ }
203
+ this._goRefCounts[id]++;
204
+ let typeFlag = 1n;
205
+ switch (typeof v) {
206
+ case "string":
207
+ typeFlag = 2n;
208
+ break;
209
+ case "symbol":
210
+ typeFlag = 3n;
211
+ break;
212
+ case "function":
213
+ typeFlag = 4n;
214
+ break;
215
+ }
216
+ return id | ((nanHead | typeFlag) << 32n);
217
+ }
218
+
219
+ const storeValue = (addr, v) => {
220
+ let v_ref = boxValue(v);
221
+ mem().setBigUint64(addr, v_ref, true);
222
+ }
223
+
224
+ const loadSlice = (array, len, cap) => {
225
+ return new Uint8Array(this._inst.exports.memory.buffer, array, len);
226
+ }
227
+
228
+ const loadSliceOfValues = (array, len, cap) => {
229
+ const a = new Array(len);
230
+ for (let i = 0; i < len; i++) {
231
+ a[i] = loadValue(array + i * 8);
232
+ }
233
+ return a;
234
+ }
235
+
236
+ const loadString = (ptr, len) => {
237
+ return decoder.decode(new DataView(this._inst.exports.memory.buffer, ptr, len));
238
+ }
239
+
240
+ const timeOrigin = Date.now() - performance.now();
241
+ this.importObject = {
242
+ wasi_snapshot_preview1: {
243
+ // https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#fd_write
244
+ fd_write: function(fd, iovs_ptr, iovs_len, nwritten_ptr) {
245
+ let nwritten = 0;
246
+ if (fd == 1) {
247
+ for (let iovs_i=0; iovs_i<iovs_len;iovs_i++) {
248
+ let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32
249
+ let ptr = mem().getUint32(iov_ptr + 0, true);
250
+ let len = mem().getUint32(iov_ptr + 4, true);
251
+ nwritten += len;
252
+ for (let i=0; i<len; i++) {
253
+ let c = mem().getUint8(ptr+i);
254
+ if (c == 13) { // CR
255
+ // ignore
256
+ } else if (c == 10) { // LF
257
+ // write line
258
+ let line = decoder.decode(new Uint8Array(logLine));
259
+ logLine = [];
260
+ console.log(line);
261
+ } else {
262
+ logLine.push(c);
263
+ }
264
+ }
265
+ }
266
+ } else {
267
+ console.error('invalid file descriptor:', fd);
268
+ }
269
+ mem().setUint32(nwritten_ptr, nwritten, true);
270
+ return 0;
271
+ },
272
+ fd_close: () => 0, // dummy
273
+ fd_fdstat_get: () => 0, // dummy
274
+ fd_seek: () => 0, // dummy
275
+ proc_exit: (code) => {
276
+ this.exited = true;
277
+ this.exitCode = code;
278
+ this._resolveExitPromise();
279
+ throw wasmExit;
280
+ },
281
+ random_get: (bufPtr, bufLen) => {
282
+ crypto.getRandomValues(loadSlice(bufPtr, bufLen));
283
+ return 0;
284
+ },
285
+ },
286
+ gojs: {
287
+ // func ticks() float64
288
+ "runtime.ticks": () => {
289
+ return timeOrigin + performance.now();
290
+ },
291
+
292
+ // func sleepTicks(timeout float64)
293
+ "runtime.sleepTicks": (timeout) => {
294
+ // Do not sleep, only reactivate scheduler after the given timeout.
295
+ setTimeout(() => {
296
+ if (this.exited) return;
297
+ try {
298
+ this._inst.exports.go_scheduler();
299
+ } catch (e) {
300
+ if (e !== wasmExit) throw e;
301
+ }
302
+ }, timeout);
303
+ },
304
+
305
+ // func finalizeRef(v ref)
306
+ "syscall/js.finalizeRef": (v_ref) => {
307
+ // Note: TinyGo does not support finalizers so this is only called
308
+ // for one specific case, by js.go:jsString. and can/might leak memory.
309
+ const id = v_ref & 0xffffffffn;
310
+ if (this._goRefCounts?.[id] !== undefined) {
311
+ this._goRefCounts[id]--;
312
+ if (this._goRefCounts[id] === 0) {
313
+ const v = this._values[id];
314
+ this._values[id] = null;
315
+ this._ids.delete(v);
316
+ this._idPool.push(id);
317
+ }
318
+ } else {
319
+ console.error("syscall/js.finalizeRef: unknown id", id);
320
+ }
321
+ },
322
+
323
+ // func stringVal(value string) ref
324
+ "syscall/js.stringVal": (value_ptr, value_len) => {
325
+ value_ptr >>>= 0;
326
+ const s = loadString(value_ptr, value_len);
327
+ return boxValue(s);
328
+ },
329
+
330
+ // func valueGet(v ref, p string) ref
331
+ "syscall/js.valueGet": (v_ref, p_ptr, p_len) => {
332
+ let prop = loadString(p_ptr, p_len);
333
+ let v = unboxValue(v_ref);
334
+ let result = Reflect.get(v, prop);
335
+ return boxValue(result);
336
+ },
337
+
338
+ // func valueSet(v ref, p string, x ref)
339
+ "syscall/js.valueSet": (v_ref, p_ptr, p_len, x_ref) => {
340
+ const v = unboxValue(v_ref);
341
+ const p = loadString(p_ptr, p_len);
342
+ const x = unboxValue(x_ref);
343
+ Reflect.set(v, p, x);
344
+ },
345
+
346
+ // func valueDelete(v ref, p string)
347
+ "syscall/js.valueDelete": (v_ref, p_ptr, p_len) => {
348
+ const v = unboxValue(v_ref);
349
+ const p = loadString(p_ptr, p_len);
350
+ Reflect.deleteProperty(v, p);
351
+ },
352
+
353
+ // func valueIndex(v ref, i int) ref
354
+ "syscall/js.valueIndex": (v_ref, i) => {
355
+ return boxValue(Reflect.get(unboxValue(v_ref), i));
356
+ },
357
+
358
+ // valueSetIndex(v ref, i int, x ref)
359
+ "syscall/js.valueSetIndex": (v_ref, i, x_ref) => {
360
+ Reflect.set(unboxValue(v_ref), i, unboxValue(x_ref));
361
+ },
362
+
363
+ // func valueCall(v ref, m string, args []ref) (ref, bool)
364
+ "syscall/js.valueCall": (ret_addr, v_ref, m_ptr, m_len, args_ptr, args_len, args_cap) => {
365
+ const v = unboxValue(v_ref);
366
+ const name = loadString(m_ptr, m_len);
367
+ const args = loadSliceOfValues(args_ptr, args_len, args_cap);
368
+ try {
369
+ const m = Reflect.get(v, name);
370
+ storeValue(ret_addr, Reflect.apply(m, v, args));
371
+ mem().setUint8(ret_addr + 8, 1);
372
+ } catch (err) {
373
+ storeValue(ret_addr, err);
374
+ mem().setUint8(ret_addr + 8, 0);
375
+ }
376
+ },
377
+
378
+ // func valueInvoke(v ref, args []ref) (ref, bool)
379
+ "syscall/js.valueInvoke": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
380
+ try {
381
+ const v = unboxValue(v_ref);
382
+ const args = loadSliceOfValues(args_ptr, args_len, args_cap);
383
+ storeValue(ret_addr, Reflect.apply(v, undefined, args));
384
+ mem().setUint8(ret_addr + 8, 1);
385
+ } catch (err) {
386
+ storeValue(ret_addr, err);
387
+ mem().setUint8(ret_addr + 8, 0);
388
+ }
389
+ },
390
+
391
+ // func valueNew(v ref, args []ref) (ref, bool)
392
+ "syscall/js.valueNew": (ret_addr, v_ref, args_ptr, args_len, args_cap) => {
393
+ const v = unboxValue(v_ref);
394
+ const args = loadSliceOfValues(args_ptr, args_len, args_cap);
395
+ try {
396
+ storeValue(ret_addr, Reflect.construct(v, args));
397
+ mem().setUint8(ret_addr + 8, 1);
398
+ } catch (err) {
399
+ storeValue(ret_addr, err);
400
+ mem().setUint8(ret_addr+ 8, 0);
401
+ }
402
+ },
403
+
404
+ // func valueLength(v ref) int
405
+ "syscall/js.valueLength": (v_ref) => {
406
+ return unboxValue(v_ref).length;
407
+ },
408
+
409
+ // valuePrepareString(v ref) (ref, int)
410
+ "syscall/js.valuePrepareString": (ret_addr, v_ref) => {
411
+ const s = String(unboxValue(v_ref));
412
+ const str = encoder.encode(s);
413
+ storeValue(ret_addr, str);
414
+ mem().setInt32(ret_addr + 8, str.length, true);
415
+ },
416
+
417
+ // valueLoadString(v ref, b []byte)
418
+ "syscall/js.valueLoadString": (v_ref, slice_ptr, slice_len, slice_cap) => {
419
+ const str = unboxValue(v_ref);
420
+ loadSlice(slice_ptr, slice_len, slice_cap).set(str);
421
+ },
422
+
423
+ // func valueInstanceOf(v ref, t ref) bool
424
+ "syscall/js.valueInstanceOf": (v_ref, t_ref) => {
425
+ return unboxValue(v_ref) instanceof unboxValue(t_ref);
426
+ },
427
+
428
+ // func copyBytesToGo(dst []byte, src ref) (int, bool)
429
+ "syscall/js.copyBytesToGo": (ret_addr, dest_addr, dest_len, dest_cap, src_ref) => {
430
+ let num_bytes_copied_addr = ret_addr;
431
+ let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
432
+
433
+ const dst = loadSlice(dest_addr, dest_len);
434
+ const src = unboxValue(src_ref);
435
+ if (!(src instanceof Uint8Array || src instanceof Uint8ClampedArray)) {
436
+ mem().setUint8(returned_status_addr, 0); // Return "not ok" status
437
+ return;
438
+ }
439
+ const toCopy = src.subarray(0, dst.length);
440
+ dst.set(toCopy);
441
+ mem().setUint32(num_bytes_copied_addr, toCopy.length, true);
442
+ mem().setUint8(returned_status_addr, 1); // Return "ok" status
443
+ },
444
+
445
+ // copyBytesToJS(dst ref, src []byte) (int, bool)
446
+ // Originally copied from upstream Go project, then modified:
447
+ // https://github.com/golang/go/blob/3f995c3f3b43033013013e6c7ccc93a9b1411ca9/misc/wasm/wasm_exec.js#L404-L416
448
+ "syscall/js.copyBytesToJS": (ret_addr, dst_ref, src_addr, src_len, src_cap) => {
449
+ let num_bytes_copied_addr = ret_addr;
450
+ let returned_status_addr = ret_addr + 4; // Address of returned boolean status variable
451
+
452
+ const dst = unboxValue(dst_ref);
453
+ const src = loadSlice(src_addr, src_len);
454
+ if (!(dst instanceof Uint8Array || dst instanceof Uint8ClampedArray)) {
455
+ mem().setUint8(returned_status_addr, 0); // Return "not ok" status
456
+ return;
457
+ }
458
+ const toCopy = src.subarray(0, dst.length);
459
+ dst.set(toCopy);
460
+ mem().setUint32(num_bytes_copied_addr, toCopy.length, true);
461
+ mem().setUint8(returned_status_addr, 1); // Return "ok" status
462
+ },
463
+ }
464
+ };
465
+
466
+ // Go 1.20 uses 'env'. Go 1.21 uses 'gojs'.
467
+ // For compatibility, we use both as long as Go 1.20 is supported.
468
+ this.importObject.env = this.importObject.gojs;
469
+ }
470
+
471
+ async run(instance) {
472
+ this._inst = instance;
473
+ this._values = [ // JS values that Go currently has references to, indexed by reference id
474
+ NaN,
475
+ 0,
476
+ null,
477
+ true,
478
+ false,
479
+ global,
480
+ this,
481
+ ];
482
+ this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id
483
+ this._ids = new Map(); // mapping from JS values to reference ids
484
+ this._idPool = []; // unused ids that have been garbage collected
485
+ this.exited = false; // whether the Go program has exited
486
+ this.exitCode = 0;
487
+
488
+ if (this._inst.exports._start) {
489
+ let exitPromise = new Promise((resolve, reject) => {
490
+ this._resolveExitPromise = resolve;
491
+ });
492
+
493
+ // Run program, but catch the wasmExit exception that's thrown
494
+ // to return back here.
495
+ try {
496
+ this._inst.exports._start();
497
+ } catch (e) {
498
+ if (e !== wasmExit) throw e;
499
+ }
500
+
501
+ await exitPromise;
502
+ return this.exitCode;
503
+ } else {
504
+ this._inst.exports._initialize();
505
+ }
506
+ }
507
+
508
+ _resume() {
509
+ if (this.exited) {
510
+ throw new Error("Go program has already exited");
511
+ }
512
+ try {
513
+ this._inst.exports.resume();
514
+ } catch (e) {
515
+ if (e !== wasmExit) throw e;
516
+ }
517
+ if (this.exited) {
518
+ this._resolveExitPromise();
519
+ }
520
+ }
521
+
522
+ _makeFuncWrapper(id) {
523
+ const go = this;
524
+ return function () {
525
+ const event = { id: id, this: this, args: arguments };
526
+ go._pendingEvent = event;
527
+ go._resume();
528
+ return event.result;
529
+ };
530
+ }
531
+ }
532
+
533
+ if (
534
+ global.require &&
535
+ global.require.main === module &&
536
+ global.process &&
537
+ global.process.versions &&
538
+ !global.process.versions.electron
539
+ ) {
540
+ if (process.argv.length != 3) {
541
+ console.error("usage: go_js_wasm_exec [wasm binary] [arguments]");
542
+ process.exit(1);
543
+ }
544
+
545
+ const go = new Go();
546
+ WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then(async (result) => {
547
+ let exitCode = await go.run(result.instance);
548
+ process.exit(exitCode);
549
+ }).catch((err) => {
550
+ console.error(err);
551
+ process.exit(1);
552
+ });
553
+ }
554
+ })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reteps/dockerfmt",
3
- "version": "0.2.0-alpha",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1",