@tomgiee/tsdp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/dist/src/builder/media-builder.d.ts +221 -0
  4. package/dist/src/builder/media-builder.d.ts.map +1 -0
  5. package/dist/src/builder/media-builder.js +385 -0
  6. package/dist/src/builder/session-builder.d.ts +195 -0
  7. package/dist/src/builder/session-builder.d.ts.map +1 -0
  8. package/dist/src/builder/session-builder.js +366 -0
  9. package/dist/src/index.d.ts +67 -0
  10. package/dist/src/index.d.ts.map +1 -0
  11. package/dist/src/index.js +250 -0
  12. package/dist/src/parser/attribute-parser.d.ts +100 -0
  13. package/dist/src/parser/attribute-parser.d.ts.map +1 -0
  14. package/dist/src/parser/attribute-parser.js +217 -0
  15. package/dist/src/parser/field-parser.d.ts +124 -0
  16. package/dist/src/parser/field-parser.d.ts.map +1 -0
  17. package/dist/src/parser/field-parser.js +335 -0
  18. package/dist/src/parser/media-parser.d.ts +45 -0
  19. package/dist/src/parser/media-parser.d.ts.map +1 -0
  20. package/dist/src/parser/media-parser.js +157 -0
  21. package/dist/src/parser/primitive-parser.d.ts +138 -0
  22. package/dist/src/parser/primitive-parser.d.ts.map +1 -0
  23. package/dist/src/parser/primitive-parser.js +316 -0
  24. package/dist/src/parser/scanner.d.ts +142 -0
  25. package/dist/src/parser/scanner.d.ts.map +1 -0
  26. package/dist/src/parser/scanner.js +284 -0
  27. package/dist/src/parser/session-parser.d.ts +35 -0
  28. package/dist/src/parser/session-parser.d.ts.map +1 -0
  29. package/dist/src/parser/session-parser.js +207 -0
  30. package/dist/src/parser/time-parser.d.ts +74 -0
  31. package/dist/src/parser/time-parser.d.ts.map +1 -0
  32. package/dist/src/parser/time-parser.js +168 -0
  33. package/dist/src/serializer/attribute-serializer.d.ts +18 -0
  34. package/dist/src/serializer/attribute-serializer.d.ts.map +1 -0
  35. package/dist/src/serializer/attribute-serializer.js +34 -0
  36. package/dist/src/serializer/field-serializer.d.ts +112 -0
  37. package/dist/src/serializer/field-serializer.d.ts.map +1 -0
  38. package/dist/src/serializer/field-serializer.js +212 -0
  39. package/dist/src/serializer/media-serializer.d.ts +31 -0
  40. package/dist/src/serializer/media-serializer.d.ts.map +1 -0
  41. package/dist/src/serializer/media-serializer.js +83 -0
  42. package/dist/src/serializer/session-serializer.d.ts +29 -0
  43. package/dist/src/serializer/session-serializer.d.ts.map +1 -0
  44. package/dist/src/serializer/session-serializer.js +99 -0
  45. package/dist/src/serializer/time-serializer.d.ts +46 -0
  46. package/dist/src/serializer/time-serializer.d.ts.map +1 -0
  47. package/dist/src/serializer/time-serializer.js +86 -0
  48. package/dist/src/types/attributes.d.ts +318 -0
  49. package/dist/src/types/attributes.d.ts.map +1 -0
  50. package/dist/src/types/attributes.js +225 -0
  51. package/dist/src/types/errors.d.ts +129 -0
  52. package/dist/src/types/errors.d.ts.map +1 -0
  53. package/dist/src/types/errors.js +186 -0
  54. package/dist/src/types/fields.d.ts +100 -0
  55. package/dist/src/types/fields.d.ts.map +1 -0
  56. package/dist/src/types/fields.js +48 -0
  57. package/dist/src/types/media.d.ts +148 -0
  58. package/dist/src/types/media.d.ts.map +1 -0
  59. package/dist/src/types/media.js +137 -0
  60. package/dist/src/types/network.d.ts +136 -0
  61. package/dist/src/types/network.d.ts.map +1 -0
  62. package/dist/src/types/network.js +130 -0
  63. package/dist/src/types/primitives.d.ts +193 -0
  64. package/dist/src/types/primitives.d.ts.map +1 -0
  65. package/dist/src/types/primitives.js +195 -0
  66. package/dist/src/types/session.d.ts +122 -0
  67. package/dist/src/types/session.d.ts.map +1 -0
  68. package/dist/src/types/session.js +81 -0
  69. package/dist/src/types/time.d.ts +129 -0
  70. package/dist/src/types/time.d.ts.map +1 -0
  71. package/dist/src/types/time.js +84 -0
  72. package/dist/src/utils/address-parser.d.ts +100 -0
  73. package/dist/src/utils/address-parser.d.ts.map +1 -0
  74. package/dist/src/utils/address-parser.js +338 -0
  75. package/dist/src/utils/format-validators.d.ts +77 -0
  76. package/dist/src/utils/format-validators.d.ts.map +1 -0
  77. package/dist/src/utils/format-validators.js +504 -0
  78. package/dist/src/utils/line-reader.d.ts +84 -0
  79. package/dist/src/utils/line-reader.d.ts.map +1 -0
  80. package/dist/src/utils/line-reader.js +169 -0
  81. package/dist/src/utils/time-converter.d.ts +99 -0
  82. package/dist/src/utils/time-converter.d.ts.map +1 -0
  83. package/dist/src/utils/time-converter.js +195 -0
  84. package/dist/src/validator/media-validator.d.ts +27 -0
  85. package/dist/src/validator/media-validator.d.ts.map +1 -0
  86. package/dist/src/validator/media-validator.js +241 -0
  87. package/dist/src/validator/semantic-validator.d.ts +47 -0
  88. package/dist/src/validator/semantic-validator.d.ts.map +1 -0
  89. package/dist/src/validator/semantic-validator.js +207 -0
  90. package/dist/src/validator/session-validator.d.ts +36 -0
  91. package/dist/src/validator/session-validator.d.ts.map +1 -0
  92. package/dist/src/validator/session-validator.js +280 -0
  93. package/dist/tests/integration/round-trip.test.d.ts +5 -0
  94. package/dist/tests/integration/round-trip.test.d.ts.map +1 -0
  95. package/dist/tests/integration/round-trip.test.js +320 -0
  96. package/dist/tests/integration/voip-examples.test.d.ts +5 -0
  97. package/dist/tests/integration/voip-examples.test.d.ts.map +1 -0
  98. package/dist/tests/integration/voip-examples.test.js +361 -0
  99. package/dist/tests/unit/builder/media-builder.test.d.ts +5 -0
  100. package/dist/tests/unit/builder/media-builder.test.d.ts.map +1 -0
  101. package/dist/tests/unit/builder/media-builder.test.js +524 -0
  102. package/dist/tests/unit/builder/session-builder.test.d.ts +5 -0
  103. package/dist/tests/unit/builder/session-builder.test.d.ts.map +1 -0
  104. package/dist/tests/unit/builder/session-builder.test.js +367 -0
  105. package/dist/tests/unit/parser/attribute-parser.test.d.ts +5 -0
  106. package/dist/tests/unit/parser/attribute-parser.test.d.ts.map +1 -0
  107. package/dist/tests/unit/parser/attribute-parser.test.js +319 -0
  108. package/dist/tests/unit/parser/field-parser.test.d.ts +5 -0
  109. package/dist/tests/unit/parser/field-parser.test.d.ts.map +1 -0
  110. package/dist/tests/unit/parser/field-parser.test.js +355 -0
  111. package/dist/tests/unit/parser/media-parser.test.d.ts +5 -0
  112. package/dist/tests/unit/parser/media-parser.test.d.ts.map +1 -0
  113. package/dist/tests/unit/parser/media-parser.test.js +241 -0
  114. package/dist/tests/unit/parser/primitive-parser.test.d.ts +5 -0
  115. package/dist/tests/unit/parser/primitive-parser.test.d.ts.map +1 -0
  116. package/dist/tests/unit/parser/primitive-parser.test.js +261 -0
  117. package/dist/tests/unit/parser/scanner.test.d.ts +5 -0
  118. package/dist/tests/unit/parser/scanner.test.d.ts.map +1 -0
  119. package/dist/tests/unit/parser/scanner.test.js +241 -0
  120. package/dist/tests/unit/parser/session-parser.test.d.ts +5 -0
  121. package/dist/tests/unit/parser/session-parser.test.d.ts.map +1 -0
  122. package/dist/tests/unit/parser/session-parser.test.js +346 -0
  123. package/dist/tests/unit/parser/time-parser.test.d.ts +5 -0
  124. package/dist/tests/unit/parser/time-parser.test.d.ts.map +1 -0
  125. package/dist/tests/unit/parser/time-parser.test.js +173 -0
  126. package/dist/tests/unit/serializer/attribute-serializer.test.d.ts +5 -0
  127. package/dist/tests/unit/serializer/attribute-serializer.test.d.ts.map +1 -0
  128. package/dist/tests/unit/serializer/attribute-serializer.test.js +78 -0
  129. package/dist/tests/unit/serializer/field-serializer.test.d.ts +5 -0
  130. package/dist/tests/unit/serializer/field-serializer.test.d.ts.map +1 -0
  131. package/dist/tests/unit/serializer/field-serializer.test.js +159 -0
  132. package/dist/tests/unit/serializer/media-serializer.test.d.ts +5 -0
  133. package/dist/tests/unit/serializer/media-serializer.test.d.ts.map +1 -0
  134. package/dist/tests/unit/serializer/media-serializer.test.js +155 -0
  135. package/dist/tests/unit/serializer/session-serializer.test.d.ts +5 -0
  136. package/dist/tests/unit/serializer/session-serializer.test.d.ts.map +1 -0
  137. package/dist/tests/unit/serializer/session-serializer.test.js +317 -0
  138. package/dist/tests/unit/serializer/time-serializer.test.d.ts +5 -0
  139. package/dist/tests/unit/serializer/time-serializer.test.d.ts.map +1 -0
  140. package/dist/tests/unit/serializer/time-serializer.test.js +115 -0
  141. package/dist/tests/unit/types/errors.test.d.ts +5 -0
  142. package/dist/tests/unit/types/errors.test.d.ts.map +1 -0
  143. package/dist/tests/unit/types/errors.test.js +127 -0
  144. package/dist/tests/unit/types/network.test.d.ts +5 -0
  145. package/dist/tests/unit/types/network.test.d.ts.map +1 -0
  146. package/dist/tests/unit/types/network.test.js +132 -0
  147. package/dist/tests/unit/types/primitives.test.d.ts +5 -0
  148. package/dist/tests/unit/types/primitives.test.d.ts.map +1 -0
  149. package/dist/tests/unit/types/primitives.test.js +108 -0
  150. package/dist/tests/unit/utils/address-parser.test.d.ts +5 -0
  151. package/dist/tests/unit/utils/address-parser.test.d.ts.map +1 -0
  152. package/dist/tests/unit/utils/address-parser.test.js +203 -0
  153. package/dist/tests/unit/utils/format-validators.test.d.ts +5 -0
  154. package/dist/tests/unit/utils/format-validators.test.d.ts.map +1 -0
  155. package/dist/tests/unit/utils/format-validators.test.js +224 -0
  156. package/dist/tests/unit/utils/line-reader.test.d.ts +5 -0
  157. package/dist/tests/unit/utils/line-reader.test.d.ts.map +1 -0
  158. package/dist/tests/unit/utils/line-reader.test.js +157 -0
  159. package/dist/tests/unit/utils/time-converter.test.d.ts +5 -0
  160. package/dist/tests/unit/utils/time-converter.test.d.ts.map +1 -0
  161. package/dist/tests/unit/utils/time-converter.test.js +190 -0
  162. package/dist/tests/unit/validator/media-validator.test.d.ts +5 -0
  163. package/dist/tests/unit/validator/media-validator.test.d.ts.map +1 -0
  164. package/dist/tests/unit/validator/media-validator.test.js +313 -0
  165. package/dist/tests/unit/validator/semantic-validator.test.d.ts +5 -0
  166. package/dist/tests/unit/validator/semantic-validator.test.d.ts.map +1 -0
  167. package/dist/tests/unit/validator/semantic-validator.test.js +262 -0
  168. package/dist/tests/unit/validator/session-validator.test.d.ts +5 -0
  169. package/dist/tests/unit/validator/session-validator.test.d.ts.map +1 -0
  170. package/dist/tests/unit/validator/session-validator.test.js +447 -0
  171. package/package.json +50 -0
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Time description types for SDP (RFC 8866)
3
+ *
4
+ * Defines types for timing, repeat times, and timezone adjustments.
5
+ */
6
+ import { NtpTime, TypedTime } from './primitives';
7
+ /**
8
+ * Time Description (RFC 8866 Section 5.9-5.11)
9
+ *
10
+ * A time description consists of:
11
+ * - One timing field (t=)
12
+ * - Zero or more repeat fields (r=)
13
+ * - Zero or one timezone field (z=)
14
+ *
15
+ * At least one time description MUST be present in every session description.
16
+ */
17
+ export interface TimeDescription {
18
+ /**
19
+ * Timing field (required)
20
+ */
21
+ readonly timing: Timing;
22
+ /**
23
+ * Repeat fields (optional, multiple allowed)
24
+ */
25
+ readonly repeats: readonly Repeat[];
26
+ /**
27
+ * Timezone field (optional, only one allowed)
28
+ */
29
+ readonly timezone?: Timezone;
30
+ }
31
+ /**
32
+ * Timing field (RFC 8866 Section 5.9)
33
+ *
34
+ * Format: t=<start-time> <stop-time>
35
+ *
36
+ * Specifies the start and stop times for a session.
37
+ * Times are in NTP format (seconds since Jan 1, 1900 00:00:00 UTC).
38
+ * A value of 0 means the session is unbounded or permanent.
39
+ */
40
+ export interface Timing {
41
+ /**
42
+ * Start time of the session (NTP timestamp)
43
+ * 0 means the session is not bounded
44
+ */
45
+ readonly startTime: NtpTime;
46
+ /**
47
+ * Stop time of the session (NTP timestamp)
48
+ * 0 means the session is not bounded
49
+ */
50
+ readonly stopTime: NtpTime;
51
+ }
52
+ /**
53
+ * Repeat field (RFC 8866 Section 5.10)
54
+ *
55
+ * Format: r=<repeat-interval> <active-duration> <offsets-from-start-time>
56
+ *
57
+ * Specifies repeat times for a session.
58
+ * For example, a session that repeats daily for 1 hour starting at 10am.
59
+ */
60
+ export interface Repeat {
61
+ /**
62
+ * Interval between repetitions
63
+ * For example: 7d for weekly, 1d for daily
64
+ */
65
+ readonly interval: TypedTime;
66
+ /**
67
+ * Duration of each repetition
68
+ * For example: 1h for one hour
69
+ */
70
+ readonly duration: TypedTime;
71
+ /**
72
+ * Offsets from the start time when the session becomes active
73
+ * For example: [0, 3600] for sessions at start time and 1 hour later
74
+ */
75
+ readonly offsets: readonly TypedTime[];
76
+ }
77
+ /**
78
+ * Timezone field (RFC 8866 Section 5.11)
79
+ *
80
+ * Format: z=<adjustment-time> <offset> [<adjustment-time> <offset> ...]
81
+ *
82
+ * Specifies timezone adjustments for repeat sessions.
83
+ * Useful for dealing with daylight saving time changes.
84
+ */
85
+ export interface Timezone {
86
+ /**
87
+ * List of timezone adjustments
88
+ */
89
+ readonly adjustments: readonly TimezoneAdjustment[];
90
+ }
91
+ /**
92
+ * A single timezone adjustment
93
+ */
94
+ export interface TimezoneAdjustment {
95
+ /**
96
+ * Time at which the adjustment takes effect (NTP timestamp)
97
+ */
98
+ readonly time: NtpTime;
99
+ /**
100
+ * Offset to apply at the adjustment time
101
+ * Positive for forward adjustment, negative for backward
102
+ */
103
+ readonly offset: TypedTime;
104
+ }
105
+ /**
106
+ * Create a Timing field
107
+ */
108
+ export declare function createTiming(startTime: number, stopTime: number): Timing;
109
+ /**
110
+ * Create a permanent/unbounded Timing (0 to 0)
111
+ */
112
+ export declare function createPermanentTiming(): Timing;
113
+ /**
114
+ * Create a Repeat field
115
+ */
116
+ export declare function createRepeat(interval: TypedTime, duration: TypedTime, offsets: TypedTime[]): Repeat;
117
+ /**
118
+ * Create a TimezoneAdjustment
119
+ */
120
+ export declare function createTimezoneAdjustment(time: number, offset: TypedTime): TimezoneAdjustment;
121
+ /**
122
+ * Create a Timezone field
123
+ */
124
+ export declare function createTimezone(adjustments: TimezoneAdjustment[]): Timezone;
125
+ /**
126
+ * Create a TimeDescription
127
+ */
128
+ export declare function createTimeDescription(timing: Timing, repeats?: Repeat[], timezone?: Timezone): TimeDescription;
129
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../../src/types/time.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,SAAS,EAAkC,MAAM,cAAc,CAAC;AAMlF;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAEpC;;OAEG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;CAC9B;AAMD;;;;;;;;GAQG;AACH,MAAM,WAAW,MAAM;IACrB;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAE5B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAMD;;;;;;;GAOG;AACH,MAAM,WAAW,MAAM;IACrB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAE7B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC;IAE7B;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;CACxC;AAMD;;;;;;;GAOG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACrD;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAEvB;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;CAC5B;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAWxE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAK9C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,SAAS,EACnB,QAAQ,EAAE,SAAS,EACnB,OAAO,EAAE,SAAS,EAAE,GACnB,MAAM,CASR;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,kBAAkB,CAK5F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,kBAAkB,EAAE,GAAG,QAAQ,CAO1E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAM,EAAO,EACtB,QAAQ,CAAC,EAAE,QAAQ,GAClB,eAAe,CAMjB"}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ /**
3
+ * Time description types for SDP (RFC 8866)
4
+ *
5
+ * Defines types for timing, repeat times, and timezone adjustments.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.createTiming = createTiming;
9
+ exports.createPermanentTiming = createPermanentTiming;
10
+ exports.createRepeat = createRepeat;
11
+ exports.createTimezoneAdjustment = createTimezoneAdjustment;
12
+ exports.createTimezone = createTimezone;
13
+ exports.createTimeDescription = createTimeDescription;
14
+ const primitives_1 = require("./primitives");
15
+ // ============================================================================
16
+ // Helper Functions for Creating Time Types
17
+ // ============================================================================
18
+ /**
19
+ * Create a Timing field
20
+ */
21
+ function createTiming(startTime, stopTime) {
22
+ if (startTime < 0 || stopTime < 0) {
23
+ throw new Error('NTP times must be non-negative');
24
+ }
25
+ if (startTime !== 0 && stopTime !== 0 && startTime > stopTime) {
26
+ throw new Error('Start time must be before or equal to stop time');
27
+ }
28
+ return {
29
+ startTime: (0, primitives_1.createNtpTime)(startTime),
30
+ stopTime: (0, primitives_1.createNtpTime)(stopTime),
31
+ };
32
+ }
33
+ /**
34
+ * Create a permanent/unbounded Timing (0 to 0)
35
+ */
36
+ function createPermanentTiming() {
37
+ return {
38
+ startTime: (0, primitives_1.createNtpTime)(0),
39
+ stopTime: (0, primitives_1.createNtpTime)(0),
40
+ };
41
+ }
42
+ /**
43
+ * Create a Repeat field
44
+ */
45
+ function createRepeat(interval, duration, offsets) {
46
+ if (offsets.length === 0) {
47
+ throw new Error('At least one offset is required for repeat');
48
+ }
49
+ return {
50
+ interval,
51
+ duration,
52
+ offsets,
53
+ };
54
+ }
55
+ /**
56
+ * Create a TimezoneAdjustment
57
+ */
58
+ function createTimezoneAdjustment(time, offset) {
59
+ return {
60
+ time: (0, primitives_1.createNtpTime)(time),
61
+ offset,
62
+ };
63
+ }
64
+ /**
65
+ * Create a Timezone field
66
+ */
67
+ function createTimezone(adjustments) {
68
+ if (adjustments.length === 0) {
69
+ throw new Error('At least one adjustment is required for timezone');
70
+ }
71
+ return {
72
+ adjustments,
73
+ };
74
+ }
75
+ /**
76
+ * Create a TimeDescription
77
+ */
78
+ function createTimeDescription(timing, repeats = [], timezone) {
79
+ return {
80
+ timing,
81
+ repeats,
82
+ timezone,
83
+ };
84
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Network address parsing and validation utilities (RFC 8866)
3
+ *
4
+ * Handles IPv4, IPv6 (RFC 5952), and FQDN (RFC 1035) address formats
5
+ */
6
+ import { IP4Address, IP6Address, FQDN, UnicastAddress } from '../types/network';
7
+ import { AddrType } from '../types/primitives';
8
+ /**
9
+ * Check if a string is a valid IPv4 address
10
+ *
11
+ * Format: a.b.c.d where each octet is 0-255
12
+ *
13
+ * @param input - String to check
14
+ * @returns true if valid IPv4 address
15
+ */
16
+ export declare function isIP4Address(input: string): boolean;
17
+ /**
18
+ * Check if an IPv4 address is multicast (224.0.0.0 - 239.255.255.255)
19
+ *
20
+ * @param input - IPv4 address string
21
+ * @returns true if multicast address
22
+ */
23
+ export declare function isIP4MulticastAddress(input: string): boolean;
24
+ /**
25
+ * Parse an IPv4 address string
26
+ *
27
+ * @param input - IPv4 address string
28
+ * @returns IP4Address
29
+ * @throws ParseError if invalid
30
+ */
31
+ export declare function parseIP4Address(input: string): IP4Address;
32
+ /**
33
+ * Check if a string is a valid IPv6 address (RFC 5952)
34
+ *
35
+ * Supports all IPv6 formats:
36
+ * - Full form: 2001:0db8:0000:0000:0000:0000:0000:0001
37
+ * - Compressed: 2001:db8::1
38
+ * - Mixed IPv4: ::ffff:192.0.2.1
39
+ *
40
+ * @param input - String to check
41
+ * @returns true if valid IPv6 address
42
+ */
43
+ export declare function isIP6Address(input: string): boolean;
44
+ /**
45
+ * Check if an IPv6 address is multicast (starts with FF)
46
+ *
47
+ * @param input - IPv6 address string
48
+ * @returns true if multicast address
49
+ */
50
+ export declare function isIP6MulticastAddress(input: string): boolean;
51
+ /**
52
+ * Parse an IPv6 address string
53
+ *
54
+ * @param input - IPv6 address string
55
+ * @returns IP6Address
56
+ * @throws ParseError if invalid
57
+ */
58
+ export declare function parseIP6Address(input: string): IP6Address;
59
+ /**
60
+ * Check if a string is a valid FQDN (RFC 1035)
61
+ *
62
+ * Rules:
63
+ * - Labels separated by dots
64
+ * - Each label: 1-63 characters
65
+ * - Label format: alphanumeric + hyphens, must start/end with alphanumeric
66
+ * - Total length <= 253 characters
67
+ *
68
+ * @param input - String to check
69
+ * @returns true if valid FQDN
70
+ */
71
+ export declare function isFQDN(input: string): boolean;
72
+ /**
73
+ * Parse an FQDN string
74
+ *
75
+ * @param input - FQDN string
76
+ * @returns FQDN
77
+ * @throws ParseError if invalid
78
+ */
79
+ export declare function parseFQDN(input: string): FQDN;
80
+ /**
81
+ * Parse a unicast address based on address type
82
+ *
83
+ * For IP4: IPv4 address or FQDN
84
+ * For IP6: IPv6 address or FQDN
85
+ * For other: Extension address
86
+ *
87
+ * @param input - Address string
88
+ * @param addrType - Address type (IP4, IP6, or extension)
89
+ * @returns UnicastAddress
90
+ * @throws ParseError if invalid for the given address type
91
+ */
92
+ export declare function parseUnicastAddress(input: string, addrType: AddrType): UnicastAddress;
93
+ /**
94
+ * Detect the type of an address string
95
+ *
96
+ * @param input - Address string
97
+ * @returns AddrType ('IP4', 'IP6', or 'extension')
98
+ */
99
+ export declare function detectAddressType(input: string): AddrType;
100
+ //# sourceMappingURL=address-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"address-parser.d.ts","sourceRoot":"","sources":["../../../src/utils/address-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,UAAU,EACV,UAAU,EACV,IAAI,EAEJ,cAAc,EAKf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAO/C;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CA4BnD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAU5D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAKzD;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAsFnD;AAeD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAoB5D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAKzD;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CA4B7C;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAK7C;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,cAAc,CAkCrF;AAMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,CAgBzD"}
@@ -0,0 +1,338 @@
1
+ "use strict";
2
+ /**
3
+ * Network address parsing and validation utilities (RFC 8866)
4
+ *
5
+ * Handles IPv4, IPv6 (RFC 5952), and FQDN (RFC 1035) address formats
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.isIP4Address = isIP4Address;
9
+ exports.isIP4MulticastAddress = isIP4MulticastAddress;
10
+ exports.parseIP4Address = parseIP4Address;
11
+ exports.isIP6Address = isIP6Address;
12
+ exports.isIP6MulticastAddress = isIP6MulticastAddress;
13
+ exports.parseIP6Address = parseIP6Address;
14
+ exports.isFQDN = isFQDN;
15
+ exports.parseFQDN = parseFQDN;
16
+ exports.parseUnicastAddress = parseUnicastAddress;
17
+ exports.detectAddressType = detectAddressType;
18
+ const network_1 = require("../types/network");
19
+ const errors_1 = require("../types/errors");
20
+ // ============================================================================
21
+ // IPv4 Address Validation
22
+ // ============================================================================
23
+ /**
24
+ * Check if a string is a valid IPv4 address
25
+ *
26
+ * Format: a.b.c.d where each octet is 0-255
27
+ *
28
+ * @param input - String to check
29
+ * @returns true if valid IPv4 address
30
+ */
31
+ function isIP4Address(input) {
32
+ // Must have exactly 4 octets
33
+ const parts = input.split('.');
34
+ if (parts.length !== 4) {
35
+ return false;
36
+ }
37
+ // Each octet must be 0-255
38
+ return parts.every(part => {
39
+ // Must be numeric
40
+ if (!/^\d+$/.test(part)) {
41
+ return false;
42
+ }
43
+ const num = parseInt(part, 10);
44
+ // Must be 0-255
45
+ if (num < 0 || num > 255) {
46
+ return false;
47
+ }
48
+ // No leading zeros (except for "0" itself)
49
+ if (part.length > 1 && part[0] === '0') {
50
+ return false;
51
+ }
52
+ return true;
53
+ });
54
+ }
55
+ /**
56
+ * Check if an IPv4 address is multicast (224.0.0.0 - 239.255.255.255)
57
+ *
58
+ * @param input - IPv4 address string
59
+ * @returns true if multicast address
60
+ */
61
+ function isIP4MulticastAddress(input) {
62
+ if (!isIP4Address(input)) {
63
+ return false;
64
+ }
65
+ const parts = input.split('.');
66
+ const firstOctet = parseInt(parts[0], 10);
67
+ // Multicast range: 224-239
68
+ return firstOctet >= 224 && firstOctet <= 239;
69
+ }
70
+ /**
71
+ * Parse an IPv4 address string
72
+ *
73
+ * @param input - IPv4 address string
74
+ * @returns IP4Address
75
+ * @throws ParseError if invalid
76
+ */
77
+ function parseIP4Address(input) {
78
+ if (!isIP4Address(input)) {
79
+ throw new errors_1.ParseError(`Invalid IPv4 address: '${input}'`, 0, 0);
80
+ }
81
+ return (0, network_1.createIP4Address)(input);
82
+ }
83
+ // ============================================================================
84
+ // IPv6 Address Validation
85
+ // ============================================================================
86
+ /**
87
+ * Check if a string is a valid IPv6 address (RFC 5952)
88
+ *
89
+ * Supports all IPv6 formats:
90
+ * - Full form: 2001:0db8:0000:0000:0000:0000:0000:0001
91
+ * - Compressed: 2001:db8::1
92
+ * - Mixed IPv4: ::ffff:192.0.2.1
93
+ *
94
+ * @param input - String to check
95
+ * @returns true if valid IPv6 address
96
+ */
97
+ function isIP6Address(input) {
98
+ // IPv6 address validation regex (comprehensive)
99
+ // Handles all forms including compression and mixed IPv4
100
+ // Special case: "::"
101
+ if (input === '::') {
102
+ return true;
103
+ }
104
+ // Check for invalid characters
105
+ if (!/^[0-9a-fA-F:.]+$/.test(input)) {
106
+ return false;
107
+ }
108
+ // Can have at most one occurrence of "::"
109
+ const doubleColonCount = (input.match(/::/g) || []).length;
110
+ if (doubleColonCount > 1) {
111
+ return false;
112
+ }
113
+ // Split by :: to handle compression
114
+ if (input.includes('::')) {
115
+ const parts = input.split('::');
116
+ if (parts.length !== 2) {
117
+ return false;
118
+ }
119
+ const before = parts[0] ? parts[0].split(':') : [];
120
+ const after = parts[1] ? parts[1].split(':') : [];
121
+ // Check for mixed IPv4 in the last part
122
+ const lastPart = after.length > 0 ? after[after.length - 1] : before[before.length - 1];
123
+ const hasMixedIPv4 = lastPart && lastPart.includes('.');
124
+ if (hasMixedIPv4) {
125
+ // Validate IPv4 part
126
+ if (!isIP4Address(lastPart)) {
127
+ return false;
128
+ }
129
+ // Remove IPv4 part for hex validation
130
+ if (after.length > 0) {
131
+ after.pop();
132
+ }
133
+ else {
134
+ before.pop();
135
+ }
136
+ // IPv4 counts as 2 hex groups
137
+ const totalGroups = before.length + after.length + 2;
138
+ if (totalGroups > 8) {
139
+ return false;
140
+ }
141
+ }
142
+ else {
143
+ const totalGroups = before.length + after.length;
144
+ if (totalGroups >= 8) {
145
+ return false; // Compression not needed
146
+ }
147
+ }
148
+ // Validate hex groups
149
+ return [...before, ...after].every(isValidHexGroup);
150
+ }
151
+ else {
152
+ // No compression, must have exactly 8 groups (or 6 + IPv4)
153
+ const groups = input.split(':');
154
+ // Check for mixed IPv4 in last group
155
+ const lastGroup = groups[groups.length - 1];
156
+ if (lastGroup && lastGroup.includes('.')) {
157
+ // Mixed form: 6 hex groups + IPv4
158
+ if (groups.length !== 7) {
159
+ return false;
160
+ }
161
+ if (!isIP4Address(lastGroup)) {
162
+ return false;
163
+ }
164
+ // Validate first 6 hex groups
165
+ return groups.slice(0, 6).every(isValidHexGroup);
166
+ }
167
+ else {
168
+ // Pure IPv6: must have 8 groups
169
+ if (groups.length !== 8) {
170
+ return false;
171
+ }
172
+ return groups.every(isValidHexGroup);
173
+ }
174
+ }
175
+ }
176
+ /**
177
+ * Check if a string is a valid IPv6 hex group (1-4 hex digits)
178
+ *
179
+ * @param group - Hex group string
180
+ * @returns true if valid
181
+ */
182
+ function isValidHexGroup(group) {
183
+ if (group.length === 0 || group.length > 4) {
184
+ return false;
185
+ }
186
+ return /^[0-9a-fA-F]+$/.test(group);
187
+ }
188
+ /**
189
+ * Check if an IPv6 address is multicast (starts with FF)
190
+ *
191
+ * @param input - IPv6 address string
192
+ * @returns true if multicast address
193
+ */
194
+ function isIP6MulticastAddress(input) {
195
+ if (!isIP6Address(input)) {
196
+ return false;
197
+ }
198
+ // Normalize :: compression for checking
199
+ // Multicast addresses start with FF (binary: 11111111)
200
+ const upperInput = input.toUpperCase();
201
+ // Check if first group starts with FF
202
+ if (upperInput.startsWith('FF')) {
203
+ return true;
204
+ }
205
+ // Check after :: compression
206
+ if (upperInput.startsWith('::FF')) {
207
+ return true;
208
+ }
209
+ return false;
210
+ }
211
+ /**
212
+ * Parse an IPv6 address string
213
+ *
214
+ * @param input - IPv6 address string
215
+ * @returns IP6Address
216
+ * @throws ParseError if invalid
217
+ */
218
+ function parseIP6Address(input) {
219
+ if (!isIP6Address(input)) {
220
+ throw new errors_1.ParseError(`Invalid IPv6 address: '${input}'`, 0, 0);
221
+ }
222
+ return (0, network_1.createIP6Address)(input);
223
+ }
224
+ // ============================================================================
225
+ // FQDN Validation
226
+ // ============================================================================
227
+ /**
228
+ * Check if a string is a valid FQDN (RFC 1035)
229
+ *
230
+ * Rules:
231
+ * - Labels separated by dots
232
+ * - Each label: 1-63 characters
233
+ * - Label format: alphanumeric + hyphens, must start/end with alphanumeric
234
+ * - Total length <= 253 characters
235
+ *
236
+ * @param input - String to check
237
+ * @returns true if valid FQDN
238
+ */
239
+ function isFQDN(input) {
240
+ // Total length limit
241
+ if (input.length === 0 || input.length > 253) {
242
+ return false;
243
+ }
244
+ // Split into labels
245
+ const labels = input.split('.');
246
+ // Each label must be valid
247
+ return labels.every(label => {
248
+ // Label length: 1-63 characters
249
+ if (label.length === 0 || label.length > 63) {
250
+ return false;
251
+ }
252
+ // Must start and end with alphanumeric
253
+ if (!/^[a-zA-Z0-9]/.test(label) || !/[a-zA-Z0-9]$/.test(label)) {
254
+ return false;
255
+ }
256
+ // Can only contain alphanumeric and hyphens
257
+ if (!/^[a-zA-Z0-9-]+$/.test(label)) {
258
+ return false;
259
+ }
260
+ return true;
261
+ });
262
+ }
263
+ /**
264
+ * Parse an FQDN string
265
+ *
266
+ * @param input - FQDN string
267
+ * @returns FQDN
268
+ * @throws ParseError if invalid
269
+ */
270
+ function parseFQDN(input) {
271
+ if (!isFQDN(input)) {
272
+ throw new errors_1.ParseError(`Invalid FQDN: '${input}'`, 0, 0);
273
+ }
274
+ return (0, network_1.createFQDN)(input);
275
+ }
276
+ // ============================================================================
277
+ // Unicast Address Parsing
278
+ // ============================================================================
279
+ /**
280
+ * Parse a unicast address based on address type
281
+ *
282
+ * For IP4: IPv4 address or FQDN
283
+ * For IP6: IPv6 address or FQDN
284
+ * For other: Extension address
285
+ *
286
+ * @param input - Address string
287
+ * @param addrType - Address type (IP4, IP6, or extension)
288
+ * @returns UnicastAddress
289
+ * @throws ParseError if invalid for the given address type
290
+ */
291
+ function parseUnicastAddress(input, addrType) {
292
+ switch (addrType) {
293
+ case 'IP4':
294
+ // Try IPv4 first, then FQDN
295
+ if (isIP4Address(input)) {
296
+ return (0, network_1.createIP4Address)(input);
297
+ }
298
+ if (isFQDN(input)) {
299
+ return (0, network_1.createFQDN)(input);
300
+ }
301
+ throw new errors_1.ParseError(`Invalid address for type IP4: '${input}' (expected IPv4 or FQDN)`, 0, 0);
302
+ case 'IP6':
303
+ // Try IPv6 first, then FQDN
304
+ if (isIP6Address(input)) {
305
+ return (0, network_1.createIP6Address)(input);
306
+ }
307
+ if (isFQDN(input)) {
308
+ return (0, network_1.createFQDN)(input);
309
+ }
310
+ throw new errors_1.ParseError(`Invalid address for type IP6: '${input}' (expected IPv6 or FQDN)`, 0, 0);
311
+ default:
312
+ // Extension address type
313
+ return (0, network_1.createExtensionAddress)(input);
314
+ }
315
+ }
316
+ // ============================================================================
317
+ // Address Type Detection
318
+ // ============================================================================
319
+ /**
320
+ * Detect the type of an address string
321
+ *
322
+ * @param input - Address string
323
+ * @returns AddrType ('IP4', 'IP6', or 'extension')
324
+ */
325
+ function detectAddressType(input) {
326
+ if (isIP4Address(input)) {
327
+ return 'IP4';
328
+ }
329
+ if (isIP6Address(input)) {
330
+ return 'IP6';
331
+ }
332
+ if (isFQDN(input)) {
333
+ // FQDNs can work with both IP4 and IP6, default to IP4
334
+ return 'IP4';
335
+ }
336
+ // Unknown type
337
+ return 'extension';
338
+ }