musicxml-io 0.2.5 → 0.2.7

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.
@@ -1,3 +1,9 @@
1
+ // src/id.ts
2
+ import { nanoid } from "nanoid";
3
+ function generateId() {
4
+ return "i" + nanoid(10);
5
+ }
6
+
1
7
  // src/utils/index.ts
2
8
  var STEPS = ["C", "D", "E", "F", "G", "A", "B"];
3
9
  var STEP_SEMITONES = {
@@ -1074,6 +1080,34 @@ function operationError(code, message, location = {}, details) {
1074
1080
  function cloneScore(score) {
1075
1081
  return JSON.parse(JSON.stringify(score));
1076
1082
  }
1083
+ function cloneNoteWithNewId(note) {
1084
+ const cloned = JSON.parse(JSON.stringify(note));
1085
+ cloned._id = generateId();
1086
+ return cloned;
1087
+ }
1088
+ function cloneEntryWithNewId(entry) {
1089
+ const cloned = JSON.parse(JSON.stringify(entry));
1090
+ cloned._id = generateId();
1091
+ return cloned;
1092
+ }
1093
+ function cloneMeasureWithNewIds(measure) {
1094
+ const cloned = JSON.parse(JSON.stringify(measure));
1095
+ cloned._id = generateId();
1096
+ cloned.entries = cloned.entries.map((entry) => cloneEntryWithNewId(entry));
1097
+ if (cloned.barlines) {
1098
+ cloned.barlines = cloned.barlines.map((barline) => ({
1099
+ ...barline,
1100
+ _id: generateId()
1101
+ }));
1102
+ }
1103
+ return cloned;
1104
+ }
1105
+ function clonePartWithNewIds(part) {
1106
+ const cloned = JSON.parse(JSON.stringify(part));
1107
+ cloned._id = generateId();
1108
+ cloned.measures = cloned.measures.map((measure) => cloneMeasureWithNewIds(measure));
1109
+ return cloned;
1110
+ }
1077
1111
  function getMeasureDuration(divisions, time) {
1078
1112
  const beats = parseInt(time.beats, 10);
1079
1113
  if (isNaN(beats)) return divisions * 4;
@@ -1136,6 +1170,7 @@ function hasNotesInRange(voiceEntries, startPos, endPos) {
1136
1170
  }
1137
1171
  function createRest(duration, voice, staff) {
1138
1172
  return {
1173
+ _id: generateId(),
1139
1174
  type: "note",
1140
1175
  rest: { displayStep: void 0, displayOctave: void 0 },
1141
1176
  duration,
@@ -1197,10 +1232,11 @@ function rebuildMeasureWithVoice(measure, voice, newEntries, measureDuration, st
1197
1232
  for (const { position: targetPos, entry } of allEntries) {
1198
1233
  const diff = targetPos - currentPosition;
1199
1234
  if (diff < 0) {
1200
- result.push({ type: "backup", duration: -diff });
1235
+ result.push({ _id: generateId(), type: "backup", duration: -diff });
1201
1236
  currentPosition = targetPos;
1202
1237
  } else if (diff > 0) {
1203
1238
  result.push({
1239
+ _id: generateId(),
1204
1240
  type: "forward",
1205
1241
  duration: diff,
1206
1242
  voice: entry.type === "note" ? entry.voice : 1,
@@ -1255,6 +1291,7 @@ function insertNote(score, options) {
1255
1291
  )]);
1256
1292
  }
1257
1293
  const newNote = {
1294
+ _id: generateId(),
1258
1295
  type: "note",
1259
1296
  pitch: options.pitch,
1260
1297
  duration: options.duration,
@@ -1361,6 +1398,7 @@ function addChord(score, options) {
1361
1398
  return failure([operationError("NOTE_NOT_FOUND", `Note index ${options.noteIndex} not found`, { partIndex: options.partIndex, measureIndex: options.measureIndex })]);
1362
1399
  }
1363
1400
  const chordNote = {
1401
+ _id: generateId(),
1364
1402
  type: "note",
1365
1403
  pitch: options.pitch,
1366
1404
  duration: targetEntry.duration,
@@ -1689,7 +1727,7 @@ function addVoice(score, options) {
1689
1727
  const rest = createRest(measureDuration, options.voice, options.staff);
1690
1728
  const currentEnd = getMeasureEndPosition(measure);
1691
1729
  if (currentEnd > 0) {
1692
- measure.entries.push({ type: "backup", duration: currentEnd });
1730
+ measure.entries.push({ _id: generateId(), type: "backup", duration: currentEnd });
1693
1731
  }
1694
1732
  measure.entries.push(rest);
1695
1733
  return success(result);
@@ -1740,6 +1778,7 @@ function addPart(score, options) {
1740
1778
  const result = cloneScore(score);
1741
1779
  const insertIndex = options.insertIndex ?? result.parts.length;
1742
1780
  const partInfo = {
1781
+ _id: generateId(),
1743
1782
  type: "score-part",
1744
1783
  id: options.id,
1745
1784
  name: options.name,
@@ -1758,10 +1797,10 @@ function addPart(score, options) {
1758
1797
  }
1759
1798
  result.partList.splice(partListInsertIndex, 0, partInfo);
1760
1799
  const measureCount = result.parts.length > 0 ? result.parts[0].measures.length : 1;
1761
- const newPart = { id: options.id, measures: [] };
1800
+ const newPart = { _id: generateId(), id: options.id, measures: [] };
1762
1801
  for (let i = 0; i < measureCount; i++) {
1763
1802
  const measureNumber = result.parts.length > 0 ? result.parts[0].measures[i]?.number ?? String(i + 1) : String(i + 1);
1764
- const measure = { number: measureNumber, entries: [] };
1803
+ const measure = { _id: generateId(), number: measureNumber, entries: [] };
1765
1804
  if (i === 0) {
1766
1805
  measure.attributes = {
1767
1806
  divisions: options.divisions ?? 4,
@@ -1805,10 +1844,11 @@ function duplicatePart(score, options) {
1805
1844
  }
1806
1845
  const result = cloneScore(score);
1807
1846
  const sourcePart = result.parts[sourceIndex];
1808
- const newPart = JSON.parse(JSON.stringify(sourcePart));
1847
+ const newPart = clonePartWithNewIds(sourcePart);
1809
1848
  newPart.id = options.newPartId;
1810
1849
  const sourcePartInfo = result.partList.find((e) => e.type === "score-part" && e.id === options.sourcePartId);
1811
1850
  const newPartInfo = {
1851
+ _id: generateId(),
1812
1852
  type: "score-part",
1813
1853
  id: options.newPartId,
1814
1854
  name: options.newPartName ?? sourcePartInfo?.name,
@@ -1922,7 +1962,7 @@ function insertMeasure(score, options) {
1922
1962
  if (insertIndex === -1) continue;
1923
1963
  const numericPart = parseInt(targetMeasure, 10);
1924
1964
  const newMeasureNumber = String(isNaN(numericPart) ? insertIndex + 2 : numericPart + 1);
1925
- const newMeasure = { number: newMeasureNumber, entries: [] };
1965
+ const newMeasure = { _id: generateId(), number: newMeasureNumber, entries: [] };
1926
1966
  if (options.copyAttributes && part.measures[insertIndex].attributes) {
1927
1967
  newMeasure.attributes = { ...part.measures[insertIndex].attributes };
1928
1968
  }
@@ -2198,6 +2238,7 @@ function addDynamics(score, options) {
2198
2238
  const result = cloneScore(score);
2199
2239
  const measure = result.parts[options.partIndex].measures[options.measureIndex];
2200
2240
  const directionEntry = {
2241
+ _id: generateId(),
2201
2242
  type: "direction",
2202
2243
  directionTypes: [{
2203
2244
  kind: "dynamics",
@@ -2274,6 +2315,7 @@ function insertClefChange(score, options) {
2274
2315
  }
2275
2316
  } else {
2276
2317
  const attributesEntry = {
2318
+ _id: generateId(),
2277
2319
  type: "attributes",
2278
2320
  attributes: {
2279
2321
  clef: [options.clef]
@@ -2742,7 +2784,7 @@ function copyNotes(score, options) {
2742
2784
  if (!entry.chord) {
2743
2785
  const noteEnd = position + entry.duration;
2744
2786
  if (position < options.endPosition && noteEnd > options.startPosition) {
2745
- const clonedNote = JSON.parse(JSON.stringify(entry));
2787
+ const clonedNote = cloneNoteWithNewId(entry);
2746
2788
  if (clonedNote.tie) {
2747
2789
  }
2748
2790
  copiedNotes.push({
@@ -2755,7 +2797,7 @@ function copyNotes(score, options) {
2755
2797
  if (copiedNotes.length > 0) {
2756
2798
  const lastCopied = copiedNotes[copiedNotes.length - 1];
2757
2799
  if (lastCopied.note.voice === entry.voice && (options.staff === void 0 || (lastCopied.note.staff ?? 1) === (entry.staff ?? 1))) {
2758
- const clonedNote = JSON.parse(JSON.stringify(entry));
2800
+ const clonedNote = cloneNoteWithNewId(entry);
2759
2801
  copiedNotes.push({
2760
2802
  relativePosition: lastCopied.relativePosition,
2761
2803
  note: clonedNote
@@ -2831,7 +2873,7 @@ function pasteNotes(score, options) {
2831
2873
  }
2832
2874
  for (const { relativePosition, note } of options.selection.notes) {
2833
2875
  const pastePosition = options.position + Math.max(0, relativePosition);
2834
- const newNote = JSON.parse(JSON.stringify(note));
2876
+ const newNote = cloneNoteWithNewId(note);
2835
2877
  newNote.voice = targetVoice;
2836
2878
  if (targetStaff !== void 0) {
2837
2879
  newNote.staff = targetStaff;
@@ -2866,7 +2908,7 @@ function pasteNotes(score, options) {
2866
2908
  const existingNotes = voiceEntries.filter((e) => e.entry.type === "note").map((e) => ({ position: e.position, entry: e.entry }));
2867
2909
  for (const { relativePosition, note } of options.selection.notes) {
2868
2910
  const pastePosition = options.position + Math.max(0, relativePosition);
2869
- const newNote = JSON.parse(JSON.stringify(note));
2911
+ const newNote = cloneNoteWithNewId(note);
2870
2912
  newNote.voice = targetVoice;
2871
2913
  if (targetStaff !== void 0) {
2872
2914
  newNote.staff = targetStaff;
@@ -2974,14 +3016,14 @@ function copyNotesMultiMeasure(score, options) {
2974
3016
  if (entry.type === "note") {
2975
3017
  if (entry.voice === options.voice && (options.staff === void 0 || (entry.staff ?? 1) === options.staff)) {
2976
3018
  if (!entry.chord && !entry.rest) {
2977
- const clonedNote = JSON.parse(JSON.stringify(entry));
3019
+ const clonedNote = cloneNoteWithNewId(entry);
2978
3020
  copiedNotes.push({
2979
3021
  relativePosition: position,
2980
3022
  note: clonedNote
2981
3023
  });
2982
3024
  position += entry.duration;
2983
3025
  } else if (entry.chord && copiedNotes.length > 0) {
2984
- const clonedNote = JSON.parse(JSON.stringify(entry));
3026
+ const clonedNote = cloneNoteWithNewId(entry);
2985
3027
  copiedNotes.push({
2986
3028
  relativePosition: copiedNotes[copiedNotes.length - 1].relativePosition,
2987
3029
  note: clonedNote
@@ -3035,7 +3077,7 @@ function pasteNotesMultiMeasure(score, options) {
3035
3077
  entriesToKeep = voiceEntries.filter((e) => e.entry.type === "note").map((e) => ({ position: e.position, entry: e.entry }));
3036
3078
  }
3037
3079
  for (const { relativePosition, note } of measureData.notes) {
3038
- const newNote = JSON.parse(JSON.stringify(note));
3080
+ const newNote = cloneNoteWithNewId(note);
3039
3081
  newNote.voice = targetVoice;
3040
3082
  if (targetStaff !== void 0) {
3041
3083
  newNote.staff = targetStaff;
@@ -3097,6 +3139,7 @@ function addTempo(score, options) {
3097
3139
  });
3098
3140
  }
3099
3141
  const direction = {
3142
+ _id: generateId(),
3100
3143
  type: "direction",
3101
3144
  directionTypes,
3102
3145
  placement: options.placement ?? "above",
@@ -3149,6 +3192,7 @@ function addWedge(score, options) {
3149
3192
  const result = cloneScore(score);
3150
3193
  const startMeasure = result.parts[options.partIndex].measures[options.startMeasureIndex];
3151
3194
  const startDirection = {
3195
+ _id: generateId(),
3152
3196
  type: "direction",
3153
3197
  directionTypes: [{
3154
3198
  kind: "wedge",
@@ -3160,6 +3204,7 @@ function addWedge(score, options) {
3160
3204
  insertDirectionAtPosition(startMeasure, startDirection, options.startPosition);
3161
3205
  const endMeasure = result.parts[options.partIndex].measures[options.endMeasureIndex];
3162
3206
  const endDirection = {
3207
+ _id: generateId(),
3163
3208
  type: "direction",
3164
3209
  directionTypes: [{
3165
3210
  kind: "wedge",
@@ -3340,6 +3385,7 @@ function addPedal(score, options) {
3340
3385
  const result = cloneScore(score);
3341
3386
  const measure = result.parts[options.partIndex].measures[options.measureIndex];
3342
3387
  const direction = {
3388
+ _id: generateId(),
3343
3389
  type: "direction",
3344
3390
  directionTypes: [{
3345
3391
  kind: "pedal",
@@ -3392,6 +3438,7 @@ function addTextDirection(score, options) {
3392
3438
  const result = cloneScore(score);
3393
3439
  const measure = result.parts[options.partIndex].measures[options.measureIndex];
3394
3440
  const direction = {
3441
+ _id: generateId(),
3395
3442
  type: "direction",
3396
3443
  directionTypes: [{
3397
3444
  kind: "words",
@@ -3415,6 +3462,7 @@ function addRehearsalMark(score, options) {
3415
3462
  const result = cloneScore(score);
3416
3463
  const measure = result.parts[options.partIndex].measures[options.measureIndex];
3417
3464
  const direction = {
3465
+ _id: generateId(),
3418
3466
  type: "direction",
3419
3467
  directionTypes: [{
3420
3468
  kind: "rehearsal",
@@ -3473,6 +3521,7 @@ function addRepeatBarline(score, options) {
3473
3521
  measure.barlines.splice(nonRepeatIndex, 1);
3474
3522
  }
3475
3523
  measure.barlines.push({
3524
+ _id: generateId(),
3476
3525
  location,
3477
3526
  barStyle,
3478
3527
  repeat: {
@@ -3535,7 +3584,7 @@ function addEnding(score, options) {
3535
3584
  }
3536
3585
  let barline = measure.barlines.find((b) => b.location === location);
3537
3586
  if (!barline) {
3538
- barline = { location };
3587
+ barline = { _id: generateId(), location };
3539
3588
  measure.barlines.push(barline);
3540
3589
  }
3541
3590
  if (barline.ending) {
@@ -3597,7 +3646,7 @@ function changeBarline(score, options) {
3597
3646
  }
3598
3647
  let barline = measure.barlines.find((b) => b.location === location);
3599
3648
  if (!barline) {
3600
- barline = { location };
3649
+ barline = { _id: generateId(), location };
3601
3650
  measure.barlines.push(barline);
3602
3651
  }
3603
3652
  barline.barStyle = barStyle;
@@ -3616,6 +3665,7 @@ function addSegno(score, options) {
3616
3665
  const result = cloneScore(score);
3617
3666
  const measure = result.parts[partIndex].measures[measureIndex];
3618
3667
  const direction = {
3668
+ _id: generateId(),
3619
3669
  type: "direction",
3620
3670
  directionTypes: [{ kind: "segno" }],
3621
3671
  placement: "above"
@@ -3635,6 +3685,7 @@ function addCoda(score, options) {
3635
3685
  const result = cloneScore(score);
3636
3686
  const measure = result.parts[partIndex].measures[measureIndex];
3637
3687
  const direction = {
3688
+ _id: generateId(),
3638
3689
  type: "direction",
3639
3690
  directionTypes: [{ kind: "coda" }],
3640
3691
  placement: "above"
@@ -3657,12 +3708,14 @@ function addDaCapo(score, options) {
3657
3708
  const measureDuration = getMeasureDuration(attrs.divisions ?? 1, attrs.time ?? { beats: "4", beatType: 4 });
3658
3709
  const insertPos = position ?? measureDuration;
3659
3710
  const direction = {
3711
+ _id: generateId(),
3660
3712
  type: "direction",
3661
3713
  directionTypes: [{ kind: "words", text: "D.C." }],
3662
3714
  placement: "above"
3663
3715
  };
3664
3716
  insertDirectionAtPosition(measure, direction, insertPos);
3665
3717
  const sound = {
3718
+ _id: generateId(),
3666
3719
  type: "sound",
3667
3720
  dacapo: true
3668
3721
  };
@@ -3684,12 +3737,14 @@ function addDalSegno(score, options) {
3684
3737
  const measureDuration = getMeasureDuration(attrs.divisions ?? 1, attrs.time ?? { beats: "4", beatType: 4 });
3685
3738
  const insertPos = position ?? measureDuration;
3686
3739
  const direction = {
3740
+ _id: generateId(),
3687
3741
  type: "direction",
3688
3742
  directionTypes: [{ kind: "words", text: "D.S." }],
3689
3743
  placement: "above"
3690
3744
  };
3691
3745
  insertDirectionAtPosition(measure, direction, insertPos);
3692
3746
  const sound = {
3747
+ _id: generateId(),
3693
3748
  type: "sound",
3694
3749
  dalsegno: "segno"
3695
3750
  };
@@ -3711,12 +3766,14 @@ function addFine(score, options) {
3711
3766
  const measureDuration = getMeasureDuration(attrs.divisions ?? 1, attrs.time ?? { beats: "4", beatType: 4 });
3712
3767
  const insertPos = position ?? measureDuration;
3713
3768
  const direction = {
3769
+ _id: generateId(),
3714
3770
  type: "direction",
3715
3771
  directionTypes: [{ kind: "words", text: "Fine" }],
3716
3772
  placement: "above"
3717
3773
  };
3718
3774
  insertDirectionAtPosition(measure, direction, insertPos);
3719
3775
  const sound = {
3776
+ _id: generateId(),
3720
3777
  type: "sound",
3721
3778
  fine: true
3722
3779
  };
@@ -3738,12 +3795,14 @@ function addToCoda(score, options) {
3738
3795
  const measureDuration = getMeasureDuration(attrs.divisions ?? 1, attrs.time ?? { beats: "4", beatType: 4 });
3739
3796
  const insertPos = position ?? measureDuration;
3740
3797
  const direction = {
3798
+ _id: generateId(),
3741
3799
  type: "direction",
3742
3800
  directionTypes: [{ kind: "words", text: "To Coda" }],
3743
3801
  placement: "above"
3744
3802
  };
3745
3803
  insertDirectionAtPosition(measure, direction, insertPos);
3746
3804
  const sound = {
3805
+ _id: generateId(),
3747
3806
  type: "sound",
3748
3807
  tocoda: "coda"
3749
3808
  };
@@ -3780,6 +3839,7 @@ function addGraceNote(score, options) {
3780
3839
  const result = cloneScore(score);
3781
3840
  const resultMeasure = result.parts[partIndex].measures[measureIndex];
3782
3841
  const graceNote = {
3842
+ _id: generateId(),
3783
3843
  type: "note",
3784
3844
  pitch,
3785
3845
  duration: 0,
@@ -4020,6 +4080,7 @@ function addHarmony(score, options) {
4020
4080
  const result = cloneScore(score);
4021
4081
  const measure = result.parts[partIndex].measures[measureIndex];
4022
4082
  const harmony = {
4083
+ _id: generateId(),
4023
4084
  type: "harmony",
4024
4085
  root: {
4025
4086
  rootStep: root.step.toUpperCase(),
@@ -4418,6 +4479,7 @@ function addOctaveShift(score, options) {
4418
4479
  const result = cloneScore(score);
4419
4480
  const measure = result.parts[partIndex].measures[measureIndex];
4420
4481
  const direction = {
4482
+ _id: generateId(),
4421
4483
  type: "direction",
4422
4484
  directionTypes: [{
4423
4485
  kind: "octave-shift",
@@ -4441,6 +4503,7 @@ function stopOctaveShift(score, options) {
4441
4503
  const result = cloneScore(score);
4442
4504
  const measure = result.parts[partIndex].measures[measureIndex];
4443
4505
  const direction = {
4506
+ _id: generateId(),
4444
4507
  type: "direction",
4445
4508
  directionTypes: [{
4446
4509
  kind: "octave-shift",
@@ -1,4 +1,4 @@
1
- import { S as Score, M as Measure, f as MeasureAttributes, P as Pitch, N as NoteEntry, e as Part } from '../types-D3LhKCDK.mjs';
1
+ import { S as Score, M as Measure, f as MeasureAttributes, P as Pitch, N as NoteEntry, e as Part } from '../types-CSI8kV28.mjs';
2
2
 
3
3
  /**
4
4
  * Get a specific measure from the score
@@ -1,4 +1,4 @@
1
- import { S as Score, M as Measure, f as MeasureAttributes, P as Pitch, N as NoteEntry, e as Part } from '../types-D3LhKCDK.js';
1
+ import { S as Score, M as Measure, f as MeasureAttributes, P as Pitch, N as NoteEntry, e as Part } from '../types-CSI8kV28.js';
2
2
 
3
3
  /**
4
4
  * Get a specific measure from the score
@@ -1,4 +1,5 @@
1
1
  interface Score {
2
+ _id: string;
2
3
  metadata: ScoreMetadata;
3
4
  partList: PartListEntry[];
4
5
  parts: Part[];
@@ -90,6 +91,7 @@ interface FontInfo {
90
91
  fontWeight?: string;
91
92
  }
92
93
  interface Credit {
94
+ _id: string;
93
95
  page?: number;
94
96
  creditType?: string[];
95
97
  creditWords?: CreditWords[];
@@ -116,6 +118,7 @@ interface CreditWords {
116
118
  }
117
119
  type PartListEntry = PartInfo | PartGroup;
118
120
  interface PartInfo {
121
+ _id: string;
119
122
  type: 'score-part';
120
123
  id: string;
121
124
  name?: string;
@@ -161,6 +164,7 @@ interface MidiInstrument {
161
164
  elevation?: number;
162
165
  }
163
166
  interface PartGroup {
167
+ _id: string;
164
168
  type: 'part-group';
165
169
  groupType: 'start' | 'stop';
166
170
  number?: number;
@@ -173,10 +177,12 @@ interface PartGroup {
173
177
  groupBarline?: 'yes' | 'no' | 'Mensurstrich';
174
178
  }
175
179
  interface Part {
180
+ _id: string;
176
181
  id: string;
177
182
  measures: Measure[];
178
183
  }
179
184
  interface Measure {
185
+ _id: string;
180
186
  number: string;
181
187
  width?: number;
182
188
  implicit?: boolean;
@@ -294,6 +300,7 @@ interface Transpose {
294
300
  octaveChange?: number;
295
301
  }
296
302
  interface Barline {
303
+ _id: string;
297
304
  location: 'left' | 'right' | 'middle';
298
305
  barStyle?: 'regular' | 'dotted' | 'dashed' | 'heavy' | 'light-light' | 'light-heavy' | 'heavy-light' | 'heavy-heavy' | 'tick' | 'short' | 'none';
299
306
  repeat?: {
@@ -307,10 +314,12 @@ interface Barline {
307
314
  }
308
315
  type MeasureEntry = NoteEntry | BackupEntry | ForwardEntry | DirectionEntry | HarmonyEntry | FiguredBassEntry | SoundEntry | AttributesEntry;
309
316
  interface AttributesEntry {
317
+ _id: string;
310
318
  type: 'attributes';
311
319
  attributes: MeasureAttributes;
312
320
  }
313
321
  interface NoteEntry {
322
+ _id: string;
314
323
  type: 'note';
315
324
  pitch?: Pitch;
316
325
  rest?: RestInfo;
@@ -356,10 +365,12 @@ interface NoteEntry {
356
365
  };
357
366
  }
358
367
  interface BackupEntry {
368
+ _id: string;
359
369
  type: 'backup';
360
370
  duration: number;
361
371
  }
362
372
  interface ForwardEntry {
373
+ _id: string;
363
374
  type: 'forward';
364
375
  duration: number;
365
376
  voice?: number;
@@ -380,6 +391,7 @@ interface DirectionSound {
380
391
  sostenutoPedal?: 'yes' | 'no';
381
392
  }
382
393
  interface DirectionEntry {
394
+ _id: string;
383
395
  type: 'direction';
384
396
  directionTypes: DirectionType[];
385
397
  placement?: 'above' | 'below';
@@ -398,6 +410,7 @@ interface Swing {
398
410
  swingType?: string;
399
411
  }
400
412
  interface SoundEntry {
413
+ _id: string;
401
414
  type: 'sound';
402
415
  tempo?: number;
403
416
  dynamics?: number;
@@ -414,6 +427,7 @@ interface SoundEntry {
414
427
  sostenutoPedal?: boolean | 'yes' | 'no';
415
428
  }
416
429
  interface HarmonyEntry {
430
+ _id: string;
417
431
  type: 'harmony';
418
432
  root: {
419
433
  rootStep: string;
@@ -455,6 +469,7 @@ interface FrameNote {
455
469
  barre?: 'start' | 'stop';
456
470
  }
457
471
  interface FiguredBassEntry {
472
+ _id: string;
458
473
  type: 'figured-bass';
459
474
  figures: Figure[];
460
475
  duration?: number;
@@ -1,4 +1,5 @@
1
1
  interface Score {
2
+ _id: string;
2
3
  metadata: ScoreMetadata;
3
4
  partList: PartListEntry[];
4
5
  parts: Part[];
@@ -90,6 +91,7 @@ interface FontInfo {
90
91
  fontWeight?: string;
91
92
  }
92
93
  interface Credit {
94
+ _id: string;
93
95
  page?: number;
94
96
  creditType?: string[];
95
97
  creditWords?: CreditWords[];
@@ -116,6 +118,7 @@ interface CreditWords {
116
118
  }
117
119
  type PartListEntry = PartInfo | PartGroup;
118
120
  interface PartInfo {
121
+ _id: string;
119
122
  type: 'score-part';
120
123
  id: string;
121
124
  name?: string;
@@ -161,6 +164,7 @@ interface MidiInstrument {
161
164
  elevation?: number;
162
165
  }
163
166
  interface PartGroup {
167
+ _id: string;
164
168
  type: 'part-group';
165
169
  groupType: 'start' | 'stop';
166
170
  number?: number;
@@ -173,10 +177,12 @@ interface PartGroup {
173
177
  groupBarline?: 'yes' | 'no' | 'Mensurstrich';
174
178
  }
175
179
  interface Part {
180
+ _id: string;
176
181
  id: string;
177
182
  measures: Measure[];
178
183
  }
179
184
  interface Measure {
185
+ _id: string;
180
186
  number: string;
181
187
  width?: number;
182
188
  implicit?: boolean;
@@ -294,6 +300,7 @@ interface Transpose {
294
300
  octaveChange?: number;
295
301
  }
296
302
  interface Barline {
303
+ _id: string;
297
304
  location: 'left' | 'right' | 'middle';
298
305
  barStyle?: 'regular' | 'dotted' | 'dashed' | 'heavy' | 'light-light' | 'light-heavy' | 'heavy-light' | 'heavy-heavy' | 'tick' | 'short' | 'none';
299
306
  repeat?: {
@@ -307,10 +314,12 @@ interface Barline {
307
314
  }
308
315
  type MeasureEntry = NoteEntry | BackupEntry | ForwardEntry | DirectionEntry | HarmonyEntry | FiguredBassEntry | SoundEntry | AttributesEntry;
309
316
  interface AttributesEntry {
317
+ _id: string;
310
318
  type: 'attributes';
311
319
  attributes: MeasureAttributes;
312
320
  }
313
321
  interface NoteEntry {
322
+ _id: string;
314
323
  type: 'note';
315
324
  pitch?: Pitch;
316
325
  rest?: RestInfo;
@@ -356,10 +365,12 @@ interface NoteEntry {
356
365
  };
357
366
  }
358
367
  interface BackupEntry {
368
+ _id: string;
359
369
  type: 'backup';
360
370
  duration: number;
361
371
  }
362
372
  interface ForwardEntry {
373
+ _id: string;
363
374
  type: 'forward';
364
375
  duration: number;
365
376
  voice?: number;
@@ -380,6 +391,7 @@ interface DirectionSound {
380
391
  sostenutoPedal?: 'yes' | 'no';
381
392
  }
382
393
  interface DirectionEntry {
394
+ _id: string;
383
395
  type: 'direction';
384
396
  directionTypes: DirectionType[];
385
397
  placement?: 'above' | 'below';
@@ -398,6 +410,7 @@ interface Swing {
398
410
  swingType?: string;
399
411
  }
400
412
  interface SoundEntry {
413
+ _id: string;
401
414
  type: 'sound';
402
415
  tempo?: number;
403
416
  dynamics?: number;
@@ -414,6 +427,7 @@ interface SoundEntry {
414
427
  sostenutoPedal?: boolean | 'yes' | 'no';
415
428
  }
416
429
  interface HarmonyEntry {
430
+ _id: string;
417
431
  type: 'harmony';
418
432
  root: {
419
433
  rootStep: string;
@@ -455,6 +469,7 @@ interface FrameNote {
455
469
  barre?: 'start' | 'stop';
456
470
  }
457
471
  interface FiguredBassEntry {
472
+ _id: string;
458
473
  type: 'figured-bass';
459
474
  figures: Figure[];
460
475
  duration?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musicxml-io",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Parse and serialize MusicXML (.xml/.mxl) with high round-trip fidelity",
5
5
  "author": "tan-z-tan",
6
6
  "license": "MIT",
@@ -76,7 +76,8 @@
76
76
  },
77
77
  "dependencies": {
78
78
  "fast-xml-parser": "^4.3.2",
79
- "fflate": "^0.8.2"
79
+ "fflate": "^0.8.2",
80
+ "nanoid": "^5.1.6"
80
81
  },
81
82
  "files": [
82
83
  "dist"