taglib-wasm 1.5.2 → 1.6.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 (51) hide show
  1. package/LICENSE +12 -9
  2. package/README.md +6 -3
  3. package/dist/index.browser.js +255 -85
  4. package/dist/index.d.ts +1 -1
  5. package/dist/simple.browser.js +253 -84
  6. package/dist/src/constants/complex-properties.d.ts +18 -12
  7. package/dist/src/constants/complex-properties.d.ts.map +1 -1
  8. package/dist/src/constants/properties.d.ts +24 -0
  9. package/dist/src/constants/properties.d.ts.map +1 -1
  10. package/dist/src/constants/properties.js +34 -0
  11. package/dist/src/constants/specialized-properties.d.ts +23 -0
  12. package/dist/src/constants/specialized-properties.d.ts.map +1 -1
  13. package/dist/src/constants/specialized-properties.js +20 -0
  14. package/dist/src/constants/tags.d.ts.map +1 -1
  15. package/dist/src/constants/tags.js +2 -1
  16. package/dist/src/deno-compile.d.ts +12 -0
  17. package/dist/src/deno-compile.d.ts.map +1 -1
  18. package/dist/src/deno-compile.js +9 -8
  19. package/dist/src/msgpack/encoder.d.ts.map +1 -1
  20. package/dist/src/msgpack/encoder.js +6 -4
  21. package/dist/src/runtime/detector.d.ts +0 -6
  22. package/dist/src/runtime/detector.d.ts.map +1 -1
  23. package/dist/src/runtime/detector.js +1 -14
  24. package/dist/src/runtime/wasi-adapter/file-handle.d.ts +2 -0
  25. package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
  26. package/dist/src/runtime/wasi-adapter/file-handle.js +61 -52
  27. package/dist/src/taglib/audio-file-base.d.ts.map +1 -1
  28. package/dist/src/taglib/audio-file-base.js +33 -77
  29. package/dist/src/taglib/mp4-item-names.d.ts +12 -0
  30. package/dist/src/taglib/mp4-item-names.d.ts.map +1 -0
  31. package/dist/src/taglib/mp4-item-names.js +4 -0
  32. package/dist/src/taglib/mutable-tag-impl.d.ts +14 -0
  33. package/dist/src/taglib/mutable-tag-impl.d.ts.map +1 -0
  34. package/dist/src/taglib/mutable-tag-impl.js +88 -0
  35. package/dist/src/taglib/save-reconstruct.d.ts.map +1 -1
  36. package/dist/src/taglib/save-reconstruct.js +4 -0
  37. package/dist/src/types/metadata-mappings.d.ts.map +1 -1
  38. package/dist/src/types/metadata-mappings.js +14 -0
  39. package/dist/src/types/tags.d.ts +16 -0
  40. package/dist/src/types/tags.d.ts.map +1 -1
  41. package/dist/src/utils/mirror-fields.d.ts +79 -0
  42. package/dist/src/utils/mirror-fields.d.ts.map +1 -0
  43. package/dist/src/utils/mirror-fields.js +67 -0
  44. package/dist/src/utils/tag-mapping.d.ts.map +1 -1
  45. package/dist/src/utils/tag-mapping.js +47 -5
  46. package/dist/src/version.d.ts +1 -1
  47. package/dist/src/version.js +1 -1
  48. package/dist/taglib-wasi.wasm +0 -0
  49. package/dist/taglib-web.wasm +0 -0
  50. package/dist/taglib-wrapper.js +1 -1
  51. package/package.json +8 -5
@@ -123,6 +123,26 @@ const SPECIALIZED_PROPERTIES = {
123
123
  vorbis: "ITUNNORM",
124
124
  mp4: "----:com.apple.iTunes:iTunNORM"
125
125
  }
126
+ },
127
+ /**
128
+ * Apple gapless-playback data (encoder delay, padding, original sample count).
129
+ *
130
+ * Exists so the two Apple freeform atoms are presented consistently: without
131
+ * it, `properties()` answered `appleSoundCheck` for iTunNORM but the raw
132
+ * uppercased `ITUNSMPB` for its sibling, because only one had an alias. The
133
+ * name mirrors `appleSoundCheck` — vendor-prefixed, feature-named rather than
134
+ * atom-named (tuneup-ibo).
135
+ */
136
+ appleGaplessInfo: {
137
+ key: "ITUNSMPB",
138
+ description: "Apple gapless playback data (encoder delay and padding)",
139
+ type: "string",
140
+ supportedFormats: ["ID3v2", "MP4", "Vorbis"],
141
+ mappings: {
142
+ id3v2: { frame: "TXXX", description: "iTunSMPB" },
143
+ vorbis: "ITUNSMPB",
144
+ mp4: "----:com.apple.iTunes:iTunSMPB"
145
+ }
126
146
  }
127
147
  };
128
148
  export {
@@ -1 +1 @@
1
- {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../../src/constants/tags.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmFP,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,MAAM,OAAO,IAAI,CAAC,CAAC;AAErD;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,OAAO,CAE5D;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,SAAS,OAAO,EAAE,CAEnD"}
1
+ {"version":3,"file":"tags.d.ts","sourceRoot":"","sources":["../../../src/constants/tags.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmFP,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,MAAM,OAAO,IAAI,CAAC,CAAC;AAYrD;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,OAAO,CAE5D;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,SAAS,OAAO,EAAE,CAEnD"}
@@ -72,8 +72,9 @@ const Tags = {
72
72
  InvolvedPeople: "involvedPeople",
73
73
  Encoding: "encoding"
74
74
  };
75
+ const VALID_TAG_NAMES = new Set(Object.values(Tags));
75
76
  function isValidTagName(name) {
76
- return Object.values(Tags).includes(name);
77
+ return VALID_TAG_NAMES.has(name);
77
78
  }
78
79
  function getAllTagNames() {
79
80
  return Object.values(Tags);
@@ -8,6 +8,18 @@
8
8
  */
9
9
  import { TagLib } from "./taglib.js";
10
10
  export { isDenoCompiled } from "./runtime/deno-detect.js";
11
+ /**
12
+ * Where {@link prepareWasmForEmbedding} looks for the Emscripten binary, in
13
+ * order, resolved relative to this module.
14
+ *
15
+ * `build/` comes first deliberately: it holds the committed, authoritative
16
+ * artifact, while `dist/` is gitignored scratch that only the npm chain
17
+ * (`build:copy-wasm`/`postbuild`) refreshes. A stale `dist/` in a working
18
+ * checkout would otherwise silently shadow a freshly built binary. Published
19
+ * packages ship no `build/` (it is absent from package.json's `files`), so
20
+ * resolution there falls through to `dist/` exactly as before.
21
+ */
22
+ export declare const WASM_EMBED_SEARCH_PATHS: readonly string[];
11
23
  /**
12
24
  * Initialize TagLib with automatic handling for Deno compiled binaries.
13
25
  *
@@ -1 +1 @@
1
- {"version":3,"file":"deno-compile.d.ts","sourceRoot":"","sources":["../../src/deno-compile.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,wBAAwB,CAC5C,gBAAgB,SAAsB,GACrC,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,SAAsB,GAC/B,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
1
+ {"version":3,"file":"deno-compile.d.ts","sourceRoot":"","sources":["../../src/deno-compile.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAIpD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,wBAAwB,CAC5C,gBAAgB,SAAsB,GACrC,OAAO,CAAC,MAAM,CAAC,CA8BjB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,SAAsB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAuCf"}
@@ -3,6 +3,11 @@ import { FileOperationError } from "./errors/classes.js";
3
3
  import { isDenoCompiled } from "./runtime/deno-detect.js";
4
4
  import { fileUrlToPath } from "./utils/path.js";
5
5
  import { isDenoCompiled as isDenoCompiled2 } from "./runtime/deno-detect.js";
6
+ const WASM_EMBED_SEARCH_PATHS = [
7
+ "../build/taglib-web.wasm",
8
+ "../dist/taglib-web.wasm",
9
+ "./node_modules/taglib-wasm/dist/taglib-web.wasm"
10
+ ];
6
11
  async function initializeForDenoCompile(embeddedWasmPath = "./taglib-web.wasm") {
7
12
  if (isDenoCompiled()) {
8
13
  const strategies = [
@@ -30,14 +35,9 @@ async function initializeForDenoCompile(embeddedWasmPath = "./taglib-web.wasm")
30
35
  }
31
36
  async function prepareWasmForEmbedding(outputPath = "./taglib-web.wasm") {
32
37
  try {
33
- const possiblePaths = [
34
- new URL("../dist/taglib-web.wasm", import.meta.url),
35
- new URL("../taglib-web.wasm", import.meta.url),
36
- new URL(
37
- "./node_modules/taglib-wasm/dist/taglib-web.wasm",
38
- import.meta.url
39
- )
40
- ];
38
+ const possiblePaths = WASM_EMBED_SEARCH_PATHS.map(
39
+ (p) => new URL(p, import.meta.url)
40
+ );
41
41
  let wasmData = null;
42
42
  let sourcePath = null;
43
43
  for (const path of possiblePaths) {
@@ -68,6 +68,7 @@ async function prepareWasmForEmbedding(outputPath = "./taglib-web.wasm") {
68
68
  }
69
69
  }
70
70
  export {
71
+ WASM_EMBED_SEARCH_PATHS,
71
72
  initializeForDenoCompile,
72
73
  isDenoCompiled2 as isDenoCompiled,
73
74
  prepareWasmForEmbedding
@@ -1 +1 @@
1
- {"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../../../src/msgpack/encoder.ts"],"names":[],"mappings":"AAAA,qFAAqF;AAErF,OAAO,EAAU,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE/D,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EACX,OAAO,EACP,WAAW,EACZ,MAAM,aAAa,CAAC;AAGrB;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,aAW3B,CAAC;AA4BH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAsC9D;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,eAAe,GAAG,UAAU,CAS7E;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAStE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAe1D;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,UAAU,CAelE;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,IAAI,EAAE,CAAC,EACP,OAAO,GAAE,OAAO,CAAC,cAAc,CAAM,GACpC,UAAU,CAUZ;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,UAAU,CAe/D;AAqBD,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,UAAU,CAc1E;AAED,wBAAiB,uBAAuB,CAAC,CAAC,EACxC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,GACxB,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,CActC;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAS7D;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,GAC1E,UAAU,CAeZ;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAW7D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG;IACxD,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAaA"}
1
+ {"version":3,"file":"encoder.d.ts","sourceRoot":"","sources":["../../../src/msgpack/encoder.ts"],"names":[],"mappings":"AAAA,qFAAqF;AAErF,OAAO,EAAU,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE/D,OAAO,KAAK,EACV,eAAe,EACf,WAAW,EACX,OAAO,EACP,WAAW,EACZ,MAAM,aAAa,CAAC;AAIrB;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,aAa3B,CAAC;AA4BH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAoC9D;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,eAAe,GAAG,UAAU,CAS7E;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,WAAW,GAAG,UAAU,CAStE;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAe1D;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,UAAU,CAelE;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,IAAI,EAAE,CAAC,EACP,OAAO,GAAE,OAAO,CAAC,cAAc,CAAM,GACpC,UAAU,CAUZ;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,UAAU,CAe/D;AAqBD,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,WAAW,EAAE,GAAG,UAAU,CAc1E;AAED,wBAAiB,uBAAuB,CAAC,CAAC,EACxC,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,GACxB,SAAS,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,CActC;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAS7D;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,GAC1E,UAAU,CAeZ;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAW7D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG;IACxD,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAaA"}
@@ -1,6 +1,7 @@
1
1
  import { encode } from "@msgpack/msgpack";
2
2
  import { errorMessage, MetadataError } from "../errors/classes.js";
3
3
  import { toTagLibKey } from "../constants/properties.js";
4
+ import { isShadowedNumericMirror } from "../utils/mirror-fields.js";
4
5
  const PASSTHROUGH_KEYS = /* @__PURE__ */ new Set([
5
6
  "pictures",
6
7
  "ratings",
@@ -11,7 +12,9 @@ const PASSTHROUGH_KEYS = /* @__PURE__ */ new Set([
11
12
  "bextData",
12
13
  "ixml",
13
14
  "id3Tags",
14
- "_stripId3"
15
+ "_stripId3",
16
+ // Exact MP4 atom names — must reach C++ verbatim, not via toTagLibKey.
17
+ "_mp4ItemNames"
15
18
  ]);
16
19
  const MSGPACK_ENCODE_OPTIONS = {
17
20
  sortKeys: false,
@@ -29,11 +32,10 @@ function lyricsTexts(value) {
29
32
  }
30
33
  function encodeTagData(tagData) {
31
34
  try {
32
- const dateVal = tagData.date;
33
- const hasDate = Array.isArray(dateVal) ? dateVal.length > 0 : dateVal !== void 0 && dateVal !== null && dateVal !== "";
35
+ const data = tagData;
34
36
  const remapped = {};
35
37
  for (const [key, value] of Object.entries(tagData)) {
36
- if (key === "year" && hasDate) continue;
38
+ if (isShadowedNumericMirror(data, key)) continue;
37
39
  if (key === "lyrics") {
38
40
  const texts = lyricsTexts(value);
39
41
  if (texts.length > 0) remapped["LYRICS"] = texts;
@@ -23,10 +23,4 @@ export declare function detectRuntime(): RuntimeDetectionResult;
23
23
  export declare function supportsExnref(): boolean;
24
24
  export declare function getEnvironmentDescription(env: RuntimeEnvironment): string;
25
25
  export declare function canLoadWasmType(wasmType: WasmBinaryType): boolean;
26
- /** @internal */
27
- export declare function _forceRuntime(result: RuntimeDetectionResult): void;
28
- /** @internal */
29
- export declare function _clearRuntimeOverride(): void;
30
- /** @internal */
31
- export declare function _getDetectionResult(): RuntimeDetectionResult;
32
26
  //# sourceMappingURL=detector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../../src/runtime/detector.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,WAAW,GACX,UAAU,GACV,SAAS,GACT,iBAAiB,GACjB,QAAQ,GACR,YAAY,CAAC;AAEjB,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,YAAY,CAAC;AAEnD,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,kBAAkB,CAAC;IAChC,QAAQ,EAAE,cAAc,CAAC;IACzB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,eAAe,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAC5B;AAOD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,MAAM,GAAG,SAAS,CAepB;AAuCD,qFAAqF;AACrF,wBAAgB,MAAM,IAAI,OAAO,CAEhC;AAED,wBAAgB,aAAa,IAAI,sBAAsB,CA8EtD;AAWD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAMxC;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,kBAAkB,GAAG,MAAM,CAmBzE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAQjE;AAED,gBAAgB;AAChB,wBAAgB,aAAa,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI,CAElE;AAED,gBAAgB;AAChB,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED,gBAAgB;AAChB,wBAAgB,mBAAmB,IAAI,sBAAsB,CAG5D"}
1
+ {"version":3,"file":"detector.d.ts","sourceRoot":"","sources":["../../../src/runtime/detector.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAC1B,WAAW,GACX,WAAW,GACX,UAAU,GACV,SAAS,GACT,iBAAiB,GACjB,QAAQ,GACR,YAAY,CAAC;AAEjB,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,YAAY,CAAC;AAEnD,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,kBAAkB,CAAC;IAChC,QAAQ,EAAE,cAAc,CAAC;IACzB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,eAAe,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;CAC5B;AAOD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,MAAM,GAAG,SAAS,CAepB;AAuCD,qFAAqF;AACrF,wBAAgB,MAAM,IAAI,OAAO,CAEhC;AAED,wBAAgB,aAAa,IAAI,sBAAsB,CA8EtD;AAWD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAMxC;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,kBAAkB,GAAG,MAAM,CAmBzE;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAQjE"}
@@ -145,7 +145,7 @@ function getEnvironmentDescription(env) {
145
145
  case "node-wasi":
146
146
  return "Node.js with WASI (high performance)";
147
147
  case "bun-wasi":
148
- return "Bun with WASI (via node:fs)";
148
+ return "Bun with WASI (Bun-native file I/O)";
149
149
  case "browser":
150
150
  return "Browser with Emscripten (web compatibility)";
151
151
  case "worker":
@@ -165,20 +165,7 @@ function canLoadWasmType(wasmType) {
165
165
  }
166
166
  return true;
167
167
  }
168
- function _forceRuntime(result) {
169
- globalThis.__taglib_wasm_runtime_override = result;
170
- }
171
- function _clearRuntimeOverride() {
172
- delete globalThis.__taglib_wasm_runtime_override;
173
- }
174
- function _getDetectionResult() {
175
- const override = globalThis.__taglib_wasm_runtime_override;
176
- return override || detectRuntime();
177
- }
178
168
  export {
179
- _clearRuntimeOverride,
180
- _forceRuntime,
181
- _getDetectionResult,
182
169
  canLoadWasmType,
183
170
  checkNodeVersion,
184
171
  detectRuntime,
@@ -30,6 +30,8 @@ export declare class WasiFileHandle implements FileHandle {
30
30
  isMP4(): boolean;
31
31
  getMP4Item(key: string): string;
32
32
  setMP4Item(key: string, value: string): void;
33
+ /** Record an exact atom name to be repaired after the PropertyMap write. */
34
+ private registerMp4ItemName;
33
35
  removeMP4Item(key: string): void;
34
36
  getPictures(): RawPicture[];
35
37
  setPictures(pictures: RawPicture[]): void;
@@ -1 +1 @@
1
- {"version":3,"file":"file-handle.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/file-handle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-handle.d.ts","sourceRoot":"","sources":["../../../../src/runtime/wasi-adapter/file-handle.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,21 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import { WasmerExecutionError } from "../wasmer-sdk-loader/types.js";
5
5
  import { decodeTagData } from "../../msgpack/decoder.js";
6
- import { fromTagLibKey, toTagLibKey } from "../../constants/properties.js";
6
+ import {
7
+ fromTagLibKey,
8
+ mp4AtomWireKey,
9
+ toTagLibKey
10
+ } from "../../constants/properties.js";
11
+ import {
12
+ DATE_MIRROR,
13
+ firstValueString,
14
+ isShadowedNumericMirror,
15
+ mirrorForRawKey,
16
+ readNumericMirror,
17
+ stageNumericWrite,
18
+ stageRawWrite,
19
+ TRACK_MIRROR
20
+ } from "../../utils/mirror-fields.js";
7
21
  import {
8
22
  readId3v2FramesFromWasm,
9
23
  readTagsFromWasm,
@@ -36,7 +50,9 @@ const INTERNAL_KEYS = /* @__PURE__ */ new Set([
36
50
  "chapters",
37
51
  "_mp4ChapterStyle",
38
52
  "bextData",
39
- "ixml"
53
+ "ixml",
54
+ // Exact MP4 atom names for the write path, not a readable property.
55
+ "_mp4ItemNames"
40
56
  ]);
41
57
  const CONTAINER_TO_FORMAT = {
42
58
  MP3: "MP3",
@@ -50,22 +66,11 @@ const CONTAINER_TO_FORMAT = {
50
66
  ASF: "ASF",
51
67
  Matroska: "MATROSKA"
52
68
  };
53
- const NUMERIC_FIELD_ALIASES = {
54
- trackNumber: "track"
55
- };
56
- function hasValue(v) {
57
- if (Array.isArray(v)) return v.length > 0;
58
- return v !== void 0 && v !== null && v !== "";
59
- }
60
- function firstString(v) {
61
- if (Array.isArray(v)) return v[0] ?? "";
62
- return v || "";
63
- }
64
69
  function mp4ItemPropertyKey(key) {
65
70
  if (key.startsWith("----:")) {
66
71
  return key.slice(key.lastIndexOf(":") + 1).toUpperCase();
67
72
  }
68
- return key;
73
+ return mp4AtomWireKey(key) ?? key;
69
74
  }
70
75
  class WasiFileHandle {
71
76
  constructor(wasiModule) {
@@ -123,21 +128,23 @@ class WasiFileHandle {
123
128
  this.checkNotDestroyed();
124
129
  const d = this.tagData ?? {};
125
130
  return {
126
- title: firstString(d.title),
127
- artist: firstString(d.artist),
128
- album: firstString(d.album),
129
- comment: firstString(d.comment),
130
- genre: firstString(d.genre),
131
- year: d.year || 0,
132
- track: d.track || 0
131
+ title: firstValueString(d.title),
132
+ artist: firstValueString(d.artist),
133
+ album: firstValueString(d.album),
134
+ comment: firstValueString(d.comment),
135
+ genre: firstValueString(d.genre),
136
+ year: readNumericMirror(d, DATE_MIRROR),
137
+ track: readNumericMirror(d, TRACK_MIRROR)
133
138
  };
134
139
  }
135
140
  setTagData(data) {
136
141
  this.checkNotDestroyed();
137
142
  const merged = { ...this.tagData, ...data };
138
143
  if (data.year !== void 0) {
139
- if (data.year > 0) merged.date = String(data.year);
140
- else delete merged.date;
144
+ stageNumericWrite(merged, DATE_MIRROR, data.year);
145
+ }
146
+ if (data.track !== void 0) {
147
+ stageNumericWrite(merged, TRACK_MIRROR, data.track);
141
148
  }
142
149
  this.tagData = merged;
143
150
  }
@@ -211,7 +218,7 @@ class WasiFileHandle {
211
218
  const data = this.tagData ?? {};
212
219
  for (const [key, value] of Object.entries(data)) {
213
220
  if (AUDIO_KEYS.has(key) || INTERNAL_KEYS.has(key)) continue;
214
- if (key === "year" && hasValue(data.date)) continue;
221
+ if (isShadowedNumericMirror(data, key)) continue;
215
222
  if (value === void 0 || value === null) continue;
216
223
  if (value === 0 || value === "") continue;
217
224
  const propKey = toTagLibKey(key);
@@ -230,17 +237,16 @@ class WasiFileHandle {
230
237
  const next = { ...this.tagData };
231
238
  for (const [key, values] of Object.entries(props)) {
232
239
  const camelKey = fromTagLibKey(key);
233
- const storeKey = NUMERIC_FIELD_ALIASES[camelKey] ?? camelKey;
234
- if (values.length === 0) {
235
- delete next[storeKey];
236
- if (camelKey === "date") delete next.year;
237
- } else if (storeKey === "track") {
238
- const parsed = Number.parseInt(values[0] ?? "", 10);
239
- if (!Number.isNaN(parsed)) next[storeKey] = parsed;
240
- } else if (camelKey === "date") {
241
- next.date = values;
242
- const year = Number.parseInt(values[0] ?? "", 10);
243
- if (!Number.isNaN(year)) next.year = year;
240
+ if (camelKey === "_mp4ItemNames") {
241
+ const existing = next._mp4ItemNames ?? [];
242
+ next._mp4ItemNames = [.../* @__PURE__ */ new Set([...existing, ...values])];
243
+ continue;
244
+ }
245
+ const mirror = mirrorForRawKey(camelKey);
246
+ if (mirror !== void 0) {
247
+ stageRawWrite(next, mirror, values);
248
+ } else if (values.length === 0) {
249
+ delete next[camelKey];
244
250
  } else {
245
251
  next[camelKey] = values;
246
252
  }
@@ -250,25 +256,17 @@ class WasiFileHandle {
250
256
  getProperty(key) {
251
257
  this.checkNotDestroyed();
252
258
  const mappedKey = fromTagLibKey(key);
253
- const storeKey = NUMERIC_FIELD_ALIASES[mappedKey] ?? mappedKey;
254
- return this.tagData?.[storeKey]?.toString() ?? "";
259
+ const stored = this.tagData?.[mappedKey];
260
+ return Array.isArray(stored) ? stored[0]?.toString() ?? "" : stored?.toString() ?? "";
255
261
  }
256
262
  setProperty(key, value) {
257
263
  this.checkNotDestroyed();
258
264
  const mappedKey = fromTagLibKey(key);
259
- const storeKey = NUMERIC_FIELD_ALIASES[mappedKey] ?? mappedKey;
260
- if (storeKey === "track") {
261
- const parsed = Number.parseInt(value, 10);
262
- if (!Number.isNaN(parsed)) {
263
- this.tagData = { ...this.tagData, [storeKey]: parsed };
264
- }
265
- } else if (mappedKey === "date") {
266
- const year = Number.parseInt(value, 10);
267
- this.tagData = {
268
- ...this.tagData,
269
- date: value,
270
- ...Number.isNaN(year) ? {} : { year }
271
- };
265
+ const mirror = mirrorForRawKey(mappedKey);
266
+ if (mirror !== void 0) {
267
+ const next = { ...this.tagData };
268
+ stageRawWrite(next, mirror, value === "" ? [] : [value]);
269
+ this.tagData = next;
272
270
  } else {
273
271
  this.tagData = { ...this.tagData, [mappedKey]: value };
274
272
  }
@@ -289,13 +287,24 @@ class WasiFileHandle {
289
287
  setMP4Item(key, value) {
290
288
  this.checkNotDestroyed();
291
289
  this.setProperty(mp4ItemPropertyKey(key), value);
290
+ if (key.startsWith("----:")) this.registerMp4ItemName(key);
291
+ }
292
+ /** Record an exact atom name to be repaired after the PropertyMap write. */
293
+ registerMp4ItemName(name) {
294
+ const existing = this.tagData?._mp4ItemNames ?? [];
295
+ if (existing.includes(name)) return;
296
+ this.tagData = {
297
+ ...this.tagData,
298
+ _mp4ItemNames: [...existing, name]
299
+ };
292
300
  }
293
301
  removeMP4Item(key) {
294
302
  this.checkNotDestroyed();
295
303
  if (this.tagData) {
296
304
  const mappedKey = fromTagLibKey(mp4ItemPropertyKey(key));
297
- const storeKey = NUMERIC_FIELD_ALIASES[mappedKey] ?? mappedKey;
298
- delete this.tagData[storeKey];
305
+ delete this.tagData[mappedKey];
306
+ const mirror = mirrorForRawKey(mappedKey);
307
+ if (mirror !== void 0) delete this.tagData[mirror.numeric];
299
308
  }
300
309
  }
301
310
  getPictures() {
@@ -1 +1 @@
1
- {"version":3,"file":"audio-file-base.d.ts","sourceRoot":"","sources":["../../../src/taglib/audio-file-base.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EACV,eAAe,EACf,QAAQ,EACR,WAAW,EACX,WAAW,EACZ,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAchE;;;;;GAKG;AACH,8BAAsB,iBAAiB;IAWnC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY;IAVzC,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IACxC,SAAS,CAAC,qBAAqB,EAAE,eAAe,GAAG,IAAI,CAAQ;IAC/D,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IACvC,SAAS,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,IAAI,CAAC;IACpE,SAAS,CAAC,iBAAiB,EAAE,OAAO,CAAS;IAC7C,SAAS,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC;IACpD,gFAAgF;IAChF,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;gBAGtC,MAAM,EAAE,YAAY,EACvC,UAAU,EAAE,UAAU,EACtB,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,IAAI,EACzD,iBAAiB,GAAE,OAAe,EAClC,kBAAkB,CAAC,EAAE,WAAW;IAYlC;;;;;;OAMG;IACH,SAAS,CAAC,0BAA0B,IAAI,WAAW,CAAC,MAAM,CAAC;IAU3D,SAAS,KAAK,MAAM,IAAI,UAAU,CAKjC;IAED,SAAS,IAAI,QAAQ;IAIrB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,IAAI,cAAc,CAAC,CAAC,CAAC;IAIlE,GAAG,IAAI,UAAU;IA4EjB,eAAe,IAAI,eAAe,GAAG,SAAS;IAO9C,UAAU,IAAI,WAAW;IAMzB,aAAa,CAAC,UAAU,EAAE,WAAW,GAAG,IAAI;IAiB5C,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAK5C,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAI7C,KAAK,IAAI,OAAO;IAIhB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQ3C,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAO5C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAOhC,OAAO,IAAI,OAAO;IAIlB,OAAO,IAAI,IAAI;IAQf,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;CAGzB"}
1
+ {"version":3,"file":"audio-file-base.d.ts","sourceRoot":"","sources":["../../../src/taglib/audio-file-base.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EACV,eAAe,EACf,QAAQ,EACR,WAAW,EACX,WAAW,EACZ,MAAM,aAAa,CAAC;AAQrB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAgBhE;;;;;GAKG;AACH,8BAAsB,iBAAiB;IAWnC,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,YAAY;IAVzC,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IACxC,SAAS,CAAC,qBAAqB,EAAE,eAAe,GAAG,IAAI,CAAQ;IAC/D,SAAS,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IACvC,SAAS,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,IAAI,CAAC;IACpE,SAAS,CAAC,iBAAiB,EAAE,OAAO,CAAS;IAC7C,SAAS,CAAC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC;IACpD,gFAAgF;IAChF,SAAS,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;gBAGtC,MAAM,EAAE,YAAY,EACvC,UAAU,EAAE,UAAU,EACtB,UAAU,CAAC,EAAE,MAAM,EACnB,cAAc,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,IAAI,EACzD,iBAAiB,GAAE,OAAe,EAClC,kBAAkB,CAAC,EAAE,WAAW;IAYlC;;;;;;OAMG;IACH,SAAS,CAAC,0BAA0B,IAAI,WAAW,CAAC,MAAM,CAAC;IAU3D,SAAS,KAAK,MAAM,IAAI,UAAU,CAKjC;IAED,SAAS,IAAI,QAAQ;IAIrB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,IAAI,cAAc,CAAC,CAAC,CAAC;IAIlE,GAAG,IAAI,UAAU;IAIjB,eAAe,IAAI,eAAe,GAAG,SAAS;IAO9C,UAAU,IAAI,WAAW;IAMzB,aAAa,CAAC,UAAU,EAAE,WAAW,GAAG,IAAI;IAsB5C,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAkB5C,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAkB7C,KAAK,IAAI,OAAO;IAIhB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAe3C,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAoB5C,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAOhC,OAAO,IAAI,OAAO;IAIlB,OAAO,IAAI,IAAI;IAQf,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;CAGzB"}
@@ -1,8 +1,15 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { remapKeysFromTagLib, toTagLibKey } from "../constants/properties.js";
4
+ import {
5
+ mp4AtomWireKey,
6
+ mp4FreeformAtomNames,
7
+ remapKeysFromTagLib,
8
+ toTagLibKey
9
+ } from "../constants/properties.js";
5
10
  import { MetadataError, UnsupportedFormatError } from "../errors.js";
11
+ import { MP4_ITEM_NAMES_KEY } from "./mp4-item-names.js";
12
+ import { buildMutableTag } from "./mutable-tag-impl.js";
6
13
  const LYRICS_PROPERTY_KEY = "lyrics";
7
14
  const LYRICS_WIRE_KEY = toTagLibKey(LYRICS_PROPERTY_KEY);
8
15
  const EMPTY_KEY_SET = /* @__PURE__ */ new Set();
@@ -53,79 +60,7 @@ class BaseAudioFileImpl {
53
60
  return this.getFormat() === format;
54
61
  }
55
62
  tag() {
56
- const handle = this.handle;
57
- let data = handle.getTagData();
58
- const tag = {
59
- get title() {
60
- return data.title;
61
- },
62
- get artist() {
63
- return data.artist;
64
- },
65
- get album() {
66
- return data.album;
67
- },
68
- get comment() {
69
- return data.comment;
70
- },
71
- get genre() {
72
- return data.genre;
73
- },
74
- get year() {
75
- return data.year;
76
- },
77
- get track() {
78
- return data.track;
79
- },
80
- get date() {
81
- return handle.getProperty("DATE") || void 0;
82
- },
83
- setTitle: (value) => {
84
- handle.setTagData({ title: value });
85
- data = handle.getTagData();
86
- return tag;
87
- },
88
- setArtist: (value) => {
89
- handle.setTagData({ artist: value });
90
- data = handle.getTagData();
91
- return tag;
92
- },
93
- setAlbum: (value) => {
94
- handle.setTagData({ album: value });
95
- data = handle.getTagData();
96
- return tag;
97
- },
98
- setComment: (value) => {
99
- handle.setTagData({ comment: value });
100
- data = handle.getTagData();
101
- return tag;
102
- },
103
- setGenre: (value) => {
104
- handle.setTagData({ genre: value });
105
- data = handle.getTagData();
106
- return tag;
107
- },
108
- setYear: (value) => {
109
- handle.setTagData({ year: value });
110
- data = handle.getTagData();
111
- return tag;
112
- },
113
- setTrack: (value) => {
114
- handle.setTagData({ track: value });
115
- data = handle.getTagData();
116
- return tag;
117
- },
118
- setDate: (value) => {
119
- if (value === "") {
120
- handle.setTagData({ year: 0 });
121
- } else {
122
- handle.setProperty("DATE", value);
123
- }
124
- data = handle.getTagData();
125
- return tag;
126
- }
127
- };
128
- return tag;
63
+ return buildMutableTag(this.handle);
129
64
  }
130
65
  audioProperties() {
131
66
  if (!this.cachedAudioProperties) {
@@ -147,14 +82,29 @@ class BaseAudioFileImpl {
147
82
  const existing = this.handle.getProperties()[LYRICS_WIRE_KEY];
148
83
  if (existing !== void 0) translated[LYRICS_WIRE_KEY] = existing;
149
84
  }
85
+ const atomNames = mp4FreeformAtomNames(Object.keys(translated));
86
+ if (atomNames.length > 0) translated[MP4_ITEM_NAMES_KEY] = atomNames;
150
87
  this.handle.setProperties(translated);
151
88
  }
152
89
  getProperty(key) {
153
90
  const value = this.handle.getProperty(toTagLibKey(key));
154
- return value === "" ? void 0 : value;
91
+ if (value !== "") return value;
92
+ if (!this.isMP4()) return void 0;
93
+ const remapped = this.properties()[key]?.[0];
94
+ return remapped === "" ? void 0 : remapped;
155
95
  }
156
96
  setProperty(key, value) {
157
- this.handle.setProperty(toTagLibKey(key), value);
97
+ const wireKey = toTagLibKey(key);
98
+ const atomNames = mp4FreeformAtomNames([wireKey]);
99
+ if (atomNames.length > 0 && this.isMP4()) {
100
+ this.handle.setProperties({
101
+ ...this.handle.getProperties(),
102
+ [wireKey]: [value],
103
+ [MP4_ITEM_NAMES_KEY]: atomNames
104
+ });
105
+ return;
106
+ }
107
+ this.handle.setProperty(wireKey, value);
158
108
  }
159
109
  isMP4() {
160
110
  return this.handle.isMP4();
@@ -163,13 +113,19 @@ class BaseAudioFileImpl {
163
113
  if (!this.isMP4()) {
164
114
  throw new UnsupportedFormatError(this.getFormat(), ["MP4", "M4A"]);
165
115
  }
166
- const value = this.handle.getMP4Item(key);
116
+ const wireKey = mp4AtomWireKey(key);
117
+ const value = wireKey !== void 0 ? this.handle.getProperty(wireKey) : this.handle.getMP4Item(key);
167
118
  return value === "" ? void 0 : value;
168
119
  }
169
120
  setMP4Item(key, value) {
170
121
  if (!this.isMP4()) {
171
122
  throw new UnsupportedFormatError(this.getFormat(), ["MP4", "M4A"]);
172
123
  }
124
+ const wireKey = mp4AtomWireKey(key);
125
+ if (wireKey !== void 0) {
126
+ this.handle.setProperty(wireKey, value);
127
+ return;
128
+ }
173
129
  this.handle.setMP4Item(key, value);
174
130
  }
175
131
  removeMP4Item(key) {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Reserved property-map key carrying exact MP4 atom names to the C++ write path.
3
+ *
4
+ * NOT a property and NOT handle state: it is a write-time directive recomputed at
5
+ * each write site from the PROPERTIES table, so it needs no entry in the
6
+ * extra-state reconstruct registry — but every write site that builds a property
7
+ * map must add it, or MP4 freeform atoms get TagLib's upper-cased name
8
+ * (taglib-bnhl). Sites: `BaseAudioFileImpl.setProperties`/`setProperty` and the
9
+ * `saveToFile` reconstruct in `save-reconstruct.ts`.
10
+ */
11
+ export declare const MP4_ITEM_NAMES_KEY = "_mp4ItemNames";
12
+ //# sourceMappingURL=mp4-item-names.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mp4-item-names.d.ts","sourceRoot":"","sources":["../../../src/taglib/mp4-item-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,kBAAkB,CAAC"}
@@ -0,0 +1,4 @@
1
+ const MP4_ITEM_NAMES_KEY = "_mp4ItemNames";
2
+ export {
3
+ MP4_ITEM_NAMES_KEY
4
+ };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @fileoverview The mutable `tag()` facade over a FileHandle.
3
+ *
4
+ * Extracted from audio-file-base.ts, which the additions for taglib-qpl/bnhl/eq3
5
+ * pushed past CLAUDE.md's 250-line limit. This is the whole of the basic-tag
6
+ * surface: five plain string fields plus the two that are backed by a raw
7
+ * string with a numeric mirror (`date`/`year`, `trackNumber`/`track`), whose
8
+ * write rules are the interesting part.
9
+ */
10
+ import type { FileHandle } from "../wasm.js";
11
+ import type { MutableTag } from "./mutable-tag.js";
12
+ /** Build the mutable tag facade. Reads re-snapshot after every write. */
13
+ export declare function buildMutableTag(handle: FileHandle): MutableTag;
14
+ //# sourceMappingURL=mutable-tag-impl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutable-tag-impl.d.ts","sourceRoot":"","sources":["../../../src/taglib/mutable-tag-impl.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAMnD,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CA2F9D"}