@irbisadm/simple-sdp 1.1.0 → 1.2.1-rc.1
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/CHANGELOG.md +16 -0
- package/dist/index.mjs +1 -1
- package/package.json +16 -4
- package/readme.md +13 -6
- package/types/index.d.ts +1 -1
- package/wasm/dist/index.d.ts +2 -0
- package/wasm/dist/index.mjs +51 -0
- package/wasm/dist/simple-sdp.mjs +144 -0
- package/wasm/dist/simple-sdp.wasm +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The project
|
|
4
4
|
follows [Semantic Versioning](https://semver.org/).
|
|
5
5
|
|
|
6
|
+
## [1.2.1-rc.1] — 2026-11-01
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- Releases are published to npm by CI (`release.yml`) with npm trusted publishing, so
|
|
10
|
+
every version from now on carries an npm provenance attestation.
|
|
11
|
+
|
|
12
|
+
## [1.2.0] — 2026-11-01
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- `@irbisadm/simple-sdp/wasm`: the same API and types as the main entry point, run by the
|
|
16
|
+
WebAssembly module (ESM with top-level await; works in Node.js and with bundlers that
|
|
17
|
+
handle `new URL(…, import.meta.url)`, checked with Vite 6). The module itself is
|
|
18
|
+
exported as `@irbisadm/simple-sdp/wasm/simple-sdp.wasm`.
|
|
19
|
+
- Kotlin library on JitPack: `com.github.irbisadm:simple-sdp:v1.2.0` (`jitpack.yml`).
|
|
20
|
+
- GitHub Releases carry `simple-sdp.wasm` for other runtimes (`.github/workflows/release.yml`).
|
|
21
|
+
|
|
6
22
|
## [1.1.0] — 2026-11-01
|
|
7
23
|
|
|
8
24
|
### Added
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
class e extends Error{constructor(e,t,r){super(t>0?`SDP line ${t} "${r}": ${e}`:`SDP: ${e}`),this.name="SdpParseError",this.reason=e,this.lineNumber=t,this.line=r}}const t=/^\d+$/,r=/^\d+$/,i=(e,t="value")=>{if(!r.test(e))throw new Error(`${t} "${e}" is not a non-negative integer`);return parseInt(e,10)},s={d:86400,h:3600,m:60,s:1},n=(e,t=!1)=>{if(t&&e.startsWith("-"))return-n(e.slice(1));const r=e.slice(-1);return r in s?s[r]*i(e.slice(0,-1),"time"):i(e,"time")},o=e=>{const t=e.indexOf(":"),r=-1===t?e:e.slice(0,t);if(0===r.length)throw new Error("empty key method");return-1===t?{method:r}:{method:r,encryptionKey:e.slice(t+1)}},a=e=>/(^|\/)RTP\//.test(e),d=e=>{const t=e.indexOf(":");if(t<=0)throw new Error('expected "<bwtype>:<bandwidth>"');return{bwtype:e.slice(0,t),bandwidth:i(e.slice(t+1),"bandwidth")}},c=e=>{const t=e.split(" ");if(3!==t.length)throw new Error('expected "<nettype> <addrtype> <connection-address>"');const[r,i,s]=t;return{netType:r,addrType:i,connectionAddress:s}},p=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0,l=(e,t,r)=>{Object.defineProperty(e,t,{value:r,writable:!0,enumerable:!0,configurable:!0})},m=(e,t)=>{if(void 0===e||0===e.length)throw new Error(`expected a value: ${t}`);return e},u=e=>{if(void 0!==e)throw new Error("flag attribute must not have a value");return!0},f=e=>{const t=e.indexOf(" ");return-1===t?[e,void 0]:[e.slice(0,t),e.slice(t+1)]},v=(e,t)=>{var r;const s=i(t,"payload type");return null===(r=e.codecs)||void 0===r?void 0:r.find((e=>e.payloadType===s))},h=(e,t,r,i)=>{if(void 0!==e[t])throw new Error(`duplicate a=${i}`);e[t]=r},g=(e,t,r)=>{void 0===e[t]&&(e[t]=[]),e[t].push(r)},$=["sendrecv","sendonly","recvonly","inactive"],w=e=>{const t={};for(const r of e.split(";")){const e=r.trim();if(0===e.length)continue;const i=e.indexOf("=");-1===i?l(t,e,null):l(t,e.slice(0,i),e.slice(i+1))}return t},y='"<foundation> <component> <transport> <priority> <address> <port> typ <type> [raddr <addr>] [rport <port>] *(<ext-name> <ext-value>)"',b=e=>{const t=i(e,"ssrc-id");if(t>4294967295)throw new Error(`ssrc-id ${t} does not fit into 32 bits`);return t},x=/^[A-Za-z0-9_-]+$/,k=["session","media"],E=["media"],T=["session"],j=["active","passive","actpass","holdconn"],P=e=>({scopes:k,parse:t=>h(t,"direction",e,e)}),F={sendrecv:P("sendrecv"),sendonly:P("sendonly"),recvonly:P("recvonly"),inactive:P("inactive"),"ice-ufrag":{scopes:k,parse:(e,t)=>h(e,"iceUfrag",m(t,"a=ice-ufrag:<ufrag>"),"ice-ufrag")},"ice-pwd":{scopes:k,parse:(e,t)=>h(e,"icePwd",m(t,"a=ice-pwd:<password>"),"ice-pwd")},"ice-options":{scopes:k,parse:(e,t)=>h(e,"iceOptions",m(t,"a=ice-options:<option> ...").split(" "),"ice-options")},"ice-lite":{scopes:T,parse:(e,t)=>h(e,"iceLite",u(t),"ice-lite")},candidate:{scopes:E,parse:(e,t)=>g(e,"candidates",(e=>{var t;const r=e.split(" ");if(r.length<8||"typ"!==r[6]||r.length%2!=0)throw new Error(`expected ${y}`);const[s,n,o,a,d,c,,p]=r,m={foundation:s,component:i(n,"component id"),transport:o,priority:i(a,"priority"),address:d,port:i(c,"port"),type:p};for(let e=8;e<r.length;e+=2){const[s,n]=[r[e],r[e+1]];if(0===s.length||0===n.length)throw new Error(`expected ${y}`);switch(s){case"raddr":m.relatedAddress=n;break;case"rport":m.relatedPort=i(n,"rport");break;case"tcptype":m.tcpType=n;break;default:l(null!==(t=m.extensions)&&void 0!==t?t:m.extensions={},s,n)}}return m})(m(t,"a=candidate:<candidate>")))},"end-of-candidates":{scopes:k,parse:(e,t)=>h(e,"endOfCandidates",u(t),"end-of-candidates")},fingerprint:{scopes:k,parse:(e,t)=>{const[r,i]=f(m(t,"a=fingerprint:<hash-function> <fingerprint>"));if(!i||i.includes(" "))throw new Error('expected "a=fingerprint:<hash-function> <fingerprint>"');g(e,"fingerprints",{hashFunction:r,fingerprint:i})}},setup:{scopes:k,parse:(e,t)=>{if(!j.includes(t))throw new Error(`a=setup must be one of ${j.join(", ")}`);h(e,"setup",t,"setup")}},extmap:{scopes:k,parse:(e,t)=>g(e,"extmap",(e=>{const[t,r]=f(e);if(!r)throw new Error('expected "<id>[/<direction>] <uri> [<attributes>]"');const[s,n,...o]=t.split("/");if(o.length||void 0!==n&&!$.includes(n))throw new Error(`invalid extmap id "${t}"`);const[a,d]=f(r),c={id:i(s,"extmap id"),uri:a};return void 0!==n&&(c.direction=n),void 0!==d&&(c.attributes=d),c})(m(t,"a=extmap:<id> <uri>")))},group:{scopes:T,parse:(e,t)=>g(e,"group",(e=>{const[t,...r]=e.split(" ");if(0===t.length||r.some((e=>0===e.length)))throw new Error('expected "<semantics> <mid> ..."');return{semantics:t,mids:r}})(m(t,"a=group:<semantics> <mid> ...")))},mid:{scopes:E,parse:(e,t)=>h(e,"mid",m(t,"a=mid:<identification-tag>"),"mid")},msid:{scopes:E,parse:(e,t)=>g(e,"msid",(e=>{const[t,r,...i]=e.split(" ");if(0===t.length||""===r||i.length)throw new Error('expected "<stream id> [<track id>]"');return void 0===r?{streamId:t}:{streamId:t,trackId:r}})(m(t,"a=msid:<stream id> [<track id>]")))},ssrc:{scopes:E,parse:(e,t)=>((e,t)=>{var r;const[i,s]=f(t);if(!s)throw new Error('expected "<ssrc-id> <attribute>[:<value>]"');const n=b(i),o=s.indexOf(":"),a=-1===o?s:s.slice(0,o),d=-1===o?null:s.slice(o+1),c=null!==(r=e.ssrcs)&&void 0!==r?r:e.ssrcs=[];let m=c.find((e=>e.id===n));return m||(m={id:n,attributes:{}},c.push(m)),void 0===p(m.attributes,a)&&(l(m.attributes,a,d),!0)})(e,m(t,"a=ssrc:<ssrc-id> <attribute>[:<value>]"))},"ssrc-group":{scopes:E,parse:(e,t)=>g(e,"ssrcGroups",(e=>{const[t,...r]=e.split(" ");if(0===t.length)throw new Error('expected "<semantics> <ssrc-id> ..."');return{semantics:t,ssrcs:r.map(b)}})(m(t,"a=ssrc-group:<semantics> <ssrc-id> ...")))},rid:{scopes:E,parse:(e,t)=>g(e,"rids",(e=>{const[t,r]=f(e),[s,n]=r?f(r):[void 0,void 0];if(!x.test(t)||"send"!==s&&"recv"!==s||""===n)throw new Error('expected "<rid-id> <send|recv> [<params>]"');const o={id:t,direction:s};if(void 0!==n){const e=w(n);if("pt"in e){if(!n.startsWith("pt=")||null===e.pt)throw new Error('"pt=" must be the first rid parameter');o.payloadTypes=e.pt.split(",").map((e=>i(e,"payload type"))),delete e.pt}Object.keys(e).length&&(o.restrictions=e)}return o})(m(t,"a=rid:<rid-id> <send|recv>")))},simulcast:{scopes:E,parse:(e,t)=>h(e,"simulcast",(e=>{const t=e.split(" ");if(2!==t.length&&4!==t.length)throw new Error('expected "send|recv <streams> [send|recv <streams>]"');const r={};for(let e=0;e<t.length;e+=2){const i=t[e];if("send"!==i&&"recv"!==i||r[i])throw new Error('expected "send|recv <streams> [send|recv <streams>]"');r[i]=t[e+1].split(";").map((e=>e.split(",").map((e=>{const t=e.startsWith("~"),r=t?e.slice(1):e;if(!x.test(r))throw new Error(`invalid simulcast rid "${e}"`);return t?{rid:r,paused:t}:{rid:r}}))))}return r})(m(t,"a=simulcast:<send|recv> <streams>")),"simulcast")},"extmap-allow-mixed":{scopes:k,parse:(e,t)=>h(e,"extmapAllowMixed",u(t),"extmap-allow-mixed")},"rtcp-mux":{scopes:E,parse:(e,t)=>h(e,"rtcpMux",u(t),"rtcp-mux")},"rtcp-rsize":{scopes:E,parse:(e,t)=>h(e,"rtcpRsize",u(t),"rtcp-rsize")},rtcp:{scopes:E,parse:(e,t)=>{const r=m(t,"a=rtcp:<port> [<nettype> <addrtype> <address>]").split(" ");if(1!==r.length&&4!==r.length)throw new Error('expected "a=rtcp:<port> [<nettype> <addrtype> <address>]"');const[s,n,o,a]=r;h(e,"rtcp",1===r.length?{port:i(s,"port")}:{port:i(s,"port"),netType:n,addrType:o,address:a},"rtcp")}},"sctp-port":{scopes:E,parse:(e,t)=>h(e,"sctpPort",i(m(t,"a=sctp-port:<port>"),"sctp-port"),"sctp-port")},"max-message-size":{scopes:E,parse:(e,t)=>h(e,"maxMessageSize",i(m(t,"a=max-message-size:<size>"),"max-message-size"),"max-message-size")},rtpmap:{scopes:E,parse:(e,t)=>{const[r,s]=f(m(t,"a=rtpmap:<payload type> <encoding>")),n=e.codecs?v(e,r):void 0;if(!n||void 0!==n.encodingName)return!1;((e,t)=>{const[r,s,n,...o]=t.split("/");if(!r||void 0===s||o.length)throw new Error('expected "<payload type> <encoding name>/<clock rate>[/<encoding parameters>]"');e.encodingName=r,e.clockRate=i(s,"clock rate"),void 0!==n&&(e.encodingParameters=i(n,"encoding parameters"))})(n,null!=s?s:"")}},fmtp:{scopes:E,parse:(e,t)=>{const[r,i]=f(m(t,"a=fmtp:<format> <parameters>")),s=e.codecs?v(e,r):void 0;if(!s||void 0!==s.parameters||void 0===i)return!1;s.parameters=w(i)}},"rtcp-fb":{scopes:E,parse:(e,t)=>{const[r,i]=f(m(t,"a=rtcp-fb:<payload type> <feedback>"));if(!i||!e.codecs)return!1;if("*"===r)return void g(e,"rtcpFeedbackWildcard",i);const s=v(e,r);if(!s)return!1;g(s,"rtcpFeedback",i)}}},z=(e,t,r)=>{const i=r.indexOf(":"),s=-1===i?r:r.slice(0,i),n=-1===i?void 0:r.slice(i+1);if(0===s.length)throw new Error("empty attribute name");const o=p(F,s);o&&o.scopes.includes(t)&&!1!==o.parse(e,n)||g(e,"attributes",void 0===n?{name:s}:{name:s,value:n})},I={v:0,o:1,s:2,i:3,u:4,e:5,p:6,c:7,b:8,t:9,r:10,z:11,k:12,a:13},O=new Set(["e","p","b","a"]),C={m:0,i:1,c:2,b:3,k:4,a:5},R=new Set(["b","a"]),A={session:"v o s i u e p c b t r z k a",media:"m i c b k a"},N=r=>{const s=r.split(/\r?\n/);""===s[s.length-1]&&s.pop();const p={media:[]};let l=null,m=null,u=-1;s.forEach(((r,s)=>{var f,v,h,g,$,w,y;const b=t=>{throw new e(t,s+1,r)};0===r.length&&b("empty line"),(r.length<2||"="!==r[1])&&b('expected "<type>=<value>"');const x=r[0],k=r.slice(2);if(0===s&&"v"!==x&&b('a session description must start with "v="'),"m"===x)u=0;else{const e=l?"media":"session",t=l?R:O,r=(l?C:I)[x];void 0===r&&b(x in I?`"${x}=" is not allowed in a media description`:`unknown line type "${x}"`),l||"t"!==x?l||"r"!==x&&"z"!==x?r<u?b(`line is out of order (${e} order: ${A[e]})`):r!==u||t.has(x)||b(l&&"c"===x?'multiple "c=" lines in one media description are not supported':`duplicate "${x}=" line`):u!==I.t&&u!==I.r&&b(`"${x}=" must follow "t=" or "r="`):u>I.z&&b(`"t=" must precede "k=" and "a=" (order: ${A.session})`),u=r}try{if(l){switch(x){case"i":l.information=k;break;case"c":l.connectionInformation=c(k);break;case"b":(null!==(f=l.bandwidths)&&void 0!==f?f:l.bandwidths=[]).push(d(k));break;case"k":l.encryptionKey=o(k);break;case"a":z(l,"media",k)}if("m"!==x)return}switch(x){case"v":"0"!==k&&b(`unsupported protocol version "${k}"`),p.protocolVersion=0;break;case"o":p.origin=(e=>{const r=e.split(" ");if(6!==r.length)throw new Error('expected "<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>"');const[i,s,n,o,a,d]=r;if(!t.test(s))throw new Error(`sess-id "${s}" is not numeric`);if(!t.test(n))throw new Error(`sess-version "${n}" is not numeric`);return{username:i,sessId:s,sessVersion:n,netType:o,addrType:a,unicastAddress:d}})(k);break;case"s":p.sessionName=k;break;case"i":p.sessionInformation=k;break;case"u":p.uri=k;break;case"e":(null!==(v=p.emailAddresses)&&void 0!==v?v:p.emailAddresses=[]).push(k);break;case"p":(null!==(h=p.phoneNumbers)&&void 0!==h?h:p.phoneNumbers=[]).push(k);break;case"c":p.connectionInformation=c(k);break;case"b":(null!==(g=p.bandwidths)&&void 0!==g?g:p.bandwidths=[]).push(d(k));break;case"t":m=(e=>{const t=e.split(" ");if(2!==t.length)throw new Error('expected "<start-time> <stop-time>"');return{startTime:i(t[0],"start time"),stopTime:i(t[1],"stop time")}})(k),(null!==($=p.timing)&&void 0!==$?$:p.timing=[]).push(m);break;case"r":(null!==(w=(y=m).repeat)&&void 0!==w?w:y.repeat=[]).push((e=>{const t=e.split(" ");if(t.length<3)throw new Error('expected "<repeat interval> <active duration> <offset>..."');const[r,i,...s]=t.map((e=>n(e)));return{repeatInterval:r,activeDuration:i,offsets:s}})(k));break;case"z":m.zoneAdjustments=(e=>{const t=e.split(" ");if(t.length<2||t.length%2!=0)throw new Error('expected pairs of "<adjustment time> <offset>"');const r=[];for(let e=0;e<t.length;e+=2)r.push({adjustmentTime:i(t[e],"adjustment time"),offset:n(t[e+1],!0)});return r})(k);break;case"k":p.encryptionKey=o(k);break;case"a":z(p,"session",k);break;case"m":l=(e=>{const t=e.split(" ");if(t.length<4)throw new Error('expected "<media> <port> <proto> <fmt> ..."');const[r,s,n,...o]=t,[d,c,...p]=s.split("/");if(p.length)throw new Error(`invalid port "${s}"`);const l={type:r,port:i(d,"port"),proto:n};return void 0!==c&&(l.numberOfPorts=i(c,"number of ports")),a(n)?l.codecs=o.map((e=>{const t=i(e,"payload type");if(t>127)throw new Error(`payload type ${t} is out of range 0-127`);return{payloadType:t}})):l.formats=o,l})(k),p.media.push(l)}}catch(t){if(t instanceof e)throw t;b(t.message)}}));for(const[t,r]of[["protocolVersion","v"],["origin","o"],["sessionName","s"],["timing","t"]])if(void 0===p[t])throw new e(`missing mandatory "${r}=" line`,0,"");return p},S={parse:N,isValid:(e,t=!1)=>{try{return N(e),!0}catch(e){return t&&console.error(e),!1}}},U=e=>`c=${e.netType} ${e.addrType} ${e.connectionAddress}`,M=e=>`b=${e.bwtype}:${e.bandwidth}`,q=e=>`k=${e.method}${void 0!==e.encryptionKey?`:${e.encryptionKey}`:""}`,K=e=>Object.entries(e).map((([e,t])=>null===t?e:`${e}=${t}`)).join(";"),V=e=>{var t,r;return[`t=${e.startTime} ${e.stopTime}`,...(null!==(t=e.repeat)&&void 0!==t?t:[]).map((e=>`r=${[e.repeatInterval,e.activeDuration,...e.offsets].join(" ")}`)),...(null===(r=e.zoneAdjustments)||void 0===r?void 0:r.length)?[`z=${e.zoneAdjustments.map((e=>`${e.adjustmentTime} ${e.offset}`)).join(" ")}`]:[]]},D=e=>{var t;return[...void 0!==e.encodingName?[`a=rtpmap:${e.payloadType} ${e.encodingName}/${e.clockRate}${void 0!==e.encodingParameters?`/${e.encodingParameters}`:""}`]:[],...(null!==(t=e.rtcpFeedback)&&void 0!==t?t:[]).map((t=>`a=rtcp-fb:${e.payloadType} ${t}`)),...void 0!==e.parameters?[`a=fmtp:${e.payloadType} ${K(e.parameters)}`]:[]]},L=e=>{const t=[...e.payloadTypes?[`pt=${e.payloadTypes.join(",")}`]:[],...e.restrictions&&Object.keys(e.restrictions).length?[K(e.restrictions)]:[]].join(";");return`a=rid:${e.id} ${e.direction}${t?` ${t}`:""}`},W=(e,t)=>e?[`a=${t}`]:[],G=e=>{var t;return[...void 0!==e.iceUfrag?[`a=ice-ufrag:${e.iceUfrag}`]:[],...void 0!==e.icePwd?[`a=ice-pwd:${e.icePwd}`]:[],...e.iceOptions?[`a=ice-options:${e.iceOptions.join(" ")}`]:[],...(null!==(t=e.fingerprints)&&void 0!==t?t:[]).map((e=>`a=fingerprint:${e.hashFunction} ${e.fingerprint}`)),...void 0!==e.setup?[`a=setup:${e.setup}`]:[]]},B=e=>{var t;return[...(null!==(t=e.extmap)&&void 0!==t?t:[]).map((e=>`a=extmap:${e.id}${e.direction?`/${e.direction}`:""} ${e.uri}${void 0!==e.attributes?` ${e.attributes}`:""}`)),...W(e.extmapAllowMixed,"extmap-allow-mixed")]},Z=e=>{var t;return(null!==(t=e.attributes)&&void 0!==t?t:[]).map((e=>void 0===e.value?`a=${e.name}`:`a=${e.name}:${e.value}`))},_=e=>{var t,r,i,s,n,o,a,d;const c=e.codecs?e.codecs.map((e=>String(e.payloadType))):e.formats;if(!c||0===c.length)throw new Error(`media section "${e.type}" has neither codecs nor formats`);return[`m=${e.type} ${e.port}${void 0!==e.numberOfPorts?`/${e.numberOfPorts}`:""} ${e.proto} ${c.join(" ")}`,...void 0!==e.information?[`i=${e.information}`]:[],...e.connectionInformation?[U(e.connectionInformation)]:[],...(null!==(t=e.bandwidths)&&void 0!==t?t:[]).map(M),...e.encryptionKey?[q(e.encryptionKey)]:[],...e.rtcp?[`a=rtcp:${e.rtcp.port}${void 0!==e.rtcp.netType?` ${e.rtcp.netType} ${e.rtcp.addrType} ${e.rtcp.address}`:""}`]:[],...G(e),...void 0!==e.mid?[`a=mid:${e.mid}`]:[],...B(e),...e.direction?[`a=${e.direction}`]:[],...(null!==(r=e.msid)&&void 0!==r?r:[]).map((e=>`a=msid:${e.streamId}${void 0!==e.trackId?` ${e.trackId}`:""}`)),...W(e.rtcpMux,"rtcp-mux"),...W(e.rtcpRsize,"rtcp-rsize"),...(null!==(i=e.codecs)&&void 0!==i?i:[]).flatMap(D),...(null!==(s=e.rtcpFeedbackWildcard)&&void 0!==s?s:[]).map((e=>`a=rtcp-fb:* ${e}`)),...void 0!==e.sctpPort?[`a=sctp-port:${e.sctpPort}`]:[],...void 0!==e.maxMessageSize?[`a=max-message-size:${e.maxMessageSize}`]:[],...(null!==(n=e.rids)&&void 0!==n?n:[]).map(L),...e.simulcast?[(p=e.simulcast,`a=simulcast:${["send","recv"].filter((e=>p[e])).map((e=>`${e} ${p[e].map((e=>e.map((e=>`${e.paused?"~":""}${e.rid}`)).join(","))).join(";")}`)).join(" ")}`)]:[],...(null!==(o=e.ssrcGroups)&&void 0!==o?o:[]).map((e=>`a=ssrc-group:${[e.semantics,...e.ssrcs].join(" ")}`)),...(null!==(a=e.ssrcs)&&void 0!==a?a:[]).flatMap((e=>Object.entries(e.attributes).map((([t,r])=>`a=ssrc:${e.id} ${t}${null===r?"":`:${r}`}`)))),...(null!==(d=e.candidates)&&void 0!==d?d:[]).map((e=>`a=candidate:${(e=>{var t;return[e.foundation,e.component,e.transport,e.priority,e.address,e.port,"typ",e.type,...void 0!==e.relatedAddress?["raddr",e.relatedAddress]:[],...void 0!==e.relatedPort?["rport",e.relatedPort]:[],...void 0!==e.tcpType?["tcptype",e.tcpType]:[],...Object.entries(null!==(t=e.extensions)&&void 0!==t?t:{}).flat()].join(" ")})(e)}`)),...W(e.endOfCandidates,"end-of-candidates"),...Z(e)];var p},H={write:e=>{var t,r,i,s,n,o,a,d;const{origin:c}=e;if(!c)throw new Error("origin (o=) is required");if(!(null===(t=e.timing)||void 0===t?void 0:t.length))throw new Error("at least one timing (t=) entry is required");return[`v=${null!==(r=e.protocolVersion)&&void 0!==r?r:0}`,`o=${c.username} ${c.sessId} ${c.sessVersion} ${c.netType} ${c.addrType} ${c.unicastAddress}`,`s=${null!==(i=e.sessionName)&&void 0!==i?i:"-"}`,...void 0!==e.sessionInformation?[`i=${e.sessionInformation}`]:[],...void 0!==e.uri?[`u=${e.uri}`]:[],...(null!==(s=e.emailAddresses)&&void 0!==s?s:[]).map((e=>`e=${e}`)),...(null!==(n=e.phoneNumbers)&&void 0!==n?n:[]).map((e=>`p=${e}`)),...e.connectionInformation?[U(e.connectionInformation)]:[],...(null!==(o=e.bandwidths)&&void 0!==o?o:[]).map(M),...e.timing.flatMap(V),...e.encryptionKey?[q(e.encryptionKey)]:[],...(null!==(a=e.group)&&void 0!==a?a:[]).map((e=>`a=group:${[e.semantics,...e.mids].join(" ")}`)),...W(e.iceLite,"ice-lite"),...G(e),...B(e),...e.direction?[`a=${e.direction}`]:[],...W(e.endOfCandidates,"end-of-candidates"),...Z(e),...(null!==(d=e.media)&&void 0!==d?d:[]).flatMap(_)].join("\r\n")+"\r\n"}},J={md2:16,md5:16,"sha-1":20,"sha-224":28,"sha-256":32,"sha-384":48,"sha-512":64},Q=/^[0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2})*$/,X={validate:(t,r={})=>{var i,s,n,o;let d;if("string"==typeof t)try{d=S.parse(t)}catch(t){if(t instanceof e)return[{severity:"error",code:"parse-error",message:t.message,lineNumber:t.lineNumber}];throw t}else d=t;const c=[],l=(e,t,r)=>c.push(void 0===r?{severity:"error",code:e,message:t}:{severity:"error",code:e,message:t,mediaIndex:r}),m=(e,t,r)=>c.push(void 0===r?{severity:"warning",code:e,message:t}:{severity:"warning",code:e,message:t,mediaIndex:r}),u=null!==(i=d.media)&&void 0!==i?i:[];0!==d.protocolVersion&&l("invalid-version",`v= must be 0, got ${d.protocolVersion}`),d.origin||l("missing-origin","o= is required"),void 0===d.sessionName?l("missing-session-name","s= is required"):""===d.sessionName&&m("empty-session-name",'s= must not be empty; use "s= " or "s=-" (RFC 8866 §5.3)'),(null===(s=d.timing)||void 0===s?void 0:s.length)||l("missing-timing","at least one t= line is required"),d.connectionInformation||u.forEach(((e,t)=>{e.connectionInformation||l("missing-connection","c= is required at session level or in every media description (RFC 8866 §5.7)",t)}));const f=new Set;u.forEach(((e,t)=>{void 0!==e.mid&&(f.has(e.mid)&&l("duplicate-mid",`a=mid:${e.mid} is used by more than one media description`,t),f.add(e.mid))}));for(const e of null!==(n=d.group)&&void 0!==n?n:[])for(const t of e.mids)f.has(t)||l("group-unknown-mid",`a=group:${e.semantics} references unknown mid "${t}"`);(null===(o=d.group)||void 0===o?void 0:o.some((e=>"BUNDLE"===e.semantics)))&&u.forEach(((e,t)=>{void 0===e.mid&&l("bundle-missing-mid","every media description needs a=mid when BUNDLE is used (RFC 9143)",t)}));const v=void 0!==d.iceUfrag||void 0!==d.icePwd||d.iceLite||u.some((e=>{var t;return void 0!==e.iceUfrag||void 0!==e.icePwd||(null===(t=e.candidates)||void 0===t?void 0:t.length)})),h=(e,t,r)=>{void 0!==e&&(e.length<4||e.length>256)&&l("ice-ufrag-length",`ice-ufrag must be 4-256 characters, got ${e.length} (RFC 8839 §5.4)`,r),void 0!==t&&(t.length<22||t.length>256)&&l("ice-pwd-length",`ice-pwd must be 22-256 characters, got ${t.length} (RFC 8839 §5.4)`,r)};h(d.iceUfrag,d.icePwd);const g=(e,t)=>{var r;for(const{hashFunction:i,fingerprint:s}of null!==(r=e.fingerprints)&&void 0!==r?r:[]){const e=p(J,i.toLowerCase());Q.test(s)?void 0!==e&&s.split(":").length!==e?l("invalid-fingerprint",`a=fingerprint:${i} has ${s.split(":").length} bytes, expected ${e} (RFC 8122 §5)`,t):s!==s.toUpperCase()&&m("fingerprint-not-uppercase",`a=fingerprint:${i} must use upper-case hex (RFC 8122 §5)`,t):l("invalid-fingerprint",`a=fingerprint:${i} must be colon-separated hex pairs (RFC 8122 §5)`,t)}};return g(d),u.forEach(((e,t)=>{var i,s,n,o,c,p,u,f,$,w,y,b,x,k,E,T;const j=0!==e.port;if(h(e.iceUfrag,e.icePwd,t),g(e,t),v&&j&&(void 0===(null!==(i=e.iceUfrag)&&void 0!==i?i:d.iceUfrag)&&l("missing-ice-ufrag","a=ice-ufrag is required (RFC 8839 §5.4)",t),void 0===(null!==(s=e.icePwd)&&void 0!==s?s:d.icePwd)&&l("missing-ice-pwd","a=ice-pwd is required (RFC 8839 §5.4)",t)),(e=>/(^|\/)(D?TLS)(\/|$)/.test(e.proto))(e)&&j){(null===(n=e.fingerprints)||void 0===n?void 0:n.length)||(null===(o=d.fingerprints)||void 0===o?void 0:o.length)||l("missing-fingerprint",`a=fingerprint is required for ${e.proto} (RFC 8842 §5.2, §5.3)`,t);const i=null!==(c=e.setup)&&void 0!==c?c:d.setup;void 0===i?l("missing-setup",`a=setup is required for ${e.proto} (RFC 8842 §5.2, §5.3)`,t):"answer"===r.type&&"actpass"===i&&l("setup-actpass-in-answer","an answer must use a=setup:active or a=setup:passive (RFC 5763 §5)",t)}if(a(e.proto)){for(const r of null!==(p=e.codecs)&&void 0!==p?p:[])r.payloadType>34&&void 0===r.encodingName&&l("missing-rtpmap",`payload type ${r.payloadType} is dynamic and needs a=rtpmap (RFC 8866 §6.6)`,t);for(const r of null!==(u=e.attributes)&&void 0!==u?u:[])"rtpmap"!==r.name&&"fmtp"!==r.name&&"rtcp-fb"!==r.name||m("unknown-payload-type",`a=${r.name}:${r.value} refers to a payload type that is not in the m= line, or repeats`,t)}const P=new Set;for(const r of null!==(f=e.extmap)&&void 0!==f?f:[])(r.id<1||15===r.id||r.id>255)&&l("extmap-invalid-id",`extmap id ${r.id} must be 1-14 or 16-255 (RFC 8285)`,t),P.has(r.id)&&l("extmap-duplicate-id",`extmap id ${r.id} is used more than once`,t),P.add(r.id);for(const r of["send","recv"])for(const i of(null!==(w=null===($=e.simulcast)||void 0===$?void 0:$[r])&&void 0!==w?w:[]).flat())(null===(y=e.rids)||void 0===y?void 0:y.some((e=>e.id===i.rid&&e.direction===r)))||l("simulcast-unknown-rid",`a=simulcast ${r} references "${i.rid}" without a matching a=rid:${i.rid} ${r} (RFC 8853 §5.1)`,t);for(const r of null!==(b=e.rids)&&void 0!==b?b:[])for(const i of null!==(x=r.payloadTypes)&&void 0!==x?x:[])(null===(k=e.codecs)||void 0===k?void 0:k.some((e=>e.payloadType===i)))||l("rid-unknown-payload-type",`a=rid:${r.id} references payload type ${i} that is not in the m= line (RFC 8851)`,t);for(const r of null!==(E=e.ssrcGroups)&&void 0!==E?E:[])for(const i of r.ssrcs)(null===(T=e.ssrcs)||void 0===T?void 0:T.some((e=>e.id===i)))||m("ssrc-group-unknown-ssrc",`a=ssrc-group:${r.semantics} references ssrc ${i} without an a=ssrc line`,t)})),c}},Y="1.1.0";export{e as SdpParseError,S as sdpReader,X as sdpValidator,H as sdpWriter,Y as version};
|
|
1
|
+
class e extends Error{constructor(e,t,r){super(t>0?`SDP line ${t} "${r}": ${e}`:`SDP: ${e}`),this.name="SdpParseError",this.reason=e,this.lineNumber=t,this.line=r}}const t=/^\d+$/,r=/^\d+$/,i=(e,t="value")=>{if(!r.test(e))throw new Error(`${t} "${e}" is not a non-negative integer`);return parseInt(e,10)},s={d:86400,h:3600,m:60,s:1},n=(e,t=!1)=>{if(t&&e.startsWith("-"))return-n(e.slice(1));const r=e.slice(-1);return r in s?s[r]*i(e.slice(0,-1),"time"):i(e,"time")},o=e=>{const t=e.indexOf(":"),r=-1===t?e:e.slice(0,t);if(0===r.length)throw new Error("empty key method");return-1===t?{method:r}:{method:r,encryptionKey:e.slice(t+1)}},a=e=>/(^|\/)RTP\//.test(e),d=e=>{const t=e.indexOf(":");if(t<=0)throw new Error('expected "<bwtype>:<bandwidth>"');return{bwtype:e.slice(0,t),bandwidth:i(e.slice(t+1),"bandwidth")}},c=e=>{const t=e.split(" ");if(3!==t.length)throw new Error('expected "<nettype> <addrtype> <connection-address>"');const[r,i,s]=t;return{netType:r,addrType:i,connectionAddress:s}},p=(e,t)=>Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0,l=(e,t,r)=>{Object.defineProperty(e,t,{value:r,writable:!0,enumerable:!0,configurable:!0})},m=(e,t)=>{if(void 0===e||0===e.length)throw new Error(`expected a value: ${t}`);return e},u=e=>{if(void 0!==e)throw new Error("flag attribute must not have a value");return!0},f=e=>{const t=e.indexOf(" ");return-1===t?[e,void 0]:[e.slice(0,t),e.slice(t+1)]},v=(e,t)=>{var r;const s=i(t,"payload type");return null===(r=e.codecs)||void 0===r?void 0:r.find((e=>e.payloadType===s))},h=(e,t,r,i)=>{if(void 0!==e[t])throw new Error(`duplicate a=${i}`);e[t]=r},g=(e,t,r)=>{void 0===e[t]&&(e[t]=[]),e[t].push(r)},$=["sendrecv","sendonly","recvonly","inactive"],w=e=>{const t={};for(const r of e.split(";")){const e=r.trim();if(0===e.length)continue;const i=e.indexOf("=");-1===i?l(t,e,null):l(t,e.slice(0,i),e.slice(i+1))}return t},y='"<foundation> <component> <transport> <priority> <address> <port> typ <type> [raddr <addr>] [rport <port>] *(<ext-name> <ext-value>)"',b=e=>{const t=i(e,"ssrc-id");if(t>4294967295)throw new Error(`ssrc-id ${t} does not fit into 32 bits`);return t},x=/^[A-Za-z0-9_-]+$/,k=["session","media"],E=["media"],T=["session"],j=["active","passive","actpass","holdconn"],P=e=>({scopes:k,parse:t=>h(t,"direction",e,e)}),F={sendrecv:P("sendrecv"),sendonly:P("sendonly"),recvonly:P("recvonly"),inactive:P("inactive"),"ice-ufrag":{scopes:k,parse:(e,t)=>h(e,"iceUfrag",m(t,"a=ice-ufrag:<ufrag>"),"ice-ufrag")},"ice-pwd":{scopes:k,parse:(e,t)=>h(e,"icePwd",m(t,"a=ice-pwd:<password>"),"ice-pwd")},"ice-options":{scopes:k,parse:(e,t)=>h(e,"iceOptions",m(t,"a=ice-options:<option> ...").split(" "),"ice-options")},"ice-lite":{scopes:T,parse:(e,t)=>h(e,"iceLite",u(t),"ice-lite")},candidate:{scopes:E,parse:(e,t)=>g(e,"candidates",(e=>{var t;const r=e.split(" ");if(r.length<8||"typ"!==r[6]||r.length%2!=0)throw new Error(`expected ${y}`);const[s,n,o,a,d,c,,p]=r,m={foundation:s,component:i(n,"component id"),transport:o,priority:i(a,"priority"),address:d,port:i(c,"port"),type:p};for(let e=8;e<r.length;e+=2){const[s,n]=[r[e],r[e+1]];if(0===s.length||0===n.length)throw new Error(`expected ${y}`);switch(s){case"raddr":m.relatedAddress=n;break;case"rport":m.relatedPort=i(n,"rport");break;case"tcptype":m.tcpType=n;break;default:l(null!==(t=m.extensions)&&void 0!==t?t:m.extensions={},s,n)}}return m})(m(t,"a=candidate:<candidate>")))},"end-of-candidates":{scopes:k,parse:(e,t)=>h(e,"endOfCandidates",u(t),"end-of-candidates")},fingerprint:{scopes:k,parse:(e,t)=>{const[r,i]=f(m(t,"a=fingerprint:<hash-function> <fingerprint>"));if(!i||i.includes(" "))throw new Error('expected "a=fingerprint:<hash-function> <fingerprint>"');g(e,"fingerprints",{hashFunction:r,fingerprint:i})}},setup:{scopes:k,parse:(e,t)=>{if(!j.includes(t))throw new Error(`a=setup must be one of ${j.join(", ")}`);h(e,"setup",t,"setup")}},extmap:{scopes:k,parse:(e,t)=>g(e,"extmap",(e=>{const[t,r]=f(e);if(!r)throw new Error('expected "<id>[/<direction>] <uri> [<attributes>]"');const[s,n,...o]=t.split("/");if(o.length||void 0!==n&&!$.includes(n))throw new Error(`invalid extmap id "${t}"`);const[a,d]=f(r),c={id:i(s,"extmap id"),uri:a};return void 0!==n&&(c.direction=n),void 0!==d&&(c.attributes=d),c})(m(t,"a=extmap:<id> <uri>")))},group:{scopes:T,parse:(e,t)=>g(e,"group",(e=>{const[t,...r]=e.split(" ");if(0===t.length||r.some((e=>0===e.length)))throw new Error('expected "<semantics> <mid> ..."');return{semantics:t,mids:r}})(m(t,"a=group:<semantics> <mid> ...")))},mid:{scopes:E,parse:(e,t)=>h(e,"mid",m(t,"a=mid:<identification-tag>"),"mid")},msid:{scopes:E,parse:(e,t)=>g(e,"msid",(e=>{const[t,r,...i]=e.split(" ");if(0===t.length||""===r||i.length)throw new Error('expected "<stream id> [<track id>]"');return void 0===r?{streamId:t}:{streamId:t,trackId:r}})(m(t,"a=msid:<stream id> [<track id>]")))},ssrc:{scopes:E,parse:(e,t)=>((e,t)=>{var r;const[i,s]=f(t);if(!s)throw new Error('expected "<ssrc-id> <attribute>[:<value>]"');const n=b(i),o=s.indexOf(":"),a=-1===o?s:s.slice(0,o),d=-1===o?null:s.slice(o+1),c=null!==(r=e.ssrcs)&&void 0!==r?r:e.ssrcs=[];let m=c.find((e=>e.id===n));return m||(m={id:n,attributes:{}},c.push(m)),void 0===p(m.attributes,a)&&(l(m.attributes,a,d),!0)})(e,m(t,"a=ssrc:<ssrc-id> <attribute>[:<value>]"))},"ssrc-group":{scopes:E,parse:(e,t)=>g(e,"ssrcGroups",(e=>{const[t,...r]=e.split(" ");if(0===t.length)throw new Error('expected "<semantics> <ssrc-id> ..."');return{semantics:t,ssrcs:r.map(b)}})(m(t,"a=ssrc-group:<semantics> <ssrc-id> ...")))},rid:{scopes:E,parse:(e,t)=>g(e,"rids",(e=>{const[t,r]=f(e),[s,n]=r?f(r):[void 0,void 0];if(!x.test(t)||"send"!==s&&"recv"!==s||""===n)throw new Error('expected "<rid-id> <send|recv> [<params>]"');const o={id:t,direction:s};if(void 0!==n){const e=w(n);if("pt"in e){if(!n.startsWith("pt=")||null===e.pt)throw new Error('"pt=" must be the first rid parameter');o.payloadTypes=e.pt.split(",").map((e=>i(e,"payload type"))),delete e.pt}Object.keys(e).length&&(o.restrictions=e)}return o})(m(t,"a=rid:<rid-id> <send|recv>")))},simulcast:{scopes:E,parse:(e,t)=>h(e,"simulcast",(e=>{const t=e.split(" ");if(2!==t.length&&4!==t.length)throw new Error('expected "send|recv <streams> [send|recv <streams>]"');const r={};for(let e=0;e<t.length;e+=2){const i=t[e];if("send"!==i&&"recv"!==i||r[i])throw new Error('expected "send|recv <streams> [send|recv <streams>]"');r[i]=t[e+1].split(";").map((e=>e.split(",").map((e=>{const t=e.startsWith("~"),r=t?e.slice(1):e;if(!x.test(r))throw new Error(`invalid simulcast rid "${e}"`);return t?{rid:r,paused:t}:{rid:r}}))))}return r})(m(t,"a=simulcast:<send|recv> <streams>")),"simulcast")},"extmap-allow-mixed":{scopes:k,parse:(e,t)=>h(e,"extmapAllowMixed",u(t),"extmap-allow-mixed")},"rtcp-mux":{scopes:E,parse:(e,t)=>h(e,"rtcpMux",u(t),"rtcp-mux")},"rtcp-rsize":{scopes:E,parse:(e,t)=>h(e,"rtcpRsize",u(t),"rtcp-rsize")},rtcp:{scopes:E,parse:(e,t)=>{const r=m(t,"a=rtcp:<port> [<nettype> <addrtype> <address>]").split(" ");if(1!==r.length&&4!==r.length)throw new Error('expected "a=rtcp:<port> [<nettype> <addrtype> <address>]"');const[s,n,o,a]=r;h(e,"rtcp",1===r.length?{port:i(s,"port")}:{port:i(s,"port"),netType:n,addrType:o,address:a},"rtcp")}},"sctp-port":{scopes:E,parse:(e,t)=>h(e,"sctpPort",i(m(t,"a=sctp-port:<port>"),"sctp-port"),"sctp-port")},"max-message-size":{scopes:E,parse:(e,t)=>h(e,"maxMessageSize",i(m(t,"a=max-message-size:<size>"),"max-message-size"),"max-message-size")},rtpmap:{scopes:E,parse:(e,t)=>{const[r,s]=f(m(t,"a=rtpmap:<payload type> <encoding>")),n=e.codecs?v(e,r):void 0;if(!n||void 0!==n.encodingName)return!1;((e,t)=>{const[r,s,n,...o]=t.split("/");if(!r||void 0===s||o.length)throw new Error('expected "<payload type> <encoding name>/<clock rate>[/<encoding parameters>]"');e.encodingName=r,e.clockRate=i(s,"clock rate"),void 0!==n&&(e.encodingParameters=i(n,"encoding parameters"))})(n,null!=s?s:"")}},fmtp:{scopes:E,parse:(e,t)=>{const[r,i]=f(m(t,"a=fmtp:<format> <parameters>")),s=e.codecs?v(e,r):void 0;if(!s||void 0!==s.parameters||void 0===i)return!1;s.parameters=w(i)}},"rtcp-fb":{scopes:E,parse:(e,t)=>{const[r,i]=f(m(t,"a=rtcp-fb:<payload type> <feedback>"));if(!i||!e.codecs)return!1;if("*"===r)return void g(e,"rtcpFeedbackWildcard",i);const s=v(e,r);if(!s)return!1;g(s,"rtcpFeedback",i)}}},z=(e,t,r)=>{const i=r.indexOf(":"),s=-1===i?r:r.slice(0,i),n=-1===i?void 0:r.slice(i+1);if(0===s.length)throw new Error("empty attribute name");const o=p(F,s);o&&o.scopes.includes(t)&&!1!==o.parse(e,n)||g(e,"attributes",void 0===n?{name:s}:{name:s,value:n})},I={v:0,o:1,s:2,i:3,u:4,e:5,p:6,c:7,b:8,t:9,r:10,z:11,k:12,a:13},O=new Set(["e","p","b","a"]),C={m:0,i:1,c:2,b:3,k:4,a:5},R=new Set(["b","a"]),A={session:"v o s i u e p c b t r z k a",media:"m i c b k a"},N=r=>{const s=r.split(/\r?\n/);""===s[s.length-1]&&s.pop();const p={media:[]};let l=null,m=null,u=-1;s.forEach(((r,s)=>{var f,v,h,g,$,w,y;const b=t=>{throw new e(t,s+1,r)};0===r.length&&b("empty line"),(r.length<2||"="!==r[1])&&b('expected "<type>=<value>"');const x=r[0],k=r.slice(2);if(0===s&&"v"!==x&&b('a session description must start with "v="'),"m"===x)u=0;else{const e=l?"media":"session",t=l?R:O,r=(l?C:I)[x];void 0===r&&b(x in I?`"${x}=" is not allowed in a media description`:`unknown line type "${x}"`),l||"t"!==x?l||"r"!==x&&"z"!==x?r<u?b(`line is out of order (${e} order: ${A[e]})`):r!==u||t.has(x)||b(l&&"c"===x?'multiple "c=" lines in one media description are not supported':`duplicate "${x}=" line`):u!==I.t&&u!==I.r&&b(`"${x}=" must follow "t=" or "r="`):u>I.z&&b(`"t=" must precede "k=" and "a=" (order: ${A.session})`),u=r}try{if(l){switch(x){case"i":l.information=k;break;case"c":l.connectionInformation=c(k);break;case"b":(null!==(f=l.bandwidths)&&void 0!==f?f:l.bandwidths=[]).push(d(k));break;case"k":l.encryptionKey=o(k);break;case"a":z(l,"media",k)}if("m"!==x)return}switch(x){case"v":"0"!==k&&b(`unsupported protocol version "${k}"`),p.protocolVersion=0;break;case"o":p.origin=(e=>{const r=e.split(" ");if(6!==r.length)throw new Error('expected "<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>"');const[i,s,n,o,a,d]=r;if(!t.test(s))throw new Error(`sess-id "${s}" is not numeric`);if(!t.test(n))throw new Error(`sess-version "${n}" is not numeric`);return{username:i,sessId:s,sessVersion:n,netType:o,addrType:a,unicastAddress:d}})(k);break;case"s":p.sessionName=k;break;case"i":p.sessionInformation=k;break;case"u":p.uri=k;break;case"e":(null!==(v=p.emailAddresses)&&void 0!==v?v:p.emailAddresses=[]).push(k);break;case"p":(null!==(h=p.phoneNumbers)&&void 0!==h?h:p.phoneNumbers=[]).push(k);break;case"c":p.connectionInformation=c(k);break;case"b":(null!==(g=p.bandwidths)&&void 0!==g?g:p.bandwidths=[]).push(d(k));break;case"t":m=(e=>{const t=e.split(" ");if(2!==t.length)throw new Error('expected "<start-time> <stop-time>"');return{startTime:i(t[0],"start time"),stopTime:i(t[1],"stop time")}})(k),(null!==($=p.timing)&&void 0!==$?$:p.timing=[]).push(m);break;case"r":(null!==(w=(y=m).repeat)&&void 0!==w?w:y.repeat=[]).push((e=>{const t=e.split(" ");if(t.length<3)throw new Error('expected "<repeat interval> <active duration> <offset>..."');const[r,i,...s]=t.map((e=>n(e)));return{repeatInterval:r,activeDuration:i,offsets:s}})(k));break;case"z":m.zoneAdjustments=(e=>{const t=e.split(" ");if(t.length<2||t.length%2!=0)throw new Error('expected pairs of "<adjustment time> <offset>"');const r=[];for(let e=0;e<t.length;e+=2)r.push({adjustmentTime:i(t[e],"adjustment time"),offset:n(t[e+1],!0)});return r})(k);break;case"k":p.encryptionKey=o(k);break;case"a":z(p,"session",k);break;case"m":l=(e=>{const t=e.split(" ");if(t.length<4)throw new Error('expected "<media> <port> <proto> <fmt> ..."');const[r,s,n,...o]=t,[d,c,...p]=s.split("/");if(p.length)throw new Error(`invalid port "${s}"`);const l={type:r,port:i(d,"port"),proto:n};return void 0!==c&&(l.numberOfPorts=i(c,"number of ports")),a(n)?l.codecs=o.map((e=>{const t=i(e,"payload type");if(t>127)throw new Error(`payload type ${t} is out of range 0-127`);return{payloadType:t}})):l.formats=o,l})(k),p.media.push(l)}}catch(t){if(t instanceof e)throw t;b(t.message)}}));for(const[t,r]of[["protocolVersion","v"],["origin","o"],["sessionName","s"],["timing","t"]])if(void 0===p[t])throw new e(`missing mandatory "${r}=" line`,0,"");return p},S={parse:N,isValid:(e,t=!1)=>{try{return N(e),!0}catch(e){return t&&console.error(e),!1}}},U=e=>`c=${e.netType} ${e.addrType} ${e.connectionAddress}`,M=e=>`b=${e.bwtype}:${e.bandwidth}`,q=e=>`k=${e.method}${void 0!==e.encryptionKey?`:${e.encryptionKey}`:""}`,K=e=>Object.entries(e).map((([e,t])=>null===t?e:`${e}=${t}`)).join(";"),V=e=>{var t,r;return[`t=${e.startTime} ${e.stopTime}`,...(null!==(t=e.repeat)&&void 0!==t?t:[]).map((e=>`r=${[e.repeatInterval,e.activeDuration,...e.offsets].join(" ")}`)),...(null===(r=e.zoneAdjustments)||void 0===r?void 0:r.length)?[`z=${e.zoneAdjustments.map((e=>`${e.adjustmentTime} ${e.offset}`)).join(" ")}`]:[]]},D=e=>{var t;return[...void 0!==e.encodingName?[`a=rtpmap:${e.payloadType} ${e.encodingName}/${e.clockRate}${void 0!==e.encodingParameters?`/${e.encodingParameters}`:""}`]:[],...(null!==(t=e.rtcpFeedback)&&void 0!==t?t:[]).map((t=>`a=rtcp-fb:${e.payloadType} ${t}`)),...void 0!==e.parameters?[`a=fmtp:${e.payloadType} ${K(e.parameters)}`]:[]]},L=e=>{const t=[...e.payloadTypes?[`pt=${e.payloadTypes.join(",")}`]:[],...e.restrictions&&Object.keys(e.restrictions).length?[K(e.restrictions)]:[]].join(";");return`a=rid:${e.id} ${e.direction}${t?` ${t}`:""}`},W=(e,t)=>e?[`a=${t}`]:[],G=e=>{var t;return[...void 0!==e.iceUfrag?[`a=ice-ufrag:${e.iceUfrag}`]:[],...void 0!==e.icePwd?[`a=ice-pwd:${e.icePwd}`]:[],...e.iceOptions?[`a=ice-options:${e.iceOptions.join(" ")}`]:[],...(null!==(t=e.fingerprints)&&void 0!==t?t:[]).map((e=>`a=fingerprint:${e.hashFunction} ${e.fingerprint}`)),...void 0!==e.setup?[`a=setup:${e.setup}`]:[]]},B=e=>{var t;return[...(null!==(t=e.extmap)&&void 0!==t?t:[]).map((e=>`a=extmap:${e.id}${e.direction?`/${e.direction}`:""} ${e.uri}${void 0!==e.attributes?` ${e.attributes}`:""}`)),...W(e.extmapAllowMixed,"extmap-allow-mixed")]},Z=e=>{var t;return(null!==(t=e.attributes)&&void 0!==t?t:[]).map((e=>void 0===e.value?`a=${e.name}`:`a=${e.name}:${e.value}`))},_=e=>{var t,r,i,s,n,o,a,d;const c=e.codecs?e.codecs.map((e=>String(e.payloadType))):e.formats;if(!c||0===c.length)throw new Error(`media section "${e.type}" has neither codecs nor formats`);return[`m=${e.type} ${e.port}${void 0!==e.numberOfPorts?`/${e.numberOfPorts}`:""} ${e.proto} ${c.join(" ")}`,...void 0!==e.information?[`i=${e.information}`]:[],...e.connectionInformation?[U(e.connectionInformation)]:[],...(null!==(t=e.bandwidths)&&void 0!==t?t:[]).map(M),...e.encryptionKey?[q(e.encryptionKey)]:[],...e.rtcp?[`a=rtcp:${e.rtcp.port}${void 0!==e.rtcp.netType?` ${e.rtcp.netType} ${e.rtcp.addrType} ${e.rtcp.address}`:""}`]:[],...G(e),...void 0!==e.mid?[`a=mid:${e.mid}`]:[],...B(e),...e.direction?[`a=${e.direction}`]:[],...(null!==(r=e.msid)&&void 0!==r?r:[]).map((e=>`a=msid:${e.streamId}${void 0!==e.trackId?` ${e.trackId}`:""}`)),...W(e.rtcpMux,"rtcp-mux"),...W(e.rtcpRsize,"rtcp-rsize"),...(null!==(i=e.codecs)&&void 0!==i?i:[]).flatMap(D),...(null!==(s=e.rtcpFeedbackWildcard)&&void 0!==s?s:[]).map((e=>`a=rtcp-fb:* ${e}`)),...void 0!==e.sctpPort?[`a=sctp-port:${e.sctpPort}`]:[],...void 0!==e.maxMessageSize?[`a=max-message-size:${e.maxMessageSize}`]:[],...(null!==(n=e.rids)&&void 0!==n?n:[]).map(L),...e.simulcast?[(p=e.simulcast,`a=simulcast:${["send","recv"].filter((e=>p[e])).map((e=>`${e} ${p[e].map((e=>e.map((e=>`${e.paused?"~":""}${e.rid}`)).join(","))).join(";")}`)).join(" ")}`)]:[],...(null!==(o=e.ssrcGroups)&&void 0!==o?o:[]).map((e=>`a=ssrc-group:${[e.semantics,...e.ssrcs].join(" ")}`)),...(null!==(a=e.ssrcs)&&void 0!==a?a:[]).flatMap((e=>Object.entries(e.attributes).map((([t,r])=>`a=ssrc:${e.id} ${t}${null===r?"":`:${r}`}`)))),...(null!==(d=e.candidates)&&void 0!==d?d:[]).map((e=>`a=candidate:${(e=>{var t;return[e.foundation,e.component,e.transport,e.priority,e.address,e.port,"typ",e.type,...void 0!==e.relatedAddress?["raddr",e.relatedAddress]:[],...void 0!==e.relatedPort?["rport",e.relatedPort]:[],...void 0!==e.tcpType?["tcptype",e.tcpType]:[],...Object.entries(null!==(t=e.extensions)&&void 0!==t?t:{}).flat()].join(" ")})(e)}`)),...W(e.endOfCandidates,"end-of-candidates"),...Z(e)];var p},H={write:e=>{var t,r,i,s,n,o,a,d;const{origin:c}=e;if(!c)throw new Error("origin (o=) is required");if(!(null===(t=e.timing)||void 0===t?void 0:t.length))throw new Error("at least one timing (t=) entry is required");return[`v=${null!==(r=e.protocolVersion)&&void 0!==r?r:0}`,`o=${c.username} ${c.sessId} ${c.sessVersion} ${c.netType} ${c.addrType} ${c.unicastAddress}`,`s=${null!==(i=e.sessionName)&&void 0!==i?i:"-"}`,...void 0!==e.sessionInformation?[`i=${e.sessionInformation}`]:[],...void 0!==e.uri?[`u=${e.uri}`]:[],...(null!==(s=e.emailAddresses)&&void 0!==s?s:[]).map((e=>`e=${e}`)),...(null!==(n=e.phoneNumbers)&&void 0!==n?n:[]).map((e=>`p=${e}`)),...e.connectionInformation?[U(e.connectionInformation)]:[],...(null!==(o=e.bandwidths)&&void 0!==o?o:[]).map(M),...e.timing.flatMap(V),...e.encryptionKey?[q(e.encryptionKey)]:[],...(null!==(a=e.group)&&void 0!==a?a:[]).map((e=>`a=group:${[e.semantics,...e.mids].join(" ")}`)),...W(e.iceLite,"ice-lite"),...G(e),...B(e),...e.direction?[`a=${e.direction}`]:[],...W(e.endOfCandidates,"end-of-candidates"),...Z(e),...(null!==(d=e.media)&&void 0!==d?d:[]).flatMap(_)].join("\r\n")+"\r\n"}},J={md2:16,md5:16,"sha-1":20,"sha-224":28,"sha-256":32,"sha-384":48,"sha-512":64},Q=/^[0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2})*$/,X={validate:(t,r={})=>{var i,s,n,o;let d;if("string"==typeof t)try{d=S.parse(t)}catch(t){if(t instanceof e)return[{severity:"error",code:"parse-error",message:t.message,lineNumber:t.lineNumber}];throw t}else d=t;const c=[],l=(e,t,r)=>c.push(void 0===r?{severity:"error",code:e,message:t}:{severity:"error",code:e,message:t,mediaIndex:r}),m=(e,t,r)=>c.push(void 0===r?{severity:"warning",code:e,message:t}:{severity:"warning",code:e,message:t,mediaIndex:r}),u=null!==(i=d.media)&&void 0!==i?i:[];0!==d.protocolVersion&&l("invalid-version",`v= must be 0, got ${d.protocolVersion}`),d.origin||l("missing-origin","o= is required"),void 0===d.sessionName?l("missing-session-name","s= is required"):""===d.sessionName&&m("empty-session-name",'s= must not be empty; use "s= " or "s=-" (RFC 8866 §5.3)'),(null===(s=d.timing)||void 0===s?void 0:s.length)||l("missing-timing","at least one t= line is required"),d.connectionInformation||u.forEach(((e,t)=>{e.connectionInformation||l("missing-connection","c= is required at session level or in every media description (RFC 8866 §5.7)",t)}));const f=new Set;u.forEach(((e,t)=>{void 0!==e.mid&&(f.has(e.mid)&&l("duplicate-mid",`a=mid:${e.mid} is used by more than one media description`,t),f.add(e.mid))}));for(const e of null!==(n=d.group)&&void 0!==n?n:[])for(const t of e.mids)f.has(t)||l("group-unknown-mid",`a=group:${e.semantics} references unknown mid "${t}"`);(null===(o=d.group)||void 0===o?void 0:o.some((e=>"BUNDLE"===e.semantics)))&&u.forEach(((e,t)=>{void 0===e.mid&&l("bundle-missing-mid","every media description needs a=mid when BUNDLE is used (RFC 9143)",t)}));const v=void 0!==d.iceUfrag||void 0!==d.icePwd||d.iceLite||u.some((e=>{var t;return void 0!==e.iceUfrag||void 0!==e.icePwd||(null===(t=e.candidates)||void 0===t?void 0:t.length)})),h=(e,t,r)=>{void 0!==e&&(e.length<4||e.length>256)&&l("ice-ufrag-length",`ice-ufrag must be 4-256 characters, got ${e.length} (RFC 8839 §5.4)`,r),void 0!==t&&(t.length<22||t.length>256)&&l("ice-pwd-length",`ice-pwd must be 22-256 characters, got ${t.length} (RFC 8839 §5.4)`,r)};h(d.iceUfrag,d.icePwd);const g=(e,t)=>{var r;for(const{hashFunction:i,fingerprint:s}of null!==(r=e.fingerprints)&&void 0!==r?r:[]){const e=p(J,i.toLowerCase());Q.test(s)?void 0!==e&&s.split(":").length!==e?l("invalid-fingerprint",`a=fingerprint:${i} has ${s.split(":").length} bytes, expected ${e} (RFC 8122 §5)`,t):s!==s.toUpperCase()&&m("fingerprint-not-uppercase",`a=fingerprint:${i} must use upper-case hex (RFC 8122 §5)`,t):l("invalid-fingerprint",`a=fingerprint:${i} must be colon-separated hex pairs (RFC 8122 §5)`,t)}};return g(d),u.forEach(((e,t)=>{var i,s,n,o,c,p,u,f,$,w,y,b,x,k,E,T;const j=0!==e.port;if(h(e.iceUfrag,e.icePwd,t),g(e,t),v&&j&&(void 0===(null!==(i=e.iceUfrag)&&void 0!==i?i:d.iceUfrag)&&l("missing-ice-ufrag","a=ice-ufrag is required (RFC 8839 §5.4)",t),void 0===(null!==(s=e.icePwd)&&void 0!==s?s:d.icePwd)&&l("missing-ice-pwd","a=ice-pwd is required (RFC 8839 §5.4)",t)),(e=>/(^|\/)(D?TLS)(\/|$)/.test(e.proto))(e)&&j){(null===(n=e.fingerprints)||void 0===n?void 0:n.length)||(null===(o=d.fingerprints)||void 0===o?void 0:o.length)||l("missing-fingerprint",`a=fingerprint is required for ${e.proto} (RFC 8842 §5.2, §5.3)`,t);const i=null!==(c=e.setup)&&void 0!==c?c:d.setup;void 0===i?l("missing-setup",`a=setup is required for ${e.proto} (RFC 8842 §5.2, §5.3)`,t):"answer"===r.type&&"actpass"===i&&l("setup-actpass-in-answer","an answer must use a=setup:active or a=setup:passive (RFC 5763 §5)",t)}if(a(e.proto)){for(const r of null!==(p=e.codecs)&&void 0!==p?p:[])r.payloadType>34&&void 0===r.encodingName&&l("missing-rtpmap",`payload type ${r.payloadType} is dynamic and needs a=rtpmap (RFC 8866 §6.6)`,t);for(const r of null!==(u=e.attributes)&&void 0!==u?u:[])"rtpmap"!==r.name&&"fmtp"!==r.name&&"rtcp-fb"!==r.name||m("unknown-payload-type",`a=${r.name}:${r.value} refers to a payload type that is not in the m= line, or repeats`,t)}const P=new Set;for(const r of null!==(f=e.extmap)&&void 0!==f?f:[])(r.id<1||15===r.id||r.id>255)&&l("extmap-invalid-id",`extmap id ${r.id} must be 1-14 or 16-255 (RFC 8285)`,t),P.has(r.id)&&l("extmap-duplicate-id",`extmap id ${r.id} is used more than once`,t),P.add(r.id);for(const r of["send","recv"])for(const i of(null!==(w=null===($=e.simulcast)||void 0===$?void 0:$[r])&&void 0!==w?w:[]).flat())(null===(y=e.rids)||void 0===y?void 0:y.some((e=>e.id===i.rid&&e.direction===r)))||l("simulcast-unknown-rid",`a=simulcast ${r} references "${i.rid}" without a matching a=rid:${i.rid} ${r} (RFC 8853 §5.1)`,t);for(const r of null!==(b=e.rids)&&void 0!==b?b:[])for(const i of null!==(x=r.payloadTypes)&&void 0!==x?x:[])(null===(k=e.codecs)||void 0===k?void 0:k.some((e=>e.payloadType===i)))||l("rid-unknown-payload-type",`a=rid:${r.id} references payload type ${i} that is not in the m= line (RFC 8851)`,t);for(const r of null!==(E=e.ssrcGroups)&&void 0!==E?E:[])for(const i of r.ssrcs)(null===(T=e.ssrcs)||void 0===T?void 0:T.some((e=>e.id===i)))||m("ssrc-group-unknown-ssrc",`a=ssrc-group:${r.semantics} references ssrc ${i} without an a=ssrc line`,t)})),c}},Y="1.2.1-rc.1";export{e as SdpParseError,S as sdpReader,X as sdpValidator,H as sdpWriter,Y as version};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@irbisadm/simple-sdp",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.2.1-rc.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Parse, modify, write and validate SDP (RFC 8866) for WebRTC and SIP. Typed ICE, DTLS, BUNDLE, simulcast, codecs. Zero dependencies.",
|
|
6
6
|
"author": {
|
|
@@ -22,7 +22,13 @@
|
|
|
22
22
|
"types": "./types/index.d.ts",
|
|
23
23
|
"import": "./dist/index.mjs",
|
|
24
24
|
"default": "./dist/index.mjs"
|
|
25
|
-
}
|
|
25
|
+
},
|
|
26
|
+
"./wasm": {
|
|
27
|
+
"types": "./wasm/dist/index.d.ts",
|
|
28
|
+
"import": "./wasm/dist/index.mjs",
|
|
29
|
+
"default": "./wasm/dist/index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"./wasm/simple-sdp.wasm": "./wasm/dist/simple-sdp.wasm"
|
|
26
32
|
},
|
|
27
33
|
"sideEffects": false,
|
|
28
34
|
"keywords": [
|
|
@@ -37,11 +43,16 @@
|
|
|
37
43
|
"typescript",
|
|
38
44
|
"parser",
|
|
39
45
|
"simulcast",
|
|
40
|
-
"ice"
|
|
46
|
+
"ice",
|
|
47
|
+
"wasm"
|
|
41
48
|
],
|
|
42
49
|
"files": [
|
|
43
50
|
"./dist/index.mjs",
|
|
44
51
|
"./types/index.d.ts",
|
|
52
|
+
"./wasm/dist/index.mjs",
|
|
53
|
+
"./wasm/dist/index.d.ts",
|
|
54
|
+
"./wasm/dist/simple-sdp.mjs",
|
|
55
|
+
"./wasm/dist/simple-sdp.wasm",
|
|
45
56
|
"./readme.md",
|
|
46
57
|
"./CHANGELOG.md"
|
|
47
58
|
],
|
|
@@ -68,6 +79,7 @@
|
|
|
68
79
|
"typecheck": "tsc --noEmit",
|
|
69
80
|
"asbuild": "asc --config asconfig.json --target release && node scripts/sync-wasm.mjs",
|
|
70
81
|
"asbuild:debug": "asc --config asconfig.json --target debug",
|
|
71
|
-
"test:wasm": "jest -c jest.wasm.config.js"
|
|
82
|
+
"test:wasm": "jest -c jest.wasm.config.js && node test-wasm/npm-entry.mjs",
|
|
83
|
+
"prepack": "yarn asbuild && yarn build"
|
|
72
84
|
}
|
|
73
85
|
}
|
package/readme.md
CHANGED
|
@@ -199,11 +199,17 @@ this package against the wasm build, plus a differential test that compares both
|
|
|
199
199
|
implementations on all fixtures and on thousands of mutations of them: same model, same
|
|
200
200
|
written text, same issues, same error messages.
|
|
201
201
|
|
|
202
|
-
| Platform |
|
|
202
|
+
| Platform | Get it | Runtime |
|
|
203
203
|
|---|---|---|
|
|
204
|
-
| iOS 18+, macOS 15+ |
|
|
205
|
-
| Android, JVM 11+ |
|
|
206
|
-
| Browser, Node.js
|
|
204
|
+
| iOS 18+, macOS 15+ | SwiftPM: `.package(url: "https://github.com/irbisadm/simple-sdp.git", from: "1.2.0")`, product `SimpleSdp` ([`platforms/apple`](platforms/apple)) | [WasmKit](https://github.com/swiftwasm/WasmKit) interpreter |
|
|
205
|
+
| Android, JVM 11+ | JitPack: `maven("https://jitpack.io")`, `implementation("com.github.irbisadm:simple-sdp:v1.2.0")` ([`platforms/kotlin`](platforms/kotlin)) | [Chicory](https://chicory.dev), compiled to bytecode at build time |
|
|
206
|
+
| Browser, Node.js | npm: `import {sdpReader} from '@irbisadm/simple-sdp/wasm'` — the API of the main entry point | the JS engine |
|
|
207
|
+
| Other wasm hosts | `simple-sdp.wasm` from [GitHub Releases](https://github.com/irbisadm/simple-sdp/releases) or `@irbisadm/simple-sdp/wasm/simple-sdp.wasm` | any |
|
|
208
|
+
|
|
209
|
+
For JavaScript the TypeScript build is the faster choice (see the timings below); the
|
|
210
|
+
`/wasm` entry point exists for parity checks and for environments that prefer one shared
|
|
211
|
+
implementation. It is an ES module with top-level await that loads `simple-sdp.wasm` through
|
|
212
|
+
`new URL("simple-sdp.wasm", import.meta.url)`: Node.js 17+ and Vite handle it as is.
|
|
207
213
|
|
|
208
214
|
The model crosses the wasm boundary as JSON with the shape of the TypeScript `Sdp` interface.
|
|
209
215
|
|
|
@@ -246,8 +252,9 @@ Approximate time to parse / write an 18 KB Chrome offer on x86-64, JSON transfer
|
|
|
246
252
|
wasm in Node.js 1.3 / 1.2 ms (the TypeScript build: 0.35 / 0.25 ms), JVM with the compiled
|
|
247
253
|
module 6 / 9 ms, WasmKit (release build) ≈10 ms each.
|
|
248
254
|
|
|
249
|
-
|
|
250
|
-
|
|
255
|
+
The generated ESM bindings (`wasm/build/simple-sdp.js`, published as
|
|
256
|
+
`wasm/dist/simple-sdp.mjs`) take strings: `parse(text)`, `write(json)`, `validate(text, type)`,
|
|
257
|
+
`validateModel(json, type)`.
|
|
251
258
|
|
|
252
259
|
Build: `yarn asbuild` compiles the module and copies it into `platforms/` (the copies are
|
|
253
260
|
committed so that SwiftPM and Gradle builds do not need Node.js).
|
package/types/index.d.ts
CHANGED
|
@@ -332,6 +332,6 @@ declare class SdpParseError extends Error {
|
|
|
332
332
|
constructor(reason: string, lineNumber: number, line: string);
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
declare const version = "1.1.
|
|
335
|
+
declare const version = "1.2.1-rc.1";
|
|
336
336
|
|
|
337
337
|
export { Sdp, SdpBandwidthInformation, SdpCandidate, SdpCodec, SdpCommonAttributes, SdpConnectionInformation, SdpDirection, SdpEncryptionKey, SdpExtmap, SdpFingerprint, SdpGenericAttribute, SdpGroup, SdpIssue, SdpIssueCode, SdpMediaSection, SdpMsid, SdpOrigin, SdpPair, SdpParseError, SdpRepeatTimes, SdpRid, SdpRtcp, SdpSetup, SdpSimulcast, SdpSimulcastRid, SdpSimulcastStreams, SdpSsrc, SdpSsrcGroup, SdpTimeZoneAdjustment, SdpTiming, SdpValidateOptions, sdpReader, sdpValidator, sdpWriter, version };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// @irbisadm/simple-sdp/wasm — the API of the main entry point, run by the WebAssembly build
|
|
2
|
+
// (assembly/). Published as wasm/dist/index.mjs next to simple-sdp.mjs and simple-sdp.wasm.
|
|
3
|
+
import {parse as rawParse, write as rawWrite, validate as rawValidate, validateModel as rawValidateModel, VERSION}
|
|
4
|
+
from './simple-sdp.mjs';
|
|
5
|
+
import {SdpParseError} from '../../dist/index.mjs';
|
|
6
|
+
|
|
7
|
+
export {SdpParseError};
|
|
8
|
+
|
|
9
|
+
const parse = (sdp) => {
|
|
10
|
+
const result = JSON.parse(rawParse(sdp));
|
|
11
|
+
if (result.error) {
|
|
12
|
+
throw new SdpParseError(result.error.reason, result.error.lineNumber, result.error.line);
|
|
13
|
+
}
|
|
14
|
+
return result.sdp;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const isValid = (sdp, writeLog = false) => {
|
|
18
|
+
try {
|
|
19
|
+
parse(sdp);
|
|
20
|
+
return true;
|
|
21
|
+
} catch (e) {
|
|
22
|
+
if (writeLog) {
|
|
23
|
+
console.error(e);
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const write = (sdp) => {
|
|
30
|
+
const result = JSON.parse(rawWrite(JSON.stringify(sdp)));
|
|
31
|
+
if (result.error) {
|
|
32
|
+
throw new Error(result.error.message);
|
|
33
|
+
}
|
|
34
|
+
return result.sdp;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const validate = (input, options = {}) => {
|
|
38
|
+
const type = options.type ?? null;
|
|
39
|
+
const result = JSON.parse(typeof input === 'string'
|
|
40
|
+
? rawValidate(input, type)
|
|
41
|
+
: rawValidateModel(JSON.stringify(input), type));
|
|
42
|
+
if (result.error) {
|
|
43
|
+
throw new Error(result.error.message);
|
|
44
|
+
}
|
|
45
|
+
return result.issues;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const sdpReader = {parse, isValid};
|
|
49
|
+
export const sdpWriter = {write};
|
|
50
|
+
export const sdpValidator = {validate};
|
|
51
|
+
export const version = VERSION.value;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
async function instantiate(module, imports = {}) {
|
|
2
|
+
const { exports } = await WebAssembly.instantiate(module, imports);
|
|
3
|
+
const memory = exports.memory || imports.env.memory;
|
|
4
|
+
const adaptedExports = Object.setPrototypeOf({
|
|
5
|
+
VERSION: {
|
|
6
|
+
// assembly/index/VERSION: ~lib/string/String
|
|
7
|
+
valueOf() { return this.value; },
|
|
8
|
+
get value() {
|
|
9
|
+
return __liftString(exports.VERSION.value >>> 0);
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
isValid(sdp) {
|
|
13
|
+
// assembly/index/isValid(~lib/string/String) => bool
|
|
14
|
+
sdp = __lowerString(sdp) || __notnull();
|
|
15
|
+
return exports.isValid(sdp) != 0;
|
|
16
|
+
},
|
|
17
|
+
parse(sdp) {
|
|
18
|
+
// assembly/index/parse(~lib/string/String) => ~lib/string/String
|
|
19
|
+
sdp = __lowerString(sdp) || __notnull();
|
|
20
|
+
return __liftString(exports.parse(sdp) >>> 0);
|
|
21
|
+
},
|
|
22
|
+
write(model) {
|
|
23
|
+
// assembly/index/write(~lib/string/String) => ~lib/string/String
|
|
24
|
+
model = __lowerString(model) || __notnull();
|
|
25
|
+
return __liftString(exports.write(model) >>> 0);
|
|
26
|
+
},
|
|
27
|
+
validate(sdp, type) {
|
|
28
|
+
// assembly/index/validate(~lib/string/String, ~lib/string/String | null?) => ~lib/string/String
|
|
29
|
+
sdp = __retain(__lowerString(sdp) || __notnull());
|
|
30
|
+
type = __lowerString(type);
|
|
31
|
+
try {
|
|
32
|
+
exports.__setArgumentsLength(arguments.length);
|
|
33
|
+
return __liftString(exports.validate(sdp, type) >>> 0);
|
|
34
|
+
} finally {
|
|
35
|
+
__release(sdp);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
validateModel(model, type) {
|
|
39
|
+
// assembly/index/validateModel(~lib/string/String, ~lib/string/String | null?) => ~lib/string/String
|
|
40
|
+
model = __retain(__lowerString(model) || __notnull());
|
|
41
|
+
type = __lowerString(type);
|
|
42
|
+
try {
|
|
43
|
+
exports.__setArgumentsLength(arguments.length);
|
|
44
|
+
return __liftString(exports.validateModel(model, type) >>> 0);
|
|
45
|
+
} finally {
|
|
46
|
+
__release(model);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
sdp_alloc(len) {
|
|
50
|
+
// assembly/index/sdp_alloc(i32) => usize
|
|
51
|
+
return exports.sdp_alloc(len) >>> 0;
|
|
52
|
+
},
|
|
53
|
+
sdp_parse(ptr, len) {
|
|
54
|
+
// assembly/index/sdp_parse(usize, i32) => usize
|
|
55
|
+
return exports.sdp_parse(ptr, len) >>> 0;
|
|
56
|
+
},
|
|
57
|
+
sdp_write(ptr, len) {
|
|
58
|
+
// assembly/index/sdp_write(usize, i32) => usize
|
|
59
|
+
return exports.sdp_write(ptr, len) >>> 0;
|
|
60
|
+
},
|
|
61
|
+
sdp_validate(ptr, len, type) {
|
|
62
|
+
// assembly/index/sdp_validate(usize, i32, i32) => usize
|
|
63
|
+
return exports.sdp_validate(ptr, len, type) >>> 0;
|
|
64
|
+
},
|
|
65
|
+
sdp_validate_model(ptr, len, type) {
|
|
66
|
+
// assembly/index/sdp_validate_model(usize, i32, i32) => usize
|
|
67
|
+
return exports.sdp_validate_model(ptr, len, type) >>> 0;
|
|
68
|
+
},
|
|
69
|
+
sdp_version() {
|
|
70
|
+
// assembly/index/sdp_version() => usize
|
|
71
|
+
return exports.sdp_version() >>> 0;
|
|
72
|
+
},
|
|
73
|
+
}, exports);
|
|
74
|
+
function __liftString(pointer) {
|
|
75
|
+
if (!pointer) return null;
|
|
76
|
+
const
|
|
77
|
+
end = pointer + new Uint32Array(memory.buffer)[pointer - 4 >>> 2] >>> 1,
|
|
78
|
+
memoryU16 = new Uint16Array(memory.buffer);
|
|
79
|
+
let
|
|
80
|
+
start = pointer >>> 1,
|
|
81
|
+
string = "";
|
|
82
|
+
while (end - start > 1024) string += String.fromCharCode(...memoryU16.subarray(start, start += 1024));
|
|
83
|
+
return string + String.fromCharCode(...memoryU16.subarray(start, end));
|
|
84
|
+
}
|
|
85
|
+
function __lowerString(value) {
|
|
86
|
+
if (value == null) return 0;
|
|
87
|
+
const
|
|
88
|
+
length = value.length,
|
|
89
|
+
pointer = exports.__new(length << 1, 2) >>> 0,
|
|
90
|
+
memoryU16 = new Uint16Array(memory.buffer);
|
|
91
|
+
for (let i = 0; i < length; ++i) memoryU16[(pointer >>> 1) + i] = value.charCodeAt(i);
|
|
92
|
+
return pointer;
|
|
93
|
+
}
|
|
94
|
+
const refcounts = new Map();
|
|
95
|
+
function __retain(pointer) {
|
|
96
|
+
if (pointer) {
|
|
97
|
+
const refcount = refcounts.get(pointer);
|
|
98
|
+
if (refcount) refcounts.set(pointer, refcount + 1);
|
|
99
|
+
else refcounts.set(exports.__pin(pointer), 1);
|
|
100
|
+
}
|
|
101
|
+
return pointer;
|
|
102
|
+
}
|
|
103
|
+
function __release(pointer) {
|
|
104
|
+
if (pointer) {
|
|
105
|
+
const refcount = refcounts.get(pointer);
|
|
106
|
+
if (refcount === 1) exports.__unpin(pointer), refcounts.delete(pointer);
|
|
107
|
+
else if (refcount) refcounts.set(pointer, refcount - 1);
|
|
108
|
+
else throw Error(`invalid refcount '${refcount}' for reference '${pointer}'`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function __notnull() {
|
|
112
|
+
throw TypeError("value must not be null");
|
|
113
|
+
}
|
|
114
|
+
return adaptedExports;
|
|
115
|
+
}
|
|
116
|
+
export const {
|
|
117
|
+
memory,
|
|
118
|
+
__new,
|
|
119
|
+
__pin,
|
|
120
|
+
__unpin,
|
|
121
|
+
__collect,
|
|
122
|
+
__rtti_base,
|
|
123
|
+
VERSION,
|
|
124
|
+
isValid,
|
|
125
|
+
parse,
|
|
126
|
+
write,
|
|
127
|
+
validate,
|
|
128
|
+
validateModel,
|
|
129
|
+
sdp_alloc,
|
|
130
|
+
sdp_free,
|
|
131
|
+
sdp_len,
|
|
132
|
+
sdp_parse,
|
|
133
|
+
sdp_write,
|
|
134
|
+
sdp_validate,
|
|
135
|
+
sdp_validate_model,
|
|
136
|
+
sdp_version,
|
|
137
|
+
} = await (async url => instantiate(
|
|
138
|
+
await (async () => {
|
|
139
|
+
const isNodeOrBun = typeof process != "undefined" && process.versions != null && (process.versions.node != null || process.versions.bun != null);
|
|
140
|
+
if (isNodeOrBun) { return globalThis.WebAssembly.compile(await (await import("node:fs/promises")).readFile(url)); }
|
|
141
|
+
else { return await globalThis.WebAssembly.compileStreaming(globalThis.fetch(url)); }
|
|
142
|
+
})(), {
|
|
143
|
+
}
|
|
144
|
+
))(new URL("simple-sdp.wasm", import.meta.url));
|
|
Binary file
|