ts-flp 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -674
- package/README.md +12 -1
- package/dist/index.cjs +154 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +154 -147
- package/dist/index.js.map +1 -1
- package/dist/src/api/ProjectApi.d.ts.map +1 -1
- package/dist/src/generated/events.generated.d.ts.map +1 -1
- package/dist/src/parser/FlpParser.d.ts +2 -6
- package/dist/src/parser/FlpParser.d.ts.map +1 -1
- package/package.json +7 -5
- package/dist/test/dump-all-info.d.ts +0 -2
- package/dist/test/dump-all-info.d.ts.map +0 -1
- package/dist/test/flparser.test.d.ts +0 -6
- package/dist/test/flparser.test.d.ts.map +0 -1
- package/dist/vitest.config.d.ts +0 -3
- package/dist/vitest.config.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -160,6 +160,14 @@ interface PluginRef {
|
|
|
160
160
|
| `getFlVersion(parsed)` | Get the FL Studio version string |
|
|
161
161
|
| `getPPQ(parsed)` | Get the PPQ (Pulses Per Quarter note) |
|
|
162
162
|
|
|
163
|
+
### Write behavior
|
|
164
|
+
|
|
165
|
+
- `undefined` leaves the corresponding value unchanged.
|
|
166
|
+
- `null` clears text metadata, leaves BPM unchanged, and resets time values to zero.
|
|
167
|
+
- Missing metadata and timestamp events are created in the project preamble.
|
|
168
|
+
- BPM values are validated against the limits supported by the detected FL Studio version.
|
|
169
|
+
- Invalid dates, negative work times, and non-finite numeric values throw an error.
|
|
170
|
+
|
|
163
171
|
## Constraints and limitations
|
|
164
172
|
|
|
165
173
|
### What this library does
|
|
@@ -197,6 +205,9 @@ src/
|
|
|
197
205
|
# Install dependencies
|
|
198
206
|
pnpm install
|
|
199
207
|
|
|
208
|
+
# Add at least one private .flp file directly to test_projs/
|
|
209
|
+
# These fixtures are ignored by Git and are required by the integration tests.
|
|
210
|
+
|
|
200
211
|
# Type checking
|
|
201
212
|
pnpm run typecheck
|
|
202
213
|
|
|
@@ -215,7 +226,7 @@ pnpm run build
|
|
|
215
226
|
|
|
216
227
|
## License
|
|
217
228
|
|
|
218
|
-
[
|
|
229
|
+
[MIT](LICENSE)
|
|
219
230
|
|
|
220
231
|
---
|
|
221
232
|
|
package/dist/index.cjs
CHANGED
|
@@ -65,6 +65,9 @@ var WORD = 64;
|
|
|
65
65
|
var DWORD = 128;
|
|
66
66
|
var TEXT = 192;
|
|
67
67
|
var DATA = 208;
|
|
68
|
+
var FIXED_SIZE_OVERRIDES = {
|
|
69
|
+
172: 3
|
|
70
|
+
};
|
|
68
71
|
var NEW_TEXT_IDS = [
|
|
69
72
|
TEXT + 49,
|
|
70
73
|
// ArrangementID.Name
|
|
@@ -211,6 +214,7 @@ var EVENT_ID = {
|
|
|
211
214
|
// 236
|
|
212
215
|
};
|
|
213
216
|
var EVENT_KIND = {
|
|
217
|
+
[172]: "unknown",
|
|
214
218
|
// Project events
|
|
215
219
|
[EVENT_ID.PROJECT_LOOP_ACTIVE]: "u8",
|
|
216
220
|
[EVENT_ID.PROJECT_SHOW_INFO]: "u8",
|
|
@@ -319,6 +323,10 @@ function getEventKind(eventId) {
|
|
|
319
323
|
}
|
|
320
324
|
}
|
|
321
325
|
function getEventFixedSize(eventId) {
|
|
326
|
+
const override = FIXED_SIZE_OVERRIDES[eventId];
|
|
327
|
+
if (override !== void 0) {
|
|
328
|
+
return override;
|
|
329
|
+
}
|
|
322
330
|
if (eventId < WORD) {
|
|
323
331
|
return 1;
|
|
324
332
|
} else if (eventId < DWORD) {
|
|
@@ -595,62 +603,9 @@ function varIntSize(value) {
|
|
|
595
603
|
}
|
|
596
604
|
|
|
597
605
|
// src/parser/FlpParser.ts
|
|
598
|
-
var KNOWN_DWORD_IDS = new Set(
|
|
599
|
-
Object.keys(EVENT_KIND).map(Number).filter((id) => id >= DWORD && id < TEXT)
|
|
600
|
-
);
|
|
601
606
|
var FLP_HEADER_MAGIC = "FLhd";
|
|
602
607
|
var FLP_DATA_MAGIC = "FLdt";
|
|
603
608
|
var FLP_HEADER_SIZE = 6;
|
|
604
|
-
var LOOKAHEAD_BYTES = 200;
|
|
605
|
-
var LOOKAHEAD_VARINT_MAX = 1e5;
|
|
606
|
-
function readVarIntRaw(buf, off, end) {
|
|
607
|
-
let shift = 0;
|
|
608
|
-
let size = 0;
|
|
609
|
-
const start = off;
|
|
610
|
-
while (true) {
|
|
611
|
-
if (off >= end) return { size: -1, bytesRead: 0 };
|
|
612
|
-
const byte = buf[off];
|
|
613
|
-
off++;
|
|
614
|
-
size |= (byte & 127) << shift;
|
|
615
|
-
shift += 7;
|
|
616
|
-
if (!(byte & 128)) break;
|
|
617
|
-
}
|
|
618
|
-
return { size, bytesRead: off - start };
|
|
619
|
-
}
|
|
620
|
-
function scoreAlignment(buf, off, end, bytesToCheck) {
|
|
621
|
-
const limit = Math.min(off + bytesToCheck, end);
|
|
622
|
-
let textDataCount = 0;
|
|
623
|
-
let consecutiveSmallByte = 0;
|
|
624
|
-
let maxConsecutiveSmallByte = 0;
|
|
625
|
-
while (off < limit) {
|
|
626
|
-
const id = buf[off];
|
|
627
|
-
if (id < WORD) {
|
|
628
|
-
off += 2;
|
|
629
|
-
if (id < 32 && !KNOWN_DWORD_IDS.has(id)) {
|
|
630
|
-
consecutiveSmallByte++;
|
|
631
|
-
maxConsecutiveSmallByte = Math.max(maxConsecutiveSmallByte, consecutiveSmallByte);
|
|
632
|
-
} else {
|
|
633
|
-
consecutiveSmallByte = 0;
|
|
634
|
-
}
|
|
635
|
-
} else if (id < DWORD) {
|
|
636
|
-
off += 3;
|
|
637
|
-
consecutiveSmallByte = 0;
|
|
638
|
-
} else if (id < TEXT) {
|
|
639
|
-
off += 5;
|
|
640
|
-
consecutiveSmallByte = 0;
|
|
641
|
-
} else {
|
|
642
|
-
const vi = readVarIntRaw(buf, off + 1, end);
|
|
643
|
-
if (vi.size >= 0 && vi.size < LOOKAHEAD_VARINT_MAX && off + 1 + vi.bytesRead + vi.size <= end) {
|
|
644
|
-
textDataCount++;
|
|
645
|
-
off = off + 1 + vi.bytesRead + vi.size;
|
|
646
|
-
} else {
|
|
647
|
-
return -100;
|
|
648
|
-
}
|
|
649
|
-
consecutiveSmallByte = 0;
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
return textDataCount * 10 - maxConsecutiveSmallByte * 3;
|
|
653
|
-
}
|
|
654
609
|
function parseFlp(buffer) {
|
|
655
610
|
const reader = new BinaryReader(buffer);
|
|
656
611
|
const headerMagic = reader.readBytes(4).toString("ascii");
|
|
@@ -662,8 +617,8 @@ function parseFlp(buffer) {
|
|
|
662
617
|
throw new Error(`Invalid header size: expected ${FLP_HEADER_SIZE}, got ${headerSize}`);
|
|
663
618
|
}
|
|
664
619
|
const format = reader.readI16LE();
|
|
665
|
-
|
|
666
|
-
|
|
620
|
+
reader.readU16LE();
|
|
621
|
+
reader.readU16LE();
|
|
667
622
|
if (format < -1 || format > 80) {
|
|
668
623
|
throw new Error(`Invalid file format: ${format}`);
|
|
669
624
|
}
|
|
@@ -690,39 +645,10 @@ function parseFlp(buffer) {
|
|
|
690
645
|
const eventId = reader.readU8();
|
|
691
646
|
let payloadSize;
|
|
692
647
|
let headerSize2;
|
|
693
|
-
|
|
694
|
-
|
|
648
|
+
const fixedSize = getEventFixedSize(eventId);
|
|
649
|
+
if (fixedSize >= 0) {
|
|
650
|
+
payloadSize = fixedSize;
|
|
695
651
|
headerSize2 = 1;
|
|
696
|
-
} else if (eventId < DWORD) {
|
|
697
|
-
payloadSize = 2;
|
|
698
|
-
headerSize2 = 1;
|
|
699
|
-
} else if (eventId < TEXT) {
|
|
700
|
-
if (KNOWN_DWORD_IDS.has(eventId)) {
|
|
701
|
-
payloadSize = 4;
|
|
702
|
-
headerSize2 = 1;
|
|
703
|
-
} else {
|
|
704
|
-
const afterDword = eventStart + 1 + 4;
|
|
705
|
-
const vi = readVarIntRaw(buffer, eventStart + 1, eventsEnd);
|
|
706
|
-
if (vi.size < 0 || vi.size > LOOKAHEAD_VARINT_MAX || eventStart + 1 + vi.bytesRead + vi.size > eventsEnd) {
|
|
707
|
-
payloadSize = 4;
|
|
708
|
-
headerSize2 = 1;
|
|
709
|
-
} else if (vi.size === 3) {
|
|
710
|
-
payloadSize = 4;
|
|
711
|
-
headerSize2 = 1;
|
|
712
|
-
} else {
|
|
713
|
-
const afterVarInt = eventStart + 1 + vi.bytesRead + vi.size;
|
|
714
|
-
const dwordScore = scoreAlignment(buffer, afterDword, eventsEnd, LOOKAHEAD_BYTES);
|
|
715
|
-
const varIntScore = scoreAlignment(buffer, afterVarInt, eventsEnd, LOOKAHEAD_BYTES);
|
|
716
|
-
if (varIntScore > dwordScore + 2) {
|
|
717
|
-
payloadSize = vi.size;
|
|
718
|
-
reader.seek(eventStart + 1 + vi.bytesRead);
|
|
719
|
-
headerSize2 = 1 + vi.bytesRead;
|
|
720
|
-
} else {
|
|
721
|
-
payloadSize = 4;
|
|
722
|
-
headerSize2 = 1;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
652
|
} else {
|
|
727
653
|
const sizeStart = reader.tell();
|
|
728
654
|
payloadSize = reader.readVarInt();
|
|
@@ -905,6 +831,46 @@ function createNumberPayload(value, kind) {
|
|
|
905
831
|
}
|
|
906
832
|
|
|
907
833
|
// src/api/ProjectApi.ts
|
|
834
|
+
var PROJECT_EVENT_ORDER = [
|
|
835
|
+
EVENT_ID.PROJECT_TEMPO,
|
|
836
|
+
EVENT_ID.PROJECT_TITLE,
|
|
837
|
+
EVENT_ID.PROJECT_GENRE,
|
|
838
|
+
EVENT_ID.PROJECT_ARTISTS,
|
|
839
|
+
EVENT_ID.PROJECT_COMMENTS,
|
|
840
|
+
EVENT_ID.PROJECT_TIMESTAMP
|
|
841
|
+
];
|
|
842
|
+
function insertProjectEvent(parsed, event) {
|
|
843
|
+
const orderIndex = PROJECT_EVENT_ORDER.indexOf(event.id);
|
|
844
|
+
let insertionIndex = -1;
|
|
845
|
+
if (orderIndex >= 0) {
|
|
846
|
+
const laterIds = new Set(PROJECT_EVENT_ORDER.slice(orderIndex + 1));
|
|
847
|
+
insertionIndex = parsed.events.findIndex((candidate) => laterIds.has(candidate.id));
|
|
848
|
+
if (insertionIndex < 0) {
|
|
849
|
+
const previousIds = new Set(PROJECT_EVENT_ORDER.slice(0, orderIndex + 1));
|
|
850
|
+
for (let index = parsed.events.length - 1; index >= 0; index--) {
|
|
851
|
+
if (previousIds.has(parsed.events[index].id)) {
|
|
852
|
+
insertionIndex = index + 1;
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
if (insertionIndex < 0) {
|
|
859
|
+
insertionIndex = parsed.events.findIndex((candidate) => candidate.id === EVENT_ID.CHANNEL_NEW);
|
|
860
|
+
}
|
|
861
|
+
if (insertionIndex < 0) {
|
|
862
|
+
const versionIndex = parsed.events.findIndex(
|
|
863
|
+
(candidate) => candidate.id === EVENT_ID.PROJECT_FL_VERSION
|
|
864
|
+
);
|
|
865
|
+
insertionIndex = versionIndex >= 0 ? versionIndex + 1 : parsed.events.length;
|
|
866
|
+
}
|
|
867
|
+
const events = [...parsed.events];
|
|
868
|
+
events.splice(insertionIndex, 0, event);
|
|
869
|
+
return { ...parsed, events };
|
|
870
|
+
}
|
|
871
|
+
function replaceEventPayload(parsed, target, payload) {
|
|
872
|
+
return patchEvents(parsed, (event) => event === target ? { ...event, payload } : event);
|
|
873
|
+
}
|
|
908
874
|
function readBpm(parsed) {
|
|
909
875
|
const tempoEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TEMPO);
|
|
910
876
|
if (tempoEvent) {
|
|
@@ -934,48 +900,97 @@ function readProjectMeta(parsed) {
|
|
|
934
900
|
bpm: readBpm(parsed)
|
|
935
901
|
};
|
|
936
902
|
}
|
|
903
|
+
function compareVersion(version, expected) {
|
|
904
|
+
const actual = version.split(".").map((part) => Number.parseInt(part, 10));
|
|
905
|
+
const length = Math.max(actual.length, expected.length);
|
|
906
|
+
for (let index = 0; index < length; index++) {
|
|
907
|
+
const difference = (actual[index] ?? 0) - (expected[index] ?? 0);
|
|
908
|
+
if (difference !== 0) return Math.sign(difference);
|
|
909
|
+
}
|
|
910
|
+
return 0;
|
|
911
|
+
}
|
|
912
|
+
function validateBpm(parsed, bpm) {
|
|
913
|
+
if (!Number.isFinite(bpm)) {
|
|
914
|
+
throw new RangeError("BPM must be a finite number");
|
|
915
|
+
}
|
|
916
|
+
const legacyHighTempo = compareVersion(parsed.flVersion, [1, 4, 2]) >= 0 && compareVersion(parsed.flVersion, [11]) < 0;
|
|
917
|
+
const maximum = legacyHighTempo ? 999 : 522;
|
|
918
|
+
if (bpm < 10 || bpm > maximum) {
|
|
919
|
+
throw new RangeError(`BPM must be between 10 and ${maximum} for FL Studio ${parsed.flVersion}`);
|
|
920
|
+
}
|
|
921
|
+
if (parsed.flVersion !== "0.0.0" && compareVersion(parsed.flVersion, [3, 4]) < 0 && !Number.isInteger(bpm)) {
|
|
922
|
+
throw new TypeError(`FL Studio ${parsed.flVersion} does not support decimal BPM values`);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
function writeTextProjectEvent(parsed, eventId, value) {
|
|
926
|
+
if (value === void 0) return parsed;
|
|
927
|
+
const payload = createTextPayload(value ?? "", parsed.useUnicode);
|
|
928
|
+
const existing = findFirstEvent(parsed, eventId);
|
|
929
|
+
return existing ? replaceEventPayload(parsed, existing, payload) : insertProjectEvent(parsed, createEvent(eventId, payload));
|
|
930
|
+
}
|
|
931
|
+
function writeProjectBpm(parsed, bpm) {
|
|
932
|
+
if (bpm === void 0 || bpm === null) return parsed;
|
|
933
|
+
validateBpm(parsed, bpm);
|
|
934
|
+
const scaledBpm = Math.round(bpm * 1e3);
|
|
935
|
+
const coarseBpm = Math.floor(scaledBpm / 1e3);
|
|
936
|
+
const fineBpm = scaledBpm % 1e3;
|
|
937
|
+
const modernEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TEMPO);
|
|
938
|
+
const coarseEvent = findFirstEvent(parsed, EVENT_ID.PROJECT__TEMPO_COARSE);
|
|
939
|
+
const fineEvent = findFirstEvent(parsed, EVENT_ID.PROJECT__TEMPO_FINE);
|
|
940
|
+
let result = parsed;
|
|
941
|
+
if (modernEvent) {
|
|
942
|
+
result = replaceEventPayload(result, modernEvent, createNumberPayload(scaledBpm, "u32"));
|
|
943
|
+
}
|
|
944
|
+
if (coarseEvent) {
|
|
945
|
+
result = replaceEventPayload(result, coarseEvent, createNumberPayload(coarseBpm, "u16"));
|
|
946
|
+
}
|
|
947
|
+
if (fineEvent) {
|
|
948
|
+
result = replaceEventPayload(result, fineEvent, createNumberPayload(fineBpm, "u16"));
|
|
949
|
+
}
|
|
950
|
+
if (!modernEvent && !coarseEvent && !fineEvent) {
|
|
951
|
+
result = insertProjectEvent(
|
|
952
|
+
result,
|
|
953
|
+
createEvent(EVENT_ID.PROJECT_TEMPO, createNumberPayload(scaledBpm, "u32"))
|
|
954
|
+
);
|
|
955
|
+
} else if (!modernEvent && coarseEvent && !fineEvent && fineBpm > 0) {
|
|
956
|
+
const updatedCoarse = findFirstEvent(result, EVENT_ID.PROJECT__TEMPO_COARSE);
|
|
957
|
+
const index = result.events.indexOf(updatedCoarse) + 1;
|
|
958
|
+
const events = [...result.events];
|
|
959
|
+
events.splice(
|
|
960
|
+
index,
|
|
961
|
+
0,
|
|
962
|
+
createEvent(EVENT_ID.PROJECT__TEMPO_FINE, createNumberPayload(fineBpm, "u16"))
|
|
963
|
+
);
|
|
964
|
+
result = { ...result, events };
|
|
965
|
+
} else if (!modernEvent && !coarseEvent && fineEvent) {
|
|
966
|
+
const updatedFine = findFirstEvent(result, EVENT_ID.PROJECT__TEMPO_FINE);
|
|
967
|
+
const index = result.events.indexOf(updatedFine);
|
|
968
|
+
const events = [...result.events];
|
|
969
|
+
events.splice(
|
|
970
|
+
index,
|
|
971
|
+
0,
|
|
972
|
+
createEvent(EVENT_ID.PROJECT__TEMPO_COARSE, createNumberPayload(coarseBpm, "u16"))
|
|
973
|
+
);
|
|
974
|
+
result = { ...result, events };
|
|
975
|
+
}
|
|
976
|
+
return result;
|
|
977
|
+
}
|
|
937
978
|
function writeProjectMeta(parsed, meta) {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
if (meta.description !== void 0 && event.id === EVENT_ID.PROJECT_COMMENTS) {
|
|
946
|
-
return {
|
|
947
|
-
...event,
|
|
948
|
-
payload: createTextPayload(meta.description ?? "", parsed.useUnicode)
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
if (meta.artist !== void 0 && event.id === EVENT_ID.PROJECT_ARTISTS) {
|
|
952
|
-
return {
|
|
953
|
-
...event,
|
|
954
|
-
payload: createTextPayload(meta.artist ?? "", parsed.useUnicode)
|
|
955
|
-
};
|
|
956
|
-
}
|
|
957
|
-
if (meta.genre !== void 0 && event.id === EVENT_ID.PROJECT_GENRE) {
|
|
958
|
-
return {
|
|
959
|
-
...event,
|
|
960
|
-
payload: createTextPayload(meta.genre ?? "", parsed.useUnicode)
|
|
961
|
-
};
|
|
962
|
-
}
|
|
963
|
-
if (meta.bpm && Math.abs(meta.bpm) > 0 && event.id === EVENT_ID.PROJECT_TEMPO) {
|
|
964
|
-
return {
|
|
965
|
-
...event,
|
|
966
|
-
payload: createNumberPayload(Math.round(Math.abs(meta.bpm) * 1e3), "u32")
|
|
967
|
-
};
|
|
968
|
-
}
|
|
969
|
-
return event;
|
|
970
|
-
});
|
|
979
|
+
let result = parsed;
|
|
980
|
+
result = writeProjectBpm(result, meta.bpm);
|
|
981
|
+
result = writeTextProjectEvent(result, EVENT_ID.PROJECT_TITLE, meta.name);
|
|
982
|
+
result = writeTextProjectEvent(result, EVENT_ID.PROJECT_GENRE, meta.genre);
|
|
983
|
+
result = writeTextProjectEvent(result, EVENT_ID.PROJECT_ARTISTS, meta.artist);
|
|
984
|
+
result = writeTextProjectEvent(result, EVENT_ID.PROJECT_COMMENTS, meta.description);
|
|
985
|
+
return result;
|
|
971
986
|
}
|
|
972
|
-
var
|
|
987
|
+
var DELPHI_EPOCH_MS = Date.UTC(1899, 11, 30);
|
|
973
988
|
function delphiToDate(days) {
|
|
974
989
|
const ms = days * 24 * 60 * 60 * 1e3;
|
|
975
|
-
return new Date(
|
|
990
|
+
return new Date(DELPHI_EPOCH_MS + ms);
|
|
976
991
|
}
|
|
977
992
|
function dateToDelphiDays(date) {
|
|
978
|
-
const ms = date.getTime() -
|
|
993
|
+
const ms = date.getTime() - DELPHI_EPOCH_MS;
|
|
979
994
|
return ms / (24 * 60 * 60 * 1e3);
|
|
980
995
|
}
|
|
981
996
|
function readProjectTimeInfo(parsed) {
|
|
@@ -996,31 +1011,23 @@ function readProjectTimeInfo(parsed) {
|
|
|
996
1011
|
};
|
|
997
1012
|
}
|
|
998
1013
|
function writeProjectTimeInfo(parsed, info) {
|
|
1014
|
+
if (info.creationDate === void 0 && info.workTimeSeconds === void 0) {
|
|
1015
|
+
return parsed;
|
|
1016
|
+
}
|
|
999
1017
|
const current = readProjectTimeInfo(parsed);
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
writer.writeF64LE(info.workTimeSeconds / (24 * 60 * 60));
|
|
1014
|
-
} else if (current.workTimeSeconds !== null) {
|
|
1015
|
-
writer.writeF64LE(current.workTimeSeconds / (24 * 60 * 60));
|
|
1016
|
-
} else {
|
|
1017
|
-
writer.writeF64LE(0);
|
|
1018
|
-
}
|
|
1019
|
-
return {
|
|
1020
|
-
...event,
|
|
1021
|
-
payload: writer.toBuffer()
|
|
1022
|
-
};
|
|
1023
|
-
});
|
|
1018
|
+
if (info.creationDate !== void 0 && info.creationDate !== null && Number.isNaN(info.creationDate.getTime())) {
|
|
1019
|
+
throw new RangeError("creationDate must be a valid Date");
|
|
1020
|
+
}
|
|
1021
|
+
if (info.workTimeSeconds !== void 0 && info.workTimeSeconds !== null && (!Number.isFinite(info.workTimeSeconds) || info.workTimeSeconds < 0)) {
|
|
1022
|
+
throw new RangeError("workTimeSeconds must be a finite, non-negative number");
|
|
1023
|
+
}
|
|
1024
|
+
const creationDateDays = info.creationDate === null ? 0 : info.creationDate !== void 0 ? dateToDelphiDays(info.creationDate) : current.creationDate !== null ? dateToDelphiDays(current.creationDate) : 0;
|
|
1025
|
+
const workTimeDays = info.workTimeSeconds === null ? 0 : info.workTimeSeconds !== void 0 ? info.workTimeSeconds / (24 * 60 * 60) : current.workTimeSeconds !== null ? current.workTimeSeconds / (24 * 60 * 60) : 0;
|
|
1026
|
+
const writer = new BinaryWriter();
|
|
1027
|
+
writer.writeF64LE(creationDateDays);
|
|
1028
|
+
writer.writeF64LE(workTimeDays);
|
|
1029
|
+
const timestampEvent = findFirstEvent(parsed, EVENT_ID.PROJECT_TIMESTAMP);
|
|
1030
|
+
return timestampEvent ? replaceEventPayload(parsed, timestampEvent, writer.toBuffer()) : insertProjectEvent(parsed, createEvent(EVENT_ID.PROJECT_TIMESTAMP, writer.toBuffer()));
|
|
1024
1031
|
}
|
|
1025
1032
|
function listSamples(parsed) {
|
|
1026
1033
|
const samples = [];
|