node-datachannel 0.11.0 → 0.20.0-alpha.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.
Files changed (131) hide show
  1. package/.eslintignore +5 -1
  2. package/.eslintrc.json +27 -0
  3. package/.prettierrc +12 -0
  4. package/CMakeLists.txt +13 -13
  5. package/jest.config.ts +14 -0
  6. package/package.json +47 -44
  7. package/rollup.config.mjs +63 -0
  8. package/src/index.ts +10 -0
  9. package/{lib/datachannel-stream.js → src/lib/datachannel-stream.ts} +16 -11
  10. package/src/lib/index.ts +180 -0
  11. package/src/lib/node-datachannel.ts +13 -0
  12. package/src/lib/types.ts +126 -0
  13. package/{lib/websocket-server.js → src/lib/websocket-server.ts} +10 -8
  14. package/src/lib/websocket.ts +26 -0
  15. package/src/polyfill/Events.ts +33 -0
  16. package/src/polyfill/Exception.ts +37 -0
  17. package/src/polyfill/RTCCertificate.ts +17 -0
  18. package/{polyfill/RTCDataChannel.js → src/polyfill/RTCDataChannel.ts} +43 -40
  19. package/src/polyfill/RTCDtlsTransport.ts +47 -0
  20. package/src/polyfill/RTCError.ts +71 -0
  21. package/src/polyfill/RTCIceCandidate.ts +125 -0
  22. package/{polyfill/RTCIceTransport.js → src/polyfill/RTCIceTransport.ts} +22 -20
  23. package/{polyfill/RTCPeerConnection.js → src/polyfill/RTCPeerConnection.ts} +137 -115
  24. package/{polyfill/RTCSctpTransport.js → src/polyfill/RTCSctpTransport.ts} +16 -14
  25. package/{polyfill/RTCSessionDescription.js → src/polyfill/RTCSessionDescription.ts} +7 -12
  26. package/{polyfill/index.js → src/polyfill/index.ts} +12 -9
  27. package/tsconfig.json +20 -13
  28. package/.eslintrc.cjs +0 -8
  29. package/.github/FUNDING.yml +0 -2
  30. package/.github/workflows/build-linux.yml +0 -104
  31. package/.github/workflows/build-mac-m1.yml +0 -38
  32. package/.github/workflows/build-mac-x64.yml +0 -40
  33. package/.github/workflows/build-win.yml +0 -74
  34. package/.github/workflows/lint.yml +0 -20
  35. package/.github/workflows/npm-publish-manual.yml +0 -23
  36. package/.prettierrc.cjs +0 -8
  37. package/build-containers/Dockerfile.alpine +0 -6
  38. package/build-containers/Dockerfile.debian +0 -7
  39. package/cmake/toolchain/ci.cmake +0 -23
  40. package/examples/client-server/README.md +0 -22
  41. package/examples/client-server/client-benchmark.js +0 -230
  42. package/examples/client-server/client-periodic.js +0 -189
  43. package/examples/client-server/client.js +0 -111
  44. package/examples/client-server/package-lock.json +0 -289
  45. package/examples/client-server/package.json +0 -16
  46. package/examples/client-server/signaling-server.js +0 -39
  47. package/examples/electron-demo/README.md +0 -58
  48. package/examples/electron-demo/forge.config.js +0 -49
  49. package/examples/electron-demo/package-lock.json +0 -15933
  50. package/examples/electron-demo/package.json +0 -43
  51. package/examples/electron-demo/src/index.css +0 -7
  52. package/examples/electron-demo/src/index.html +0 -29
  53. package/examples/electron-demo/src/main.js +0 -53
  54. package/examples/electron-demo/src/node-datachannel.js +0 -123
  55. package/examples/electron-demo/src/preload.js +0 -28
  56. package/examples/electron-demo/src/renderer.js +0 -58
  57. package/examples/electron-demo/webpack.main.config.js +0 -11
  58. package/examples/electron-demo/webpack.renderer.config.js +0 -13
  59. package/examples/electron-demo/webpack.rules.js +0 -35
  60. package/examples/media/README.md +0 -34
  61. package/examples/media/main.html +0 -45
  62. package/examples/media/media.js +0 -54
  63. package/examples/simple-peer-usage/main.js +0 -28
  64. package/examples/simple-peer-usage/package-lock.json +0 -317
  65. package/examples/simple-peer-usage/package.json +0 -16
  66. package/examples/websocket/websocket-client.js +0 -20
  67. package/examples/websocket/websocket-server.js +0 -22
  68. package/jest.config.cjs +0 -7
  69. package/lib/index.cjs +0 -194
  70. package/lib/index.d.cts +0 -300
  71. package/lib/index.d.ts +0 -300
  72. package/lib/index.js +0 -58
  73. package/lib/node-datachannel.js +0 -7
  74. package/polyfill/Events.d.ts +0 -9
  75. package/polyfill/Events.js +0 -29
  76. package/polyfill/Exception.js +0 -23
  77. package/polyfill/RTCCertificate.d.ts +0 -7
  78. package/polyfill/RTCCertificate.js +0 -17
  79. package/polyfill/RTCDataChannel.d.ts +0 -35
  80. package/polyfill/RTCDtlsTransport.d.ts +0 -10
  81. package/polyfill/RTCDtlsTransport.js +0 -47
  82. package/polyfill/RTCIceCandidate.d.ts +0 -20
  83. package/polyfill/RTCIceCandidate.js +0 -126
  84. package/polyfill/RTCIceTransport.d.ts +0 -18
  85. package/polyfill/RTCPeerConnection.d.ts +0 -62
  86. package/polyfill/RTCSctpTransport.d.ts +0 -10
  87. package/polyfill/RTCSessionDescription.d.ts +0 -8
  88. package/polyfill/index.cjs +0 -1258
  89. package/polyfill/index.d.cts +0 -37
  90. package/polyfill/index.d.ts +0 -37
  91. package/test/connectivity.js +0 -86
  92. package/test/jest-tests/basic.test.js +0 -37
  93. package/test/jest-tests/multiple-run.test.js +0 -73
  94. package/test/jest-tests/p2p.test.js +0 -99
  95. package/test/jest-tests/polyfill.test.js +0 -10
  96. package/test/jest-tests/streams.test.js +0 -48
  97. package/test/jest-tests/websocket.test.js +0 -69
  98. package/test/polyfill-connectivity.js +0 -89
  99. package/test/websockets.js +0 -50
  100. package/test/wpt-tests/README.md +0 -46
  101. package/test/wpt-tests/chrome-failed-tests.js +0 -42
  102. package/test/wpt-tests/index.js +0 -96
  103. package/test/wpt-tests/last-test-results.md +0 -205
  104. package/test/wpt-tests/package-lock.json +0 -1712
  105. package/test/wpt-tests/package.json +0 -19
  106. package/test/wpt-tests/wpt-test-list.js +0 -137
  107. package/test/wpt-tests/wpt.js +0 -131
  108. /package/src/{data-channel-wrapper.cpp → cpp/data-channel-wrapper.cpp} +0 -0
  109. /package/src/{data-channel-wrapper.h → cpp/data-channel-wrapper.h} +0 -0
  110. /package/src/{main.cpp → cpp/main.cpp} +0 -0
  111. /package/src/{media-audio-wrapper.cpp → cpp/media-audio-wrapper.cpp} +0 -0
  112. /package/src/{media-audio-wrapper.h → cpp/media-audio-wrapper.h} +0 -0
  113. /package/src/{media-direction.cpp → cpp/media-direction.cpp} +0 -0
  114. /package/src/{media-direction.h → cpp/media-direction.h} +0 -0
  115. /package/src/{media-rtcpreceivingsession-wrapper.cpp → cpp/media-rtcpreceivingsession-wrapper.cpp} +0 -0
  116. /package/src/{media-rtcpreceivingsession-wrapper.h → cpp/media-rtcpreceivingsession-wrapper.h} +0 -0
  117. /package/src/{media-track-wrapper.cpp → cpp/media-track-wrapper.cpp} +0 -0
  118. /package/src/{media-track-wrapper.h → cpp/media-track-wrapper.h} +0 -0
  119. /package/src/{media-video-wrapper.cpp → cpp/media-video-wrapper.cpp} +0 -0
  120. /package/src/{media-video-wrapper.h → cpp/media-video-wrapper.h} +0 -0
  121. /package/src/{peer-connection-wrapper.cpp → cpp/peer-connection-wrapper.cpp} +0 -0
  122. /package/src/{peer-connection-wrapper.h → cpp/peer-connection-wrapper.h} +0 -0
  123. /package/src/{rtc-wrapper.cpp → cpp/rtc-wrapper.cpp} +0 -0
  124. /package/src/{rtc-wrapper.h → cpp/rtc-wrapper.h} +0 -0
  125. /package/src/{thread-safe-callback.cpp → cpp/thread-safe-callback.cpp} +0 -0
  126. /package/src/{thread-safe-callback.h → cpp/thread-safe-callback.h} +0 -0
  127. /package/src/{web-socket-server-wrapper.cpp → cpp/web-socket-server-wrapper.cpp} +0 -0
  128. /package/src/{web-socket-server-wrapper.h → cpp/web-socket-server-wrapper.h} +0 -0
  129. /package/src/{web-socket-wrapper.cpp → cpp/web-socket-wrapper.cpp} +0 -0
  130. /package/src/{web-socket-wrapper.h → cpp/web-socket-wrapper.h} +0 -0
  131. /package/{polyfill → src/polyfill}/README.md +0 -0
@@ -1,23 +0,0 @@
1
- export const InvalidStateError = (msg) => {
2
- return new DOMException(msg, 'InvalidStateError');
3
- };
4
-
5
- export const InvalidAccessError = (msg) => {
6
- return new DOMException(msg, 'InvalidAccessError');
7
- };
8
-
9
- export const NotFoundError = (msg) => {
10
- return new DOMException(msg, 'NotFoundError');
11
- };
12
-
13
- export const OperationError = (msg) => {
14
- return new DOMException(msg, 'OperationError');
15
- };
16
-
17
- export const TypeError = (msg) => {
18
- return new DOMException(msg, 'TypeError');
19
- };
20
-
21
- export const SyntaxError = (msg) => {
22
- return new DOMException(msg, 'SyntaxError');
23
- };
@@ -1,7 +0,0 @@
1
- /// <reference lib="dom" />
2
-
3
- export default class RTCCertificate implements RTCCertificate {
4
- constructor();
5
- readonly expires: number;
6
- getFingerprints(): RTCDtlsFingerprint[];
7
- }
@@ -1,17 +0,0 @@
1
- export default class RTCCertificate {
2
- #expires;
3
- #fingerprints;
4
-
5
- constructor() {
6
- this.#expires = null;
7
- this.#fingerprints = [];
8
- }
9
-
10
- get expires() {
11
- return this.#expires;
12
- }
13
-
14
- getFingerprints() {
15
- return this.#fingerprints;
16
- }
17
- }
@@ -1,35 +0,0 @@
1
- /// <reference lib="dom" />
2
-
3
- import * as NodeDataChannel from '../lib/index.js';
4
-
5
- export default class _RTCDataChannel extends EventTarget implements RTCDataChannel {
6
- constructor(dataChannel: NodeDataChannel.DataChannel, opts: RTCDataChannelInit);
7
-
8
- // events
9
- onbufferedamountlow: ((this: RTCDataChannel, ev: Event) => any) | null;
10
- onclose: ((this: RTCDataChannel, ev: Event) => any) | null;
11
- onclosing: ((this: RTCDataChannel, ev: Event) => any) | null;
12
- onerror: ((this: RTCDataChannel, ev: Event) => any) | null;
13
- onmessage: ((this: RTCDataChannel, ev: MessageEvent) => any) | null;
14
- onopen: ((this: RTCDataChannel, ev: Event) => any) | null;
15
-
16
- // props
17
- binaryType: BinaryType;
18
- bufferedAmountLowThreshold: number;
19
- readonly bufferedAmount: number;
20
- readonly id: number | null;
21
- readonly label: string;
22
- readonly maxPacketLifeTime: number | null;
23
- readonly maxRetransmits: number | null;
24
- readonly negotiated: boolean;
25
- readonly ordered: boolean;
26
- readonly protocol: string;
27
- readonly readyState: RTCDataChannelState;
28
-
29
- // methods
30
- close(): void;
31
- send(data: string): void;
32
- send(data: ArrayBuffer): void;
33
- send(data: ArrayBufferView): void;
34
- send(data: Blob): void;
35
- }
@@ -1,10 +0,0 @@
1
- /// <reference lib="dom" />
2
-
3
- export default class _RTCDtlsTransport extends EventTarget implements RTCDtlsTransport {
4
- readonly iceTransport: RTCIceTransport;
5
- readonly state: RTCDtlsTransportState;
6
- getRemoteCertificates(): ArrayBuffer[];
7
-
8
- onstatechange: ((this: RTCDtlsTransport, ev: Event) => any) | null;
9
- onerror: ((this: RTCDtlsTransport, ev: Event) => any) | null;
10
- }
@@ -1,47 +0,0 @@
1
- import RTCIceTransport from './RTCIceTransport.js';
2
-
3
- export default class _RTCDtlsTransport extends EventTarget {
4
- #pc = null;
5
- #extraFunctions = null;
6
- #iceTransport = null;
7
-
8
- onerror = null;
9
- onstatechange = null;
10
-
11
- constructor({ pc, extraFunctions }) {
12
- super();
13
- this.#pc = pc;
14
- this.#extraFunctions = extraFunctions;
15
-
16
- this.#iceTransport = new RTCIceTransport({ pc, extraFunctions });
17
-
18
- // forward peerConnection events
19
- this.#pc.addEventListener('connectionstatechange', () => {
20
- this.dispatchEvent(new Event('statechange'));
21
- });
22
-
23
- // forward events to properties
24
- this.addEventListener('statechange', (e) => {
25
- if (this.onstatechange) this.onstatechange(e);
26
- });
27
- }
28
-
29
- get iceTransport() {
30
- return this.#iceTransport;
31
- }
32
-
33
- get state() {
34
- // reduce state from new, connecting, connected, disconnected, failed, closed, unknown
35
- // to RTCDtlsTRansport states new, connecting, connected, closed, failed
36
- let state = this.#pc ? this.#pc.connectionState : 'new';
37
- if (state === 'disconnected' || state === 'unknown') {
38
- state = 'closed';
39
- }
40
- return state;
41
- }
42
-
43
- getRemoteCertificates() {
44
- // TODO: implement
45
- return new ArrayBuffer(0);
46
- }
47
- }
@@ -1,20 +0,0 @@
1
- /// <reference lib="dom" />
2
-
3
- export default class _RTCIceCandidate implements RTCIceCandidate {
4
- constructor(init: RTCIceCandidateInit);
5
- readonly address: string | null;
6
- readonly candidate: string;
7
- readonly component: RTCIceComponent | null;
8
- readonly foundation: string | null;
9
- readonly port: number | null;
10
- readonly priority: number | null;
11
- readonly protocol: RTCIceProtocol | null;
12
- readonly relatedAddress: string | null;
13
- readonly relatedPort: number | null;
14
- readonly sdpMLineIndex: number | null;
15
- readonly sdpMid: string | null;
16
- readonly tcpType: RTCIceTcpCandidateType | null;
17
- readonly type: RTCIceCandidateType | null;
18
- readonly usernameFragment: string | null;
19
- toJSON(): RTCIceCandidateInit;
20
- }
@@ -1,126 +0,0 @@
1
- // https://developer.mozilla.org/docs/Web/API/RTCIceCandidate
2
- //
3
- // Example: candidate:123456 1 UDP 123456 192.168.1.1 12345 typ host raddr=10.0.0.1 rport=54321 generation 0
4
-
5
- import DOMException from 'node-domexception';
6
-
7
- export default class _RTCIceCandidate {
8
- #address;
9
- #candidate;
10
- #component;
11
- #foundation;
12
- #port;
13
- #priority;
14
- #protocol;
15
- #relatedAddress;
16
- #relatedPort;
17
- #sdpMLineIndex;
18
- #sdpMid;
19
- #tcpType;
20
- #type;
21
- #usernameFragment;
22
- #ip;
23
-
24
- constructor({ candidate, sdpMLineIndex, sdpMid, usernameFragment }) {
25
- if (candidate == null) {
26
- throw new DOMException('candidate must be specified');
27
- }
28
-
29
- this.#candidate = candidate;
30
- this.#sdpMLineIndex = sdpMLineIndex || null;
31
- this.#sdpMid = sdpMid || null;
32
- this.#usernameFragment = usernameFragment || null;
33
-
34
- if (candidate) {
35
- const fields = candidate.split(' ');
36
- this.#foundation = fields[0];
37
- this.#component = fields[1] == '1' ? 'rtp' : 'rtcp';
38
- this.#protocol = fields[2];
39
- this.#priority = parseInt(fields[3], 10);
40
- this.#ip = fields[4];
41
- this.#port = parseInt(fields[5], 10);
42
- this.#type = fields[7];
43
- if (fields[6] === 'typ') {
44
- this.#tcpType = null;
45
- } else if (fields[6] === 'tcp') {
46
- this.#tcpType = fields[7];
47
- this.#type = fields[8];
48
- }
49
-
50
- // Parse the candidate string to extract relatedPort and relatedAddress
51
- for (let i = 9; i < fields.length; i++) {
52
- const field = fields[i];
53
- if (field.startsWith('raddr')) {
54
- this.#relatedAddress = field.split('=')[1];
55
- } else if (field.startsWith('rport')) {
56
- this.#relatedPort = parseInt(field.split('=')[1], 10);
57
- }
58
- }
59
- }
60
- }
61
-
62
- get address() {
63
- return this.#address || null;
64
- }
65
-
66
- get candidate() {
67
- return this.#candidate || '';
68
- }
69
-
70
- get component() {
71
- return this.#component;
72
- }
73
-
74
- get foundation() {
75
- return this.#foundation || null;
76
- }
77
-
78
- get port() {
79
- return this.#port || null;
80
- }
81
-
82
- get priority() {
83
- return this.#priority || null;
84
- }
85
-
86
- get protocol() {
87
- return this.#protocol || null;
88
- }
89
-
90
- get relatedAddress() {
91
- return this.#relatedAddress;
92
- }
93
-
94
- get relatedPort() {
95
- return this.#relatedPort || null;
96
- }
97
-
98
- get sdpMLineIndex() {
99
- return this.#sdpMLineIndex;
100
- }
101
-
102
- get sdpMid() {
103
- return this.#sdpMid;
104
- }
105
-
106
- get tcpType() {
107
- return this.#tcpType;
108
- }
109
-
110
- get type() {
111
- return this.#type || null;
112
- }
113
-
114
- get usernameFragment() {
115
- return this.#usernameFragment;
116
- }
117
-
118
- toJSON() {
119
- return {
120
- candidate: this.#candidate,
121
- sdpMLineIndex: this.#sdpMLineIndex,
122
- sdpMid: this.#sdpMid,
123
- usernameFragment: this.#usernameFragment,
124
- };
125
- }
126
- }
@@ -1,18 +0,0 @@
1
- /// <reference lib="dom" />
2
-
3
- export default class _RTCIceTransport extends EventTarget implements RTCIceTransport {
4
- readonly component: RTCIceComponent;
5
- readonly gatheringState: RTCIceGatheringState;
6
- readonly role: string; // RTCIceRole;
7
- readonly state: RTCIceTransportState;
8
-
9
- getLocalCandidates(): RTCIceCandidate[];
10
- getLocalParameters(): any; // RTCIceParameters;
11
- getRemoteCandidates(): RTCIceCandidate[];
12
- getRemoteParameters(): any; // RTCIceParameters;
13
- getSelectedCandidatePair(): RTCIceCandidatePair | null;
14
-
15
- ongatheringstatechange: ((this: RTCIceTransport, ev: Event) => any) | null;
16
- onselectedcandidatepairchange: ((this: RTCIceTransport, ev: Event) => any) | null;
17
- onstatechange: ((this: RTCIceTransport, ev: Event) => any) | null;
18
- }
@@ -1,62 +0,0 @@
1
- /// <reference lib="dom" />
2
-
3
- export default class _RTCPeerConnection extends EventTarget implements RTCPeerConnection {
4
- constructor(config?: RTCConfiguration);
5
-
6
- // events
7
- onconnectionstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
8
- ondatachannel: ((this: RTCPeerConnection, ev: RTCDataChannelEvent) => any) | null;
9
- onicecandidate: ((this: RTCPeerConnection, ev: RTCPeerConnectionIceEvent) => any) | null;
10
- onicecandidateerror: ((this: RTCPeerConnection, ev: Event) => any) | null;
11
- oniceconnectionstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
12
- onicegatheringstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
13
- onnegotiationneeded: ((this: RTCPeerConnection, ev: Event) => any) | null;
14
- onsignalingstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null;
15
- ontrack: ((this: RTCPeerConnection, ev: RTCTrackEvent) => any) | null;
16
-
17
- // props
18
- readonly canTrickleIceCandidates: boolean | null;
19
- readonly connectionState: RTCPeerConnectionState;
20
- readonly iceConnectionState: RTCIceConnectionState;
21
- readonly iceGatheringState: RTCIceGatheringState;
22
- readonly currentLocalDescription: RTCSessionDescription | null;
23
- readonly currentRemoteDescription: RTCSessionDescription | null;
24
- readonly localDescription: RTCSessionDescription | null;
25
- readonly pendingLocalDescription: RTCSessionDescription | null;
26
- readonly pendingRemoteDescription: RTCSessionDescription | null;
27
- readonly remoteDescription: RTCSessionDescription | null;
28
- readonly sctp: RTCSctpTransport | null;
29
- readonly signalingState: RTCSignalingState;
30
-
31
- // methods
32
- generateCertificate(keygenAlgorithm: AlgorithmIdentifier): Promise<RTCCertificate>;
33
- addIceCandidate(candidate?: RTCIceCandidateInit | RTCIceCandidate): Promise<void>;
34
- addTrack(track: MediaStreamTrack, ...streams: MediaStream[]): RTCRtpSender;
35
- addTransceiver(trackOrKind: MediaStreamTrack | string, init?: RTCRtpTransceiverInit): RTCRtpTransceiver;
36
- close(): void;
37
- createAnswer(options?: RTCAnswerOptions): Promise<RTCSessionDescriptionInit>;
38
- createAnswer(
39
- successCallback: RTCSessionDescriptionCallback,
40
- failureCallback: RTCPeerConnectionErrorCallback,
41
- ): Promise<void>;
42
- createDataChannel(label: string, dataChannelDict?: RTCDataChannelInit): RTCDataChannel;
43
- createOffer(options?: RTCOfferOptions): Promise<RTCSessionDescriptionInit>;
44
- createOffer(
45
- successCallback: RTCSessionDescriptionCallback,
46
- failureCallback: RTCPeerConnectionErrorCallback,
47
- options?: RTCOfferOptions,
48
- ): Promise<void>;
49
- getConfiguration(): RTCConfiguration;
50
- getReceivers(): RTCRtpReceiver[];
51
- getSenders(): RTCRtpSender[];
52
- getStats(selector?: MediaStreamTrack | null): Promise<RTCStatsReport>;
53
- getTransceivers(): RTCRtpTransceiver[];
54
- removeTrack(sender: RTCRtpSender): void;
55
- restartIce(): Promise<void>;
56
- setConfiguration(configuration: RTCConfiguration): void;
57
- setLocalDescription(description: RTCSessionDescriptionInit): Promise<void>;
58
- setRemoteDescription(description: RTCSessionDescriptionInit): Promise<void>;
59
-
60
- // static methods
61
- static generateCertificate(keygenAlgorithm: AlgorithmIdentifier): Promise<RTCCertificate>;
62
- }
@@ -1,10 +0,0 @@
1
- /// <reference lib="dom" />
2
-
3
- export default class _RTCSctpTransport extends EventTarget implements RTCSctpTransport {
4
- readonly maxChannels: number | null;
5
- readonly maxMessageSize: number;
6
- readonly transport: RTCDtlsTransport;
7
- readonly state: RTCSctpTransportState;
8
-
9
- onstatechange: ((this: RTCSctpTransport, ev: Event) => any) | null;
10
- }
@@ -1,8 +0,0 @@
1
- /// <reference lib="dom" />
2
-
3
- export default class _RTCSessionDescription implements RTCSessionDescription {
4
- readonly sdp: string;
5
- readonly type: RTCSdpType;
6
- toJSON(): any;
7
- constructor(descriptionInitDict?: RTCSessionDescriptionInit);
8
- }