77 * @packageDocumentation
88 */
99
10+ import * as event from "./event" ;
11+
1012
1113/**
1214 * Converts payload to Buffer or string regardless of the supplied type
@@ -121,4 +123,115 @@ export function isValidTopic(topic: any) : boolean {
121123 let topicAsString = topic as string ;
122124
123125 return isValidTopicInternal ( topicAsString , false ) ;
124- }
126+ }
127+
128+ export type PublishAcknowledgementFunctor = ( ) => void ;
129+
130+ /**
131+ * Wrapper class containing a one-use singleton handle that can be used to trigger sending the acknowledgement (Puback in
132+ * QoS 1, Pubrec in QoS 2) packet for an incoming publish.
133+ */
134+ export class PublishAcknowledgementHandleWrapper {
135+
136+ private ackHandle : PublishAcknowledgementHandle | null ;
137+
138+ constructor ( handle : PublishAcknowledgementHandle | null ) {
139+ this . ackHandle = handle ;
140+ }
141+
142+ /**
143+ * Attempt to take the acknowledgement handle held by the wrapper. This will only succeed for the first caller;
144+ * after the initial call, null will be returned. By taking the handle, the caller assumes responsibility
145+ * for sending the acknowledgement packet associated with the incoming publish packet. Failing to trigger the
146+ * acknowledgement will cause the broker to potentially re-send the publish.
147+ */
148+ acquireHandle ( ) : PublishAcknowledgementHandle | null {
149+ let handle = this . ackHandle ;
150+ this . ackHandle = null ;
151+
152+ return handle ;
153+ }
154+ }
155+
156+ function movePublishAcknowledgementHandleWrapper ( wrapper : PublishAcknowledgementHandleWrapper | undefined , compositionFunctor ?: PublishAcknowledgementFunctor ) : PublishAcknowledgementHandleWrapper | undefined {
157+ if ( wrapper ) {
158+ let handle = wrapper . acquireHandle ( ) ;
159+ if ( compositionFunctor && handle ) {
160+ let interiorHandle = handle ;
161+ handle = new PublishAcknowledgementHandle ( ( ) => {
162+ interiorHandle . invokeAcknowledgement ( ) ;
163+ compositionFunctor ( ) ;
164+ } ) ;
165+ }
166+
167+ return new PublishAcknowledgementHandleWrapper ( handle ) ;
168+ }
169+
170+ return undefined ;
171+ }
172+
173+ /** @internal */
174+ export function emitAcknowledgeableEvent < T > ( emitter : event . BufferedEventEmitter , ackEvent : string , ackEventPayload : T , wrapperFieldName : string , ackHandleWrapper ?: PublishAcknowledgementHandleWrapper , compositionFunctor ?: PublishAcknowledgementFunctor ) : void {
175+ ackHandleWrapper = movePublishAcknowledgementHandleWrapper ( ackHandleWrapper , compositionFunctor ) ;
176+ if ( ackHandleWrapper ) {
177+ ( ackEventPayload as any ) [ wrapperFieldName ] = ackHandleWrapper ;
178+ emitter . emitWithCallback ( ackEvent , ( ) => {
179+ if ( ackHandleWrapper ) {
180+ let handle = ackHandleWrapper . acquireHandle ( ) ;
181+ if ( handle ) {
182+ // Even if corked, all listeners have had a chance to react to the event
183+ // and acquire the acknowledgement handle if they wanted to. If no one did so, then we do it ourselves.
184+ handle . invokeAcknowledgement ( ) ;
185+ }
186+ }
187+ } , ackEventPayload ) ;
188+ } else {
189+ emitter . emit ( ackEvent , ackEventPayload ) ;
190+ }
191+ }
192+
193+ /** @internal */
194+ export function queueAcknowledgeableEvent < T > ( emitter : event . BufferedEventEmitter , ackEvent : string , ackEventPayload : T , wrapperFieldName : string , ackHandleWrapper ?: PublishAcknowledgementHandleWrapper , compositionFunctor ?: PublishAcknowledgementFunctor ) : void {
195+ let wrapper : PublishAcknowledgementHandleWrapper | undefined = movePublishAcknowledgementHandleWrapper ( ackHandleWrapper , compositionFunctor ) ;
196+
197+ queueMicrotask ( ( ) => {
198+ if ( wrapper ) {
199+ ( ackEventPayload as any ) [ wrapperFieldName ] = wrapper ;
200+ emitter . emitWithCallback ( ackEvent , ( ) => {
201+ if ( wrapper ) {
202+ let handle = wrapper . acquireHandle ( ) ;
203+ if ( handle ) {
204+ // Even if corked, all listeners have had a chance to react to the event
205+ // and acquire the acknowledgement handle if they wanted to. If no one did so, then we do it ourselves.
206+ handle . invokeAcknowledgement ( ) ;
207+ }
208+ }
209+ } , ackEventPayload ) ;
210+ } else {
211+ emitter . emit ( ackEvent , ackEventPayload ) ;
212+ }
213+ } ) ;
214+ }
215+
216+ /**
217+ * Object that allows the holder to trigger the acknowledgement for an associated publish packet.
218+ */
219+ export class PublishAcknowledgementHandle {
220+
221+ private acknowledgementFunction ? : PublishAcknowledgementFunctor ;
222+
223+ constructor ( acknowledgementFunction : PublishAcknowledgementFunctor ) {
224+ this . acknowledgementFunction = acknowledgementFunction ;
225+ }
226+
227+ /**
228+ * trigger the acknowledgement for an associated Publish packet
229+ */
230+ invokeAcknowledgement ( ) : void {
231+ let acknowledgementFunction = this . acknowledgementFunction ;
232+ this . acknowledgementFunction = undefined ;
233+ if ( acknowledgementFunction ) {
234+ acknowledgementFunction ( ) ;
235+ }
236+ }
237+ }
0 commit comments