@sudobility/music_types 0.11.19 → 0.11.21

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 (153) hide show
  1. package/dist/domain/commands/edit-commands.d.ts +66 -0
  2. package/dist/domain/commands/edit-commands.d.ts.map +1 -0
  3. package/dist/domain/commands/edit-commands.js +185 -0
  4. package/dist/domain/commands/edit-commands.js.map +1 -0
  5. package/dist/domain/commands/note-commands.d.ts +73 -0
  6. package/dist/domain/commands/note-commands.d.ts.map +1 -0
  7. package/dist/domain/commands/note-commands.js +292 -0
  8. package/dist/domain/commands/note-commands.js.map +1 -0
  9. package/dist/domain/commands/reflow.d.ts +69 -0
  10. package/dist/domain/commands/reflow.d.ts.map +1 -0
  11. package/dist/domain/commands/reflow.js +395 -0
  12. package/dist/domain/commands/reflow.js.map +1 -0
  13. package/dist/domain/commands/region-commands.d.ts +21 -0
  14. package/dist/domain/commands/region-commands.d.ts.map +1 -0
  15. package/dist/domain/commands/region-commands.js +47 -0
  16. package/dist/domain/commands/region-commands.js.map +1 -0
  17. package/dist/domain/commands/relocate-commands.d.ts +18 -0
  18. package/dist/domain/commands/relocate-commands.d.ts.map +1 -0
  19. package/dist/domain/commands/relocate-commands.js +108 -0
  20. package/dist/domain/commands/relocate-commands.js.map +1 -0
  21. package/dist/domain/commands/ripple-commands.d.ts +44 -0
  22. package/dist/domain/commands/ripple-commands.d.ts.map +1 -0
  23. package/dist/domain/commands/ripple-commands.js +104 -0
  24. package/dist/domain/commands/ripple-commands.js.map +1 -0
  25. package/dist/domain/commands/snapshot.d.ts +38 -0
  26. package/dist/domain/commands/snapshot.d.ts.map +1 -0
  27. package/dist/domain/commands/snapshot.js +44 -0
  28. package/dist/domain/commands/snapshot.js.map +1 -0
  29. package/dist/domain/commands/structure-commands.d.ts +38 -0
  30. package/dist/domain/commands/structure-commands.d.ts.map +1 -0
  31. package/dist/domain/commands/structure-commands.js +220 -0
  32. package/dist/domain/commands/structure-commands.js.map +1 -0
  33. package/dist/domain/commands/track-commands.d.ts +15 -0
  34. package/dist/domain/commands/track-commands.d.ts.map +1 -0
  35. package/dist/domain/commands/track-commands.js +56 -0
  36. package/dist/domain/commands/track-commands.js.map +1 -0
  37. package/dist/domain/commands/types.d.ts +31 -0
  38. package/dist/domain/commands/types.d.ts.map +1 -0
  39. package/dist/domain/commands/types.js +2 -0
  40. package/dist/domain/commands/types.js.map +1 -0
  41. package/dist/domain/instruments/gm-kit.d.ts +45 -0
  42. package/dist/domain/instruments/gm-kit.d.ts.map +1 -0
  43. package/dist/domain/instruments/gm-kit.js +59 -0
  44. package/dist/domain/instruments/gm-kit.js.map +1 -0
  45. package/dist/domain/instruments/gm-percussion.d.ts +42 -0
  46. package/dist/domain/instruments/gm-percussion.d.ts.map +1 -0
  47. package/dist/domain/instruments/gm-percussion.js +85 -0
  48. package/dist/domain/instruments/gm-percussion.js.map +1 -0
  49. package/dist/domain/instruments/gm-polyphony.d.ts +13 -0
  50. package/dist/domain/instruments/gm-polyphony.d.ts.map +1 -0
  51. package/dist/domain/instruments/gm-polyphony.js +76 -0
  52. package/dist/domain/instruments/gm-polyphony.js.map +1 -0
  53. package/dist/domain/instruments/gm-range.d.ts +15 -0
  54. package/dist/domain/instruments/gm-range.d.ts.map +1 -0
  55. package/dist/domain/instruments/gm-range.js +97 -0
  56. package/dist/domain/instruments/gm-range.js.map +1 -0
  57. package/dist/domain/instruments/gm-transposition.d.ts +11 -0
  58. package/dist/domain/instruments/gm-transposition.d.ts.map +1 -0
  59. package/dist/domain/instruments/gm-transposition.js +69 -0
  60. package/dist/domain/instruments/gm-transposition.js.map +1 -0
  61. package/dist/domain/instruments/gm.d.ts +28 -0
  62. package/dist/domain/instruments/gm.d.ts.map +1 -0
  63. package/dist/domain/instruments/gm.js +217 -0
  64. package/dist/domain/instruments/gm.js.map +1 -0
  65. package/dist/domain/pitch/pitch.d.ts +15 -0
  66. package/dist/domain/pitch/pitch.d.ts.map +1 -0
  67. package/dist/domain/pitch/pitch.js +81 -0
  68. package/dist/domain/pitch/pitch.js.map +1 -0
  69. package/dist/domain/pitch/transpose.d.ts +37 -0
  70. package/dist/domain/pitch/transpose.d.ts.map +1 -0
  71. package/dist/domain/pitch/transpose.js +65 -0
  72. package/dist/domain/pitch/transpose.js.map +1 -0
  73. package/dist/domain/quantization/options.d.ts +55 -0
  74. package/dist/domain/quantization/options.d.ts.map +1 -0
  75. package/dist/domain/quantization/options.js +2 -0
  76. package/dist/domain/quantization/options.js.map +1 -0
  77. package/dist/domain/quantization/quantize.d.ts +13 -0
  78. package/dist/domain/quantization/quantize.d.ts.map +1 -0
  79. package/dist/domain/quantization/quantize.js +199 -0
  80. package/dist/domain/quantization/quantize.js.map +1 -0
  81. package/dist/domain/score/factory.d.ts +39 -0
  82. package/dist/domain/score/factory.d.ts.map +1 -0
  83. package/dist/domain/score/factory.js +148 -0
  84. package/dist/domain/score/factory.js.map +1 -0
  85. package/dist/domain/score/fragment.d.ts +28 -0
  86. package/dist/domain/score/fragment.d.ts.map +1 -0
  87. package/dist/domain/score/fragment.js +68 -0
  88. package/dist/domain/score/fragment.js.map +1 -0
  89. package/dist/domain/score/ids.d.ts +8 -0
  90. package/dist/domain/score/ids.d.ts.map +1 -0
  91. package/dist/domain/score/ids.js +38 -0
  92. package/dist/domain/score/ids.js.map +1 -0
  93. package/dist/domain/score/queries.d.ts +34 -0
  94. package/dist/domain/score/queries.d.ts.map +1 -0
  95. package/dist/domain/score/queries.js +132 -0
  96. package/dist/domain/score/queries.js.map +1 -0
  97. package/dist/domain/score/ties.d.ts +73 -0
  98. package/dist/domain/score/ties.d.ts.map +1 -0
  99. package/dist/domain/score/ties.js +200 -0
  100. package/dist/domain/score/ties.js.map +1 -0
  101. package/dist/domain/selection/selection.d.ts +63 -0
  102. package/dist/domain/selection/selection.d.ts.map +1 -0
  103. package/dist/domain/selection/selection.js +129 -0
  104. package/dist/domain/selection/selection.js.map +1 -0
  105. package/dist/domain/selection/types.d.ts +10 -0
  106. package/dist/domain/selection/types.d.ts.map +1 -0
  107. package/dist/domain/selection/types.js +5 -0
  108. package/dist/domain/selection/types.js.map +1 -0
  109. package/dist/domain/time/durations.d.ts +21 -0
  110. package/dist/domain/time/durations.d.ts.map +1 -0
  111. package/dist/domain/time/durations.js +53 -0
  112. package/dist/domain/time/durations.js.map +1 -0
  113. package/dist/domain/time/fraction.d.ts +19 -0
  114. package/dist/domain/time/fraction.d.ts.map +1 -0
  115. package/dist/domain/time/fraction.js +60 -0
  116. package/dist/domain/time/fraction.js.map +1 -0
  117. package/dist/domain/time/tempo-map.d.ts +24 -0
  118. package/dist/domain/time/tempo-map.d.ts.map +1 -0
  119. package/dist/domain/time/tempo-map.js +85 -0
  120. package/dist/domain/time/tempo-map.js.map +1 -0
  121. package/dist/domain/time/ticks.d.ts +34 -0
  122. package/dist/domain/time/ticks.d.ts.map +1 -0
  123. package/dist/domain/time/ticks.js +93 -0
  124. package/dist/domain/time/ticks.js.map +1 -0
  125. package/dist/domain/validation/issues.d.ts +44 -0
  126. package/dist/domain/validation/issues.d.ts.map +1 -0
  127. package/dist/domain/validation/issues.js +34 -0
  128. package/dist/domain/validation/issues.js.map +1 -0
  129. package/dist/domain/validation/validator.d.ts +11 -0
  130. package/dist/domain/validation/validator.d.ts.map +1 -0
  131. package/dist/domain/validation/validator.js +375 -0
  132. package/dist/domain/validation/validator.js.map +1 -0
  133. package/dist/domain/voicing/allocate.d.ts +19 -0
  134. package/dist/domain/voicing/allocate.d.ts.map +1 -0
  135. package/dist/domain/voicing/allocate.js +100 -0
  136. package/dist/domain/voicing/allocate.js.map +1 -0
  137. package/dist/index.d.ts +46 -4
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/index.js +50 -2
  140. package/dist/index.js.map +1 -1
  141. package/dist/platform/audio.d.ts +12 -0
  142. package/dist/platform/audio.d.ts.map +1 -1
  143. package/dist/platform/playback.d.ts +85 -15
  144. package/dist/platform/playback.d.ts.map +1 -1
  145. package/dist/services/regeneration/controller.d.ts +60 -0
  146. package/dist/services/regeneration/controller.d.ts.map +1 -0
  147. package/dist/services/regeneration/controller.js +202 -0
  148. package/dist/services/regeneration/controller.js.map +1 -0
  149. package/dist/test/fixtures.d.ts +45 -0
  150. package/dist/test/fixtures.d.ts.map +1 -0
  151. package/dist/test/fixtures.js +420 -0
  152. package/dist/test/fixtures.js.map +1 -0
  153. package/package.json +2 -1
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Reusable quantization engine (spec §24). `quantizeEvents` is a pure
3
+ * function: it never mutates `events` or any event within it, and its
4
+ * output depends only on its inputs (no randomness — "humanization" here
5
+ * means a deterministic "already close enough, leave it" tolerance, not
6
+ * random jitter). Exposed for both MIDI import cleanup and manual editing.
7
+ */
8
+ import { createId } from '../score/ids.js';
9
+ import { isNoteEvent } from '../../index.js';
10
+ /** `tripletGrid` subdivides `grid` into thirds-of-two (2/3 of `grid`); otherwise the grid unit is used as-is. */
11
+ function effectiveGridTicks(opts) {
12
+ return opts.tripletGrid ? (opts.grid * 2) / 3 : opts.grid;
13
+ }
14
+ /**
15
+ * Snaps `original` to the nearest grid line (round-half-up, i.e. a note
16
+ * exactly at the midpoint between two grid lines snaps to the later one),
17
+ * then, if `swing` is set, delays every second grid slot (odd slot index)
18
+ * by `swing` * one grid unit. If `humanizeToleranceTicks` is set and
19
+ * `original` is already within that many ticks of the computed target, the
20
+ * event is left at `original` instead (not force-snapped).
21
+ */
22
+ function computeSnappedStart(original, opts) {
23
+ const grid = effectiveGridTicks(opts);
24
+ if (grid <= 0)
25
+ return original;
26
+ const slotIndex = Math.round(original / grid);
27
+ let target = slotIndex * grid;
28
+ if (opts.swing && slotIndex % 2 !== 0) {
29
+ target += opts.swing * grid;
30
+ }
31
+ target = Math.round(target);
32
+ if (opts.humanizeToleranceTicks !== undefined &&
33
+ Math.abs(original - target) <= opts.humanizeToleranceTicks) {
34
+ return original;
35
+ }
36
+ return target;
37
+ }
38
+ /**
39
+ * Snaps `original` to the nearest multiple of the grid unit, then clamps
40
+ * the result up to at least `minDurationTicks` (default a hard 1-tick
41
+ * floor when unset) so quantization can never produce a zero/negative
42
+ * duration, which every other event in the domain model forbids.
43
+ */
44
+ function computeSnappedDuration(original, opts) {
45
+ const grid = effectiveGridTicks(opts);
46
+ if (grid <= 0)
47
+ return Math.max(original, opts.minDurationTicks ?? 1);
48
+ const units = Math.round(original / grid);
49
+ const snapped = Math.round(units * grid);
50
+ return Math.max(snapped, opts.minDurationTicks ?? 1);
51
+ }
52
+ /** Drops `NoteEvent`s whose *original* duration is shorter than `minDurationTicks`. `RestEvent`s are never dropped. */
53
+ function dropShortNotes(events, minDurationTicks) {
54
+ if (minDurationTicks === undefined)
55
+ return events;
56
+ return events.filter(event => !isNoteEvent(event) || event.durationTicks >= minDurationTicks);
57
+ }
58
+ /** Groups events by (trackId, voiceId), preserving each group's first-seen order. */
59
+ function groupByVoice(events) {
60
+ const order = [];
61
+ const groups = new Map();
62
+ for (const event of events) {
63
+ const key = `${event.trackId}::${event.voiceId}`;
64
+ let group = groups.get(key);
65
+ if (!group) {
66
+ group = [];
67
+ groups.set(key, group);
68
+ order.push(key);
69
+ }
70
+ group.push(event);
71
+ }
72
+ return order.map(key => groups.get(key));
73
+ }
74
+ /**
75
+ * Clusters near-simultaneous onsets (already sorted by `startTick`) onto a
76
+ * shared start tick: the first event in a run seeds a cluster "anchor";
77
+ * every subsequent event within `tolerance` ticks of the anchor is
78
+ * reassigned to the anchor's tick, otherwise it becomes the next cluster's
79
+ * anchor. Anchoring (rather than comparing each event to its immediate
80
+ * neighbor) bounds how far a chain of close onsets can drift in total.
81
+ */
82
+ function applyChordGrouping(sorted, tolerance) {
83
+ let anchor = 0;
84
+ return sorted.map((event, index) => {
85
+ if (index === 0) {
86
+ anchor = event.startTick;
87
+ return event;
88
+ }
89
+ if (event.startTick - anchor <= tolerance) {
90
+ return { ...event, startTick: anchor };
91
+ }
92
+ anchor = event.startTick;
93
+ return event;
94
+ });
95
+ }
96
+ /**
97
+ * Trims an event that overlaps the following event's start (already
98
+ * sorted by `startTick`) down to end exactly at that start. Events sharing
99
+ * an exact start tick are a chord, not an overlap, and are left alone.
100
+ * Only adjacent pairs are compared, matching the everyday "this note runs
101
+ * into the next one" meaning of overlap resolution.
102
+ */
103
+ function applyResolveOverlaps(sorted) {
104
+ const result = sorted.map(event => ({ ...event }));
105
+ for (let i = 0; i < result.length - 1; i += 1) {
106
+ const current = result[i];
107
+ const next = result[i + 1];
108
+ if (current.startTick === next.startTick)
109
+ continue;
110
+ const currentEnd = current.startTick + current.durationTicks;
111
+ if (currentEnd > next.startTick) {
112
+ current.durationTicks = Math.max(1, next.startTick - current.startTick);
113
+ }
114
+ }
115
+ return result;
116
+ }
117
+ /**
118
+ * Extends an event that falls short of the following event's start
119
+ * (already sorted by `startTick`) so it ends exactly there, closing small
120
+ * silent gaps. Never shrinks an event that already reaches or overlaps the
121
+ * next one. Events sharing an exact start tick (a chord) are left alone.
122
+ */
123
+ function applyLegatoCleanup(sorted) {
124
+ const result = sorted.map(event => ({ ...event }));
125
+ for (let i = 0; i < result.length - 1; i += 1) {
126
+ const current = result[i];
127
+ const next = result[i + 1];
128
+ if (current.startTick === next.startTick)
129
+ continue;
130
+ const currentEnd = current.startTick + current.durationTicks;
131
+ if (currentEnd < next.startTick) {
132
+ current.durationTicks = next.startTick - current.startTick;
133
+ }
134
+ }
135
+ return result;
136
+ }
137
+ /** Inserts a fresh `RestEvent` for every remaining silent gap between consecutive events (already sorted by `startTick`). */
138
+ function applyFillGaps(sorted, trackId, voiceId) {
139
+ const result = [];
140
+ let cursor = null;
141
+ for (const event of sorted) {
142
+ if (cursor !== null && event.startTick > cursor) {
143
+ result.push({
144
+ id: createId(),
145
+ startTick: cursor,
146
+ durationTicks: event.startTick - cursor,
147
+ voiceId,
148
+ trackId,
149
+ });
150
+ }
151
+ result.push(event);
152
+ const end = event.startTick + event.durationTicks;
153
+ cursor = cursor === null ? end : Math.max(cursor, end);
154
+ }
155
+ return result;
156
+ }
157
+ /** Runs the chord-grouping/overlap-resolution/legato/gap-filling stages (each opt-in) over one (trackId, voiceId) group. */
158
+ function processVoiceGroup(group, opts) {
159
+ let current = [...group].sort((a, b) => a.startTick - b.startTick);
160
+ if (opts.chordToleranceTicks !== undefined) {
161
+ current = applyChordGrouping(current, opts.chordToleranceTicks);
162
+ }
163
+ if (opts.resolveOverlaps) {
164
+ current = applyResolveOverlaps(current);
165
+ }
166
+ if (opts.legatoCleanup) {
167
+ current = applyLegatoCleanup(current);
168
+ }
169
+ if (opts.fillGaps) {
170
+ const [{ trackId, voiceId }] = group;
171
+ current = applyFillGaps(current, trackId, voiceId);
172
+ }
173
+ return current;
174
+ }
175
+ /**
176
+ * Quantizes `events` per `opts` (spec §24): drops undersized notes, snaps
177
+ * starts (grid/triplet/swing/humanize tolerance) and durations (grid,
178
+ * clamped to a minimum), then — per (trackId, voiceId) group — clusters
179
+ * near-simultaneous onsets into chords, resolves overlaps, closes gaps
180
+ * (legato), and fills remaining silence with rests. Every stage after
181
+ * dropping is opt-in via `opts`; omitting a field disables that stage.
182
+ * Returns a new array; never mutates `events` or its elements.
183
+ */
184
+ export function quantizeEvents(events, opts) {
185
+ const survivors = dropShortNotes(events, opts.minDurationTicks);
186
+ const withStarts = survivors.map(event => opts.quantizeStarts
187
+ ? { ...event, startTick: computeSnappedStart(event.startTick, opts) }
188
+ : { ...event });
189
+ const withDurations = withStarts.map(event => opts.quantizeDurations
190
+ ? {
191
+ ...event,
192
+ durationTicks: computeSnappedDuration(event.durationTicks, opts),
193
+ }
194
+ : event);
195
+ return groupByVoice(withDurations)
196
+ .map(group => processVoiceGroup(group, opts))
197
+ .flat();
198
+ }
199
+ //# sourceMappingURL=quantize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quantize.js","sourceRoot":"","sources":["../../../src/domain/quantization/quantize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { KeySignature, Score, TimeSignature, Track } from '../../index.js';
2
+ export type CreateTrackOptions = Partial<Track> & {
3
+ name: string;
4
+ };
5
+ /** Builds a `Track` with sensible defaults, overridden by any fields in `opts`. */
6
+ export declare function createTrack(opts: CreateTrackOptions): Track;
7
+ export type CreateEmptyScoreOptions = {
8
+ title: string;
9
+ ppq?: number;
10
+ tempo?: number;
11
+ timeSignature?: TimeSignature;
12
+ keySignature?: KeySignature;
13
+ measures?: number;
14
+ tracks?: Array<Partial<Track> & {
15
+ name: string;
16
+ }>;
17
+ };
18
+ /**
19
+ * Builds a new `Score` with `measures` fully-rested measures on each of
20
+ * `tracks`, one default voice per track per measure. Defaults: 480 PPQ,
21
+ * 120 bpm, 4/4, C major, one measure, one Piano track.
22
+ */
23
+ export declare function createEmptyScore(opts: CreateEmptyScoreOptions): Score;
24
+ /**
25
+ * Appends one fully-rested measure to every track, continuing each track's
26
+ * own tick numbering and reusing its last measure's time/key signature
27
+ * (falling back to 4/4 C major for a track with no existing measures).
28
+ */
29
+ export declare function appendMeasure(score: Score): Score;
30
+ /**
31
+ * Recomputes `index`/`startTick` for every track's measures from their
32
+ * current array order (each measure keeps its own `durationTicks`), and
33
+ * shifts each measure's events by the same delta so they stay positioned
34
+ * consistently within their (possibly moved) measure. Measures whose
35
+ * index/startTick are already consistent are returned unchanged
36
+ * (referentially equal) so unaffected structure is preserved.
37
+ */
38
+ export declare function rebuildMeasureTicks(score: Score): Score;
39
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/domain/score/factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EAEZ,KAAK,EAEL,aAAa,EACb,KAAK,EACN,MAAM,gBAAgB,CAAC;AAUxB,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,KAAK,CAc3D;AAwDD,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,KAAK,CAwCrE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CA0BjD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CA6BvD"}
@@ -0,0 +1,148 @@
1
+ import { createId } from './ids.js';
2
+ import { measureDurationTicks } from '../time/ticks.js';
3
+ const DEFAULT_PPQ = 480;
4
+ const DEFAULT_TEMPO_BPM = 120;
5
+ const DEFAULT_TIME_SIGNATURE = { numerator: 4, denominator: 4 };
6
+ const DEFAULT_KEY_SIGNATURE = { fifths: 0, mode: 'major' };
7
+ const DEFAULT_MEASURE_COUNT = 1;
8
+ const DEFAULT_VOICE_NAME = 'Voice 1';
9
+ /** Builds a `Track` with sensible defaults, overridden by any fields in `opts`. */
10
+ export function createTrack(opts) {
11
+ return {
12
+ id: opts.id ?? createId(),
13
+ name: opts.name,
14
+ instrumentName: opts.instrumentName ?? 'Piano',
15
+ midiProgram: opts.midiProgram ?? 0,
16
+ midiChannel: opts.midiChannel ?? 0,
17
+ clef: opts.clef ?? 'treble',
18
+ volume: opts.volume ?? 1,
19
+ pan: opts.pan ?? 0,
20
+ muted: opts.muted ?? false,
21
+ solo: opts.solo ?? false,
22
+ measures: opts.measures ?? [],
23
+ };
24
+ }
25
+ /** Builds a single fully-rested measure at the given index/startTick. */
26
+ function buildRestMeasure(index, startTick, timeSignature, keySignature, durationTicks, trackId) {
27
+ const voiceId = createId();
28
+ return {
29
+ id: createId(),
30
+ index,
31
+ startTick,
32
+ durationTicks,
33
+ timeSignature,
34
+ keySignature,
35
+ voices: [
36
+ {
37
+ id: voiceId,
38
+ name: DEFAULT_VOICE_NAME,
39
+ events: [
40
+ { id: createId(), startTick, durationTicks, voiceId, trackId },
41
+ ],
42
+ },
43
+ ],
44
+ };
45
+ }
46
+ /** Builds `count` consecutive fully-rested measures starting at tick 0. */
47
+ function buildMeasures(count, timeSignature, keySignature, ppq, trackId) {
48
+ const durationTicks = measureDurationTicks(timeSignature, ppq);
49
+ const measures = [];
50
+ for (let index = 0; index < count; index += 1) {
51
+ measures.push(buildRestMeasure(index, index * durationTicks, timeSignature, keySignature, durationTicks, trackId));
52
+ }
53
+ return measures;
54
+ }
55
+ /**
56
+ * Builds a new `Score` with `measures` fully-rested measures on each of
57
+ * `tracks`, one default voice per track per measure. Defaults: 480 PPQ,
58
+ * 120 bpm, 4/4, C major, one measure, one Piano track.
59
+ */
60
+ export function createEmptyScore(opts) {
61
+ const ppq = opts.ppq ?? DEFAULT_PPQ;
62
+ const timeSignature = opts.timeSignature ?? DEFAULT_TIME_SIGNATURE;
63
+ const keySignature = opts.keySignature ?? DEFAULT_KEY_SIGNATURE;
64
+ const measureCount = opts.measures ?? DEFAULT_MEASURE_COUNT;
65
+ const trackOptions = opts.tracks ?? [
66
+ { name: 'Piano', instrumentName: 'Piano', clef: 'treble' },
67
+ ];
68
+ const tracks = trackOptions.map(trackOpts => {
69
+ const track = createTrack(trackOpts);
70
+ return {
71
+ ...track,
72
+ measures: buildMeasures(measureCount, timeSignature, keySignature, ppq, track.id),
73
+ };
74
+ });
75
+ const now = new Date().toISOString();
76
+ const metadata = {
77
+ title: opts.title,
78
+ createdAt: now,
79
+ updatedAt: now,
80
+ };
81
+ return {
82
+ id: createId(),
83
+ version: 1,
84
+ ppq,
85
+ metadata,
86
+ tempoMap: [
87
+ { id: createId(), tick: 0, bpm: opts.tempo ?? DEFAULT_TEMPO_BPM },
88
+ ],
89
+ tracks,
90
+ };
91
+ }
92
+ /**
93
+ * Appends one fully-rested measure to every track, continuing each track's
94
+ * own tick numbering and reusing its last measure's time/key signature
95
+ * (falling back to 4/4 C major for a track with no existing measures).
96
+ */
97
+ export function appendMeasure(score) {
98
+ const tracks = score.tracks.map(track => {
99
+ const lastMeasure = track.measures[track.measures.length - 1];
100
+ const timeSignature = lastMeasure?.timeSignature ?? DEFAULT_TIME_SIGNATURE;
101
+ const keySignature = lastMeasure?.keySignature ?? DEFAULT_KEY_SIGNATURE;
102
+ const durationTicks = measureDurationTicks(timeSignature, score.ppq);
103
+ const startTick = lastMeasure
104
+ ? lastMeasure.startTick + lastMeasure.durationTicks
105
+ : 0;
106
+ const newMeasure = buildRestMeasure(track.measures.length, startTick, timeSignature, keySignature, durationTicks, track.id);
107
+ return { ...track, measures: [...track.measures, newMeasure] };
108
+ });
109
+ return {
110
+ ...score,
111
+ tracks,
112
+ metadata: { ...score.metadata, updatedAt: new Date().toISOString() },
113
+ };
114
+ }
115
+ /**
116
+ * Recomputes `index`/`startTick` for every track's measures from their
117
+ * current array order (each measure keeps its own `durationTicks`), and
118
+ * shifts each measure's events by the same delta so they stay positioned
119
+ * consistently within their (possibly moved) measure. Measures whose
120
+ * index/startTick are already consistent are returned unchanged
121
+ * (referentially equal) so unaffected structure is preserved.
122
+ */
123
+ export function rebuildMeasureTicks(score) {
124
+ const tracks = score.tracks.map(track => {
125
+ let cursor = 0;
126
+ const measures = track.measures.map((measure, index) => {
127
+ const startTick = cursor;
128
+ const delta = startTick - measure.startTick;
129
+ cursor += measure.durationTicks;
130
+ if (delta === 0 && index === measure.index) {
131
+ return measure;
132
+ }
133
+ const voices = delta === 0
134
+ ? measure.voices
135
+ : measure.voices.map(voice => ({
136
+ ...voice,
137
+ events: voice.events.map(event => ({
138
+ ...event,
139
+ startTick: event.startTick + delta,
140
+ })),
141
+ }));
142
+ return { ...measure, index, startTick, voices };
143
+ });
144
+ return { ...track, measures };
145
+ });
146
+ return { ...score, tracks };
147
+ }
148
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/domain/score/factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Score, ScoreFragment } from '../../index.js';
2
+ import type { ScoreRange } from '../selection/types.js';
3
+ /**
4
+ * `ScoreFragment` is canonical in @sudobility/music_types and re-exported
5
+ * here so domain modules keep one import site. A fragment is a snapshot of
6
+ * a tick range of a score (measures shared by reference, not deep-cloned).
7
+ */
8
+ export type { ScoreFragment } from '../../index.js';
9
+ /** Captures the measures overlapping `range` (per track named in `range.trackIds`, or all tracks if empty). */
10
+ export declare function extractFragment(score: Score, range: ScoreRange): ScoreFragment;
11
+ /**
12
+ * Splices `fragment`'s measures into `score`, one track at a time. For each
13
+ * track named in `fragment.tracks`, the contiguous block of that track's
14
+ * existing measures overlapping `fragment.range` is replaced by the
15
+ * fragment's measures for that track (deep-replacing their voices); tracks
16
+ * with no measures overlapping the range are left untouched. Track ids
17
+ * named in the fragment but absent from the score are ignored.
18
+ *
19
+ * Every replaced event's `trackId`/`voiceId` is renumbered to match its
20
+ * new, actual track and voice (never trusted from the incoming fragment),
21
+ * so a regenerated fragment's placeholder ids never leak into the score.
22
+ *
23
+ * Measure `index`/`startTick` for every track are then recomputed via
24
+ * `rebuildMeasureTicks`, which leaves measures outside the edited region
25
+ * referentially unchanged.
26
+ */
27
+ export declare function replaceFragment(score: Score, fragment: ScoreFragment): Score;
28
+ //# sourceMappingURL=fragment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragment.d.ts","sourceRoot":"","sources":["../../../src/domain/score/fragment.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD;;;;GAIG;AACH,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,+GAA+G;AAC/G,wBAAgB,eAAe,CAC7B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,UAAU,GAChB,aAAa,CAMf;AAUD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,GAAG,KAAK,CA6C5E"}
@@ -0,0 +1,68 @@
1
+ import { rebuildMeasureTicks } from './factory.js';
2
+ import { measuresInRange } from './queries.js';
3
+ /** Captures the measures overlapping `range` (per track named in `range.trackIds`, or all tracks if empty). */
4
+ export function extractFragment(score, range) {
5
+ return {
6
+ range,
7
+ ppq: score.ppq,
8
+ tracks: measuresInRange(score, range),
9
+ };
10
+ }
11
+ /** Whether a measure's span overlaps `[range.startTick, range.endTick)`. */
12
+ function overlapsRange(measure, range) {
13
+ return (measure.startTick < range.endTick &&
14
+ measure.startTick + measure.durationTicks > range.startTick);
15
+ }
16
+ /**
17
+ * Splices `fragment`'s measures into `score`, one track at a time. For each
18
+ * track named in `fragment.tracks`, the contiguous block of that track's
19
+ * existing measures overlapping `fragment.range` is replaced by the
20
+ * fragment's measures for that track (deep-replacing their voices); tracks
21
+ * with no measures overlapping the range are left untouched. Track ids
22
+ * named in the fragment but absent from the score are ignored.
23
+ *
24
+ * Every replaced event's `trackId`/`voiceId` is renumbered to match its
25
+ * new, actual track and voice (never trusted from the incoming fragment),
26
+ * so a regenerated fragment's placeholder ids never leak into the score.
27
+ *
28
+ * Measure `index`/`startTick` for every track are then recomputed via
29
+ * `rebuildMeasureTicks`, which leaves measures outside the edited region
30
+ * referentially unchanged.
31
+ */
32
+ export function replaceFragment(score, fragment) {
33
+ const replacementsByTrackId = new Map(fragment.tracks.map(t => [t.trackId, t.measures]));
34
+ const tracks = score.tracks.map(track => {
35
+ const replacementMeasures = replacementsByTrackId.get(track.id);
36
+ if (!replacementMeasures)
37
+ return track;
38
+ const firstIndex = track.measures.findIndex(m => overlapsRange(m, fragment.range));
39
+ if (firstIndex === -1)
40
+ return track;
41
+ let lastIndex = firstIndex;
42
+ while (lastIndex < track.measures.length &&
43
+ overlapsRange(track.measures[lastIndex], fragment.range)) {
44
+ lastIndex += 1;
45
+ }
46
+ const normalizedMeasures = replacementMeasures.map(measure => ({
47
+ ...measure,
48
+ voices: measure.voices.map(voice => ({
49
+ ...voice,
50
+ events: voice.events.map(event => ({
51
+ ...event,
52
+ trackId: track.id,
53
+ voiceId: voice.id,
54
+ })),
55
+ })),
56
+ }));
57
+ return {
58
+ ...track,
59
+ measures: [
60
+ ...track.measures.slice(0, firstIndex),
61
+ ...normalizedMeasures,
62
+ ...track.measures.slice(lastIndex),
63
+ ],
64
+ };
65
+ });
66
+ return rebuildMeasureTicks({ ...score, tracks });
67
+ }
68
+ //# sourceMappingURL=fragment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fragment.js","sourceRoot":"","sources":["../../../src/domain/score/fragment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAW/C,+GAA+G;AAC/G,MAAM,UAAU,eAAe,CAC7B,KAAY,EACZ,KAAiB;IAEjB,OAAO;QACL,KAAK;QACL,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,SAAS,aAAa,CAAC,OAAgB,EAAE,KAAiB;IACxD,OAAO,CACL,OAAO,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO;QACjC,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,aAAa,GAAG,KAAK,CAAC,SAAS,CAC5D,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAAC,KAAY,EAAE,QAAuB;IACnE,MAAM,qBAAqB,GAAG,IAAI,GAAG,CACnC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAClD,CAAC;IAEF,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QACtC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,mBAAmB;YAAE,OAAO,KAAK,CAAC;QAEvC,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAC9C,aAAa,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CACjC,CAAC;QACF,IAAI,UAAU,KAAK,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QAEpC,IAAI,SAAS,GAAG,UAAU,CAAC;QAC3B,OACE,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM;YACjC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,EACxD,CAAC;YACD,SAAS,IAAI,CAAC,CAAC;QACjB,CAAC;QAED,MAAM,kBAAkB,GAAG,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC7D,GAAG,OAAO;YACV,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACnC,GAAG,KAAK;gBACR,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBACjC,GAAG,KAAK;oBACR,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,OAAO,EAAE,KAAK,CAAC,EAAE;iBAClB,CAAC,CAAC;aACJ,CAAC,CAAC;SACJ,CAAC,CAAC,CAAC;QAEJ,OAAO;YACL,GAAG,KAAK;YACR,QAAQ,EAAE;gBACR,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;gBACtC,GAAG,kBAAkB;gBACrB,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;aACnC;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,mBAAmB,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { UUID } from '../../index.js';
2
+ /**
3
+ * Generates a v4 UUID. Uses `crypto.randomUUID` when available, falling back
4
+ * to a `Math.random`-based generator in non-secure contexts (e.g. `http://`
5
+ * origins other than `localhost`) where `crypto.randomUUID` is undefined.
6
+ */
7
+ export declare function createId(): UUID;
8
+ //# sourceMappingURL=ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../../src/domain/score/ids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AA0B3C;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,IAAI,CAQ/B"}
@@ -0,0 +1,38 @@
1
+ const HEX_CHARS = '0123456789abcdef';
2
+ /**
3
+ * Fallback v4-UUID generator for non-secure contexts, where
4
+ * `crypto.randomUUID` is unavailable (it requires a secure context per the
5
+ * Web Crypto spec). Not cryptographically strong; adequate for local
6
+ * identifiers only.
7
+ */
8
+ function randomUuidFallback() {
9
+ let result = '';
10
+ for (let i = 0; i < 36; i += 1) {
11
+ if (i === 8 || i === 13 || i === 18 || i === 23) {
12
+ result += '-';
13
+ }
14
+ else if (i === 14) {
15
+ result += '4';
16
+ }
17
+ else if (i === 19) {
18
+ result += HEX_CHARS[8 + Math.floor(Math.random() * 4)]; // one of 8, 9, a, b
19
+ }
20
+ else {
21
+ result += HEX_CHARS[Math.floor(Math.random() * 16)];
22
+ }
23
+ }
24
+ return result;
25
+ }
26
+ /**
27
+ * Generates a v4 UUID. Uses `crypto.randomUUID` when available, falling back
28
+ * to a `Math.random`-based generator in non-secure contexts (e.g. `http://`
29
+ * origins other than `localhost`) where `crypto.randomUUID` is undefined.
30
+ */
31
+ export function createId() {
32
+ if (typeof crypto !== 'undefined' &&
33
+ typeof crypto.randomUUID === 'function') {
34
+ return crypto.randomUUID();
35
+ }
36
+ return randomUuidFallback();
37
+ }
38
+ //# sourceMappingURL=ids.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ids.js","sourceRoot":"","sources":["../../../src/domain/score/ids.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,kBAAkB,CAAC;AAErC;;;;;GAKG;AACH,SAAS,kBAAkB;IACzB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YAChD,MAAM,IAAI,GAAG,CAAC;QAChB,CAAC;aAAM,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,CAAC;QAChB,CAAC;aAAM,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB;QAC9E,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ;IACtB,IACE,OAAO,MAAM,KAAK,WAAW;QAC7B,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,EACvC,CAAC;QACD,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;IAC7B,CAAC;IACD,OAAO,kBAAkB,EAAE,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,34 @@
1
+ import type { ScoreRange } from '../selection/types.js';
2
+ import type { Measure, MusicalEvent, NoteEvent, Score, Track, UUID } from '../../index.js';
3
+ /** Finds a track by id, or `null` if no track has that id. */
4
+ export declare function findTrack(score: Score, trackId: UUID): Track | null;
5
+ /** Finds a measure by id across all tracks, or `null` if none has that id. */
6
+ export declare function findMeasure(score: Score, measureId: UUID): Measure | null;
7
+ /** Finds an event (note or rest) by id across all tracks/measures/voices, or `null`. */
8
+ export declare function findEvent(score: Score, eventId: UUID): MusicalEvent | null;
9
+ /** All note events (rests excluded) on the requested tracks overlapping the tick range. */
10
+ export declare function eventsInRange(score: Score, range: ScoreRange): NoteEvent[];
11
+ /** Measures overlapping the tick range, grouped by track. */
12
+ export declare function measuresInRange(score: Score, range: ScoreRange): Array<{
13
+ trackId: UUID;
14
+ measures: Measure[];
15
+ }>;
16
+ /**
17
+ * Finds a note event on `trackId` whose span covers `tick`
18
+ * (`startTick <= tick < startTick + durationTicks`), optionally also
19
+ * matching a specific MIDI pitch. Returns `null` if none match.
20
+ */
21
+ export declare function noteAt(score: Score, trackId: UUID, tick: number, midi?: number): NoteEvent | null;
22
+ /** The tick at which the score's last measure (across all tracks) ends; 0 if there are none. */
23
+ export declare function scoreEndTick(score: Score): number;
24
+ /** All note events (rests excluded) across every track/measure/voice. */
25
+ export declare function allNotes(score: Score): NoteEvent[];
26
+ /**
27
+ * `score` with only the named tracks, in score order.
28
+ *
29
+ * Returns `score` itself when `trackIds` names every track, so the common
30
+ * "nothing is hidden" export path costs nothing. Kept tracks are returned by
31
+ * reference — this filters, it never rewrites the music.
32
+ */
33
+ export declare function scoreWithTracks(score: Score, trackIds: string[]): Score;
34
+ //# sourceMappingURL=queries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../../src/domain/score/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,SAAS,EACT,KAAK,EACL,KAAK,EACL,IAAI,EACL,MAAM,gBAAgB,CAAC;AAIxB,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,CAEnE;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,GAAG,OAAO,GAAG,IAAI,CAMzE;AAED,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,GAAG,YAAY,GAAG,IAAI,CAU1E;AAkBD,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,CAkB1E;AAED,6DAA6D;AAC7D,wBAAgB,eAAe,CAC7B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,UAAU,GAChB,KAAK,CAAC;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC,CAU/C;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CACpB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,IAAI,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,GACZ,SAAS,GAAG,IAAI,CAmBlB;AAED,gGAAgG;AAChG,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CASjD;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,EAAE,CAYlD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,CAKvE"}