@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,138 @@
1
+ /**
2
+ * Primitive parsers for SDP (RFC 8866)
3
+ *
4
+ * Parsers for basic grammar elements: tokens, integers, addresses, times, etc.
5
+ */
6
+ import { Scanner } from './scanner';
7
+ import { NtpTime, TypedTime } from '../types/primitives';
8
+ import { IP4Address, IP6Address, FQDN } from '../types/network';
9
+ /**
10
+ * Parse a token (RFC 8866 Section 9)
11
+ *
12
+ * token = 1*token-char
13
+ * token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E
14
+ *
15
+ * Essentially: any visible ASCII character except space and separators like ()[]{}:;,<>?=@\"
16
+ *
17
+ * @param scanner - Scanner instance
18
+ * @returns Parsed token string
19
+ * @throws ParseError if no valid token found
20
+ */
21
+ export declare function parseToken(scanner: Scanner): string;
22
+ /**
23
+ * Parse an integer
24
+ *
25
+ * @param scanner - Scanner instance
26
+ * @returns Parsed integer
27
+ * @throws ParseError if no valid integer found
28
+ */
29
+ export declare function parseInteger(scanner: Scanner): number;
30
+ /**
31
+ * Parse a byte (0-255)
32
+ *
33
+ * @param scanner - Scanner instance
34
+ * @returns Parsed byte value
35
+ * @throws ParseError if not a valid byte
36
+ */
37
+ export declare function parseByte(scanner: Scanner): number;
38
+ /**
39
+ * Parse a non-whitespace string
40
+ *
41
+ * Reads characters until whitespace or end of line.
42
+ *
43
+ * @param scanner - Scanner instance
44
+ * @returns Parsed string
45
+ * @throws ParseError if empty
46
+ */
47
+ export declare function parseNonWSString(scanner: Scanner): string;
48
+ /**
49
+ * Parse a byte-string (RFC 8866 Section 9)
50
+ *
51
+ * byte-string = 1*(%x01-09 / %x0B-0C / %x0E-FF)
52
+ * (any byte except NUL, CR, LF)
53
+ *
54
+ * @param scanner - Scanner instance
55
+ * @returns Parsed byte-string
56
+ * @throws ParseError if invalid characters found
57
+ */
58
+ export declare function parseByteString(scanner: Scanner): string;
59
+ /**
60
+ * Parse an IPv4 address
61
+ *
62
+ * Format: a.b.c.d where each octet is 0-255
63
+ *
64
+ * @param scanner - Scanner instance
65
+ * @returns Parsed IPv4 address
66
+ * @throws ParseError if invalid IPv4 address
67
+ */
68
+ export declare function parseIP4Address(scanner: Scanner): IP4Address;
69
+ /**
70
+ * Parse an IPv6 address
71
+ *
72
+ * Supports all IPv6 formats including compression (::) and mixed IPv4
73
+ *
74
+ * @param scanner - Scanner instance
75
+ * @returns Parsed IPv6 address
76
+ * @throws ParseError if invalid IPv6 address
77
+ */
78
+ export declare function parseIP6Address(scanner: Scanner): IP6Address;
79
+ /**
80
+ * Parse an FQDN (Fully Qualified Domain Name)
81
+ *
82
+ * @param scanner - Scanner instance
83
+ * @returns Parsed FQDN
84
+ * @throws ParseError if invalid FQDN
85
+ */
86
+ export declare function parseFQDN(scanner: Scanner): FQDN;
87
+ /**
88
+ * Parse an NTP timestamp
89
+ *
90
+ * NTP time is seconds since January 1, 1900 00:00:00 UTC
91
+ * 0 means unbounded/permanent
92
+ *
93
+ * @param scanner - Scanner instance
94
+ * @returns Parsed NTP time
95
+ * @throws ParseError if invalid
96
+ */
97
+ export declare function parseNtpTime(scanner: Scanner): NtpTime;
98
+ /**
99
+ * Parse a typed time value
100
+ *
101
+ * Format: [-]<integer>[d|h|m|s]
102
+ * - d = days
103
+ * - h = hours
104
+ * - m = minutes
105
+ * - s or no suffix = seconds
106
+ * - Can be negative (for timezone offsets)
107
+ *
108
+ * @param scanner - Scanner instance
109
+ * @returns Parsed typed time
110
+ * @throws ParseError if invalid
111
+ */
112
+ export declare function parseTypedTime(scanner: Scanner): TypedTime;
113
+ /**
114
+ * Parse an email address or display name with email
115
+ *
116
+ * Format: email-address / displayable-name <email-address>
117
+ *
118
+ * @param scanner - Scanner instance
119
+ * @returns Parsed email string
120
+ */
121
+ export declare function parseEmail(scanner: Scanner): string;
122
+ /**
123
+ * Parse a phone number or display name with phone
124
+ *
125
+ * Format: phone-number / displayable-name <phone-number>
126
+ *
127
+ * @param scanner - Scanner instance
128
+ * @returns Parsed phone string
129
+ */
130
+ export declare function parsePhone(scanner: Scanner): string;
131
+ /**
132
+ * Parse a URI
133
+ *
134
+ * @param scanner - Scanner instance
135
+ * @returns Parsed URI string
136
+ */
137
+ export declare function parseURI(scanner: Scanner): string;
138
+ //# sourceMappingURL=primitive-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitive-parser.d.ts","sourceRoot":"","sources":["../../../src/parser/primitive-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAiB,MAAM,qBAAqB,CAAC;AACxE,OAAO,EACL,UAAU,EACV,UAAU,EACV,IAAI,EAIL,MAAM,kBAAkB,CAAC;AAY1B;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAanD;AAwBD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAuBrD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAalD;AAaD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAazD;AASD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAaxD;AAkBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAe5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,UAAU,CAiB5D;AAaD;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiBhD;AAiBD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAatD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAiB1D;AAMD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAGnD;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAGnD;AAMD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAGjD"}
@@ -0,0 +1,316 @@
1
+ "use strict";
2
+ /**
3
+ * Primitive parsers for SDP (RFC 8866)
4
+ *
5
+ * Parsers for basic grammar elements: tokens, integers, addresses, times, etc.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.parseToken = parseToken;
9
+ exports.parseInteger = parseInteger;
10
+ exports.parseByte = parseByte;
11
+ exports.parseNonWSString = parseNonWSString;
12
+ exports.parseByteString = parseByteString;
13
+ exports.parseIP4Address = parseIP4Address;
14
+ exports.parseIP6Address = parseIP6Address;
15
+ exports.parseFQDN = parseFQDN;
16
+ exports.parseNtpTime = parseNtpTime;
17
+ exports.parseTypedTime = parseTypedTime;
18
+ exports.parseEmail = parseEmail;
19
+ exports.parsePhone = parsePhone;
20
+ exports.parseURI = parseURI;
21
+ const errors_1 = require("../types/errors");
22
+ const primitives_1 = require("../types/primitives");
23
+ const network_1 = require("../types/network");
24
+ const address_parser_1 = require("../utils/address-parser");
25
+ const time_converter_1 = require("../utils/time-converter");
26
+ // ============================================================================
27
+ // Token Parsing
28
+ // ============================================================================
29
+ /**
30
+ * Parse a token (RFC 8866 Section 9)
31
+ *
32
+ * token = 1*token-char
33
+ * token-char = %x21 / %x23-27 / %x2A-2B / %x2D-2E / %x30-39 / %x41-5A / %x5E-7E
34
+ *
35
+ * Essentially: any visible ASCII character except space and separators like ()[]{}:;,<>?=@\"
36
+ *
37
+ * @param scanner - Scanner instance
38
+ * @returns Parsed token string
39
+ * @throws ParseError if no valid token found
40
+ */
41
+ function parseToken(scanner) {
42
+ const start = scanner.getPosition();
43
+ const token = scanner.readWhile(isTokenChar);
44
+ if (token.length === 0) {
45
+ throw new errors_1.ParseError('Expected token', start.line, start.column);
46
+ }
47
+ return token;
48
+ }
49
+ /**
50
+ * Check if a character is a valid token character
51
+ */
52
+ function isTokenChar(ch) {
53
+ const code = ch.charCodeAt(0);
54
+ // token-char per RFC 8866
55
+ return (code === 0x21 || // !
56
+ (code >= 0x23 && code <= 0x27) || // # $ % & '
57
+ (code >= 0x2a && code <= 0x2b) || // * +
58
+ (code >= 0x2d && code <= 0x2e) || // - .
59
+ (code >= 0x30 && code <= 0x39) || // 0-9
60
+ (code >= 0x41 && code <= 0x5a) || // A-Z
61
+ (code >= 0x5e && code <= 0x7e) // ^ _ ` a-z { | } ~
62
+ );
63
+ }
64
+ // ============================================================================
65
+ // Integer Parsing
66
+ // ============================================================================
67
+ /**
68
+ * Parse an integer
69
+ *
70
+ * @param scanner - Scanner instance
71
+ * @returns Parsed integer
72
+ * @throws ParseError if no valid integer found
73
+ */
74
+ function parseInteger(scanner) {
75
+ const start = scanner.getPosition();
76
+ const digits = scanner.readWhile(isDigit);
77
+ if (digits.length === 0) {
78
+ throw new errors_1.ParseError('Expected integer', start.line, start.column);
79
+ }
80
+ const value = parseInt(digits, 10);
81
+ if (isNaN(value)) {
82
+ throw new errors_1.ParseError(`Invalid integer: ${digits}`, start.line, start.column);
83
+ }
84
+ return value;
85
+ }
86
+ /**
87
+ * Parse a byte (0-255)
88
+ *
89
+ * @param scanner - Scanner instance
90
+ * @returns Parsed byte value
91
+ * @throws ParseError if not a valid byte
92
+ */
93
+ function parseByte(scanner) {
94
+ const start = scanner.getPosition();
95
+ const value = parseInteger(scanner);
96
+ if (value < 0 || value > 255) {
97
+ throw new errors_1.ParseError(`Byte value out of range: ${value} (expected 0-255)`, start.line, start.column);
98
+ }
99
+ return value;
100
+ }
101
+ /**
102
+ * Check if character is a digit
103
+ */
104
+ function isDigit(ch) {
105
+ return ch >= '0' && ch <= '9';
106
+ }
107
+ // ============================================================================
108
+ // String Parsing
109
+ // ============================================================================
110
+ /**
111
+ * Parse a non-whitespace string
112
+ *
113
+ * Reads characters until whitespace or end of line.
114
+ *
115
+ * @param scanner - Scanner instance
116
+ * @returns Parsed string
117
+ * @throws ParseError if empty
118
+ */
119
+ function parseNonWSString(scanner) {
120
+ const start = scanner.getPosition();
121
+ const str = scanner.readWhile(isNonWhitespace);
122
+ if (str.length === 0) {
123
+ throw new errors_1.ParseError('Expected non-whitespace string', start.line, start.column);
124
+ }
125
+ return str;
126
+ }
127
+ /**
128
+ * Check if character is non-whitespace
129
+ */
130
+ function isNonWhitespace(ch) {
131
+ return ch !== ' ' && ch !== '\t' && ch !== '\r' && ch !== '\n';
132
+ }
133
+ /**
134
+ * Parse a byte-string (RFC 8866 Section 9)
135
+ *
136
+ * byte-string = 1*(%x01-09 / %x0B-0C / %x0E-FF)
137
+ * (any byte except NUL, CR, LF)
138
+ *
139
+ * @param scanner - Scanner instance
140
+ * @returns Parsed byte-string
141
+ * @throws ParseError if invalid characters found
142
+ */
143
+ function parseByteString(scanner) {
144
+ const start = scanner.getPosition();
145
+ const str = scanner.readWhile(isByteStringChar);
146
+ if (str.length === 0) {
147
+ throw new errors_1.ParseError('Expected byte-string', start.line, start.column);
148
+ }
149
+ return str;
150
+ }
151
+ /**
152
+ * Check if character is valid in byte-string
153
+ */
154
+ function isByteStringChar(ch) {
155
+ const code = ch.charCodeAt(0);
156
+ return ((code >= 0x01 && code <= 0x09) ||
157
+ (code >= 0x0b && code <= 0x0c) ||
158
+ (code >= 0x0e && code <= 0xff));
159
+ }
160
+ // ============================================================================
161
+ // IP Address Parsing
162
+ // ============================================================================
163
+ /**
164
+ * Parse an IPv4 address
165
+ *
166
+ * Format: a.b.c.d where each octet is 0-255
167
+ *
168
+ * @param scanner - Scanner instance
169
+ * @returns Parsed IPv4 address
170
+ * @throws ParseError if invalid IPv4 address
171
+ */
172
+ function parseIP4Address(scanner) {
173
+ const start = scanner.getPosition();
174
+ // Read until whitespace or special characters
175
+ const addr = scanner.readWhile(ch => isDigit(ch) || ch === '.');
176
+ if (!(0, address_parser_1.isIP4Address)(addr)) {
177
+ throw new errors_1.ParseError(`Invalid IPv4 address: ${addr}`, start.line, start.column);
178
+ }
179
+ return (0, network_1.createIP4Address)(addr);
180
+ }
181
+ /**
182
+ * Parse an IPv6 address
183
+ *
184
+ * Supports all IPv6 formats including compression (::) and mixed IPv4
185
+ *
186
+ * @param scanner - Scanner instance
187
+ * @returns Parsed IPv6 address
188
+ * @throws ParseError if invalid IPv6 address
189
+ */
190
+ function parseIP6Address(scanner) {
191
+ const start = scanner.getPosition();
192
+ // Read until whitespace (IPv6 can contain : and . and hex digits)
193
+ const addr = scanner.readWhile(ch => isHexDigit(ch) || ch === ':' || ch === '.');
194
+ if (!(0, address_parser_1.isIP6Address)(addr)) {
195
+ throw new errors_1.ParseError(`Invalid IPv6 address: ${addr}`, start.line, start.column);
196
+ }
197
+ return (0, network_1.createIP6Address)(addr);
198
+ }
199
+ /**
200
+ * Check if character is a hexadecimal digit
201
+ */
202
+ function isHexDigit(ch) {
203
+ return ((ch >= '0' && ch <= '9') ||
204
+ (ch >= 'a' && ch <= 'f') ||
205
+ (ch >= 'A' && ch <= 'F'));
206
+ }
207
+ /**
208
+ * Parse an FQDN (Fully Qualified Domain Name)
209
+ *
210
+ * @param scanner - Scanner instance
211
+ * @returns Parsed FQDN
212
+ * @throws ParseError if invalid FQDN
213
+ */
214
+ function parseFQDN(scanner) {
215
+ const start = scanner.getPosition();
216
+ // Read until whitespace (FQDN can contain alphanumeric, -, and .)
217
+ const fqdn = scanner.readWhile(ch => isAlphanumeric(ch) || ch === '-' || ch === '.');
218
+ if (!(0, address_parser_1.isFQDN)(fqdn)) {
219
+ throw new errors_1.ParseError(`Invalid FQDN: ${fqdn}`, start.line, start.column);
220
+ }
221
+ return (0, network_1.createFQDN)(fqdn);
222
+ }
223
+ /**
224
+ * Check if character is alphanumeric
225
+ */
226
+ function isAlphanumeric(ch) {
227
+ return ((ch >= '0' && ch <= '9') ||
228
+ (ch >= 'a' && ch <= 'z') ||
229
+ (ch >= 'A' && ch <= 'Z'));
230
+ }
231
+ // ============================================================================
232
+ // Time Parsing
233
+ // ============================================================================
234
+ /**
235
+ * Parse an NTP timestamp
236
+ *
237
+ * NTP time is seconds since January 1, 1900 00:00:00 UTC
238
+ * 0 means unbounded/permanent
239
+ *
240
+ * @param scanner - Scanner instance
241
+ * @returns Parsed NTP time
242
+ * @throws ParseError if invalid
243
+ */
244
+ function parseNtpTime(scanner) {
245
+ const start = scanner.getPosition();
246
+ const value = parseInteger(scanner);
247
+ if (value < 0) {
248
+ throw new errors_1.ParseError(`NTP time cannot be negative: ${value}`, start.line, start.column);
249
+ }
250
+ return (0, primitives_1.createNtpTime)(value);
251
+ }
252
+ /**
253
+ * Parse a typed time value
254
+ *
255
+ * Format: [-]<integer>[d|h|m|s]
256
+ * - d = days
257
+ * - h = hours
258
+ * - m = minutes
259
+ * - s or no suffix = seconds
260
+ * - Can be negative (for timezone offsets)
261
+ *
262
+ * @param scanner - Scanner instance
263
+ * @returns Parsed typed time
264
+ * @throws ParseError if invalid
265
+ */
266
+ function parseTypedTime(scanner) {
267
+ const start = scanner.getPosition();
268
+ // Read optional minus sign, digits, and optional unit suffix
269
+ const timeStr = scanner.readWhile(ch => ch === '-' || isDigit(ch) || ch === 'd' || ch === 'h' || ch === 'm' || ch === 's');
270
+ try {
271
+ return (0, time_converter_1.parseTypedTime)(timeStr);
272
+ }
273
+ catch (error) {
274
+ throw new errors_1.ParseError(`Invalid typed time: ${timeStr}`, start.line, start.column);
275
+ }
276
+ }
277
+ // ============================================================================
278
+ // Email and Phone Parsing
279
+ // ============================================================================
280
+ /**
281
+ * Parse an email address or display name with email
282
+ *
283
+ * Format: email-address / displayable-name <email-address>
284
+ *
285
+ * @param scanner - Scanner instance
286
+ * @returns Parsed email string
287
+ */
288
+ function parseEmail(scanner) {
289
+ // Email can contain most characters, read until end of line
290
+ return scanner.readUntil(/[\r\n]/);
291
+ }
292
+ /**
293
+ * Parse a phone number or display name with phone
294
+ *
295
+ * Format: phone-number / displayable-name <phone-number>
296
+ *
297
+ * @param scanner - Scanner instance
298
+ * @returns Parsed phone string
299
+ */
300
+ function parsePhone(scanner) {
301
+ // Phone can contain most characters, read until end of line
302
+ return scanner.readUntil(/[\r\n]/);
303
+ }
304
+ // ============================================================================
305
+ // URI Parsing
306
+ // ============================================================================
307
+ /**
308
+ * Parse a URI
309
+ *
310
+ * @param scanner - Scanner instance
311
+ * @returns Parsed URI string
312
+ */
313
+ function parseURI(scanner) {
314
+ // URI can contain many characters, read until end of line
315
+ return scanner.readUntil(/[\r\n]/);
316
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Scanner for SDP parsing (RFC 8866)
3
+ *
4
+ * Provides low-level tokenization and character-level operations
5
+ * with position tracking for detailed error reporting.
6
+ */
7
+ import { Position } from '../types/primitives';
8
+ /**
9
+ * Scanner class for parsing SDP text
10
+ *
11
+ * Maintains position information and provides character-level operations
12
+ * for parsing SDP fields according to RFC 8866 grammar.
13
+ */
14
+ export declare class Scanner {
15
+ private readonly input;
16
+ private position;
17
+ private line;
18
+ private column;
19
+ /**
20
+ * Create a new Scanner
21
+ *
22
+ * @param input - SDP text to scan
23
+ */
24
+ constructor(input: string);
25
+ /**
26
+ * Peek at the current character without consuming it
27
+ *
28
+ * @returns Current character or null if at EOF
29
+ */
30
+ peek(): string | null;
31
+ /**
32
+ * Peek ahead n characters without consuming
33
+ *
34
+ * @param n - Number of characters to peek ahead
35
+ * @returns Character at position+n or null if beyond EOF
36
+ */
37
+ peekAhead(n: number): string | null;
38
+ /**
39
+ * Advance to the next character and return the current one
40
+ *
41
+ * Updates line and column tracking when encountering newlines.
42
+ *
43
+ * @returns Current character or null if at EOF
44
+ */
45
+ advance(): string | null;
46
+ /**
47
+ * Read characters until a delimiter is found
48
+ *
49
+ * Does NOT consume the delimiter.
50
+ *
51
+ * @param delimiter - String or RegExp to match
52
+ * @returns String of characters read (may be empty)
53
+ */
54
+ readUntil(delimiter: string | RegExp): string;
55
+ /**
56
+ * Read while a condition is true
57
+ *
58
+ * @param predicate - Function that returns true to continue reading
59
+ * @returns String of characters read
60
+ */
61
+ readWhile(predicate: (ch: string) => boolean): string;
62
+ /**
63
+ * Skip whitespace characters (space and tab)
64
+ *
65
+ * Does NOT skip newlines (CRLF/LF/CR).
66
+ */
67
+ skipWhitespace(): void;
68
+ /**
69
+ * Skip to the next line
70
+ *
71
+ * Consumes all characters until and including the next line ending (CRLF/LF/CR).
72
+ */
73
+ skipToNextLine(): void;
74
+ /**
75
+ * Get current position in the input
76
+ *
77
+ * @returns Position object with offset, line, and column
78
+ */
79
+ getPosition(): Position;
80
+ /**
81
+ * Check if at end of file
82
+ *
83
+ * @returns true if no more characters to read
84
+ */
85
+ isEOF(): boolean;
86
+ /**
87
+ * Expect a specific string at the current position
88
+ *
89
+ * Consumes the string if it matches, throws otherwise.
90
+ *
91
+ * @param expected - String to expect
92
+ * @throws ParseError if string doesn't match
93
+ */
94
+ expect(expected: string): void;
95
+ /**
96
+ * Expect a character that matches a condition
97
+ *
98
+ * Consumes the character if it matches, throws otherwise.
99
+ *
100
+ * @param predicate - Function that returns true for valid character
101
+ * @param description - Description of expected character for error messages
102
+ * @returns The matched character
103
+ * @throws ParseError if character doesn't match
104
+ */
105
+ expectChar(predicate: (ch: string) => boolean, description: string): string;
106
+ /**
107
+ * Expect end of line (CRLF, LF, or CR)
108
+ *
109
+ * @throws ParseError if not at end of line
110
+ */
111
+ expectEOL(): void;
112
+ /**
113
+ * Expect space character
114
+ *
115
+ * @throws ParseError if not a space
116
+ */
117
+ expectSpace(): void;
118
+ /**
119
+ * Get context around a position for error messages
120
+ *
121
+ * Returns a snippet of the input around the position.
122
+ *
123
+ * @param pos - Position to get context for
124
+ * @returns Context string (may be empty)
125
+ */
126
+ private getContext;
127
+ /**
128
+ * Get remaining input from current position
129
+ *
130
+ * @returns Remaining input string
131
+ */
132
+ getRemainingInput(): string;
133
+ /**
134
+ * Get a substring of the input
135
+ *
136
+ * @param start - Start position
137
+ * @param end - End position (optional, defaults to current position)
138
+ * @returns Substring
139
+ */
140
+ getSubstring(start: number, end?: number): string;
141
+ }
142
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../../src/parser/scanner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAG/C;;;;;GAKG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,MAAM,CAAS;IAEvB;;;;OAIG;gBACS,KAAK,EAAE,MAAM;IAOzB;;;;OAIG;IACH,IAAI,IAAI,MAAM,GAAG,IAAI;IAOrB;;;;;OAKG;IACH,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAQnC;;;;;;OAMG;IACH,OAAO,IAAI,MAAM,GAAG,IAAI;IA2BxB;;;;;;;OAOG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM;IAwB7C;;;;;OAKG;IACH,SAAS,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,MAAM;IAcrD;;;;OAIG;IACH,cAAc,IAAI,IAAI;IAUtB;;;;OAIG;IACH,cAAc,IAAI,IAAI;IAStB;;;;OAIG;IACH,WAAW,IAAI,QAAQ;IAQvB;;;;OAIG;IACH,KAAK,IAAI,OAAO;IAIhB;;;;;;;OAOG;IACH,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IA4B9B;;;;;;;;;OASG;IACH,UAAU,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM;IA0B3E;;;;OAIG;IACH,SAAS,IAAI,IAAI;IAqBjB;;;;OAIG;IACH,WAAW,IAAI,IAAI;IAgBnB;;;;;;;OAOG;IACH,OAAO,CAAC,UAAU;IAQlB;;;;OAIG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;;;;;OAMG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM;CAGlD"}