@tier0/sdk-v2 0.0.0-stage → 0.2.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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +7 -0
  3. package/README.md +201 -2
  4. package/dist/cjs/app-identity-contract.generated.d.ts +22 -0
  5. package/dist/cjs/app-identity-contract.generated.js +51 -0
  6. package/dist/cjs/app-identity-contract.generated.js.map +1 -0
  7. package/dist/cjs/capabilities.d.ts +54 -0
  8. package/dist/cjs/capabilities.js +134 -0
  9. package/dist/cjs/capabilities.js.map +1 -0
  10. package/dist/cjs/client.d.ts +604 -0
  11. package/dist/cjs/client.js +35 -0
  12. package/dist/cjs/client.js.map +1 -0
  13. package/dist/cjs/cloud-server.d.ts +31 -0
  14. package/dist/cjs/cloud-server.js +114 -0
  15. package/dist/cjs/cloud-server.js.map +1 -0
  16. package/dist/cjs/cloud-transport.d.ts +67 -0
  17. package/dist/cjs/cloud-transport.js +272 -0
  18. package/dist/cjs/cloud-transport.js.map +1 -0
  19. package/dist/cjs/controls.d.ts +93 -0
  20. package/dist/cjs/controls.js +169 -0
  21. package/dist/cjs/controls.js.map +1 -0
  22. package/dist/cjs/data/bootstrap.d.ts +70 -0
  23. package/dist/cjs/data/bootstrap.js +291 -0
  24. package/dist/cjs/data/bootstrap.js.map +1 -0
  25. package/dist/cjs/data/call.d.ts +33 -0
  26. package/dist/cjs/data/call.js +216 -0
  27. package/dist/cjs/data/call.js.map +1 -0
  28. package/dist/cjs/data/client.d.ts +134 -0
  29. package/dist/cjs/data/client.js +185 -0
  30. package/dist/cjs/data/client.js.map +1 -0
  31. package/dist/cjs/data/collection.d.ts +335 -0
  32. package/dist/cjs/data/collection.js +601 -0
  33. package/dist/cjs/data/collection.js.map +1 -0
  34. package/dist/cjs/data/context.d.ts +12 -0
  35. package/dist/cjs/data/context.js +50 -0
  36. package/dist/cjs/data/context.js.map +1 -0
  37. package/dist/cjs/data/declarations.d.ts +11 -0
  38. package/dist/cjs/data/declarations.js +18 -0
  39. package/dist/cjs/data/declarations.js.map +1 -0
  40. package/dist/cjs/data/define.d.ts +77 -0
  41. package/dist/cjs/data/define.js +31 -0
  42. package/dist/cjs/data/define.js.map +1 -0
  43. package/dist/cjs/data/error.d.ts +78 -0
  44. package/dist/cjs/data/error.js +182 -0
  45. package/dist/cjs/data/error.js.map +1 -0
  46. package/dist/cjs/data/index.d.ts +24 -0
  47. package/dist/cjs/data/index.js +49 -0
  48. package/dist/cjs/data/index.js.map +1 -0
  49. package/dist/cjs/data/schema.d.ts +88 -0
  50. package/dist/cjs/data/schema.js +657 -0
  51. package/dist/cjs/data/schema.js.map +1 -0
  52. package/dist/cjs/data/sort.d.ts +8 -0
  53. package/dist/cjs/data/sort.js +21 -0
  54. package/dist/cjs/data/sort.js.map +1 -0
  55. package/dist/cjs/data/subscribe.d.ts +108 -0
  56. package/dist/cjs/data/subscribe.js +270 -0
  57. package/dist/cjs/data/subscribe.js.map +1 -0
  58. package/dist/cjs/files.d.ts +67 -0
  59. package/dist/cjs/files.js +119 -0
  60. package/dist/cjs/files.js.map +1 -0
  61. package/dist/cjs/framework-scope.d.ts +1 -0
  62. package/dist/cjs/framework-scope.js +14 -0
  63. package/dist/cjs/framework-scope.js.map +1 -0
  64. package/dist/cjs/index.d.ts +14 -0
  65. package/dist/cjs/index.js +33 -0
  66. package/dist/cjs/index.js.map +1 -0
  67. package/dist/cjs/mq/broker.d.ts +65 -0
  68. package/dist/cjs/mq/broker.js +115 -0
  69. package/dist/cjs/mq/broker.js.map +1 -0
  70. package/dist/cjs/mq/client.d.ts +37 -0
  71. package/dist/cjs/mq/client.js +321 -0
  72. package/dist/cjs/mq/client.js.map +1 -0
  73. package/dist/cjs/mq/index.d.ts +5 -0
  74. package/dist/cjs/mq/index.js +11 -0
  75. package/dist/cjs/mq/index.js.map +1 -0
  76. package/dist/cjs/mq/types.d.ts +48 -0
  77. package/dist/cjs/mq/types.js +3 -0
  78. package/dist/cjs/mq/types.js.map +1 -0
  79. package/dist/cjs/openapi/api.generated.d.ts +820 -0
  80. package/dist/cjs/openapi/api.generated.js +607 -0
  81. package/dist/cjs/openapi/api.generated.js.map +1 -0
  82. package/dist/cjs/openapi/client.d.ts +56 -0
  83. package/dist/cjs/openapi/client.js +219 -0
  84. package/dist/cjs/openapi/client.js.map +1 -0
  85. package/dist/cjs/openapi/codecs.generated.d.ts +2234 -0
  86. package/dist/cjs/openapi/codecs.generated.js +6 -0
  87. package/dist/cjs/openapi/codecs.generated.js.map +1 -0
  88. package/dist/cjs/openapi/index.d.ts +5 -0
  89. package/dist/cjs/openapi/index.js +12 -0
  90. package/dist/cjs/openapi/index.js.map +1 -0
  91. package/dist/cjs/openapi/types.generated.d.ts +3227 -0
  92. package/dist/cjs/openapi/types.generated.js +3 -0
  93. package/dist/cjs/openapi/types.generated.js.map +1 -0
  94. package/dist/cjs/package.json +1 -0
  95. package/dist/cjs/react.d.ts +9 -0
  96. package/dist/cjs/react.js +16 -0
  97. package/dist/cjs/react.js.map +1 -0
  98. package/dist/cjs/runtime-context.d.ts +9 -0
  99. package/dist/cjs/runtime-context.js +25 -0
  100. package/dist/cjs/runtime-context.js.map +1 -0
  101. package/dist/cjs/runtime-env.d.ts +1 -0
  102. package/dist/cjs/runtime-env.js +18 -0
  103. package/dist/cjs/runtime-env.js.map +1 -0
  104. package/dist/cjs/source.d.ts +39 -0
  105. package/dist/cjs/source.js +29 -0
  106. package/dist/cjs/source.js.map +1 -0
  107. package/dist/cjs/stream.d.ts +40 -0
  108. package/dist/cjs/stream.js +33 -0
  109. package/dist/cjs/stream.js.map +1 -0
  110. package/dist/cjs/uns-v2/client.d.ts +132 -0
  111. package/dist/cjs/uns-v2/client.js +352 -0
  112. package/dist/cjs/uns-v2/client.js.map +1 -0
  113. package/dist/cjs/uns-v2/codec.d.ts +11 -0
  114. package/dist/cjs/uns-v2/codec.js +290 -0
  115. package/dist/cjs/uns-v2/codec.js.map +1 -0
  116. package/dist/cjs/uns-v2/data-client.d.ts +10 -0
  117. package/dist/cjs/uns-v2/data-client.js +18 -0
  118. package/dist/cjs/uns-v2/data-client.js.map +1 -0
  119. package/dist/cjs/uns-v2/generated.d.ts +18178 -0
  120. package/dist/cjs/uns-v2/generated.js +144 -0
  121. package/dist/cjs/uns-v2/generated.js.map +1 -0
  122. package/dist/cjs/uns-v2/index.d.ts +5 -0
  123. package/dist/cjs/uns-v2/index.js +24 -0
  124. package/dist/cjs/uns-v2/index.js.map +1 -0
  125. package/dist/cjs/uns-v2/sse.d.ts +20 -0
  126. package/dist/cjs/uns-v2/sse.js +65 -0
  127. package/dist/cjs/uns-v2/sse.js.map +1 -0
  128. package/dist/cjs/vue.d.ts +9 -0
  129. package/dist/cjs/vue.js +39 -0
  130. package/dist/cjs/vue.js.map +1 -0
  131. package/dist/esm/app-identity-contract.generated.d.ts +22 -0
  132. package/dist/esm/app-identity-contract.generated.js +48 -0
  133. package/dist/esm/app-identity-contract.generated.js.map +1 -0
  134. package/dist/esm/capabilities.d.ts +54 -0
  135. package/dist/esm/capabilities.js +129 -0
  136. package/dist/esm/capabilities.js.map +1 -0
  137. package/dist/esm/client.d.ts +604 -0
  138. package/dist/esm/client.js +32 -0
  139. package/dist/esm/client.js.map +1 -0
  140. package/dist/esm/cloud-server.d.ts +31 -0
  141. package/dist/esm/cloud-server.js +109 -0
  142. package/dist/esm/cloud-server.js.map +1 -0
  143. package/dist/esm/cloud-transport.d.ts +67 -0
  144. package/dist/esm/cloud-transport.js +267 -0
  145. package/dist/esm/cloud-transport.js.map +1 -0
  146. package/dist/esm/controls.d.ts +93 -0
  147. package/dist/esm/controls.js +163 -0
  148. package/dist/esm/controls.js.map +1 -0
  149. package/dist/esm/data/bootstrap.d.ts +70 -0
  150. package/dist/esm/data/bootstrap.js +286 -0
  151. package/dist/esm/data/bootstrap.js.map +1 -0
  152. package/dist/esm/data/call.d.ts +33 -0
  153. package/dist/esm/data/call.js +211 -0
  154. package/dist/esm/data/call.js.map +1 -0
  155. package/dist/esm/data/client.d.ts +134 -0
  156. package/dist/esm/data/client.js +181 -0
  157. package/dist/esm/data/client.js.map +1 -0
  158. package/dist/esm/data/collection.d.ts +335 -0
  159. package/dist/esm/data/collection.js +594 -0
  160. package/dist/esm/data/collection.js.map +1 -0
  161. package/dist/esm/data/context.d.ts +12 -0
  162. package/dist/esm/data/context.js +44 -0
  163. package/dist/esm/data/context.js.map +1 -0
  164. package/dist/esm/data/declarations.d.ts +11 -0
  165. package/dist/esm/data/declarations.js +10 -0
  166. package/dist/esm/data/declarations.js.map +1 -0
  167. package/dist/esm/data/define.d.ts +77 -0
  168. package/dist/esm/data/define.js +27 -0
  169. package/dist/esm/data/define.js.map +1 -0
  170. package/dist/esm/data/error.d.ts +78 -0
  171. package/dist/esm/data/error.js +176 -0
  172. package/dist/esm/data/error.js.map +1 -0
  173. package/dist/esm/data/index.d.ts +24 -0
  174. package/dist/esm/data/index.js +20 -0
  175. package/dist/esm/data/index.js.map +1 -0
  176. package/dist/esm/data/schema.d.ts +88 -0
  177. package/dist/esm/data/schema.js +646 -0
  178. package/dist/esm/data/schema.js.map +1 -0
  179. package/dist/esm/data/sort.d.ts +8 -0
  180. package/dist/esm/data/sort.js +18 -0
  181. package/dist/esm/data/sort.js.map +1 -0
  182. package/dist/esm/data/subscribe.d.ts +108 -0
  183. package/dist/esm/data/subscribe.js +265 -0
  184. package/dist/esm/data/subscribe.js.map +1 -0
  185. package/dist/esm/files.d.ts +67 -0
  186. package/dist/esm/files.js +113 -0
  187. package/dist/esm/files.js.map +1 -0
  188. package/dist/esm/framework-scope.d.ts +1 -0
  189. package/dist/esm/framework-scope.js +11 -0
  190. package/dist/esm/framework-scope.js.map +1 -0
  191. package/dist/esm/index.d.ts +14 -0
  192. package/dist/esm/index.js +10 -0
  193. package/dist/esm/index.js.map +1 -0
  194. package/dist/esm/mq/broker.d.ts +65 -0
  195. package/dist/esm/mq/broker.js +109 -0
  196. package/dist/esm/mq/broker.js.map +1 -0
  197. package/dist/esm/mq/client.d.ts +37 -0
  198. package/dist/esm/mq/client.js +314 -0
  199. package/dist/esm/mq/client.js.map +1 -0
  200. package/dist/esm/mq/index.d.ts +5 -0
  201. package/dist/esm/mq/index.js +3 -0
  202. package/dist/esm/mq/index.js.map +1 -0
  203. package/dist/esm/mq/types.d.ts +48 -0
  204. package/dist/esm/mq/types.js +2 -0
  205. package/dist/esm/mq/types.js.map +1 -0
  206. package/dist/esm/openapi/api.generated.d.ts +820 -0
  207. package/dist/esm/openapi/api.generated.js +603 -0
  208. package/dist/esm/openapi/api.generated.js.map +1 -0
  209. package/dist/esm/openapi/client.d.ts +56 -0
  210. package/dist/esm/openapi/client.js +210 -0
  211. package/dist/esm/openapi/client.js.map +1 -0
  212. package/dist/esm/openapi/codecs.generated.d.ts +2234 -0
  213. package/dist/esm/openapi/codecs.generated.js +3 -0
  214. package/dist/esm/openapi/codecs.generated.js.map +1 -0
  215. package/dist/esm/openapi/index.d.ts +5 -0
  216. package/dist/esm/openapi/index.js +3 -0
  217. package/dist/esm/openapi/index.js.map +1 -0
  218. package/dist/esm/openapi/types.generated.d.ts +3227 -0
  219. package/dist/esm/openapi/types.generated.js +2 -0
  220. package/dist/esm/openapi/types.generated.js.map +1 -0
  221. package/dist/esm/package.json +1 -0
  222. package/dist/esm/react.d.ts +9 -0
  223. package/dist/esm/react.js +12 -0
  224. package/dist/esm/react.js.map +1 -0
  225. package/dist/esm/runtime-context.d.ts +9 -0
  226. package/dist/esm/runtime-context.js +22 -0
  227. package/dist/esm/runtime-context.js.map +1 -0
  228. package/dist/esm/runtime-env.d.ts +1 -0
  229. package/dist/esm/runtime-env.js +15 -0
  230. package/dist/esm/runtime-env.js.map +1 -0
  231. package/dist/esm/source.d.ts +39 -0
  232. package/dist/esm/source.js +25 -0
  233. package/dist/esm/source.js.map +1 -0
  234. package/dist/esm/stream.d.ts +40 -0
  235. package/dist/esm/stream.js +29 -0
  236. package/dist/esm/stream.js.map +1 -0
  237. package/dist/esm/uns-v2/client.d.ts +132 -0
  238. package/dist/esm/uns-v2/client.js +343 -0
  239. package/dist/esm/uns-v2/client.js.map +1 -0
  240. package/dist/esm/uns-v2/codec.d.ts +11 -0
  241. package/dist/esm/uns-v2/codec.js +284 -0
  242. package/dist/esm/uns-v2/codec.js.map +1 -0
  243. package/dist/esm/uns-v2/data-client.d.ts +10 -0
  244. package/dist/esm/uns-v2/data-client.js +14 -0
  245. package/dist/esm/uns-v2/data-client.js.map +1 -0
  246. package/dist/esm/uns-v2/generated.d.ts +18178 -0
  247. package/dist/esm/uns-v2/generated.js +141 -0
  248. package/dist/esm/uns-v2/generated.js.map +1 -0
  249. package/dist/esm/uns-v2/index.d.ts +5 -0
  250. package/dist/esm/uns-v2/index.js +5 -0
  251. package/dist/esm/uns-v2/index.js.map +1 -0
  252. package/dist/esm/uns-v2/sse.d.ts +20 -0
  253. package/dist/esm/uns-v2/sse.js +60 -0
  254. package/dist/esm/uns-v2/sse.js.map +1 -0
  255. package/dist/esm/vue.d.ts +9 -0
  256. package/dist/esm/vue.js +36 -0
  257. package/dist/esm/vue.js.map +1 -0
  258. package/docs/acceptance-2026-10-22.md +88 -0
  259. package/package.json +243 -4
  260. package/skills/tier0-sdk-v2/SKILL.md +66 -0
  261. package/skills/tier0-sdk-v2/references/integration.md +72 -0
  262. package/skills/tier0-sdk-v2/tier0-sdk-v2-agent/SKILL.md +73 -0
  263. package/skills/tier0-sdk-v2/tier0-sdk-v2-agent/references/results.md +45 -0
  264. package/skills/tier0-sdk-v2/tier0-sdk-v2-controls/SKILL.md +63 -0
  265. package/skills/tier0-sdk-v2/tier0-sdk-v2-data/SKILL.md +58 -0
  266. package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/consistency.md +70 -0
  267. package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/errors.md +82 -0
  268. package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/modeling.md +113 -0
  269. package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/read-write.md +168 -0
  270. package/skills/tier0-sdk-v2/tier0-sdk-v2-data/references/schema-evolution.md +36 -0
  271. package/skills/tier0-sdk-v2/tier0-sdk-v2-files/SKILL.md +85 -0
  272. package/skills/tier0-sdk-v2/tier0-sdk-v2-files/references/delete.md +22 -0
  273. package/skills/tier0-sdk-v2/tier0-sdk-v2-files/references/download.md +49 -0
  274. package/skills/tier0-sdk-v2/tier0-sdk-v2-files/references/multipart.md +42 -0
  275. package/skills/tier0-sdk-v2/tier0-sdk-v2-files/references/upload.md +27 -0
  276. package/skills/tier0-sdk-v2/tier0-sdk-v2-files/references/url.md +22 -0
  277. package/skills/tier0-sdk-v2/tier0-sdk-v2-members/SKILL.md +56 -0
  278. package/skills/tier0-sdk-v2/tier0-sdk-v2-members/references/app-members.md +41 -0
  279. package/skills/tier0-sdk-v2/tier0-sdk-v2-members/references/platform-members.md +56 -0
  280. package/skills/tier0-sdk-v2/tier0-sdk-v2-mq/SKILL.md +78 -0
  281. package/skills/tier0-sdk-v2/tier0-sdk-v2-mq/references/quickstart.md +69 -0
  282. package/skills/tier0-sdk-v2/tier0-sdk-v2-notifications/SKILL.md +42 -0
  283. package/skills/tier0-sdk-v2/tier0-sdk-v2-notifications/references/get.md +34 -0
  284. package/skills/tier0-sdk-v2/tier0-sdk-v2-notifications/references/send.md +72 -0
  285. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/SKILL.md +38 -0
  286. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/changes.md +40 -0
  287. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/create.md +63 -0
  288. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/delete.md +47 -0
  289. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/protocols.md +36 -0
  290. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/read.md +34 -0
  291. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/state.md +37 -0
  292. package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/update.md +62 -0
  293. package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/SKILL.md +43 -0
  294. package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/create.md +56 -0
  295. package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/delete.md +35 -0
  296. package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/deploy.md +64 -0
  297. package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/lifecycle.md +26 -0
  298. package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/read.md +35 -0
  299. package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/update.md +36 -0
  300. package/skills/tier0-sdk-v2/tier0-sdk-v2-system/SKILL.md +64 -0
  301. package/skills/tier0-sdk-v2/tier0-sdk-v2-system/references/info.md +23 -0
  302. package/skills/tier0-sdk-v2/tier0-sdk-v2-system/references/whoami.md +21 -0
  303. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/SKILL.md +55 -0
  304. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/create.md +56 -0
  305. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/data-workflows.md +69 -0
  306. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/delete.md +53 -0
  307. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/namespace.md +38 -0
  308. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/read.md +34 -0
  309. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/restore.md +39 -0
  310. package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/update.md +62 -0
@@ -0,0 +1,321 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Tier0MQClient = void 0;
7
+ const mqtt_1 = __importDefault(require("mqtt"));
8
+ const runtime_env_js_1 = require("../runtime-env.js");
9
+ const broker_js_1 = require("./broker.js");
10
+ function parseWorkspaceIDFromApiKey(apiKey) {
11
+ // Match the backend's sk-<type>-ws<base36>_<secret> contract, not only
12
+ // service keys. Other workspace-encoded keys use the same MQTT identity.
13
+ const match = /^sk-[^-]+-ws([0-9a-zA-Z]+)_.+$/.exec(apiKey.trim());
14
+ if (!match)
15
+ return undefined;
16
+ // Keep the int64 workspace ID exact; parseInt can round IDs above 2^53.
17
+ let workspaceID = 0n;
18
+ for (const digit of match[1]) {
19
+ workspaceID = workspaceID * 36n + BigInt(parseInt(digit, 36));
20
+ if (workspaceID > 9223372036854775807n)
21
+ return undefined;
22
+ }
23
+ return workspaceID > 0n ? workspaceID.toString() : undefined;
24
+ }
25
+ function generateRandomString(length = 8) {
26
+ const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
27
+ let result = '';
28
+ for (let i = 0; i < length; i++) {
29
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
30
+ }
31
+ return result;
32
+ }
33
+ class Tier0MQClient {
34
+ client = null;
35
+ config;
36
+ explicitClientId;
37
+ explicitUsername;
38
+ listeners = {};
39
+ subscriptions = [];
40
+ connectingPromise = null;
41
+ _connected = false;
42
+ constructor(config) {
43
+ this.explicitClientId = config?.clientId !== undefined;
44
+ this.explicitUsername = config?.username !== undefined;
45
+ const envHost = (0, runtime_env_js_1.getEnvVar)('TIER0_MQTT_HOST');
46
+ const envPort = (0, runtime_env_js_1.getEnvVar)('TIER0_MQTT_PORT');
47
+ const host = config?.host || envHost || '';
48
+ const port = config?.port || parseInt(envPort || '8084') || 8084;
49
+ const password = config?.password || (0, runtime_env_js_1.getEnvVar)('TIER0_API_KEY') || '';
50
+ // 从 apiKey 解析 workspaceID,自动生成 clientId 和 username
51
+ const workspaceID = config?.clientId && config?.username
52
+ ? undefined
53
+ : parseWorkspaceIDFromApiKey(password);
54
+ const autoClientId = workspaceID
55
+ ? `${workspaceID}&${generateRandomString(8)}`
56
+ : undefined;
57
+ const autoUsername = workspaceID
58
+ ? `${workspaceID}&open`
59
+ : undefined;
60
+ this.config = {
61
+ host,
62
+ port,
63
+ clientId: config?.clientId || autoClientId,
64
+ username: config?.username || autoUsername,
65
+ password,
66
+ keepAlive: config?.keepAlive ?? 60,
67
+ reconnectPeriod: config?.reconnectPeriod ?? 5000,
68
+ connectTimeout: config?.connectTimeout ?? 30000,
69
+ clean: config?.clean ?? true,
70
+ ...config,
71
+ };
72
+ }
73
+ get mqttUrl() {
74
+ const { host, port, secure } = this.config;
75
+ if (!host) {
76
+ throw new Error('MQTT host is required. Provide it via MQTTConfig or TIER0_MQTT_HOST environment variable.');
77
+ }
78
+ const normalizedHost = host.trim().replace(/\/+$/, '');
79
+ if (/^wss?:\/\//i.test(normalizedHost)) {
80
+ const url = (0, broker_js_1.toWebSocketUrl)(normalizedHost);
81
+ if (!url)
82
+ throw new TypeError('Invalid MQTT WebSocket URL');
83
+ return url;
84
+ }
85
+ // 裸 host(可含端口):按运行环境自适应 ws/wss
86
+ const endpoint = (0, broker_js_1.parseMqttBroker)(normalizedHost);
87
+ const hostname = endpoint?.hostname ?? normalizedHost;
88
+ // 裸 host:port 和 ws(s) URL 的端口按 WebSocket 端口处理;
89
+ // tcp/mqtt/mqtts 的端口是 TCP 端口(如 1883),wss 下不能沿用,回退到配置的 port
90
+ const effectivePort = !endpoint?.scheme || endpoint.scheme === 'ws' || endpoint.scheme === 'wss'
91
+ ? endpoint?.port ?? port
92
+ : port;
93
+ const useSecure = secure ?? ((0, broker_js_1.isBrowserHttps)() || effectivePort === broker_js_1.DEFAULT_WSS_PORT);
94
+ return `${useSecure ? 'wss' : 'ws'}://${hostname}:${effectivePort}/mqtt`;
95
+ }
96
+ // 内部确保已连接(懒连接)
97
+ async ensureConnected() {
98
+ if (this._connected)
99
+ return;
100
+ if (this.connectingPromise)
101
+ return this.connectingPromise;
102
+ void this.mqttUrl;
103
+ if (!this.config.password || !this.config.username || !this.config.clientId) {
104
+ throw new Error('Tier0 SDK v2: provide a workspace-encoded API key or explicit MQTT username, clientId and password.');
105
+ }
106
+ this.connectingPromise = this.doConnect();
107
+ return this.connectingPromise;
108
+ }
109
+ doConnect() {
110
+ return new Promise((resolve, reject) => {
111
+ const options = {
112
+ clientId: this.config.clientId,
113
+ username: this.config.username,
114
+ password: this.config.password,
115
+ keepalive: this.config.keepAlive,
116
+ reconnectPeriod: this.config.reconnectPeriod,
117
+ connectTimeout: this.config.connectTimeout,
118
+ clean: this.config.clean,
119
+ };
120
+ this.client = mqtt_1.default.connect(this.mqttUrl, options);
121
+ this.client.on('connect', () => {
122
+ this._connected = true;
123
+ this.connectingPromise = null;
124
+ this.emit('connect');
125
+ // 断连重连后自动恢复所有订阅
126
+ this.resubscribeAll();
127
+ resolve();
128
+ });
129
+ this.client.on('error', (err) => {
130
+ this.emit('error', err);
131
+ this.connectingPromise = null;
132
+ reject(err);
133
+ });
134
+ this.client.on('disconnect', () => {
135
+ this._connected = false;
136
+ this.emit('disconnect');
137
+ });
138
+ this.client.on('close', () => {
139
+ this._connected = false;
140
+ });
141
+ this.client.on('message', (topic, payload, _packet) => {
142
+ const payloadStr = payload.toString();
143
+ // 复制一份快照:handler 触发背压自动退订时会重建 this.subscriptions
144
+ this.subscriptions.slice().forEach((sub) => {
145
+ if (this.topicMatch(sub.topic, topic)) {
146
+ try {
147
+ // 公开类型为 void 返回(兼容 async/简写 handler),运行时仅识别严格的 false
148
+ const accepted = sub.handler(topic, payloadStr);
149
+ if (accepted === false) {
150
+ sub.backpressuredCount = (sub.backpressuredCount ?? 0) + 1;
151
+ const limit = this.config.maxBackpressuredDeliveries ?? 0;
152
+ if (limit > 0 && sub.backpressuredCount >= limit) {
153
+ this.unsubscribe(sub.topic, sub.handler);
154
+ this.emit('subscriptionDropped', {
155
+ topic: sub.topic,
156
+ reason: `handler 连续 ${sub.backpressuredCount} 次返回背压(false),已按 maxBackpressuredDeliveries=${limit} 自动退订`,
157
+ });
158
+ }
159
+ }
160
+ else {
161
+ sub.backpressuredCount = 0;
162
+ }
163
+ }
164
+ catch (e) {
165
+ this.emit('error', e);
166
+ }
167
+ }
168
+ });
169
+ });
170
+ });
171
+ }
172
+ // 显式连接(需要等待连接完成时使用)
173
+ async connect(config) {
174
+ if (config) {
175
+ const previousPassword = this.config.password;
176
+ this.config = { ...this.config, ...config };
177
+ if (config.clientId !== undefined)
178
+ this.explicitClientId = true;
179
+ if (config.username !== undefined)
180
+ this.explicitUsername = true;
181
+ // A connect-time key may belong to a different workspace. Preserve only
182
+ // caller-supplied identity fields, not values derived from the previous key.
183
+ if (config.password !== undefined && config.password !== previousPassword) {
184
+ const workspaceID = parseWorkspaceIDFromApiKey(config.password);
185
+ if (!this.explicitClientId) {
186
+ this.config.clientId = workspaceID ? workspaceID + '&' + generateRandomString(8) : undefined;
187
+ }
188
+ if (!this.explicitUsername)
189
+ this.config.username = workspaceID ? workspaceID + '&open' : undefined;
190
+ }
191
+ }
192
+ return this.ensureConnected();
193
+ }
194
+ // 订阅 topic,内部 qos 固定为 1,handler 接收 (topic, payload)
195
+ // 无需先调用 connect(),自动处理连接
196
+ subscribe(topic, handler) {
197
+ // 避免重复注册同一 topic 的同一 handler
198
+ const exists = this.subscriptions.some((s) => s.topic === topic && s.handler === handler);
199
+ if (exists) {
200
+ return;
201
+ }
202
+ this.subscriptions.push({ topic, handler });
203
+ if (this._connected && this.client) {
204
+ this.client.subscribe(topic, { qos: 1 }, (err) => {
205
+ if (err) {
206
+ this.emit('error', err);
207
+ // 订阅失败时回滚
208
+ this.subscriptions = this.subscriptions.filter((s) => !(s.topic === topic && s.handler === handler));
209
+ }
210
+ });
211
+ }
212
+ else {
213
+ // 未连接时触发懒连接,连接成功后 resubscribeAll 会自动 subscribe
214
+ this.ensureConnected().catch((err) => this.emit('error', err));
215
+ }
216
+ }
217
+ // 取消订阅:如果提供了 handler 则仅移除该 handler,否则移除该 topic 下所有 handler
218
+ unsubscribe(topic, handler) {
219
+ if (handler) {
220
+ this.subscriptions = this.subscriptions.filter((s) => !(s.topic === topic && s.handler === handler));
221
+ }
222
+ else {
223
+ this.subscriptions = this.subscriptions.filter((s) => s.topic !== topic);
224
+ }
225
+ if (!this.client)
226
+ return;
227
+ // 如果该 topic 下已无任何 handler,则向 broker 发送 unsubscribe
228
+ const stillHasHandler = this.subscriptions.some((s) => s.topic === topic);
229
+ if (!stillHasHandler) {
230
+ this.client.unsubscribe(topic, (err) => {
231
+ if (err) {
232
+ this.emit('error', err);
233
+ }
234
+ });
235
+ }
236
+ }
237
+ // 发布消息,无需先调用 connect(),内部自动处理连接
238
+ async publish(topic, payload, options) {
239
+ await this.ensureConnected();
240
+ const message = typeof payload === 'string' ? payload : JSON.stringify(payload);
241
+ this.client.publish(topic, message, {
242
+ qos: (options?.qos ?? 0),
243
+ retain: options?.retain ?? false,
244
+ });
245
+ }
246
+ disconnect() {
247
+ if (!this.client)
248
+ return;
249
+ this.client.end(true, () => {
250
+ this.emit('disconnect');
251
+ });
252
+ this.client = null;
253
+ this._connected = false;
254
+ this.connectingPromise = null;
255
+ this.subscriptions = [];
256
+ }
257
+ on(event, handler) {
258
+ if (!this.listeners[event]) {
259
+ this.listeners[event] = [];
260
+ }
261
+ this.listeners[event].push(handler);
262
+ }
263
+ off(event, handler) {
264
+ const handlers = this.listeners[event];
265
+ if (!handlers)
266
+ return;
267
+ const idx = handlers.indexOf(handler);
268
+ if (idx !== -1) {
269
+ handlers.splice(idx, 1);
270
+ }
271
+ }
272
+ emit(event, ...args) {
273
+ const handlers = this.listeners[event];
274
+ if (!handlers)
275
+ return;
276
+ handlers.forEach((h) => h(...args));
277
+ }
278
+ // 断连后自动重新订阅所有已注册的 topic
279
+ resubscribeAll() {
280
+ if (!this.client)
281
+ return;
282
+ // 按 topic 去重,每个 topic 只订阅一次
283
+ const uniqueTopics = Array.from(new Set(this.subscriptions.map((s) => s.topic)));
284
+ uniqueTopics.forEach((topic) => {
285
+ this.client.subscribe(topic, { qos: 1 }, (err) => {
286
+ if (err) {
287
+ this.emit('error', err);
288
+ }
289
+ });
290
+ });
291
+ }
292
+ // 判断接收到的 topic 是否匹配订阅的 topic(支持通配符 # 和 +)
293
+ topicMatch(subscribed, received) {
294
+ const subParts = subscribed.split('/');
295
+ const recParts = received.split('/');
296
+ for (let i = 0; i < subParts.length; i++) {
297
+ const sp = subParts[i];
298
+ const rp = recParts[i];
299
+ if (sp === '#') {
300
+ // # 必须是最后一个层级,匹配后面所有
301
+ return true;
302
+ }
303
+ if (sp === '+') {
304
+ // + 匹配单个层级
305
+ continue;
306
+ }
307
+ if (sp !== rp) {
308
+ return false;
309
+ }
310
+ }
311
+ return subParts.length === recParts.length;
312
+ }
313
+ get isConnected() {
314
+ return this._connected;
315
+ }
316
+ get subscribedTopics() {
317
+ return Array.from(new Set(this.subscriptions.map((s) => s.topic)));
318
+ }
319
+ }
320
+ exports.Tier0MQClient = Tier0MQClient;
321
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/mq/client.ts"],"names":[],"mappings":"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","sourcesContent":["import mqtt from 'mqtt';\nimport type { MqttClient, IClientOptions } from 'mqtt';\nimport { getEnvVar } from '../runtime-env.js';\nimport { DEFAULT_WSS_PORT, isBrowserHttps, parseMqttBroker, toWebSocketUrl } from './broker.js';\nimport type { MQTTConfig, MQTTEventMap } from './types.js';\n\n/**\n * 消息回调。公开类型保持 `void` 返回,以兼容 async handler、`push()` 简写等\n * 既有写法;运行时额外识别返回值:handler 返回严格的 `false` 视为下游停滞\n * (背压),SDK 会统计连续背压次数,配合 MQTTConfig.maxBackpressuredDeliveries\n * 可在阈值内自动移除订阅。\n */\nexport type TopicHandler = (topic: string, payload: string) => void;\n\nfunction parseWorkspaceIDFromApiKey(apiKey: string): string | undefined {\n // Match the backend's sk-<type>-ws<base36>_<secret> contract, not only\n // service keys. Other workspace-encoded keys use the same MQTT identity.\n const match = /^sk-[^-]+-ws([0-9a-zA-Z]+)_.+$/.exec(apiKey.trim());\n if (!match) return undefined;\n\n // Keep the int64 workspace ID exact; parseInt can round IDs above 2^53.\n let workspaceID = 0n;\n for (const digit of match[1]) {\n workspaceID = workspaceID * 36n + BigInt(parseInt(digit, 36));\n if (workspaceID > 9223372036854775807n) return undefined;\n }\n return workspaceID > 0n ? workspaceID.toString() : undefined;\n}\n\nfunction generateRandomString(length = 8): string {\n const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';\n let result = '';\n for (let i = 0; i < length; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n}\n\ninterface Subscription {\n topic: string;\n handler: TopicHandler;\n /** handler 连续返回 false(背压)的次数,正常返回时清零。 */\n backpressuredCount?: number;\n}\n\nexport class Tier0MQClient {\n private client: MqttClient | null = null;\n private config: MQTTConfig;\n private explicitClientId: boolean;\n private explicitUsername: boolean;\n private listeners: { [K in keyof MQTTEventMap]?: Array<MQTTEventMap[K]> } = {};\n private subscriptions: Subscription[] = [];\n private connectingPromise: Promise<void> | null = null;\n private _connected = false;\n\n constructor(config?: MQTTConfig) {\n this.explicitClientId = config?.clientId !== undefined;\n this.explicitUsername = config?.username !== undefined;\n const envHost = getEnvVar('TIER0_MQTT_HOST');\n const envPort = getEnvVar('TIER0_MQTT_PORT');\n\n const host = config?.host || envHost || '';\n const port = config?.port || parseInt(envPort || '8084') || 8084;\n const password = config?.password || getEnvVar('TIER0_API_KEY') || '';\n\n // 从 apiKey 解析 workspaceID,自动生成 clientId 和 username\n const workspaceID = config?.clientId && config?.username\n ? undefined\n : parseWorkspaceIDFromApiKey(password);\n\n const autoClientId = workspaceID\n ? `${workspaceID}&${generateRandomString(8)}`\n : undefined;\n const autoUsername = workspaceID\n ? `${workspaceID}&open`\n : undefined;\n\n this.config = {\n host,\n port,\n clientId: config?.clientId || autoClientId,\n username: config?.username || autoUsername,\n password,\n keepAlive: config?.keepAlive ?? 60,\n reconnectPeriod: config?.reconnectPeriod ?? 5000,\n connectTimeout: config?.connectTimeout ?? 30000,\n clean: config?.clean ?? true,\n ...config,\n };\n }\n\n private get mqttUrl(): string {\n const { host, port, secure } = this.config;\n if (!host) {\n throw new Error(\n 'MQTT host is required. Provide it via MQTTConfig or TIER0_MQTT_HOST environment variable.'\n );\n }\n const normalizedHost = host.trim().replace(/\\/+$/, '');\n if (/^wss?:\\/\\//i.test(normalizedHost)) {\n const url = toWebSocketUrl(normalizedHost);\n if (!url) throw new TypeError('Invalid MQTT WebSocket URL');\n return url;\n }\n // 裸 host(可含端口):按运行环境自适应 ws/wss\n const endpoint = parseMqttBroker(normalizedHost);\n const hostname = endpoint?.hostname ?? normalizedHost;\n // 裸 host:port 和 ws(s) URL 的端口按 WebSocket 端口处理;\n // tcp/mqtt/mqtts 的端口是 TCP 端口(如 1883),wss 下不能沿用,回退到配置的 port\n const effectivePort =\n !endpoint?.scheme || endpoint.scheme === 'ws' || endpoint.scheme === 'wss'\n ? endpoint?.port ?? port\n : port;\n const useSecure = secure ?? (isBrowserHttps() || effectivePort === DEFAULT_WSS_PORT);\n return `${useSecure ? 'wss' : 'ws'}://${hostname}:${effectivePort}/mqtt`;\n }\n\n // 内部确保已连接(懒连接)\n private async ensureConnected(): Promise<void> {\n if (this._connected) return;\n if (this.connectingPromise) return this.connectingPromise;\n\n void this.mqttUrl;\n if (!this.config.password || !this.config.username || !this.config.clientId) {\n throw new Error('Tier0 SDK v2: provide a workspace-encoded API key or explicit MQTT username, clientId and password.');\n }\n\n this.connectingPromise = this.doConnect();\n return this.connectingPromise;\n }\n\n private doConnect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const options: IClientOptions = {\n clientId: this.config.clientId,\n username: this.config.username,\n password: this.config.password,\n keepalive: this.config.keepAlive,\n reconnectPeriod: this.config.reconnectPeriod,\n connectTimeout: this.config.connectTimeout,\n clean: this.config.clean,\n };\n\n this.client = mqtt.connect(this.mqttUrl, options);\n\n this.client.on('connect', () => {\n this._connected = true;\n this.connectingPromise = null;\n this.emit('connect');\n // 断连重连后自动恢复所有订阅\n this.resubscribeAll();\n resolve();\n });\n\n this.client.on('error', (err) => {\n this.emit('error', err);\n this.connectingPromise = null;\n reject(err);\n });\n\n this.client.on('disconnect', () => {\n this._connected = false;\n this.emit('disconnect');\n });\n\n this.client.on('close', () => {\n this._connected = false;\n });\n\n this.client.on(\n 'message',\n (topic: string, payload: Buffer, _packet: mqtt.IPublishPacket) => {\n const payloadStr = payload.toString();\n // 复制一份快照:handler 触发背压自动退订时会重建 this.subscriptions\n this.subscriptions.slice().forEach((sub) => {\n if (this.topicMatch(sub.topic, topic)) {\n try {\n // 公开类型为 void 返回(兼容 async/简写 handler),运行时仅识别严格的 false\n const accepted = (\n sub.handler as (t: string, p: string) => unknown\n )(topic, payloadStr);\n if (accepted === false) {\n sub.backpressuredCount = (sub.backpressuredCount ?? 0) + 1;\n const limit = this.config.maxBackpressuredDeliveries ?? 0;\n if (limit > 0 && sub.backpressuredCount >= limit) {\n this.unsubscribe(sub.topic, sub.handler);\n this.emit('subscriptionDropped', {\n topic: sub.topic,\n reason: `handler 连续 ${sub.backpressuredCount} 次返回背压(false),已按 maxBackpressuredDeliveries=${limit} 自动退订`,\n });\n }\n } else {\n sub.backpressuredCount = 0;\n }\n } catch (e) {\n this.emit('error', e as Error);\n }\n }\n });\n }\n );\n });\n }\n\n // 显式连接(需要等待连接完成时使用)\n async connect(config?: MQTTConfig): Promise<void> {\n if (config) {\n const previousPassword = this.config.password;\n this.config = { ...this.config, ...config };\n if (config.clientId !== undefined) this.explicitClientId = true;\n if (config.username !== undefined) this.explicitUsername = true;\n // A connect-time key may belong to a different workspace. Preserve only\n // caller-supplied identity fields, not values derived from the previous key.\n if (config.password !== undefined && config.password !== previousPassword) {\n const workspaceID = parseWorkspaceIDFromApiKey(config.password);\n if (!this.explicitClientId) {\n this.config.clientId = workspaceID ? workspaceID + '&' + generateRandomString(8) : undefined;\n }\n if (!this.explicitUsername) this.config.username = workspaceID ? workspaceID + '&open' : undefined;\n }\n }\n return this.ensureConnected();\n }\n\n // 订阅 topic,内部 qos 固定为 1,handler 接收 (topic, payload)\n // 无需先调用 connect(),自动处理连接\n subscribe(topic: string, handler: TopicHandler): void {\n // 避免重复注册同一 topic 的同一 handler\n const exists = this.subscriptions.some(\n (s) => s.topic === topic && s.handler === handler\n );\n if (exists) {\n return;\n }\n\n this.subscriptions.push({ topic, handler });\n\n if (this._connected && this.client) {\n this.client.subscribe(topic, { qos: 1 as 0 | 1 | 2 }, (err) => {\n if (err) {\n this.emit('error', err);\n // 订阅失败时回滚\n this.subscriptions = this.subscriptions.filter(\n (s) => !(s.topic === topic && s.handler === handler)\n );\n }\n });\n } else {\n // 未连接时触发懒连接,连接成功后 resubscribeAll 会自动 subscribe\n this.ensureConnected().catch((err) => this.emit('error', err));\n }\n }\n\n // 取消订阅:如果提供了 handler 则仅移除该 handler,否则移除该 topic 下所有 handler\n unsubscribe(topic: string, handler?: TopicHandler): void {\n if (handler) {\n this.subscriptions = this.subscriptions.filter(\n (s) => !(s.topic === topic && s.handler === handler)\n );\n } else {\n this.subscriptions = this.subscriptions.filter((s) => s.topic !== topic);\n }\n\n if (!this.client) return;\n\n // 如果该 topic 下已无任何 handler,则向 broker 发送 unsubscribe\n const stillHasHandler = this.subscriptions.some((s) => s.topic === topic);\n if (!stillHasHandler) {\n this.client.unsubscribe(topic, (err) => {\n if (err) {\n this.emit('error', err);\n }\n });\n }\n }\n\n // 发布消息,无需先调用 connect(),内部自动处理连接\n async publish(\n topic: string,\n payload: string | object,\n options?: { qos?: number; retain?: boolean }\n ): Promise<void> {\n await this.ensureConnected();\n\n const message =\n typeof payload === 'string' ? payload : JSON.stringify(payload);\n this.client!.publish(topic, message, {\n qos: (options?.qos ?? 0) as 0 | 1 | 2,\n retain: options?.retain ?? false,\n });\n }\n\n disconnect(): void {\n if (!this.client) return;\n\n this.client.end(true, () => {\n this.emit('disconnect');\n });\n this.client = null;\n this._connected = false;\n this.connectingPromise = null;\n this.subscriptions = [];\n }\n\n on<K extends keyof MQTTEventMap>(event: K, handler: MQTTEventMap[K]): void {\n if (!this.listeners[event]) {\n this.listeners[event] = [];\n }\n this.listeners[event]!.push(handler);\n }\n\n off<K extends keyof MQTTEventMap>(event: K, handler: MQTTEventMap[K]): void {\n const handlers = this.listeners[event];\n if (!handlers) return;\n const idx = handlers.indexOf(handler);\n if (idx !== -1) {\n handlers.splice(idx, 1);\n }\n }\n\n private emit<K extends keyof MQTTEventMap>(\n event: K,\n ...args: Parameters<MQTTEventMap[K]>\n ): void {\n const handlers = this.listeners[event];\n if (!handlers) return;\n handlers.forEach((h) => (h as any)(...args));\n }\n\n // 断连后自动重新订阅所有已注册的 topic\n private resubscribeAll(): void {\n if (!this.client) return;\n\n // 按 topic 去重,每个 topic 只订阅一次\n const uniqueTopics = Array.from(\n new Set(this.subscriptions.map((s) => s.topic))\n );\n uniqueTopics.forEach((topic) => {\n this.client!.subscribe(topic, { qos: 1 as 0 | 1 | 2 }, (err) => {\n if (err) {\n this.emit('error', err);\n }\n });\n });\n }\n\n // 判断接收到的 topic 是否匹配订阅的 topic(支持通配符 # 和 +)\n private topicMatch(subscribed: string, received: string): boolean {\n const subParts = subscribed.split('/');\n const recParts = received.split('/');\n\n for (let i = 0; i < subParts.length; i++) {\n const sp = subParts[i];\n const rp = recParts[i];\n\n if (sp === '#') {\n // # 必须是最后一个层级,匹配后面所有\n return true;\n }\n if (sp === '+') {\n // + 匹配单个层级\n continue;\n }\n if (sp !== rp) {\n return false;\n }\n }\n\n return subParts.length === recParts.length;\n }\n\n get isConnected(): boolean {\n return this._connected;\n }\n\n get subscribedTopics(): string[] {\n return Array.from(new Set(this.subscriptions.map((s) => s.topic)));\n }\n}\n"]}
@@ -0,0 +1,5 @@
1
+ export { Tier0MQClient } from './client.js';
2
+ export type { TopicHandler } from './client.js';
3
+ export { parseMqttBroker, toWebSocketUrl, DEFAULT_WSS_PORT, DEFAULT_WS_PATH, } from './broker.js';
4
+ export type { MqttBrokerEndpoint, ToWebSocketUrlOptions } from './broker.js';
5
+ export type { MQTTConfig, MQTTMessage, MQTTEventMap } from './types.js';
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_WS_PATH = exports.DEFAULT_WSS_PORT = exports.toWebSocketUrl = exports.parseMqttBroker = exports.Tier0MQClient = void 0;
4
+ var client_js_1 = require("./client.js");
5
+ Object.defineProperty(exports, "Tier0MQClient", { enumerable: true, get: function () { return client_js_1.Tier0MQClient; } });
6
+ var broker_js_1 = require("./broker.js");
7
+ Object.defineProperty(exports, "parseMqttBroker", { enumerable: true, get: function () { return broker_js_1.parseMqttBroker; } });
8
+ Object.defineProperty(exports, "toWebSocketUrl", { enumerable: true, get: function () { return broker_js_1.toWebSocketUrl; } });
9
+ Object.defineProperty(exports, "DEFAULT_WSS_PORT", { enumerable: true, get: function () { return broker_js_1.DEFAULT_WSS_PORT; } });
10
+ Object.defineProperty(exports, "DEFAULT_WS_PATH", { enumerable: true, get: function () { return broker_js_1.DEFAULT_WS_PATH; } });
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mq/index.ts"],"names":[],"mappings":";;;AAAA,yCAA4C;AAAnC,0GAAA,aAAa,OAAA;AAEtB,yCAKqB;AAJnB,4GAAA,eAAe,OAAA;AACf,2GAAA,cAAc,OAAA;AACd,6GAAA,gBAAgB,OAAA;AAChB,4GAAA,eAAe,OAAA","sourcesContent":["export { Tier0MQClient } from './client.js';\nexport type { TopicHandler } from './client.js';\nexport {\n parseMqttBroker,\n toWebSocketUrl,\n DEFAULT_WSS_PORT,\n DEFAULT_WS_PATH,\n} from './broker.js';\nexport type { MqttBrokerEndpoint, ToWebSocketUrlOptions } from './broker.js';\nexport type { MQTTConfig, MQTTMessage, MQTTEventMap } from './types.js';\n"]}
@@ -0,0 +1,48 @@
1
+ export interface MQTTConfig {
2
+ /**
3
+ * broker 地址。支持三种形态:
4
+ * - 完整 WebSocket URL,如 `wss://host:8084/mqtt`(推荐浏览器场景显式使用);
5
+ * - 裸 `host`(可含端口,如 `host:8084`);
6
+ * - 平台 `/openapi/v1/info` 返回的原始 `mqttBroker`(如 `tcp://host:1883`,
7
+ * 建议先用 {@link toWebSocketUrl} 归一后再传入,见 `@tier0/sdk-v2/mq` 的 broker 工具)。
8
+ */
9
+ host?: string;
10
+ /** WebSocket port fallback when host has no embedded WebSocket port. */
11
+ port?: number;
12
+ /**
13
+ * 当 host 不带 ws(s) scheme 时,显式指定用 wss(true) 还是 ws(false)。
14
+ * 缺省自适应:浏览器 https 页面或端口 8084 用 wss,其余用 ws。
15
+ */
16
+ secure?: boolean;
17
+ clientId?: string;
18
+ username?: string;
19
+ /** Original API key; connect-time changes refresh automatically derived identity fields. */
20
+ password?: string;
21
+ keepAlive?: number;
22
+ reconnectPeriod?: number;
23
+ connectTimeout?: number;
24
+ clean?: boolean;
25
+ /**
26
+ * 背压保护阈值:handler 连续返回 false(表示下游停滞/消费者已断开)达到
27
+ * 该次数后,SDK 自动移除该订阅并向 broker 退订,避免消息在无消费者的
28
+ * 应用内队列中持续堆积。0 或不设置表示关闭该保护(保持原有行为)。
29
+ */
30
+ maxBackpressuredDeliveries?: number;
31
+ }
32
+ export type MQTTMessage = {
33
+ topic: string;
34
+ payload: string | Buffer;
35
+ qos: number;
36
+ retain: boolean;
37
+ };
38
+ export type MQTTEventMap = {
39
+ connect: () => void;
40
+ disconnect: () => void;
41
+ error: (error: Error) => void;
42
+ message: (message: MQTTMessage) => void;
43
+ /** 订阅因持续背压被 SDK 自动移除时触发。 */
44
+ subscriptionDropped: (info: {
45
+ topic: string;
46
+ reason: string;
47
+ }) => void;
48
+ };
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/mq/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface MQTTConfig {\n /**\n * broker 地址。支持三种形态:\n * - 完整 WebSocket URL,如 `wss://host:8084/mqtt`(推荐浏览器场景显式使用);\n * - 裸 `host`(可含端口,如 `host:8084`);\n * - 平台 `/openapi/v1/info` 返回的原始 `mqttBroker`(如 `tcp://host:1883`,\n * 建议先用 {@link toWebSocketUrl} 归一后再传入,见 `@tier0/sdk-v2/mq` 的 broker 工具)。\n */\n host?: string;\n /** WebSocket port fallback when host has no embedded WebSocket port. */\n port?: number;\n /**\n * 当 host 不带 ws(s) scheme 时,显式指定用 wss(true) 还是 ws(false)。\n * 缺省自适应:浏览器 https 页面或端口 8084 用 wss,其余用 ws。\n */\n secure?: boolean;\n clientId?: string;\n username?: string;\n /** Original API key; connect-time changes refresh automatically derived identity fields. */\n password?: string;\n keepAlive?: number;\n reconnectPeriod?: number;\n connectTimeout?: number;\n clean?: boolean;\n /**\n * 背压保护阈值:handler 连续返回 false(表示下游停滞/消费者已断开)达到\n * 该次数后,SDK 自动移除该订阅并向 broker 退订,避免消息在无消费者的\n * 应用内队列中持续堆积。0 或不设置表示关闭该保护(保持原有行为)。\n */\n maxBackpressuredDeliveries?: number;\n}\n\nexport type MQTTMessage = {\n topic: string;\n payload: string | Buffer;\n qos: number;\n retain: boolean;\n};\n\nexport type MQTTEventMap = {\n connect: () => void;\n disconnect: () => void;\n error: (error: Error) => void;\n message: (message: MQTTMessage) => void;\n /** 订阅因持续背压被 SDK 自动移除时触发。 */\n subscriptionDropped: (info: { topic: string; reason: string }) => void;\n};\n"]}