minotor 3.0.1 → 3.0.2
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/.cspell.json +12 -1
- package/.gitattributes +3 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +3 -0
- package/.github/workflows/minotor.yml +17 -1
- package/CHANGELOG.md +2 -2
- package/README.md +34 -14
- package/dist/__e2e__/router.test.d.ts +1 -0
- package/dist/cli/perf.d.ts +28 -0
- package/dist/cli/utils.d.ts +6 -2
- package/dist/cli.mjs +1967 -823
- package/dist/cli.mjs.map +1 -1
- package/dist/gtfs/trips.d.ts +1 -0
- package/dist/gtfs/utils.d.ts +1 -1
- package/dist/parser.cjs.js +1030 -627
- package/dist/parser.cjs.js.map +1 -1
- package/dist/parser.d.ts +4 -2
- package/dist/parser.esm.js +1030 -627
- package/dist/parser.esm.js.map +1 -1
- package/dist/router.cjs.js +1 -1
- package/dist/router.cjs.js.map +1 -1
- package/dist/router.d.ts +10 -5
- package/dist/router.esm.js +1 -1
- package/dist/router.esm.js.map +1 -1
- package/dist/router.umd.js +1 -1
- package/dist/router.umd.js.map +1 -1
- package/dist/routing/__tests__/result.test.d.ts +1 -0
- package/dist/routing/query.d.ts +27 -6
- package/dist/routing/result.d.ts +1 -1
- package/dist/routing/route.d.ts +47 -2
- package/dist/routing/router.d.ts +15 -1
- package/dist/stops/stopsIndex.d.ts +3 -3
- package/dist/timetable/__tests__/route.test.d.ts +1 -0
- package/dist/timetable/__tests__/time.test.d.ts +1 -0
- package/dist/timetable/io.d.ts +7 -1
- package/dist/timetable/proto/timetable.d.ts +1 -1
- package/dist/timetable/route.d.ts +155 -0
- package/dist/timetable/time.d.ts +21 -0
- package/dist/timetable/timetable.d.ts +41 -61
- package/package.json +36 -34
- package/src/__e2e__/benchmark.json +22 -0
- package/src/__e2e__/router.test.ts +209 -0
- package/src/__e2e__/timetable/stops.bin +3 -0
- package/src/__e2e__/timetable/timetable.bin +3 -0
- package/src/cli/minotor.ts +51 -1
- package/src/cli/perf.ts +136 -0
- package/src/cli/repl.ts +26 -13
- package/src/cli/utils.ts +6 -28
- package/src/gtfs/__tests__/parser.test.ts +12 -15
- package/src/gtfs/__tests__/services.test.ts +1 -0
- package/src/gtfs/__tests__/transfers.test.ts +0 -1
- package/src/gtfs/__tests__/trips.test.ts +67 -74
- package/src/gtfs/profiles/ch.ts +1 -1
- package/src/gtfs/routes.ts +4 -4
- package/src/gtfs/services.ts +15 -2
- package/src/gtfs/stops.ts +7 -3
- package/src/gtfs/transfers.ts +6 -3
- package/src/gtfs/trips.ts +33 -16
- package/src/gtfs/utils.ts +13 -2
- package/src/parser.ts +4 -2
- package/src/router.ts +17 -11
- package/src/routing/__tests__/result.test.ts +392 -0
- package/src/routing/__tests__/router.test.ts +94 -137
- package/src/routing/query.ts +28 -7
- package/src/routing/result.ts +10 -5
- package/src/routing/route.ts +95 -9
- package/src/routing/router.ts +82 -66
- package/src/stops/__tests__/io.test.ts +1 -1
- package/src/stops/__tests__/stopFinder.test.ts +1 -1
- package/src/stops/proto/stops.ts +4 -4
- package/src/stops/stopsIndex.ts +3 -3
- package/src/timetable/__tests__/io.test.ts +16 -23
- package/src/timetable/__tests__/route.test.ts +317 -0
- package/src/timetable/__tests__/time.test.ts +494 -0
- package/src/timetable/__tests__/timetable.test.ts +64 -75
- package/src/timetable/io.ts +32 -26
- package/src/timetable/proto/timetable.proto +1 -1
- package/src/timetable/proto/timetable.ts +13 -13
- package/src/timetable/route.ts +347 -0
- package/src/timetable/time.ts +40 -8
- package/src/timetable/timetable.ts +74 -165
- package/tsconfig.build.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import require$$2$1 from 'path';
|
|
|
10
10
|
import require$$3$1 from 'events';
|
|
11
11
|
import require$$4$1 from 'zlib';
|
|
12
12
|
import require$$5, { Transform } from 'stream';
|
|
13
|
+
import { performance } from 'perf_hooks';
|
|
13
14
|
import repl from 'node:repl';
|
|
14
15
|
|
|
15
16
|
/******************************************************************************
|
|
@@ -44,7 +45,7 @@ function __values(o) {
|
|
|
44
45
|
if (m) return m.call(o);
|
|
45
46
|
if (o && typeof o.length === "number") return {
|
|
46
47
|
next: function () {
|
|
47
|
-
if (o && i >= o.length) o =
|
|
48
|
+
if (o && i >= o.length) o = void 0;
|
|
48
49
|
return { value: o && o[i++], done: !o };
|
|
49
50
|
}
|
|
50
51
|
};
|
|
@@ -302,11 +303,24 @@ function requireHelp () {
|
|
|
302
303
|
class Help {
|
|
303
304
|
constructor() {
|
|
304
305
|
this.helpWidth = undefined;
|
|
306
|
+
this.minWidthToWrap = 40;
|
|
305
307
|
this.sortSubcommands = false;
|
|
306
308
|
this.sortOptions = false;
|
|
307
309
|
this.showGlobalOptions = false;
|
|
308
310
|
}
|
|
309
311
|
|
|
312
|
+
/**
|
|
313
|
+
* prepareContext is called by Commander after applying overrides from `Command.configureHelp()`
|
|
314
|
+
* and just before calling `formatHelp()`.
|
|
315
|
+
*
|
|
316
|
+
* Commander just uses the helpWidth and the rest is provided for optional use by more complex subclasses.
|
|
317
|
+
*
|
|
318
|
+
* @param {{ error?: boolean, helpWidth?: number, outputHasColors?: boolean }} contextOptions
|
|
319
|
+
*/
|
|
320
|
+
prepareContext(contextOptions) {
|
|
321
|
+
this.helpWidth = this.helpWidth ?? contextOptions.helpWidth ?? 80;
|
|
322
|
+
}
|
|
323
|
+
|
|
310
324
|
/**
|
|
311
325
|
* Get an array of the visible subcommands. Includes a placeholder for the implicit help command, if there is one.
|
|
312
326
|
*
|
|
@@ -481,7 +495,12 @@ function requireHelp () {
|
|
|
481
495
|
|
|
482
496
|
longestSubcommandTermLength(cmd, helper) {
|
|
483
497
|
return helper.visibleCommands(cmd).reduce((max, command) => {
|
|
484
|
-
return Math.max(
|
|
498
|
+
return Math.max(
|
|
499
|
+
max,
|
|
500
|
+
this.displayWidth(
|
|
501
|
+
helper.styleSubcommandTerm(helper.subcommandTerm(command)),
|
|
502
|
+
),
|
|
503
|
+
);
|
|
485
504
|
}, 0);
|
|
486
505
|
}
|
|
487
506
|
|
|
@@ -495,7 +514,10 @@ function requireHelp () {
|
|
|
495
514
|
|
|
496
515
|
longestOptionTermLength(cmd, helper) {
|
|
497
516
|
return helper.visibleOptions(cmd).reduce((max, option) => {
|
|
498
|
-
return Math.max(
|
|
517
|
+
return Math.max(
|
|
518
|
+
max,
|
|
519
|
+
this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))),
|
|
520
|
+
);
|
|
499
521
|
}, 0);
|
|
500
522
|
}
|
|
501
523
|
|
|
@@ -509,7 +531,10 @@ function requireHelp () {
|
|
|
509
531
|
|
|
510
532
|
longestGlobalOptionTermLength(cmd, helper) {
|
|
511
533
|
return helper.visibleGlobalOptions(cmd).reduce((max, option) => {
|
|
512
|
-
return Math.max(
|
|
534
|
+
return Math.max(
|
|
535
|
+
max,
|
|
536
|
+
this.displayWidth(helper.styleOptionTerm(helper.optionTerm(option))),
|
|
537
|
+
);
|
|
513
538
|
}, 0);
|
|
514
539
|
}
|
|
515
540
|
|
|
@@ -523,7 +548,12 @@ function requireHelp () {
|
|
|
523
548
|
|
|
524
549
|
longestArgumentTermLength(cmd, helper) {
|
|
525
550
|
return helper.visibleArguments(cmd).reduce((max, argument) => {
|
|
526
|
-
return Math.max(
|
|
551
|
+
return Math.max(
|
|
552
|
+
max,
|
|
553
|
+
this.displayWidth(
|
|
554
|
+
helper.styleArgumentTerm(helper.argumentTerm(argument)),
|
|
555
|
+
),
|
|
556
|
+
);
|
|
527
557
|
}, 0);
|
|
528
558
|
}
|
|
529
559
|
|
|
@@ -613,7 +643,11 @@ function requireHelp () {
|
|
|
613
643
|
extraInfo.push(`env: ${option.envVar}`);
|
|
614
644
|
}
|
|
615
645
|
if (extraInfo.length > 0) {
|
|
616
|
-
|
|
646
|
+
const extraDescription = `(${extraInfo.join(', ')})`;
|
|
647
|
+
if (option.description) {
|
|
648
|
+
return `${option.description} ${extraDescription}`;
|
|
649
|
+
}
|
|
650
|
+
return extraDescription;
|
|
617
651
|
}
|
|
618
652
|
|
|
619
653
|
return option.description;
|
|
@@ -640,15 +674,55 @@ function requireHelp () {
|
|
|
640
674
|
);
|
|
641
675
|
}
|
|
642
676
|
if (extraInfo.length > 0) {
|
|
643
|
-
const
|
|
677
|
+
const extraDescription = `(${extraInfo.join(', ')})`;
|
|
644
678
|
if (argument.description) {
|
|
645
|
-
return `${argument.description} ${
|
|
679
|
+
return `${argument.description} ${extraDescription}`;
|
|
646
680
|
}
|
|
647
|
-
return
|
|
681
|
+
return extraDescription;
|
|
648
682
|
}
|
|
649
683
|
return argument.description;
|
|
650
684
|
}
|
|
651
685
|
|
|
686
|
+
/**
|
|
687
|
+
* Format a list of items, given a heading and an array of formatted items.
|
|
688
|
+
*
|
|
689
|
+
* @param {string} heading
|
|
690
|
+
* @param {string[]} items
|
|
691
|
+
* @param {Help} helper
|
|
692
|
+
* @returns string[]
|
|
693
|
+
*/
|
|
694
|
+
formatItemList(heading, items, helper) {
|
|
695
|
+
if (items.length === 0) return [];
|
|
696
|
+
|
|
697
|
+
return [helper.styleTitle(heading), ...items, ''];
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Group items by their help group heading.
|
|
702
|
+
*
|
|
703
|
+
* @param {Command[] | Option[]} unsortedItems
|
|
704
|
+
* @param {Command[] | Option[]} visibleItems
|
|
705
|
+
* @param {Function} getGroup
|
|
706
|
+
* @returns {Map<string, Command[] | Option[]>}
|
|
707
|
+
*/
|
|
708
|
+
groupItems(unsortedItems, visibleItems, getGroup) {
|
|
709
|
+
const result = new Map();
|
|
710
|
+
// Add groups in order of appearance in unsortedItems.
|
|
711
|
+
unsortedItems.forEach((item) => {
|
|
712
|
+
const group = getGroup(item);
|
|
713
|
+
if (!result.has(group)) result.set(group, []);
|
|
714
|
+
});
|
|
715
|
+
// Add items in order of appearance in visibleItems.
|
|
716
|
+
visibleItems.forEach((item) => {
|
|
717
|
+
const group = getGroup(item);
|
|
718
|
+
if (!result.has(group)) {
|
|
719
|
+
result.set(group, []);
|
|
720
|
+
}
|
|
721
|
+
result.get(group).push(item);
|
|
722
|
+
});
|
|
723
|
+
return result;
|
|
724
|
+
}
|
|
725
|
+
|
|
652
726
|
/**
|
|
653
727
|
* Generate the built-in help text.
|
|
654
728
|
*
|
|
@@ -659,90 +733,171 @@ function requireHelp () {
|
|
|
659
733
|
|
|
660
734
|
formatHelp(cmd, helper) {
|
|
661
735
|
const termWidth = helper.padWidth(cmd, helper);
|
|
662
|
-
const helpWidth = helper.helpWidth
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
if (description) {
|
|
667
|
-
const fullText = `${term.padEnd(termWidth + itemSeparatorWidth)}${description}`;
|
|
668
|
-
return helper.wrap(
|
|
669
|
-
fullText,
|
|
670
|
-
helpWidth - itemIndentWidth,
|
|
671
|
-
termWidth + itemSeparatorWidth,
|
|
672
|
-
);
|
|
673
|
-
}
|
|
674
|
-
return term;
|
|
675
|
-
}
|
|
676
|
-
function formatList(textArray) {
|
|
677
|
-
return textArray.join('\n').replace(/^/gm, ' '.repeat(itemIndentWidth));
|
|
736
|
+
const helpWidth = helper.helpWidth ?? 80; // in case prepareContext() was not called
|
|
737
|
+
|
|
738
|
+
function callFormatItem(term, description) {
|
|
739
|
+
return helper.formatItem(term, termWidth, description, helper);
|
|
678
740
|
}
|
|
679
741
|
|
|
680
742
|
// Usage
|
|
681
|
-
let output = [
|
|
743
|
+
let output = [
|
|
744
|
+
`${helper.styleTitle('Usage:')} ${helper.styleUsage(helper.commandUsage(cmd))}`,
|
|
745
|
+
'',
|
|
746
|
+
];
|
|
682
747
|
|
|
683
748
|
// Description
|
|
684
749
|
const commandDescription = helper.commandDescription(cmd);
|
|
685
750
|
if (commandDescription.length > 0) {
|
|
686
751
|
output = output.concat([
|
|
687
|
-
helper.
|
|
752
|
+
helper.boxWrap(
|
|
753
|
+
helper.styleCommandDescription(commandDescription),
|
|
754
|
+
helpWidth,
|
|
755
|
+
),
|
|
688
756
|
'',
|
|
689
757
|
]);
|
|
690
758
|
}
|
|
691
759
|
|
|
692
760
|
// Arguments
|
|
693
761
|
const argumentList = helper.visibleArguments(cmd).map((argument) => {
|
|
694
|
-
return
|
|
695
|
-
helper.argumentTerm(argument),
|
|
696
|
-
helper.argumentDescription(argument),
|
|
762
|
+
return callFormatItem(
|
|
763
|
+
helper.styleArgumentTerm(helper.argumentTerm(argument)),
|
|
764
|
+
helper.styleArgumentDescription(helper.argumentDescription(argument)),
|
|
697
765
|
);
|
|
698
766
|
});
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
767
|
+
output = output.concat(
|
|
768
|
+
this.formatItemList('Arguments:', argumentList, helper),
|
|
769
|
+
);
|
|
702
770
|
|
|
703
771
|
// Options
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
772
|
+
const optionGroups = this.groupItems(
|
|
773
|
+
cmd.options,
|
|
774
|
+
helper.visibleOptions(cmd),
|
|
775
|
+
(option) => option.helpGroupHeading ?? 'Options:',
|
|
776
|
+
);
|
|
777
|
+
optionGroups.forEach((options, group) => {
|
|
778
|
+
const optionList = options.map((option) => {
|
|
779
|
+
return callFormatItem(
|
|
780
|
+
helper.styleOptionTerm(helper.optionTerm(option)),
|
|
781
|
+
helper.styleOptionDescription(helper.optionDescription(option)),
|
|
782
|
+
);
|
|
783
|
+
});
|
|
784
|
+
output = output.concat(this.formatItemList(group, optionList, helper));
|
|
709
785
|
});
|
|
710
|
-
if (optionList.length > 0) {
|
|
711
|
-
output = output.concat(['Options:', formatList(optionList), '']);
|
|
712
|
-
}
|
|
713
786
|
|
|
714
|
-
if (
|
|
787
|
+
if (helper.showGlobalOptions) {
|
|
715
788
|
const globalOptionList = helper
|
|
716
789
|
.visibleGlobalOptions(cmd)
|
|
717
790
|
.map((option) => {
|
|
718
|
-
return
|
|
719
|
-
helper.optionTerm(option),
|
|
720
|
-
helper.optionDescription(option),
|
|
791
|
+
return callFormatItem(
|
|
792
|
+
helper.styleOptionTerm(helper.optionTerm(option)),
|
|
793
|
+
helper.styleOptionDescription(helper.optionDescription(option)),
|
|
721
794
|
);
|
|
722
795
|
});
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
formatList(globalOptionList),
|
|
727
|
-
'',
|
|
728
|
-
]);
|
|
729
|
-
}
|
|
796
|
+
output = output.concat(
|
|
797
|
+
this.formatItemList('Global Options:', globalOptionList, helper),
|
|
798
|
+
);
|
|
730
799
|
}
|
|
731
800
|
|
|
732
801
|
// Commands
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
802
|
+
const commandGroups = this.groupItems(
|
|
803
|
+
cmd.commands,
|
|
804
|
+
helper.visibleCommands(cmd),
|
|
805
|
+
(sub) => sub.helpGroup() || 'Commands:',
|
|
806
|
+
);
|
|
807
|
+
commandGroups.forEach((commands, group) => {
|
|
808
|
+
const commandList = commands.map((sub) => {
|
|
809
|
+
return callFormatItem(
|
|
810
|
+
helper.styleSubcommandTerm(helper.subcommandTerm(sub)),
|
|
811
|
+
helper.styleSubcommandDescription(helper.subcommandDescription(sub)),
|
|
812
|
+
);
|
|
813
|
+
});
|
|
814
|
+
output = output.concat(this.formatItemList(group, commandList, helper));
|
|
738
815
|
});
|
|
739
|
-
if (commandList.length > 0) {
|
|
740
|
-
output = output.concat(['Commands:', formatList(commandList), '']);
|
|
741
|
-
}
|
|
742
816
|
|
|
743
817
|
return output.join('\n');
|
|
744
818
|
}
|
|
745
819
|
|
|
820
|
+
/**
|
|
821
|
+
* Return display width of string, ignoring ANSI escape sequences. Used in padding and wrapping calculations.
|
|
822
|
+
*
|
|
823
|
+
* @param {string} str
|
|
824
|
+
* @returns {number}
|
|
825
|
+
*/
|
|
826
|
+
displayWidth(str) {
|
|
827
|
+
return stripColor(str).length;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
/**
|
|
831
|
+
* Style the title for displaying in the help. Called with 'Usage:', 'Options:', etc.
|
|
832
|
+
*
|
|
833
|
+
* @param {string} str
|
|
834
|
+
* @returns {string}
|
|
835
|
+
*/
|
|
836
|
+
styleTitle(str) {
|
|
837
|
+
return str;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
styleUsage(str) {
|
|
841
|
+
// Usage has lots of parts the user might like to color separately! Assume default usage string which is formed like:
|
|
842
|
+
// command subcommand [options] [command] <foo> [bar]
|
|
843
|
+
return str
|
|
844
|
+
.split(' ')
|
|
845
|
+
.map((word) => {
|
|
846
|
+
if (word === '[options]') return this.styleOptionText(word);
|
|
847
|
+
if (word === '[command]') return this.styleSubcommandText(word);
|
|
848
|
+
if (word[0] === '[' || word[0] === '<')
|
|
849
|
+
return this.styleArgumentText(word);
|
|
850
|
+
return this.styleCommandText(word); // Restrict to initial words?
|
|
851
|
+
})
|
|
852
|
+
.join(' ');
|
|
853
|
+
}
|
|
854
|
+
styleCommandDescription(str) {
|
|
855
|
+
return this.styleDescriptionText(str);
|
|
856
|
+
}
|
|
857
|
+
styleOptionDescription(str) {
|
|
858
|
+
return this.styleDescriptionText(str);
|
|
859
|
+
}
|
|
860
|
+
styleSubcommandDescription(str) {
|
|
861
|
+
return this.styleDescriptionText(str);
|
|
862
|
+
}
|
|
863
|
+
styleArgumentDescription(str) {
|
|
864
|
+
return this.styleDescriptionText(str);
|
|
865
|
+
}
|
|
866
|
+
styleDescriptionText(str) {
|
|
867
|
+
return str;
|
|
868
|
+
}
|
|
869
|
+
styleOptionTerm(str) {
|
|
870
|
+
return this.styleOptionText(str);
|
|
871
|
+
}
|
|
872
|
+
styleSubcommandTerm(str) {
|
|
873
|
+
// This is very like usage with lots of parts! Assume default string which is formed like:
|
|
874
|
+
// subcommand [options] <foo> [bar]
|
|
875
|
+
return str
|
|
876
|
+
.split(' ')
|
|
877
|
+
.map((word) => {
|
|
878
|
+
if (word === '[options]') return this.styleOptionText(word);
|
|
879
|
+
if (word[0] === '[' || word[0] === '<')
|
|
880
|
+
return this.styleArgumentText(word);
|
|
881
|
+
return this.styleSubcommandText(word); // Restrict to initial words?
|
|
882
|
+
})
|
|
883
|
+
.join(' ');
|
|
884
|
+
}
|
|
885
|
+
styleArgumentTerm(str) {
|
|
886
|
+
return this.styleArgumentText(str);
|
|
887
|
+
}
|
|
888
|
+
styleOptionText(str) {
|
|
889
|
+
return str;
|
|
890
|
+
}
|
|
891
|
+
styleArgumentText(str) {
|
|
892
|
+
return str;
|
|
893
|
+
}
|
|
894
|
+
styleSubcommandText(str) {
|
|
895
|
+
return str;
|
|
896
|
+
}
|
|
897
|
+
styleCommandText(str) {
|
|
898
|
+
return str;
|
|
899
|
+
}
|
|
900
|
+
|
|
746
901
|
/**
|
|
747
902
|
* Calculate the pad width from the maximum term length.
|
|
748
903
|
*
|
|
@@ -761,53 +916,126 @@ function requireHelp () {
|
|
|
761
916
|
}
|
|
762
917
|
|
|
763
918
|
/**
|
|
764
|
-
*
|
|
765
|
-
* Do not wrap if insufficient room for wrapping (minColumnWidth), or string is manually formatted.
|
|
919
|
+
* Detect manually wrapped and indented strings by checking for line break followed by whitespace.
|
|
766
920
|
*
|
|
767
921
|
* @param {string} str
|
|
768
|
-
* @
|
|
769
|
-
* @param {number} indent
|
|
770
|
-
* @param {number} [minColumnWidth=40]
|
|
771
|
-
* @return {string}
|
|
772
|
-
*
|
|
922
|
+
* @returns {boolean}
|
|
773
923
|
*/
|
|
924
|
+
preformatted(str) {
|
|
925
|
+
return /\n[^\S\r\n]/.test(str);
|
|
926
|
+
}
|
|
774
927
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
const
|
|
790
|
-
const
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
928
|
+
/**
|
|
929
|
+
* Format the "item", which consists of a term and description. Pad the term and wrap the description, indenting the following lines.
|
|
930
|
+
*
|
|
931
|
+
* So "TTT", 5, "DDD DDDD DD DDD" might be formatted for this.helpWidth=17 like so:
|
|
932
|
+
* TTT DDD DDDD
|
|
933
|
+
* DD DDD
|
|
934
|
+
*
|
|
935
|
+
* @param {string} term
|
|
936
|
+
* @param {number} termWidth
|
|
937
|
+
* @param {string} description
|
|
938
|
+
* @param {Help} helper
|
|
939
|
+
* @returns {string}
|
|
940
|
+
*/
|
|
941
|
+
formatItem(term, termWidth, description, helper) {
|
|
942
|
+
const itemIndent = 2;
|
|
943
|
+
const itemIndentStr = ' '.repeat(itemIndent);
|
|
944
|
+
if (!description) return itemIndentStr + term;
|
|
945
|
+
|
|
946
|
+
// Pad the term out to a consistent width, so descriptions are aligned.
|
|
947
|
+
const paddedTerm = term.padEnd(
|
|
948
|
+
termWidth + term.length - helper.displayWidth(term),
|
|
796
949
|
);
|
|
797
|
-
|
|
950
|
+
|
|
951
|
+
// Format the description.
|
|
952
|
+
const spacerWidth = 2; // between term and description
|
|
953
|
+
const helpWidth = this.helpWidth ?? 80; // in case prepareContext() was not called
|
|
954
|
+
const remainingWidth = helpWidth - termWidth - spacerWidth - itemIndent;
|
|
955
|
+
let formattedDescription;
|
|
956
|
+
if (
|
|
957
|
+
remainingWidth < this.minWidthToWrap ||
|
|
958
|
+
helper.preformatted(description)
|
|
959
|
+
) {
|
|
960
|
+
formattedDescription = description;
|
|
961
|
+
} else {
|
|
962
|
+
const wrappedDescription = helper.boxWrap(description, remainingWidth);
|
|
963
|
+
formattedDescription = wrappedDescription.replace(
|
|
964
|
+
/\n/g,
|
|
965
|
+
'\n' + ' '.repeat(termWidth + spacerWidth),
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// Construct and overall indent.
|
|
798
970
|
return (
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
return (i > 0 ? indentString : '') + line.trimEnd();
|
|
804
|
-
})
|
|
805
|
-
.join('\n')
|
|
971
|
+
itemIndentStr +
|
|
972
|
+
paddedTerm +
|
|
973
|
+
' '.repeat(spacerWidth) +
|
|
974
|
+
formattedDescription.replace(/\n/g, `\n${itemIndentStr}`)
|
|
806
975
|
);
|
|
807
976
|
}
|
|
977
|
+
|
|
978
|
+
/**
|
|
979
|
+
* Wrap a string at whitespace, preserving existing line breaks.
|
|
980
|
+
* Wrapping is skipped if the width is less than `minWidthToWrap`.
|
|
981
|
+
*
|
|
982
|
+
* @param {string} str
|
|
983
|
+
* @param {number} width
|
|
984
|
+
* @returns {string}
|
|
985
|
+
*/
|
|
986
|
+
boxWrap(str, width) {
|
|
987
|
+
if (width < this.minWidthToWrap) return str;
|
|
988
|
+
|
|
989
|
+
const rawLines = str.split(/\r\n|\n/);
|
|
990
|
+
// split up text by whitespace
|
|
991
|
+
const chunkPattern = /[\s]*[^\s]+/g;
|
|
992
|
+
const wrappedLines = [];
|
|
993
|
+
rawLines.forEach((line) => {
|
|
994
|
+
const chunks = line.match(chunkPattern);
|
|
995
|
+
if (chunks === null) {
|
|
996
|
+
wrappedLines.push('');
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
let sumChunks = [chunks.shift()];
|
|
1001
|
+
let sumWidth = this.displayWidth(sumChunks[0]);
|
|
1002
|
+
chunks.forEach((chunk) => {
|
|
1003
|
+
const visibleWidth = this.displayWidth(chunk);
|
|
1004
|
+
// Accumulate chunks while they fit into width.
|
|
1005
|
+
if (sumWidth + visibleWidth <= width) {
|
|
1006
|
+
sumChunks.push(chunk);
|
|
1007
|
+
sumWidth += visibleWidth;
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
wrappedLines.push(sumChunks.join(''));
|
|
1011
|
+
|
|
1012
|
+
const nextChunk = chunk.trimStart(); // trim space at line break
|
|
1013
|
+
sumChunks = [nextChunk];
|
|
1014
|
+
sumWidth = this.displayWidth(nextChunk);
|
|
1015
|
+
});
|
|
1016
|
+
wrappedLines.push(sumChunks.join(''));
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
return wrappedLines.join('\n');
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/**
|
|
1024
|
+
* Strip style ANSI escape sequences from the string. In particular, SGR (Select Graphic Rendition) codes.
|
|
1025
|
+
*
|
|
1026
|
+
* @param {string} str
|
|
1027
|
+
* @returns {string}
|
|
1028
|
+
* @package
|
|
1029
|
+
*/
|
|
1030
|
+
|
|
1031
|
+
function stripColor(str) {
|
|
1032
|
+
// eslint-disable-next-line no-control-regex
|
|
1033
|
+
const sgrPattern = /\x1b\[\d*(;\d*)*m/g;
|
|
1034
|
+
return str.replace(sgrPattern, '');
|
|
808
1035
|
}
|
|
809
1036
|
|
|
810
1037
|
help.Help = Help;
|
|
1038
|
+
help.stripColor = stripColor;
|
|
811
1039
|
return help;
|
|
812
1040
|
}
|
|
813
1041
|
|
|
@@ -838,7 +1066,7 @@ function requireOption () {
|
|
|
838
1066
|
this.variadic = /\w\.\.\.[>\]]$/.test(flags); // The option can take multiple values.
|
|
839
1067
|
this.mandatory = false; // The option must have a value after parsing, which usually means it must be specified on command line.
|
|
840
1068
|
const optionFlags = splitOptionFlags(flags);
|
|
841
|
-
this.short = optionFlags.shortFlag;
|
|
1069
|
+
this.short = optionFlags.shortFlag; // May be a short flag, undefined, or even a long flag (if option has two long flags).
|
|
842
1070
|
this.long = optionFlags.longFlag;
|
|
843
1071
|
this.negate = false;
|
|
844
1072
|
if (this.long) {
|
|
@@ -853,6 +1081,7 @@ function requireOption () {
|
|
|
853
1081
|
this.argChoices = undefined;
|
|
854
1082
|
this.conflictsWith = [];
|
|
855
1083
|
this.implied = undefined;
|
|
1084
|
+
this.helpGroupHeading = undefined; // soft initialised when option added to command
|
|
856
1085
|
}
|
|
857
1086
|
|
|
858
1087
|
/**
|
|
@@ -1027,13 +1256,27 @@ function requireOption () {
|
|
|
1027
1256
|
|
|
1028
1257
|
/**
|
|
1029
1258
|
* Return option name, in a camelcase format that can be used
|
|
1030
|
-
* as
|
|
1259
|
+
* as an object attribute key.
|
|
1031
1260
|
*
|
|
1032
1261
|
* @return {string}
|
|
1033
1262
|
*/
|
|
1034
1263
|
|
|
1035
1264
|
attributeName() {
|
|
1036
|
-
|
|
1265
|
+
if (this.negate) {
|
|
1266
|
+
return camelcase(this.name().replace(/^no-/, ''));
|
|
1267
|
+
}
|
|
1268
|
+
return camelcase(this.name());
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
/**
|
|
1272
|
+
* Set the help group heading.
|
|
1273
|
+
*
|
|
1274
|
+
* @param {string} heading
|
|
1275
|
+
* @return {Option}
|
|
1276
|
+
*/
|
|
1277
|
+
helpGroup(heading) {
|
|
1278
|
+
this.helpGroupHeading = heading;
|
|
1279
|
+
return this;
|
|
1037
1280
|
}
|
|
1038
1281
|
|
|
1039
1282
|
/**
|
|
@@ -1132,17 +1375,51 @@ function requireOption () {
|
|
|
1132
1375
|
function splitOptionFlags(flags) {
|
|
1133
1376
|
let shortFlag;
|
|
1134
1377
|
let longFlag;
|
|
1135
|
-
//
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1378
|
+
// short flag, single dash and single character
|
|
1379
|
+
const shortFlagExp = /^-[^-]$/;
|
|
1380
|
+
// long flag, double dash and at least one character
|
|
1381
|
+
const longFlagExp = /^--[^-]/;
|
|
1382
|
+
|
|
1383
|
+
const flagParts = flags.split(/[ |,]+/).concat('guard');
|
|
1384
|
+
// Normal is short and/or long.
|
|
1385
|
+
if (shortFlagExp.test(flagParts[0])) shortFlag = flagParts.shift();
|
|
1386
|
+
if (longFlagExp.test(flagParts[0])) longFlag = flagParts.shift();
|
|
1387
|
+
// Long then short. Rarely used but fine.
|
|
1388
|
+
if (!shortFlag && shortFlagExp.test(flagParts[0]))
|
|
1139
1389
|
shortFlag = flagParts.shift();
|
|
1140
|
-
|
|
1141
|
-
//
|
|
1142
|
-
if (!shortFlag &&
|
|
1390
|
+
// Allow two long flags, like '--ws, --workspace'
|
|
1391
|
+
// This is the supported way to have a shortish option flag.
|
|
1392
|
+
if (!shortFlag && longFlagExp.test(flagParts[0])) {
|
|
1143
1393
|
shortFlag = longFlag;
|
|
1144
|
-
longFlag =
|
|
1394
|
+
longFlag = flagParts.shift();
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
// Check for unprocessed flag. Fail noisily rather than silently ignore.
|
|
1398
|
+
if (flagParts[0].startsWith('-')) {
|
|
1399
|
+
const unsupportedFlag = flagParts[0];
|
|
1400
|
+
const baseError = `option creation failed due to '${unsupportedFlag}' in option flags '${flags}'`;
|
|
1401
|
+
if (/^-[^-][^-]/.test(unsupportedFlag))
|
|
1402
|
+
throw new Error(
|
|
1403
|
+
`${baseError}
|
|
1404
|
+
- a short flag is a single dash and a single character
|
|
1405
|
+
- either use a single dash and a single character (for a short flag)
|
|
1406
|
+
- or use a double dash for a long option (and can have two, like '--ws, --workspace')`,
|
|
1407
|
+
);
|
|
1408
|
+
if (shortFlagExp.test(unsupportedFlag))
|
|
1409
|
+
throw new Error(`${baseError}
|
|
1410
|
+
- too many short flags`);
|
|
1411
|
+
if (longFlagExp.test(unsupportedFlag))
|
|
1412
|
+
throw new Error(`${baseError}
|
|
1413
|
+
- too many long flags`);
|
|
1414
|
+
|
|
1415
|
+
throw new Error(`${baseError}
|
|
1416
|
+
- unrecognised flag format`);
|
|
1145
1417
|
}
|
|
1418
|
+
if (shortFlag === undefined && longFlag === undefined)
|
|
1419
|
+
throw new Error(
|
|
1420
|
+
`option creation failed due to no flags found in '${flags}'.`,
|
|
1421
|
+
);
|
|
1422
|
+
|
|
1146
1423
|
return { shortFlag, longFlag };
|
|
1147
1424
|
}
|
|
1148
1425
|
|
|
@@ -1275,7 +1552,7 @@ function requireCommand () {
|
|
|
1275
1552
|
|
|
1276
1553
|
const { Argument, humanReadableArgName } = requireArgument();
|
|
1277
1554
|
const { CommanderError } = requireError();
|
|
1278
|
-
const { Help } = requireHelp();
|
|
1555
|
+
const { Help, stripColor } = requireHelp();
|
|
1279
1556
|
const { Option, DualOptions } = requireOption();
|
|
1280
1557
|
const { suggestSimilar } = requireSuggestSimilar();
|
|
1281
1558
|
|
|
@@ -1294,7 +1571,7 @@ function requireCommand () {
|
|
|
1294
1571
|
this.options = [];
|
|
1295
1572
|
this.parent = null;
|
|
1296
1573
|
this._allowUnknownOption = false;
|
|
1297
|
-
this._allowExcessArguments =
|
|
1574
|
+
this._allowExcessArguments = false;
|
|
1298
1575
|
/** @type {Argument[]} */
|
|
1299
1576
|
this.registeredArguments = [];
|
|
1300
1577
|
this._args = this.registeredArguments; // deprecated old name
|
|
@@ -1324,16 +1601,22 @@ function requireCommand () {
|
|
|
1324
1601
|
/** @type {(boolean | string)} */
|
|
1325
1602
|
this._showHelpAfterError = false;
|
|
1326
1603
|
this._showSuggestionAfterError = true;
|
|
1604
|
+
this._savedState = null; // used in save/restoreStateBeforeParse
|
|
1327
1605
|
|
|
1328
|
-
// see
|
|
1606
|
+
// see configureOutput() for docs
|
|
1329
1607
|
this._outputConfiguration = {
|
|
1330
1608
|
writeOut: (str) => process.stdout.write(str),
|
|
1331
1609
|
writeErr: (str) => process.stderr.write(str),
|
|
1610
|
+
outputError: (str, write) => write(str),
|
|
1332
1611
|
getOutHelpWidth: () =>
|
|
1333
1612
|
process.stdout.isTTY ? process.stdout.columns : undefined,
|
|
1334
1613
|
getErrHelpWidth: () =>
|
|
1335
1614
|
process.stderr.isTTY ? process.stderr.columns : undefined,
|
|
1336
|
-
|
|
1615
|
+
getOutHasColors: () =>
|
|
1616
|
+
useColor() ?? (process.stdout.isTTY && process.stdout.hasColors?.()),
|
|
1617
|
+
getErrHasColors: () =>
|
|
1618
|
+
useColor() ?? (process.stderr.isTTY && process.stderr.hasColors?.()),
|
|
1619
|
+
stripColor: (str) => stripColor(str),
|
|
1337
1620
|
};
|
|
1338
1621
|
|
|
1339
1622
|
this._hidden = false;
|
|
@@ -1343,6 +1626,12 @@ function requireCommand () {
|
|
|
1343
1626
|
/** @type {Command} */
|
|
1344
1627
|
this._helpCommand = undefined; // lazy initialised, inherited
|
|
1345
1628
|
this._helpConfiguration = {};
|
|
1629
|
+
/** @type {string | undefined} */
|
|
1630
|
+
this._helpGroupHeading = undefined; // soft initialised when added to parent
|
|
1631
|
+
/** @type {string | undefined} */
|
|
1632
|
+
this._defaultCommandGroup = undefined;
|
|
1633
|
+
/** @type {string | undefined} */
|
|
1634
|
+
this._defaultOptionGroup = undefined;
|
|
1346
1635
|
}
|
|
1347
1636
|
|
|
1348
1637
|
/**
|
|
@@ -1482,14 +1771,18 @@ function requireCommand () {
|
|
|
1482
1771
|
*
|
|
1483
1772
|
* The configuration properties are all functions:
|
|
1484
1773
|
*
|
|
1485
|
-
* //
|
|
1774
|
+
* // change how output being written, defaults to stdout and stderr
|
|
1486
1775
|
* writeOut(str)
|
|
1487
1776
|
* writeErr(str)
|
|
1488
|
-
* //
|
|
1777
|
+
* // change how output being written for errors, defaults to writeErr
|
|
1778
|
+
* outputError(str, write) // used for displaying errors and not used for displaying help
|
|
1779
|
+
* // specify width for wrapping help
|
|
1489
1780
|
* getOutHelpWidth()
|
|
1490
1781
|
* getErrHelpWidth()
|
|
1491
|
-
* //
|
|
1492
|
-
*
|
|
1782
|
+
* // color support, currently only used with Help
|
|
1783
|
+
* getOutHasColors()
|
|
1784
|
+
* getErrHasColors()
|
|
1785
|
+
* stripColor() // used to remove ANSI escape codes if output does not have colors
|
|
1493
1786
|
*
|
|
1494
1787
|
* @param {object} [configuration] - configuration options
|
|
1495
1788
|
* @return {(Command | object)} `this` command for chaining, or stored configuration
|
|
@@ -1498,7 +1791,11 @@ function requireCommand () {
|
|
|
1498
1791
|
configureOutput(configuration) {
|
|
1499
1792
|
if (configuration === undefined) return this._outputConfiguration;
|
|
1500
1793
|
|
|
1501
|
-
Object.assign(
|
|
1794
|
+
this._outputConfiguration = Object.assign(
|
|
1795
|
+
{},
|
|
1796
|
+
this._outputConfiguration,
|
|
1797
|
+
configuration,
|
|
1798
|
+
);
|
|
1502
1799
|
return this;
|
|
1503
1800
|
}
|
|
1504
1801
|
|
|
@@ -1579,16 +1876,16 @@ function requireCommand () {
|
|
|
1579
1876
|
*
|
|
1580
1877
|
* @param {string} name
|
|
1581
1878
|
* @param {string} [description]
|
|
1582
|
-
* @param {(Function|*)} [
|
|
1879
|
+
* @param {(Function|*)} [parseArg] - custom argument processing function or default value
|
|
1583
1880
|
* @param {*} [defaultValue]
|
|
1584
1881
|
* @return {Command} `this` command for chaining
|
|
1585
1882
|
*/
|
|
1586
|
-
argument(name, description,
|
|
1883
|
+
argument(name, description, parseArg, defaultValue) {
|
|
1587
1884
|
const argument = this.createArgument(name, description);
|
|
1588
|
-
if (typeof
|
|
1589
|
-
argument.default(defaultValue).argParser(
|
|
1885
|
+
if (typeof parseArg === 'function') {
|
|
1886
|
+
argument.default(defaultValue).argParser(parseArg);
|
|
1590
1887
|
} else {
|
|
1591
|
-
argument.default(
|
|
1888
|
+
argument.default(parseArg);
|
|
1592
1889
|
}
|
|
1593
1890
|
this.addArgument(argument);
|
|
1594
1891
|
return this;
|
|
@@ -1659,11 +1956,15 @@ function requireCommand () {
|
|
|
1659
1956
|
helpCommand(enableOrNameAndArgs, description) {
|
|
1660
1957
|
if (typeof enableOrNameAndArgs === 'boolean') {
|
|
1661
1958
|
this._addImplicitHelpCommand = enableOrNameAndArgs;
|
|
1959
|
+
if (enableOrNameAndArgs && this._defaultCommandGroup) {
|
|
1960
|
+
// make the command to store the group
|
|
1961
|
+
this._initCommandGroup(this._getHelpCommand());
|
|
1962
|
+
}
|
|
1662
1963
|
return this;
|
|
1663
1964
|
}
|
|
1664
1965
|
|
|
1665
|
-
|
|
1666
|
-
const [, helpName, helpArgs] =
|
|
1966
|
+
const nameAndArgs = enableOrNameAndArgs ?? 'help [command]';
|
|
1967
|
+
const [, helpName, helpArgs] = nameAndArgs.match(/([^ ]+) *(.*)/);
|
|
1667
1968
|
const helpDescription = description ?? 'display help for command';
|
|
1668
1969
|
|
|
1669
1970
|
const helpCommand = this.createCommand(helpName);
|
|
@@ -1673,6 +1974,8 @@ function requireCommand () {
|
|
|
1673
1974
|
|
|
1674
1975
|
this._addImplicitHelpCommand = true;
|
|
1675
1976
|
this._helpCommand = helpCommand;
|
|
1977
|
+
// init group unless lazy create
|
|
1978
|
+
if (enableOrNameAndArgs || description) this._initCommandGroup(helpCommand);
|
|
1676
1979
|
|
|
1677
1980
|
return this;
|
|
1678
1981
|
}
|
|
@@ -1694,6 +1997,7 @@ function requireCommand () {
|
|
|
1694
1997
|
|
|
1695
1998
|
this._addImplicitHelpCommand = true;
|
|
1696
1999
|
this._helpCommand = helpCommand;
|
|
2000
|
+
this._initCommandGroup(helpCommand);
|
|
1697
2001
|
return this;
|
|
1698
2002
|
}
|
|
1699
2003
|
|
|
@@ -1870,6 +2174,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1870
2174
|
- already used by option '${matchingOption.flags}'`);
|
|
1871
2175
|
}
|
|
1872
2176
|
|
|
2177
|
+
this._initOptionGroup(option);
|
|
1873
2178
|
this.options.push(option);
|
|
1874
2179
|
}
|
|
1875
2180
|
|
|
@@ -1897,6 +2202,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1897
2202
|
);
|
|
1898
2203
|
}
|
|
1899
2204
|
|
|
2205
|
+
this._initCommandGroup(command);
|
|
1900
2206
|
this.commands.push(command);
|
|
1901
2207
|
}
|
|
1902
2208
|
|
|
@@ -2013,7 +2319,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2013
2319
|
* @example
|
|
2014
2320
|
* program
|
|
2015
2321
|
* .option('-p, --pepper', 'add pepper')
|
|
2016
|
-
* .option('
|
|
2322
|
+
* .option('--pt, --pizza-type <TYPE>', 'type of pizza') // required option-argument
|
|
2017
2323
|
* .option('-c, --cheese [CHEESE]', 'add extra cheese', 'mozzarella') // optional option-argument with default
|
|
2018
2324
|
* .option('-t, --tip <VALUE>', 'add tip to purchase cost', parseFloat) // custom parse function
|
|
2019
2325
|
*
|
|
@@ -2327,6 +2633,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2327
2633
|
*/
|
|
2328
2634
|
|
|
2329
2635
|
parse(argv, parseOptions) {
|
|
2636
|
+
this._prepareForParse();
|
|
2330
2637
|
const userArgs = this._prepareUserArgs(argv, parseOptions);
|
|
2331
2638
|
this._parseCommand([], userArgs);
|
|
2332
2639
|
|
|
@@ -2355,12 +2662,82 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2355
2662
|
*/
|
|
2356
2663
|
|
|
2357
2664
|
async parseAsync(argv, parseOptions) {
|
|
2665
|
+
this._prepareForParse();
|
|
2358
2666
|
const userArgs = this._prepareUserArgs(argv, parseOptions);
|
|
2359
2667
|
await this._parseCommand([], userArgs);
|
|
2360
2668
|
|
|
2361
2669
|
return this;
|
|
2362
2670
|
}
|
|
2363
2671
|
|
|
2672
|
+
_prepareForParse() {
|
|
2673
|
+
if (this._savedState === null) {
|
|
2674
|
+
this.saveStateBeforeParse();
|
|
2675
|
+
} else {
|
|
2676
|
+
this.restoreStateBeforeParse();
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
/**
|
|
2681
|
+
* Called the first time parse is called to save state and allow a restore before subsequent calls to parse.
|
|
2682
|
+
* Not usually called directly, but available for subclasses to save their custom state.
|
|
2683
|
+
*
|
|
2684
|
+
* This is called in a lazy way. Only commands used in parsing chain will have state saved.
|
|
2685
|
+
*/
|
|
2686
|
+
saveStateBeforeParse() {
|
|
2687
|
+
this._savedState = {
|
|
2688
|
+
// name is stable if supplied by author, but may be unspecified for root command and deduced during parsing
|
|
2689
|
+
_name: this._name,
|
|
2690
|
+
// option values before parse have default values (including false for negated options)
|
|
2691
|
+
// shallow clones
|
|
2692
|
+
_optionValues: { ...this._optionValues },
|
|
2693
|
+
_optionValueSources: { ...this._optionValueSources },
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
/**
|
|
2698
|
+
* Restore state before parse for calls after the first.
|
|
2699
|
+
* Not usually called directly, but available for subclasses to save their custom state.
|
|
2700
|
+
*
|
|
2701
|
+
* This is called in a lazy way. Only commands used in parsing chain will have state restored.
|
|
2702
|
+
*/
|
|
2703
|
+
restoreStateBeforeParse() {
|
|
2704
|
+
if (this._storeOptionsAsProperties)
|
|
2705
|
+
throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
|
|
2706
|
+
- either make a new Command for each call to parse, or stop storing options as properties`);
|
|
2707
|
+
|
|
2708
|
+
// clear state from _prepareUserArgs
|
|
2709
|
+
this._name = this._savedState._name;
|
|
2710
|
+
this._scriptPath = null;
|
|
2711
|
+
this.rawArgs = [];
|
|
2712
|
+
// clear state from setOptionValueWithSource
|
|
2713
|
+
this._optionValues = { ...this._savedState._optionValues };
|
|
2714
|
+
this._optionValueSources = { ...this._savedState._optionValueSources };
|
|
2715
|
+
// clear state from _parseCommand
|
|
2716
|
+
this.args = [];
|
|
2717
|
+
// clear state from _processArguments
|
|
2718
|
+
this.processedArgs = [];
|
|
2719
|
+
}
|
|
2720
|
+
|
|
2721
|
+
/**
|
|
2722
|
+
* Throw if expected executable is missing. Add lots of help for author.
|
|
2723
|
+
*
|
|
2724
|
+
* @param {string} executableFile
|
|
2725
|
+
* @param {string} executableDir
|
|
2726
|
+
* @param {string} subcommandName
|
|
2727
|
+
*/
|
|
2728
|
+
_checkForMissingExecutable(executableFile, executableDir, subcommandName) {
|
|
2729
|
+
if (fs.existsSync(executableFile)) return;
|
|
2730
|
+
|
|
2731
|
+
const executableDirMessage = executableDir
|
|
2732
|
+
? `searched for local subcommand relative to directory '${executableDir}'`
|
|
2733
|
+
: 'no directory for search for local subcommand, use .executableDir() to supply a custom directory';
|
|
2734
|
+
const executableMissing = `'${executableFile}' does not exist
|
|
2735
|
+
- if '${subcommandName}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
|
|
2736
|
+
- if the default executable name is not suitable, use the executableFile option to supply a custom name or path
|
|
2737
|
+
- ${executableDirMessage}`;
|
|
2738
|
+
throw new Error(executableMissing);
|
|
2739
|
+
}
|
|
2740
|
+
|
|
2364
2741
|
/**
|
|
2365
2742
|
* Execute a sub-command executable.
|
|
2366
2743
|
*
|
|
@@ -2401,7 +2778,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2401
2778
|
let resolvedScriptPath; // resolve possible symlink for installed npm binary
|
|
2402
2779
|
try {
|
|
2403
2780
|
resolvedScriptPath = fs.realpathSync(this._scriptPath);
|
|
2404
|
-
} catch
|
|
2781
|
+
} catch {
|
|
2405
2782
|
resolvedScriptPath = this._scriptPath;
|
|
2406
2783
|
}
|
|
2407
2784
|
executableDir = path.resolve(
|
|
@@ -2444,6 +2821,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2444
2821
|
proc = childProcess.spawn(executableFile, args, { stdio: 'inherit' });
|
|
2445
2822
|
}
|
|
2446
2823
|
} else {
|
|
2824
|
+
this._checkForMissingExecutable(
|
|
2825
|
+
executableFile,
|
|
2826
|
+
executableDir,
|
|
2827
|
+
subcommand._name,
|
|
2828
|
+
);
|
|
2447
2829
|
args.unshift(executableFile);
|
|
2448
2830
|
// add executable arguments to spawn
|
|
2449
2831
|
args = incrementNodeInspectorPort(process.execArgv).concat(args);
|
|
@@ -2482,14 +2864,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2482
2864
|
proc.on('error', (err) => {
|
|
2483
2865
|
// @ts-ignore: because err.code is an unknown property
|
|
2484
2866
|
if (err.code === 'ENOENT') {
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
- if the default executable name is not suitable, use the executableFile option to supply a custom name or path
|
|
2491
|
-
- ${executableDirMessage}`;
|
|
2492
|
-
throw new Error(executableMissing);
|
|
2867
|
+
this._checkForMissingExecutable(
|
|
2868
|
+
executableFile,
|
|
2869
|
+
executableDir,
|
|
2870
|
+
subcommand._name,
|
|
2871
|
+
);
|
|
2493
2872
|
// @ts-ignore: because err.code is an unknown property
|
|
2494
2873
|
} else if (err.code === 'EACCES') {
|
|
2495
2874
|
throw new Error(`'${executableFile}' not executable`);
|
|
@@ -2519,6 +2898,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2519
2898
|
const subCommand = this._findCommand(commandName);
|
|
2520
2899
|
if (!subCommand) this.help({ error: true });
|
|
2521
2900
|
|
|
2901
|
+
subCommand._prepareForParse();
|
|
2522
2902
|
let promiseChain;
|
|
2523
2903
|
promiseChain = this._chainOrCallSubCommandHook(
|
|
2524
2904
|
promiseChain,
|
|
@@ -2896,6 +3276,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2896
3276
|
* Parse options from `argv` removing known options,
|
|
2897
3277
|
* and return argv split into operands and unknown arguments.
|
|
2898
3278
|
*
|
|
3279
|
+
* Side effects: modifies command by storing options. Does not reset state if called again.
|
|
3280
|
+
*
|
|
2899
3281
|
* Examples:
|
|
2900
3282
|
*
|
|
2901
3283
|
* argv => operands, unknown
|
|
@@ -2918,6 +3300,17 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2918
3300
|
return arg.length > 1 && arg[0] === '-';
|
|
2919
3301
|
}
|
|
2920
3302
|
|
|
3303
|
+
const negativeNumberArg = (arg) => {
|
|
3304
|
+
// return false if not a negative number
|
|
3305
|
+
if (!/^-\d*\.?\d+(e[+-]?\d+)?$/.test(arg)) return false;
|
|
3306
|
+
// negative number is ok unless digit used as an option in command hierarchy
|
|
3307
|
+
return !this._getCommandAndAncestors().some((cmd) =>
|
|
3308
|
+
cmd.options
|
|
3309
|
+
.map((opt) => opt.short)
|
|
3310
|
+
.some((short) => /^-\d$/.test(short)),
|
|
3311
|
+
);
|
|
3312
|
+
};
|
|
3313
|
+
|
|
2921
3314
|
// parse options
|
|
2922
3315
|
let activeVariadicOption = null;
|
|
2923
3316
|
while (args.length) {
|
|
@@ -2930,7 +3323,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2930
3323
|
break;
|
|
2931
3324
|
}
|
|
2932
3325
|
|
|
2933
|
-
if (
|
|
3326
|
+
if (
|
|
3327
|
+
activeVariadicOption &&
|
|
3328
|
+
(!maybeOption(arg) || negativeNumberArg(arg))
|
|
3329
|
+
) {
|
|
2934
3330
|
this.emit(`option:${activeVariadicOption.name()}`, arg);
|
|
2935
3331
|
continue;
|
|
2936
3332
|
}
|
|
@@ -2947,7 +3343,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2947
3343
|
} else if (option.optional) {
|
|
2948
3344
|
let value = null;
|
|
2949
3345
|
// historical behaviour is optional value is following arg unless an option
|
|
2950
|
-
if (
|
|
3346
|
+
if (
|
|
3347
|
+
args.length > 0 &&
|
|
3348
|
+
(!maybeOption(args[0]) || negativeNumberArg(args[0]))
|
|
3349
|
+
) {
|
|
2951
3350
|
value = args.shift();
|
|
2952
3351
|
}
|
|
2953
3352
|
this.emit(`option:${option.name()}`, value);
|
|
@@ -2993,7 +3392,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2993
3392
|
// Might be a command-argument, or subcommand option, or unknown option, or help command or option.
|
|
2994
3393
|
|
|
2995
3394
|
// An unknown option means further arguments also classified as unknown so can be reprocessed by subcommands.
|
|
2996
|
-
|
|
3395
|
+
// A negative number in a leaf command is not an unknown option.
|
|
3396
|
+
if (
|
|
3397
|
+
dest === operands &&
|
|
3398
|
+
maybeOption(arg) &&
|
|
3399
|
+
!(this.commands.length === 0 && negativeNumberArg(arg))
|
|
3400
|
+
) {
|
|
2997
3401
|
dest = unknown;
|
|
2998
3402
|
}
|
|
2999
3403
|
|
|
@@ -3475,6 +3879,75 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3475
3879
|
return this;
|
|
3476
3880
|
}
|
|
3477
3881
|
|
|
3882
|
+
/**
|
|
3883
|
+
* Set/get the help group heading for this subcommand in parent command's help.
|
|
3884
|
+
*
|
|
3885
|
+
* @param {string} [heading]
|
|
3886
|
+
* @return {Command | string}
|
|
3887
|
+
*/
|
|
3888
|
+
|
|
3889
|
+
helpGroup(heading) {
|
|
3890
|
+
if (heading === undefined) return this._helpGroupHeading ?? '';
|
|
3891
|
+
this._helpGroupHeading = heading;
|
|
3892
|
+
return this;
|
|
3893
|
+
}
|
|
3894
|
+
|
|
3895
|
+
/**
|
|
3896
|
+
* Set/get the default help group heading for subcommands added to this command.
|
|
3897
|
+
* (This does not override a group set directly on the subcommand using .helpGroup().)
|
|
3898
|
+
*
|
|
3899
|
+
* @example
|
|
3900
|
+
* program.commandsGroup('Development Commands:);
|
|
3901
|
+
* program.command('watch')...
|
|
3902
|
+
* program.command('lint')...
|
|
3903
|
+
* ...
|
|
3904
|
+
*
|
|
3905
|
+
* @param {string} [heading]
|
|
3906
|
+
* @returns {Command | string}
|
|
3907
|
+
*/
|
|
3908
|
+
commandsGroup(heading) {
|
|
3909
|
+
if (heading === undefined) return this._defaultCommandGroup ?? '';
|
|
3910
|
+
this._defaultCommandGroup = heading;
|
|
3911
|
+
return this;
|
|
3912
|
+
}
|
|
3913
|
+
|
|
3914
|
+
/**
|
|
3915
|
+
* Set/get the default help group heading for options added to this command.
|
|
3916
|
+
* (This does not override a group set directly on the option using .helpGroup().)
|
|
3917
|
+
*
|
|
3918
|
+
* @example
|
|
3919
|
+
* program
|
|
3920
|
+
* .optionsGroup('Development Options:')
|
|
3921
|
+
* .option('-d, --debug', 'output extra debugging')
|
|
3922
|
+
* .option('-p, --profile', 'output profiling information')
|
|
3923
|
+
*
|
|
3924
|
+
* @param {string} [heading]
|
|
3925
|
+
* @returns {Command | string}
|
|
3926
|
+
*/
|
|
3927
|
+
optionsGroup(heading) {
|
|
3928
|
+
if (heading === undefined) return this._defaultOptionGroup ?? '';
|
|
3929
|
+
this._defaultOptionGroup = heading;
|
|
3930
|
+
return this;
|
|
3931
|
+
}
|
|
3932
|
+
|
|
3933
|
+
/**
|
|
3934
|
+
* @param {Option} option
|
|
3935
|
+
* @private
|
|
3936
|
+
*/
|
|
3937
|
+
_initOptionGroup(option) {
|
|
3938
|
+
if (this._defaultOptionGroup && !option.helpGroupHeading)
|
|
3939
|
+
option.helpGroup(this._defaultOptionGroup);
|
|
3940
|
+
}
|
|
3941
|
+
|
|
3942
|
+
/**
|
|
3943
|
+
* @param {Command} cmd
|
|
3944
|
+
* @private
|
|
3945
|
+
*/
|
|
3946
|
+
_initCommandGroup(cmd) {
|
|
3947
|
+
if (this._defaultCommandGroup && !cmd.helpGroup())
|
|
3948
|
+
cmd.helpGroup(this._defaultCommandGroup);
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3478
3951
|
/**
|
|
3479
3952
|
* Set the name of the command from script filename, such as process.argv[1],
|
|
3480
3953
|
* or require.main.filename, or __filename.
|
|
@@ -3521,31 +3994,49 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3521
3994
|
|
|
3522
3995
|
helpInformation(contextOptions) {
|
|
3523
3996
|
const helper = this.createHelp();
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
}
|
|
3530
|
-
|
|
3997
|
+
const context = this._getOutputContext(contextOptions);
|
|
3998
|
+
helper.prepareContext({
|
|
3999
|
+
error: context.error,
|
|
4000
|
+
helpWidth: context.helpWidth,
|
|
4001
|
+
outputHasColors: context.hasColors,
|
|
4002
|
+
});
|
|
4003
|
+
const text = helper.formatHelp(this, helper);
|
|
4004
|
+
if (context.hasColors) return text;
|
|
4005
|
+
return this._outputConfiguration.stripColor(text);
|
|
3531
4006
|
}
|
|
3532
4007
|
|
|
3533
4008
|
/**
|
|
4009
|
+
* @typedef HelpContext
|
|
4010
|
+
* @type {object}
|
|
4011
|
+
* @property {boolean} error
|
|
4012
|
+
* @property {number} helpWidth
|
|
4013
|
+
* @property {boolean} hasColors
|
|
4014
|
+
* @property {function} write - includes stripColor if needed
|
|
4015
|
+
*
|
|
4016
|
+
* @returns {HelpContext}
|
|
3534
4017
|
* @private
|
|
3535
4018
|
*/
|
|
3536
4019
|
|
|
3537
|
-
|
|
4020
|
+
_getOutputContext(contextOptions) {
|
|
3538
4021
|
contextOptions = contextOptions || {};
|
|
3539
|
-
const
|
|
3540
|
-
let
|
|
3541
|
-
|
|
3542
|
-
|
|
4022
|
+
const error = !!contextOptions.error;
|
|
4023
|
+
let baseWrite;
|
|
4024
|
+
let hasColors;
|
|
4025
|
+
let helpWidth;
|
|
4026
|
+
if (error) {
|
|
4027
|
+
baseWrite = (str) => this._outputConfiguration.writeErr(str);
|
|
4028
|
+
hasColors = this._outputConfiguration.getErrHasColors();
|
|
4029
|
+
helpWidth = this._outputConfiguration.getErrHelpWidth();
|
|
3543
4030
|
} else {
|
|
3544
|
-
|
|
4031
|
+
baseWrite = (str) => this._outputConfiguration.writeOut(str);
|
|
4032
|
+
hasColors = this._outputConfiguration.getOutHasColors();
|
|
4033
|
+
helpWidth = this._outputConfiguration.getOutHelpWidth();
|
|
3545
4034
|
}
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
4035
|
+
const write = (str) => {
|
|
4036
|
+
if (!hasColors) str = this._outputConfiguration.stripColor(str);
|
|
4037
|
+
return baseWrite(str);
|
|
4038
|
+
};
|
|
4039
|
+
return { error, write, hasColors, helpWidth };
|
|
3549
4040
|
}
|
|
3550
4041
|
|
|
3551
4042
|
/**
|
|
@@ -3562,14 +4053,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3562
4053
|
deprecatedCallback = contextOptions;
|
|
3563
4054
|
contextOptions = undefined;
|
|
3564
4055
|
}
|
|
3565
|
-
|
|
4056
|
+
|
|
4057
|
+
const outputContext = this._getOutputContext(contextOptions);
|
|
4058
|
+
/** @type {HelpTextEventContext} */
|
|
4059
|
+
const eventContext = {
|
|
4060
|
+
error: outputContext.error,
|
|
4061
|
+
write: outputContext.write,
|
|
4062
|
+
command: this,
|
|
4063
|
+
};
|
|
3566
4064
|
|
|
3567
4065
|
this._getCommandAndAncestors()
|
|
3568
4066
|
.reverse()
|
|
3569
|
-
.forEach((command) => command.emit('beforeAllHelp',
|
|
3570
|
-
this.emit('beforeHelp',
|
|
4067
|
+
.forEach((command) => command.emit('beforeAllHelp', eventContext));
|
|
4068
|
+
this.emit('beforeHelp', eventContext);
|
|
3571
4069
|
|
|
3572
|
-
let helpInformation = this.helpInformation(
|
|
4070
|
+
let helpInformation = this.helpInformation({ error: outputContext.error });
|
|
3573
4071
|
if (deprecatedCallback) {
|
|
3574
4072
|
helpInformation = deprecatedCallback(helpInformation);
|
|
3575
4073
|
if (
|
|
@@ -3579,14 +4077,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3579
4077
|
throw new Error('outputHelp callback must return a string or a Buffer');
|
|
3580
4078
|
}
|
|
3581
4079
|
}
|
|
3582
|
-
|
|
4080
|
+
outputContext.write(helpInformation);
|
|
3583
4081
|
|
|
3584
4082
|
if (this._getHelpOption()?.long) {
|
|
3585
4083
|
this.emit(this._getHelpOption().long); // deprecated
|
|
3586
4084
|
}
|
|
3587
|
-
this.emit('afterHelp',
|
|
4085
|
+
this.emit('afterHelp', eventContext);
|
|
3588
4086
|
this._getCommandAndAncestors().forEach((command) =>
|
|
3589
|
-
command.emit('afterAllHelp',
|
|
4087
|
+
command.emit('afterAllHelp', eventContext),
|
|
3590
4088
|
);
|
|
3591
4089
|
}
|
|
3592
4090
|
|
|
@@ -3604,10 +4102,14 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3604
4102
|
*/
|
|
3605
4103
|
|
|
3606
4104
|
helpOption(flags, description) {
|
|
3607
|
-
// Support disabling built-in help option.
|
|
4105
|
+
// Support enabling/disabling built-in help option.
|
|
3608
4106
|
if (typeof flags === 'boolean') {
|
|
3609
4107
|
if (flags) {
|
|
3610
|
-
this._helpOption
|
|
4108
|
+
if (this._helpOption === null) this._helpOption = undefined; // reenable
|
|
4109
|
+
if (this._defaultOptionGroup) {
|
|
4110
|
+
// make the option to store the group
|
|
4111
|
+
this._initOptionGroup(this._getHelpOption());
|
|
4112
|
+
}
|
|
3611
4113
|
} else {
|
|
3612
4114
|
this._helpOption = null; // disable
|
|
3613
4115
|
}
|
|
@@ -3615,9 +4117,12 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3615
4117
|
}
|
|
3616
4118
|
|
|
3617
4119
|
// Customise flags and description.
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
4120
|
+
this._helpOption = this.createOption(
|
|
4121
|
+
flags ?? '-h, --help',
|
|
4122
|
+
description ?? 'display help for command',
|
|
4123
|
+
);
|
|
4124
|
+
// init group unless lazy create
|
|
4125
|
+
if (flags || description) this._initOptionGroup(this._helpOption);
|
|
3621
4126
|
|
|
3622
4127
|
return this;
|
|
3623
4128
|
}
|
|
@@ -3646,6 +4151,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3646
4151
|
*/
|
|
3647
4152
|
addHelpOption(option) {
|
|
3648
4153
|
this._helpOption = option;
|
|
4154
|
+
this._initOptionGroup(option);
|
|
3649
4155
|
return this;
|
|
3650
4156
|
}
|
|
3651
4157
|
|
|
@@ -3659,7 +4165,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3659
4165
|
|
|
3660
4166
|
help(contextOptions) {
|
|
3661
4167
|
this.outputHelp(contextOptions);
|
|
3662
|
-
let exitCode = process.exitCode
|
|
4168
|
+
let exitCode = Number(process.exitCode ?? 0); // process.exitCode does allow a string or an integer, but we prefer just a number
|
|
3663
4169
|
if (
|
|
3664
4170
|
exitCode === 0 &&
|
|
3665
4171
|
contextOptions &&
|
|
@@ -3672,6 +4178,15 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3672
4178
|
this._exit(exitCode, 'commander.help', '(outputHelp)');
|
|
3673
4179
|
}
|
|
3674
4180
|
|
|
4181
|
+
/**
|
|
4182
|
+
* // Do a little typing to coordinate emit and listener for the help text events.
|
|
4183
|
+
* @typedef HelpTextEventContext
|
|
4184
|
+
* @type {object}
|
|
4185
|
+
* @property {boolean} error
|
|
4186
|
+
* @property {Command} command
|
|
4187
|
+
* @property {function} write
|
|
4188
|
+
*/
|
|
4189
|
+
|
|
3675
4190
|
/**
|
|
3676
4191
|
* Add additional text to be displayed with the built-in help.
|
|
3677
4192
|
*
|
|
@@ -3682,14 +4197,16 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3682
4197
|
* @param {(string | Function)} text - string to add, or a function returning a string
|
|
3683
4198
|
* @return {Command} `this` command for chaining
|
|
3684
4199
|
*/
|
|
4200
|
+
|
|
3685
4201
|
addHelpText(position, text) {
|
|
3686
4202
|
const allowedValues = ['beforeAll', 'before', 'after', 'afterAll'];
|
|
3687
4203
|
if (!allowedValues.includes(position)) {
|
|
3688
4204
|
throw new Error(`Unexpected value for position to addHelpText.
|
|
3689
4205
|
Expecting one of '${allowedValues.join("', '")}'`);
|
|
3690
4206
|
}
|
|
4207
|
+
|
|
3691
4208
|
const helpEvent = `${position}Help`;
|
|
3692
|
-
this.on(helpEvent, (context) => {
|
|
4209
|
+
this.on(helpEvent, (/** @type {HelpTextEventContext} */ context) => {
|
|
3693
4210
|
let helpStr;
|
|
3694
4211
|
if (typeof text === 'function') {
|
|
3695
4212
|
helpStr = text({ error: context.error, command: context.command });
|
|
@@ -3773,7 +4290,36 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
3773
4290
|
});
|
|
3774
4291
|
}
|
|
3775
4292
|
|
|
4293
|
+
/**
|
|
4294
|
+
* @returns {boolean | undefined}
|
|
4295
|
+
* @package
|
|
4296
|
+
*/
|
|
4297
|
+
function useColor() {
|
|
4298
|
+
// Test for common conventions.
|
|
4299
|
+
// NB: the observed behaviour is in combination with how author adds color! For example:
|
|
4300
|
+
// - we do not test NODE_DISABLE_COLORS, but util:styletext does
|
|
4301
|
+
// - we do test NO_COLOR, but Chalk does not
|
|
4302
|
+
//
|
|
4303
|
+
// References:
|
|
4304
|
+
// https://no-color.org
|
|
4305
|
+
// https://bixense.com/clicolors/
|
|
4306
|
+
// https://github.com/nodejs/node/blob/0a00217a5f67ef4a22384cfc80eb6dd9a917fdc1/lib/internal/tty.js#L109
|
|
4307
|
+
// https://github.com/chalk/supports-color/blob/c214314a14bcb174b12b3014b2b0a8de375029ae/index.js#L33
|
|
4308
|
+
// (https://force-color.org recent web page from 2023, does not match major javascript implementations)
|
|
4309
|
+
|
|
4310
|
+
if (
|
|
4311
|
+
process.env.NO_COLOR ||
|
|
4312
|
+
process.env.FORCE_COLOR === '0' ||
|
|
4313
|
+
process.env.FORCE_COLOR === 'false'
|
|
4314
|
+
)
|
|
4315
|
+
return false;
|
|
4316
|
+
if (process.env.FORCE_COLOR || process.env.CLICOLOR_FORCE !== undefined)
|
|
4317
|
+
return true;
|
|
4318
|
+
return undefined;
|
|
4319
|
+
}
|
|
4320
|
+
|
|
3776
4321
|
command.Command = Command;
|
|
4322
|
+
command.useColor = useColor; // exporting for tests
|
|
3777
4323
|
return command;
|
|
3778
4324
|
}
|
|
3779
4325
|
|
|
@@ -4591,12 +5137,13 @@ class SystemZone extends Zone {
|
|
|
4591
5137
|
}
|
|
4592
5138
|
}
|
|
4593
5139
|
|
|
4594
|
-
|
|
4595
|
-
function makeDTF(
|
|
4596
|
-
|
|
4597
|
-
|
|
5140
|
+
const dtfCache = new Map();
|
|
5141
|
+
function makeDTF(zoneName) {
|
|
5142
|
+
let dtf = dtfCache.get(zoneName);
|
|
5143
|
+
if (dtf === undefined) {
|
|
5144
|
+
dtf = new Intl.DateTimeFormat("en-US", {
|
|
4598
5145
|
hour12: false,
|
|
4599
|
-
timeZone:
|
|
5146
|
+
timeZone: zoneName,
|
|
4600
5147
|
year: "numeric",
|
|
4601
5148
|
month: "2-digit",
|
|
4602
5149
|
day: "2-digit",
|
|
@@ -4605,8 +5152,9 @@ function makeDTF(zone) {
|
|
|
4605
5152
|
second: "2-digit",
|
|
4606
5153
|
era: "short",
|
|
4607
5154
|
});
|
|
5155
|
+
dtfCache.set(zoneName, dtf);
|
|
4608
5156
|
}
|
|
4609
|
-
return
|
|
5157
|
+
return dtf;
|
|
4610
5158
|
}
|
|
4611
5159
|
|
|
4612
5160
|
const typeToPos = {
|
|
@@ -4642,7 +5190,7 @@ function partsOffset(dtf, date) {
|
|
|
4642
5190
|
return filled;
|
|
4643
5191
|
}
|
|
4644
5192
|
|
|
4645
|
-
|
|
5193
|
+
const ianaZoneCache = new Map();
|
|
4646
5194
|
/**
|
|
4647
5195
|
* A zone identified by an IANA identifier, like America/New_York
|
|
4648
5196
|
* @implements {Zone}
|
|
@@ -4653,10 +5201,11 @@ class IANAZone extends Zone {
|
|
|
4653
5201
|
* @return {IANAZone}
|
|
4654
5202
|
*/
|
|
4655
5203
|
static create(name) {
|
|
4656
|
-
|
|
4657
|
-
|
|
5204
|
+
let zone = ianaZoneCache.get(name);
|
|
5205
|
+
if (zone === undefined) {
|
|
5206
|
+
ianaZoneCache.set(name, (zone = new IANAZone(name)));
|
|
4658
5207
|
}
|
|
4659
|
-
return
|
|
5208
|
+
return zone;
|
|
4660
5209
|
}
|
|
4661
5210
|
|
|
4662
5211
|
/**
|
|
@@ -4664,8 +5213,8 @@ class IANAZone extends Zone {
|
|
|
4664
5213
|
* @return {void}
|
|
4665
5214
|
*/
|
|
4666
5215
|
static resetCache() {
|
|
4667
|
-
ianaZoneCache
|
|
4668
|
-
dtfCache
|
|
5216
|
+
ianaZoneCache.clear();
|
|
5217
|
+
dtfCache.clear();
|
|
4669
5218
|
}
|
|
4670
5219
|
|
|
4671
5220
|
/**
|
|
@@ -4768,6 +5317,7 @@ class IANAZone extends Zone {
|
|
|
4768
5317
|
* @return {number}
|
|
4769
5318
|
*/
|
|
4770
5319
|
offset(ts) {
|
|
5320
|
+
if (!this.valid) return NaN;
|
|
4771
5321
|
const date = new Date(ts);
|
|
4772
5322
|
|
|
4773
5323
|
if (isNaN(date)) return NaN;
|
|
@@ -4833,36 +5383,36 @@ function getCachedLF(locString, opts = {}) {
|
|
|
4833
5383
|
return dtf;
|
|
4834
5384
|
}
|
|
4835
5385
|
|
|
4836
|
-
|
|
5386
|
+
const intlDTCache = new Map();
|
|
4837
5387
|
function getCachedDTF(locString, opts = {}) {
|
|
4838
5388
|
const key = JSON.stringify([locString, opts]);
|
|
4839
|
-
let dtf = intlDTCache
|
|
4840
|
-
if (
|
|
5389
|
+
let dtf = intlDTCache.get(key);
|
|
5390
|
+
if (dtf === undefined) {
|
|
4841
5391
|
dtf = new Intl.DateTimeFormat(locString, opts);
|
|
4842
|
-
intlDTCache
|
|
5392
|
+
intlDTCache.set(key, dtf);
|
|
4843
5393
|
}
|
|
4844
5394
|
return dtf;
|
|
4845
5395
|
}
|
|
4846
5396
|
|
|
4847
|
-
|
|
5397
|
+
const intlNumCache = new Map();
|
|
4848
5398
|
function getCachedINF(locString, opts = {}) {
|
|
4849
5399
|
const key = JSON.stringify([locString, opts]);
|
|
4850
|
-
let inf = intlNumCache
|
|
4851
|
-
if (
|
|
5400
|
+
let inf = intlNumCache.get(key);
|
|
5401
|
+
if (inf === undefined) {
|
|
4852
5402
|
inf = new Intl.NumberFormat(locString, opts);
|
|
4853
|
-
intlNumCache
|
|
5403
|
+
intlNumCache.set(key, inf);
|
|
4854
5404
|
}
|
|
4855
5405
|
return inf;
|
|
4856
5406
|
}
|
|
4857
5407
|
|
|
4858
|
-
|
|
5408
|
+
const intlRelCache = new Map();
|
|
4859
5409
|
function getCachedRTF(locString, opts = {}) {
|
|
4860
5410
|
const { base, ...cacheKeyOpts } = opts; // exclude `base` from the options
|
|
4861
5411
|
const key = JSON.stringify([locString, cacheKeyOpts]);
|
|
4862
|
-
let inf = intlRelCache
|
|
4863
|
-
if (
|
|
5412
|
+
let inf = intlRelCache.get(key);
|
|
5413
|
+
if (inf === undefined) {
|
|
4864
5414
|
inf = new Intl.RelativeTimeFormat(locString, opts);
|
|
4865
|
-
intlRelCache
|
|
5415
|
+
intlRelCache.set(key, inf);
|
|
4866
5416
|
}
|
|
4867
5417
|
return inf;
|
|
4868
5418
|
}
|
|
@@ -4877,14 +5427,28 @@ function systemLocale() {
|
|
|
4877
5427
|
}
|
|
4878
5428
|
}
|
|
4879
5429
|
|
|
4880
|
-
|
|
5430
|
+
const intlResolvedOptionsCache = new Map();
|
|
5431
|
+
function getCachedIntResolvedOptions(locString) {
|
|
5432
|
+
let opts = intlResolvedOptionsCache.get(locString);
|
|
5433
|
+
if (opts === undefined) {
|
|
5434
|
+
opts = new Intl.DateTimeFormat(locString).resolvedOptions();
|
|
5435
|
+
intlResolvedOptionsCache.set(locString, opts);
|
|
5436
|
+
}
|
|
5437
|
+
return opts;
|
|
5438
|
+
}
|
|
5439
|
+
|
|
5440
|
+
const weekInfoCache = new Map();
|
|
4881
5441
|
function getCachedWeekInfo(locString) {
|
|
4882
|
-
let data = weekInfoCache
|
|
5442
|
+
let data = weekInfoCache.get(locString);
|
|
4883
5443
|
if (!data) {
|
|
4884
5444
|
const locale = new Intl.Locale(locString);
|
|
4885
5445
|
// browsers currently implement this as a property, but spec says it should be a getter function
|
|
4886
5446
|
data = "getWeekInfo" in locale ? locale.getWeekInfo() : locale.weekInfo;
|
|
4887
|
-
|
|
5447
|
+
// minimalDays was removed from WeekInfo: https://github.com/tc39/proposal-intl-locale-info/issues/86
|
|
5448
|
+
if (!("minimalDays" in data)) {
|
|
5449
|
+
data = { ...fallbackWeekSettings, ...data };
|
|
5450
|
+
}
|
|
5451
|
+
weekInfoCache.set(locString, data);
|
|
4888
5452
|
}
|
|
4889
5453
|
return data;
|
|
4890
5454
|
}
|
|
@@ -4983,7 +5547,7 @@ function supportsFastNumbers(loc) {
|
|
|
4983
5547
|
loc.numberingSystem === "latn" ||
|
|
4984
5548
|
!loc.locale ||
|
|
4985
5549
|
loc.locale.startsWith("en") ||
|
|
4986
|
-
|
|
5550
|
+
getCachedIntResolvedOptions(loc.locale).numberingSystem === "latn"
|
|
4987
5551
|
);
|
|
4988
5552
|
}
|
|
4989
5553
|
}
|
|
@@ -5142,7 +5706,6 @@ const fallbackWeekSettings = {
|
|
|
5142
5706
|
/**
|
|
5143
5707
|
* @private
|
|
5144
5708
|
*/
|
|
5145
|
-
|
|
5146
5709
|
class Locale {
|
|
5147
5710
|
static fromOpts(opts) {
|
|
5148
5711
|
return Locale.create(
|
|
@@ -5166,9 +5729,11 @@ class Locale {
|
|
|
5166
5729
|
|
|
5167
5730
|
static resetCache() {
|
|
5168
5731
|
sysLocaleCache = null;
|
|
5169
|
-
intlDTCache
|
|
5170
|
-
intlNumCache
|
|
5171
|
-
intlRelCache
|
|
5732
|
+
intlDTCache.clear();
|
|
5733
|
+
intlNumCache.clear();
|
|
5734
|
+
intlRelCache.clear();
|
|
5735
|
+
intlResolvedOptionsCache.clear();
|
|
5736
|
+
weekInfoCache.clear();
|
|
5172
5737
|
}
|
|
5173
5738
|
|
|
5174
5739
|
static fromObject({ locale, numberingSystem, outputCalendar, weekSettings } = {}) {
|
|
@@ -5233,10 +5798,18 @@ class Locale {
|
|
|
5233
5798
|
|
|
5234
5799
|
months(length, format = false) {
|
|
5235
5800
|
return listStuff(this, length, months, () => {
|
|
5801
|
+
// Workaround for "ja" locale: formatToParts does not label all parts of the month
|
|
5802
|
+
// as "month" and for this locale there is no difference between "format" and "non-format".
|
|
5803
|
+
// As such, just use format() instead of formatToParts() and take the whole string
|
|
5804
|
+
const monthSpecialCase = this.intl === "ja" || this.intl.startsWith("ja-");
|
|
5805
|
+
format &= !monthSpecialCase;
|
|
5236
5806
|
const intl = format ? { month: length, day: "numeric" } : { month: length },
|
|
5237
5807
|
formatStr = format ? "format" : "standalone";
|
|
5238
5808
|
if (!this.monthsCache[formatStr][length]) {
|
|
5239
|
-
|
|
5809
|
+
const mapper = !monthSpecialCase
|
|
5810
|
+
? (dt) => this.extract(dt, intl, "month")
|
|
5811
|
+
: (dt) => this.dtFormatter(dt, intl).format();
|
|
5812
|
+
this.monthsCache[formatStr][length] = mapMonths(mapper);
|
|
5240
5813
|
}
|
|
5241
5814
|
return this.monthsCache[formatStr][length];
|
|
5242
5815
|
});
|
|
@@ -5322,7 +5895,7 @@ class Locale {
|
|
|
5322
5895
|
return (
|
|
5323
5896
|
this.locale === "en" ||
|
|
5324
5897
|
this.locale.toLowerCase() === "en-us" ||
|
|
5325
|
-
|
|
5898
|
+
getCachedIntResolvedOptions(this.intl).locale.startsWith("en-us")
|
|
5326
5899
|
);
|
|
5327
5900
|
}
|
|
5328
5901
|
|
|
@@ -5661,22 +6234,26 @@ function parseDigits(str) {
|
|
|
5661
6234
|
}
|
|
5662
6235
|
|
|
5663
6236
|
// cache of {numberingSystem: {append: regex}}
|
|
5664
|
-
|
|
6237
|
+
const digitRegexCache = new Map();
|
|
5665
6238
|
function resetDigitRegexCache() {
|
|
5666
|
-
digitRegexCache
|
|
6239
|
+
digitRegexCache.clear();
|
|
5667
6240
|
}
|
|
5668
6241
|
|
|
5669
6242
|
function digitRegex({ numberingSystem }, append = "") {
|
|
5670
6243
|
const ns = numberingSystem || "latn";
|
|
5671
6244
|
|
|
5672
|
-
|
|
5673
|
-
|
|
6245
|
+
let appendCache = digitRegexCache.get(ns);
|
|
6246
|
+
if (appendCache === undefined) {
|
|
6247
|
+
appendCache = new Map();
|
|
6248
|
+
digitRegexCache.set(ns, appendCache);
|
|
5674
6249
|
}
|
|
5675
|
-
|
|
5676
|
-
|
|
6250
|
+
let regex = appendCache.get(append);
|
|
6251
|
+
if (regex === undefined) {
|
|
6252
|
+
regex = new RegExp(`${numberingSystems[ns]}${append}`);
|
|
6253
|
+
appendCache.set(append, regex);
|
|
5677
6254
|
}
|
|
5678
6255
|
|
|
5679
|
-
return
|
|
6256
|
+
return regex;
|
|
5680
6257
|
}
|
|
5681
6258
|
|
|
5682
6259
|
let now = () => Date.now(),
|
|
@@ -6218,10 +6795,24 @@ function parseMillis(fraction) {
|
|
|
6218
6795
|
}
|
|
6219
6796
|
}
|
|
6220
6797
|
|
|
6221
|
-
function roundTo(number, digits,
|
|
6222
|
-
const factor = 10 ** digits
|
|
6223
|
-
|
|
6224
|
-
|
|
6798
|
+
function roundTo(number, digits, rounding = "round") {
|
|
6799
|
+
const factor = 10 ** digits;
|
|
6800
|
+
switch (rounding) {
|
|
6801
|
+
case "expand":
|
|
6802
|
+
return number > 0
|
|
6803
|
+
? Math.ceil(number * factor) / factor
|
|
6804
|
+
: Math.floor(number * factor) / factor;
|
|
6805
|
+
case "trunc":
|
|
6806
|
+
return Math.trunc(number * factor) / factor;
|
|
6807
|
+
case "round":
|
|
6808
|
+
return Math.round(number * factor) / factor;
|
|
6809
|
+
case "floor":
|
|
6810
|
+
return Math.floor(number * factor) / factor;
|
|
6811
|
+
case "ceil":
|
|
6812
|
+
return Math.ceil(number * factor) / factor;
|
|
6813
|
+
default:
|
|
6814
|
+
throw new RangeError(`Value rounding ${rounding} is out of range`);
|
|
6815
|
+
}
|
|
6225
6816
|
}
|
|
6226
6817
|
|
|
6227
6818
|
// DATE BASICS
|
|
@@ -6329,7 +6920,7 @@ function signedOffset(offHourStr, offMinuteStr) {
|
|
|
6329
6920
|
|
|
6330
6921
|
function asNumber(value) {
|
|
6331
6922
|
const numericValue = Number(value);
|
|
6332
|
-
if (typeof value === "boolean" || value === "" || Number.
|
|
6923
|
+
if (typeof value === "boolean" || value === "" || !Number.isFinite(numericValue))
|
|
6333
6924
|
throw new InvalidArgumentError(`Invalid unit value ${value}`);
|
|
6334
6925
|
return numericValue;
|
|
6335
6926
|
}
|
|
@@ -6588,8 +7179,12 @@ class Formatter {
|
|
|
6588
7179
|
for (let i = 0; i < fmt.length; i++) {
|
|
6589
7180
|
const c = fmt.charAt(i);
|
|
6590
7181
|
if (c === "'") {
|
|
6591
|
-
|
|
6592
|
-
|
|
7182
|
+
// turn '' into a literal signal quote instead of just skipping the empty literal
|
|
7183
|
+
if (currentFull.length > 0 || bracketed) {
|
|
7184
|
+
splits.push({
|
|
7185
|
+
literal: bracketed || /^\s+$/.test(currentFull),
|
|
7186
|
+
val: currentFull === "" ? "'" : currentFull,
|
|
7187
|
+
});
|
|
6593
7188
|
}
|
|
6594
7189
|
current = null;
|
|
6595
7190
|
currentFull = "";
|
|
@@ -6653,7 +7248,7 @@ class Formatter {
|
|
|
6653
7248
|
return this.dtFormatter(dt, opts).resolvedOptions();
|
|
6654
7249
|
}
|
|
6655
7250
|
|
|
6656
|
-
num(n, p = 0) {
|
|
7251
|
+
num(n, p = 0, signDisplay = undefined) {
|
|
6657
7252
|
// we get some perf out of doing this here, annoyingly
|
|
6658
7253
|
if (this.opts.forceSimple) {
|
|
6659
7254
|
return padStart(n, p);
|
|
@@ -6664,6 +7259,9 @@ class Formatter {
|
|
|
6664
7259
|
if (p > 0) {
|
|
6665
7260
|
opts.padTo = p;
|
|
6666
7261
|
}
|
|
7262
|
+
if (signDisplay) {
|
|
7263
|
+
opts.signDisplay = signDisplay;
|
|
7264
|
+
}
|
|
6667
7265
|
|
|
6668
7266
|
return this.loc.numberFormatter(opts).format(n);
|
|
6669
7267
|
}
|
|
@@ -6899,32 +7497,44 @@ class Formatter {
|
|
|
6899
7497
|
}
|
|
6900
7498
|
|
|
6901
7499
|
formatDurationFromString(dur, fmt) {
|
|
7500
|
+
const invertLargest = this.opts.signMode === "negativeLargestOnly" ? -1 : 1;
|
|
6902
7501
|
const tokenToField = (token) => {
|
|
6903
7502
|
switch (token[0]) {
|
|
6904
7503
|
case "S":
|
|
6905
|
-
return "
|
|
7504
|
+
return "milliseconds";
|
|
6906
7505
|
case "s":
|
|
6907
|
-
return "
|
|
7506
|
+
return "seconds";
|
|
6908
7507
|
case "m":
|
|
6909
|
-
return "
|
|
7508
|
+
return "minutes";
|
|
6910
7509
|
case "h":
|
|
6911
|
-
return "
|
|
7510
|
+
return "hours";
|
|
6912
7511
|
case "d":
|
|
6913
|
-
return "
|
|
7512
|
+
return "days";
|
|
6914
7513
|
case "w":
|
|
6915
|
-
return "
|
|
7514
|
+
return "weeks";
|
|
6916
7515
|
case "M":
|
|
6917
|
-
return "
|
|
7516
|
+
return "months";
|
|
6918
7517
|
case "y":
|
|
6919
|
-
return "
|
|
7518
|
+
return "years";
|
|
6920
7519
|
default:
|
|
6921
7520
|
return null;
|
|
6922
7521
|
}
|
|
6923
7522
|
},
|
|
6924
|
-
tokenToString = (lildur) => (token) => {
|
|
7523
|
+
tokenToString = (lildur, info) => (token) => {
|
|
6925
7524
|
const mapped = tokenToField(token);
|
|
6926
7525
|
if (mapped) {
|
|
6927
|
-
|
|
7526
|
+
const inversionFactor =
|
|
7527
|
+
info.isNegativeDuration && mapped !== info.largestUnit ? invertLargest : 1;
|
|
7528
|
+
let signDisplay;
|
|
7529
|
+
if (this.opts.signMode === "negativeLargestOnly" && mapped !== info.largestUnit) {
|
|
7530
|
+
signDisplay = "never";
|
|
7531
|
+
} else if (this.opts.signMode === "all") {
|
|
7532
|
+
signDisplay = "always";
|
|
7533
|
+
} else {
|
|
7534
|
+
// "auto" and "negative" are the same, but "auto" has better support
|
|
7535
|
+
signDisplay = "auto";
|
|
7536
|
+
}
|
|
7537
|
+
return this.num(lildur.get(mapped) * inversionFactor, token.length, signDisplay);
|
|
6928
7538
|
} else {
|
|
6929
7539
|
return token;
|
|
6930
7540
|
}
|
|
@@ -6934,8 +7544,14 @@ class Formatter {
|
|
|
6934
7544
|
(found, { literal, val }) => (literal ? found : found.concat(val)),
|
|
6935
7545
|
[]
|
|
6936
7546
|
),
|
|
6937
|
-
collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t) => t))
|
|
6938
|
-
|
|
7547
|
+
collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t) => t)),
|
|
7548
|
+
durationInfo = {
|
|
7549
|
+
isNegativeDuration: collapsed < 0,
|
|
7550
|
+
// this relies on "collapsed" being based on "shiftTo", which builds up the object
|
|
7551
|
+
// in order
|
|
7552
|
+
largestUnit: Object.keys(collapsed.values)[0],
|
|
7553
|
+
};
|
|
7554
|
+
return stringifyTokens(tokens, tokenToString(collapsed, durationInfo));
|
|
6939
7555
|
}
|
|
6940
7556
|
}
|
|
6941
7557
|
|
|
@@ -6996,11 +7612,11 @@ function simpleParse(...keys) {
|
|
|
6996
7612
|
}
|
|
6997
7613
|
|
|
6998
7614
|
// ISO and SQL parsing
|
|
6999
|
-
const offsetRegex = /(?:(
|
|
7615
|
+
const offsetRegex = /(?:([Zz])|([+-]\d\d)(?::?(\d\d))?)/;
|
|
7000
7616
|
const isoExtendedZone = `(?:${offsetRegex.source}?(?:\\[(${ianaRegex.source})\\])?)?`;
|
|
7001
7617
|
const isoTimeBaseRegex = /(\d\d)(?::?(\d\d)(?::?(\d\d)(?:[.,](\d{1,30}))?)?)?/;
|
|
7002
7618
|
const isoTimeRegex = RegExp(`${isoTimeBaseRegex.source}${isoExtendedZone}`);
|
|
7003
|
-
const isoTimeExtensionRegex = RegExp(`(?:
|
|
7619
|
+
const isoTimeExtensionRegex = RegExp(`(?:[Tt]${isoTimeRegex.source})?`);
|
|
7004
7620
|
const isoYmdRegex = /([+-]\d{6}|\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?/;
|
|
7005
7621
|
const isoWeekRegex = /(\d{4})-?W(\d\d)(?:-?(\d))?/;
|
|
7006
7622
|
const isoOrdinalRegex = /(\d{4})-?(\d{3})/;
|
|
@@ -7715,9 +8331,13 @@ let Duration$1 = class Duration {
|
|
|
7715
8331
|
* @param {string} fmt - the format string
|
|
7716
8332
|
* @param {Object} opts - options
|
|
7717
8333
|
* @param {boolean} [opts.floor=true] - floor numerical values
|
|
8334
|
+
* @param {'negative'|'all'|'negativeLargestOnly'} [opts.signMode=negative] - How to handle signs
|
|
7718
8335
|
* @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("y d s") //=> "1 6 2"
|
|
7719
8336
|
* @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("yy dd sss") //=> "01 06 002"
|
|
7720
8337
|
* @example Duration.fromObject({ years: 1, days: 6, seconds: 2 }).toFormat("M S") //=> "12 518402000"
|
|
8338
|
+
* @example Duration.fromObject({ days: 6, seconds: 2 }).toFormat("d s", { signMode: "all" }) //=> "+6 +2"
|
|
8339
|
+
* @example Duration.fromObject({ days: -6, seconds: -2 }).toFormat("d s", { signMode: "all" }) //=> "-6 -2"
|
|
8340
|
+
* @example Duration.fromObject({ days: -6, seconds: -2 }).toFormat("d s", { signMode: "negativeLargestOnly" }) //=> "-6 2"
|
|
7721
8341
|
* @return {string}
|
|
7722
8342
|
*/
|
|
7723
8343
|
toFormat(fmt, opts = {}) {
|
|
@@ -7737,21 +8357,25 @@ let Duration$1 = class Duration {
|
|
|
7737
8357
|
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options
|
|
7738
8358
|
* @param {Object} opts - Formatting options. Accepts the same keys as the options parameter of the native `Intl.NumberFormat` constructor, as well as `listStyle`.
|
|
7739
8359
|
* @param {string} [opts.listStyle='narrow'] - How to format the merged list. Corresponds to the `style` property of the options parameter of the native `Intl.ListFormat` constructor.
|
|
8360
|
+
* @param {boolean} [opts.showZeros=true] - Show all units previously used by the duration even if they are zero
|
|
7740
8361
|
* @example
|
|
7741
8362
|
* ```js
|
|
7742
|
-
* var dur = Duration.fromObject({
|
|
7743
|
-
* dur.toHuman() //=> '1
|
|
7744
|
-
* dur.toHuman({ listStyle: "long" }) //=> '1
|
|
7745
|
-
* dur.toHuman({ unitDisplay: "short" }) //=> '1
|
|
8363
|
+
* var dur = Duration.fromObject({ months: 1, weeks: 0, hours: 5, minutes: 6 })
|
|
8364
|
+
* dur.toHuman() //=> '1 month, 0 weeks, 5 hours, 6 minutes'
|
|
8365
|
+
* dur.toHuman({ listStyle: "long" }) //=> '1 month, 0 weeks, 5 hours, and 6 minutes'
|
|
8366
|
+
* dur.toHuman({ unitDisplay: "short" }) //=> '1 mth, 0 wks, 5 hr, 6 min'
|
|
8367
|
+
* dur.toHuman({ showZeros: false }) //=> '1 month, 5 hours, 6 minutes'
|
|
7746
8368
|
* ```
|
|
7747
8369
|
*/
|
|
7748
8370
|
toHuman(opts = {}) {
|
|
7749
8371
|
if (!this.isValid) return INVALID$2;
|
|
7750
8372
|
|
|
8373
|
+
const showZeros = opts.showZeros !== false;
|
|
8374
|
+
|
|
7751
8375
|
const l = orderedUnits$1
|
|
7752
8376
|
.map((unit) => {
|
|
7753
8377
|
const val = this.values[unit];
|
|
7754
|
-
if (isUndefined(val)) {
|
|
8378
|
+
if (isUndefined(val) || (val === 0 && !showZeros)) {
|
|
7755
8379
|
return null;
|
|
7756
8380
|
}
|
|
7757
8381
|
return this.loc
|
|
@@ -8111,6 +8735,17 @@ let Duration$1 = class Duration {
|
|
|
8111
8735
|
return clone$1(this, { values: negated }, true);
|
|
8112
8736
|
}
|
|
8113
8737
|
|
|
8738
|
+
/**
|
|
8739
|
+
* Removes all units with values equal to 0 from this Duration.
|
|
8740
|
+
* @example Duration.fromObject({ years: 2, days: 0, hours: 0, minutes: 0 }).removeZeros().toObject() //=> { years: 2 }
|
|
8741
|
+
* @return {Duration}
|
|
8742
|
+
*/
|
|
8743
|
+
removeZeros() {
|
|
8744
|
+
if (!this.isValid) return this;
|
|
8745
|
+
const vals = removeZeroes(this.values);
|
|
8746
|
+
return clone$1(this, { values: vals }, true);
|
|
8747
|
+
}
|
|
8748
|
+
|
|
8114
8749
|
/**
|
|
8115
8750
|
* Get the years.
|
|
8116
8751
|
* @type {number}
|
|
@@ -8421,7 +9056,8 @@ class Interval {
|
|
|
8421
9056
|
}
|
|
8422
9057
|
|
|
8423
9058
|
/**
|
|
8424
|
-
* Returns the end of the Interval
|
|
9059
|
+
* Returns the end of the Interval. This is the first instant which is not part of the interval
|
|
9060
|
+
* (Interval is half-open).
|
|
8425
9061
|
* @type {DateTime}
|
|
8426
9062
|
*/
|
|
8427
9063
|
get end() {
|
|
@@ -8429,8 +9065,16 @@ class Interval {
|
|
|
8429
9065
|
}
|
|
8430
9066
|
|
|
8431
9067
|
/**
|
|
8432
|
-
* Returns
|
|
8433
|
-
* @type {
|
|
9068
|
+
* Returns the last DateTime included in the interval (since end is not part of the interval)
|
|
9069
|
+
* @type {DateTime}
|
|
9070
|
+
*/
|
|
9071
|
+
get lastDateTime() {
|
|
9072
|
+
return this.isValid ? (this.e ? this.e.minus(1) : null) : null;
|
|
9073
|
+
}
|
|
9074
|
+
|
|
9075
|
+
/**
|
|
9076
|
+
* Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'.
|
|
9077
|
+
* @type {boolean}
|
|
8434
9078
|
*/
|
|
8435
9079
|
get isValid() {
|
|
8436
9080
|
return this.invalidReason === null;
|
|
@@ -8692,8 +9336,11 @@ class Interval {
|
|
|
8692
9336
|
}
|
|
8693
9337
|
|
|
8694
9338
|
/**
|
|
8695
|
-
* Merge an array of Intervals into
|
|
9339
|
+
* Merge an array of Intervals into an equivalent minimal set of Intervals.
|
|
8696
9340
|
* Combines overlapping and adjacent Intervals.
|
|
9341
|
+
* The resulting array will contain the Intervals in ascending order, that is, starting with the earliest Interval
|
|
9342
|
+
* and ending with the latest.
|
|
9343
|
+
*
|
|
8697
9344
|
* @param {Array} intervals
|
|
8698
9345
|
* @return {Array}
|
|
8699
9346
|
*/
|
|
@@ -9841,21 +10488,22 @@ function toTechFormat(dt, format, allowZ = true) {
|
|
|
9841
10488
|
: null;
|
|
9842
10489
|
}
|
|
9843
10490
|
|
|
9844
|
-
function toISODate(o, extended) {
|
|
10491
|
+
function toISODate(o, extended, precision) {
|
|
9845
10492
|
const longFormat = o.c.year > 9999 || o.c.year < 0;
|
|
9846
10493
|
let c = "";
|
|
9847
10494
|
if (longFormat && o.c.year >= 0) c += "+";
|
|
9848
10495
|
c += padStart(o.c.year, longFormat ? 6 : 4);
|
|
9849
|
-
|
|
10496
|
+
if (precision === "year") return c;
|
|
9850
10497
|
if (extended) {
|
|
9851
10498
|
c += "-";
|
|
9852
10499
|
c += padStart(o.c.month);
|
|
10500
|
+
if (precision === "month") return c;
|
|
9853
10501
|
c += "-";
|
|
9854
|
-
c += padStart(o.c.day);
|
|
9855
10502
|
} else {
|
|
9856
10503
|
c += padStart(o.c.month);
|
|
9857
|
-
|
|
10504
|
+
if (precision === "month") return c;
|
|
9858
10505
|
}
|
|
10506
|
+
c += padStart(o.c.day);
|
|
9859
10507
|
return c;
|
|
9860
10508
|
}
|
|
9861
10509
|
|
|
@@ -9865,26 +10513,39 @@ function toISOTime(
|
|
|
9865
10513
|
suppressSeconds,
|
|
9866
10514
|
suppressMilliseconds,
|
|
9867
10515
|
includeOffset,
|
|
9868
|
-
extendedZone
|
|
10516
|
+
extendedZone,
|
|
10517
|
+
precision
|
|
9869
10518
|
) {
|
|
9870
|
-
let
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
|
|
9887
|
-
|
|
10519
|
+
let showSeconds = !suppressSeconds || o.c.millisecond !== 0 || o.c.second !== 0,
|
|
10520
|
+
c = "";
|
|
10521
|
+
switch (precision) {
|
|
10522
|
+
case "day":
|
|
10523
|
+
case "month":
|
|
10524
|
+
case "year":
|
|
10525
|
+
break;
|
|
10526
|
+
default:
|
|
10527
|
+
c += padStart(o.c.hour);
|
|
10528
|
+
if (precision === "hour") break;
|
|
10529
|
+
if (extended) {
|
|
10530
|
+
c += ":";
|
|
10531
|
+
c += padStart(o.c.minute);
|
|
10532
|
+
if (precision === "minute") break;
|
|
10533
|
+
if (showSeconds) {
|
|
10534
|
+
c += ":";
|
|
10535
|
+
c += padStart(o.c.second);
|
|
10536
|
+
}
|
|
10537
|
+
} else {
|
|
10538
|
+
c += padStart(o.c.minute);
|
|
10539
|
+
if (precision === "minute") break;
|
|
10540
|
+
if (showSeconds) {
|
|
10541
|
+
c += padStart(o.c.second);
|
|
10542
|
+
}
|
|
10543
|
+
}
|
|
10544
|
+
if (precision === "second") break;
|
|
10545
|
+
if (showSeconds && (!suppressMilliseconds || o.c.millisecond !== 0)) {
|
|
10546
|
+
c += ".";
|
|
10547
|
+
c += padStart(o.c.millisecond, 3);
|
|
10548
|
+
}
|
|
9888
10549
|
}
|
|
9889
10550
|
|
|
9890
10551
|
if (includeOffset) {
|
|
@@ -10016,15 +10677,27 @@ function normalizeUnitWithLocalWeeks(unit) {
|
|
|
10016
10677
|
// This is safe for quickDT (used by local() and utc()) because we don't fill in
|
|
10017
10678
|
// higher-order units from tsNow (as we do in fromObject, this requires that
|
|
10018
10679
|
// offset is calculated from tsNow).
|
|
10680
|
+
/**
|
|
10681
|
+
* @param {Zone} zone
|
|
10682
|
+
* @return {number}
|
|
10683
|
+
*/
|
|
10019
10684
|
function guessOffsetForZone(zone) {
|
|
10020
|
-
if (
|
|
10021
|
-
|
|
10022
|
-
|
|
10023
|
-
}
|
|
10685
|
+
if (zoneOffsetTs === undefined) {
|
|
10686
|
+
zoneOffsetTs = Settings.now();
|
|
10687
|
+
}
|
|
10024
10688
|
|
|
10025
|
-
|
|
10689
|
+
// Do not cache anything but IANA zones, because it is not safe to do so.
|
|
10690
|
+
// Guessing an offset which is not present in the zone can cause wrong results from fixOffset
|
|
10691
|
+
if (zone.type !== "iana") {
|
|
10692
|
+
return zone.offset(zoneOffsetTs);
|
|
10693
|
+
}
|
|
10694
|
+
const zoneName = zone.name;
|
|
10695
|
+
let offsetGuess = zoneOffsetGuessCache.get(zoneName);
|
|
10696
|
+
if (offsetGuess === undefined) {
|
|
10697
|
+
offsetGuess = zone.offset(zoneOffsetTs);
|
|
10698
|
+
zoneOffsetGuessCache.set(zoneName, offsetGuess);
|
|
10026
10699
|
}
|
|
10027
|
-
return
|
|
10700
|
+
return offsetGuess;
|
|
10028
10701
|
}
|
|
10029
10702
|
|
|
10030
10703
|
// this is a dumbed down version of fromObject() that runs about 60% faster
|
|
@@ -10064,8 +10737,9 @@ function quickDT(obj, opts) {
|
|
|
10064
10737
|
|
|
10065
10738
|
function diffRelative(start, end, opts) {
|
|
10066
10739
|
const round = isUndefined(opts.round) ? true : opts.round,
|
|
10740
|
+
rounding = isUndefined(opts.rounding) ? "trunc" : opts.rounding,
|
|
10067
10741
|
format = (c, unit) => {
|
|
10068
|
-
c = roundTo(c, round || opts.calendary ? 0 : 2,
|
|
10742
|
+
c = roundTo(c, round || opts.calendary ? 0 : 2, opts.calendary ? "round" : rounding);
|
|
10069
10743
|
const formatter = end.loc.clone(opts).relFormatter(opts);
|
|
10070
10744
|
return formatter.format(c, unit);
|
|
10071
10745
|
},
|
|
@@ -10114,7 +10788,7 @@ let zoneOffsetTs;
|
|
|
10114
10788
|
* This optimizes quickDT via guessOffsetForZone to avoid repeated calls of
|
|
10115
10789
|
* zone.offset().
|
|
10116
10790
|
*/
|
|
10117
|
-
|
|
10791
|
+
const zoneOffsetGuessCache = new Map();
|
|
10118
10792
|
|
|
10119
10793
|
/**
|
|
10120
10794
|
* A DateTime is an immutable data structure representing a specific date and time and accompanying methods. It contains class and instance methods for creating, parsing, interrogating, transforming, and formatting them.
|
|
@@ -10318,7 +10992,7 @@ class DateTime {
|
|
|
10318
10992
|
throw new InvalidArgumentError(
|
|
10319
10993
|
`fromMillis requires a numerical input, but received a ${typeof milliseconds} with value ${milliseconds}`
|
|
10320
10994
|
);
|
|
10321
|
-
} else if (milliseconds < -
|
|
10995
|
+
} else if (milliseconds < -MAX_DATE || milliseconds > MAX_DATE) {
|
|
10322
10996
|
// this isn't perfect because we can still end up out of range because of additional shifting, but it's a start
|
|
10323
10997
|
return DateTime.invalid("Timestamp out of range");
|
|
10324
10998
|
} else {
|
|
@@ -10679,7 +11353,7 @@ class DateTime {
|
|
|
10679
11353
|
|
|
10680
11354
|
static resetCache() {
|
|
10681
11355
|
zoneOffsetTs = undefined;
|
|
10682
|
-
zoneOffsetGuessCache
|
|
11356
|
+
zoneOffsetGuessCache.clear();
|
|
10683
11357
|
}
|
|
10684
11358
|
|
|
10685
11359
|
// INFO
|
|
@@ -11444,11 +12118,14 @@ class DateTime {
|
|
|
11444
12118
|
* @param {boolean} [opts.includeOffset=true] - include the offset, such as 'Z' or '-04:00'
|
|
11445
12119
|
* @param {boolean} [opts.extendedZone=false] - add the time zone format extension
|
|
11446
12120
|
* @param {string} [opts.format='extended'] - choose between the basic and extended format
|
|
12121
|
+
* @param {string} [opts.precision='milliseconds'] - truncate output to desired presicion: 'years', 'months', 'days', 'hours', 'minutes', 'seconds' or 'milliseconds'. When precision and suppressSeconds or suppressMilliseconds are used together, precision sets the maximum unit shown in the output, however seconds or milliseconds will still be suppressed if they are 0.
|
|
11447
12122
|
* @example DateTime.utc(1983, 5, 25).toISO() //=> '1982-05-25T00:00:00.000Z'
|
|
11448
12123
|
* @example DateTime.now().toISO() //=> '2017-04-22T20:47:05.335-04:00'
|
|
11449
12124
|
* @example DateTime.now().toISO({ includeOffset: false }) //=> '2017-04-22T20:47:05.335'
|
|
11450
12125
|
* @example DateTime.now().toISO({ format: 'basic' }) //=> '20170422T204705.335-0400'
|
|
11451
|
-
* @
|
|
12126
|
+
* @example DateTime.now().toISO({ precision: 'day' }) //=> '2017-04-22Z'
|
|
12127
|
+
* @example DateTime.now().toISO({ precision: 'minute' }) //=> '2017-04-22T20:47Z'
|
|
12128
|
+
* @return {string|null}
|
|
11452
12129
|
*/
|
|
11453
12130
|
toISO({
|
|
11454
12131
|
format = "extended",
|
|
@@ -11456,16 +12133,26 @@ class DateTime {
|
|
|
11456
12133
|
suppressMilliseconds = false,
|
|
11457
12134
|
includeOffset = true,
|
|
11458
12135
|
extendedZone = false,
|
|
12136
|
+
precision = "milliseconds",
|
|
11459
12137
|
} = {}) {
|
|
11460
12138
|
if (!this.isValid) {
|
|
11461
12139
|
return null;
|
|
11462
12140
|
}
|
|
11463
12141
|
|
|
12142
|
+
precision = normalizeUnit(precision);
|
|
11464
12143
|
const ext = format === "extended";
|
|
11465
12144
|
|
|
11466
|
-
let c = toISODate(this, ext);
|
|
11467
|
-
c += "T";
|
|
11468
|
-
c += toISOTime(
|
|
12145
|
+
let c = toISODate(this, ext, precision);
|
|
12146
|
+
if (orderedUnits.indexOf(precision) >= 3) c += "T";
|
|
12147
|
+
c += toISOTime(
|
|
12148
|
+
this,
|
|
12149
|
+
ext,
|
|
12150
|
+
suppressSeconds,
|
|
12151
|
+
suppressMilliseconds,
|
|
12152
|
+
includeOffset,
|
|
12153
|
+
extendedZone,
|
|
12154
|
+
precision
|
|
12155
|
+
);
|
|
11469
12156
|
return c;
|
|
11470
12157
|
}
|
|
11471
12158
|
|
|
@@ -11473,16 +12160,17 @@ class DateTime {
|
|
|
11473
12160
|
* Returns an ISO 8601-compliant string representation of this DateTime's date component
|
|
11474
12161
|
* @param {Object} opts - options
|
|
11475
12162
|
* @param {string} [opts.format='extended'] - choose between the basic and extended format
|
|
12163
|
+
* @param {string} [opts.precision='day'] - truncate output to desired precision: 'years', 'months', or 'days'.
|
|
11476
12164
|
* @example DateTime.utc(1982, 5, 25).toISODate() //=> '1982-05-25'
|
|
11477
12165
|
* @example DateTime.utc(1982, 5, 25).toISODate({ format: 'basic' }) //=> '19820525'
|
|
11478
|
-
* @
|
|
12166
|
+
* @example DateTime.utc(1982, 5, 25).toISODate({ precision: 'month' }) //=> '1982-05'
|
|
12167
|
+
* @return {string|null}
|
|
11479
12168
|
*/
|
|
11480
|
-
toISODate({ format = "extended" } = {}) {
|
|
12169
|
+
toISODate({ format = "extended", precision = "day" } = {}) {
|
|
11481
12170
|
if (!this.isValid) {
|
|
11482
12171
|
return null;
|
|
11483
12172
|
}
|
|
11484
|
-
|
|
11485
|
-
return toISODate(this, format === "extended");
|
|
12173
|
+
return toISODate(this, format === "extended", normalizeUnit(precision));
|
|
11486
12174
|
}
|
|
11487
12175
|
|
|
11488
12176
|
/**
|
|
@@ -11503,10 +12191,12 @@ class DateTime {
|
|
|
11503
12191
|
* @param {boolean} [opts.extendedZone=true] - add the time zone format extension
|
|
11504
12192
|
* @param {boolean} [opts.includePrefix=false] - include the `T` prefix
|
|
11505
12193
|
* @param {string} [opts.format='extended'] - choose between the basic and extended format
|
|
12194
|
+
* @param {string} [opts.precision='milliseconds'] - truncate output to desired presicion: 'hours', 'minutes', 'seconds' or 'milliseconds'. When precision and suppressSeconds or suppressMilliseconds are used together, precision sets the maximum unit shown in the output, however seconds or milliseconds will still be suppressed if they are 0.
|
|
11506
12195
|
* @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime() //=> '07:34:19.361Z'
|
|
11507
12196
|
* @example DateTime.utc().set({ hour: 7, minute: 34, seconds: 0, milliseconds: 0 }).toISOTime({ suppressSeconds: true }) //=> '07:34Z'
|
|
11508
12197
|
* @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ format: 'basic' }) //=> '073419.361Z'
|
|
11509
12198
|
* @example DateTime.utc().set({ hour: 7, minute: 34 }).toISOTime({ includePrefix: true }) //=> 'T07:34:19.361Z'
|
|
12199
|
+
* @example DateTime.utc().set({ hour: 7, minute: 34, second: 56 }).toISOTime({ precision: 'minute' }) //=> '07:34Z'
|
|
11510
12200
|
* @return {string}
|
|
11511
12201
|
*/
|
|
11512
12202
|
toISOTime({
|
|
@@ -11516,12 +12206,14 @@ class DateTime {
|
|
|
11516
12206
|
includePrefix = false,
|
|
11517
12207
|
extendedZone = false,
|
|
11518
12208
|
format = "extended",
|
|
12209
|
+
precision = "milliseconds",
|
|
11519
12210
|
} = {}) {
|
|
11520
12211
|
if (!this.isValid) {
|
|
11521
12212
|
return null;
|
|
11522
12213
|
}
|
|
11523
12214
|
|
|
11524
|
-
|
|
12215
|
+
precision = normalizeUnit(precision);
|
|
12216
|
+
let c = includePrefix && orderedUnits.indexOf(precision) >= 3 ? "T" : "";
|
|
11525
12217
|
return (
|
|
11526
12218
|
c +
|
|
11527
12219
|
toISOTime(
|
|
@@ -11530,7 +12222,8 @@ class DateTime {
|
|
|
11530
12222
|
suppressSeconds,
|
|
11531
12223
|
suppressMilliseconds,
|
|
11532
12224
|
includeOffset,
|
|
11533
|
-
extendedZone
|
|
12225
|
+
extendedZone,
|
|
12226
|
+
precision
|
|
11534
12227
|
)
|
|
11535
12228
|
);
|
|
11536
12229
|
}
|
|
@@ -11560,7 +12253,7 @@ class DateTime {
|
|
|
11560
12253
|
/**
|
|
11561
12254
|
* Returns a string representation of this DateTime appropriate for use in SQL Date
|
|
11562
12255
|
* @example DateTime.utc(2014, 7, 13).toSQLDate() //=> '2014-07-13'
|
|
11563
|
-
* @return {string}
|
|
12256
|
+
* @return {string|null}
|
|
11564
12257
|
*/
|
|
11565
12258
|
toSQLDate() {
|
|
11566
12259
|
if (!this.isValid) {
|
|
@@ -11655,7 +12348,7 @@ class DateTime {
|
|
|
11655
12348
|
}
|
|
11656
12349
|
|
|
11657
12350
|
/**
|
|
11658
|
-
* Returns the epoch seconds of this DateTime.
|
|
12351
|
+
* Returns the epoch seconds (including milliseconds in the fractional part) of this DateTime.
|
|
11659
12352
|
* @return {number}
|
|
11660
12353
|
*/
|
|
11661
12354
|
toSeconds() {
|
|
@@ -11762,7 +12455,7 @@ class DateTime {
|
|
|
11762
12455
|
/**
|
|
11763
12456
|
* Return an Interval spanning between this DateTime and another DateTime
|
|
11764
12457
|
* @param {DateTime} otherDateTime - the other end point of the Interval
|
|
11765
|
-
* @return {Interval}
|
|
12458
|
+
* @return {Interval|DateTime}
|
|
11766
12459
|
*/
|
|
11767
12460
|
until(otherDateTime) {
|
|
11768
12461
|
return this.isValid ? Interval.fromDateTimes(this, otherDateTime) : this;
|
|
@@ -11808,12 +12501,13 @@ class DateTime {
|
|
|
11808
12501
|
|
|
11809
12502
|
/**
|
|
11810
12503
|
* Returns a string representation of a this time relative to now, such as "in two days". Can only internationalize if your
|
|
11811
|
-
* platform supports Intl.RelativeTimeFormat. Rounds
|
|
12504
|
+
* platform supports Intl.RelativeTimeFormat. Rounds towards zero by default.
|
|
11812
12505
|
* @param {Object} options - options that affect the output
|
|
11813
12506
|
* @param {DateTime} [options.base=DateTime.now()] - the DateTime to use as the basis to which this time is compared. Defaults to now.
|
|
11814
12507
|
* @param {string} [options.style="long"] - the style of units, must be "long", "short", or "narrow"
|
|
11815
12508
|
* @param {string|string[]} options.unit - use a specific unit or array of units; if omitted, or an array, the method will pick the best unit. Use an array or one of "years", "quarters", "months", "weeks", "days", "hours", "minutes", or "seconds"
|
|
11816
12509
|
* @param {boolean} [options.round=true] - whether to round the numbers in the output.
|
|
12510
|
+
* @param {string} [options.rounding="trunc"] - rounding method to use when rounding the numbers in the output. Can be "trunc" (toward zero), "expand" (away from zero), "round", "floor", or "ceil".
|
|
11817
12511
|
* @param {number} [options.padding=0] - padding in milliseconds. This allows you to round up the result if it fits inside the threshold. Don't use in combination with {round: false} because the decimal output will include the padding.
|
|
11818
12512
|
* @param {string} options.locale - override the locale of this DateTime
|
|
11819
12513
|
* @param {string} options.numberingSystem - override the numberingSystem of this DateTime. The Intl system may choose not to honor this
|
|
@@ -14201,7 +14895,7 @@ class BinaryReader {
|
|
|
14201
14895
|
// ignore
|
|
14202
14896
|
}
|
|
14203
14897
|
break;
|
|
14204
|
-
// @ts-
|
|
14898
|
+
// @ts-ignore TS7029: Fallthrough case in switch -- ignore instead of expect-error for compiler settings without noFallthroughCasesInSwitch: true
|
|
14205
14899
|
case WireType.Bit64:
|
|
14206
14900
|
this.pos += 4;
|
|
14207
14901
|
case WireType.Bit32:
|
|
@@ -14482,7 +15176,6 @@ function around(index, lng, lat, maxResults = Infinity, maxDistance = Infinity,
|
|
|
14482
15176
|
left: 0, // left index in the kd-tree array
|
|
14483
15177
|
right: index.ids.length - 1, // right index
|
|
14484
15178
|
axis: 0, // 0 for longitude axis and 1 for latitude axis
|
|
14485
|
-
dist: 0, // will hold the lower bound of children's distances to the query point
|
|
14486
15179
|
minLng: -180, // bounding box of the node
|
|
14487
15180
|
minLat: -90,
|
|
14488
15181
|
maxLng: 180,
|
|
@@ -14950,7 +15643,7 @@ function sqDist(ax, ay, bx, by) {
|
|
|
14950
15643
|
return dx * dx + dy * dy;
|
|
14951
15644
|
}
|
|
14952
15645
|
|
|
14953
|
-
const xt="ENTRIES",B="KEYS",G="VALUES",g="";class V{set;_type;_path;constructor(e,n){const o=e._tree,s=Array.from(o.keys());this.set=e,this._type=n,this._path=s.length>0?[{node:o,keys:s}]:[];}next(){const e=this.dive();return this.backtrack(),e}dive(){if(this._path.length===0)return {done:true,value:undefined};const{node:e,keys:n}=z(this._path);if(z(n)===g)return {done:false,value:this.result()};const o=e.get(z(n));return this._path.push({node:o,keys:Array.from(o.keys())}),this.dive()}backtrack(){if(this._path.length===0)return;const e=z(this._path).keys;e.pop(),!(e.length>0)&&(this._path.pop(),this.backtrack());}key(){return this.set._prefix+this._path.map(({keys:e})=>z(e)).filter(e=>e!==g).join("")}value(){return z(this._path).node.get(g)}result(){switch(this._type){case G:return this.value();case B:return this.key();default:return [this.key(),this.value()]}}[Symbol.iterator](){return this}}const z=t=>t[t.length-1],zt=(t,e,n)=>{const o=new Map;if(typeof e!="string")return o;const s=e.length+1,r=s+n,i=new Uint8Array(r*s).fill(n+1);for(let c=0;c<s;++c)i[c]=c;for(let c=1;c<r;++c)i[c*s]=c;return K(t,e,n,o,i,1,s,""),o},K=(t,e,n,o,s,r,i,c)=>{const u=r*i;t:for(const d of t.keys())if(d===g){const a=s[u-1];a<=n&&o.set(c,[t.get(d),a]);}else {let a=r;for(let h=0;h<d.length;++h,++a){const f=d[h],_=i*a,p=_-i;let l=s[_];const m=Math.max(0,a-n-1),y=Math.min(i-1,a+n);for(let w=m;w<y;++w){const C=f!==e[w],O=s[p+w]+ +C,b=s[p+w+1]+1,x=s[_+w]+1,S=s[_+w+1]=Math.min(O,b,x);S<l&&(l=S);}if(l>n)continue t}K(t.get(d),e,n,o,s,a,i,c+d);}};class I{_tree;_prefix;_size=undefined;constructor(e=new Map,n=""){this._tree=e,this._prefix=n;}atPrefix(e){if(!e.startsWith(this._prefix))throw new Error("Mismatched prefix");const[n,o]=v(this._tree,e.slice(this._prefix.length));if(n===undefined){const[s,r]=L(o);for(const i of s.keys())if(i!==g&&i.startsWith(r)){const c=new Map;return c.set(i.slice(r.length),s.get(i)),new I(c,e)}}return new I(n,e)}clear(){this._size=undefined,this._tree.clear();}delete(e){return this._size=undefined,St(this._tree,e)}entries(){return new V(this,xt)}forEach(e){for(const[n,o]of this)e(n,o,this);}fuzzyGet(e,n){return zt(this._tree,e,n)}get(e){const n=T(this._tree,e);return n!==undefined?n.get(g):undefined}has(e){return T(this._tree,e)?.has(g)??false}keys(){return new V(this,B)}set(e,n){if(typeof e!="string")throw new Error("key must be a string");return this._size=undefined,M(this._tree,e).set(g,n),this}get size(){if(this._size)return this._size;this._size=0;const e=this.entries();for(;!e.next().done;)this._size+=1;return this._size}update(e,n){if(typeof e!="string")throw new Error("key must be a string");this._size=undefined;const o=M(this._tree,e);return o.set(g,n(o.get(g))),this}fetch(e,n){if(typeof e!="string")throw new Error("key must be a string");this._size=undefined;const o=M(this._tree,e);let s=o.get(g);return s===undefined&&o.set(g,s=n()),s}values(){return new V(this,G)}[Symbol.iterator](){return this.entries()}static from(e){const n=new I;for(const[o,s]of e)n.set(o,s);return n}static fromObject(e){return I.from(Object.entries(e))}}const v=(t,e,n=[])=>{if(e.length===0||t==null)return [t,n];for(const o of t.keys())if(o!==g&&e.startsWith(o))return n.push([t,o]),v(t.get(o),e.slice(o.length),n);return n.push([t,e]),v(undefined,"",n)},T=(t,e)=>{if(e.length===0||!t)return t;for(const n of t.keys())if(n!==g&&e.startsWith(n))return T(t.get(n),e.slice(n.length))},M=(t,e)=>{const n=e.length;t:for(let o=0;t&&o<n;){for(const r of t.keys())if(r!==g&&e[o]===r[0]){const i=Math.min(n-o,r.length);let c=1;for(;c<i&&e[o+c]===r[c];)++c;const u=t.get(r);if(c===r.length)t=u;else {const d=new Map;d.set(r.slice(c),u),t.set(e.slice(o,o+c),d),t.delete(r),t=d;}o+=c;continue t}const s=new Map;return t.set(e.slice(o),s),s}return t},St=(t,e)=>{const[n,o]=v(t,e);if(n!==undefined){if(n.delete(g),n.size===0)Q(o);else if(n.size===1){const[s,r]=n.entries().next().value;Y(o,s,r);}}},Q=t=>{if(t.length===0)return;const[e,n]=L(t);if(e.delete(n),e.size===0)Q(t.slice(0,-1));else if(e.size===1){const[o,s]=e.entries().next().value;o!==g&&Y(t.slice(0,-1),o,s);}},Y=(t,e,n)=>{if(t.length===0)return;const[o,s]=L(t);o.set(s+e,n),o.delete(s);},L=t=>t[t.length-1],Z=(t,e)=>t._idToShortId.has(e),vt=/[\n\r\p{Z}\p{P}]+/u,D="or",H="and",Ft="and_not",kt=(t,e)=>{t.includes(e)||t.push(e);},tt=(t,e)=>{for(const n of e)t.includes(n)||t.push(n);},et=({score:t},{score:e})=>e-t,nt=()=>new Map,E=(t,e)=>Object.prototype.hasOwnProperty.call(t,e)?t[e]:undefined,ot={[D]:(t,e)=>{for(const n of e.keys()){const o=t.get(n);if(o==null)t.set(n,e.get(n));else {const{score:s,terms:r,match:i}=e.get(n);o.score=o.score+s,o.match=Object.assign(o.match,i),tt(o.terms,r);}}return t},[H]:(t,e)=>{const n=new Map;for(const o of e.keys()){const s=t.get(o);if(s==null)continue;const{score:r,terms:i,match:c}=e.get(o);tt(s.terms,i),n.set(o,{score:s.score+r,terms:s.terms,match:Object.assign(s.match,c)});}return n},[Ft]:(t,e)=>{for(const n of e.keys())t.delete(n);return t}},Ct=(t,e,n,o,s,r)=>{const{k:i,b:c,d:u}=r;return Math.log(1+(n-e+.5)/(e+.5))*(u+t*(i+1)/(t+i*(1-c+c*o/s)))},Ot=t=>(e,n,o)=>({term:e,fuzzy:typeof t.fuzzy=="function"?t.fuzzy(e,n,o):t.fuzzy??false,prefix:typeof t.prefix=="function"?t.prefix(e,n,o):t.prefix===true,termBoost:typeof t.boostTerm=="function"?t.boostTerm(e,n,o):1}),st=(t,e,n,o)=>{for(const s of Object.keys(t._fieldIds))if(t._fieldIds[s]===n){t._options.logger("warn",`SlimSearch: document with ID ${t._documentIds.get(e)} has changed before removal: term "${o}" was not present in field "${s}". Removing a document after it has changed can corrupt the index!`,"version_conflict");return}},it=(t,e,n,o)=>{const s=t._index.fetch(o,nt);let r=s.get(e);if(r==null)r=new Map,r.set(n,1),s.set(e,r);else {const i=r.get(n);r.set(n,(i??0)+1);}},A=(t,e,n,o)=>{if(!t._index.has(o)){st(t,n,e,o);return}const s=t._index.fetch(o,nt),r=s.get(e),i=r?.get(n);!r||typeof i>"u"?st(t,n,e,o):i<=1?r.size<=1?s.delete(e):r.delete(n):r.set(n,i-1),t._index.get(o).size===0&&t._index.delete(o);},Vt=(t,e,n,o,s)=>{let r=t._fieldLength.get(e);r==null&&t._fieldLength.set(e,r=[]),r[n]=s;const i=(t._avgFieldLength[n]||0)*o+s;t._avgFieldLength[n]=i/(o+1);},Tt=(t,e)=>{const n=t._nextId;return t._idToShortId.set(e,n),t._documentIds.set(n,e),t._documentCount+=1,t._nextId+=1,n},Mt=(t,e,n)=>{const{storeFields:o,extractField:s}=t._options;if(o?.length===0)return;let r=t._storedFields.get(e);r===undefined&&t._storedFields.set(e,r={});for(const i of o){const c=s(n,i);c!=null&&(r[i]=c);}},j=(t,e)=>{const{extractField:n,tokenize:o,processTerm:s,fields:r,idField:i}=t._options,c=n(e,i);if(c==null)throw new Error(`SlimSearch: document does not have ID field "${i}"`);if(Z(t,c))throw new Error(`SlimSearch: duplicate ID ${c}`);const u=Tt(t,c);Mt(t,u,e);for(const d of r){const a=n(e,d);if(a==null)continue;const h=o(a.toString(),d),f=t._fieldIds[d],_=new Set(h).size;Vt(t,u,f,t._documentCount-1,_);for(const p of h){const l=s(p,d);if(Array.isArray(l))for(const m of l)it(t,f,u,m);else l&&it(t,f,u,l);}}},q=(t,e)=>{for(const n of e)j(t,n);},Dt={k:1.2,b:.7,d:.5},$={idField:"id",extractField:(t,e)=>t[e],tokenize:t=>t.split(vt),processTerm:t=>t.toLowerCase(),fields:undefined,searchOptions:undefined,storeFields:[],logger:(t,e)=>{console?.[t]?.(e);},autoVacuum:true},rt={combineWith:D,prefix:false,fuzzy:false,maxFuzzy:6,boost:{},weights:{fuzzy:.45,prefix:.375},bm25:Dt},Et={combineWith:H,prefix:(t,e,n)=>e===n.length-1},N={batchSize:1e3,batchWait:10},W={minDirtFactor:.1,minDirtCount:20},P={...N,...W},R=Symbol("*"),jt=(t,e)=>{const n=new Map,o={...t._options.searchOptions,...e};for(const[s,r]of t._documentIds){const i=o.boostDocument?o.boostDocument(r,"",t._storedFields.get(s)):1;n.set(s,{score:i,terms:[],match:{}});}return n},ct=(t,e=D)=>{if(t.length===0)return new Map;const n=e.toLowerCase();if(!(n in ot))throw new Error(`Invalid combination operator: ${e}`);return t.reduce(ot[n])},J=(t,e,n,o,s,r,i,c,u,d=new Map)=>{if(r==null)return d;for(const a of Object.keys(i)){const h=i[a],f=t._fieldIds[a],_=r.get(f);if(_==null)continue;let p=_.size;const l=t._avgFieldLength[f];for(const m of _.keys()){if(!t._documentIds.has(m)){A(t,f,m,n),p-=1;continue}const y=c?c(t._documentIds.get(m),n,t._storedFields.get(m)):1;if(!y)continue;const w=_.get(m),C=t._fieldLength.get(m)[f],O=Ct(w,p,t._documentCount,C,l,u),b=o*s*h*y*O,x=d.get(m);if(x){x.score+=b,kt(x.terms,e);const S=E(x.match,n);S?S.push(a):x.match[n]=[a];}else d.set(m,{score:b,terms:[e],match:{[n]:[a]}});}}return d},qt=(t,e,n)=>{const o={...t._options.searchOptions,...n},s=(o.fields??t._options.fields).reduce((l,m)=>({...l,[m]:E(o.boost,m)||1}),{}),{boostDocument:r,weights:i,maxFuzzy:c,bm25:u}=o,{fuzzy:d,prefix:a}={...rt.weights,...i},h=t._index.get(e.term),f=J(t,e.term,e.term,1,e.termBoost,h,s,r,u);let _,p;if(e.prefix&&(_=t._index.atPrefix(e.term)),e.fuzzy){const l=e.fuzzy===true?.2:e.fuzzy,m=l<1?Math.min(c,Math.round(e.term.length*l)):l;m&&(p=t._index.fuzzyGet(e.term,m));}if(_)for(const[l,m]of _){const y=l.length-e.term.length;if(!y)continue;p?.delete(l);const w=a*l.length/(l.length+.3*y);J(t,e.term,l,w,e.termBoost,m,s,r,u,f);}if(p)for(const l of p.keys()){const[m,y]=p.get(l);if(!y)continue;const w=d*l.length/(l.length+y);J(t,e.term,l,w,e.termBoost,m,s,r,u,f);}return f},ut=(t,e,n={})=>{if(e===R)return jt(t,n);if(typeof e!="string"){const a={...n,...e,queries:undefined},h=e.queries.map(f=>ut(t,f,a));return ct(h,a.combineWith)}const{tokenize:o,processTerm:s,searchOptions:r}=t._options,i={tokenize:o,processTerm:s,...r,...n},{tokenize:c,processTerm:u}=i,d=c(e).flatMap(a=>u(a)).filter(a=>!!a).map(Ot(i)).map(a=>qt(t,a,i));return ct(d,i.combineWith)},dt=(t,e,n={})=>{const{searchOptions:o}=t._options,s={...o,...n},r=ut(t,e,n),i=[];for(const[c,{score:u,terms:d,match:a}]of r){const h=d.length||1,f={id:t._documentIds.get(c),score:u*h,terms:Object.keys(a),queryTerms:d,match:a};Object.assign(f,t._storedFields.get(c)),(s.filter==null||s.filter(f))&&i.push(f);}return e===R&&s.boostDocument==null||i.sort(et),i};class Nt{_options;_index;_documentCount;_documentIds;_idToShortId;_fieldIds;_fieldLength;_avgFieldLength;_nextId;_storedFields;_dirtCount;_currentVacuum;_enqueuedVacuum;_enqueuedVacuumConditions;constructor(e){if(!e?.fields)throw new Error('SlimSearch: option "fields" must be provided');const n=e.autoVacuum==null||e.autoVacuum===true?P:e.autoVacuum;this._options={...$,...e,autoVacuum:n,searchOptions:{...rt,...e.searchOptions},autoSuggestOptions:{...Et,...e.autoSuggestOptions}},this._index=new I,this._documentCount=0,this._documentIds=new Map,this._idToShortId=new Map,this._fieldIds={},this._fieldLength=new Map,this._avgFieldLength=[],this._nextId=0,this._storedFields=new Map,this._dirtCount=0,this._currentVacuum=null,this._enqueuedVacuum=null,this._enqueuedVacuumConditions=W,this.addFields(this._options.fields);}get isVacuuming(){return this._currentVacuum!=null}get dirtCount(){return this._dirtCount}get dirtFactor(){return this._dirtCount/(1+this._documentCount+this._dirtCount)}get documentCount(){return this._documentCount}get termCount(){return this._index.size}toJSON(){const e=[];for(const[n,o]of this._index){const s={};for(const[r,i]of o)s[r]=Object.fromEntries(i);e.push([n,s]);}return {documentCount:this._documentCount,nextId:this._nextId,documentIds:Object.fromEntries(this._documentIds),fieldIds:this._fieldIds,fieldLength:Object.fromEntries(this._fieldLength),averageFieldLength:this._avgFieldLength,storedFields:Object.fromEntries(this._storedFields),dirtCount:this._dirtCount,index:e,version:2}}addFields(e){for(let n=0;n<e.length;n++)this._fieldIds[e[n]]=n;}}const lt=t=>new Nt(t);
|
|
15646
|
+
const xt="ENTRIES",B="KEYS",G="VALUES",g="";class V{set;_type;_path;constructor(e,n){const o=e._tree,s=Array.from(o.keys());this.set=e,this._type=n,this._path=s.length>0?[{node:o,keys:s}]:[];}next(){const e=this.dive();return this.backtrack(),e}dive(){if(this._path.length===0)return {done:true,value:void 0};const{node:e,keys:n}=z(this._path);if(z(n)===g)return {done:false,value:this.result()};const o=e.get(z(n));return this._path.push({node:o,keys:Array.from(o.keys())}),this.dive()}backtrack(){if(this._path.length===0)return;const e=z(this._path).keys;e.pop(),!(e.length>0)&&(this._path.pop(),this.backtrack());}key(){return this.set._prefix+this._path.map(({keys:e})=>z(e)).filter(e=>e!==g).join("")}value(){return z(this._path).node.get(g)}result(){switch(this._type){case G:return this.value();case B:return this.key();default:return [this.key(),this.value()]}}[Symbol.iterator](){return this}}const z=t=>t[t.length-1],zt=(t,e,n)=>{const o=new Map;if(typeof e!="string")return o;const s=e.length+1,r=s+n,i=new Uint8Array(r*s).fill(n+1);for(let c=0;c<s;++c)i[c]=c;for(let c=1;c<r;++c)i[c*s]=c;return K(t,e,n,o,i,1,s,""),o},K=(t,e,n,o,s,r,i,c)=>{const u=r*i;t:for(const d of t.keys())if(d===g){const a=s[u-1];a<=n&&o.set(c,[t.get(d),a]);}else {let a=r;for(let h=0;h<d.length;++h,++a){const f=d[h],_=i*a,p=_-i;let l=s[_];const m=Math.max(0,a-n-1),y=Math.min(i-1,a+n);for(let w=m;w<y;++w){const C=f!==e[w],O=s[p+w]+ +C,b=s[p+w+1]+1,x=s[_+w]+1,S=s[_+w+1]=Math.min(O,b,x);S<l&&(l=S);}if(l>n)continue t}K(t.get(d),e,n,o,s,a,i,c+d);}};class I{_tree;_prefix;_size=void 0;constructor(e=new Map,n=""){this._tree=e,this._prefix=n;}atPrefix(e){if(!e.startsWith(this._prefix))throw new Error("Mismatched prefix");const[n,o]=v(this._tree,e.slice(this._prefix.length));if(n===void 0){const[s,r]=L(o);for(const i of s.keys())if(i!==g&&i.startsWith(r)){const c=new Map;return c.set(i.slice(r.length),s.get(i)),new I(c,e)}}return new I(n,e)}clear(){this._size=void 0,this._tree.clear();}delete(e){return this._size=void 0,St(this._tree,e)}entries(){return new V(this,xt)}forEach(e){for(const[n,o]of this)e(n,o,this);}fuzzyGet(e,n){return zt(this._tree,e,n)}get(e){const n=T(this._tree,e);return n!==void 0?n.get(g):void 0}has(e){return T(this._tree,e)?.has(g)??false}keys(){return new V(this,B)}set(e,n){if(typeof e!="string")throw new Error("key must be a string");return this._size=void 0,M(this._tree,e).set(g,n),this}get size(){if(this._size)return this._size;this._size=0;const e=this.entries();for(;!e.next().done;)this._size+=1;return this._size}update(e,n){if(typeof e!="string")throw new Error("key must be a string");this._size=void 0;const o=M(this._tree,e);return o.set(g,n(o.get(g))),this}fetch(e,n){if(typeof e!="string")throw new Error("key must be a string");this._size=void 0;const o=M(this._tree,e);let s=o.get(g);return s===void 0&&o.set(g,s=n()),s}values(){return new V(this,G)}[Symbol.iterator](){return this.entries()}static from(e){const n=new I;for(const[o,s]of e)n.set(o,s);return n}static fromObject(e){return I.from(Object.entries(e))}}const v=(t,e,n=[])=>{if(e.length===0||t==null)return [t,n];for(const o of t.keys())if(o!==g&&e.startsWith(o))return n.push([t,o]),v(t.get(o),e.slice(o.length),n);return n.push([t,e]),v(void 0,"",n)},T=(t,e)=>{if(e.length===0||!t)return t;for(const n of t.keys())if(n!==g&&e.startsWith(n))return T(t.get(n),e.slice(n.length))},M=(t,e)=>{const n=e.length;t:for(let o=0;t&&o<n;){for(const r of t.keys())if(r!==g&&e[o]===r[0]){const i=Math.min(n-o,r.length);let c=1;for(;c<i&&e[o+c]===r[c];)++c;const u=t.get(r);if(c===r.length)t=u;else {const d=new Map;d.set(r.slice(c),u),t.set(e.slice(o,o+c),d),t.delete(r),t=d;}o+=c;continue t}const s=new Map;return t.set(e.slice(o),s),s}return t},St=(t,e)=>{const[n,o]=v(t,e);if(n!==void 0){if(n.delete(g),n.size===0)Q(o);else if(n.size===1){const[s,r]=n.entries().next().value;Y(o,s,r);}}},Q=t=>{if(t.length===0)return;const[e,n]=L(t);if(e.delete(n),e.size===0)Q(t.slice(0,-1));else if(e.size===1){const[o,s]=e.entries().next().value;o!==g&&Y(t.slice(0,-1),o,s);}},Y=(t,e,n)=>{if(t.length===0)return;const[o,s]=L(t);o.set(s+e,n),o.delete(s);},L=t=>t[t.length-1],Z=(t,e)=>t._idToShortId.has(e),vt=/[\n\r\p{Z}\p{P}]+/u,D="or",H="and",Ft="and_not",kt=(t,e)=>{t.includes(e)||t.push(e);},tt=(t,e)=>{for(const n of e)t.includes(n)||t.push(n);},et=({score:t},{score:e})=>e-t,nt=()=>new Map,E=(t,e)=>Object.prototype.hasOwnProperty.call(t,e)?t[e]:void 0,ot={[D]:(t,e)=>{for(const n of e.keys()){const o=t.get(n);if(o==null)t.set(n,e.get(n));else {const{score:s,terms:r,match:i}=e.get(n);o.score=o.score+s,o.match=Object.assign(o.match,i),tt(o.terms,r);}}return t},[H]:(t,e)=>{const n=new Map;for(const o of e.keys()){const s=t.get(o);if(s==null)continue;const{score:r,terms:i,match:c}=e.get(o);tt(s.terms,i),n.set(o,{score:s.score+r,terms:s.terms,match:Object.assign(s.match,c)});}return n},[Ft]:(t,e)=>{for(const n of e.keys())t.delete(n);return t}},Ct=(t,e,n,o,s,r)=>{const{k:i,b:c,d:u}=r;return Math.log(1+(n-e+.5)/(e+.5))*(u+t*(i+1)/(t+i*(1-c+c*o/s)))},Ot=t=>(e,n,o)=>({term:e,fuzzy:typeof t.fuzzy=="function"?t.fuzzy(e,n,o):t.fuzzy??false,prefix:typeof t.prefix=="function"?t.prefix(e,n,o):t.prefix===true,termBoost:typeof t.boostTerm=="function"?t.boostTerm(e,n,o):1}),st=(t,e,n,o)=>{for(const s of Object.keys(t._fieldIds))if(t._fieldIds[s]===n){t._options.logger("warn",`SlimSearch: document with ID ${t._documentIds.get(e)} has changed before removal: term "${o}" was not present in field "${s}". Removing a document after it has changed can corrupt the index!`,"version_conflict");return}},it=(t,e,n,o)=>{const s=t._index.fetch(o,nt);let r=s.get(e);if(r==null)r=new Map,r.set(n,1),s.set(e,r);else {const i=r.get(n);r.set(n,(i??0)+1);}},A=(t,e,n,o)=>{if(!t._index.has(o)){st(t,n,e,o);return}const s=t._index.fetch(o,nt),r=s.get(e),i=r?.get(n);!r||typeof i>"u"?st(t,n,e,o):i<=1?r.size<=1?s.delete(e):r.delete(n):r.set(n,i-1),t._index.get(o).size===0&&t._index.delete(o);},Vt=(t,e,n,o,s)=>{let r=t._fieldLength.get(e);r==null&&t._fieldLength.set(e,r=[]),r[n]=s;const i=(t._avgFieldLength[n]||0)*o+s;t._avgFieldLength[n]=i/(o+1);},Tt=(t,e)=>{const n=t._nextId;return t._idToShortId.set(e,n),t._documentIds.set(n,e),t._documentCount+=1,t._nextId+=1,n},Mt=(t,e,n)=>{const{storeFields:o,extractField:s}=t._options;if(o?.length===0)return;let r=t._storedFields.get(e);r===void 0&&t._storedFields.set(e,r={});for(const i of o){const c=s(n,i);c!=null&&(r[i]=c);}},j=(t,e)=>{const{extractField:n,tokenize:o,processTerm:s,fields:r,idField:i}=t._options,c=n(e,i);if(c==null)throw new Error(`SlimSearch: document does not have ID field "${i}"`);if(Z(t,c))throw new Error(`SlimSearch: duplicate ID ${c}`);const u=Tt(t,c);Mt(t,u,e);for(const d of r){const a=n(e,d);if(a==null)continue;const h=o(a.toString(),d),f=t._fieldIds[d],_=new Set(h).size;Vt(t,u,f,t._documentCount-1,_);for(const p of h){const l=s(p,d);if(Array.isArray(l))for(const m of l)it(t,f,u,m);else l&&it(t,f,u,l);}}},q=(t,e)=>{for(const n of e)j(t,n);},Dt={k:1.2,b:.7,d:.5},$={idField:"id",extractField:(t,e)=>t[e],tokenize:t=>t.split(vt),processTerm:t=>t.toLowerCase(),fields:void 0,searchOptions:void 0,storeFields:[],logger:(t,e)=>{console?.[t]?.(e);},autoVacuum:true},rt={combineWith:D,prefix:false,fuzzy:false,maxFuzzy:6,boost:{},weights:{fuzzy:.45,prefix:.375},bm25:Dt},Et={combineWith:H,prefix:(t,e,n)=>e===n.length-1},N={batchSize:1e3,batchWait:10},W={minDirtFactor:.1,minDirtCount:20},P={...N,...W},R=Symbol("*"),jt=(t,e)=>{const n=new Map,o={...t._options.searchOptions,...e};for(const[s,r]of t._documentIds){const i=o.boostDocument?o.boostDocument(r,"",t._storedFields.get(s)):1;n.set(s,{score:i,terms:[],match:{}});}return n},ct=(t,e=D)=>{if(t.length===0)return new Map;const n=e.toLowerCase();if(!(n in ot))throw new Error(`Invalid combination operator: ${e}`);return t.reduce(ot[n])},J=(t,e,n,o,s,r,i,c,u,d=new Map)=>{if(r==null)return d;for(const a of Object.keys(i)){const h=i[a],f=t._fieldIds[a],_=r.get(f);if(_==null)continue;let p=_.size;const l=t._avgFieldLength[f];for(const m of _.keys()){if(!t._documentIds.has(m)){A(t,f,m,n),p-=1;continue}const y=c?c(t._documentIds.get(m),n,t._storedFields.get(m)):1;if(!y)continue;const w=_.get(m),C=t._fieldLength.get(m)[f],O=Ct(w,p,t._documentCount,C,l,u),b=o*s*h*y*O,x=d.get(m);if(x){x.score+=b,kt(x.terms,e);const S=E(x.match,n);S?S.push(a):x.match[n]=[a];}else d.set(m,{score:b,terms:[e],match:{[n]:[a]}});}}return d},qt=(t,e,n)=>{const o={...t._options.searchOptions,...n},s=(o.fields??t._options.fields).reduce((l,m)=>({...l,[m]:E(o.boost,m)||1}),{}),{boostDocument:r,weights:i,maxFuzzy:c,bm25:u}=o,{fuzzy:d,prefix:a}={...rt.weights,...i},h=t._index.get(e.term),f=J(t,e.term,e.term,1,e.termBoost,h,s,r,u);let _,p;if(e.prefix&&(_=t._index.atPrefix(e.term)),e.fuzzy){const l=e.fuzzy===true?.2:e.fuzzy,m=l<1?Math.min(c,Math.round(e.term.length*l)):l;m&&(p=t._index.fuzzyGet(e.term,m));}if(_)for(const[l,m]of _){const y=l.length-e.term.length;if(!y)continue;p?.delete(l);const w=a*l.length/(l.length+.3*y);J(t,e.term,l,w,e.termBoost,m,s,r,u,f);}if(p)for(const l of p.keys()){const[m,y]=p.get(l);if(!y)continue;const w=d*l.length/(l.length+y);J(t,e.term,l,w,e.termBoost,m,s,r,u,f);}return f},ut=(t,e,n={})=>{if(e===R)return jt(t,n);if(typeof e!="string"){const a={...n,...e,queries:void 0},h=e.queries.map(f=>ut(t,f,a));return ct(h,a.combineWith)}const{tokenize:o,processTerm:s,searchOptions:r}=t._options,i={tokenize:o,processTerm:s,...r,...n},{tokenize:c,processTerm:u}=i,d=c(e).flatMap(a=>u(a)).filter(a=>!!a).map(Ot(i)).map(a=>qt(t,a,i));return ct(d,i.combineWith)},dt=(t,e,n={})=>{const{searchOptions:o}=t._options,s={...o,...n},r=ut(t,e,n),i=[];for(const[c,{score:u,terms:d,match:a}]of r){const h=d.length||1,f={id:t._documentIds.get(c),score:u*h,terms:Object.keys(a),queryTerms:d,match:a};Object.assign(f,t._storedFields.get(c)),(s.filter==null||s.filter(f))&&i.push(f);}return e===R&&s.boostDocument==null||i.sort(et),i};class Nt{_options;_index;_documentCount;_documentIds;_idToShortId;_fieldIds;_fieldLength;_avgFieldLength;_nextId;_storedFields;_dirtCount;_currentVacuum;_enqueuedVacuum;_enqueuedVacuumConditions;constructor(e){if(!e?.fields)throw new Error('SlimSearch: option "fields" must be provided');const n=e.autoVacuum==null||e.autoVacuum===true?P:e.autoVacuum;this._options={...$,...e,autoVacuum:n,searchOptions:{...rt,...e.searchOptions},autoSuggestOptions:{...Et,...e.autoSuggestOptions}},this._index=new I,this._documentCount=0,this._documentIds=new Map,this._idToShortId=new Map,this._fieldIds={},this._fieldLength=new Map,this._avgFieldLength=[],this._nextId=0,this._storedFields=new Map,this._dirtCount=0,this._currentVacuum=null,this._enqueuedVacuum=null,this._enqueuedVacuumConditions=W,this.addFields(this._options.fields);}get isVacuuming(){return this._currentVacuum!=null}get dirtCount(){return this._dirtCount}get dirtFactor(){return this._dirtCount/(1+this._documentCount+this._dirtCount)}get documentCount(){return this._documentCount}get termCount(){return this._index.size}toJSON(){const e=[];for(const[n,o]of this._index){const s={};for(const[r,i]of o)s[r]=Object.fromEntries(i);e.push([n,s]);}return {documentCount:this._documentCount,nextId:this._nextId,documentIds:Object.fromEntries(this._documentIds),fieldIds:this._fieldIds,fieldLength:Object.fromEntries(this._fieldLength),averageFieldLength:this._avgFieldLength,storedFields:Object.fromEntries(this._storedFields),dirtCount:this._dirtCount,index:e,version:2}}addFields(e){for(let n=0;n<e.length;n++)this._fieldIds[e[n]]=n;}}const lt=t=>new Nt(t);
|
|
14954
15647
|
|
|
14955
15648
|
/**
|
|
14956
15649
|
* Generates a list of accent variants for a given term.
|
|
@@ -14992,7 +15685,7 @@ const generateAccentVariants = (term) => {
|
|
|
14992
15685
|
|
|
14993
15686
|
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
|
14994
15687
|
// versions:
|
|
14995
|
-
// protoc-gen-ts_proto v2.
|
|
15688
|
+
// protoc-gen-ts_proto v2.7.7
|
|
14996
15689
|
// protoc v4.23.4
|
|
14997
15690
|
// source: src/stops/proto/stops.proto
|
|
14998
15691
|
/* eslint-disable */
|
|
@@ -15089,7 +15782,7 @@ const Stop = {
|
|
|
15089
15782
|
},
|
|
15090
15783
|
decode(input, length) {
|
|
15091
15784
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
15092
|
-
|
|
15785
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
15093
15786
|
const message = createBaseStop();
|
|
15094
15787
|
while (reader.pos < end) {
|
|
15095
15788
|
const tag = reader.uint32();
|
|
@@ -15171,7 +15864,7 @@ const Stop = {
|
|
|
15171
15864
|
sourceStopId: isSet$1(object.sourceStopId) ? globalThis.String(object.sourceStopId) : "",
|
|
15172
15865
|
lat: isSet$1(object.lat) ? globalThis.Number(object.lat) : undefined,
|
|
15173
15866
|
lon: isSet$1(object.lon) ? globalThis.Number(object.lon) : undefined,
|
|
15174
|
-
children: globalThis.Array.isArray(object === null || object ===
|
|
15867
|
+
children: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.children) ? object.children.map((e) => globalThis.Number(e)) : [],
|
|
15175
15868
|
parent: isSet$1(object.parent) ? globalThis.Number(object.parent) : undefined,
|
|
15176
15869
|
locationType: isSet$1(object.locationType) ? locationTypeFromJSON(object.locationType) : 0,
|
|
15177
15870
|
platform: isSet$1(object.platform) ? globalThis.String(object.platform) : undefined,
|
|
@@ -15192,7 +15885,7 @@ const Stop = {
|
|
|
15192
15885
|
if (message.lon !== undefined) {
|
|
15193
15886
|
obj.lon = message.lon;
|
|
15194
15887
|
}
|
|
15195
|
-
if ((_a = message.children) === null || _a ===
|
|
15888
|
+
if ((_a = message.children) === null || _a === void 0 ? void 0 : _a.length) {
|
|
15196
15889
|
obj.children = message.children.map((e) => Math.round(e));
|
|
15197
15890
|
}
|
|
15198
15891
|
if (message.parent !== undefined) {
|
|
@@ -15207,19 +15900,19 @@ const Stop = {
|
|
|
15207
15900
|
return obj;
|
|
15208
15901
|
},
|
|
15209
15902
|
create(base) {
|
|
15210
|
-
return Stop.fromPartial(base !== null && base !==
|
|
15903
|
+
return Stop.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
15211
15904
|
},
|
|
15212
15905
|
fromPartial(object) {
|
|
15213
15906
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
15214
15907
|
const message = createBaseStop();
|
|
15215
|
-
message.name = (_a = object.name) !== null && _a !==
|
|
15216
|
-
message.sourceStopId = (_b = object.sourceStopId) !== null && _b !==
|
|
15217
|
-
message.lat = (_c = object.lat) !== null && _c !==
|
|
15218
|
-
message.lon = (_d = object.lon) !== null && _d !==
|
|
15219
|
-
message.children = ((_e = object.children) === null || _e ===
|
|
15220
|
-
message.parent = (_f = object.parent) !== null && _f !==
|
|
15221
|
-
message.locationType = (_g = object.locationType) !== null && _g !==
|
|
15222
|
-
message.platform = (_h = object.platform) !== null && _h !==
|
|
15908
|
+
message.name = (_a = object.name) !== null && _a !== void 0 ? _a : "";
|
|
15909
|
+
message.sourceStopId = (_b = object.sourceStopId) !== null && _b !== void 0 ? _b : "";
|
|
15910
|
+
message.lat = (_c = object.lat) !== null && _c !== void 0 ? _c : undefined;
|
|
15911
|
+
message.lon = (_d = object.lon) !== null && _d !== void 0 ? _d : undefined;
|
|
15912
|
+
message.children = ((_e = object.children) === null || _e === void 0 ? void 0 : _e.map((e) => e)) || [];
|
|
15913
|
+
message.parent = (_f = object.parent) !== null && _f !== void 0 ? _f : undefined;
|
|
15914
|
+
message.locationType = (_g = object.locationType) !== null && _g !== void 0 ? _g : 0;
|
|
15915
|
+
message.platform = (_h = object.platform) !== null && _h !== void 0 ? _h : undefined;
|
|
15223
15916
|
return message;
|
|
15224
15917
|
},
|
|
15225
15918
|
};
|
|
@@ -15238,7 +15931,7 @@ const StopsMap = {
|
|
|
15238
15931
|
},
|
|
15239
15932
|
decode(input, length) {
|
|
15240
15933
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
15241
|
-
|
|
15934
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
15242
15935
|
const message = createBaseStopsMap();
|
|
15243
15936
|
while (reader.pos < end) {
|
|
15244
15937
|
const tag = reader.uint32();
|
|
@@ -15296,13 +15989,13 @@ const StopsMap = {
|
|
|
15296
15989
|
return obj;
|
|
15297
15990
|
},
|
|
15298
15991
|
create(base) {
|
|
15299
|
-
return StopsMap.fromPartial(base !== null && base !==
|
|
15992
|
+
return StopsMap.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
15300
15993
|
},
|
|
15301
15994
|
fromPartial(object) {
|
|
15302
15995
|
var _a, _b;
|
|
15303
15996
|
const message = createBaseStopsMap();
|
|
15304
|
-
message.version = (_a = object.version) !== null && _a !==
|
|
15305
|
-
message.stops = Object.entries((_b = object.stops) !== null && _b !==
|
|
15997
|
+
message.version = (_a = object.version) !== null && _a !== void 0 ? _a : "";
|
|
15998
|
+
message.stops = Object.entries((_b = object.stops) !== null && _b !== void 0 ? _b : {}).reduce((acc, [key, value]) => {
|
|
15306
15999
|
if (value !== undefined) {
|
|
15307
16000
|
acc[globalThis.Number(key)] = Stop.fromPartial(value);
|
|
15308
16001
|
}
|
|
@@ -15326,7 +16019,7 @@ const StopsMap_StopsEntry = {
|
|
|
15326
16019
|
},
|
|
15327
16020
|
decode(input, length) {
|
|
15328
16021
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
15329
|
-
|
|
16022
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
15330
16023
|
const message = createBaseStopsMap_StopsEntry();
|
|
15331
16024
|
while (reader.pos < end) {
|
|
15332
16025
|
const tag = reader.uint32();
|
|
@@ -15370,12 +16063,12 @@ const StopsMap_StopsEntry = {
|
|
|
15370
16063
|
return obj;
|
|
15371
16064
|
},
|
|
15372
16065
|
create(base) {
|
|
15373
|
-
return StopsMap_StopsEntry.fromPartial(base !== null && base !==
|
|
16066
|
+
return StopsMap_StopsEntry.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
15374
16067
|
},
|
|
15375
16068
|
fromPartial(object) {
|
|
15376
16069
|
var _a;
|
|
15377
16070
|
const message = createBaseStopsMap_StopsEntry();
|
|
15378
|
-
message.key = (_a = object.key) !== null && _a !==
|
|
16071
|
+
message.key = (_a = object.key) !== null && _a !== void 0 ? _a : 0;
|
|
15379
16072
|
message.value = (object.value !== undefined && object.value !== null) ? Stop.fromPartial(object.value) : undefined;
|
|
15380
16073
|
return message;
|
|
15381
16074
|
},
|
|
@@ -15486,7 +16179,7 @@ class StopsIndex {
|
|
|
15486
16179
|
});
|
|
15487
16180
|
const stopsSet = new Map();
|
|
15488
16181
|
for (const [id, stop] of stopsMap.entries()) {
|
|
15489
|
-
const effectiveStopId = (_a = stop.parent) !== null && _a !==
|
|
16182
|
+
const effectiveStopId = (_a = stop.parent) !== null && _a !== void 0 ? _a : id;
|
|
15490
16183
|
if (!stopsSet.has(effectiveStopId)) {
|
|
15491
16184
|
stopsSet.set(effectiveStopId, {
|
|
15492
16185
|
id: effectiveStopId,
|
|
@@ -15517,8 +16210,8 @@ class StopsIndex {
|
|
|
15517
16210
|
/**
|
|
15518
16211
|
* Deserializes a binary representation of the stops.
|
|
15519
16212
|
*
|
|
15520
|
-
* @param
|
|
15521
|
-
* @returns
|
|
16213
|
+
* @param data - The binary data to deserialize.
|
|
16214
|
+
* @returns The deserialized StopFinder.
|
|
15522
16215
|
*/
|
|
15523
16216
|
static fromData(data) {
|
|
15524
16217
|
const reader = new BinaryReader(data);
|
|
@@ -15528,7 +16221,7 @@ class StopsIndex {
|
|
|
15528
16221
|
/**
|
|
15529
16222
|
* Serializes the stops into a binary protobuf.
|
|
15530
16223
|
*
|
|
15531
|
-
* @returns
|
|
16224
|
+
* @returns The serialized binary data.
|
|
15532
16225
|
*/
|
|
15533
16226
|
serialize() {
|
|
15534
16227
|
const protoStopsMap = serializeStopsMap(this.stopsMap);
|
|
@@ -15607,7 +16300,7 @@ class StopsIndex {
|
|
|
15607
16300
|
return [];
|
|
15608
16301
|
}
|
|
15609
16302
|
const equivalentStops = stop.parent
|
|
15610
|
-
? ((_b = (_a = this.stopsMap.get(stop.parent)) === null || _a ===
|
|
16303
|
+
? ((_b = (_a = this.stopsMap.get(stop.parent)) === null || _a === void 0 ? void 0 : _a.children) !== null && _b !== void 0 ? _b : [])
|
|
15611
16304
|
: stop.children;
|
|
15612
16305
|
return Array.from(new Set([id, ...equivalentStops])).map((stopId) => this.stopsMap.get(stopId));
|
|
15613
16306
|
}
|
|
@@ -15696,7 +16389,7 @@ class Duration {
|
|
|
15696
16389
|
|
|
15697
16390
|
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
|
15698
16391
|
// versions:
|
|
15699
|
-
// protoc-gen-ts_proto v2.
|
|
16392
|
+
// protoc-gen-ts_proto v2.7.7
|
|
15700
16393
|
// protoc v4.23.4
|
|
15701
16394
|
// source: src/timetable/proto/timetable.proto
|
|
15702
16395
|
/* eslint-disable */
|
|
@@ -15830,7 +16523,7 @@ function createBaseRoute() {
|
|
|
15830
16523
|
serviceRouteId: "",
|
|
15831
16524
|
};
|
|
15832
16525
|
}
|
|
15833
|
-
const Route$
|
|
16526
|
+
const Route$2 = {
|
|
15834
16527
|
encode(message, writer = new BinaryWriter()) {
|
|
15835
16528
|
if (message.stopTimes.length !== 0) {
|
|
15836
16529
|
writer.uint32(10).bytes(message.stopTimes);
|
|
@@ -15848,7 +16541,7 @@ const Route$1 = {
|
|
|
15848
16541
|
},
|
|
15849
16542
|
decode(input, length) {
|
|
15850
16543
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
15851
|
-
|
|
16544
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
15852
16545
|
const message = createBaseRoute();
|
|
15853
16546
|
while (reader.pos < end) {
|
|
15854
16547
|
const tag = reader.uint32();
|
|
@@ -15916,15 +16609,15 @@ const Route$1 = {
|
|
|
15916
16609
|
return obj;
|
|
15917
16610
|
},
|
|
15918
16611
|
create(base) {
|
|
15919
|
-
return Route$
|
|
16612
|
+
return Route$2.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
15920
16613
|
},
|
|
15921
16614
|
fromPartial(object) {
|
|
15922
16615
|
var _a, _b, _c, _d;
|
|
15923
16616
|
const message = createBaseRoute();
|
|
15924
|
-
message.stopTimes = (_a = object.stopTimes) !== null && _a !==
|
|
15925
|
-
message.pickUpDropOffTypes = (_b = object.pickUpDropOffTypes) !== null && _b !==
|
|
15926
|
-
message.stops = (_c = object.stops) !== null && _c !==
|
|
15927
|
-
message.serviceRouteId = (_d = object.serviceRouteId) !== null && _d !==
|
|
16617
|
+
message.stopTimes = (_a = object.stopTimes) !== null && _a !== void 0 ? _a : new Uint8Array(0);
|
|
16618
|
+
message.pickUpDropOffTypes = (_b = object.pickUpDropOffTypes) !== null && _b !== void 0 ? _b : new Uint8Array(0);
|
|
16619
|
+
message.stops = (_c = object.stops) !== null && _c !== void 0 ? _c : new Uint8Array(0);
|
|
16620
|
+
message.serviceRouteId = (_d = object.serviceRouteId) !== null && _d !== void 0 ? _d : "";
|
|
15928
16621
|
return message;
|
|
15929
16622
|
},
|
|
15930
16623
|
};
|
|
@@ -15940,7 +16633,7 @@ const RoutesAdjacency = {
|
|
|
15940
16633
|
},
|
|
15941
16634
|
decode(input, length) {
|
|
15942
16635
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
15943
|
-
|
|
16636
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
15944
16637
|
const message = createBaseRoutesAdjacency();
|
|
15945
16638
|
while (reader.pos < end) {
|
|
15946
16639
|
const tag = reader.uint32();
|
|
@@ -15967,7 +16660,7 @@ const RoutesAdjacency = {
|
|
|
15967
16660
|
return {
|
|
15968
16661
|
routes: isObject(object.routes)
|
|
15969
16662
|
? Object.entries(object.routes).reduce((acc, [key, value]) => {
|
|
15970
|
-
acc[key] = Route$
|
|
16663
|
+
acc[key] = Route$2.fromJSON(value);
|
|
15971
16664
|
return acc;
|
|
15972
16665
|
}, {})
|
|
15973
16666
|
: {},
|
|
@@ -15980,21 +16673,21 @@ const RoutesAdjacency = {
|
|
|
15980
16673
|
if (entries.length > 0) {
|
|
15981
16674
|
obj.routes = {};
|
|
15982
16675
|
entries.forEach(([k, v]) => {
|
|
15983
|
-
obj.routes[k] = Route$
|
|
16676
|
+
obj.routes[k] = Route$2.toJSON(v);
|
|
15984
16677
|
});
|
|
15985
16678
|
}
|
|
15986
16679
|
}
|
|
15987
16680
|
return obj;
|
|
15988
16681
|
},
|
|
15989
16682
|
create(base) {
|
|
15990
|
-
return RoutesAdjacency.fromPartial(base !== null && base !==
|
|
16683
|
+
return RoutesAdjacency.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
15991
16684
|
},
|
|
15992
16685
|
fromPartial(object) {
|
|
15993
16686
|
var _a;
|
|
15994
16687
|
const message = createBaseRoutesAdjacency();
|
|
15995
|
-
message.routes = Object.entries((_a = object.routes) !== null && _a !==
|
|
16688
|
+
message.routes = Object.entries((_a = object.routes) !== null && _a !== void 0 ? _a : {}).reduce((acc, [key, value]) => {
|
|
15996
16689
|
if (value !== undefined) {
|
|
15997
|
-
acc[key] = Route$
|
|
16690
|
+
acc[key] = Route$2.fromPartial(value);
|
|
15998
16691
|
}
|
|
15999
16692
|
return acc;
|
|
16000
16693
|
}, {});
|
|
@@ -16010,13 +16703,13 @@ const RoutesAdjacency_RoutesEntry = {
|
|
|
16010
16703
|
writer.uint32(10).string(message.key);
|
|
16011
16704
|
}
|
|
16012
16705
|
if (message.value !== undefined) {
|
|
16013
|
-
Route$
|
|
16706
|
+
Route$2.encode(message.value, writer.uint32(18).fork()).join();
|
|
16014
16707
|
}
|
|
16015
16708
|
return writer;
|
|
16016
16709
|
},
|
|
16017
16710
|
decode(input, length) {
|
|
16018
16711
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
16019
|
-
|
|
16712
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
16020
16713
|
const message = createBaseRoutesAdjacency_RoutesEntry();
|
|
16021
16714
|
while (reader.pos < end) {
|
|
16022
16715
|
const tag = reader.uint32();
|
|
@@ -16032,7 +16725,7 @@ const RoutesAdjacency_RoutesEntry = {
|
|
|
16032
16725
|
if (tag !== 18) {
|
|
16033
16726
|
break;
|
|
16034
16727
|
}
|
|
16035
|
-
message.value = Route$
|
|
16728
|
+
message.value = Route$2.decode(reader, reader.uint32());
|
|
16036
16729
|
continue;
|
|
16037
16730
|
}
|
|
16038
16731
|
}
|
|
@@ -16046,7 +16739,7 @@ const RoutesAdjacency_RoutesEntry = {
|
|
|
16046
16739
|
fromJSON(object) {
|
|
16047
16740
|
return {
|
|
16048
16741
|
key: isSet(object.key) ? globalThis.String(object.key) : "",
|
|
16049
|
-
value: isSet(object.value) ? Route$
|
|
16742
|
+
value: isSet(object.value) ? Route$2.fromJSON(object.value) : undefined,
|
|
16050
16743
|
};
|
|
16051
16744
|
},
|
|
16052
16745
|
toJSON(message) {
|
|
@@ -16055,18 +16748,18 @@ const RoutesAdjacency_RoutesEntry = {
|
|
|
16055
16748
|
obj.key = message.key;
|
|
16056
16749
|
}
|
|
16057
16750
|
if (message.value !== undefined) {
|
|
16058
|
-
obj.value = Route$
|
|
16751
|
+
obj.value = Route$2.toJSON(message.value);
|
|
16059
16752
|
}
|
|
16060
16753
|
return obj;
|
|
16061
16754
|
},
|
|
16062
16755
|
create(base) {
|
|
16063
|
-
return RoutesAdjacency_RoutesEntry.fromPartial(base !== null && base !==
|
|
16756
|
+
return RoutesAdjacency_RoutesEntry.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
16064
16757
|
},
|
|
16065
16758
|
fromPartial(object) {
|
|
16066
16759
|
var _a;
|
|
16067
16760
|
const message = createBaseRoutesAdjacency_RoutesEntry();
|
|
16068
|
-
message.key = (_a = object.key) !== null && _a !==
|
|
16069
|
-
message.value = (object.value !== undefined && object.value !== null) ? Route$
|
|
16761
|
+
message.key = (_a = object.key) !== null && _a !== void 0 ? _a : "";
|
|
16762
|
+
message.value = (object.value !== undefined && object.value !== null) ? Route$2.fromPartial(object.value) : undefined;
|
|
16070
16763
|
return message;
|
|
16071
16764
|
},
|
|
16072
16765
|
};
|
|
@@ -16088,7 +16781,7 @@ const Transfer = {
|
|
|
16088
16781
|
},
|
|
16089
16782
|
decode(input, length) {
|
|
16090
16783
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
16091
|
-
|
|
16784
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
16092
16785
|
const message = createBaseTransfer();
|
|
16093
16786
|
while (reader.pos < end) {
|
|
16094
16787
|
const tag = reader.uint32();
|
|
@@ -16143,14 +16836,14 @@ const Transfer = {
|
|
|
16143
16836
|
return obj;
|
|
16144
16837
|
},
|
|
16145
16838
|
create(base) {
|
|
16146
|
-
return Transfer.fromPartial(base !== null && base !==
|
|
16839
|
+
return Transfer.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
16147
16840
|
},
|
|
16148
16841
|
fromPartial(object) {
|
|
16149
16842
|
var _a, _b, _c;
|
|
16150
16843
|
const message = createBaseTransfer();
|
|
16151
|
-
message.destination = (_a = object.destination) !== null && _a !==
|
|
16152
|
-
message.type = (_b = object.type) !== null && _b !==
|
|
16153
|
-
message.minTransferTime = (_c = object.minTransferTime) !== null && _c !==
|
|
16844
|
+
message.destination = (_a = object.destination) !== null && _a !== void 0 ? _a : 0;
|
|
16845
|
+
message.type = (_b = object.type) !== null && _b !== void 0 ? _b : 0;
|
|
16846
|
+
message.minTransferTime = (_c = object.minTransferTime) !== null && _c !== void 0 ? _c : undefined;
|
|
16154
16847
|
return message;
|
|
16155
16848
|
},
|
|
16156
16849
|
};
|
|
@@ -16166,7 +16859,7 @@ const StopsAdjacency = {
|
|
|
16166
16859
|
},
|
|
16167
16860
|
decode(input, length) {
|
|
16168
16861
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
16169
|
-
|
|
16862
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
16170
16863
|
const message = createBaseStopsAdjacency();
|
|
16171
16864
|
while (reader.pos < end) {
|
|
16172
16865
|
const tag = reader.uint32();
|
|
@@ -16213,12 +16906,12 @@ const StopsAdjacency = {
|
|
|
16213
16906
|
return obj;
|
|
16214
16907
|
},
|
|
16215
16908
|
create(base) {
|
|
16216
|
-
return StopsAdjacency.fromPartial(base !== null && base !==
|
|
16909
|
+
return StopsAdjacency.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
16217
16910
|
},
|
|
16218
16911
|
fromPartial(object) {
|
|
16219
16912
|
var _a;
|
|
16220
16913
|
const message = createBaseStopsAdjacency();
|
|
16221
|
-
message.stops = Object.entries((_a = object.stops) !== null && _a !==
|
|
16914
|
+
message.stops = Object.entries((_a = object.stops) !== null && _a !== void 0 ? _a : {}).reduce((acc, [key, value]) => {
|
|
16222
16915
|
if (value !== undefined) {
|
|
16223
16916
|
acc[key] = StopsAdjacency_StopAdjacency.fromPartial(value);
|
|
16224
16917
|
}
|
|
@@ -16242,7 +16935,7 @@ const StopsAdjacency_StopAdjacency = {
|
|
|
16242
16935
|
},
|
|
16243
16936
|
decode(input, length) {
|
|
16244
16937
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
16245
|
-
|
|
16938
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
16246
16939
|
const message = createBaseStopsAdjacency_StopAdjacency();
|
|
16247
16940
|
while (reader.pos < end) {
|
|
16248
16941
|
const tag = reader.uint32();
|
|
@@ -16271,31 +16964,31 @@ const StopsAdjacency_StopAdjacency = {
|
|
|
16271
16964
|
},
|
|
16272
16965
|
fromJSON(object) {
|
|
16273
16966
|
return {
|
|
16274
|
-
transfers: globalThis.Array.isArray(object === null || object ===
|
|
16967
|
+
transfers: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.transfers)
|
|
16275
16968
|
? object.transfers.map((e) => Transfer.fromJSON(e))
|
|
16276
16969
|
: [],
|
|
16277
|
-
routes: globalThis.Array.isArray(object === null || object ===
|
|
16970
|
+
routes: globalThis.Array.isArray(object === null || object === void 0 ? void 0 : object.routes) ? object.routes.map((e) => globalThis.String(e)) : [],
|
|
16278
16971
|
};
|
|
16279
16972
|
},
|
|
16280
16973
|
toJSON(message) {
|
|
16281
16974
|
var _a, _b;
|
|
16282
16975
|
const obj = {};
|
|
16283
|
-
if ((_a = message.transfers) === null || _a ===
|
|
16976
|
+
if ((_a = message.transfers) === null || _a === void 0 ? void 0 : _a.length) {
|
|
16284
16977
|
obj.transfers = message.transfers.map((e) => Transfer.toJSON(e));
|
|
16285
16978
|
}
|
|
16286
|
-
if ((_b = message.routes) === null || _b ===
|
|
16979
|
+
if ((_b = message.routes) === null || _b === void 0 ? void 0 : _b.length) {
|
|
16287
16980
|
obj.routes = message.routes;
|
|
16288
16981
|
}
|
|
16289
16982
|
return obj;
|
|
16290
16983
|
},
|
|
16291
16984
|
create(base) {
|
|
16292
|
-
return StopsAdjacency_StopAdjacency.fromPartial(base !== null && base !==
|
|
16985
|
+
return StopsAdjacency_StopAdjacency.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
16293
16986
|
},
|
|
16294
16987
|
fromPartial(object) {
|
|
16295
16988
|
var _a, _b;
|
|
16296
16989
|
const message = createBaseStopsAdjacency_StopAdjacency();
|
|
16297
|
-
message.transfers = ((_a = object.transfers) === null || _a ===
|
|
16298
|
-
message.routes = ((_b = object.routes) === null || _b ===
|
|
16990
|
+
message.transfers = ((_a = object.transfers) === null || _a === void 0 ? void 0 : _a.map((e) => Transfer.fromPartial(e))) || [];
|
|
16991
|
+
message.routes = ((_b = object.routes) === null || _b === void 0 ? void 0 : _b.map((e) => e)) || [];
|
|
16299
16992
|
return message;
|
|
16300
16993
|
},
|
|
16301
16994
|
};
|
|
@@ -16314,7 +17007,7 @@ const StopsAdjacency_StopsEntry = {
|
|
|
16314
17007
|
},
|
|
16315
17008
|
decode(input, length) {
|
|
16316
17009
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
16317
|
-
|
|
17010
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
16318
17011
|
const message = createBaseStopsAdjacency_StopsEntry();
|
|
16319
17012
|
while (reader.pos < end) {
|
|
16320
17013
|
const tag = reader.uint32();
|
|
@@ -16358,12 +17051,12 @@ const StopsAdjacency_StopsEntry = {
|
|
|
16358
17051
|
return obj;
|
|
16359
17052
|
},
|
|
16360
17053
|
create(base) {
|
|
16361
|
-
return StopsAdjacency_StopsEntry.fromPartial(base !== null && base !==
|
|
17054
|
+
return StopsAdjacency_StopsEntry.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
16362
17055
|
},
|
|
16363
17056
|
fromPartial(object) {
|
|
16364
17057
|
var _a;
|
|
16365
17058
|
const message = createBaseStopsAdjacency_StopsEntry();
|
|
16366
|
-
message.key = (_a = object.key) !== null && _a !==
|
|
17059
|
+
message.key = (_a = object.key) !== null && _a !== void 0 ? _a : "";
|
|
16367
17060
|
message.value = (object.value !== undefined && object.value !== null)
|
|
16368
17061
|
? StopsAdjacency_StopAdjacency.fromPartial(object.value)
|
|
16369
17062
|
: undefined;
|
|
@@ -16385,7 +17078,7 @@ const ServiceRoute = {
|
|
|
16385
17078
|
},
|
|
16386
17079
|
decode(input, length) {
|
|
16387
17080
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
16388
|
-
|
|
17081
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
16389
17082
|
const message = createBaseServiceRoute();
|
|
16390
17083
|
while (reader.pos < end) {
|
|
16391
17084
|
const tag = reader.uint32();
|
|
@@ -16429,13 +17122,13 @@ const ServiceRoute = {
|
|
|
16429
17122
|
return obj;
|
|
16430
17123
|
},
|
|
16431
17124
|
create(base) {
|
|
16432
|
-
return ServiceRoute.fromPartial(base !== null && base !==
|
|
17125
|
+
return ServiceRoute.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
16433
17126
|
},
|
|
16434
17127
|
fromPartial(object) {
|
|
16435
17128
|
var _a, _b;
|
|
16436
17129
|
const message = createBaseServiceRoute();
|
|
16437
|
-
message.type = (_a = object.type) !== null && _a !==
|
|
16438
|
-
message.name = (_b = object.name) !== null && _b !==
|
|
17130
|
+
message.type = (_a = object.type) !== null && _a !== void 0 ? _a : 0;
|
|
17131
|
+
message.name = (_b = object.name) !== null && _b !== void 0 ? _b : "";
|
|
16439
17132
|
return message;
|
|
16440
17133
|
},
|
|
16441
17134
|
};
|
|
@@ -16451,7 +17144,7 @@ const ServiceRoutesMap = {
|
|
|
16451
17144
|
},
|
|
16452
17145
|
decode(input, length) {
|
|
16453
17146
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
16454
|
-
|
|
17147
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
16455
17148
|
const message = createBaseServiceRoutesMap();
|
|
16456
17149
|
while (reader.pos < end) {
|
|
16457
17150
|
const tag = reader.uint32();
|
|
@@ -16498,12 +17191,12 @@ const ServiceRoutesMap = {
|
|
|
16498
17191
|
return obj;
|
|
16499
17192
|
},
|
|
16500
17193
|
create(base) {
|
|
16501
|
-
return ServiceRoutesMap.fromPartial(base !== null && base !==
|
|
17194
|
+
return ServiceRoutesMap.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
16502
17195
|
},
|
|
16503
17196
|
fromPartial(object) {
|
|
16504
17197
|
var _a;
|
|
16505
17198
|
const message = createBaseServiceRoutesMap();
|
|
16506
|
-
message.routes = Object.entries((_a = object.routes) !== null && _a !==
|
|
17199
|
+
message.routes = Object.entries((_a = object.routes) !== null && _a !== void 0 ? _a : {}).reduce((acc, [key, value]) => {
|
|
16507
17200
|
if (value !== undefined) {
|
|
16508
17201
|
acc[key] = ServiceRoute.fromPartial(value);
|
|
16509
17202
|
}
|
|
@@ -16527,7 +17220,7 @@ const ServiceRoutesMap_RoutesEntry = {
|
|
|
16527
17220
|
},
|
|
16528
17221
|
decode(input, length) {
|
|
16529
17222
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
16530
|
-
|
|
17223
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
16531
17224
|
const message = createBaseServiceRoutesMap_RoutesEntry();
|
|
16532
17225
|
while (reader.pos < end) {
|
|
16533
17226
|
const tag = reader.uint32();
|
|
@@ -16571,12 +17264,12 @@ const ServiceRoutesMap_RoutesEntry = {
|
|
|
16571
17264
|
return obj;
|
|
16572
17265
|
},
|
|
16573
17266
|
create(base) {
|
|
16574
|
-
return ServiceRoutesMap_RoutesEntry.fromPartial(base !== null && base !==
|
|
17267
|
+
return ServiceRoutesMap_RoutesEntry.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
16575
17268
|
},
|
|
16576
17269
|
fromPartial(object) {
|
|
16577
17270
|
var _a;
|
|
16578
17271
|
const message = createBaseServiceRoutesMap_RoutesEntry();
|
|
16579
|
-
message.key = (_a = object.key) !== null && _a !==
|
|
17272
|
+
message.key = (_a = object.key) !== null && _a !== void 0 ? _a : "";
|
|
16580
17273
|
message.value = (object.value !== undefined && object.value !== null)
|
|
16581
17274
|
? ServiceRoute.fromPartial(object.value)
|
|
16582
17275
|
: undefined;
|
|
@@ -16604,7 +17297,7 @@ const Timetable$1 = {
|
|
|
16604
17297
|
},
|
|
16605
17298
|
decode(input, length) {
|
|
16606
17299
|
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
|
|
16607
|
-
|
|
17300
|
+
const end = length === undefined ? reader.len : reader.pos + length;
|
|
16608
17301
|
const message = createBaseTimetable();
|
|
16609
17302
|
while (reader.pos < end) {
|
|
16610
17303
|
const tag = reader.uint32();
|
|
@@ -16670,12 +17363,12 @@ const Timetable$1 = {
|
|
|
16670
17363
|
return obj;
|
|
16671
17364
|
},
|
|
16672
17365
|
create(base) {
|
|
16673
|
-
return Timetable$1.fromPartial(base !== null && base !==
|
|
17366
|
+
return Timetable$1.fromPartial(base !== null && base !== void 0 ? base : {});
|
|
16674
17367
|
},
|
|
16675
17368
|
fromPartial(object) {
|
|
16676
17369
|
var _a;
|
|
16677
17370
|
const message = createBaseTimetable();
|
|
16678
|
-
message.version = (_a = object.version) !== null && _a !==
|
|
17371
|
+
message.version = (_a = object.version) !== null && _a !== void 0 ? _a : "";
|
|
16679
17372
|
message.stopsAdjacency = (object.stopsAdjacency !== undefined && object.stopsAdjacency !== null)
|
|
16680
17373
|
? StopsAdjacency.fromPartial(object.stopsAdjacency)
|
|
16681
17374
|
: undefined;
|
|
@@ -16720,231 +17413,6 @@ function isSet(value) {
|
|
|
16720
17413
|
return value !== null && value !== undefined;
|
|
16721
17414
|
}
|
|
16722
17415
|
|
|
16723
|
-
const isLittleEndian = (() => {
|
|
16724
|
-
const buffer = new ArrayBuffer(4);
|
|
16725
|
-
const view = new DataView(buffer);
|
|
16726
|
-
view.setUint32(0, 0x12345678);
|
|
16727
|
-
return new Uint8Array(buffer)[0] === 0x78;
|
|
16728
|
-
})();
|
|
16729
|
-
const STANDARD_ENDIANNESS = true; // true = little-endian
|
|
16730
|
-
function uint32ArrayToBytes(array) {
|
|
16731
|
-
if (isLittleEndian === STANDARD_ENDIANNESS) {
|
|
16732
|
-
return new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
|
|
16733
|
-
}
|
|
16734
|
-
// If endianness doesn't match, we need to swap byte order
|
|
16735
|
-
const result = new Uint8Array(array.length * 4);
|
|
16736
|
-
const view = new DataView(result.buffer);
|
|
16737
|
-
for (let i = 0; i < array.length; i++) {
|
|
16738
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16739
|
-
view.setUint32(i * 4, array[i], STANDARD_ENDIANNESS);
|
|
16740
|
-
}
|
|
16741
|
-
return result;
|
|
16742
|
-
}
|
|
16743
|
-
function bytesToUint32Array(bytes) {
|
|
16744
|
-
if (bytes.byteLength % 4 !== 0) {
|
|
16745
|
-
throw new Error('Byte array length must be a multiple of 4 to convert to Uint32Array');
|
|
16746
|
-
}
|
|
16747
|
-
// If system endianness matches our standard, we can create a view directly
|
|
16748
|
-
if (isLittleEndian === STANDARD_ENDIANNESS) {
|
|
16749
|
-
return new Uint32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
|
|
16750
|
-
}
|
|
16751
|
-
// If endianness doesn't match, we need to swap byte order
|
|
16752
|
-
const result = new Uint32Array(bytes.byteLength / 4);
|
|
16753
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
16754
|
-
for (let i = 0; i < result.length; i++) {
|
|
16755
|
-
result[i] = view.getUint32(i * 4, STANDARD_ENDIANNESS);
|
|
16756
|
-
}
|
|
16757
|
-
return result;
|
|
16758
|
-
}
|
|
16759
|
-
function uint16ArrayToBytes(array) {
|
|
16760
|
-
if (isLittleEndian === STANDARD_ENDIANNESS) {
|
|
16761
|
-
return new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
|
|
16762
|
-
}
|
|
16763
|
-
// If endianness doesn't match, we need to swap byte order
|
|
16764
|
-
const result = new Uint8Array(array.length * 2);
|
|
16765
|
-
const view = new DataView(result.buffer);
|
|
16766
|
-
for (let i = 0; i < array.length; i++) {
|
|
16767
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16768
|
-
view.setUint16(i * 2, array[i], STANDARD_ENDIANNESS);
|
|
16769
|
-
}
|
|
16770
|
-
return result;
|
|
16771
|
-
}
|
|
16772
|
-
function bytesToUint16Array(bytes) {
|
|
16773
|
-
if (bytes.byteLength % 2 !== 0) {
|
|
16774
|
-
throw new Error('Byte array length must be a multiple of 2 to convert to Uint16Array');
|
|
16775
|
-
}
|
|
16776
|
-
// If system endianness matches our standard, we can create a view directly
|
|
16777
|
-
if (isLittleEndian === STANDARD_ENDIANNESS) {
|
|
16778
|
-
return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2);
|
|
16779
|
-
}
|
|
16780
|
-
// If endianness doesn't match, we need to swap byte order
|
|
16781
|
-
const result = new Uint16Array(bytes.byteLength / 2);
|
|
16782
|
-
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
16783
|
-
for (let i = 0; i < result.length; i++) {
|
|
16784
|
-
result[i] = view.getUint16(i * 2, STANDARD_ENDIANNESS);
|
|
16785
|
-
}
|
|
16786
|
-
return result;
|
|
16787
|
-
}
|
|
16788
|
-
const serializeStopsAdjacency = (stopsAdjacency) => {
|
|
16789
|
-
const protoStopsAdjacency = {
|
|
16790
|
-
stops: {},
|
|
16791
|
-
};
|
|
16792
|
-
stopsAdjacency.forEach((value, key) => {
|
|
16793
|
-
protoStopsAdjacency.stops[key] = {
|
|
16794
|
-
transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: serializeTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
|
|
16795
|
-
minTransferTime: transfer.minTransferTime.toSeconds(),
|
|
16796
|
-
})))),
|
|
16797
|
-
routes: value.routes,
|
|
16798
|
-
};
|
|
16799
|
-
});
|
|
16800
|
-
return protoStopsAdjacency;
|
|
16801
|
-
};
|
|
16802
|
-
const serializeRoutesAdjacency = (routesAdjacency) => {
|
|
16803
|
-
const protoRoutesAdjacency = {
|
|
16804
|
-
routes: {},
|
|
16805
|
-
};
|
|
16806
|
-
routesAdjacency.forEach((value, key) => {
|
|
16807
|
-
protoRoutesAdjacency.routes[key] = {
|
|
16808
|
-
stopTimes: uint16ArrayToBytes(value.stopTimes),
|
|
16809
|
-
pickUpDropOffTypes: value.pickUpDropOffTypes,
|
|
16810
|
-
stops: uint32ArrayToBytes(value.stops),
|
|
16811
|
-
serviceRouteId: value.serviceRouteId,
|
|
16812
|
-
};
|
|
16813
|
-
});
|
|
16814
|
-
return protoRoutesAdjacency;
|
|
16815
|
-
};
|
|
16816
|
-
const serializeServiceRoutesMap = (serviceRoutesMap) => {
|
|
16817
|
-
const protoServiceRoutesMap = {
|
|
16818
|
-
routes: {},
|
|
16819
|
-
};
|
|
16820
|
-
serviceRoutesMap.forEach((value, key) => {
|
|
16821
|
-
protoServiceRoutesMap.routes[key] = {
|
|
16822
|
-
type: serializeRouteType(value.type),
|
|
16823
|
-
name: value.name,
|
|
16824
|
-
};
|
|
16825
|
-
});
|
|
16826
|
-
return protoServiceRoutesMap;
|
|
16827
|
-
};
|
|
16828
|
-
const deserializeStopsAdjacency = (protoStopsAdjacency) => {
|
|
16829
|
-
const stopsAdjacency = new Map();
|
|
16830
|
-
Object.entries(protoStopsAdjacency.stops).forEach(([keyStr, value]) => {
|
|
16831
|
-
const key = parseInt(keyStr, 10);
|
|
16832
|
-
stopsAdjacency.set(key, {
|
|
16833
|
-
transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: parseTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
|
|
16834
|
-
minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
|
|
16835
|
-
})))),
|
|
16836
|
-
routes: value.routes,
|
|
16837
|
-
});
|
|
16838
|
-
});
|
|
16839
|
-
return stopsAdjacency;
|
|
16840
|
-
};
|
|
16841
|
-
const deserializeRoutesAdjacency = (protoRoutesAdjacency) => {
|
|
16842
|
-
const routesAdjacency = new Map();
|
|
16843
|
-
Object.entries(protoRoutesAdjacency.routes).forEach(([key, value]) => {
|
|
16844
|
-
const stops = bytesToUint32Array(value.stops);
|
|
16845
|
-
const indices = new Map();
|
|
16846
|
-
for (let i = 0; i < stops.length; i++) {
|
|
16847
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
16848
|
-
indices.set(stops[i], i);
|
|
16849
|
-
}
|
|
16850
|
-
routesAdjacency.set(key, {
|
|
16851
|
-
stopTimes: bytesToUint16Array(value.stopTimes),
|
|
16852
|
-
pickUpDropOffTypes: value.pickUpDropOffTypes,
|
|
16853
|
-
stops: stops,
|
|
16854
|
-
stopIndices: indices,
|
|
16855
|
-
serviceRouteId: value.serviceRouteId,
|
|
16856
|
-
});
|
|
16857
|
-
});
|
|
16858
|
-
return routesAdjacency;
|
|
16859
|
-
};
|
|
16860
|
-
const deserializeServiceRoutesMap = (protoServiceRoutesMap) => {
|
|
16861
|
-
const serviceRoutesMap = new Map();
|
|
16862
|
-
Object.entries(protoServiceRoutesMap.routes).forEach(([key, value]) => {
|
|
16863
|
-
serviceRoutesMap.set(key, {
|
|
16864
|
-
type: parseRouteType(value.type),
|
|
16865
|
-
name: value.name,
|
|
16866
|
-
});
|
|
16867
|
-
});
|
|
16868
|
-
return serviceRoutesMap;
|
|
16869
|
-
};
|
|
16870
|
-
const parseTransferType = (type) => {
|
|
16871
|
-
switch (type) {
|
|
16872
|
-
case TransferType.RECOMMENDED_TRANSFER_POINT:
|
|
16873
|
-
return 'RECOMMENDED';
|
|
16874
|
-
case TransferType.TIMED_TRANSFER:
|
|
16875
|
-
return 'GUARANTEED';
|
|
16876
|
-
case TransferType.REQUIRES_MINIMAL_TIME:
|
|
16877
|
-
return 'REQUIRES_MINIMAL_TIME';
|
|
16878
|
-
case TransferType.IN_SEAT_TRANSFER:
|
|
16879
|
-
return 'IN_SEAT';
|
|
16880
|
-
case TransferType.UNRECOGNIZED:
|
|
16881
|
-
throw new Error('Unrecognized protobuf transfer type.');
|
|
16882
|
-
}
|
|
16883
|
-
};
|
|
16884
|
-
const serializeTransferType = (type) => {
|
|
16885
|
-
switch (type) {
|
|
16886
|
-
case 'RECOMMENDED':
|
|
16887
|
-
return TransferType.RECOMMENDED_TRANSFER_POINT;
|
|
16888
|
-
case 'GUARANTEED':
|
|
16889
|
-
return TransferType.TIMED_TRANSFER;
|
|
16890
|
-
case 'REQUIRES_MINIMAL_TIME':
|
|
16891
|
-
return TransferType.REQUIRES_MINIMAL_TIME;
|
|
16892
|
-
case 'IN_SEAT':
|
|
16893
|
-
return TransferType.IN_SEAT_TRANSFER;
|
|
16894
|
-
}
|
|
16895
|
-
};
|
|
16896
|
-
const parseRouteType = (type) => {
|
|
16897
|
-
switch (type) {
|
|
16898
|
-
case RouteType.TRAM:
|
|
16899
|
-
return 'TRAM';
|
|
16900
|
-
case RouteType.SUBWAY:
|
|
16901
|
-
return 'SUBWAY';
|
|
16902
|
-
case RouteType.RAIL:
|
|
16903
|
-
return 'RAIL';
|
|
16904
|
-
case RouteType.BUS:
|
|
16905
|
-
return 'BUS';
|
|
16906
|
-
case RouteType.FERRY:
|
|
16907
|
-
return 'FERRY';
|
|
16908
|
-
case RouteType.CABLE_TRAM:
|
|
16909
|
-
return 'CABLE_TRAM';
|
|
16910
|
-
case RouteType.AERIAL_LIFT:
|
|
16911
|
-
return 'AERIAL_LIFT';
|
|
16912
|
-
case RouteType.FUNICULAR:
|
|
16913
|
-
return 'FUNICULAR';
|
|
16914
|
-
case RouteType.TROLLEYBUS:
|
|
16915
|
-
return 'TROLLEYBUS';
|
|
16916
|
-
case RouteType.MONORAIL:
|
|
16917
|
-
return 'MONORAIL';
|
|
16918
|
-
case RouteType.UNRECOGNIZED:
|
|
16919
|
-
default:
|
|
16920
|
-
throw new Error('Unrecognized protobuf route type.');
|
|
16921
|
-
}
|
|
16922
|
-
};
|
|
16923
|
-
const serializeRouteType = (type) => {
|
|
16924
|
-
switch (type) {
|
|
16925
|
-
case 'TRAM':
|
|
16926
|
-
return RouteType.TRAM;
|
|
16927
|
-
case 'SUBWAY':
|
|
16928
|
-
return RouteType.SUBWAY;
|
|
16929
|
-
case 'RAIL':
|
|
16930
|
-
return RouteType.RAIL;
|
|
16931
|
-
case 'BUS':
|
|
16932
|
-
return RouteType.BUS;
|
|
16933
|
-
case 'FERRY':
|
|
16934
|
-
return RouteType.FERRY;
|
|
16935
|
-
case 'CABLE_TRAM':
|
|
16936
|
-
return RouteType.CABLE_TRAM;
|
|
16937
|
-
case 'AERIAL_LIFT':
|
|
16938
|
-
return RouteType.AERIAL_LIFT;
|
|
16939
|
-
case 'FUNICULAR':
|
|
16940
|
-
return RouteType.FUNICULAR;
|
|
16941
|
-
case 'TROLLEYBUS':
|
|
16942
|
-
return RouteType.TROLLEYBUS;
|
|
16943
|
-
case 'MONORAIL':
|
|
16944
|
-
return RouteType.MONORAIL;
|
|
16945
|
-
}
|
|
16946
|
-
};
|
|
16947
|
-
|
|
16948
17416
|
/**
|
|
16949
17417
|
* A class representing a time as minutes since midnight.
|
|
16950
17418
|
*/
|
|
@@ -17034,9 +17502,12 @@ class Time {
|
|
|
17034
17502
|
const [hoursStr, minutesStr, secondsStr] = timeStr.split(':');
|
|
17035
17503
|
if (hoursStr === undefined ||
|
|
17036
17504
|
minutesStr === undefined ||
|
|
17505
|
+
hoursStr.trim() === '' ||
|
|
17506
|
+
minutesStr.trim() === '' ||
|
|
17037
17507
|
isNaN(Number(hoursStr)) ||
|
|
17038
17508
|
isNaN(Number(minutesStr)) ||
|
|
17039
|
-
(secondsStr !== undefined &&
|
|
17509
|
+
(secondsStr !== undefined &&
|
|
17510
|
+
(secondsStr.trim() === '' || isNaN(Number(secondsStr))))) {
|
|
17040
17511
|
throw new Error('Input string must be in the format "HH:MM:SS" or "HH:MM".');
|
|
17041
17512
|
}
|
|
17042
17513
|
const hours = parseInt(hoursStr, 10);
|
|
@@ -17050,8 +17521,11 @@ class Time {
|
|
|
17050
17521
|
* @returns A string representing the time.
|
|
17051
17522
|
*/
|
|
17052
17523
|
toString() {
|
|
17053
|
-
|
|
17524
|
+
let hours = Math.floor(this.minutesSinceMidnight / 60);
|
|
17054
17525
|
const minutes = Math.floor(this.minutesSinceMidnight % 60);
|
|
17526
|
+
if (hours >= 24) {
|
|
17527
|
+
hours = hours % 24;
|
|
17528
|
+
}
|
|
17055
17529
|
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
|
|
17056
17530
|
}
|
|
17057
17531
|
/**
|
|
@@ -17106,9 +17580,7 @@ class Time {
|
|
|
17106
17580
|
throw new Error('At least one Time instance is required.');
|
|
17107
17581
|
}
|
|
17108
17582
|
return times.reduce((maxTime, currentTime) => {
|
|
17109
|
-
return currentTime.
|
|
17110
|
-
? currentTime
|
|
17111
|
-
: maxTime;
|
|
17583
|
+
return currentTime.isAfter(maxTime) ? currentTime : maxTime;
|
|
17112
17584
|
});
|
|
17113
17585
|
}
|
|
17114
17586
|
/**
|
|
@@ -17122,18 +17594,476 @@ class Time {
|
|
|
17122
17594
|
throw new Error('At least one Time instance is required.');
|
|
17123
17595
|
}
|
|
17124
17596
|
return times.reduce((minTime, currentTime) => {
|
|
17125
|
-
return currentTime.
|
|
17126
|
-
? currentTime
|
|
17127
|
-
: minTime;
|
|
17597
|
+
return currentTime.isBefore(minTime) ? currentTime : minTime;
|
|
17128
17598
|
});
|
|
17129
17599
|
}
|
|
17600
|
+
/**
|
|
17601
|
+
* Determines if the current Time instance is after another Time instance.
|
|
17602
|
+
*
|
|
17603
|
+
* @param otherTime - A Time instance to compare against.
|
|
17604
|
+
* @returns True if the current Time instance is after the other Time instance, otherwise false.
|
|
17605
|
+
*/
|
|
17606
|
+
isAfter(otherTime) {
|
|
17607
|
+
return this.minutesSinceMidnight > otherTime.toMinutes();
|
|
17608
|
+
}
|
|
17609
|
+
/**
|
|
17610
|
+
* Determines if the current Time instance is before another Time instance.
|
|
17611
|
+
*
|
|
17612
|
+
* @param otherTime - A Time instance to compare against.
|
|
17613
|
+
* @returns True if the current Time instance is before the other Time instance, otherwise false.
|
|
17614
|
+
*/
|
|
17615
|
+
isBefore(otherTime) {
|
|
17616
|
+
return this.minutesSinceMidnight < otherTime.toMinutes();
|
|
17617
|
+
}
|
|
17618
|
+
/**
|
|
17619
|
+
* Determines if the current Time instance is equal to another Time instance.
|
|
17620
|
+
*
|
|
17621
|
+
* @param otherTime - A Time instance to compare against.
|
|
17622
|
+
* @returns True if the current Time instance is equal to the other Time instance, otherwise false.
|
|
17623
|
+
*/
|
|
17624
|
+
equals(otherTime) {
|
|
17625
|
+
return this.minutesSinceMidnight === otherTime.toMinutes();
|
|
17626
|
+
}
|
|
17130
17627
|
}
|
|
17131
17628
|
|
|
17132
17629
|
const REGULAR = 0;
|
|
17133
17630
|
const NOT_AVAILABLE = 1;
|
|
17134
17631
|
const MUST_PHONE_AGENCY = 2;
|
|
17135
17632
|
const MUST_COORDINATE_WITH_DRIVER = 3;
|
|
17136
|
-
const
|
|
17633
|
+
const pickUpDropOffTypeMap = [
|
|
17634
|
+
'REGULAR',
|
|
17635
|
+
'NOT_AVAILABLE',
|
|
17636
|
+
'MUST_PHONE_AGENCY',
|
|
17637
|
+
'MUST_COORDINATE_WITH_DRIVER',
|
|
17638
|
+
];
|
|
17639
|
+
/**
|
|
17640
|
+
* Converts a numerical representation of a pick-up/drop-off type
|
|
17641
|
+
* into its corresponding string representation.
|
|
17642
|
+
*
|
|
17643
|
+
* @param numericalType - The numerical value representing the pick-up/drop-off type.
|
|
17644
|
+
* @returns The corresponding PickUpDropOffType as a string.
|
|
17645
|
+
* @throws An error if the numerical type is invalid.
|
|
17646
|
+
*/
|
|
17647
|
+
const toPickupDropOffType = (numericalType) => {
|
|
17648
|
+
const type = pickUpDropOffTypeMap[numericalType];
|
|
17649
|
+
if (!type) {
|
|
17650
|
+
throw new Error(`Invalid pickup/drop-off type ${numericalType}`);
|
|
17651
|
+
}
|
|
17652
|
+
return type;
|
|
17653
|
+
};
|
|
17654
|
+
/**
|
|
17655
|
+
* A route identifies all trips of a given service route sharing the same list of stops.
|
|
17656
|
+
*/
|
|
17657
|
+
let Route$1 = class Route {
|
|
17658
|
+
constructor(stopTimes, pickUpDropOffTypes, stops, serviceRouteId) {
|
|
17659
|
+
this.stopTimes = stopTimes;
|
|
17660
|
+
this.pickUpDropOffTypes = pickUpDropOffTypes;
|
|
17661
|
+
this.stops = stops;
|
|
17662
|
+
this.serviceRouteId = serviceRouteId;
|
|
17663
|
+
this.nbStops = stops.length;
|
|
17664
|
+
this.nbTrips = this.stopTimes.length / (this.stops.length * 2);
|
|
17665
|
+
this.stopIndices = new Map();
|
|
17666
|
+
for (let i = 0; i < stops.length; i++) {
|
|
17667
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17668
|
+
this.stopIndices.set(stops[i], i);
|
|
17669
|
+
}
|
|
17670
|
+
}
|
|
17671
|
+
/**
|
|
17672
|
+
* Serializes the Route into binary arrays.
|
|
17673
|
+
*
|
|
17674
|
+
* @returns The serialized binary data.
|
|
17675
|
+
*/
|
|
17676
|
+
serialize() {
|
|
17677
|
+
return {
|
|
17678
|
+
stopTimes: this.stopTimes,
|
|
17679
|
+
pickUpDropOffTypes: this.pickUpDropOffTypes,
|
|
17680
|
+
stops: this.stops,
|
|
17681
|
+
serviceRouteId: this.serviceRouteId,
|
|
17682
|
+
};
|
|
17683
|
+
}
|
|
17684
|
+
/**
|
|
17685
|
+
* Checks if stop A is before stop B in the route.
|
|
17686
|
+
*
|
|
17687
|
+
* @param stopA - The StopId of the first stop.
|
|
17688
|
+
* @param stopB - The StopId of the second stop.
|
|
17689
|
+
* @returns True if stop A is before stop B, false otherwise.
|
|
17690
|
+
*/
|
|
17691
|
+
isBefore(stopA, stopB) {
|
|
17692
|
+
const stopAIndex = this.stopIndices.get(stopA);
|
|
17693
|
+
if (stopAIndex === undefined) {
|
|
17694
|
+
throw new Error(`Stop index ${stopAIndex} not found in route ${this.serviceRouteId}`);
|
|
17695
|
+
}
|
|
17696
|
+
const stopBIndex = this.stopIndices.get(stopB);
|
|
17697
|
+
if (stopBIndex === undefined) {
|
|
17698
|
+
throw new Error(`Stop index ${stopBIndex} not found in route ${this.serviceRouteId}`);
|
|
17699
|
+
}
|
|
17700
|
+
return stopAIndex < stopBIndex;
|
|
17701
|
+
}
|
|
17702
|
+
/**
|
|
17703
|
+
* Retrieves the number of stops in the route.
|
|
17704
|
+
*
|
|
17705
|
+
* @returns The total number of stops in the route.
|
|
17706
|
+
*/
|
|
17707
|
+
getNbStops() {
|
|
17708
|
+
return this.nbStops;
|
|
17709
|
+
}
|
|
17710
|
+
/**
|
|
17711
|
+
* Finds the ServiceRouteId of the route. It corresponds the identifier
|
|
17712
|
+
* of the service shown to the end user as a route.
|
|
17713
|
+
*
|
|
17714
|
+
* @returns The ServiceRouteId of the route.
|
|
17715
|
+
*/
|
|
17716
|
+
serviceRoute() {
|
|
17717
|
+
return this.serviceRouteId;
|
|
17718
|
+
}
|
|
17719
|
+
/**
|
|
17720
|
+
* Retrieves the arrival time at a specific stop for a given trip.
|
|
17721
|
+
*
|
|
17722
|
+
* @param stopId - The identifier of the stop.
|
|
17723
|
+
* @param tripIndex - The index of the trip.
|
|
17724
|
+
* @returns The arrival time at the specified stop and trip as a Time object.
|
|
17725
|
+
*/
|
|
17726
|
+
arrivalAt(stopId, tripIndex) {
|
|
17727
|
+
const arrivalIndex = (tripIndex * this.stops.length + this.stopIndex(stopId)) * 2;
|
|
17728
|
+
const arrival = this.stopTimes[arrivalIndex];
|
|
17729
|
+
if (arrival === undefined) {
|
|
17730
|
+
throw new Error(`Arrival time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17731
|
+
}
|
|
17732
|
+
return Time.fromMinutes(arrival);
|
|
17733
|
+
}
|
|
17734
|
+
/**
|
|
17735
|
+
* Retrieves the departure time at a specific stop for a given trip.
|
|
17736
|
+
*
|
|
17737
|
+
* @param stopId - The identifier of the stop.
|
|
17738
|
+
* @param tripIndex - The index of the trip.
|
|
17739
|
+
* @returns The departure time at the specified stop and trip as a Time object.
|
|
17740
|
+
*/
|
|
17741
|
+
departureFrom(stopId, tripIndex) {
|
|
17742
|
+
const departureIndex = (tripIndex * this.stops.length + this.stopIndex(stopId)) * 2 + 1;
|
|
17743
|
+
const departure = this.stopTimes[departureIndex];
|
|
17744
|
+
if (departure === undefined) {
|
|
17745
|
+
throw new Error(`Departure time not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17746
|
+
}
|
|
17747
|
+
return Time.fromMinutes(departure);
|
|
17748
|
+
}
|
|
17749
|
+
/**
|
|
17750
|
+
* Retrieves the pick-up type for a specific stop and trip.
|
|
17751
|
+
*
|
|
17752
|
+
* @param stopId - The identifier of the stop.
|
|
17753
|
+
* @param tripIndex - The index of the trip.
|
|
17754
|
+
* @returns The pick-up type at the specified stop and trip.
|
|
17755
|
+
*/
|
|
17756
|
+
pickUpTypeFrom(stopId, tripIndex) {
|
|
17757
|
+
const pickUpIndex = (tripIndex * this.stops.length + this.stopIndex(stopId)) * 2;
|
|
17758
|
+
const pickUpValue = this.pickUpDropOffTypes[pickUpIndex];
|
|
17759
|
+
if (pickUpValue === undefined) {
|
|
17760
|
+
throw new Error(`Pick up type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17761
|
+
}
|
|
17762
|
+
return toPickupDropOffType(pickUpValue);
|
|
17763
|
+
}
|
|
17764
|
+
/**
|
|
17765
|
+
* Retrieves the drop-off type for a specific stop and trip.
|
|
17766
|
+
*
|
|
17767
|
+
* @param stopId - The identifier of the stop.
|
|
17768
|
+
* @param tripIndex - The index of the trip.
|
|
17769
|
+
* @returns The drop-off type at the specified stop and trip.
|
|
17770
|
+
*/
|
|
17771
|
+
dropOffTypeAt(stopId, tripIndex) {
|
|
17772
|
+
const dropOffIndex = (tripIndex * this.stops.length + this.stopIndex(stopId)) * 2 + 1;
|
|
17773
|
+
const dropOffValue = this.pickUpDropOffTypes[dropOffIndex];
|
|
17774
|
+
if (dropOffValue === undefined) {
|
|
17775
|
+
throw new Error(`Drop off type not found for stop ${stopId} at trip index ${tripIndex} in route ${this.serviceRouteId}`);
|
|
17776
|
+
}
|
|
17777
|
+
return toPickupDropOffType(dropOffValue);
|
|
17778
|
+
}
|
|
17779
|
+
/**
|
|
17780
|
+
* Iterates over the stops in the route, starting from an optional specified stop.
|
|
17781
|
+
* If no start stop is provided, the iteration begins from the first stop in the route.
|
|
17782
|
+
*
|
|
17783
|
+
* @param [startStopId] - (Optional) The StopId of the stop to start the iteration from.
|
|
17784
|
+
* @returns An IterableIterator of StopIds, starting from the specified stop or the first stop.
|
|
17785
|
+
* @throws An error if the specified start stop is not found in the route.
|
|
17786
|
+
*/
|
|
17787
|
+
stopsIterator(startStopId) {
|
|
17788
|
+
const startIndex = startStopId !== undefined ? this.stopIndices.get(startStopId) : 0;
|
|
17789
|
+
if (startIndex === undefined) {
|
|
17790
|
+
throw new Error(`Start stop ${startStopId} not found in route ${this.serviceRouteId}`);
|
|
17791
|
+
}
|
|
17792
|
+
function* generator(stops, startIndex) {
|
|
17793
|
+
for (let i = startIndex; i < stops.length; i++) {
|
|
17794
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17795
|
+
yield stops[i];
|
|
17796
|
+
}
|
|
17797
|
+
}
|
|
17798
|
+
return generator(this.stops, startIndex);
|
|
17799
|
+
}
|
|
17800
|
+
/**
|
|
17801
|
+
* Finds the earliest trip that can be taken from a specific stop on a given route,
|
|
17802
|
+
* optionally constrained by a latest trip index and a time before which the trip
|
|
17803
|
+
* should not depart.
|
|
17804
|
+
* *
|
|
17805
|
+
* @param stopId - The StopId of the stop where the trip should be found.
|
|
17806
|
+
* @param [after=Time.origin()] - The earliest time after which the trip should depart.
|
|
17807
|
+
* If not provided, searches all available trips.
|
|
17808
|
+
* @param [beforeTrip] - (Optional) The index of the trip before which the search should be constrained.
|
|
17809
|
+
* If not provided, searches all available trips.
|
|
17810
|
+
* @returns The index of the earliest trip meeting the criteria, or undefined if no such trip is found.
|
|
17811
|
+
*/
|
|
17812
|
+
findEarliestTrip(stopId, after = Time.origin(), beforeTrip) {
|
|
17813
|
+
const maxTripIndex = beforeTrip !== undefined
|
|
17814
|
+
? Math.min(beforeTrip - 1, this.nbTrips - 1)
|
|
17815
|
+
: this.nbTrips - 1;
|
|
17816
|
+
if (maxTripIndex < 0) {
|
|
17817
|
+
return undefined;
|
|
17818
|
+
}
|
|
17819
|
+
let earliestTripIndex;
|
|
17820
|
+
let lowTrip = 0;
|
|
17821
|
+
let highTrip = maxTripIndex;
|
|
17822
|
+
while (lowTrip <= highTrip) {
|
|
17823
|
+
const midTrip = Math.floor((lowTrip + highTrip) / 2);
|
|
17824
|
+
const departure = this.departureFrom(stopId, midTrip);
|
|
17825
|
+
const pickUpType = this.pickUpTypeFrom(stopId, midTrip);
|
|
17826
|
+
if ((departure.isAfter(after) || departure.equals(after)) &&
|
|
17827
|
+
pickUpType !== 'NOT_AVAILABLE') {
|
|
17828
|
+
earliestTripIndex = midTrip;
|
|
17829
|
+
highTrip = midTrip - 1;
|
|
17830
|
+
}
|
|
17831
|
+
else {
|
|
17832
|
+
lowTrip = midTrip + 1;
|
|
17833
|
+
}
|
|
17834
|
+
}
|
|
17835
|
+
return earliestTripIndex;
|
|
17836
|
+
}
|
|
17837
|
+
/**
|
|
17838
|
+
* Retrieves the index of a stop within the route.
|
|
17839
|
+
* @param stopId The StopId of the stop to locate in the route.
|
|
17840
|
+
* @returns The index of the stop in the route.
|
|
17841
|
+
*/
|
|
17842
|
+
stopIndex(stopId) {
|
|
17843
|
+
const stopIndex = this.stopIndices.get(stopId);
|
|
17844
|
+
if (stopIndex === undefined) {
|
|
17845
|
+
throw new Error(`Stop index for ${stopId} not found in route ${this.serviceRouteId}`);
|
|
17846
|
+
}
|
|
17847
|
+
return stopIndex;
|
|
17848
|
+
}
|
|
17849
|
+
};
|
|
17850
|
+
|
|
17851
|
+
const isLittleEndian = (() => {
|
|
17852
|
+
const buffer = new ArrayBuffer(4);
|
|
17853
|
+
const view = new DataView(buffer);
|
|
17854
|
+
view.setUint32(0, 0x12345678);
|
|
17855
|
+
return new Uint8Array(buffer)[0] === 0x78;
|
|
17856
|
+
})();
|
|
17857
|
+
const STANDARD_ENDIANNESS = true; // true = little-endian
|
|
17858
|
+
const uint32ArrayToBytes = (array) => {
|
|
17859
|
+
if (isLittleEndian === STANDARD_ENDIANNESS) {
|
|
17860
|
+
return new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
|
|
17861
|
+
}
|
|
17862
|
+
// If endianness doesn't match, we need to swap byte order
|
|
17863
|
+
const result = new Uint8Array(array.length * 4);
|
|
17864
|
+
const view = new DataView(result.buffer);
|
|
17865
|
+
for (let i = 0; i < array.length; i++) {
|
|
17866
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17867
|
+
view.setUint32(i * 4, array[i], STANDARD_ENDIANNESS);
|
|
17868
|
+
}
|
|
17869
|
+
return result;
|
|
17870
|
+
};
|
|
17871
|
+
const bytesToUint32Array = (bytes) => {
|
|
17872
|
+
if (bytes.byteLength % 4 !== 0) {
|
|
17873
|
+
throw new Error('Byte array length must be a multiple of 4 to convert to Uint32Array');
|
|
17874
|
+
}
|
|
17875
|
+
// If system endianness matches our standard, we can create a view directly
|
|
17876
|
+
if (isLittleEndian === STANDARD_ENDIANNESS) {
|
|
17877
|
+
return new Uint32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
|
|
17878
|
+
}
|
|
17879
|
+
// If endianness doesn't match, we need to swap byte order
|
|
17880
|
+
const result = new Uint32Array(bytes.byteLength / 4);
|
|
17881
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
17882
|
+
for (let i = 0; i < result.length; i++) {
|
|
17883
|
+
result[i] = view.getUint32(i * 4, STANDARD_ENDIANNESS);
|
|
17884
|
+
}
|
|
17885
|
+
return result;
|
|
17886
|
+
};
|
|
17887
|
+
const uint16ArrayToBytes = (array) => {
|
|
17888
|
+
if (isLittleEndian === STANDARD_ENDIANNESS) {
|
|
17889
|
+
return new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
|
|
17890
|
+
}
|
|
17891
|
+
// If endianness doesn't match, we need to swap byte order
|
|
17892
|
+
const result = new Uint8Array(array.length * 2);
|
|
17893
|
+
const view = new DataView(result.buffer);
|
|
17894
|
+
for (let i = 0; i < array.length; i++) {
|
|
17895
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17896
|
+
view.setUint16(i * 2, array[i], STANDARD_ENDIANNESS);
|
|
17897
|
+
}
|
|
17898
|
+
return result;
|
|
17899
|
+
};
|
|
17900
|
+
const bytesToUint16Array = (bytes) => {
|
|
17901
|
+
if (bytes.byteLength % 2 !== 0) {
|
|
17902
|
+
throw new Error('Byte array length must be a multiple of 2 to convert to Uint16Array');
|
|
17903
|
+
}
|
|
17904
|
+
// If system endianness matches our standard, we can create a view directly
|
|
17905
|
+
if (isLittleEndian === STANDARD_ENDIANNESS) {
|
|
17906
|
+
return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2);
|
|
17907
|
+
}
|
|
17908
|
+
// If endianness doesn't match, we need to swap byte order
|
|
17909
|
+
const result = new Uint16Array(bytes.byteLength / 2);
|
|
17910
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
17911
|
+
for (let i = 0; i < result.length; i++) {
|
|
17912
|
+
result[i] = view.getUint16(i * 2, STANDARD_ENDIANNESS);
|
|
17913
|
+
}
|
|
17914
|
+
return result;
|
|
17915
|
+
};
|
|
17916
|
+
const serializeStopsAdjacency = (stopsAdjacency) => {
|
|
17917
|
+
const protoStopsAdjacency = {
|
|
17918
|
+
stops: {},
|
|
17919
|
+
};
|
|
17920
|
+
stopsAdjacency.forEach((value, key) => {
|
|
17921
|
+
protoStopsAdjacency.stops[key] = {
|
|
17922
|
+
transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: serializeTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
|
|
17923
|
+
minTransferTime: transfer.minTransferTime.toSeconds(),
|
|
17924
|
+
})))),
|
|
17925
|
+
routes: value.routes,
|
|
17926
|
+
};
|
|
17927
|
+
});
|
|
17928
|
+
return protoStopsAdjacency;
|
|
17929
|
+
};
|
|
17930
|
+
const serializeRoutesAdjacency = (routesAdjacency) => {
|
|
17931
|
+
const protoRoutesAdjacency = {
|
|
17932
|
+
routes: {},
|
|
17933
|
+
};
|
|
17934
|
+
routesAdjacency.forEach((route, key) => {
|
|
17935
|
+
const routeData = route.serialize();
|
|
17936
|
+
protoRoutesAdjacency.routes[key] = {
|
|
17937
|
+
stopTimes: uint16ArrayToBytes(routeData.stopTimes),
|
|
17938
|
+
pickUpDropOffTypes: routeData.pickUpDropOffTypes,
|
|
17939
|
+
stops: uint32ArrayToBytes(routeData.stops),
|
|
17940
|
+
serviceRouteId: routeData.serviceRouteId,
|
|
17941
|
+
};
|
|
17942
|
+
});
|
|
17943
|
+
return protoRoutesAdjacency;
|
|
17944
|
+
};
|
|
17945
|
+
const serializeServiceRoutesMap = (serviceRoutesMap) => {
|
|
17946
|
+
const protoServiceRoutesMap = {
|
|
17947
|
+
routes: {},
|
|
17948
|
+
};
|
|
17949
|
+
serviceRoutesMap.forEach((value, key) => {
|
|
17950
|
+
protoServiceRoutesMap.routes[key] = {
|
|
17951
|
+
type: serializeRouteType(value.type),
|
|
17952
|
+
name: value.name,
|
|
17953
|
+
};
|
|
17954
|
+
});
|
|
17955
|
+
return protoServiceRoutesMap;
|
|
17956
|
+
};
|
|
17957
|
+
const deserializeStopsAdjacency = (protoStopsAdjacency) => {
|
|
17958
|
+
const stopsAdjacency = new Map();
|
|
17959
|
+
Object.entries(protoStopsAdjacency.stops).forEach(([keyStr, value]) => {
|
|
17960
|
+
const key = parseInt(keyStr, 10);
|
|
17961
|
+
stopsAdjacency.set(key, {
|
|
17962
|
+
transfers: value.transfers.map((transfer) => (Object.assign({ destination: transfer.destination, type: parseTransferType(transfer.type) }, (transfer.minTransferTime !== undefined && {
|
|
17963
|
+
minTransferTime: Duration.fromSeconds(transfer.minTransferTime),
|
|
17964
|
+
})))),
|
|
17965
|
+
routes: value.routes,
|
|
17966
|
+
});
|
|
17967
|
+
});
|
|
17968
|
+
return stopsAdjacency;
|
|
17969
|
+
};
|
|
17970
|
+
const deserializeRoutesAdjacency = (protoRoutesAdjacency) => {
|
|
17971
|
+
const routesAdjacency = new Map();
|
|
17972
|
+
Object.entries(protoRoutesAdjacency.routes).forEach(([key, value]) => {
|
|
17973
|
+
const stops = bytesToUint32Array(value.stops);
|
|
17974
|
+
routesAdjacency.set(key, new Route$1(bytesToUint16Array(value.stopTimes), value.pickUpDropOffTypes, stops, value.serviceRouteId));
|
|
17975
|
+
});
|
|
17976
|
+
return routesAdjacency;
|
|
17977
|
+
};
|
|
17978
|
+
const deserializeServiceRoutesMap = (protoServiceRoutesMap) => {
|
|
17979
|
+
const serviceRoutesMap = new Map();
|
|
17980
|
+
Object.entries(protoServiceRoutesMap.routes).forEach(([key, value]) => {
|
|
17981
|
+
serviceRoutesMap.set(key, {
|
|
17982
|
+
type: parseRouteType(value.type),
|
|
17983
|
+
name: value.name,
|
|
17984
|
+
});
|
|
17985
|
+
});
|
|
17986
|
+
return serviceRoutesMap;
|
|
17987
|
+
};
|
|
17988
|
+
const parseTransferType = (type) => {
|
|
17989
|
+
switch (type) {
|
|
17990
|
+
case TransferType.RECOMMENDED_TRANSFER_POINT:
|
|
17991
|
+
return 'RECOMMENDED';
|
|
17992
|
+
case TransferType.TIMED_TRANSFER:
|
|
17993
|
+
return 'GUARANTEED';
|
|
17994
|
+
case TransferType.REQUIRES_MINIMAL_TIME:
|
|
17995
|
+
return 'REQUIRES_MINIMAL_TIME';
|
|
17996
|
+
case TransferType.IN_SEAT_TRANSFER:
|
|
17997
|
+
return 'IN_SEAT';
|
|
17998
|
+
case TransferType.UNRECOGNIZED:
|
|
17999
|
+
throw new Error('Unrecognized protobuf transfer type.');
|
|
18000
|
+
}
|
|
18001
|
+
};
|
|
18002
|
+
const serializeTransferType = (type) => {
|
|
18003
|
+
switch (type) {
|
|
18004
|
+
case 'RECOMMENDED':
|
|
18005
|
+
return TransferType.RECOMMENDED_TRANSFER_POINT;
|
|
18006
|
+
case 'GUARANTEED':
|
|
18007
|
+
return TransferType.TIMED_TRANSFER;
|
|
18008
|
+
case 'REQUIRES_MINIMAL_TIME':
|
|
18009
|
+
return TransferType.REQUIRES_MINIMAL_TIME;
|
|
18010
|
+
case 'IN_SEAT':
|
|
18011
|
+
return TransferType.IN_SEAT_TRANSFER;
|
|
18012
|
+
}
|
|
18013
|
+
};
|
|
18014
|
+
const parseRouteType = (type) => {
|
|
18015
|
+
switch (type) {
|
|
18016
|
+
case RouteType.TRAM:
|
|
18017
|
+
return 'TRAM';
|
|
18018
|
+
case RouteType.SUBWAY:
|
|
18019
|
+
return 'SUBWAY';
|
|
18020
|
+
case RouteType.RAIL:
|
|
18021
|
+
return 'RAIL';
|
|
18022
|
+
case RouteType.BUS:
|
|
18023
|
+
return 'BUS';
|
|
18024
|
+
case RouteType.FERRY:
|
|
18025
|
+
return 'FERRY';
|
|
18026
|
+
case RouteType.CABLE_TRAM:
|
|
18027
|
+
return 'CABLE_TRAM';
|
|
18028
|
+
case RouteType.AERIAL_LIFT:
|
|
18029
|
+
return 'AERIAL_LIFT';
|
|
18030
|
+
case RouteType.FUNICULAR:
|
|
18031
|
+
return 'FUNICULAR';
|
|
18032
|
+
case RouteType.TROLLEYBUS:
|
|
18033
|
+
return 'TROLLEYBUS';
|
|
18034
|
+
case RouteType.MONORAIL:
|
|
18035
|
+
return 'MONORAIL';
|
|
18036
|
+
case RouteType.UNRECOGNIZED:
|
|
18037
|
+
default:
|
|
18038
|
+
throw new Error('Unrecognized protobuf route type.');
|
|
18039
|
+
}
|
|
18040
|
+
};
|
|
18041
|
+
const serializeRouteType = (type) => {
|
|
18042
|
+
switch (type) {
|
|
18043
|
+
case 'TRAM':
|
|
18044
|
+
return RouteType.TRAM;
|
|
18045
|
+
case 'SUBWAY':
|
|
18046
|
+
return RouteType.SUBWAY;
|
|
18047
|
+
case 'RAIL':
|
|
18048
|
+
return RouteType.RAIL;
|
|
18049
|
+
case 'BUS':
|
|
18050
|
+
return RouteType.BUS;
|
|
18051
|
+
case 'FERRY':
|
|
18052
|
+
return RouteType.FERRY;
|
|
18053
|
+
case 'CABLE_TRAM':
|
|
18054
|
+
return RouteType.CABLE_TRAM;
|
|
18055
|
+
case 'AERIAL_LIFT':
|
|
18056
|
+
return RouteType.AERIAL_LIFT;
|
|
18057
|
+
case 'FUNICULAR':
|
|
18058
|
+
return RouteType.FUNICULAR;
|
|
18059
|
+
case 'TROLLEYBUS':
|
|
18060
|
+
return RouteType.TROLLEYBUS;
|
|
18061
|
+
case 'MONORAIL':
|
|
18062
|
+
return RouteType.MONORAIL;
|
|
18063
|
+
}
|
|
18064
|
+
};
|
|
18065
|
+
|
|
18066
|
+
const ALL_TRANSPORT_MODES = new Set([
|
|
17137
18067
|
'TRAM',
|
|
17138
18068
|
'SUBWAY',
|
|
17139
18069
|
'RAIL',
|
|
@@ -17144,11 +18074,10 @@ const ALL_TRANSPORT_MODES = [
|
|
|
17144
18074
|
'FUNICULAR',
|
|
17145
18075
|
'TROLLEYBUS',
|
|
17146
18076
|
'MONORAIL',
|
|
17147
|
-
];
|
|
18077
|
+
]);
|
|
17148
18078
|
const CURRENT_VERSION = '0.0.3';
|
|
17149
18079
|
/**
|
|
17150
|
-
* The internal transit timetable format
|
|
17151
|
-
* reuses some GTFS concepts for the sake of simplicity for now.
|
|
18080
|
+
* The internal transit timetable format.
|
|
17152
18081
|
*/
|
|
17153
18082
|
class Timetable {
|
|
17154
18083
|
constructor(stopsAdjacency, routesAdjacency, routes) {
|
|
@@ -17157,9 +18086,9 @@ class Timetable {
|
|
|
17157
18086
|
this.routes = routes;
|
|
17158
18087
|
}
|
|
17159
18088
|
/**
|
|
17160
|
-
* Serializes the Timetable into a binary
|
|
18089
|
+
* Serializes the Timetable into a binary array.
|
|
17161
18090
|
*
|
|
17162
|
-
* @returns
|
|
18091
|
+
* @returns The serialized binary data.
|
|
17163
18092
|
*/
|
|
17164
18093
|
serialize() {
|
|
17165
18094
|
const protoTimetable = {
|
|
@@ -17175,8 +18104,8 @@ class Timetable {
|
|
|
17175
18104
|
/**
|
|
17176
18105
|
* Deserializes a binary protobuf into a Timetable object.
|
|
17177
18106
|
*
|
|
17178
|
-
* @param
|
|
17179
|
-
* @returns
|
|
18107
|
+
* @param data - The binary data to deserialize.
|
|
18108
|
+
* @returns The deserialized Timetable object.
|
|
17180
18109
|
*/
|
|
17181
18110
|
static fromData(data) {
|
|
17182
18111
|
const reader = new BinaryReader(data);
|
|
@@ -17192,108 +18121,89 @@ class Timetable {
|
|
|
17192
18121
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
17193
18122
|
deserializeServiceRoutesMap(protoTimetable.routes));
|
|
17194
18123
|
}
|
|
17195
|
-
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
|
|
17199
|
-
|
|
17200
|
-
|
|
17201
|
-
|
|
18124
|
+
/**
|
|
18125
|
+
* Retrieves the route associated with the given route ID.
|
|
18126
|
+
*
|
|
18127
|
+
* @param routeId - The ID of the route to be retrieved.
|
|
18128
|
+
* @returns The route corresponding to the provided ID,
|
|
18129
|
+
* or undefined if no such route exists.
|
|
18130
|
+
*/
|
|
17202
18131
|
getRoute(routeId) {
|
|
17203
18132
|
return this.routesAdjacency.get(routeId);
|
|
17204
18133
|
}
|
|
18134
|
+
/**
|
|
18135
|
+
* Retrieves all transfer options available at the specified stop.
|
|
18136
|
+
*
|
|
18137
|
+
* @param stopId - The ID of the stop to get transfers for.
|
|
18138
|
+
* @returns An array of transfer options available at the stop.
|
|
18139
|
+
*/
|
|
17205
18140
|
getTransfers(stopId) {
|
|
17206
18141
|
var _a, _b;
|
|
17207
|
-
return (_b = (_a = this.stopsAdjacency.get(stopId)) === null || _a ===
|
|
18142
|
+
return (_b = (_a = this.stopsAdjacency.get(stopId)) === null || _a === void 0 ? void 0 : _a.transfers) !== null && _b !== void 0 ? _b : [];
|
|
18143
|
+
}
|
|
18144
|
+
/**
|
|
18145
|
+
* Retrieves the service route associated with the given route.
|
|
18146
|
+
* A service route refers to a collection of trips that are displayed
|
|
18147
|
+
* to riders as a single service.
|
|
18148
|
+
*
|
|
18149
|
+
* @param route - The route for which the service route is to be retrieved.
|
|
18150
|
+
* @returns The service route corresponding to the provided route.
|
|
18151
|
+
*/
|
|
18152
|
+
getServiceRoute(route) {
|
|
18153
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
18154
|
+
return this.routes.get(route.serviceRoute());
|
|
18155
|
+
}
|
|
18156
|
+
/**
|
|
18157
|
+
* Finds all routes passing through a stop.
|
|
18158
|
+
*
|
|
18159
|
+
* @param stopId - The ID of the stop to find routes for.
|
|
18160
|
+
* @returns An array of routes passing through the specified stop.
|
|
18161
|
+
*/
|
|
18162
|
+
routesPassingThrough(stopId) {
|
|
18163
|
+
const stopData = this.stopsAdjacency.get(stopId);
|
|
18164
|
+
if (!stopData) {
|
|
18165
|
+
return [];
|
|
18166
|
+
}
|
|
18167
|
+
const routes = [];
|
|
18168
|
+
for (const routeId of stopData.routes) {
|
|
18169
|
+
const route = this.routesAdjacency.get(routeId);
|
|
18170
|
+
if (route) {
|
|
18171
|
+
routes.push(route);
|
|
18172
|
+
}
|
|
18173
|
+
}
|
|
18174
|
+
return routes;
|
|
17208
18175
|
}
|
|
17209
18176
|
/**
|
|
17210
18177
|
* Finds routes that are reachable from a set of stop IDs.
|
|
17211
18178
|
* Also identifies the first stop available to hop on each route among
|
|
17212
18179
|
* the input stops.
|
|
18180
|
+
*
|
|
18181
|
+
* @param fromStops - The set of stop IDs to find reachable routes from.
|
|
18182
|
+
* @param transportModes - The set of transport modes to consider for reachable routes.
|
|
18183
|
+
* @returns A map of reachable routes to the first stop available to hop on each route.
|
|
17213
18184
|
*/
|
|
17214
|
-
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
17215
18185
|
findReachableRoutes(fromStops, transportModes = ALL_TRANSPORT_MODES) {
|
|
17216
|
-
var _a;
|
|
17217
18186
|
const reachableRoutes = new Map();
|
|
17218
|
-
for (const
|
|
17219
|
-
const validRoutes = (
|
|
17220
|
-
|
|
17221
|
-
|
|
17222
|
-
if (!serviceRoute) {
|
|
17223
|
-
return false;
|
|
17224
|
-
}
|
|
17225
|
-
return transportModes.includes(serviceRoute.type);
|
|
18187
|
+
for (const originStop of fromStops) {
|
|
18188
|
+
const validRoutes = this.routesPassingThrough(originStop).filter((route) => {
|
|
18189
|
+
const serviceRoute = this.getServiceRoute(route);
|
|
18190
|
+
return transportModes.has(serviceRoute.type);
|
|
17226
18191
|
});
|
|
17227
|
-
for (const
|
|
17228
|
-
const hopOnStop = reachableRoutes.get(
|
|
18192
|
+
for (const route of validRoutes) {
|
|
18193
|
+
const hopOnStop = reachableRoutes.get(route);
|
|
17229
18194
|
if (hopOnStop) {
|
|
17230
|
-
|
|
17231
|
-
const routeStopIndices = this.routesAdjacency.get(routeId).stopIndices;
|
|
17232
|
-
const stopIndex = routeStopIndices.get(stop);
|
|
17233
|
-
const hopOnStopIndex = routeStopIndices.get(hopOnStop);
|
|
17234
|
-
if (stopIndex < hopOnStopIndex) {
|
|
18195
|
+
if (route.isBefore(originStop, hopOnStop)) {
|
|
17235
18196
|
// if the current stop is before the existing hop on stop, replace it
|
|
17236
|
-
reachableRoutes.set(
|
|
18197
|
+
reachableRoutes.set(route, originStop);
|
|
17237
18198
|
}
|
|
17238
18199
|
}
|
|
17239
18200
|
else {
|
|
17240
|
-
reachableRoutes.set(
|
|
18201
|
+
reachableRoutes.set(route, originStop);
|
|
17241
18202
|
}
|
|
17242
18203
|
}
|
|
17243
18204
|
}
|
|
17244
18205
|
return reachableRoutes;
|
|
17245
18206
|
}
|
|
17246
|
-
getServiceRouteFromRouteId(routeId) {
|
|
17247
|
-
const route = this.routesAdjacency.get(routeId);
|
|
17248
|
-
if (!route) {
|
|
17249
|
-
console.warn(`Route ${routeId} not found.`);
|
|
17250
|
-
return undefined;
|
|
17251
|
-
}
|
|
17252
|
-
return this.routes.get(route.serviceRouteId);
|
|
17253
|
-
}
|
|
17254
|
-
getServiceRoute(serviceRouteId) {
|
|
17255
|
-
return this.routes.get(serviceRouteId);
|
|
17256
|
-
}
|
|
17257
|
-
/**
|
|
17258
|
-
* Finds the earliest trip that can be taken from a specific stop on a given route,
|
|
17259
|
-
* optionally constrained by a latest trip index and a time before which the trip
|
|
17260
|
-
* should not depart.
|
|
17261
|
-
*/
|
|
17262
|
-
findEarliestTrip(route, stopId, beforeTrip, after = Time.origin()) {
|
|
17263
|
-
const stopIndex = route.stopIndices.get(stopId);
|
|
17264
|
-
const stopsNumber = route.stops.length;
|
|
17265
|
-
if (beforeTrip === undefined) {
|
|
17266
|
-
for (let tripIndex = 0; tripIndex < route.stopTimes.length / stopsNumber; tripIndex++) {
|
|
17267
|
-
const stopTimeIndex = tripIndex * stopsNumber + stopIndex;
|
|
17268
|
-
const departure = route.stopTimes[stopTimeIndex * 2 + 1];
|
|
17269
|
-
const pickUpType = route.pickUpDropOffTypes[stopTimeIndex * 2];
|
|
17270
|
-
if (departure >= after.toMinutes() && pickUpType !== NOT_AVAILABLE) {
|
|
17271
|
-
return tripIndex;
|
|
17272
|
-
}
|
|
17273
|
-
}
|
|
17274
|
-
return undefined;
|
|
17275
|
-
}
|
|
17276
|
-
else {
|
|
17277
|
-
let earliestTripIndex;
|
|
17278
|
-
let earliestDeparture;
|
|
17279
|
-
for (let tripIndex = beforeTrip; // ?? route.stopTimes.length / stopsNumber - 1;
|
|
17280
|
-
tripIndex >= 0; tripIndex--) {
|
|
17281
|
-
const stopTimeIndex = tripIndex * stopsNumber + stopIndex;
|
|
17282
|
-
const departure = route.stopTimes[stopTimeIndex * 2 + 1];
|
|
17283
|
-
const pickUpType = route.pickUpDropOffTypes[stopTimeIndex * 2];
|
|
17284
|
-
if (departure < after.toMinutes()) {
|
|
17285
|
-
break;
|
|
17286
|
-
}
|
|
17287
|
-
if (pickUpType !== NOT_AVAILABLE &&
|
|
17288
|
-
(earliestDeparture === undefined ||
|
|
17289
|
-
departure < earliestDeparture.toMinutes())) {
|
|
17290
|
-
earliestTripIndex = tripIndex;
|
|
17291
|
-
earliestDeparture = Time.fromMinutes(departure);
|
|
17292
|
-
}
|
|
17293
|
-
}
|
|
17294
|
-
return earliestTripIndex;
|
|
17295
|
-
}
|
|
17296
|
-
}
|
|
17297
18207
|
}
|
|
17298
18208
|
|
|
17299
18209
|
const standardProfile = {
|
|
@@ -17610,7 +18520,7 @@ const normalize_options = function (opts) {
|
|
|
17610
18520
|
);
|
|
17611
18521
|
}
|
|
17612
18522
|
// Normalize option `columns`
|
|
17613
|
-
options.cast_first_line_to_header =
|
|
18523
|
+
options.cast_first_line_to_header = undefined;
|
|
17614
18524
|
if (options.columns === true) {
|
|
17615
18525
|
// Fields in the first line are converted as-is to columns
|
|
17616
18526
|
options.cast_first_line_to_header = undefined;
|
|
@@ -18142,7 +19052,7 @@ const normalize_options = function (opts) {
|
|
|
18142
19052
|
// Normalize option `to`
|
|
18143
19053
|
if (options.to === undefined || options.to === null) {
|
|
18144
19054
|
options.to = -1;
|
|
18145
|
-
} else {
|
|
19055
|
+
} else if (options.to !== -1) {
|
|
18146
19056
|
if (typeof options.to === "string" && /\d+/.test(options.to)) {
|
|
18147
19057
|
options.to = parseInt(options.to);
|
|
18148
19058
|
}
|
|
@@ -18161,7 +19071,7 @@ const normalize_options = function (opts) {
|
|
|
18161
19071
|
// Normalize option `to_line`
|
|
18162
19072
|
if (options.to_line === undefined || options.to_line === null) {
|
|
18163
19073
|
options.to_line = -1;
|
|
18164
|
-
} else {
|
|
19074
|
+
} else if (options.to_line !== -1) {
|
|
18165
19075
|
if (typeof options.to_line === "string" && /\d+/.test(options.to_line)) {
|
|
18166
19076
|
options.to_line = parseInt(options.to_line);
|
|
18167
19077
|
}
|
|
@@ -18292,10 +19202,14 @@ const transform = function (original_options = {}) {
|
|
|
18292
19202
|
this.state.bufBytesStart += bomLength;
|
|
18293
19203
|
buf = buf.slice(bomLength);
|
|
18294
19204
|
// Renormalize original options with the new encoding
|
|
18295
|
-
|
|
19205
|
+
const options = normalize_options({
|
|
18296
19206
|
...this.original_options,
|
|
18297
19207
|
encoding: encoding,
|
|
18298
19208
|
});
|
|
19209
|
+
// Properties are merged with the existing options instance
|
|
19210
|
+
for (const key in options) {
|
|
19211
|
+
this.options[key] = options[key];
|
|
19212
|
+
}
|
|
18299
19213
|
// Options will re-evaluate the Buffer with the new encoding
|
|
18300
19214
|
({ comment, escape, quote } = this.options);
|
|
18301
19215
|
break;
|
|
@@ -19038,10 +19952,14 @@ const transform = function (original_options = {}) {
|
|
|
19038
19952
|
if (skip_records_with_error) {
|
|
19039
19953
|
this.state.recordHasError = true;
|
|
19040
19954
|
if (this.options.on_skip !== undefined) {
|
|
19041
|
-
|
|
19042
|
-
|
|
19043
|
-
|
|
19044
|
-
|
|
19955
|
+
try {
|
|
19956
|
+
this.options.on_skip(
|
|
19957
|
+
err,
|
|
19958
|
+
raw ? this.state.rawBuffer.toString(encoding) : undefined,
|
|
19959
|
+
);
|
|
19960
|
+
} catch (err) {
|
|
19961
|
+
return err;
|
|
19962
|
+
}
|
|
19045
19963
|
}
|
|
19046
19964
|
// this.emit('skip', err, raw ? this.state.rawBuffer.toString(encoding) : undefined);
|
|
19047
19965
|
return undefined;
|
|
@@ -19238,11 +20156,17 @@ const hashIds = (ids) => {
|
|
|
19238
20156
|
* @param stream The CSV stream.
|
|
19239
20157
|
* @returns A parser from the csv-parse library.
|
|
19240
20158
|
*/
|
|
19241
|
-
const parseCsv = (stream) => {
|
|
20159
|
+
const parseCsv = (stream, numericColumns = []) => {
|
|
19242
20160
|
return stream.pipe(parse({
|
|
19243
20161
|
delimiter: ',',
|
|
19244
20162
|
columns: true,
|
|
19245
|
-
cast:
|
|
20163
|
+
cast: (value, context) => {
|
|
20164
|
+
if (typeof context.column === 'string' &&
|
|
20165
|
+
numericColumns.includes(context.column)) {
|
|
20166
|
+
return Number(value);
|
|
20167
|
+
}
|
|
20168
|
+
return value;
|
|
20169
|
+
},
|
|
19246
20170
|
bom: true,
|
|
19247
20171
|
ignore_last_delimiters: true,
|
|
19248
20172
|
relax_column_count: true,
|
|
@@ -19256,11 +20180,11 @@ const parseCsv = (stream) => {
|
|
|
19256
20180
|
* @param profile A configuration object defining the specificities of the GTFS feed.
|
|
19257
20181
|
* @returns A map of all the valid routes.
|
|
19258
20182
|
*/
|
|
19259
|
-
const parseRoutes = (routesStream_1, ...args_1) => __awaiter(
|
|
20183
|
+
const parseRoutes = (routesStream_1, ...args_1) => __awaiter(void 0, [routesStream_1, ...args_1], void 0, function* (routesStream, profile = standardProfile) {
|
|
19260
20184
|
var _a, e_1, _b, _c;
|
|
19261
20185
|
const routes = new Map();
|
|
19262
20186
|
try {
|
|
19263
|
-
for (var _d = true, _e = __asyncValues(parseCsv(routesStream)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
20187
|
+
for (var _d = true, _e = __asyncValues(parseCsv(routesStream, ['route_type'])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
19264
20188
|
_c = _f.value;
|
|
19265
20189
|
_d = false;
|
|
19266
20190
|
const rawLine = _c;
|
|
@@ -19271,7 +20195,7 @@ const parseRoutes = (routesStream_1, ...args_1) => __awaiter(undefined, [routesS
|
|
|
19271
20195
|
continue;
|
|
19272
20196
|
}
|
|
19273
20197
|
routes.set(line.route_id, {
|
|
19274
|
-
name: line.route_short_name
|
|
20198
|
+
name: line.route_short_name,
|
|
19275
20199
|
type: routeType,
|
|
19276
20200
|
});
|
|
19277
20201
|
}
|
|
@@ -19324,13 +20248,23 @@ const weekdays = {
|
|
|
19324
20248
|
* @param date The active date.
|
|
19325
20249
|
* @param calendarStream A readable stream for the GTFS calendar.txt file.
|
|
19326
20250
|
*/
|
|
19327
|
-
const parseCalendar = (calendarStream, serviceIds, date) => __awaiter(
|
|
20251
|
+
const parseCalendar = (calendarStream, serviceIds, date) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19328
20252
|
var _a, e_1, _b, _c;
|
|
19329
20253
|
const activeDate = toGtfsDate(date);
|
|
19330
20254
|
const weekday = date.weekday;
|
|
19331
20255
|
const weekdayIndex = weekdays[weekday];
|
|
19332
20256
|
try {
|
|
19333
|
-
for (var _d = true, _e = __asyncValues(parseCsv(calendarStream
|
|
20257
|
+
for (var _d = true, _e = __asyncValues(parseCsv(calendarStream, [
|
|
20258
|
+
'monday',
|
|
20259
|
+
'tuesday',
|
|
20260
|
+
'wednesday',
|
|
20261
|
+
'thursday',
|
|
20262
|
+
'friday',
|
|
20263
|
+
'saturday',
|
|
20264
|
+
'sunday',
|
|
20265
|
+
'start_date',
|
|
20266
|
+
'end_date',
|
|
20267
|
+
])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
19334
20268
|
_c = _f.value;
|
|
19335
20269
|
_d = false;
|
|
19336
20270
|
const rawLine = _c;
|
|
@@ -19361,11 +20295,14 @@ const parseCalendar = (calendarStream, serviceIds, date) => __awaiter(undefined,
|
|
|
19361
20295
|
* @param date The active date, in the format "YYYYMMDD".
|
|
19362
20296
|
* @param calendarDatesStream A readable stream for the GTFS calendar_dates.txt file.
|
|
19363
20297
|
*/
|
|
19364
|
-
const parseCalendarDates = (calendarDatesStream, serviceIds, date) => __awaiter(
|
|
20298
|
+
const parseCalendarDates = (calendarDatesStream, serviceIds, date) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19365
20299
|
var _a, e_2, _b, _c;
|
|
19366
20300
|
const activeDate = toGtfsDate(date);
|
|
19367
20301
|
try {
|
|
19368
|
-
for (var _d = true, _e = __asyncValues(parseCsv(calendarDatesStream
|
|
20302
|
+
for (var _d = true, _e = __asyncValues(parseCsv(calendarDatesStream, [
|
|
20303
|
+
'date',
|
|
20304
|
+
'exception_type',
|
|
20305
|
+
])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
19369
20306
|
_c = _f.value;
|
|
19370
20307
|
_d = false;
|
|
19371
20308
|
const rawLine = _c;
|
|
@@ -19398,17 +20335,21 @@ const parseCalendarDates = (calendarDatesStream, serviceIds, date) => __awaiter(
|
|
|
19398
20335
|
* @param stopsStream The readable stream containing the stops data.
|
|
19399
20336
|
* @return A mapping of stop IDs to corresponding stop details.
|
|
19400
20337
|
*/
|
|
19401
|
-
const parseStops = (stopsStream, platformParser) => __awaiter(
|
|
20338
|
+
const parseStops = (stopsStream, platformParser) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19402
20339
|
var _a, e_1, _b, _c;
|
|
19403
20340
|
const parsedStops = new Map();
|
|
19404
20341
|
let i = 0;
|
|
19405
20342
|
try {
|
|
19406
|
-
for (var _d = true, _e = __asyncValues(parseCsv(stopsStream
|
|
20343
|
+
for (var _d = true, _e = __asyncValues(parseCsv(stopsStream, [
|
|
20344
|
+
'stop_lat',
|
|
20345
|
+
'stop_lon',
|
|
20346
|
+
'location_type',
|
|
20347
|
+
])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
19407
20348
|
_c = _f.value;
|
|
19408
20349
|
_d = false;
|
|
19409
20350
|
const rawLine = _c;
|
|
19410
20351
|
const line = rawLine;
|
|
19411
|
-
const stop = Object.assign({ id: i, sourceStopId: line.stop_id
|
|
20352
|
+
const stop = Object.assign({ id: i, sourceStopId: line.stop_id, name: line.stop_name, lat: line.stop_lat, lon: line.stop_lon, locationType: line.location_type
|
|
19412
20353
|
? parseGtfsLocationType(line.location_type)
|
|
19413
20354
|
: 'SIMPLE_STOP_OR_PLATFORM', children: [] }, (line.parent_station && { parentSourceId: line.parent_station }));
|
|
19414
20355
|
if (platformParser) {
|
|
@@ -19422,7 +20363,7 @@ const parseStops = (stopsStream, platformParser) => __awaiter(undefined, undefin
|
|
|
19422
20363
|
console.info(`Could not parse platform for stop ${line.stop_id}.`);
|
|
19423
20364
|
}
|
|
19424
20365
|
}
|
|
19425
|
-
parsedStops.set(line.stop_id
|
|
20366
|
+
parsedStops.set(line.stop_id, stop);
|
|
19426
20367
|
i = i + 1;
|
|
19427
20368
|
}
|
|
19428
20369
|
}
|
|
@@ -19499,11 +20440,14 @@ const parseGtfsLocationType = (gtfsLocationType) => {
|
|
|
19499
20440
|
* @param stopsStream The readable stream containing the stops data.
|
|
19500
20441
|
* @return A mapping of stop IDs to corresponding stop details.
|
|
19501
20442
|
*/
|
|
19502
|
-
const parseTransfers = (transfersStream, stopsMap) => __awaiter(
|
|
20443
|
+
const parseTransfers = (transfersStream, stopsMap) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19503
20444
|
var _a, e_1, _b, _c;
|
|
19504
20445
|
const transfers = new Map();
|
|
19505
20446
|
try {
|
|
19506
|
-
for (var _d = true, _e = __asyncValues(parseCsv(transfersStream
|
|
20447
|
+
for (var _d = true, _e = __asyncValues(parseCsv(transfersStream, [
|
|
20448
|
+
'transfer_type',
|
|
20449
|
+
'min_transfer_time',
|
|
20450
|
+
])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
19507
20451
|
_c = _f.value;
|
|
19508
20452
|
_d = false;
|
|
19509
20453
|
const rawLine = _c;
|
|
@@ -19528,9 +20472,9 @@ const parseTransfers = (transfersStream, stopsMap) => __awaiter(undefined, undef
|
|
|
19528
20472
|
console.info(`Missing minimum transfer time between ${transferEntry.from_stop_id} and ${transferEntry.to_stop_id}.`);
|
|
19529
20473
|
}
|
|
19530
20474
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
19531
|
-
const fromStop = stopsMap.get(transferEntry.from_stop_id
|
|
20475
|
+
const fromStop = stopsMap.get(transferEntry.from_stop_id);
|
|
19532
20476
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
19533
|
-
const toStop = stopsMap.get(transferEntry.to_stop_id
|
|
20477
|
+
const toStop = stopsMap.get(transferEntry.to_stop_id);
|
|
19534
20478
|
const transfer = Object.assign({ destination: toStop.id, type: parseGtfsTransferType(transferEntry.transfer_type) }, (transferEntry.min_transfer_time && {
|
|
19535
20479
|
minTransferTime: Duration.fromSeconds(transferEntry.min_transfer_time),
|
|
19536
20480
|
}));
|
|
@@ -19570,11 +20514,11 @@ const parseGtfsTransferType = (gtfsTransferType) => {
|
|
|
19570
20514
|
* @param routeIds A mapping of route IDs to route details.
|
|
19571
20515
|
* @returns A mapping of trip IDs to corresponding route IDs.
|
|
19572
20516
|
*/
|
|
19573
|
-
const parseTrips = (tripsStream, serviceIds, routeIds) => __awaiter(
|
|
20517
|
+
const parseTrips = (tripsStream, serviceIds, routeIds) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19574
20518
|
var _a, e_1, _b, _c;
|
|
19575
20519
|
const trips = new Map();
|
|
19576
20520
|
try {
|
|
19577
|
-
for (var _d = true, _e = __asyncValues(parseCsv(tripsStream)), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
20521
|
+
for (var _d = true, _e = __asyncValues(parseCsv(tripsStream, ['stop_sequence'])), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
|
|
19578
20522
|
_c = _f.value;
|
|
19579
20523
|
_d = false;
|
|
19580
20524
|
const rawLine = _c;
|
|
@@ -19604,11 +20548,14 @@ const buildStopsAdjacencyStructure = (validStops, routes, transfersMap) => {
|
|
|
19604
20548
|
const stopsAdjacency = new Map();
|
|
19605
20549
|
for (const routeId of routes.keys()) {
|
|
19606
20550
|
const route = routes.get(routeId);
|
|
19607
|
-
|
|
20551
|
+
if (!route) {
|
|
20552
|
+
throw new Error(`Route ${routeId} not found`);
|
|
20553
|
+
}
|
|
20554
|
+
for (const stop of route.stopsIterator()) {
|
|
19608
20555
|
if (!stopsAdjacency.get(stop) && validStops.has(stop)) {
|
|
19609
20556
|
stopsAdjacency.set(stop, { routes: [], transfers: [] });
|
|
19610
20557
|
}
|
|
19611
|
-
(_a = stopsAdjacency.get(stop)) === null || _a ===
|
|
20558
|
+
(_a = stopsAdjacency.get(stop)) === null || _a === void 0 ? void 0 : _a.routes.push(routeId);
|
|
19612
20559
|
}
|
|
19613
20560
|
}
|
|
19614
20561
|
for (const [stop, transfers] of transfersMap) {
|
|
@@ -19632,7 +20579,7 @@ const buildStopsAdjacencyStructure = (validStops, routes, transfersMap) => {
|
|
|
19632
20579
|
* @param validStopIds A set of valid stop IDs.
|
|
19633
20580
|
* @returns A mapping of route IDs to route details. The routes returned correspond to the set of trips from GTFS that share the same stop list.
|
|
19634
20581
|
*/
|
|
19635
|
-
const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) => __awaiter(
|
|
20582
|
+
const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) => __awaiter(void 0, void 0, void 0, function* () {
|
|
19636
20583
|
var _a, e_2, _b, _c;
|
|
19637
20584
|
var _d, _e;
|
|
19638
20585
|
/**
|
|
@@ -19670,7 +20617,6 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
|
|
|
19670
20617
|
route = {
|
|
19671
20618
|
serviceRouteId: gtfsRouteId,
|
|
19672
20619
|
stops: stopsArray,
|
|
19673
|
-
stopIndices: new Map(stops.map((stop, i) => [stop, i])),
|
|
19674
20620
|
stopTimes: stopTimesArray,
|
|
19675
20621
|
pickUpDropOffTypes: pickUpDropOffTypesArray,
|
|
19676
20622
|
};
|
|
@@ -19735,7 +20681,7 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
|
|
|
19735
20681
|
let dropOffTypes = [];
|
|
19736
20682
|
let currentTripId = undefined;
|
|
19737
20683
|
try {
|
|
19738
|
-
for (var _f = true, _g = __asyncValues(parseCsv(stopTimesStream)), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
|
|
20684
|
+
for (var _f = true, _g = __asyncValues(parseCsv(stopTimesStream, ['stop_sequence'])), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
|
|
19739
20685
|
_c = _h.value;
|
|
19740
20686
|
_f = false;
|
|
19741
20687
|
const rawLine = _c;
|
|
@@ -19756,7 +20702,7 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
|
|
|
19756
20702
|
}
|
|
19757
20703
|
currentTripId = line.trip_id;
|
|
19758
20704
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
19759
|
-
stops.push(stopsMap.get(line.stop_id
|
|
20705
|
+
stops.push(stopsMap.get(line.stop_id).id);
|
|
19760
20706
|
const departure = (_d = line.departure_time) !== null && _d !== void 0 ? _d : line.arrival_time;
|
|
19761
20707
|
const arrival = (_e = line.arrival_time) !== null && _e !== void 0 ? _e : line.departure_time;
|
|
19762
20708
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
@@ -19778,12 +20724,15 @@ const parseStopTimes = (stopTimesStream, stopsMap, validTripIds, validStopIds) =
|
|
|
19778
20724
|
if (currentTripId) {
|
|
19779
20725
|
addTrip(currentTripId);
|
|
19780
20726
|
}
|
|
19781
|
-
|
|
20727
|
+
const routesAdjacency = new Map();
|
|
20728
|
+
for (const [routeId, routeData] of routes) {
|
|
20729
|
+
routesAdjacency.set(routeId, new Route$1(routeData.stopTimes, routeData.pickUpDropOffTypes, routeData.stops, routeData.serviceRouteId));
|
|
20730
|
+
}
|
|
20731
|
+
return routesAdjacency;
|
|
19782
20732
|
});
|
|
19783
20733
|
const parsePickupDropOffType = (gtfsType) => {
|
|
19784
20734
|
switch (gtfsType) {
|
|
19785
20735
|
default:
|
|
19786
|
-
console.warn(`Unknown pickup/drop-off type ${gtfsType}`);
|
|
19787
20736
|
return REGULAR;
|
|
19788
20737
|
case 0:
|
|
19789
20738
|
return REGULAR;
|
|
@@ -19818,7 +20767,7 @@ class GtfsParser {
|
|
|
19818
20767
|
* @returns An object containing the timetable and stops map.
|
|
19819
20768
|
*/
|
|
19820
20769
|
parse(date) {
|
|
19821
|
-
return __awaiter(this,
|
|
20770
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19822
20771
|
const zip = new StreamZip.async({ file: this.path });
|
|
19823
20772
|
const entries = yield zip.entries();
|
|
19824
20773
|
const datetime = DateTime.fromJSDate(date);
|
|
@@ -19879,7 +20828,7 @@ class GtfsParser {
|
|
|
19879
20828
|
* @returns An object containing the timetable and stops map.
|
|
19880
20829
|
*/
|
|
19881
20830
|
parseStops() {
|
|
19882
|
-
return __awaiter(this,
|
|
20831
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19883
20832
|
const zip = new StreamZip.async({ file: this.path });
|
|
19884
20833
|
log.info(`Parsing ${STOPS_FILE}`);
|
|
19885
20834
|
const stopsStream = yield zip.stream(STOPS_FILE);
|
|
@@ -19897,7 +20846,7 @@ class GtfsParser {
|
|
|
19897
20846
|
* @returns The platform corresponding to this stop.
|
|
19898
20847
|
*/
|
|
19899
20848
|
const platformParser = (stopEntry) => {
|
|
19900
|
-
const stopId =
|
|
20849
|
+
const stopId = stopEntry.stop_id;
|
|
19901
20850
|
const stopParts = stopId.split(':');
|
|
19902
20851
|
if (stopParts.length > 2) {
|
|
19903
20852
|
return stopParts[2];
|
|
@@ -20018,7 +20967,7 @@ class Query {
|
|
|
20018
20967
|
}
|
|
20019
20968
|
Query.Builder = class {
|
|
20020
20969
|
constructor() {
|
|
20021
|
-
this.toValue =
|
|
20970
|
+
this.toValue = new Set();
|
|
20022
20971
|
// lastDepartureTimeValue?: Date;
|
|
20023
20972
|
// via: StopId[] = [];
|
|
20024
20973
|
this.optionsValue = {
|
|
@@ -20027,26 +20976,47 @@ Query.Builder = class {
|
|
|
20027
20976
|
transportModes: ALL_TRANSPORT_MODES,
|
|
20028
20977
|
};
|
|
20029
20978
|
}
|
|
20979
|
+
/**
|
|
20980
|
+
* Sets the starting stop.
|
|
20981
|
+
*/
|
|
20030
20982
|
from(from) {
|
|
20031
20983
|
this.fromValue = from;
|
|
20032
20984
|
return this;
|
|
20033
20985
|
}
|
|
20986
|
+
/**
|
|
20987
|
+
* Sets the destination stops(s), routing will stop when all the provided stops are reached.
|
|
20988
|
+
*/
|
|
20034
20989
|
to(to) {
|
|
20035
|
-
this.toValue =
|
|
20990
|
+
this.toValue = to instanceof Set ? to : new Set([to]);
|
|
20036
20991
|
return this;
|
|
20037
20992
|
}
|
|
20993
|
+
/**
|
|
20994
|
+
* Sets the departure time for the query.
|
|
20995
|
+
* Note that the router will favor routes that depart shortly after the provided departure time,
|
|
20996
|
+
* even if a later route might arrive at the same time.
|
|
20997
|
+
* Range queries will allow to specify a range of departure times in the future.
|
|
20998
|
+
*/
|
|
20038
20999
|
departureTime(departureTime) {
|
|
20039
21000
|
this.departureTimeValue = departureTime;
|
|
20040
21001
|
return this;
|
|
20041
21002
|
}
|
|
21003
|
+
/**
|
|
21004
|
+
* Sets the maximum number of transfers allowed.
|
|
21005
|
+
*/
|
|
20042
21006
|
maxTransfers(maxTransfers) {
|
|
20043
21007
|
this.optionsValue.maxTransfers = maxTransfers;
|
|
20044
21008
|
return this;
|
|
20045
21009
|
}
|
|
21010
|
+
/**
|
|
21011
|
+
* Sets the minimum transfer time to use when no transfer time is provided in the data.
|
|
21012
|
+
*/
|
|
20046
21013
|
minTransferTime(minTransferTime) {
|
|
20047
21014
|
this.optionsValue.minTransferTime = minTransferTime;
|
|
20048
21015
|
return this;
|
|
20049
21016
|
}
|
|
21017
|
+
/**
|
|
21018
|
+
* Sets the transport modes to consider.
|
|
21019
|
+
*/
|
|
20050
21020
|
transportModes(transportModes) {
|
|
20051
21021
|
this.optionsValue.transportModes = transportModes;
|
|
20052
21022
|
return this;
|
|
@@ -20056,10 +21026,20 @@ Query.Builder = class {
|
|
|
20056
21026
|
}
|
|
20057
21027
|
};
|
|
20058
21028
|
|
|
21029
|
+
/**
|
|
21030
|
+
* Represents a resolved route consisting of multiple legs,
|
|
21031
|
+
* which can be either vehicle legs or transfer legs.
|
|
21032
|
+
*/
|
|
20059
21033
|
class Route {
|
|
20060
21034
|
constructor(legs) {
|
|
20061
21035
|
this.legs = legs;
|
|
20062
21036
|
}
|
|
21037
|
+
/**
|
|
21038
|
+
* Calculates the departure time of the route.
|
|
21039
|
+
*
|
|
21040
|
+
* @returns The departure time of the route.
|
|
21041
|
+
* @throws If no vehicle leg is found in the route.
|
|
21042
|
+
*/
|
|
20063
21043
|
departureTime() {
|
|
20064
21044
|
const cumulativeTransferTime = Duration.zero();
|
|
20065
21045
|
for (let i = 0; i < this.legs.length; i++) {
|
|
@@ -20074,6 +21054,12 @@ class Route {
|
|
|
20074
21054
|
}
|
|
20075
21055
|
throw new Error('No vehicle leg found in route');
|
|
20076
21056
|
}
|
|
21057
|
+
/**
|
|
21058
|
+
* Calculates the arrival time of the route.
|
|
21059
|
+
*
|
|
21060
|
+
* @returns The arrival time of the route.
|
|
21061
|
+
* @throws If no vehicle leg is found in the route.
|
|
21062
|
+
*/
|
|
20077
21063
|
arrivalTime() {
|
|
20078
21064
|
let lastVehicleArrivalTime = Time.origin();
|
|
20079
21065
|
const totalTransferTime = Duration.zero();
|
|
@@ -20096,25 +21082,71 @@ class Route {
|
|
|
20096
21082
|
}
|
|
20097
21083
|
return lastVehicleArrivalTime.plus(totalTransferTime);
|
|
20098
21084
|
}
|
|
21085
|
+
/**
|
|
21086
|
+
* Calculates the total duration of the route.
|
|
21087
|
+
*
|
|
21088
|
+
* @returns The total duration of the route.
|
|
21089
|
+
*/
|
|
20099
21090
|
totalDuration() {
|
|
20100
21091
|
if (this.legs.length === 0)
|
|
20101
21092
|
return Duration.zero();
|
|
20102
21093
|
return this.arrivalTime().diff(this.departureTime());
|
|
20103
21094
|
}
|
|
20104
|
-
|
|
21095
|
+
/**
|
|
21096
|
+
* Generates a human-readable string representation of the route.
|
|
21097
|
+
*
|
|
21098
|
+
* @returns A formatted string describing each leg of the route.
|
|
21099
|
+
*/
|
|
21100
|
+
toString() {
|
|
20105
21101
|
return this.legs
|
|
20106
21102
|
.map((leg, index) => {
|
|
20107
|
-
var _a
|
|
20108
|
-
|
|
20109
|
-
|
|
20110
|
-
|
|
20111
|
-
|
|
20112
|
-
|
|
20113
|
-
|
|
20114
|
-
|
|
21103
|
+
var _a;
|
|
21104
|
+
const fromStop = `From: ${leg.from.name}${leg.from.platform ? ` (Pl. ${leg.from.platform})` : ''}`;
|
|
21105
|
+
const toStop = `To: ${leg.to.name}${leg.to.platform ? ` (Pl. ${leg.to.platform})` : ''}`;
|
|
21106
|
+
const transferDetails = 'minTransferTime' in leg
|
|
21107
|
+
? `Minimum Transfer Time: ${(_a = leg.minTransferTime) === null || _a === void 0 ? void 0 : _a.toString()}`
|
|
21108
|
+
: '';
|
|
21109
|
+
const travelDetails = 'route' in leg && 'departureTime' in leg && 'arrivalTime' in leg
|
|
21110
|
+
? `Route: ${leg.route.type} ${leg.route.name}, Departure: ${leg.departureTime.toString()}, Arrival: ${leg.arrivalTime.toString()}`
|
|
21111
|
+
: '';
|
|
21112
|
+
return [
|
|
21113
|
+
`Leg ${index + 1}:`,
|
|
21114
|
+
` ${fromStop}`,
|
|
21115
|
+
` ${toStop}`,
|
|
21116
|
+
transferDetails ? ` ${transferDetails}` : '',
|
|
21117
|
+
travelDetails ? ` ${travelDetails}` : '',
|
|
21118
|
+
]
|
|
21119
|
+
.filter((line) => line.trim() !== '')
|
|
21120
|
+
.join('\n');
|
|
20115
21121
|
})
|
|
20116
21122
|
.join('\n');
|
|
20117
21123
|
}
|
|
21124
|
+
/**
|
|
21125
|
+
* Generates a concise JSON representation of the route.
|
|
21126
|
+
* This is particularly useful for generating regression tests
|
|
21127
|
+
* to verify the correctness of route calculations.
|
|
21128
|
+
*
|
|
21129
|
+
* @returns A JSON representation of the route.
|
|
21130
|
+
*/
|
|
21131
|
+
asJson() {
|
|
21132
|
+
const jsonLegs = this.legs.map((leg) => {
|
|
21133
|
+
if ('route' in leg) {
|
|
21134
|
+
return {
|
|
21135
|
+
from: leg.from.sourceStopId,
|
|
21136
|
+
to: leg.to.sourceStopId,
|
|
21137
|
+
departure: leg.departureTime.toString(),
|
|
21138
|
+
arrival: leg.arrivalTime.toString(),
|
|
21139
|
+
route: leg.route,
|
|
21140
|
+
};
|
|
21141
|
+
}
|
|
21142
|
+
else {
|
|
21143
|
+
return Object.assign({ from: leg.from.sourceStopId, to: leg.to.sourceStopId, type: leg.type }, (leg.minTransferTime !== undefined && {
|
|
21144
|
+
minTransferTime: leg.minTransferTime.toString(),
|
|
21145
|
+
}));
|
|
21146
|
+
}
|
|
21147
|
+
});
|
|
21148
|
+
return jsonLegs;
|
|
21149
|
+
}
|
|
20118
21150
|
}
|
|
20119
21151
|
|
|
20120
21152
|
class Result {
|
|
@@ -20133,7 +21165,11 @@ class Result {
|
|
|
20133
21165
|
*/
|
|
20134
21166
|
bestRoute(to) {
|
|
20135
21167
|
var _a, _b, _c;
|
|
20136
|
-
const destinationList =
|
|
21168
|
+
const destinationList = to instanceof Set
|
|
21169
|
+
? Array.from(to)
|
|
21170
|
+
: to
|
|
21171
|
+
? [to]
|
|
21172
|
+
: Array.from(this.query.to);
|
|
20137
21173
|
const destinations = destinationList.flatMap((destination) => this.stopsIndex.equivalentStops(destination));
|
|
20138
21174
|
let fastestDestination = undefined;
|
|
20139
21175
|
let fastestTime = undefined;
|
|
@@ -20141,7 +21177,7 @@ class Result {
|
|
|
20141
21177
|
const arrivalTime = this.earliestArrivals.get(destination.id);
|
|
20142
21178
|
if (arrivalTime !== undefined) {
|
|
20143
21179
|
if (fastestTime === undefined ||
|
|
20144
|
-
arrivalTime.
|
|
21180
|
+
arrivalTime.arrival.isBefore(fastestTime.arrival)) {
|
|
20145
21181
|
fastestDestination = destination.id;
|
|
20146
21182
|
fastestTime = arrivalTime;
|
|
20147
21183
|
}
|
|
@@ -20154,9 +21190,9 @@ class Result {
|
|
|
20154
21190
|
let currentStop = fastestDestination;
|
|
20155
21191
|
let round = fastestTime.legNumber;
|
|
20156
21192
|
while (fastestTime.origin !== currentStop) {
|
|
20157
|
-
const tripLeg = (_a = this.earliestArrivalsPerRound[round]) === null || _a ===
|
|
20158
|
-
if (!(tripLeg === null || tripLeg ===
|
|
20159
|
-
throw new Error(`No leg found for a trip leg: start stop=${(_c = (_b = tripLeg === null || tripLeg ===
|
|
21193
|
+
const tripLeg = (_a = this.earliestArrivalsPerRound[round]) === null || _a === void 0 ? void 0 : _a.get(currentStop);
|
|
21194
|
+
if (!(tripLeg === null || tripLeg === void 0 ? void 0 : tripLeg.leg)) {
|
|
21195
|
+
throw new Error(`No leg found for a trip leg: start stop=${(_c = (_b = tripLeg === null || tripLeg === void 0 ? void 0 : tripLeg.leg) === null || _b === void 0 ? void 0 : _b.from.id) !== null && _c !== void 0 ? _c : 'unknown'}, end stop=${currentStop}, round=${round}, origin=${fastestTime.origin}`);
|
|
20160
21196
|
}
|
|
20161
21197
|
route.unshift(tripLeg.leg);
|
|
20162
21198
|
currentStop = tripLeg.leg.from.id;
|
|
@@ -20178,13 +21214,13 @@ class Result {
|
|
|
20178
21214
|
let earliestArrival = undefined;
|
|
20179
21215
|
const relevantArrivals = maxTransfers !== undefined
|
|
20180
21216
|
? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
20181
|
-
this.earliestArrivalsPerRound[maxTransfers
|
|
21217
|
+
this.earliestArrivalsPerRound[maxTransfers + 1]
|
|
20182
21218
|
: this.earliestArrivals;
|
|
20183
21219
|
for (const equivalentStop of equivalentStops) {
|
|
20184
21220
|
const arrivalTime = relevantArrivals.get(equivalentStop.id);
|
|
20185
21221
|
if (arrivalTime !== undefined) {
|
|
20186
21222
|
if (earliestArrival === undefined ||
|
|
20187
|
-
arrivalTime.
|
|
21223
|
+
arrivalTime.arrival.isBefore(earliestArrival.arrival)) {
|
|
20188
21224
|
earliestArrival = arrivalTime;
|
|
20189
21225
|
}
|
|
20190
21226
|
}
|
|
@@ -20194,6 +21230,12 @@ class Result {
|
|
|
20194
21230
|
}
|
|
20195
21231
|
|
|
20196
21232
|
const UNREACHED = Time.infinity();
|
|
21233
|
+
/**
|
|
21234
|
+
* A public transportation network router utilizing the RAPTOR algorithm for
|
|
21235
|
+
* efficient journey planning and routing. For more information on the RAPTOR
|
|
21236
|
+
* algorithm, refer to its detailed explanation in the research paper:
|
|
21237
|
+
* https://www.microsoft.com/en-us/research/wp-content/uploads/2012/01/raptor_alenex.pdf
|
|
21238
|
+
*/
|
|
20197
21239
|
class Router {
|
|
20198
21240
|
constructor(timetable, stopsIndex) {
|
|
20199
21241
|
this.timetable = timetable;
|
|
@@ -20205,10 +21247,18 @@ class Router {
|
|
|
20205
21247
|
* stops that can be reached through these transfers.
|
|
20206
21248
|
*/
|
|
20207
21249
|
considerTransfers(query, markedStops, arrivalsAtCurrentRound, earliestArrivals, round) {
|
|
20208
|
-
var _a, _b
|
|
21250
|
+
var _a, _b;
|
|
20209
21251
|
const { options } = query;
|
|
20210
21252
|
const newlyMarkedStops = new Set();
|
|
20211
21253
|
for (const stop of markedStops) {
|
|
21254
|
+
const currentArrival = arrivalsAtCurrentRound.get(stop);
|
|
21255
|
+
if (!currentArrival)
|
|
21256
|
+
continue;
|
|
21257
|
+
// Skip transfers if the last leg was also a transfer
|
|
21258
|
+
const previousLeg = currentArrival.leg;
|
|
21259
|
+
if (previousLeg && !('route' in previousLeg)) {
|
|
21260
|
+
continue;
|
|
21261
|
+
}
|
|
20212
21262
|
for (const transfer of this.timetable.getTransfers(stop)) {
|
|
20213
21263
|
let transferTime;
|
|
20214
21264
|
if (transfer.minTransferTime) {
|
|
@@ -20220,14 +21270,12 @@ class Router {
|
|
|
20220
21270
|
else {
|
|
20221
21271
|
transferTime = options.minTransferTime;
|
|
20222
21272
|
}
|
|
20223
|
-
const arrivalAfterTransfer =
|
|
20224
|
-
|
|
20225
|
-
|
|
20226
|
-
|
|
20227
|
-
if (arrivalAfterTransfer.toMinutes() < originalArrival.toMinutes()) {
|
|
20228
|
-
const origin = (_d = (_c = arrivalsAtCurrentRound.get(stop)) === null || _c === undefined ? undefined : _c.origin) !== null && _d !== undefined ? _d : stop;
|
|
21273
|
+
const arrivalAfterTransfer = currentArrival.arrival.plus(transferTime);
|
|
21274
|
+
const originalArrival = (_b = (_a = arrivalsAtCurrentRound.get(transfer.destination)) === null || _a === void 0 ? void 0 : _a.arrival) !== null && _b !== void 0 ? _b : UNREACHED;
|
|
21275
|
+
if (arrivalAfterTransfer.isBefore(originalArrival)) {
|
|
21276
|
+
const origin = currentArrival.origin;
|
|
20229
21277
|
arrivalsAtCurrentRound.set(transfer.destination, {
|
|
20230
|
-
|
|
21278
|
+
arrival: arrivalAfterTransfer,
|
|
20231
21279
|
legNumber: round,
|
|
20232
21280
|
origin: origin,
|
|
20233
21281
|
leg: {
|
|
@@ -20238,7 +21286,7 @@ class Router {
|
|
|
20238
21286
|
},
|
|
20239
21287
|
});
|
|
20240
21288
|
earliestArrivals.set(transfer.destination, {
|
|
20241
|
-
|
|
21289
|
+
arrival: arrivalAfterTransfer,
|
|
20242
21290
|
legNumber: round,
|
|
20243
21291
|
origin: origin,
|
|
20244
21292
|
});
|
|
@@ -20250,6 +21298,22 @@ class Router {
|
|
|
20250
21298
|
markedStops.add(newStop);
|
|
20251
21299
|
}
|
|
20252
21300
|
}
|
|
21301
|
+
/**
|
|
21302
|
+
* Finds the earliest arrival time at any stop from a given set of destinations.
|
|
21303
|
+
*
|
|
21304
|
+
* @param earliestArrivals A map of stops to their earliest reaching times.
|
|
21305
|
+
* @param destinations An array of destination stops to evaluate.
|
|
21306
|
+
* @returns The earliest arrival time among the provided destinations.
|
|
21307
|
+
*/
|
|
21308
|
+
earliestArrivalAtAnyStop(earliestArrivals, destinations) {
|
|
21309
|
+
var _a, _b;
|
|
21310
|
+
let earliestArrivalAtAnyDestination = UNREACHED;
|
|
21311
|
+
for (const destination of destinations) {
|
|
21312
|
+
const arrival = (_b = (_a = earliestArrivals.get(destination.id)) === null || _a === void 0 ? void 0 : _a.arrival) !== null && _b !== void 0 ? _b : UNREACHED;
|
|
21313
|
+
earliestArrivalAtAnyDestination = Time.min(earliestArrivalAtAnyDestination, arrival);
|
|
21314
|
+
}
|
|
21315
|
+
return earliestArrivalAtAnyDestination;
|
|
21316
|
+
}
|
|
20253
21317
|
/**
|
|
20254
21318
|
* The main Raptor algorithm implementation.
|
|
20255
21319
|
*
|
|
@@ -20257,12 +21321,12 @@ class Router {
|
|
|
20257
21321
|
* @returns A result object containing data structures allowing to reconstruct routes and .
|
|
20258
21322
|
*/
|
|
20259
21323
|
route(query) {
|
|
20260
|
-
var _a, _b, _c, _d, _e
|
|
21324
|
+
var _a, _b, _c, _d, _e;
|
|
20261
21325
|
const { from, to, departureTime, options } = query;
|
|
20262
21326
|
// Consider children or siblings of the "from" stop as potential origins
|
|
20263
21327
|
const origins = this.stopsIndex.equivalentStops(from);
|
|
20264
21328
|
// Consider children or siblings of the "to" stop(s) as potential destinations
|
|
20265
|
-
const destinations = to.flatMap((destination) => this.stopsIndex.equivalentStops(destination));
|
|
21329
|
+
const destinations = Array.from(to).flatMap((destination) => this.stopsIndex.equivalentStops(destination));
|
|
20266
21330
|
const earliestArrivals = new Map();
|
|
20267
21331
|
const earliestArrivalsWithoutAnyLeg = new Map();
|
|
20268
21332
|
const earliestArrivalsPerRound = [earliestArrivalsWithoutAnyLeg];
|
|
@@ -20271,12 +21335,12 @@ class Router {
|
|
|
20271
21335
|
for (const originStop of origins) {
|
|
20272
21336
|
markedStops.add(originStop.id);
|
|
20273
21337
|
earliestArrivals.set(originStop.id, {
|
|
20274
|
-
|
|
21338
|
+
arrival: departureTime,
|
|
20275
21339
|
legNumber: 0,
|
|
20276
21340
|
origin: originStop.id,
|
|
20277
21341
|
});
|
|
20278
21342
|
earliestArrivalsWithoutAnyLeg.set(originStop.id, {
|
|
20279
|
-
|
|
21343
|
+
arrival: departureTime,
|
|
20280
21344
|
legNumber: 0,
|
|
20281
21345
|
origin: originStop.id,
|
|
20282
21346
|
});
|
|
@@ -20292,38 +21356,19 @@ class Router {
|
|
|
20292
21356
|
const reachableRoutes = this.timetable.findReachableRoutes(markedStops, options.transportModes);
|
|
20293
21357
|
markedStops.clear();
|
|
20294
21358
|
// for each route that can be reached with at least round - 1 trips
|
|
20295
|
-
for (const [
|
|
20296
|
-
const route = this.timetable.getRoute(routeId);
|
|
21359
|
+
for (const [route, hopOnStop] of reachableRoutes.entries()) {
|
|
20297
21360
|
let currentTrip = undefined;
|
|
20298
|
-
const
|
|
20299
|
-
// for each stop in the route starting with the hop-on one
|
|
20300
|
-
for (let i = hopOnIndex; i < route.stops.length; i++) {
|
|
20301
|
-
const currentStop = route.stops[i];
|
|
20302
|
-
const stopNumbers = route.stops.length;
|
|
21361
|
+
for (const currentStop of route.stopsIterator(hopOnStop)) {
|
|
20303
21362
|
if (currentTrip !== undefined) {
|
|
20304
|
-
const
|
|
20305
|
-
const
|
|
20306
|
-
const
|
|
20307
|
-
|
|
20308
|
-
|
|
20309
|
-
|
|
20310
|
-
const
|
|
20311
|
-
// if multiple destinations are specified, the target pruning
|
|
20312
|
-
// should compare to the earliest arrival at any of them
|
|
20313
|
-
for (const destinationStop of destinations) {
|
|
20314
|
-
const earliestArrivalAtDestination = (_d = (_c = earliestArrivals.get(destinationStop.id)) === null || _c === undefined ? undefined : _c.time) !== null && _d !== undefined ? _d : UNREACHED;
|
|
20315
|
-
earliestArrivalsAtDestinations.push(earliestArrivalAtDestination);
|
|
20316
|
-
}
|
|
20317
|
-
const earliestArrivalAtDestination = Time.min(...earliestArrivalsAtDestinations);
|
|
20318
|
-
arrivalToImprove = Time.min(earliestArrivalAtCurrentStop, earliestArrivalAtDestination);
|
|
20319
|
-
}
|
|
20320
|
-
if (currentDropOffType !== NOT_AVAILABLE &&
|
|
20321
|
-
currentArrivalTime.toMinutes() < arrivalToImprove.toMinutes()) {
|
|
20322
|
-
const bestHopOnStopIndex = route.stopIndices.get(currentTrip.bestHopOnStop);
|
|
20323
|
-
const bestHopOnStopDepartureIndex = currentTrip.trip * stopNumbers * 2 + bestHopOnStopIndex * 2 + 1;
|
|
20324
|
-
const bestHopOnDepartureTime = Time.fromMinutes(route.stopTimes[bestHopOnStopDepartureIndex]);
|
|
21363
|
+
const currentArrivalTime = route.arrivalAt(currentStop, currentTrip.tripIndex);
|
|
21364
|
+
const currentDropOffType = route.dropOffTypeAt(currentStop, currentTrip.tripIndex);
|
|
21365
|
+
const earliestArrivalAtCurrentStop = (_b = (_a = earliestArrivals.get(currentStop)) === null || _a === void 0 ? void 0 : _a.arrival) !== null && _b !== void 0 ? _b : UNREACHED;
|
|
21366
|
+
if (currentDropOffType !== 'NOT_AVAILABLE' &&
|
|
21367
|
+
currentArrivalTime.isBefore(earliestArrivalAtCurrentStop) &&
|
|
21368
|
+
currentArrivalTime.isBefore(this.earliestArrivalAtAnyStop(earliestArrivals, destinations))) {
|
|
21369
|
+
const bestHopOnDepartureTime = route.departureFrom(currentTrip.bestHopOnStop, currentTrip.tripIndex);
|
|
20325
21370
|
arrivalsAtCurrentRound.set(currentStop, {
|
|
20326
|
-
|
|
21371
|
+
arrival: currentArrivalTime,
|
|
20327
21372
|
legNumber: round,
|
|
20328
21373
|
origin: currentTrip.origin,
|
|
20329
21374
|
leg: {
|
|
@@ -20331,11 +21376,11 @@ class Router {
|
|
|
20331
21376
|
to: this.stopsIndex.findStopById(currentStop),
|
|
20332
21377
|
departureTime: bestHopOnDepartureTime,
|
|
20333
21378
|
arrivalTime: currentArrivalTime,
|
|
20334
|
-
route: this.timetable.getServiceRoute(route
|
|
21379
|
+
route: this.timetable.getServiceRoute(route),
|
|
20335
21380
|
},
|
|
20336
21381
|
});
|
|
20337
21382
|
earliestArrivals.set(currentStop, {
|
|
20338
|
-
|
|
21383
|
+
arrival: currentArrivalTime,
|
|
20339
21384
|
legNumber: round,
|
|
20340
21385
|
origin: currentTrip.origin,
|
|
20341
21386
|
});
|
|
@@ -20344,18 +21389,18 @@ class Router {
|
|
|
20344
21389
|
}
|
|
20345
21390
|
// check if we can catch a previous trip at the current stop
|
|
20346
21391
|
// if there was no current trip, find the first one reachable
|
|
20347
|
-
const earliestArrivalOnPreviousRound = (
|
|
21392
|
+
const earliestArrivalOnPreviousRound = (_c = arrivalsAtPreviousRound.get(currentStop)) === null || _c === void 0 ? void 0 : _c.arrival;
|
|
20348
21393
|
if (earliestArrivalOnPreviousRound !== undefined &&
|
|
20349
21394
|
(currentTrip === undefined ||
|
|
20350
|
-
earliestArrivalOnPreviousRound.
|
|
20351
|
-
|
|
20352
|
-
const earliestTrip =
|
|
21395
|
+
earliestArrivalOnPreviousRound.isBefore(route.arrivalAt(currentStop, currentTrip.tripIndex)) ||
|
|
21396
|
+
earliestArrivalOnPreviousRound.equals(route.arrivalAt(currentStop, currentTrip.tripIndex)))) {
|
|
21397
|
+
const earliestTrip = route.findEarliestTrip(currentStop, earliestArrivalOnPreviousRound, currentTrip === null || currentTrip === void 0 ? void 0 : currentTrip.tripIndex);
|
|
20353
21398
|
if (earliestTrip !== undefined) {
|
|
20354
21399
|
currentTrip = {
|
|
20355
|
-
|
|
21400
|
+
tripIndex: earliestTrip,
|
|
20356
21401
|
// we need to keep track of the best hop-on stop to reconstruct the route at the end
|
|
20357
21402
|
bestHopOnStop: currentStop,
|
|
20358
|
-
origin: (
|
|
21403
|
+
origin: (_e = (_d = arrivalsAtPreviousRound.get(currentStop)) === null || _d === void 0 ? void 0 : _d.origin) !== null && _e !== void 0 ? _e : currentStop,
|
|
20359
21404
|
};
|
|
20360
21405
|
}
|
|
20361
21406
|
}
|
|
@@ -20369,31 +21414,97 @@ class Router {
|
|
|
20369
21414
|
}
|
|
20370
21415
|
}
|
|
20371
21416
|
|
|
20372
|
-
|
|
20373
|
-
|
|
20374
|
-
|
|
20375
|
-
|
|
20376
|
-
|
|
20377
|
-
|
|
20378
|
-
|
|
20379
|
-
|
|
20380
|
-
|
|
20381
|
-
|
|
20382
|
-
|
|
20383
|
-
|
|
21417
|
+
/**
|
|
21418
|
+
*
|
|
21419
|
+
* @param filePath
|
|
21420
|
+
* @returns
|
|
21421
|
+
*/
|
|
21422
|
+
const loadQueriesFromJson = (filePath) => {
|
|
21423
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
21424
|
+
const serializedQueries = JSON.parse(fileContent);
|
|
21425
|
+
return serializedQueries.map((serializedQuery) => {
|
|
21426
|
+
const queryBuilder = new Query.Builder()
|
|
21427
|
+
.from(serializedQuery.from)
|
|
21428
|
+
.to(new Set(serializedQuery.to))
|
|
21429
|
+
.departureTime(Time.fromString(serializedQuery.departureTime));
|
|
21430
|
+
if (serializedQuery.maxTransfers !== undefined) {
|
|
21431
|
+
queryBuilder.maxTransfers(serializedQuery.maxTransfers);
|
|
20384
21432
|
}
|
|
20385
|
-
|
|
20386
|
-
|
|
20387
|
-
|
|
20388
|
-
|
|
20389
|
-
|
|
21433
|
+
return queryBuilder.build();
|
|
21434
|
+
});
|
|
21435
|
+
};
|
|
21436
|
+
/**
|
|
21437
|
+
*
|
|
21438
|
+
* @param router
|
|
21439
|
+
* @param stopsIndex
|
|
21440
|
+
* @param tasks
|
|
21441
|
+
* @param iterations
|
|
21442
|
+
* @returns
|
|
21443
|
+
*/
|
|
21444
|
+
const testRouterPerformance = (router, stopsIndex, tasks, iterations) => {
|
|
21445
|
+
const results = [];
|
|
21446
|
+
for (const task of tasks) {
|
|
21447
|
+
const fromStop = stopsIndex.findStopBySourceStopId(task.from);
|
|
21448
|
+
const toStops = Array.from(task.to).map((stopId) => stopsIndex.findStopBySourceStopId(stopId));
|
|
21449
|
+
if (!fromStop || toStops.some((toStop) => !toStop)) {
|
|
21450
|
+
throw new Error(`Invalid task: Start or end station not found for task ${JSON.stringify(task)}`);
|
|
20390
21451
|
}
|
|
20391
|
-
|
|
20392
|
-
|
|
21452
|
+
let totalTime = 0;
|
|
21453
|
+
let totalMemory = 0;
|
|
21454
|
+
for (let i = 0; i < iterations; i++) {
|
|
21455
|
+
if (global.gc) {
|
|
21456
|
+
global.gc();
|
|
21457
|
+
}
|
|
21458
|
+
const startMemory = process.memoryUsage().heapUsed;
|
|
21459
|
+
const startTime = performance.now();
|
|
21460
|
+
router.route(task);
|
|
21461
|
+
const endTime = performance.now();
|
|
21462
|
+
const endMemory = process.memoryUsage().heapUsed;
|
|
21463
|
+
totalTime += endTime - startTime;
|
|
21464
|
+
if (endMemory >= startMemory) {
|
|
21465
|
+
totalMemory += endMemory - startMemory;
|
|
21466
|
+
}
|
|
20393
21467
|
}
|
|
21468
|
+
results.push({
|
|
21469
|
+
task,
|
|
21470
|
+
meanTimeMs: totalTime / iterations,
|
|
21471
|
+
meanMemoryMb: totalMemory / iterations / (1024 * 1024),
|
|
21472
|
+
});
|
|
21473
|
+
}
|
|
21474
|
+
return results;
|
|
21475
|
+
};
|
|
21476
|
+
/**
|
|
21477
|
+
*
|
|
21478
|
+
* @param results
|
|
21479
|
+
* @returns
|
|
21480
|
+
*/
|
|
21481
|
+
const prettyPrintPerformanceResults = (results) => {
|
|
21482
|
+
if (results.length === 0) {
|
|
21483
|
+
console.log('No performance results to display.');
|
|
21484
|
+
return;
|
|
21485
|
+
}
|
|
21486
|
+
const overallMeanTimeMs = results.reduce((sum, result) => sum + result.meanTimeMs, 0) /
|
|
21487
|
+
results.length;
|
|
21488
|
+
const overallMeanMemoryMb = results.reduce((sum, result) => sum + result.meanMemoryMb, 0) /
|
|
21489
|
+
results.length;
|
|
21490
|
+
console.log('Overall Performance Results:');
|
|
21491
|
+
console.log(` Mean Time (ms): ${overallMeanTimeMs.toFixed(2)}`);
|
|
21492
|
+
console.log(` Mean Memory (MB): ${overallMeanMemoryMb.toFixed(2)}`);
|
|
21493
|
+
console.log('');
|
|
21494
|
+
console.log('Individual Task Results:');
|
|
21495
|
+
results.forEach((result, index) => {
|
|
21496
|
+
console.log(`Task ${index + 1}:`);
|
|
21497
|
+
console.log(` Mean Time (ms): ${result.meanTimeMs.toFixed(2)}`);
|
|
21498
|
+
console.log(` Mean Memory (MB): ${result.meanMemoryMb.toFixed(2)}`);
|
|
20394
21499
|
console.log('');
|
|
20395
21500
|
});
|
|
20396
21501
|
};
|
|
21502
|
+
|
|
21503
|
+
/**
|
|
21504
|
+
* Plots the graph of the result to a dot file.
|
|
21505
|
+
* @param result - The result object to plot.
|
|
21506
|
+
* @param filePath - The path where the dot file will be saved.
|
|
21507
|
+
*/
|
|
20397
21508
|
const plotGraphToDotFile = (result, filePath) => {
|
|
20398
21509
|
const plotter = new Plotter(result);
|
|
20399
21510
|
const dotContent = plotter.plotDotGraph();
|
|
@@ -20414,9 +21525,22 @@ const startRepl = (stopsPath, timetablePath) => {
|
|
|
20414
21525
|
help: 'Find stops by name using .find <query>',
|
|
20415
21526
|
action(query) {
|
|
20416
21527
|
this.clearBufferedCommand();
|
|
20417
|
-
|
|
21528
|
+
let stops = [];
|
|
21529
|
+
const stopBySourceId = stopsIndex.findStopBySourceStopId(query);
|
|
21530
|
+
if (stopBySourceId !== undefined) {
|
|
21531
|
+
stops.push(stopBySourceId);
|
|
21532
|
+
}
|
|
21533
|
+
else if (!isNaN(Number(query))) {
|
|
21534
|
+
const stopById = stopsIndex.findStopById(Number(query));
|
|
21535
|
+
if (stopById !== undefined) {
|
|
21536
|
+
stops.push(stopById);
|
|
21537
|
+
}
|
|
21538
|
+
}
|
|
21539
|
+
else {
|
|
21540
|
+
stops = stopsIndex.findStopsByName(query);
|
|
21541
|
+
}
|
|
20418
21542
|
stops.forEach((stop) => {
|
|
20419
|
-
console.log(`${stop.name} (${stop.id})`);
|
|
21543
|
+
console.log(`${stop.name} (${stop.sourceStopId} - ${stop.id})`);
|
|
20420
21544
|
});
|
|
20421
21545
|
this.displayPrompt();
|
|
20422
21546
|
},
|
|
@@ -20439,17 +21563,21 @@ const startRepl = (stopsPath, timetablePath) => {
|
|
|
20439
21563
|
const fromIndex = parts.indexOf('from');
|
|
20440
21564
|
const toIndex = parts.indexOf('to');
|
|
20441
21565
|
const fromId = parts.slice(fromIndex + 1, toIndex).join(' ');
|
|
20442
|
-
const toId = parts
|
|
20443
|
-
.slice(toIndex + 1, withTransfersIndex === -1 ? parts.indexOf('at') : parts.indexOf('at'))
|
|
20444
|
-
.join(' ');
|
|
21566
|
+
const toId = parts.slice(toIndex + 1, parts.indexOf('at')).join(' ');
|
|
20445
21567
|
if (!fromId || !toId || !atTime) {
|
|
20446
21568
|
console.log('Usage: .route from <stationIdOrName> to <stationIdOrName> at <HH:mm> [with <N> transfers]');
|
|
20447
21569
|
this.displayPrompt();
|
|
20448
21570
|
return;
|
|
20449
21571
|
}
|
|
20450
21572
|
const fromStop = stopsIndex.findStopBySourceStopId(fromId) ||
|
|
21573
|
+
(isNaN(Number(fromId))
|
|
21574
|
+
? undefined
|
|
21575
|
+
: stopsIndex.findStopById(Number(fromId))) ||
|
|
20451
21576
|
stopsIndex.findStopsByName(fromId)[0];
|
|
20452
21577
|
const toStop = stopsIndex.findStopBySourceStopId(toId) ||
|
|
21578
|
+
(isNaN(Number(toId))
|
|
21579
|
+
? undefined
|
|
21580
|
+
: stopsIndex.findStopById(Number(toId))) ||
|
|
20453
21581
|
stopsIndex.findStopsByName(toId)[0];
|
|
20454
21582
|
if (!fromStop) {
|
|
20455
21583
|
console.log(`No stop found for 'from' ID or name: ${fromId}`);
|
|
@@ -20465,7 +21593,7 @@ const startRepl = (stopsPath, timetablePath) => {
|
|
|
20465
21593
|
try {
|
|
20466
21594
|
const query = new Query.Builder()
|
|
20467
21595
|
.from(fromStop.sourceStopId)
|
|
20468
|
-
.to(
|
|
21596
|
+
.to(toStop.sourceStopId)
|
|
20469
21597
|
.departureTime(departureTime)
|
|
20470
21598
|
.maxTransfers(maxTransfers)
|
|
20471
21599
|
.build();
|
|
@@ -20476,12 +21604,13 @@ const startRepl = (stopsPath, timetablePath) => {
|
|
|
20476
21604
|
console.log(`Destination not reachable`);
|
|
20477
21605
|
}
|
|
20478
21606
|
else {
|
|
20479
|
-
console.log(`Arriving to ${toStop.name} at ${arrivalTime.
|
|
21607
|
+
console.log(`Arriving to ${toStop.name} at ${arrivalTime.arrival.toString()} with ${arrivalTime.legNumber - 1} transfers from ${(_a = stopsIndex.findStopById(arrivalTime.origin)) === null || _a === void 0 ? void 0 : _a.name}.`);
|
|
20480
21608
|
}
|
|
20481
21609
|
const bestRoute = result.bestRoute(toStop.sourceStopId);
|
|
20482
21610
|
if (bestRoute) {
|
|
20483
21611
|
console.log(`Found route from ${fromStop.name} to ${toStop.name}:`);
|
|
20484
|
-
|
|
21612
|
+
console.log(bestRoute.toString());
|
|
21613
|
+
console.log(JSON.stringify(bestRoute.asJson(), null, 2));
|
|
20485
21614
|
}
|
|
20486
21615
|
else {
|
|
20487
21616
|
console.log('No route found');
|
|
@@ -20543,7 +21672,7 @@ const startRepl = (stopsPath, timetablePath) => {
|
|
|
20543
21672
|
try {
|
|
20544
21673
|
const query = new Query.Builder()
|
|
20545
21674
|
.from(fromStop.sourceStopId)
|
|
20546
|
-
.to(
|
|
21675
|
+
.to(toStop.sourceStopId)
|
|
20547
21676
|
.departureTime(departureTime)
|
|
20548
21677
|
.maxTransfers(maxTransfers)
|
|
20549
21678
|
.build();
|
|
@@ -20576,7 +21705,7 @@ program
|
|
|
20576
21705
|
.option('-s, --stopsOutputPath <path>', 'Path to output stops file', '/tmp/stops')
|
|
20577
21706
|
.option('-p, --profileName <name>', 'Profile name for GTFS config', 'CH')
|
|
20578
21707
|
.option('-v, --verbose', 'Verbose mode', false)
|
|
20579
|
-
.action((gtfsPath, options) => __awaiter(
|
|
21708
|
+
.action((gtfsPath, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
20580
21709
|
if (options.verbose) {
|
|
20581
21710
|
log.setDefaultLevel(log.levels.INFO);
|
|
20582
21711
|
}
|
|
@@ -20590,12 +21719,12 @@ program
|
|
|
20590
21719
|
}));
|
|
20591
21720
|
program
|
|
20592
21721
|
.command('parse-stops')
|
|
20593
|
-
.description('Parse a GTFS feed and output a
|
|
21722
|
+
.description('Parse a GTFS feed and output a stops file.')
|
|
20594
21723
|
.argument('<gtfsPath>', 'Path to GTFS data')
|
|
20595
21724
|
.option('-s, --outputPath <path>', 'Path to output stops file', '/tmp/stops')
|
|
20596
21725
|
.option('-p, --profileName <name>', 'Profile name for GTFS config', 'CH')
|
|
20597
21726
|
.option('-v, --verbose', 'Verbose mode', false)
|
|
20598
|
-
.action((gtfsPath, options) => __awaiter(
|
|
21727
|
+
.action((gtfsPath, options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
20599
21728
|
if (options.verbose) {
|
|
20600
21729
|
log.setDefaultLevel(log.levels.INFO);
|
|
20601
21730
|
}
|
|
@@ -20614,5 +21743,20 @@ program
|
|
|
20614
21743
|
.action((options) => {
|
|
20615
21744
|
startRepl(options.stopsPath, options.timetablePath);
|
|
20616
21745
|
});
|
|
21746
|
+
program
|
|
21747
|
+
.command('perf')
|
|
21748
|
+
.description('Evaluate the performance of the router on a set of routes.')
|
|
21749
|
+
.argument('<routesPath>', 'Path to the JSON file containing the routes')
|
|
21750
|
+
.option('-s, --stopsPath <path>', 'Path to the stops file', '/tmp/stops')
|
|
21751
|
+
.option('-t, --timetablePath <path>', 'Path to the timetable file', '/tmp/timetable')
|
|
21752
|
+
.option('-i, --iterations <number>', 'Number of iterations for performance tests', '20')
|
|
21753
|
+
.action((routesPath, options) => {
|
|
21754
|
+
const stopsIndex = StopsIndex.fromData(fs.readFileSync(options.stopsPath));
|
|
21755
|
+
const timetable = Timetable.fromData(fs.readFileSync(options.timetablePath));
|
|
21756
|
+
const router = new Router(timetable, stopsIndex);
|
|
21757
|
+
const queries = loadQueriesFromJson(routesPath);
|
|
21758
|
+
const performanceResults = testRouterPerformance(router, stopsIndex, queries, parseInt(options.iterations, 10));
|
|
21759
|
+
prettyPrintPerformanceResults(performanceResults);
|
|
21760
|
+
});
|
|
20617
21761
|
program.parse(process.argv);
|
|
20618
21762
|
//# sourceMappingURL=cli.mjs.map
|