@annadata/capacitor-mqtt-quic 0.1.2 → 0.1.3

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.
@@ -22,6 +22,7 @@ apply plugin: 'org.jetbrains.kotlin.android'
22
22
  android {
23
23
  namespace "ai.annadata.mqttquic"
24
24
  compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35
25
+ ndkVersion "26.1.10909125"
25
26
  defaultConfig {
26
27
  minSdk project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
27
28
  targetSdk project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35
@@ -66,7 +67,7 @@ repositories {
66
67
 
67
68
  dependencies {
68
69
  implementation fileTree(dir: 'libs', include: ['*.jar'])
69
- implementation project(':capacitor-android')
70
+ compileOnly 'com.getcapacitor:core:6.0.0'
70
71
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
71
72
  implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
72
73
  testImplementation "junit:junit:$junitVersion"
@@ -58,7 +58,7 @@ object MQTT5PropertyEncoder {
58
58
 
59
59
  when (propId) {
60
60
  MQTT5PropertyType.PAYLOAD_FORMAT_INDICATOR.toInt() -> {
61
- result.add(((value as? Int) ?: 0).toByte())
61
+ result.add((((value as? Int) ?: 0) and 0xFF).toByte())
62
62
  }
63
63
  MQTT5PropertyType.MESSAGE_EXPIRY_INTERVAL.toInt(),
64
64
  MQTT5PropertyType.SESSION_EXPIRY_INTERVAL.toInt(),
@@ -104,7 +104,7 @@ object MQTT5PropertyEncoder {
104
104
  MQTT5PropertyType.WILDCARD_SUBSCRIPTION_AVAILABLE.toInt(),
105
105
  MQTT5PropertyType.SUBSCRIPTION_IDENTIFIER_AVAILABLE.toInt(),
106
106
  MQTT5PropertyType.SHARED_SUBSCRIPTION_AVAILABLE.toInt() -> {
107
- result.add(((value as? Int) ?: 0).toByte())
107
+ result.add((((value as? Int) ?: 0) and 0xFF).toByte())
108
108
  }
109
109
  MQTT5PropertyType.USER_PROPERTY.toInt() -> {
110
110
  if (value is Pair<*, *>) {
@@ -2,14 +2,24 @@ package ai.annadata.mqttquic.mqtt
2
2
 
3
3
  /**
4
4
  * MQTT 5.0 Reason Codes. Matches MQTTD mqttd/reason_codes.py.
5
+ * MQTT 5.0 reuses byte values across packet types; we keep one constant per unique byte.
6
+ * Context-specific aliases are provided as properties for semantic clarity.
5
7
  */
6
8
  object MQTT5ReasonCode {
7
- // Success
9
+ // 0x00 - Success (CONNACK), Granted QoS 0 (SUBACK), Success (UNSUBACK), Normal disconnection (DISCONNECT)
8
10
  const val SUCCESS: Int = 0x00
9
- const val NORMAL_DISCONNECTION: Int = 0x00
11
+ // 0x01 - Granted QoS 1 (SUBACK)
12
+ const val GRANTED_QOS_1: Int = 0x01
13
+ // 0x02 - Granted QoS 2 (SUBACK)
14
+ const val GRANTED_QOS_2: Int = 0x02
15
+ // 0x04 - Disconnect with will message
10
16
  const val DISCONNECT_WITH_WILL_MESSAGE: Int = 0x04
17
+ // 0x10 - No matching subscribers (PUBACK/PUBREC/PUBREL/PUBCOMP)
18
+ const val NO_MATCHING_SUBSCRIBERS: Int = 0x10
19
+ // 0x11 - No subscription existed (UNSUBACK)
20
+ const val NO_SUBSCRIPTION_EXISTED: Int = 0x11
11
21
 
12
- // CONNACK Reason Codes
22
+ // CONNACK / generic errors (0x80+)
13
23
  const val UNSPECIFIED_ERROR: Int = 0x80
14
24
  const val MALFORMED_PACKET: Int = 0x81
15
25
  const val PROTOCOL_ERROR: Int = 0x82
@@ -21,81 +31,83 @@ object MQTT5ReasonCode {
21
31
  const val SERVER_UNAVAILABLE: Int = 0x88
22
32
  const val SERVER_BUSY: Int = 0x89
23
33
  const val BANNED: Int = 0x8A
34
+ const val SERVER_SHUTTING_DOWN: Int = 0x8B
24
35
  const val BAD_AUTHENTICATION_METHOD: Int = 0x8C
36
+ const val KEEP_ALIVE_TIMEOUT: Int = 0x8D
37
+ const val SESSION_TAKEN_OVER: Int = 0x8E
38
+ const val TOPIC_FILTER_INVALID: Int = 0x8F
25
39
  const val TOPIC_NAME_INVALID: Int = 0x90
40
+ const val PACKET_IDENTIFIER_IN_USE: Int = 0x91
41
+ const val RECEIVE_MAXIMUM_EXCEEDED: Int = 0x93
42
+ const val TOPIC_ALIAS_INVALID: Int = 0x94
26
43
  const val PACKET_TOO_LARGE: Int = 0x95
44
+ const val MESSAGE_RATE_TOO_HIGH: Int = 0x96
27
45
  const val QUOTA_EXCEEDED: Int = 0x97
46
+ const val ADMINISTRATIVE_ACTION: Int = 0x98
28
47
  const val PAYLOAD_FORMAT_INVALID: Int = 0x99
29
48
  const val RETAIN_NOT_SUPPORTED: Int = 0x9A
30
49
  const val QOS_NOT_SUPPORTED: Int = 0x9B
31
50
  const val USE_ANOTHER_SERVER: Int = 0x9C
32
51
  const val SERVER_MOVED: Int = 0x9D
33
- const val CONNECTION_RATE_EXCEEDED: Int = 0x9F
34
-
35
- // PUBACK, PUBREC, PUBREL, PUBCOMP Reason Codes
36
- const val NO_MATCHING_SUBSCRIBERS: Int = 0x10
37
- const val UNSPECIFIED_ERROR_PUB: Int = 0x80
38
- const val IMPLEMENTATION_SPECIFIC_ERROR_PUB: Int = 0x83
39
- const val NOT_AUTHORIZED_PUB: Int = 0x87
40
- const val TOPIC_NAME_INVALID_PUB: Int = 0x90
41
- const val PACKET_IDENTIFIER_IN_USE: Int = 0x91
42
- const val QUOTA_EXCEEDED_PUB: Int = 0x97
43
- const val PAYLOAD_FORMAT_INVALID_PUB: Int = 0x99
44
-
45
- // SUBACK Reason Codes
46
- const val GRANTED_QOS_0: Int = 0x00
47
- const val GRANTED_QOS_1: Int = 0x01
48
- const val GRANTED_QOS_2: Int = 0x02
49
- const val UNSPECIFIED_ERROR_SUB: Int = 0x80
50
- const val IMPLEMENTATION_SPECIFIC_ERROR_SUB: Int = 0x83
51
- const val NOT_AUTHORIZED_SUB: Int = 0x87
52
- const val TOPIC_FILTER_INVALID: Int = 0x8F
53
- const val PACKET_IDENTIFIER_IN_USE_SUB: Int = 0x91
54
- const val QUOTA_EXCEEDED_SUB: Int = 0x97
55
52
  const val SHARED_SUBSCRIPTIONS_NOT_SUPPORTED: Int = 0x9E
53
+ const val CONNECTION_RATE_EXCEEDED: Int = 0x9F
54
+ const val MAXIMUM_CONNECT_TIME: Int = 0xA0
56
55
  const val SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED: Int = 0xA1
57
56
  const val WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED: Int = 0xA2
58
57
 
59
- // UNSUBACK Reason Codes
60
- const val SUCCESS_UNSUB: Int = 0x00
61
- const val NO_SUBSCRIPTION_EXISTED: Int = 0x11
62
- const val UNSPECIFIED_ERROR_UNSUB: Int = 0x80
63
- const val IMPLEMENTATION_SPECIFIC_ERROR_UNSUB: Int = 0x83
64
- const val NOT_AUTHORIZED_UNSUB: Int = 0x87
65
- const val TOPIC_FILTER_INVALID_UNSUB: Int = 0x8F
66
- const val PACKET_IDENTIFIER_IN_USE_UNSUB: Int = 0x91
58
+ // Context-specific aliases (all map to the same underlying values)
59
+ // SUBACK aliases
60
+ const val GRANTED_QOS_0: Int = SUCCESS // 0x00
67
61
 
68
- // DISCONNECT Reason Codes
69
- const val NORMAL_DISCONNECTION_DISC: Int = 0x00
70
- const val DISCONNECT_WITH_WILL_MESSAGE_DISC: Int = 0x04
71
- const val UNSPECIFIED_ERROR_DISC: Int = 0x80
72
- const val MALFORMED_PACKET_DISC: Int = 0x81
73
- const val PROTOCOL_ERROR_DISC: Int = 0x82
74
- const val IMPLEMENTATION_SPECIFIC_ERROR_DISC: Int = 0x83
75
- const val NOT_AUTHORIZED_DISC: Int = 0x87
76
- const val SERVER_BUSY_DISC: Int = 0x89
77
- const val SERVER_SHUTTING_DOWN: Int = 0x8B
78
- const val BAD_AUTHENTICATION_METHOD_DISC: Int = 0x8C
79
- const val KEEP_ALIVE_TIMEOUT: Int = 0x8D
80
- const val SESSION_TAKEN_OVER: Int = 0x8E
81
- const val TOPIC_FILTER_INVALID_DISC: Int = 0x8F
82
- const val TOPIC_NAME_INVALID_DISC: Int = 0x90
83
- const val RECEIVE_MAXIMUM_EXCEEDED: Int = 0x93
84
- const val TOPIC_ALIAS_INVALID: Int = 0x94
85
- const val PACKET_TOO_LARGE_DISC: Int = 0x95
86
- const val MESSAGE_RATE_TOO_HIGH: Int = 0x96
87
- const val QUOTA_EXCEEDED_DISC: Int = 0x97
88
- const val ADMINISTRATIVE_ACTION: Int = 0x98
89
- const val PAYLOAD_FORMAT_INVALID_DISC: Int = 0x99
90
- const val RETAIN_NOT_SUPPORTED_DISC: Int = 0x9A
91
- const val QOS_NOT_SUPPORTED_DISC: Int = 0x9B
92
- const val USE_ANOTHER_SERVER_DISC: Int = 0x9C
93
- const val SERVER_MOVED_DISC: Int = 0x9D
94
- const val SHARED_SUBSCRIPTIONS_NOT_SUPPORTED_DISC: Int = 0x9E
95
- const val CONNECTION_RATE_EXCEEDED_DISC: Int = 0x9F
96
- const val MAXIMUM_CONNECT_TIME: Int = 0xA0
97
- const val SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED_DISC: Int = 0xA1
98
- const val WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED_DISC: Int = 0xA2
62
+ // UNSUBACK aliases
63
+ const val SUCCESS_UNSUB: Int = SUCCESS // 0x00
64
+
65
+ // DISCONNECT aliases
66
+ const val NORMAL_DISCONNECTION: Int = SUCCESS // 0x00
67
+ const val NORMAL_DISCONNECTION_DISC: Int = SUCCESS // 0x00
68
+ const val DISCONNECT_WITH_WILL_MESSAGE_DISC: Int = DISCONNECT_WITH_WILL_MESSAGE // 0x04
69
+ const val UNSPECIFIED_ERROR_DISC: Int = UNSPECIFIED_ERROR // 0x80
70
+ const val MALFORMED_PACKET_DISC: Int = MALFORMED_PACKET // 0x81
71
+ const val PROTOCOL_ERROR_DISC: Int = PROTOCOL_ERROR // 0x82
72
+ const val IMPLEMENTATION_SPECIFIC_ERROR_DISC: Int = IMPLEMENTATION_SPECIFIC_ERROR // 0x83
73
+ const val NOT_AUTHORIZED_DISC: Int = NOT_AUTHORIZED // 0x87
74
+ const val SERVER_BUSY_DISC: Int = SERVER_BUSY // 0x89
75
+ const val BAD_AUTHENTICATION_METHOD_DISC: Int = BAD_AUTHENTICATION_METHOD // 0x8C
76
+ const val TOPIC_FILTER_INVALID_DISC: Int = TOPIC_FILTER_INVALID // 0x8F
77
+ const val TOPIC_NAME_INVALID_DISC: Int = TOPIC_NAME_INVALID // 0x90
78
+ const val PACKET_TOO_LARGE_DISC: Int = PACKET_TOO_LARGE // 0x95
79
+ const val QUOTA_EXCEEDED_DISC: Int = QUOTA_EXCEEDED // 0x97
80
+ const val PAYLOAD_FORMAT_INVALID_DISC: Int = PAYLOAD_FORMAT_INVALID // 0x99
81
+ const val RETAIN_NOT_SUPPORTED_DISC: Int = RETAIN_NOT_SUPPORTED // 0x9A
82
+ const val QOS_NOT_SUPPORTED_DISC: Int = QOS_NOT_SUPPORTED // 0x9B
83
+ const val USE_ANOTHER_SERVER_DISC: Int = USE_ANOTHER_SERVER // 0x9C
84
+ const val SERVER_MOVED_DISC: Int = SERVER_MOVED // 0x9D
85
+ const val SHARED_SUBSCRIPTIONS_NOT_SUPPORTED_DISC: Int = SHARED_SUBSCRIPTIONS_NOT_SUPPORTED // 0x9E
86
+ const val CONNECTION_RATE_EXCEEDED_DISC: Int = CONNECTION_RATE_EXCEEDED // 0x9F
87
+ const val SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED_DISC: Int = SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED // 0xA1
88
+ const val WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED_DISC: Int = WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED // 0xA2
89
+
90
+ // PUBACK/PUBREC/PUBREL/PUBCOMP aliases
91
+ const val UNSPECIFIED_ERROR_PUB: Int = UNSPECIFIED_ERROR // 0x80
92
+ const val IMPLEMENTATION_SPECIFIC_ERROR_PUB: Int = IMPLEMENTATION_SPECIFIC_ERROR // 0x83
93
+ const val NOT_AUTHORIZED_PUB: Int = NOT_AUTHORIZED // 0x87
94
+ const val TOPIC_NAME_INVALID_PUB: Int = TOPIC_NAME_INVALID // 0x90
95
+ const val QUOTA_EXCEEDED_PUB: Int = QUOTA_EXCEEDED // 0x97
96
+ const val PAYLOAD_FORMAT_INVALID_PUB: Int = PAYLOAD_FORMAT_INVALID // 0x99
97
+
98
+ // SUBACK aliases
99
+ const val UNSPECIFIED_ERROR_SUB: Int = UNSPECIFIED_ERROR // 0x80
100
+ const val IMPLEMENTATION_SPECIFIC_ERROR_SUB: Int = IMPLEMENTATION_SPECIFIC_ERROR // 0x83
101
+ const val NOT_AUTHORIZED_SUB: Int = NOT_AUTHORIZED // 0x87
102
+ const val PACKET_IDENTIFIER_IN_USE_SUB: Int = PACKET_IDENTIFIER_IN_USE // 0x91
103
+ const val QUOTA_EXCEEDED_SUB: Int = QUOTA_EXCEEDED // 0x97
104
+
105
+ // UNSUBACK aliases
106
+ const val UNSPECIFIED_ERROR_UNSUB: Int = UNSPECIFIED_ERROR // 0x80
107
+ const val IMPLEMENTATION_SPECIFIC_ERROR_UNSUB: Int = IMPLEMENTATION_SPECIFIC_ERROR // 0x83
108
+ const val NOT_AUTHORIZED_UNSUB: Int = NOT_AUTHORIZED // 0x87
109
+ const val TOPIC_FILTER_INVALID_UNSUB: Int = TOPIC_FILTER_INVALID // 0x8F
110
+ const val PACKET_IDENTIFIER_IN_USE_UNSUB: Int = PACKET_IDENTIFIER_IN_USE // 0x91
99
111
  }
100
112
 
101
113
  // Compatibility mapping for MQTT 3.1.1 return codes
@@ -12,7 +12,7 @@ Pod::Spec.new do |s|
12
12
  s.source = { :git => 'https://github.com/annadata/capacitor-mqtt-quic', :tag => s.version.to_s }
13
13
  s.source_files = 'Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
14
  s.resources = ['Sources/MqttQuicPlugin/Resources/*.pem']
15
- s.ios.deployment_target = '15.0'
15
+ s.ios.deployment_target = '14.0'
16
16
  s.dependency 'Capacitor'
17
17
  s.swift_version = '5.1'
18
18
  s.vendored_libraries = [
@@ -71,7 +71,7 @@ public final class MQTT5PropertyEncoder {
71
71
 
72
72
  switch propId {
73
73
  case MQTT5PropertyType.payloadFormatIndicator.rawValue:
74
- result.append((value as? Int ?? 0) & 0xFF)
74
+ result.append(UInt8((value as? Int ?? 0) & 0xFF))
75
75
 
76
76
  case MQTT5PropertyType.messageExpiryInterval.rawValue,
77
77
  MQTT5PropertyType.sessionExpiryInterval.rawValue,
@@ -117,7 +117,7 @@ public final class MQTT5PropertyEncoder {
117
117
  MQTT5PropertyType.wildcardSubscriptionAvailable.rawValue,
118
118
  MQTT5PropertyType.subscriptionIdentifierAvailable.rawValue,
119
119
  MQTT5PropertyType.sharedSubscriptionAvailable.rawValue:
120
- result.append((value as? Int ?? 0) & 0xFF)
120
+ result.append(UInt8((value as? Int ?? 0) & 0xFF))
121
121
 
122
122
  case MQTT5PropertyType.userProperty.rawValue:
123
123
  if let pair = value as? (String, String) {
@@ -0,0 +1,131 @@
1
+ @@ -3,17 +3,27 @@
2
+ // MqttQuicPlugin
3
+ //
4
+ // MQTT 5.0 Reason Codes. Matches MQTTD mqttd/reason_codes.py.
5
+ +// Swift requires unique raw values; MQTT 5.0 reuses byte values across packet types.
6
+ +// We keep one case per unique byte; semantic aliases documented in comments.
7
+ //
8
+
9
+ import Foundation
10
+
11
+ public enum MQTT5ReasonCode: UInt8 {
12
+ - // Success
13
+ + // 0x00 - Success (CONNACK), Granted QoS 0 (SUBACK), Success (UNSUBACK), Normal disconnection (DISCONNECT)
14
+ case success = 0x00
15
+ - case normalDisconnection = 0x00
16
+ + // 0x01 - Granted QoS 1 (SUBACK)
17
+ + case grantedQoS1 = 0x01
18
+ + // 0x02 - Granted QoS 2 (SUBACK)
19
+ + case grantedQoS2 = 0x02
20
+ + // 0x04 - Disconnect with will message
21
+ case disconnectWithWillMessage = 0x04
22
+ + // 0x10 - No matching subscribers (PUBACK/PUBREC/PUBREL/PUBCOMP)
23
+ + case noMatchingSubscribers = 0x10
24
+ + // 0x11 - No subscription existed (UNSUBACK)
25
+ + case noSubscriptionExisted = 0x11
26
+
27
+ - // CONNACK Reason Codes
28
+ + // CONNACK / generic errors (0x80+)
29
+ case unspecifiedError = 0x80
30
+ case malformedPacket = 0x81
31
+ case protocolError = 0x82
32
+ @@ -25,81 +35,37 @@
33
+ case serverUnavailable = 0x88
34
+ case serverBusy = 0x89
35
+ case banned = 0x8A
36
+ + case serverShuttingDown = 0x8B
37
+ case badAuthenticationMethod = 0x8C
38
+ + case keepAliveTimeout = 0x8D
39
+ + case sessionTakenOver = 0x8E
40
+ + case topicFilterInvalid = 0x8F
41
+ case topicNameInvalid = 0x90
42
+ + case packetIdentifierInUse = 0x91
43
+ + case receiveMaximumExceeded = 0x93
44
+ + case topicAliasInvalid = 0x94
45
+ case packetTooLarge = 0x95
46
+ + case messageRateTooHigh = 0x96
47
+ case quotaExceeded = 0x97
48
+ + case administrativeAction = 0x98
49
+ case payloadFormatInvalid = 0x99
50
+ case retainNotSupported = 0x9A
51
+ case qosNotSupported = 0x9B
52
+ case useAnotherServer = 0x9C
53
+ case serverMoved = 0x9D
54
+ - case connectionRateExceeded = 0x9F
55
+ -
56
+ - // PUBACK, PUBREC, PUBREL, PUBCOMP Reason Codes
57
+ - case noMatchingSubscribers = 0x10
58
+ - case unspecifiedErrorPub = 0x80
59
+ - case implementationSpecificErrorPub = 0x83
60
+ - case notAuthorizedPub = 0x87
61
+ - case topicNameInvalidPub = 0x90
62
+ - case packetIdentifierInUse = 0x91
63
+ - case quotaExceededPub = 0x97
64
+ - case payloadFormatInvalidPub = 0x99
65
+ -
66
+ - // SUBACK Reason Codes
67
+ - case grantedQoS0 = 0x00
68
+ - case grantedQoS1 = 0x01
69
+ - case grantedQoS2 = 0x02
70
+ - case unspecifiedErrorSub = 0x80
71
+ - case implementationSpecificErrorSub = 0x83
72
+ - case notAuthorizedSub = 0x87
73
+ - case topicFilterInvalid = 0x8F
74
+ - case packetIdentifierInUseSub = 0x91
75
+ - case quotaExceededSub = 0x97
76
+ case sharedSubscriptionsNotSupported = 0x9E
77
+ + case connectionRateExceeded = 0x9F
78
+ + case maximumConnectTime = 0xA0
79
+ case subscriptionIdentifiersNotSupported = 0xA1
80
+ case wildcardSubscriptionsNotSupported = 0xA2
81
+ +}
82
+
83
+ - // UNSUBACK Reason Codes
84
+ - case successUnsub = 0x00
85
+ - case noSubscriptionExisted = 0x11
86
+ - case unspecifiedErrorUnsub = 0x80
87
+ - case implementationSpecificErrorUnsub = 0x83
88
+ - case notAuthorizedUnsub = 0x87
89
+ - case topicFilterInvalidUnsub = 0x8F
90
+ - case packetIdentifierInUseUnsub = 0x91
91
+ -
92
+ - // DISCONNECT Reason Codes
93
+ - case normalDisconnectionDisc = 0x00
94
+ - case disconnectWithWillMessageDisc = 0x04
95
+ - case unspecifiedErrorDisc = 0x80
96
+ - case malformedPacketDisc = 0x81
97
+ - case protocolErrorDisc = 0x82
98
+ - case implementationSpecificErrorDisc = 0x83
99
+ - case notAuthorizedDisc = 0x87
100
+ - case serverBusyDisc = 0x89
101
+ - case serverShuttingDown = 0x8B
102
+ - case badAuthenticationMethodDisc = 0x8C
103
+ - case keepAliveTimeout = 0x8D
104
+ - case sessionTakenOver = 0x8E
105
+ - case topicFilterInvalidDisc = 0x8F
106
+ - case topicNameInvalidDisc = 0x90
107
+ - case receiveMaximumExceeded = 0x93
108
+ - case topicAliasInvalid = 0x94
109
+ - case packetTooLargeDisc = 0x95
110
+ - case messageRateTooHigh = 0x96
111
+ - case quotaExceededDisc = 0x97
112
+ - case administrativeAction = 0x98
113
+ - case payloadFormatInvalidDisc = 0x99
114
+ - case retainNotSupportedDisc = 0x9A
115
+ - case qosNotSupportedDisc = 0x9B
116
+ - case useAnotherServerDisc = 0x9C
117
+ - case serverMovedDisc = 0x9D
118
+ - case sharedSubscriptionsNotSupportedDisc = 0x9E
119
+ - case connectionRateExceededDisc = 0x9F
120
+ - case maximumConnectTime = 0xA0
121
+ - case subscriptionIdentifiersNotSupportedDisc = 0xA1
122
+ - case wildcardSubscriptionsNotSupportedDisc = 0xA2
123
+ +// Aliases for context clarity (same raw values)
124
+ +extension MQTT5ReasonCode {
125
+ + /// SUBACK granted QoS 0 (rawValue 0x00, same as success)
126
+ + public static let grantedQoS0: MQTT5ReasonCode = .success
127
+ + /// DISCONNECT normal disconnection (rawValue 0x00)
128
+ + public static let normalDisconnectionDisc: MQTT5ReasonCode = .success
129
+ }
130
+
131
+ // Compatibility mapping for MQTT 3.1.1 return codes
@@ -11,6 +11,17 @@ import Capacitor
11
11
  @objc(MqttQuicPlugin)
12
12
  public class MqttQuicPlugin: CAPPlugin, CAPBridgedPlugin {
13
13
 
14
+ public let identifier = "MqttQuicPlugin"
15
+ public let jsName = "MqttQuic"
16
+ public let pluginMethods: [CAPPluginMethod] = [
17
+ CAPPluginMethod(name: "connect", returnType: CAPPluginReturnPromise),
18
+ CAPPluginMethod(name: "disconnect", returnType: CAPPluginReturnPromise),
19
+ CAPPluginMethod(name: "publish", returnType: CAPPluginReturnPromise),
20
+ CAPPluginMethod(name: "subscribe", returnType: CAPPluginReturnPromise),
21
+ CAPPluginMethod(name: "unsubscribe", returnType: CAPPluginReturnPromise),
22
+ CAPPluginMethod(name: "testHarness", returnType: CAPPluginReturnPromise)
23
+ ]
24
+
14
25
  private var client = MQTTClient(protocolVersion: .auto)
15
26
 
16
27
  @objc override public func load() {}
@@ -46,6 +46,10 @@ public final class MockStreamBuffer {
46
46
  return Data(out)
47
47
  }
48
48
 
49
+ public func appendWrite(_ data: Data) {
50
+ writeBuffer.append(data)
51
+ }
52
+
49
53
  public func consumeWrite() -> Data {
50
54
  let d = writeBuffer
51
55
  writeBuffer = Data()
@@ -94,7 +98,7 @@ public final class MockStreamWriter: MQTTStreamWriterProtocol {
94
98
  }
95
99
 
96
100
  public func write(_ data: Data) async throws {
97
- buffer.writeBuffer.append(data)
101
+ buffer.appendWrite(data)
98
102
  }
99
103
 
100
104
  public func drain() async throws {}
@@ -23,7 +23,12 @@ if [ -z "$PROJECT_DIR" ] && [ -f "$SCRIPT_DIR/../package.json" ]; then
23
23
  PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
24
24
  fi
25
25
  if [ -n "$PROJECT_DIR" ]; then
26
- REF_CODE_DIR="$(cd "$PROJECT_DIR/ref-code" && pwd)"
26
+ if [ -d "$PROJECT_DIR/ref-code" ]; then
27
+ REF_CODE_DIR="$(cd "$PROJECT_DIR/ref-code" && pwd)"
28
+ else
29
+ # Plugin run from repo: use parent as ref-code (e.g. ref-code/capacitor-mqtt-quic -> ref-code)
30
+ REF_CODE_DIR="$(cd "$PROJECT_DIR/.." && pwd)"
31
+ fi
27
32
  else
28
33
  if [ -d "$SCRIPT_DIR/../ref-code" ]; then
29
34
  REF_CODE_DIR="$(cd "$SCRIPT_DIR/../ref-code" && pwd)"
@@ -35,6 +40,10 @@ NGHTTP3_SOURCE_DIR="${NGHTTP3_SOURCE_DIR:-$REF_CODE_DIR/nghttp3}"
35
40
  if [[ "$NGHTTP3_SOURCE_DIR" != /* ]]; then
36
41
  NGHTTP3_SOURCE_DIR="$REF_CODE_DIR/$NGHTTP3_SOURCE_DIR"
37
42
  fi
43
+ # If default path does not exist, try sibling ref-code/nghttp3 (e.g. when plugin has ref-code/ but nghttp3 lives in repo ref-code/)
44
+ if [ -n "$PROJECT_DIR" ] && [ ! -d "$NGHTTP3_SOURCE_DIR" ] && [ -d "$(cd "$PROJECT_DIR/.." && pwd)/nghttp3" ]; then
45
+ NGHTTP3_SOURCE_DIR="$(cd "$PROJECT_DIR/../nghttp3" && pwd)"
46
+ fi
38
47
  ARCH="${ARCH:-arm64}"
39
48
  SDK="${SDK:-iphoneos}"
40
49
  BUILD_TYPE="${BUILD_TYPE:-Release}"
@@ -100,8 +109,8 @@ if [ ! -d "$NGHTTP3_SOURCE_DIR" ]; then
100
109
  exit 1
101
110
  fi
102
111
 
103
- # Ensure required submodules are present (sfparse, munit)
104
- if [ ! -f "$NGHTTP3_SOURCE_DIR/sfparse/sfparse.c" ] || [ ! -f "$NGHTTP3_SOURCE_DIR/munit/munit.c" ]; then
112
+ # Ensure required sources are present (sfparse: top-level or lib/sfparse)
113
+ if [ ! -f "$NGHTTP3_SOURCE_DIR/sfparse/sfparse.c" ] && [ ! -f "$NGHTTP3_SOURCE_DIR/lib/sfparse/sfparse.c" ]; then
105
114
  if [ -d "$NGHTTP3_SOURCE_DIR/.git" ]; then
106
115
  echo "Initializing nghttp3 submodules..."
107
116
  (cd "$NGHTTP3_SOURCE_DIR" && git submodule update --init --recursive) || {
@@ -109,7 +118,7 @@ if [ ! -f "$NGHTTP3_SOURCE_DIR/sfparse/sfparse.c" ] || [ ! -f "$NGHTTP3_SOURCE_D
109
118
  exit 1
110
119
  }
111
120
  else
112
- echo "Error: nghttp3 submodules are missing (sfparse/munit)"
121
+ echo "Error: nghttp3 submodules are missing (sfparse)"
113
122
  echo "Please clone with submodules:"
114
123
  echo " git clone --recurse-submodules https://github.com/ngtcp2/nghttp3.git $NGHTTP3_SOURCE_DIR"
115
124
  echo "Or if already cloned:"
@@ -36,6 +36,10 @@ NGTCP2_SOURCE_DIR="${NGTCP2_SOURCE_DIR:-$REF_CODE_DIR/ngtcp2}"
36
36
  if [[ "$NGTCP2_SOURCE_DIR" != /* ]]; then
37
37
  NGTCP2_SOURCE_DIR="$REF_CODE_DIR/$NGTCP2_SOURCE_DIR"
38
38
  fi
39
+ # If default path does not exist, try sibling ref-code/ngtcp2 (e.g. when plugin has ref-code/ but ngtcp2 lives in repo ref-code/)
40
+ if [ -n "$PROJECT_DIR" ] && [ ! -d "$NGTCP2_SOURCE_DIR" ] && [ -d "$(cd "$PROJECT_DIR/.." && pwd)/ngtcp2" ]; then
41
+ NGTCP2_SOURCE_DIR="$(cd "$PROJECT_DIR/../ngtcp2" && pwd)"
42
+ fi
39
43
  OPENSSL_PATH="${OPENSSL_PATH:-}"
40
44
  USE_QUICTLS="${USE_QUICTLS:-0}"
41
45
  ARCH="${ARCH:-arm64}"
@@ -111,8 +115,8 @@ if [ ! -d "$NGTCP2_SOURCE_DIR" ]; then
111
115
  exit 1
112
116
  fi
113
117
 
114
- # Ensure required submodules are present (munit)
115
- if [ ! -f "$NGTCP2_SOURCE_DIR/munit/munit.c" ]; then
118
+ # Ensure required sources are present (munit: top-level or tests/munit)
119
+ if [ ! -f "$NGTCP2_SOURCE_DIR/munit/munit.c" ] && [ ! -f "$NGTCP2_SOURCE_DIR/tests/munit/munit.c" ]; then
116
120
  if [ -d "$NGTCP2_SOURCE_DIR/.git" ]; then
117
121
  echo "Initializing ngtcp2 submodules..."
118
122
  (cd "$NGTCP2_SOURCE_DIR" && git submodule update --init --recursive) || {
Binary file
Binary file
Binary file
Binary file
package/ios/libs/libssl.a CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@annadata/capacitor-mqtt-quic",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "MQTT-over-QUIC client for Capacitor (iOS/Android). Uses ngtcp2 for QUIC; MQTT over WebSocket fallback on web.",
6
6
  "main": "dist/plugin.cjs.js",