u8-mqtt 0.4.1 → 0.5.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 (81) hide show
  1. package/cjs/basic-v4.cjs +103 -137
  2. package/cjs/basic-v4.cjs.map +1 -1
  3. package/cjs/basic-v5.cjs +102 -136
  4. package/cjs/basic-v5.cjs.map +1 -1
  5. package/cjs/index.cjs +144 -189
  6. package/cjs/index.cjs.map +1 -1
  7. package/cjs/v4.cjs +145 -190
  8. package/cjs/v4.cjs.map +1 -1
  9. package/cjs/v5.cjs +144 -189
  10. package/cjs/v5.cjs.map +1 -1
  11. package/code/_dispatch.jsy +16 -0
  12. package/code/base.jsy +68 -124
  13. package/code/core.jsy +15 -9
  14. package/code/router_path.jsy +24 -43
  15. package/code/with_topic_router.jsy +18 -10
  16. package/esm/basic-v4.js +1154 -0
  17. package/esm/basic-v4.js.map +1 -0
  18. package/esm/basic-v5.js +1416 -0
  19. package/esm/basic-v5.js.map +1 -0
  20. package/esm/deno/basic-v4.js +103 -137
  21. package/esm/deno/basic-v4.js.map +1 -1
  22. package/esm/deno/basic-v5.js +102 -136
  23. package/esm/deno/basic-v5.js.map +1 -1
  24. package/esm/deno/index.js +144 -189
  25. package/esm/deno/index.js.map +1 -1
  26. package/esm/deno/v4.js +145 -190
  27. package/esm/deno/v4.js.map +1 -1
  28. package/esm/deno/v5.js +144 -189
  29. package/esm/deno/v5.js.map +1 -1
  30. package/esm/index.js +1599 -0
  31. package/esm/index.js.map +1 -0
  32. package/esm/node/basic-v4.js +103 -137
  33. package/esm/node/basic-v4.js.map +1 -1
  34. package/esm/node/basic-v4.mjs +103 -137
  35. package/esm/node/basic-v4.mjs.map +1 -1
  36. package/esm/node/basic-v5.js +102 -136
  37. package/esm/node/basic-v5.js.map +1 -1
  38. package/esm/node/basic-v5.mjs +102 -136
  39. package/esm/node/basic-v5.mjs.map +1 -1
  40. package/esm/node/index.js +144 -189
  41. package/esm/node/index.js.map +1 -1
  42. package/esm/node/index.mjs +144 -189
  43. package/esm/node/index.mjs.map +1 -1
  44. package/esm/node/v4.js +145 -190
  45. package/esm/node/v4.js.map +1 -1
  46. package/esm/node/v4.mjs +145 -190
  47. package/esm/node/v4.mjs.map +1 -1
  48. package/esm/node/v5.js +144 -189
  49. package/esm/node/v5.js.map +1 -1
  50. package/esm/node/v5.mjs +144 -189
  51. package/esm/node/v5.mjs.map +1 -1
  52. package/esm/v4.js +1336 -0
  53. package/esm/v4.js.map +1 -0
  54. package/esm/v5.js +1599 -0
  55. package/esm/v5.js.map +1 -0
  56. package/esm/web/basic-v4.js +103 -137
  57. package/esm/web/basic-v4.js.map +1 -1
  58. package/esm/web/basic-v4.min.js +1 -1
  59. package/esm/web/basic-v4.min.js.br +0 -0
  60. package/esm/web/basic-v4.min.js.gz +0 -0
  61. package/esm/web/basic-v5.js +102 -136
  62. package/esm/web/basic-v5.js.map +1 -1
  63. package/esm/web/basic-v5.min.js +1 -1
  64. package/esm/web/basic-v5.min.js.br +0 -0
  65. package/esm/web/basic-v5.min.js.gz +0 -0
  66. package/esm/web/index.js +144 -189
  67. package/esm/web/index.js.map +1 -1
  68. package/esm/web/index.min.js +1 -1
  69. package/esm/web/index.min.js.br +0 -0
  70. package/esm/web/index.min.js.gz +0 -0
  71. package/esm/web/v4.js +145 -190
  72. package/esm/web/v4.js.map +1 -1
  73. package/esm/web/v4.min.js +1 -1
  74. package/esm/web/v4.min.js.br +0 -0
  75. package/esm/web/v4.min.js.gz +0 -0
  76. package/esm/web/v5.js +144 -189
  77. package/esm/web/v5.js.map +1 -1
  78. package/esm/web/v5.min.js +1 -1
  79. package/esm/web/v5.min.js.br +0 -0
  80. package/esm/web/v5.min.js.gz +0 -0
  81. package/package.json +14 -9
package/code/base.jsy CHANGED
@@ -9,28 +9,34 @@ export class MQTTError extends Error ::
9
9
 
10
10
  export class MQTTBase ::
11
11
  constructor(opt={}) ::
12
+ this.with(opt)
12
13
  this._conn_ = _mqtt_conn @ this,
13
14
  this._init_dispatch(opt, this)
14
15
 
16
+ with(fns_ns) ::
17
+ for let [k,v] of Object.entries(fns_ns) ::
18
+ if 'function' === typeof v :: this[k] = v
19
+ return this
20
+
15
21
  async conn_emit(evt, arg, err_arg) ::
16
22
  this.log_conn?.(evt, arg, err_arg)
17
23
  try ::
18
- let fn_evt = this[await evt] // microtask break
24
+ let fn_evt = this[await evt] // microtask break using `await evt`
19
25
  if fn_evt ::
20
26
  await fn_evt.call(this, this, arg, err_arg)
21
- else if err_arg ::
22
- await this.on_error(err_arg, evt)
27
+ else if err_arg :: throw err_arg
23
28
  catch err ::
24
29
  this.on_error(err, evt)
25
30
 
26
- on_error(err, err_path) ::
27
- console.warn @ '[[u8-mqtt error: %s]]', err_path, err
31
+ on_error(err, evt) ::
32
+ console.warn @ '[[u8-mqtt error: %s]]', evt, err
28
33
 
29
34
  // Handshaking Packets
30
35
 
31
36
  async connect(pkt={}) ::
32
- let cid = pkt.client_id || ['u8-mqtt--', '']
33
- if Array.isArray(cid) ::
37
+ let cid = pkt.client_id || this.client_id
38
+ if 'string' !== typeof cid ::
39
+ // see init_client_id implementation in core.jsy
34
40
  pkt.client_id = cid = this.init_client_id(cid)
35
41
  this.client_id = cid
36
42
 
@@ -56,12 +62,13 @@ export class MQTTBase ::
56
62
  return this._send('auth', pkt, 'auth')
57
63
 
58
64
  ping() :: return this._send('pingreq', null, 'pingresp')
59
-
65
+ puback({pkt_id}) :: return this._send('puback', {pkt_id})
60
66
 
61
67
  // alias: sub
62
68
  subscribe(pkt, ex, topic_prefix) ::
63
69
  pkt = _as_topics(pkt, ex, topic_prefix)
64
- return this._send('subscribe', pkt, pkt)
70
+ let suback = this._send('subscribe', pkt, pkt)
71
+ return this.on_sub?.(suback, pkt) ?? suback
65
72
 
66
73
  // alias: unsub
67
74
  unsubscribe(pkt, ex, topic_prefix) ::
@@ -69,48 +76,52 @@ export class MQTTBase ::
69
76
  return this._send('unsubscribe', pkt, pkt)
70
77
 
71
78
 
72
- // alias: pub
73
- publish(pkt, pub_opt) :: return _pub(this, pkt, pub_opt)
74
- post(topic, payload, pub_opt) :: return _pub.m(this, topic, payload, pub_opt)
75
- send(topic, payload, pub_opt) :: return _pub.mq(this, topic, payload, pub_opt)
76
- store(topic, payload, pub_opt) :: return _pub.mqr(this, topic, payload, pub_opt)
77
-
78
- json_post(topic, msg, pub_opt) :: return _pub.o(this, topic, msg, pub_opt)
79
- json_send(topic, msg, pub_opt) :: return _pub.oq(this, topic, msg, pub_opt)
80
- json_store(topic, msg, pub_opt) :: return _pub.oqr(this, topic, msg, pub_opt)
81
-
82
- obj_post(topic, msg, pub_opt) :: return _pub.o(this, topic, msg, pub_opt)
83
- obj_send(topic, msg, pub_opt) :: return _pub.oq(this, topic, msg, pub_opt)
84
- obj_store(topic, msg, pub_opt) :: return _pub.oqr(this, topic, msg, pub_opt)
85
-
86
-
87
-
88
- // Utility Methods
89
-
90
- init_client_id(parts) ::
91
- let cid = this.client_id
92
-
93
- if undefined === cid ::
94
- this.client_id = cid = (
95
- #IF PLAT_WEB
96
- this.sess_client_id(parts)
97
- #ELSE
98
- this.new_client_id(parts)
99
- )
100
-
101
- return cid
102
-
103
- new_client_id(parts) ::
104
- return [parts[0], Math.random().toString(36).slice(2), parts[1]].join('')
105
-
106
- #IF PLAT_WEB
107
- sess_client_id(parts) ::
108
- let key = parts.join('\x20')
109
- let cid = sessionStorage.getItem(key)
110
- if null == cid ::
111
- cid = this.new_client_id(parts)
112
- sessionStorage.setItem(key, cid)
113
- return cid
79
+ post(topic, payload, pub_opt) :: // qos:0
80
+ return this.pub({topic, payload, qos:0}, pub_opt)
81
+ send(topic, payload, pub_opt) :: // qos:1
82
+ return this.pub({topic, payload, qos:1}, pub_opt)
83
+ store(topic, payload, pub_opt) :: // qos:1, retain: 1
84
+ return this.pub({topic, payload, qos:1, retain: 1}, pub_opt)
85
+
86
+ // alias: json_post
87
+ obj_post(topic, msg, pub_opt) :: // qos:0
88
+ return this.pub({topic, msg, arg: 'msg', qos:0}, pub_opt)
89
+ // alias: json_send
90
+ obj_send(topic, msg, pub_opt) :: // qos:1
91
+ return this.pub({topic, msg, arg: 'msg', qos:1}, pub_opt)
92
+ // alias: json_store
93
+ obj_store(topic, msg, pub_opt) :: // qos:1, retain: 1
94
+ return this.pub({topic, msg, arg: 'msg', qos:1, retain: 1}, pub_opt)
95
+
96
+ // alias: publish -- because 'pub' is shorter for semantic aliases above
97
+ async pub(pkt, pub_opt) ::
98
+ if undefined === pkt.payload ::
99
+ if 'function' === typeof pub_opt ::
100
+ pub_opt = {fn_encode: pub_opt}
101
+
102
+ let {msg} = pkt
103
+ switch typeof msg ::
104
+ case 'function':
105
+ pub_opt = {...pub_opt, fn_encode: msg}
106
+ // flow into 'undefined' case
107
+ case 'undefined':
108
+ // return a single-value closure to publish packets
109
+ return v => this.pub({...pkt, [pkt.arg || 'payload']: v}, pub_opt)
110
+
111
+ // Encode payload from msg; fn_encode allows alternative to JSON.stringify
112
+ let {fn_encode} = pub_opt || {}
113
+ pkt.payload = fn_encode
114
+ ? await fn_encode(msg)
115
+ : JSON.stringify(msg)
116
+
117
+ if pub_opt ::
118
+ if pub_opt.props ::
119
+ pkt.props = pub_opt.props
120
+ if pub_opt.xform ::
121
+ pkt = pub_opt.xform(pkt) || pkt
122
+
123
+ return this._send @ 'publish', pkt,
124
+ pkt.qos ? pkt : void 0 // key
114
125
 
115
126
 
116
127
  // Internal API
@@ -125,41 +136,18 @@ export class MQTTBase ::
125
136
  return _mqtt_dispatch(this, target)
126
137
 
127
138
  static _aliases() ::
128
- return ' pub:publish sub:subscribe unsub:unsubscribe '
139
+ return ' publish:pub sub:subscribe unsub:unsubscribe json_post:obj_post json_send:obj_send json_store:obj_store'
129
140
 
130
141
  static _once_(self=this) ::
131
142
  self._once_ = _=>0
132
- self.MQTTError = MQTTError
133
143
  let p = self.prototype
144
+ p.MQTTError = MQTTError
134
145
  for let alias of self._aliases().split(/\s+/) ::
135
146
  alias = alias.split(':')
136
147
  let fn = alias[1] && p[alias[1]]
137
148
  if fn :: p[alias[0]] = fn
138
149
 
139
150
 
140
- /*
141
- on_mqtt_type = {
142
- mqtt_auth(pkt, ctx) ::
143
- mqtt_connect(pkt, ctx) ::
144
- mqtt_connack(pkt, ctx) ::
145
- mqtt_disconnect(pkt, ctx) ::
146
-
147
- mqtt_publish(pkt, ctx)
148
- mqtt_subscribe(pkt, ctx) ::
149
- mqtt_unsubscribe(pkt, ctx) ::
150
-
151
- mqtt_pingreq(pkt, ctx) ::
152
- mqtt_pingresp(pkt, ctx) ::
153
- }
154
- */
155
-
156
-
157
- const _prefix_topics = (topic_prefix, iterable) =>
158
- Array.from @ iterable, value => @
159
- value.trim // string
160
- ? _prefix_topics(topic_prefix, value)
161
- : topic_prefix + value
162
-
163
151
  function _as_topics(pkt, ex, topic_prefix) ::
164
152
  if ex?.trim :: // string
165
153
  topic_prefix = ex
@@ -176,53 +164,9 @@ function _as_topics(pkt, ex, topic_prefix) ::
176
164
  if topic_prefix ::
177
165
  // particularly useful with shared queues, e.g.
178
166
  // topic_prefix = '$share/some-queue-name/'
179
- pkt.topics = _prefix_topics(topic_prefix, pkt.topics)
180
- return pkt
167
+ let _prefix_topics = v =>
168
+ v.trim ? topic_prefix+v : v.map(_prefix_topics)
181
169
 
182
-
183
- async function _pub(self, pkt, pub_opt) ::
184
- if undefined === pkt.payload ::
185
- if 'function' === typeof pub_opt ::
186
- pub_opt = {fn_encode: pub_opt}
187
-
188
- let {msg} = pkt
189
- switch typeof msg ::
190
- case 'function':
191
- pub_opt = {...pub_opt, fn_encode: msg}
192
- // flow into 'undefined' case
193
- case 'undefined':
194
- // return a single-value closure to publish packets
195
- return v => _pub(self, {...pkt, [pkt.arg || 'payload']: v}, pub_opt)
196
-
197
- default:
198
- // Encode payload from msg; fn_encode allows alternative to JSON.stringify
199
- let {fn_encode} = pub_opt || {}
200
- pkt.payload = fn_encode
201
- ? await fn_encode(msg)
202
- : JSON.stringify(msg)
203
-
204
- if pub_opt ::
205
- if pub_opt.props ::
206
- pkt.props = pub_opt.props
207
- if pub_opt.xform ::
208
- pkt = pub_opt.xform(pkt) || pkt
209
-
210
- return self._send @ 'publish', pkt,
211
- pkt.qos ? pkt : void 0 // key
212
-
213
- ::
214
- Object.assign @ _pub, @{}
215
- m: (self, topic, payload, pub_opt) =>
216
- _pub(self, {topic, payload, qos:0}, pub_opt)
217
- mq: (self, topic, payload, pub_opt) =>
218
- _pub(self, {topic, payload, qos:1}, pub_opt)
219
- mqr: (self, topic, payload, pub_opt) =>
220
- _pub(self, {topic, payload, qos:1, retain: 1}, pub_opt)
221
-
222
- o: (self, topic, msg, pub_opt) =>
223
- _pub(self, {topic, msg, arg: 'msg', qos:0}, pub_opt)
224
- oq: (self, topic, msg, pub_opt) =>
225
- _pub(self, {topic, msg, arg: 'msg', qos:1}, pub_opt)
226
- oqr: (self, topic, msg, pub_opt) =>
227
- _pub(self, {topic, msg, arg: 'msg', qos:1, retain: 1}, pub_opt)
170
+ pkt.topics = pkt.topics.map(_prefix_topics)
171
+ return pkt
228
172
 
package/code/core.jsy CHANGED
@@ -12,20 +12,27 @@ const pkt_api = {
12
12
  }
13
13
 
14
14
  export class MQTTCore extends MQTTBase ::
15
- constructor(opt={}) ::
16
- super(opt)
17
- this.with(opt)
18
-
19
15
  static mqtt_ctx(mqtt_level, mqtt_opts, pkt_ctx=pkt_api) ::
20
16
  let self = class extends this {}
21
17
  self.prototype.mqtt_ctx =
22
18
  mqtt_pkt_ctx(mqtt_level, mqtt_opts, pkt_ctx)
23
19
  return self
24
20
 
25
- with(fns_ns) ::
26
- for let [k,v] of Object.entries(fns_ns) ::
27
- if 'function' === typeof v :: this[k] = v
28
- return this
21
+
22
+ // automatic Client Id for connect()
23
+ init_client_id(parts=['u8-mqtt--','']) ::
24
+ let sess_stg=this.sess_stg
25
+ let key, cid = sess_stg?.getItem(key=parts.join(' '))
26
+ if ! cid ::
27
+ cid = parts.join @ Math.random().toString(36).slice(2)
28
+ sess_stg?.setItem(key, cid)
29
+ return cid
30
+
31
+ get sess_stg() :: return globalThis.sessionStorage
32
+
33
+
34
+ //on_error(err, evt) ::
35
+ // console.warn @ '[[u8-mqtt error: %s]]', evt, err
29
36
 
30
37
  //log_conn(evt, arg, err_arg) ::
31
38
  // console.info @ '[[u8-mqtt log: %s]]', evt, arg, err_arg
@@ -102,7 +109,6 @@ export class MQTTCore extends MQTTBase ::
102
109
  #IF PLAT_NODEJS
103
110
  with_tcp(...opt) ::
104
111
  opt = this._conn_opt(opt)
105
- console.log @: opt
106
112
  return this._use_conn @=>
107
113
  this.with_stream @
108
114
  tcp_connect(opt)
@@ -11,7 +11,7 @@ export const with_topic_path_router = /* #__PURE__ */
11
11
  const mqtt_topic = topic_route =>
12
12
  topic_route
13
13
  .replace @ /[*].*$/, '#'
14
- .replace @ /:\w+\??/g, '+'
14
+ .replace @ /:\w[^\/]*/g, '+'
15
15
 
16
16
  /* From the [MQTT v5 Spec](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Topic_Names_and)
17
17
  4.7.1.2 Multi-level wildcard -- (‘#’ U+0023)
@@ -41,9 +41,8 @@ function mqtt_topic_path_router() ::
41
41
  let priority = args.pop()
42
42
 
43
43
  if 'function' !== typeof fn ::
44
- if false === fn ::
45
- fn = _ignore
46
- else throw new TypeError()
44
+ if fn :: throw new TypeError()
45
+ fn = _ignore
47
46
 
48
47
  let rte = _rxp_parse @ as_topic_path @ topic_route
49
48
 
@@ -59,7 +58,7 @@ function mqtt_topic_path_router() ::
59
58
  clear(priority) ::
60
59
  pri_lsts[priority ? 0 : 1] = []
61
60
  if null == priority ::
62
- pri_lsts[1] = []
61
+ pri_lsts[1] = [] // null clears both lists
63
62
 
64
63
  async invoke(pkt, ctx) ::
65
64
  ctx.idx = 0
@@ -75,51 +74,33 @@ function mqtt_topic_path_router() ::
75
74
  break
76
75
  else ctx.idx++
77
76
 
78
- let {pkt_id, qos} = pkt
79
- if 1 === qos ::
80
- await ctx.mqtt._send('puback', {pkt_id})
77
+ if 1 === pkt.qos ::
78
+ await ctx.mqtt.puback(pkt)
81
79
 
82
80
 
83
81
  function * _routes_iter(all_route_lists, topic) ::
82
+ topic = topic.replace(/^[\/]*/, '/') // ensure '/' prefix for regexparam library
84
83
  for let route_list of all_route_lists ::
85
- for let route of route_list ::
86
- let res = _route_match_one(topic, route)
87
- if undefined !== res ::
88
- yield res
89
-
90
-
91
- function _route_match_one(topic, {keys, pattern, tgt}) ::
92
- let match = '/' !== topic[0]
93
- ? pattern.exec('/'+topic)
94
- : pattern.exec(topic)
95
-
96
- if null === match ::
97
- return
98
-
99
- if false === keys ::
100
- let {groups} = match
101
- if ! groups ::
102
- return [tgt]
103
-
104
- let params = {}
105
- for let k in groups ::
106
- params[k] = groups[k]
107
-
108
- return [tgt, params]
109
-
110
- if 0 === keys.length ::
111
- return [tgt]
112
-
113
- let params = {}
114
- for let i=0; i<keys.length; i++ ::
115
- params[ keys[i] ] = match[1+i]
116
- return [tgt, params]
84
+ for let {keys, pattern, tgt} of route_list ::
85
+ let match = pattern.exec(topic)
86
+ if match ::
87
+ let params = keys
88
+ ? keys.reduce @
89
+ (o, k, i) => (o[k] = match[1+i], o)
90
+ {}
91
+ : match.groups ?? match
92
+ yield [tgt, params]
117
93
 
118
94
 
119
95
  function _route_remove(all_route_lists, query) ::
120
- let match = route => route===query || route.tgt===query || route.key===query
96
+ let fn_match = route => @
97
+ route===query
98
+ || route.tgt===query
99
+ || route.key===query
121
100
  for let lst of all_route_lists ::
122
- let i = lst.findIndex(match)
123
- if 0 <= i :: return !! lst.splice(i,1)
101
+ let i = lst.findIndex(fn_match)
102
+ if 0 <= i ::
103
+ lst.splice(i,1)
104
+ return true
124
105
  return false
125
106
 
@@ -14,29 +14,37 @@ export const with_topic_router = mqtt_topic_router =>
14
14
  return super._aliases() +
15
15
  ' sub_topic:subscribe_topic unsub_topic:unsubscribe_topic'
16
16
 
17
- _init_router(opt, client, target) ::
17
+ _init_router(opt, self, target) ::
18
+ this._subs = []
18
19
  let router = this.router = target.router =
19
- mqtt_topic_router(opt, this)
20
+ mqtt_topic_router(opt, this, target)
20
21
  return router?.invoke
21
- get on_topic() :: return this.router.add
22
22
 
23
- _sub_chain(topic, ex, topic_prefix) ::
24
- let res = this.subscribe @ [[ topic ]], ex, topic_prefix
25
- let subs = this.subs || @ this.subs = new Map()
26
- subs.set @ (res.topic = topic), (subs.last = res)
27
- return this // fluent api -- return this and track side effects
23
+ on_sub(suback, pkt) ::
24
+ suback.pkt = pkt
25
+ this._subs.push(suback)
26
+ return suback
27
+ subs_settled() ::
28
+ return Promise.allSettled @
29
+ this._subs.splice(0,Infinity)
28
30
 
29
31
  // alias: sub_topic
30
32
  subscribe_topic(topic_route, ...args) ::
31
33
  let router = this.router
32
34
  router.add @ topic_route, true, args.pop() // handler
33
35
  let topic = router.mqtt_topic(topic_route)
34
- return this._sub_chain @ topic, ...args // ex, topic_prefix
36
+ this.subscribe @ topic, ...args // ex, topic_prefix
37
+ return this // fluent api -- return this and track side effects
35
38
 
36
39
  // alias: unsub_topic
37
40
  unsubscribe_topic(topic_route, ...args) ::
38
41
  let router = this.router
39
42
  router.remove @ topic_route, true
40
43
  let topic = router.mqtt_topic(topic_route)
41
- return this.unsubscribe @ [[ topic ]], ...args // topic_prefix
44
+ return this.unsubscribe @ topic, ...args // topic_prefix
45
+
46
+ // add topic handlers without corresponding subscribe packet
47
+ on_topic(...args) ::
48
+ this.router.add(...args)
49
+ return this
42
50