taglib-wasm 1.0.0-beta.7 → 1.0.0-beta.9

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 CHANGED
@@ -12,22 +12,12 @@
12
12
  [![Node.js](https://img.shields.io/badge/Node.js-339933?logo=nodedotjs&logoColor=white)](https://nodejs.org/)
13
13
  [![Bun](https://img.shields.io/badge/Bun-000000?logo=bun&logoColor=white)](https://bun.sh/)
14
14
  [![Cloudflare Workers](https://img.shields.io/badge/Cloudflare%20Workers-F38020?logo=cloudflare&logoColor=white)](https://workers.cloudflare.com/)
15
- [![Electron](https://img.shields.io/badge/Electron-47848F?logo=electron&logoColor=white)](https://www.electronjs.org/)
15
+ [![Electron (Node.js)](https://img.shields.io/badge/Electron%20%28Node.js%29-47848F?logo=electron&logoColor=white)](https://www.electronjs.org/)
16
16
  [![Browsers](https://img.shields.io/badge/Browsers-E34C26?logo=html5&logoColor=white)](https://html.spec.whatwg.org/multipage/)
17
17
 
18
18
  TagLib-Wasm is the **universal tagging library for TypeScript/JavaScript**
19
19
  (TS|JS) platforms: **Deno**, **Node.js**, **Bun**, **Cloudflare Workers**,
20
- **Electron**, and **browsers**.
21
-
22
- This project exists because the TS|JS ecosystem had no battle-tested audio
23
- tagging library that supports reading and writing music metadata to all popular
24
- audio formats. It aspires to be a universal solution for all TS|JS-capable
25
- platforms — Deno, Node.js, Bun, Electron, Cloudflare Workers, and browsers.
26
-
27
- TagLib-Wasm stands on the shoulders of giants, including
28
- [TagLib](https://taglib.org/) itself, [Emscripten](https://emscripten.org/), and
29
- [Wasm](https://webassembly.org/) ([WebAssembly](https://webassembly.org/)).
30
- TagLib itself is legendary, and a core dependency of many music apps.
20
+ **Electron** (via Node.js), and **browsers**.
31
21
 
32
22
  ## Features
33
23
 
@@ -37,12 +27,12 @@ TagLib itself is legendary, and a core dependency of many music apps.
37
27
  (browser) for optimal performance with no configuration
38
28
  - **Full audio format support** – Supports all audio formats supported by TagLib
39
29
  - **TypeScript first** – Complete type definitions and modern API
40
- - **Wide TS/JS runtime support** – Deno, Node.js, Bun, Electron, Cloudflare
41
- Workers, and browsers
42
- - **Format abstraction** – Handles container format details automagically when
30
+ - **Wide TS/JS runtime support** – Deno, Node.js, Bun, Electron (Node.js),
31
+ Cloudflare Workers, and browsers
32
+ - **Format abstraction** – Handles container format details automatically when
43
33
  possible
44
34
  - **Zero dependencies** – Self-contained Wasm bundle
45
- - **Production ready** – Growing test suite helps ensure safety and reliability
35
+ - **Tested** – 135+ tests across all formats
46
36
  - **Two API styles** – Use the "Simple" API (3 functions), or the full "Core"
47
37
  API for more advanced applications
48
38
  - **Batch folder operations** – Scan directories, process multiple files, find
@@ -72,22 +62,23 @@ npm install taglib-wasm
72
62
  bun add taglib-wasm
73
63
  ```
74
64
 
75
- ### Electron
65
+ ### Electron (Node.js)
76
66
 
77
67
  ```bash
78
68
  npm install taglib-wasm
79
69
  ```
80
70
 
81
- Works in both main and renderer processes:
71
+ taglib-wasm works in Electron's main process (which is Node.js). For the
72
+ renderer process, expose metadata through IPC:
82
73
 
83
74
  ```typescript
84
75
  // Main process
85
76
  import { TagLib } from "taglib-wasm";
86
-
87
- // Renderer process (with nodeIntegration: true)
88
- const { TagLib } = require("taglib-wasm");
89
77
  ```
90
78
 
79
+ See [Platform Examples](docs/guide/platform-examples.md#electron) for full IPC
80
+ setup.
81
+
91
82
  ### Deno Compiled Binaries (Offline Support)
92
83
 
93
84
  For Deno compiled binaries that need to work offline, you can embed the WASM
@@ -130,7 +121,7 @@ const taglib = await TagLib.initialize({ wasmBinary });
130
121
  ```typescript
131
122
  import { applyTags, readTags, updateTags } from "taglib-wasm/simple";
132
123
 
133
- // Read tags - just one function call!
124
+ // Read tags
134
125
  const tags = await readTags("song.mp3");
135
126
  console.log(tags.title, tags.artist, tags.album);
136
127
 
@@ -153,7 +144,7 @@ await updateTags("song.mp3", {
153
144
  ```typescript
154
145
  import { readMetadataBatch, readTagsBatch } from "taglib-wasm/simple";
155
146
 
156
- // Process multiple files in parallel - dramatically faster!
147
+ // Process multiple files in parallel
157
148
  const files = ["track01.mp3", "track02.mp3", /* ... */ "track20.mp3"];
158
149
 
159
150
  // Read just tags (18x faster than sequential)
@@ -327,49 +318,21 @@ Supported formats:
327
318
  - **Additional formats** – Opus, APE, MPC, WavPack, TrueAudio, AIFF, WMA, and
328
319
  more
329
320
 
330
- ## Key Features
331
-
332
- ### Extended Metadata Support
333
-
334
- Beyond basic tags, taglib-wasm supports extended metadata:
335
-
336
- ```typescript
337
- import { Tags } from "taglib-wasm";
338
-
339
- // AcoustID fingerprints
340
- file.setProperty(
341
- Tags.AcoustidFingerprint,
342
- "AQADtMmybfGO8NCNEESLnzHyXNOHeHnG...",
343
- );
344
-
345
- // MusicBrainz IDs
346
- file.setProperty(
347
- Tags.MusicBrainzTrackId,
348
- "f4d1b6b8-8c1e-4d9a-9f2a-1234567890ab",
349
- );
350
-
351
- // ReplayGain volume normalization
352
- file.setProperty(Tags.TrackGain, "-6.54 dB");
353
- file.setProperty(Tags.TrackPeak, "0.987654");
354
- ```
355
-
356
- [View all supported tag constants →](https://charleswiltgen.github.io/taglib-wasm/api/tag-constants.html)
357
-
358
321
  ## Performance and Best Practices
359
322
 
360
323
  ### Batch Processing for Multiple Files
361
324
 
362
- When processing multiple audio files, use the optimized batch APIs for dramatic performance improvements:
325
+ When processing multiple audio files, use the optimized batch APIs for better performance:
363
326
 
364
327
  ```typescript
365
328
  import { readMetadataBatch, readTagsBatch } from "taglib-wasm/simple";
366
329
 
367
- // ❌ SLOW: Processing files one by one (can take 90+ seconds for 19 files)
330
+ // Processing files one by one (can take 90+ seconds for 19 files)
368
331
  for (const file of files) {
369
332
  const tags = await readTags(file); // Re-initializes for each file
370
333
  }
371
334
 
372
- // ✅ FAST: Batch processing (10-20x faster)
335
+ // Batch processing (10-20x faster)
373
336
  const result = await readTagsBatch(files, {
374
337
  concurrency: 8, // Process 8 files in parallel
375
338
  onProgress: (processed, total) => {
@@ -377,7 +340,7 @@ const result = await readTagsBatch(files, {
377
340
  },
378
341
  });
379
342
 
380
- // ✅ FASTEST: Read complete metadata in one batch
343
+ // Read complete metadata in one batch
381
344
  const metadata = await readMetadataBatch(files, { concurrency: 8 });
382
345
  ```
383
346
 
@@ -389,7 +352,7 @@ const metadata = await readMetadataBatch(files, { concurrency: 8 });
389
352
 
390
353
  ### Smart Partial Loading
391
354
 
392
- For large audio files (>50MB), enable partial loading to dramatically reduce memory usage:
355
+ For large audio files (>50MB), enable partial loading to reduce memory usage:
393
356
 
394
357
  ```typescript
395
358
  // Enable partial loading for large files
@@ -417,13 +380,13 @@ await file.saveToFile(); // Full file loaded only here
417
380
 
418
381
  taglib-wasm auto-selects the fastest available backend — no configuration needed:
419
382
 
420
- | Environment | Backend | How it works | Performance |
421
- | ---------------------- | ----------------- | ------------------------------------------------------ | -------------- |
422
- | **Deno / Node.js** | WASI (auto) | Seek-based filesystem I/O; reads only headers and tags | Fastest |
423
- | **Browsers / Workers** | Emscripten (auto) | Entire file loaded into memory as buffer | Baseline |
424
- | **Opt-in** | Wasmtime sidecar | Out-of-process WASI with direct filesystem access | Best for batch |
383
+ | Environment | Backend | How it works | Performance |
384
+ | ------------------------ | ----------------- | ------------------------------------------------------ | -------------- |
385
+ | **Deno / Node.js / Bun** | WASI (auto) | Seek-based filesystem I/O; reads only headers and tags | Fastest |
386
+ | **Browsers / Workers** | Emscripten (auto) | Entire file loaded into memory as buffer | Baseline |
387
+ | **Opt-in** | Wasmtime sidecar | Out-of-process WASI with direct filesystem access | Best for batch |
425
388
 
426
- On Deno and Node.js you get WASI automatically — nothing to configure. For
389
+ On Deno, Node.js, and Bun you get WASI automatically — nothing to configure. For
427
390
  heavy batch workloads, the optional Wasmtime sidecar provides direct filesystem
428
391
  access via a sandboxed subprocess:
429
392
 
@@ -448,56 +411,18 @@ See the
448
411
  [Runtime Compatibility Guide](https://charleswiltgen.github.io/taglib-wasm/concepts/runtime-compatibility.html)
449
412
  for full sidecar configuration options.
450
413
 
451
- ### WebAssembly Streaming
452
-
453
- For web applications, use CDN URLs to enable WebAssembly streaming compilation:
454
-
455
- ```typescript
456
- // ✅ FAST: Streaming compilation (200-400ms)
457
- const taglib = await TagLib.initialize({
458
- wasmUrl: "https://cdn.jsdelivr.net/npm/taglib-wasm@latest/dist/taglib.wasm",
459
- });
460
-
461
- // ❌ SLOWER: ArrayBuffer loading (400-800ms)
462
- const wasmBinary = await fetch("taglib.wasm").then((r) => r.arrayBuffer());
463
- const taglib = await TagLib.initialize({ wasmBinary });
464
- ```
465
-
466
- [View complete performance guide →](https://charleswiltgen.github.io/taglib-wasm/concepts/performance.html)
467
-
468
- ## Development
469
-
470
- ### Build from Source
471
-
472
- ```bash
473
- # Prerequisites: Emscripten SDK
474
- # Install via: https://emscripten.org/docs/getting_started/downloads.html
475
-
476
- # Clone and build
477
- git clone https://github.com/CharlesWiltgen/taglib-wasm.git
478
- cd taglib-wasm
479
-
480
- # Build Wasm module
481
- npm run build:wasm
482
-
483
- # Run tests
484
- npm test
485
- ```
486
-
487
- [View full development guide →](CONTRIBUTING.md)
488
-
489
414
  ## Runtime Compatibility
490
415
 
491
416
  `taglib-wasm` works across all major JavaScript runtimes:
492
417
 
493
- | Runtime | Status | Installation | Notes |
494
- | ---------------------- | ------ | ------------------------- | ------------------------- |
495
- | **Deno** | Full | `npm:taglib-wasm` | Native TypeScript |
496
- | **Node.js** | Full | `npm install taglib-wasm` | TypeScript via tsx |
497
- | **Bun** | Full | `bun add taglib-wasm` | Native TypeScript |
498
- | **Browser** | Full | Via bundler | Full API support |
499
- | **Cloudflare Workers** | Full | `taglib-wasm/workers` | Memory-optimized build |
500
- | **Electron** | Full | `npm install taglib-wasm` | Main & renderer processes |
418
+ | Runtime | Status | Installation | Notes |
419
+ | ---------------------- | ------- | ------------------------- | ------------------------------------------------------------------------------------------------------- |
420
+ | **Deno** | Full | `npm:taglib-wasm` | Native TypeScript |
421
+ | **Node.js** | Full | `npm install taglib-wasm` | TypeScript via tsx |
422
+ | **Bun** | Partial | `bun add taglib-wasm` | Import + init verified; full test suite is Deno-only |
423
+ | **Browser** | Full | Via bundler | Full API support |
424
+ | **Cloudflare Workers** | Partial | `taglib-wasm/workers` | Basic tags only; see [Workers limitations](docs/advanced/cloudflare-workers.md#workers-api-limitations) |
425
+ | **Electron** | Node.js | `npm install taglib-wasm` | Main process; renderer via IPC |
501
426
 
502
427
  ## Known Limitations
503
428
 
@@ -1 +1 @@
1
- {"version":3,"file":"file-handle.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/file-handle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,UAAU,EACV,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAGhE,OAAO,KAAK,EAAe,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG3D,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,SAAS,CAAS;gBAEd,UAAU,EAAE,UAAU;IAIlC,OAAO,CAAC,iBAAiB;IAQzB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO;IAQ3C,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAOpC,OAAO,IAAI,OAAO;IAKlB,IAAI,IAAI,OAAO;IAcf,MAAM,IAAI,UAAU;IASpB,OAAO,CAAC,gBAAgB;IAkCxB,kBAAkB,IAAI,sBAAsB;IAe5C,SAAS,IAAI,MAAM;IAgBnB,SAAS,IAAI,UAAU;IAKvB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAKzC,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI;IAKpD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAMhC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAK7C,KAAK,IAAI,OAAO;IAYhB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAK/B,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAK5C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQhC,WAAW,IAAI,OAAO,EAAE;IAKxB,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI;IAKtC,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAOlC,cAAc,IAAI,IAAI;IAKtB,UAAU,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE;IAKlE,UAAU,CACR,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,GAC9D,IAAI;IAaP,OAAO,IAAI,IAAI;CAKhB"}
1
+ {"version":3,"file":"file-handle.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/file-handle.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,UAAU,EACV,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAGhE,OAAO,KAAK,EAAe,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAU3D,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,SAAS,CAAS;gBAEd,UAAU,EAAE,UAAU;IAIlC,OAAO,CAAC,iBAAiB;IAQzB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO;IAQ3C,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAOpC,OAAO,IAAI,OAAO;IAKlB,IAAI,IAAI,OAAO;IAcf,MAAM,IAAI,UAAU;IASpB,OAAO,CAAC,gBAAgB;IAkCxB,kBAAkB,IAAI,sBAAsB;IAgB5C,SAAS,IAAI,MAAM;IAgBnB,SAAS,IAAI,UAAU;IAKvB,aAAa,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAKzC,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI;IAKpD,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAOhC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAM7C,KAAK,IAAI,OAAO;IAYhB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAK/B,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAK5C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAQhC,WAAW,IAAI,OAAO,EAAE;IAKxB,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI;IAKtC,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAOlC,cAAc,IAAI,IAAI;IAKtB,UAAU,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE;IAKlE,UAAU,CACR,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,GAC9D,IAAI;IAaP,OAAO,IAAI,IAAI;CAKhB"}
@@ -4,6 +4,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4
4
  import { WasmerExecutionError } from "../wasmer-sdk-loader/index.js";
5
5
  import { decodeTagData } from "../../msgpack/decoder.js";
6
6
  import { readTagsFromWasm, writeTagsToWasm } from "./wasm-io.js";
7
+ const PROPERTY_KEY_MAP = {
8
+ ALBUMARTIST: "albumArtist",
9
+ COMPOSER: "composer",
10
+ DISCNUMBER: "disc",
11
+ BPM: "bpm"
12
+ };
7
13
  class WasiFileHandle {
8
14
  constructor(wasiModule) {
9
15
  __publicField(this, "wasi");
@@ -89,12 +95,13 @@ class WasiFileHandle {
89
95
  }
90
96
  getAudioProperties() {
91
97
  this.checkNotDestroyed();
98
+ const data = this.tagData;
92
99
  return {
93
- lengthInSeconds: () => 0,
94
- lengthInMilliseconds: () => 0,
95
- bitrate: () => 0,
96
- sampleRate: () => 0,
97
- channels: () => 0,
100
+ lengthInSeconds: () => data?.length ?? 0,
101
+ lengthInMilliseconds: () => data?.lengthMs ?? 0,
102
+ bitrate: () => data?.bitrate ?? 0,
103
+ sampleRate: () => data?.sampleRate ?? 0,
104
+ channels: () => data?.channels ?? 0,
98
105
  bitsPerSample: () => 0,
99
106
  codec: () => "",
100
107
  containerFormat: () => "",
@@ -124,12 +131,14 @@ class WasiFileHandle {
124
131
  }
125
132
  getProperty(key) {
126
133
  this.checkNotDestroyed();
134
+ const mappedKey = PROPERTY_KEY_MAP[key] ?? key;
127
135
  const props = this.tagData;
128
- return props?.[key]?.toString() ?? "";
136
+ return props?.[mappedKey]?.toString() ?? "";
129
137
  }
130
138
  setProperty(key, value) {
131
139
  this.checkNotDestroyed();
132
- this.tagData = { ...this.tagData, [key]: value };
140
+ const mappedKey = PROPERTY_KEY_MAP[key] ?? key;
141
+ this.tagData = { ...this.tagData, [mappedKey]: value };
133
142
  }
134
143
  isMP4() {
135
144
  this.checkNotDestroyed();
@@ -1 +1 @@
1
- {"version":3,"file":"wasm-io.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/wasm-io.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAOhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,GACjB,UAAU,CAuCZ;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,WAAW,GACnB,UAAU,GAAG,IAAI,CAwCnB"}
1
+ {"version":3,"file":"wasm-io.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/wasm-io.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAOhE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,UAAU,GACjB,UAAU,CA2BZ;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,UAAU,EACpB,OAAO,EAAE,WAAW,GACnB,UAAU,GAAG,IAAI,CA8BnB"}
@@ -54,36 +54,25 @@ function readTagsFromWasm(wasi, buffer) {
54
54
  const arena = __using(_stack, new WasmArena(wasi));
55
55
  const inputBuf = arena.allocBuffer(buffer);
56
56
  const outSizePtr = arena.allocUint32();
57
- const sizeResult = wasi.tl_read_tags(
57
+ const resultPtr = wasi.tl_read_tags(
58
58
  0,
59
59
  inputBuf.ptr,
60
60
  inputBuf.size,
61
61
  outSizePtr.ptr
62
62
  );
63
- if (sizeResult !== 0) {
63
+ if (resultPtr === 0) {
64
64
  const errorCode = wasi.tl_get_last_error_code();
65
65
  throw new WasmMemoryError(
66
- `error code ${errorCode}`,
67
- "read tags size",
66
+ `error code ${errorCode}. Buffer size: ${buffer.length} bytes`,
67
+ "read tags",
68
68
  errorCode
69
69
  );
70
70
  }
71
- const outputSize = outSizePtr.readUint32();
72
- const outputBuf = arena.alloc(outputSize);
73
- const readResult = wasi.tl_read_tags(
74
- 0,
75
- inputBuf.ptr,
76
- inputBuf.size,
77
- outputBuf.ptr
78
- );
79
- if (readResult !== 0) {
80
- throw new WasmMemoryError(
81
- "failed to read data into buffer",
82
- "read tags data",
83
- readResult
84
- );
85
- }
86
- return new Uint8Array(outputBuf.read().slice());
71
+ const outSize = outSizePtr.readUint32();
72
+ const u8 = new Uint8Array(wasi.memory.buffer);
73
+ const result = new Uint8Array(u8.slice(resultPtr, resultPtr + outSize));
74
+ wasi.free(resultPtr);
75
+ return result;
87
76
  } catch (_) {
88
77
  var _error = _, _hasError = true;
89
78
  } finally {
@@ -97,32 +86,26 @@ function writeTagsToWasm(wasi, fileData, tagData) {
97
86
  const tagBytes = encodeTagData(tagData);
98
87
  const inputBuf = arena.allocBuffer(fileData);
99
88
  const tagBuf = arena.allocBuffer(tagBytes);
89
+ const outBufPtr = arena.allocUint32();
100
90
  const outSizePtr = arena.allocUint32();
101
- const sizeResult = wasi.tl_write_tags(
91
+ const result = wasi.tl_write_tags(
102
92
  0,
103
93
  inputBuf.ptr,
104
94
  inputBuf.size,
105
95
  tagBuf.ptr,
106
96
  tagBuf.size,
107
- 0,
97
+ outBufPtr.ptr,
108
98
  outSizePtr.ptr
109
99
  );
110
- if (sizeResult !== 0) {
111
- return null;
112
- }
113
- const outputSize = outSizePtr.readUint32();
114
- const outputBuf = arena.alloc(outputSize);
115
- const writeResult = wasi.tl_write_tags(
116
- 0,
117
- inputBuf.ptr,
118
- inputBuf.size,
119
- tagBuf.ptr,
120
- tagBuf.size,
121
- outputBuf.ptr,
122
- outSizePtr.ptr
123
- );
124
- if (writeResult === 0) {
125
- return new Uint8Array(outputBuf.read().slice());
100
+ if (result === 0) {
101
+ const bufferPtr = outBufPtr.readUint32();
102
+ const size = outSizePtr.readUint32();
103
+ if (bufferPtr && size > 0) {
104
+ const u8 = new Uint8Array(wasi.memory.buffer);
105
+ const output = new Uint8Array(u8.slice(bufferPtr, bufferPtr + size));
106
+ wasi.free(bufferPtr);
107
+ return output;
108
+ }
126
109
  }
127
110
  return null;
128
111
  } catch (_) {
@@ -6,5 +6,5 @@ import type { WasiModule } from "./types.ts";
6
6
  * High-level API for reading tags from audio files using WASI
7
7
  * Uses RAII pattern with automatic memory cleanup
8
8
  */
9
- export declare function readTagsWithWasi(audioBuffer: Uint8Array, wasiModule: WasiModule): Promise<Uint8Array>;
9
+ export declare function readTagsWithWasi(audioBuffer: Uint8Array, wasiModule: WasiModule): Uint8Array;
10
10
  //# sourceMappingURL=high-level-api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"high-level-api.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasmer-sdk-loader/high-level-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,UAAU,EACvB,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,UAAU,CAAC,CA2CrB"}
1
+ {"version":3,"file":"high-level-api.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasmer-sdk-loader/high-level-api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,UAAU,EACvB,UAAU,EAAE,UAAU,GACrB,UAAU,CA2BZ"}
@@ -47,42 +47,31 @@ import {
47
47
  WasmArena,
48
48
  WasmMemoryError
49
49
  } from "../wasi-memory.js";
50
- async function readTagsWithWasi(audioBuffer, wasiModule) {
50
+ function readTagsWithWasi(audioBuffer, wasiModule) {
51
51
  var _stack = [];
52
52
  try {
53
53
  const arena = __using(_stack, new WasmArena(wasiModule));
54
54
  const inputBuf = arena.allocBuffer(audioBuffer);
55
55
  const outSizePtr = arena.allocUint32();
56
- const sizeResult = wasiModule.tl_read_tags(
56
+ const resultPtr = wasiModule.tl_read_tags(
57
57
  0,
58
58
  inputBuf.ptr,
59
59
  inputBuf.size,
60
60
  outSizePtr.ptr
61
61
  );
62
- if (sizeResult !== 0) {
62
+ if (resultPtr === 0) {
63
63
  const errorCode = wasiModule.tl_get_last_error_code();
64
64
  throw new WasmMemoryError(
65
- `error code ${errorCode}`,
66
- "read tags size",
65
+ `error code ${errorCode}. Buffer size: ${audioBuffer.length} bytes`,
66
+ "read tags",
67
67
  errorCode
68
68
  );
69
69
  }
70
- const outputSize = outSizePtr.readUint32();
71
- const outputBuf = arena.alloc(outputSize);
72
- const readResult = wasiModule.tl_read_tags(
73
- 0,
74
- inputBuf.ptr,
75
- inputBuf.size,
76
- outputBuf.ptr
77
- );
78
- if (readResult !== 0) {
79
- throw new WasmMemoryError(
80
- "failed to read data into buffer",
81
- "read tags data",
82
- readResult
83
- );
84
- }
85
- return new Uint8Array(outputBuf.read().slice());
70
+ const outSize = outSizePtr.readUint32();
71
+ const u8 = new Uint8Array(wasiModule.memory.buffer);
72
+ const result = new Uint8Array(u8.slice(resultPtr, resultPtr + outSize));
73
+ wasiModule.free(resultPtr);
74
+ return result;
86
75
  } catch (_) {
87
76
  var _error = _, _hasError = true;
88
77
  } finally {
@@ -16,19 +16,19 @@
16
16
  */
17
17
  export interface Tag {
18
18
  /** Track title */
19
- title?: string;
19
+ readonly title?: string;
20
20
  /** Artist name */
21
- artist?: string;
21
+ readonly artist?: string;
22
22
  /** Album name */
23
- album?: string;
23
+ readonly album?: string;
24
24
  /** Comment */
25
- comment?: string;
25
+ readonly comment?: string;
26
26
  /** Genre */
27
- genre?: string;
27
+ readonly genre?: string;
28
28
  /** Year */
29
- year?: number;
29
+ readonly year?: number;
30
30
  /** Track number */
31
- track?: number;
31
+ readonly track?: number;
32
32
  }
33
33
  /**
34
34
  * Extended metadata with format-agnostic field names.
@@ -49,51 +49,51 @@ export interface Tag {
49
49
  */
50
50
  export interface ExtendedTag extends Tag {
51
51
  /** AcoustID fingerprint (Chromaprint) */
52
- acoustidFingerprint?: string;
52
+ readonly acoustidFingerprint?: string;
53
53
  /** AcoustID UUID */
54
- acoustidId?: string;
54
+ readonly acoustidId?: string;
55
55
  /** MusicBrainz Track ID */
56
- musicbrainzTrackId?: string;
56
+ readonly musicbrainzTrackId?: string;
57
57
  /** MusicBrainz Release ID */
58
- musicbrainzReleaseId?: string;
58
+ readonly musicbrainzReleaseId?: string;
59
59
  /** MusicBrainz Artist ID */
60
- musicbrainzArtistId?: string;
60
+ readonly musicbrainzArtistId?: string;
61
61
  /** MusicBrainz Release Group ID */
62
- musicbrainzReleaseGroupId?: string;
62
+ readonly musicbrainzReleaseGroupId?: string;
63
63
  /** Album artist (different from track artist) */
64
- albumArtist?: string;
64
+ readonly albumArtist?: string;
65
65
  /** Composer */
66
- composer?: string;
66
+ readonly composer?: string;
67
67
  /** Disc number */
68
- discNumber?: number;
68
+ readonly discNumber?: number;
69
69
  /** Total tracks on album */
70
- totalTracks?: number;
70
+ readonly totalTracks?: number;
71
71
  /** Total discs in release */
72
- totalDiscs?: number;
72
+ readonly totalDiscs?: number;
73
73
  /** BPM (beats per minute) */
74
- bpm?: number;
74
+ readonly bpm?: number;
75
75
  /** Compilation flag */
76
- compilation?: boolean;
76
+ readonly compilation?: boolean;
77
77
  /** Sort title for alphabetization */
78
- titleSort?: string;
78
+ readonly titleSort?: string;
79
79
  /** Sort artist for alphabetization */
80
- artistSort?: string;
80
+ readonly artistSort?: string;
81
81
  /** Sort album for alphabetization */
82
- albumSort?: string;
82
+ readonly albumSort?: string;
83
83
  /** ReplayGain track gain in dB (e.g., "-6.54 dB") */
84
- replayGainTrackGain?: string;
84
+ readonly replayGainTrackGain?: string;
85
85
  /** ReplayGain track peak value (0.0-1.0) */
86
- replayGainTrackPeak?: string;
86
+ readonly replayGainTrackPeak?: string;
87
87
  /** ReplayGain album gain in dB */
88
- replayGainAlbumGain?: string;
88
+ readonly replayGainAlbumGain?: string;
89
89
  /** ReplayGain album peak value (0.0-1.0) */
90
- replayGainAlbumPeak?: string;
90
+ readonly replayGainAlbumPeak?: string;
91
91
  /** Apple Sound Check normalization data (iTunNORM) */
92
- appleSoundCheck?: string;
92
+ readonly appleSoundCheck?: string;
93
93
  /** Embedded pictures/artwork */
94
- pictures?: import("./pictures.ts").Picture[];
94
+ readonly pictures?: import("./pictures.ts").Picture[];
95
95
  /** Popularity/rating data */
96
- ratings?: Array<{
96
+ readonly ratings?: Array<{
97
97
  rating: number;
98
98
  email: string;
99
99
  counter: number;
@@ -1 +1 @@
1
- {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../../src/types/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,GAAG;IAClB,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,WAAY,SAAQ,GAAG;IACtC,yCAAyC;IACzC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2BAA2B;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,6BAA6B;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,4BAA4B;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mCAAmC;IACnC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4BAA4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IAGnB,qDAAqD;IACrD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kCAAkC;IAClC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAG7B,sDAAsD;IACtD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,OAAO,eAAe,EAAE,OAAO,EAAE,CAAC;IAC7C,6BAA6B;IAC7B,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrE;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACzB;AAED;;GAEG;AACH,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../../src/types/tags.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,GAAG;IAClB,kBAAkB;IAClB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB;IAClB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB;IACjB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc;IACd,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY;IACZ,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW;IACX,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB;IACnB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,WAAY,SAAQ,GAAG;IACtC,yCAAyC;IACzC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,oBAAoB;IACpB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,2BAA2B;IAC3B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,6BAA6B;IAC7B,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,4BAA4B;IAC5B,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,mCAAmC;IACnC,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAC5C,iDAAiD;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,eAAe;IACf,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB;IAClB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,4BAA4B;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6BAA6B;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,6BAA6B;IAC7B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sCAAsC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,qCAAqC;IACrC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAG5B,qDAAqD;IACrD,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,4CAA4C;IAC5C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,kCAAkC;IAClC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,4CAA4C;IAC5C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAGtC,sDAAsD;IACtD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,gCAAgC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,eAAe,EAAE,OAAO,EAAE,CAAC;IACtD,6BAA6B;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC9E;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACzB;AAED;;GAEG;AACH,YAAY,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taglib-wasm",
3
- "version": "1.0.0-beta.7",
3
+ "version": "1.0.0-beta.9",
4
4
  "description": "TagLib-Wasm is the universal tagging library for TypeScript/JavaScript platforms: Browsers, Node.js, Deno, Bun, Cloudflare Workers, and Electron apps",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",