@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.
- package/LICENSE +21 -0
- package/README.md +124 -0
- package/dist/src/builder/media-builder.d.ts +221 -0
- package/dist/src/builder/media-builder.d.ts.map +1 -0
- package/dist/src/builder/media-builder.js +385 -0
- package/dist/src/builder/session-builder.d.ts +195 -0
- package/dist/src/builder/session-builder.d.ts.map +1 -0
- package/dist/src/builder/session-builder.js +366 -0
- package/dist/src/index.d.ts +67 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +250 -0
- package/dist/src/parser/attribute-parser.d.ts +100 -0
- package/dist/src/parser/attribute-parser.d.ts.map +1 -0
- package/dist/src/parser/attribute-parser.js +217 -0
- package/dist/src/parser/field-parser.d.ts +124 -0
- package/dist/src/parser/field-parser.d.ts.map +1 -0
- package/dist/src/parser/field-parser.js +335 -0
- package/dist/src/parser/media-parser.d.ts +45 -0
- package/dist/src/parser/media-parser.d.ts.map +1 -0
- package/dist/src/parser/media-parser.js +157 -0
- package/dist/src/parser/primitive-parser.d.ts +138 -0
- package/dist/src/parser/primitive-parser.d.ts.map +1 -0
- package/dist/src/parser/primitive-parser.js +316 -0
- package/dist/src/parser/scanner.d.ts +142 -0
- package/dist/src/parser/scanner.d.ts.map +1 -0
- package/dist/src/parser/scanner.js +284 -0
- package/dist/src/parser/session-parser.d.ts +35 -0
- package/dist/src/parser/session-parser.d.ts.map +1 -0
- package/dist/src/parser/session-parser.js +207 -0
- package/dist/src/parser/time-parser.d.ts +74 -0
- package/dist/src/parser/time-parser.d.ts.map +1 -0
- package/dist/src/parser/time-parser.js +168 -0
- package/dist/src/serializer/attribute-serializer.d.ts +18 -0
- package/dist/src/serializer/attribute-serializer.d.ts.map +1 -0
- package/dist/src/serializer/attribute-serializer.js +34 -0
- package/dist/src/serializer/field-serializer.d.ts +112 -0
- package/dist/src/serializer/field-serializer.d.ts.map +1 -0
- package/dist/src/serializer/field-serializer.js +212 -0
- package/dist/src/serializer/media-serializer.d.ts +31 -0
- package/dist/src/serializer/media-serializer.d.ts.map +1 -0
- package/dist/src/serializer/media-serializer.js +83 -0
- package/dist/src/serializer/session-serializer.d.ts +29 -0
- package/dist/src/serializer/session-serializer.d.ts.map +1 -0
- package/dist/src/serializer/session-serializer.js +99 -0
- package/dist/src/serializer/time-serializer.d.ts +46 -0
- package/dist/src/serializer/time-serializer.d.ts.map +1 -0
- package/dist/src/serializer/time-serializer.js +86 -0
- package/dist/src/types/attributes.d.ts +318 -0
- package/dist/src/types/attributes.d.ts.map +1 -0
- package/dist/src/types/attributes.js +225 -0
- package/dist/src/types/errors.d.ts +129 -0
- package/dist/src/types/errors.d.ts.map +1 -0
- package/dist/src/types/errors.js +186 -0
- package/dist/src/types/fields.d.ts +100 -0
- package/dist/src/types/fields.d.ts.map +1 -0
- package/dist/src/types/fields.js +48 -0
- package/dist/src/types/media.d.ts +148 -0
- package/dist/src/types/media.d.ts.map +1 -0
- package/dist/src/types/media.js +137 -0
- package/dist/src/types/network.d.ts +136 -0
- package/dist/src/types/network.d.ts.map +1 -0
- package/dist/src/types/network.js +130 -0
- package/dist/src/types/primitives.d.ts +193 -0
- package/dist/src/types/primitives.d.ts.map +1 -0
- package/dist/src/types/primitives.js +195 -0
- package/dist/src/types/session.d.ts +122 -0
- package/dist/src/types/session.d.ts.map +1 -0
- package/dist/src/types/session.js +81 -0
- package/dist/src/types/time.d.ts +129 -0
- package/dist/src/types/time.d.ts.map +1 -0
- package/dist/src/types/time.js +84 -0
- package/dist/src/utils/address-parser.d.ts +100 -0
- package/dist/src/utils/address-parser.d.ts.map +1 -0
- package/dist/src/utils/address-parser.js +338 -0
- package/dist/src/utils/format-validators.d.ts +77 -0
- package/dist/src/utils/format-validators.d.ts.map +1 -0
- package/dist/src/utils/format-validators.js +504 -0
- package/dist/src/utils/line-reader.d.ts +84 -0
- package/dist/src/utils/line-reader.d.ts.map +1 -0
- package/dist/src/utils/line-reader.js +169 -0
- package/dist/src/utils/time-converter.d.ts +99 -0
- package/dist/src/utils/time-converter.d.ts.map +1 -0
- package/dist/src/utils/time-converter.js +195 -0
- package/dist/src/validator/media-validator.d.ts +27 -0
- package/dist/src/validator/media-validator.d.ts.map +1 -0
- package/dist/src/validator/media-validator.js +241 -0
- package/dist/src/validator/semantic-validator.d.ts +47 -0
- package/dist/src/validator/semantic-validator.d.ts.map +1 -0
- package/dist/src/validator/semantic-validator.js +207 -0
- package/dist/src/validator/session-validator.d.ts +36 -0
- package/dist/src/validator/session-validator.d.ts.map +1 -0
- package/dist/src/validator/session-validator.js +280 -0
- package/dist/tests/integration/round-trip.test.d.ts +5 -0
- package/dist/tests/integration/round-trip.test.d.ts.map +1 -0
- package/dist/tests/integration/round-trip.test.js +320 -0
- package/dist/tests/integration/voip-examples.test.d.ts +5 -0
- package/dist/tests/integration/voip-examples.test.d.ts.map +1 -0
- package/dist/tests/integration/voip-examples.test.js +361 -0
- package/dist/tests/unit/builder/media-builder.test.d.ts +5 -0
- package/dist/tests/unit/builder/media-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/builder/media-builder.test.js +524 -0
- package/dist/tests/unit/builder/session-builder.test.d.ts +5 -0
- package/dist/tests/unit/builder/session-builder.test.d.ts.map +1 -0
- package/dist/tests/unit/builder/session-builder.test.js +367 -0
- package/dist/tests/unit/parser/attribute-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/attribute-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/attribute-parser.test.js +319 -0
- package/dist/tests/unit/parser/field-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/field-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/field-parser.test.js +355 -0
- package/dist/tests/unit/parser/media-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/media-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/media-parser.test.js +241 -0
- package/dist/tests/unit/parser/primitive-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/primitive-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/primitive-parser.test.js +261 -0
- package/dist/tests/unit/parser/scanner.test.d.ts +5 -0
- package/dist/tests/unit/parser/scanner.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/scanner.test.js +241 -0
- package/dist/tests/unit/parser/session-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/session-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/session-parser.test.js +346 -0
- package/dist/tests/unit/parser/time-parser.test.d.ts +5 -0
- package/dist/tests/unit/parser/time-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/parser/time-parser.test.js +173 -0
- package/dist/tests/unit/serializer/attribute-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/attribute-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/attribute-serializer.test.js +78 -0
- package/dist/tests/unit/serializer/field-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/field-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/field-serializer.test.js +159 -0
- package/dist/tests/unit/serializer/media-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/media-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/media-serializer.test.js +155 -0
- package/dist/tests/unit/serializer/session-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/session-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/session-serializer.test.js +317 -0
- package/dist/tests/unit/serializer/time-serializer.test.d.ts +5 -0
- package/dist/tests/unit/serializer/time-serializer.test.d.ts.map +1 -0
- package/dist/tests/unit/serializer/time-serializer.test.js +115 -0
- package/dist/tests/unit/types/errors.test.d.ts +5 -0
- package/dist/tests/unit/types/errors.test.d.ts.map +1 -0
- package/dist/tests/unit/types/errors.test.js +127 -0
- package/dist/tests/unit/types/network.test.d.ts +5 -0
- package/dist/tests/unit/types/network.test.d.ts.map +1 -0
- package/dist/tests/unit/types/network.test.js +132 -0
- package/dist/tests/unit/types/primitives.test.d.ts +5 -0
- package/dist/tests/unit/types/primitives.test.d.ts.map +1 -0
- package/dist/tests/unit/types/primitives.test.js +108 -0
- package/dist/tests/unit/utils/address-parser.test.d.ts +5 -0
- package/dist/tests/unit/utils/address-parser.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/address-parser.test.js +203 -0
- package/dist/tests/unit/utils/format-validators.test.d.ts +5 -0
- package/dist/tests/unit/utils/format-validators.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/format-validators.test.js +224 -0
- package/dist/tests/unit/utils/line-reader.test.d.ts +5 -0
- package/dist/tests/unit/utils/line-reader.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/line-reader.test.js +157 -0
- package/dist/tests/unit/utils/time-converter.test.d.ts +5 -0
- package/dist/tests/unit/utils/time-converter.test.d.ts.map +1 -0
- package/dist/tests/unit/utils/time-converter.test.js +190 -0
- package/dist/tests/unit/validator/media-validator.test.d.ts +5 -0
- package/dist/tests/unit/validator/media-validator.test.d.ts.map +1 -0
- package/dist/tests/unit/validator/media-validator.test.js +313 -0
- package/dist/tests/unit/validator/semantic-validator.test.d.ts +5 -0
- package/dist/tests/unit/validator/semantic-validator.test.d.ts.map +1 -0
- package/dist/tests/unit/validator/semantic-validator.test.js +262 -0
- package/dist/tests/unit/validator/session-validator.test.d.ts +5 -0
- package/dist/tests/unit/validator/session-validator.test.d.ts.map +1 -0
- package/dist/tests/unit/validator/session-validator.test.js +447 -0
- package/package.json +50 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Line reading utilities for SDP (RFC 8866)
|
|
4
|
+
*
|
|
5
|
+
* RFC 8866 specifies CRLF as the standard line terminator, but parsers
|
|
6
|
+
* SHOULD accept LF alone for robustness. This module handles all three
|
|
7
|
+
* cases: CRLF, LF, and CR.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.splitLines = splitLines;
|
|
11
|
+
exports.parseLine = parseLine;
|
|
12
|
+
exports.parseLines = parseLines;
|
|
13
|
+
exports.joinLines = joinLines;
|
|
14
|
+
exports.formatLine = formatLine;
|
|
15
|
+
exports.isValidLineType = isValidLineType;
|
|
16
|
+
exports.getLineTypeName = getLineTypeName;
|
|
17
|
+
/**
|
|
18
|
+
* Split input text into lines, handling CRLF, LF, and CR
|
|
19
|
+
*
|
|
20
|
+
* RFC 8866 Section 5: Text fields use UTF-8 encoding, line ending is CRLF,
|
|
21
|
+
* but parsers SHOULD accept LF alone for compatibility.
|
|
22
|
+
*
|
|
23
|
+
* @param input - Raw SDP text
|
|
24
|
+
* @returns Array of lines (line endings removed)
|
|
25
|
+
*/
|
|
26
|
+
function splitLines(input) {
|
|
27
|
+
const lines = [];
|
|
28
|
+
let current = '';
|
|
29
|
+
for (let i = 0; i < input.length; i++) {
|
|
30
|
+
const ch = input[i];
|
|
31
|
+
if (ch === '\r' && input[i + 1] === '\n') {
|
|
32
|
+
// CRLF (standard)
|
|
33
|
+
lines.push(current);
|
|
34
|
+
current = '';
|
|
35
|
+
i++; // Skip the \n
|
|
36
|
+
}
|
|
37
|
+
else if (ch === '\n') {
|
|
38
|
+
// LF alone (tolerant)
|
|
39
|
+
lines.push(current);
|
|
40
|
+
current = '';
|
|
41
|
+
}
|
|
42
|
+
else if (ch === '\r') {
|
|
43
|
+
// CR alone (tolerant, rare)
|
|
44
|
+
lines.push(current);
|
|
45
|
+
current = '';
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
current += ch;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Add final line if input doesn't end with line terminator
|
|
52
|
+
if (current.length > 0) {
|
|
53
|
+
lines.push(current);
|
|
54
|
+
}
|
|
55
|
+
return lines;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Parse a single SDP line into type and value
|
|
59
|
+
*
|
|
60
|
+
* SDP line format: <type>=<value>
|
|
61
|
+
* where <type> is a single character
|
|
62
|
+
*
|
|
63
|
+
* @param line - Single SDP line
|
|
64
|
+
* @returns Parsed line with type and value, or null if invalid
|
|
65
|
+
*/
|
|
66
|
+
function parseLine(line) {
|
|
67
|
+
// Empty lines are ignored
|
|
68
|
+
if (line.length === 0) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
// Valid SDP lines must have at least "x=" (3 characters minimum)
|
|
72
|
+
if (line.length < 2) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
// Second character must be '='
|
|
76
|
+
if (line[1] !== '=') {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
type: line[0],
|
|
81
|
+
value: line.substring(2)
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Parse all lines in SDP input into structured format
|
|
86
|
+
*
|
|
87
|
+
* @param input - Raw SDP text
|
|
88
|
+
* @returns Array of parsed lines (skips empty/invalid lines)
|
|
89
|
+
*/
|
|
90
|
+
function parseLines(input) {
|
|
91
|
+
const rawLines = splitLines(input);
|
|
92
|
+
const parsedLines = [];
|
|
93
|
+
for (let i = 0; i < rawLines.length; i++) {
|
|
94
|
+
const parsed = parseLine(rawLines[i]);
|
|
95
|
+
if (parsed !== null) {
|
|
96
|
+
parsedLines.push({
|
|
97
|
+
type: parsed.type,
|
|
98
|
+
value: parsed.value,
|
|
99
|
+
lineNumber: i + 1
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return parsedLines;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Join lines back into SDP text format with CRLF line endings
|
|
107
|
+
*
|
|
108
|
+
* Always outputs standard CRLF line endings per RFC 8866,
|
|
109
|
+
* even if input used LF or CR.
|
|
110
|
+
*
|
|
111
|
+
* @param lines - Array of line strings (without line endings)
|
|
112
|
+
* @returns SDP text with CRLF line endings
|
|
113
|
+
*/
|
|
114
|
+
function joinLines(lines) {
|
|
115
|
+
return lines.join('\r\n') + '\r\n';
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Format a line in SDP format: <type>=<value>
|
|
119
|
+
*
|
|
120
|
+
* @param type - Single character line type
|
|
121
|
+
* @param value - Line value
|
|
122
|
+
* @returns Formatted SDP line (without line ending)
|
|
123
|
+
*/
|
|
124
|
+
function formatLine(type, value) {
|
|
125
|
+
if (type.length !== 1) {
|
|
126
|
+
throw new Error(`Invalid line type '${type}': must be single character`);
|
|
127
|
+
}
|
|
128
|
+
return `${type}=${value}`;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Validate that a line type is a valid SDP field
|
|
132
|
+
*
|
|
133
|
+
* Valid SDP field types per RFC 8866:
|
|
134
|
+
* v, o, s, i, u, e, p, c, b, t, r, z, k, a, m
|
|
135
|
+
*
|
|
136
|
+
* @param type - Line type character
|
|
137
|
+
* @returns true if valid SDP field type
|
|
138
|
+
*/
|
|
139
|
+
function isValidLineType(type) {
|
|
140
|
+
const validTypes = ['v', 'o', 's', 'i', 'u', 'e', 'p', 'c', 'b', 't', 'r', 'z', 'k', 'a', 'm'];
|
|
141
|
+
return validTypes.includes(type);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get display name for a line type
|
|
145
|
+
*
|
|
146
|
+
* @param type - Line type character
|
|
147
|
+
* @returns Human-readable field name
|
|
148
|
+
*/
|
|
149
|
+
function getLineTypeName(type) {
|
|
150
|
+
var _a;
|
|
151
|
+
const names = {
|
|
152
|
+
'v': 'version',
|
|
153
|
+
'o': 'origin',
|
|
154
|
+
's': 'session name',
|
|
155
|
+
'i': 'information',
|
|
156
|
+
'u': 'URI',
|
|
157
|
+
'e': 'email',
|
|
158
|
+
'p': 'phone',
|
|
159
|
+
'c': 'connection',
|
|
160
|
+
'b': 'bandwidth',
|
|
161
|
+
't': 'timing',
|
|
162
|
+
'r': 'repeat',
|
|
163
|
+
'z': 'timezone',
|
|
164
|
+
'k': 'key',
|
|
165
|
+
'a': 'attribute',
|
|
166
|
+
'm': 'media'
|
|
167
|
+
};
|
|
168
|
+
return (_a = names[type]) !== null && _a !== void 0 ? _a : 'unknown';
|
|
169
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time conversion utilities for SDP (RFC 8866)
|
|
3
|
+
*
|
|
4
|
+
* SDP uses typed time values with unit suffixes for repeat and timezone fields:
|
|
5
|
+
* - d: days
|
|
6
|
+
* - h: hours
|
|
7
|
+
* - m: minutes
|
|
8
|
+
* - (no suffix): seconds
|
|
9
|
+
*/
|
|
10
|
+
import { TypedTime } from '../types/primitives';
|
|
11
|
+
/**
|
|
12
|
+
* Parse a typed time string into a TypedTime object
|
|
13
|
+
*
|
|
14
|
+
* Formats:
|
|
15
|
+
* - "7d" = 7 days
|
|
16
|
+
* - "3h" = 3 hours
|
|
17
|
+
* - "120m" = 120 minutes
|
|
18
|
+
* - "3600" = 3600 seconds (no suffix)
|
|
19
|
+
* - "30s" = 30 seconds (explicit suffix)
|
|
20
|
+
* - "-1h" = -1 hour (for timezone offsets)
|
|
21
|
+
*
|
|
22
|
+
* @param input - Time string to parse
|
|
23
|
+
* @returns TypedTime object
|
|
24
|
+
* @throws ParseError if input is invalid
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseTypedTime(input: string): TypedTime;
|
|
27
|
+
/**
|
|
28
|
+
* Serialize a TypedTime object to a string
|
|
29
|
+
*
|
|
30
|
+
* Outputs compact format per RFC 8866:
|
|
31
|
+
* - days: "7d"
|
|
32
|
+
* - hours: "3h"
|
|
33
|
+
* - minutes: "120m"
|
|
34
|
+
* - seconds: "3600" (no suffix for seconds)
|
|
35
|
+
*
|
|
36
|
+
* @param time - TypedTime to serialize
|
|
37
|
+
* @returns Formatted time string
|
|
38
|
+
*/
|
|
39
|
+
export declare function serializeTypedTime(time: TypedTime): string;
|
|
40
|
+
/**
|
|
41
|
+
* Convert TypedTime to seconds
|
|
42
|
+
*
|
|
43
|
+
* Useful for comparisons and calculations
|
|
44
|
+
*
|
|
45
|
+
* @param time - TypedTime to convert
|
|
46
|
+
* @returns Number of seconds
|
|
47
|
+
*/
|
|
48
|
+
export declare function typedTimeToSeconds(time: TypedTime): number;
|
|
49
|
+
/**
|
|
50
|
+
* Convert seconds to TypedTime with appropriate unit
|
|
51
|
+
*
|
|
52
|
+
* Chooses the largest unit that divides evenly:
|
|
53
|
+
* - 86400 seconds -> 1 day
|
|
54
|
+
* - 3600 seconds -> 1 hour
|
|
55
|
+
* - 120 seconds -> 2 minutes
|
|
56
|
+
* - 45 seconds -> 45 seconds
|
|
57
|
+
*
|
|
58
|
+
* @param seconds - Number of seconds
|
|
59
|
+
* @returns TypedTime with appropriate unit
|
|
60
|
+
*/
|
|
61
|
+
export declare function secondsToTypedTime(seconds: number): TypedTime;
|
|
62
|
+
/**
|
|
63
|
+
* Add two TypedTime values
|
|
64
|
+
*
|
|
65
|
+
* Result uses the smaller unit to avoid precision loss
|
|
66
|
+
*
|
|
67
|
+
* @param a - First time value
|
|
68
|
+
* @param b - Second time value
|
|
69
|
+
* @returns Sum of the two times
|
|
70
|
+
*/
|
|
71
|
+
export declare function addTypedTimes(a: TypedTime, b: TypedTime): TypedTime;
|
|
72
|
+
/**
|
|
73
|
+
* Compare two TypedTime values
|
|
74
|
+
*
|
|
75
|
+
* @param a - First time value
|
|
76
|
+
* @param b - Second time value
|
|
77
|
+
* @returns Negative if a < b, 0 if equal, positive if a > b
|
|
78
|
+
*/
|
|
79
|
+
export declare function compareTypedTimes(a: TypedTime, b: TypedTime): number;
|
|
80
|
+
/**
|
|
81
|
+
* Check if two TypedTime values are equal
|
|
82
|
+
*
|
|
83
|
+
* @param a - First time value
|
|
84
|
+
* @param b - Second time value
|
|
85
|
+
* @returns true if equal (same duration in seconds)
|
|
86
|
+
*/
|
|
87
|
+
export declare function typedTimesEqual(a: TypedTime, b: TypedTime): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Normalize a TypedTime to use the most compact unit
|
|
90
|
+
*
|
|
91
|
+
* Examples:
|
|
92
|
+
* - {value: 120, unit: 'minutes'} -> {value: 2, unit: 'hours'}
|
|
93
|
+
* - {value: 3600, unit: 'seconds'} -> {value: 1, unit: 'hours'}
|
|
94
|
+
*
|
|
95
|
+
* @param time - TypedTime to normalize
|
|
96
|
+
* @returns Normalized TypedTime
|
|
97
|
+
*/
|
|
98
|
+
export declare function normalizeTypedTime(time: TypedTime): TypedTime;
|
|
99
|
+
//# sourceMappingURL=time-converter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-converter.d.ts","sourceRoot":"","sources":["../../../src/utils/time-converter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAA6B,MAAM,qBAAqB,CAAC;AAG3E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAqCvD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAiB1D;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAc1D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAmB7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,SAAS,CAMnE;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CAEpE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,OAAO,CAEnE;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,CAG7D"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Time conversion utilities for SDP (RFC 8866)
|
|
4
|
+
*
|
|
5
|
+
* SDP uses typed time values with unit suffixes for repeat and timezone fields:
|
|
6
|
+
* - d: days
|
|
7
|
+
* - h: hours
|
|
8
|
+
* - m: minutes
|
|
9
|
+
* - (no suffix): seconds
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.parseTypedTime = parseTypedTime;
|
|
13
|
+
exports.serializeTypedTime = serializeTypedTime;
|
|
14
|
+
exports.typedTimeToSeconds = typedTimeToSeconds;
|
|
15
|
+
exports.secondsToTypedTime = secondsToTypedTime;
|
|
16
|
+
exports.addTypedTimes = addTypedTimes;
|
|
17
|
+
exports.compareTypedTimes = compareTypedTimes;
|
|
18
|
+
exports.typedTimesEqual = typedTimesEqual;
|
|
19
|
+
exports.normalizeTypedTime = normalizeTypedTime;
|
|
20
|
+
const primitives_1 = require("../types/primitives");
|
|
21
|
+
const errors_1 = require("../types/errors");
|
|
22
|
+
/**
|
|
23
|
+
* Parse a typed time string into a TypedTime object
|
|
24
|
+
*
|
|
25
|
+
* Formats:
|
|
26
|
+
* - "7d" = 7 days
|
|
27
|
+
* - "3h" = 3 hours
|
|
28
|
+
* - "120m" = 120 minutes
|
|
29
|
+
* - "3600" = 3600 seconds (no suffix)
|
|
30
|
+
* - "30s" = 30 seconds (explicit suffix)
|
|
31
|
+
* - "-1h" = -1 hour (for timezone offsets)
|
|
32
|
+
*
|
|
33
|
+
* @param input - Time string to parse
|
|
34
|
+
* @returns TypedTime object
|
|
35
|
+
* @throws ParseError if input is invalid
|
|
36
|
+
*/
|
|
37
|
+
function parseTypedTime(input) {
|
|
38
|
+
if (input.length === 0) {
|
|
39
|
+
throw new errors_1.ParseError('Empty time value', 0, 0);
|
|
40
|
+
}
|
|
41
|
+
// Match pattern: optional minus sign, digits, optional unit suffix
|
|
42
|
+
const match = input.match(/^(-?\d+)([dhms]?)$/);
|
|
43
|
+
if (!match) {
|
|
44
|
+
throw new errors_1.ParseError(`Invalid time format: '${input}'`, 0, 0);
|
|
45
|
+
}
|
|
46
|
+
const value = parseInt(match[1], 10);
|
|
47
|
+
const suffix = match[2] || 's'; // Default to seconds if no suffix
|
|
48
|
+
if (isNaN(value)) {
|
|
49
|
+
throw new errors_1.ParseError(`Invalid time value: '${value}'`, 0, 0);
|
|
50
|
+
}
|
|
51
|
+
// Map suffix to unit
|
|
52
|
+
const unit = (() => {
|
|
53
|
+
switch (suffix) {
|
|
54
|
+
case 'd':
|
|
55
|
+
return 'days';
|
|
56
|
+
case 'h':
|
|
57
|
+
return 'hours';
|
|
58
|
+
case 'm':
|
|
59
|
+
return 'minutes';
|
|
60
|
+
case 's':
|
|
61
|
+
case '':
|
|
62
|
+
return 'seconds';
|
|
63
|
+
default:
|
|
64
|
+
throw new errors_1.ParseError(`Invalid time unit: '${suffix}'`, 0, 0);
|
|
65
|
+
}
|
|
66
|
+
})();
|
|
67
|
+
return (0, primitives_1.createTypedTime)(value, unit);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Serialize a TypedTime object to a string
|
|
71
|
+
*
|
|
72
|
+
* Outputs compact format per RFC 8866:
|
|
73
|
+
* - days: "7d"
|
|
74
|
+
* - hours: "3h"
|
|
75
|
+
* - minutes: "120m"
|
|
76
|
+
* - seconds: "3600" (no suffix for seconds)
|
|
77
|
+
*
|
|
78
|
+
* @param time - TypedTime to serialize
|
|
79
|
+
* @returns Formatted time string
|
|
80
|
+
*/
|
|
81
|
+
function serializeTypedTime(time) {
|
|
82
|
+
const suffix = (() => {
|
|
83
|
+
switch (time.unit) {
|
|
84
|
+
case 'days':
|
|
85
|
+
return 'd';
|
|
86
|
+
case 'hours':
|
|
87
|
+
return 'h';
|
|
88
|
+
case 'minutes':
|
|
89
|
+
return 'm';
|
|
90
|
+
case 'seconds':
|
|
91
|
+
return ''; // No suffix for seconds
|
|
92
|
+
default:
|
|
93
|
+
throw new Error(`Invalid time unit: ${time.unit}`);
|
|
94
|
+
}
|
|
95
|
+
})();
|
|
96
|
+
return `${time.value}${suffix}`;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Convert TypedTime to seconds
|
|
100
|
+
*
|
|
101
|
+
* Useful for comparisons and calculations
|
|
102
|
+
*
|
|
103
|
+
* @param time - TypedTime to convert
|
|
104
|
+
* @returns Number of seconds
|
|
105
|
+
*/
|
|
106
|
+
function typedTimeToSeconds(time) {
|
|
107
|
+
const multipliers = {
|
|
108
|
+
'seconds': 1,
|
|
109
|
+
'minutes': 60,
|
|
110
|
+
'hours': 3600,
|
|
111
|
+
'days': 86400
|
|
112
|
+
};
|
|
113
|
+
const multiplier = multipliers[time.unit];
|
|
114
|
+
if (multiplier === undefined) {
|
|
115
|
+
throw new Error(`Invalid time unit: ${time.unit}`);
|
|
116
|
+
}
|
|
117
|
+
return time.value * multiplier;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Convert seconds to TypedTime with appropriate unit
|
|
121
|
+
*
|
|
122
|
+
* Chooses the largest unit that divides evenly:
|
|
123
|
+
* - 86400 seconds -> 1 day
|
|
124
|
+
* - 3600 seconds -> 1 hour
|
|
125
|
+
* - 120 seconds -> 2 minutes
|
|
126
|
+
* - 45 seconds -> 45 seconds
|
|
127
|
+
*
|
|
128
|
+
* @param seconds - Number of seconds
|
|
129
|
+
* @returns TypedTime with appropriate unit
|
|
130
|
+
*/
|
|
131
|
+
function secondsToTypedTime(seconds) {
|
|
132
|
+
if (seconds < 0) {
|
|
133
|
+
throw new Error('Time cannot be negative');
|
|
134
|
+
}
|
|
135
|
+
// Try each unit from largest to smallest
|
|
136
|
+
if (seconds % 86400 === 0) {
|
|
137
|
+
return (0, primitives_1.createTypedTime)(seconds / 86400, 'days');
|
|
138
|
+
}
|
|
139
|
+
if (seconds % 3600 === 0) {
|
|
140
|
+
return (0, primitives_1.createTypedTime)(seconds / 3600, 'hours');
|
|
141
|
+
}
|
|
142
|
+
if (seconds % 60 === 0) {
|
|
143
|
+
return (0, primitives_1.createTypedTime)(seconds / 60, 'minutes');
|
|
144
|
+
}
|
|
145
|
+
return (0, primitives_1.createTypedTime)(seconds, 'seconds');
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Add two TypedTime values
|
|
149
|
+
*
|
|
150
|
+
* Result uses the smaller unit to avoid precision loss
|
|
151
|
+
*
|
|
152
|
+
* @param a - First time value
|
|
153
|
+
* @param b - Second time value
|
|
154
|
+
* @returns Sum of the two times
|
|
155
|
+
*/
|
|
156
|
+
function addTypedTimes(a, b) {
|
|
157
|
+
const aSeconds = typedTimeToSeconds(a);
|
|
158
|
+
const bSeconds = typedTimeToSeconds(b);
|
|
159
|
+
const totalSeconds = aSeconds + bSeconds;
|
|
160
|
+
return secondsToTypedTime(totalSeconds);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Compare two TypedTime values
|
|
164
|
+
*
|
|
165
|
+
* @param a - First time value
|
|
166
|
+
* @param b - Second time value
|
|
167
|
+
* @returns Negative if a < b, 0 if equal, positive if a > b
|
|
168
|
+
*/
|
|
169
|
+
function compareTypedTimes(a, b) {
|
|
170
|
+
return typedTimeToSeconds(a) - typedTimeToSeconds(b);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Check if two TypedTime values are equal
|
|
174
|
+
*
|
|
175
|
+
* @param a - First time value
|
|
176
|
+
* @param b - Second time value
|
|
177
|
+
* @returns true if equal (same duration in seconds)
|
|
178
|
+
*/
|
|
179
|
+
function typedTimesEqual(a, b) {
|
|
180
|
+
return typedTimeToSeconds(a) === typedTimeToSeconds(b);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Normalize a TypedTime to use the most compact unit
|
|
184
|
+
*
|
|
185
|
+
* Examples:
|
|
186
|
+
* - {value: 120, unit: 'minutes'} -> {value: 2, unit: 'hours'}
|
|
187
|
+
* - {value: 3600, unit: 'seconds'} -> {value: 1, unit: 'hours'}
|
|
188
|
+
*
|
|
189
|
+
* @param time - TypedTime to normalize
|
|
190
|
+
* @returns Normalized TypedTime
|
|
191
|
+
*/
|
|
192
|
+
function normalizeTypedTime(time) {
|
|
193
|
+
const seconds = typedTimeToSeconds(time);
|
|
194
|
+
return secondsToTypedTime(seconds);
|
|
195
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Media validators for SDP (RFC 8866)
|
|
3
|
+
*
|
|
4
|
+
* Validates media descriptions and media-level constraints per RFC 8866.
|
|
5
|
+
*/
|
|
6
|
+
import { MediaDescription } from '../types/media';
|
|
7
|
+
import { ValidationResult } from './session-validator';
|
|
8
|
+
/**
|
|
9
|
+
* Validate a media description
|
|
10
|
+
*
|
|
11
|
+
* Checks RFC 8866 Section 5.14 (m=) and media-level field requirements.
|
|
12
|
+
*
|
|
13
|
+
* @param media - Media description to validate
|
|
14
|
+
* @param index - Index of media description (for error messages)
|
|
15
|
+
* @param hasSessionConnection - Whether session-level connection exists
|
|
16
|
+
* @returns ValidationResult with any errors found
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateMediaDescription(media: MediaDescription, index: number, hasSessionConnection: boolean): ValidationResult;
|
|
19
|
+
/**
|
|
20
|
+
* Validate all media descriptions in a session
|
|
21
|
+
*
|
|
22
|
+
* @param mediaDescriptions - Array of media descriptions
|
|
23
|
+
* @param hasSessionConnection - Whether session-level connection exists
|
|
24
|
+
* @returns ValidationResult with any errors found
|
|
25
|
+
*/
|
|
26
|
+
export declare function validateAllMediaDescriptions(mediaDescriptions: readonly MediaDescription[], hasSessionConnection: boolean): ValidationResult;
|
|
27
|
+
//# sourceMappingURL=media-validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"media-validator.d.ts","sourceRoot":"","sources":["../../../src/validator/media-validator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAA8B,MAAM,qBAAqB,CAAC;AAOnF;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,gBAAgB,EACvB,KAAK,EAAE,MAAM,EACb,oBAAoB,EAAE,OAAO,GAC5B,gBAAgB,CAyBlB;AAuVD;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,iBAAiB,EAAE,SAAS,gBAAgB,EAAE,EAC9C,oBAAoB,EAAE,OAAO,GAC5B,gBAAgB,CASlB"}
|