@pingroom/sdk 0.7.1 → 0.9.0

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 (191) hide show
  1. package/README.md +288 -44
  2. package/dist/client.d.ts +27 -312
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +33 -1154
  5. package/dist/client.js.map +1 -1
  6. package/dist/errors.d.ts +40 -6
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/errors.js +56 -4
  9. package/dist/errors.js.map +1 -1
  10. package/dist/http.d.ts +12 -1
  11. package/dist/http.d.ts.map +1 -1
  12. package/dist/http.js +47 -18
  13. package/dist/http.js.map +1 -1
  14. package/dist/index.d.ts +3 -5
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/internal/async.d.ts +2 -0
  19. package/dist/internal/async.d.ts.map +1 -1
  20. package/dist/internal/async.js +6 -0
  21. package/dist/internal/async.js.map +1 -1
  22. package/dist/internal/guards.d.ts +16 -0
  23. package/dist/internal/guards.d.ts.map +1 -1
  24. package/dist/internal/guards.js +22 -1
  25. package/dist/internal/guards.js.map +1 -1
  26. package/dist/internal/request.d.ts +41 -0
  27. package/dist/internal/request.d.ts.map +1 -0
  28. package/dist/internal/request.js +78 -0
  29. package/dist/internal/request.js.map +1 -0
  30. package/dist/internal/retry.d.ts +65 -0
  31. package/dist/internal/retry.d.ts.map +1 -0
  32. package/dist/internal/retry.js +126 -0
  33. package/dist/internal/retry.js.map +1 -0
  34. package/dist/internal/url.d.ts +9 -0
  35. package/dist/internal/url.d.ts.map +1 -1
  36. package/dist/internal/url.js +25 -0
  37. package/dist/internal/url.js.map +1 -1
  38. package/dist/internal/validate.d.ts +13 -0
  39. package/dist/internal/validate.d.ts.map +1 -0
  40. package/dist/internal/validate.js +36 -0
  41. package/dist/internal/validate.js.map +1 -0
  42. package/dist/linkPing.d.ts.map +1 -1
  43. package/dist/linkPing.js +6 -2
  44. package/dist/linkPing.js.map +1 -1
  45. package/dist/liveStatus.d.ts +106 -11
  46. package/dist/liveStatus.d.ts.map +1 -1
  47. package/dist/liveStatus.js +20 -3
  48. package/dist/liveStatus.js.map +1 -1
  49. package/dist/mcp.d.ts.map +1 -1
  50. package/dist/mcp.js +40 -10
  51. package/dist/mcp.js.map +1 -1
  52. package/dist/namespaces/actions.d.ts +50 -0
  53. package/dist/namespaces/actions.d.ts.map +1 -0
  54. package/dist/namespaces/actions.js +219 -0
  55. package/dist/namespaces/actions.js.map +1 -0
  56. package/dist/namespaces/approvals.d.ts +23 -0
  57. package/dist/namespaces/approvals.d.ts.map +1 -0
  58. package/dist/namespaces/approvals.js +78 -0
  59. package/dist/namespaces/approvals.js.map +1 -0
  60. package/dist/namespaces/attachments.d.ts +37 -0
  61. package/dist/namespaces/attachments.d.ts.map +1 -0
  62. package/dist/namespaces/attachments.js +91 -0
  63. package/dist/namespaces/attachments.js.map +1 -0
  64. package/dist/namespaces/auth.d.ts +58 -0
  65. package/dist/namespaces/auth.d.ts.map +1 -0
  66. package/dist/namespaces/auth.js +242 -0
  67. package/dist/namespaces/auth.js.map +1 -0
  68. package/dist/namespaces/directory.d.ts +10 -0
  69. package/dist/namespaces/directory.d.ts.map +1 -0
  70. package/dist/namespaces/directory.js +19 -0
  71. package/dist/namespaces/directory.js.map +1 -0
  72. package/dist/namespaces/handoffs.d.ts +47 -0
  73. package/dist/namespaces/handoffs.d.ts.map +1 -0
  74. package/dist/namespaces/handoffs.js +126 -0
  75. package/dist/namespaces/handoffs.js.map +1 -0
  76. package/dist/namespaces/inbox.d.ts +17 -0
  77. package/dist/namespaces/inbox.d.ts.map +1 -0
  78. package/dist/namespaces/inbox.js +192 -0
  79. package/dist/namespaces/inbox.js.map +1 -0
  80. package/dist/namespaces/live.d.ts +57 -0
  81. package/dist/namespaces/live.d.ts.map +1 -0
  82. package/dist/namespaces/live.js +116 -0
  83. package/dist/namespaces/live.js.map +1 -0
  84. package/dist/namespaces/notifications.d.ts +25 -0
  85. package/dist/namespaces/notifications.d.ts.map +1 -0
  86. package/dist/namespaces/notifications.js +128 -0
  87. package/dist/namespaces/notifications.js.map +1 -0
  88. package/dist/namespaces/profile.d.ts +19 -0
  89. package/dist/namespaces/profile.d.ts.map +1 -0
  90. package/dist/namespaces/profile.js +22 -0
  91. package/dist/namespaces/profile.js.map +1 -0
  92. package/dist/namespaces/questions.d.ts +41 -0
  93. package/dist/namespaces/questions.d.ts.map +1 -0
  94. package/dist/namespaces/questions.js +107 -0
  95. package/dist/namespaces/questions.js.map +1 -0
  96. package/dist/namespaces/rooms.d.ts +13 -0
  97. package/dist/namespaces/rooms.d.ts.map +1 -0
  98. package/dist/namespaces/rooms.js +41 -0
  99. package/dist/namespaces/rooms.js.map +1 -0
  100. package/dist/namespaces/webhooks.d.ts +11 -0
  101. package/dist/namespaces/webhooks.d.ts.map +1 -0
  102. package/dist/namespaces/webhooks.js +32 -0
  103. package/dist/namespaces/webhooks.js.map +1 -0
  104. package/dist/types/approvals.d.ts +62 -0
  105. package/dist/types/approvals.d.ts.map +1 -0
  106. package/dist/types/approvals.js +2 -0
  107. package/dist/types/approvals.js.map +1 -0
  108. package/dist/types/auth.d.ts +185 -0
  109. package/dist/types/auth.d.ts.map +1 -0
  110. package/dist/types/auth.js +2 -0
  111. package/dist/types/auth.js.map +1 -0
  112. package/dist/types/common.d.ts +43 -0
  113. package/dist/types/common.d.ts.map +1 -0
  114. package/dist/types/common.js +2 -0
  115. package/dist/types/common.js.map +1 -0
  116. package/dist/types/handoffs.d.ts +121 -0
  117. package/dist/types/handoffs.d.ts.map +1 -0
  118. package/dist/types/handoffs.js +2 -0
  119. package/dist/types/handoffs.js.map +1 -0
  120. package/dist/types/inbox.d.ts +51 -0
  121. package/dist/types/inbox.d.ts.map +1 -0
  122. package/dist/types/inbox.js +2 -0
  123. package/dist/types/inbox.js.map +1 -0
  124. package/dist/types/notifications.d.ts +376 -0
  125. package/dist/types/notifications.d.ts.map +1 -0
  126. package/dist/types/notifications.js +2 -0
  127. package/dist/types/notifications.js.map +1 -0
  128. package/dist/types/profile.d.ts +30 -0
  129. package/dist/types/profile.d.ts.map +1 -0
  130. package/dist/types/profile.js +3 -0
  131. package/dist/types/profile.js.map +1 -0
  132. package/dist/types/questions.d.ts +210 -0
  133. package/dist/types/questions.d.ts.map +1 -0
  134. package/dist/types/questions.js +2 -0
  135. package/dist/types/questions.js.map +1 -0
  136. package/dist/types/rooms.d.ts +163 -0
  137. package/dist/types/rooms.d.ts.map +1 -0
  138. package/dist/types/rooms.js +3 -0
  139. package/dist/types/rooms.js.map +1 -0
  140. package/dist/types.d.ts +9 -1035
  141. package/dist/types.d.ts.map +1 -1
  142. package/dist/version.d.ts +1 -1
  143. package/dist/version.js +1 -1
  144. package/dist/webhooks.d.ts +32 -5
  145. package/dist/webhooks.d.ts.map +1 -1
  146. package/dist/webhooks.js +44 -14
  147. package/dist/webhooks.js.map +1 -1
  148. package/package.json +4 -2
  149. package/src/client.ts +115 -0
  150. package/src/errors.ts +291 -0
  151. package/src/http.ts +225 -0
  152. package/src/index.ts +97 -0
  153. package/src/internal/async.ts +54 -0
  154. package/src/internal/guards.ts +129 -0
  155. package/src/internal/request.ts +97 -0
  156. package/src/internal/retry.ts +157 -0
  157. package/src/internal/url.ts +78 -0
  158. package/src/internal/validate.ts +42 -0
  159. package/src/linkPing.ts +56 -0
  160. package/src/liveStatus.ts +337 -0
  161. package/src/locationPing.ts +101 -0
  162. package/src/mcp.ts +161 -0
  163. package/src/namespaces/actions.ts +234 -0
  164. package/src/namespaces/approvals.ts +104 -0
  165. package/src/namespaces/attachments.ts +106 -0
  166. package/src/namespaces/auth.ts +284 -0
  167. package/src/namespaces/directory.ts +21 -0
  168. package/src/namespaces/handoffs.ts +160 -0
  169. package/src/namespaces/inbox.ts +253 -0
  170. package/src/namespaces/live.ts +134 -0
  171. package/src/namespaces/notifications.ts +152 -0
  172. package/src/namespaces/profile.ts +24 -0
  173. package/src/namespaces/questions.ts +133 -0
  174. package/src/namespaces/rooms.ts +53 -0
  175. package/src/namespaces/webhooks.ts +38 -0
  176. package/src/types/approvals.ts +67 -0
  177. package/src/types/auth.ts +202 -0
  178. package/src/types/common.ts +74 -0
  179. package/src/types/handoffs.ts +148 -0
  180. package/src/types/inbox.ts +64 -0
  181. package/src/types/notifications.ts +393 -0
  182. package/src/types/profile.ts +37 -0
  183. package/src/types/questions.ts +226 -0
  184. package/src/types/rooms.ts +169 -0
  185. package/src/types.ts +17 -0
  186. package/src/version.ts +2 -0
  187. package/src/webhooks.ts +373 -0
  188. package/dist/liveActivity.d.ts +0 -101
  189. package/dist/liveActivity.d.ts.map +0 -1
  190. package/dist/liveActivity.js +0 -83
  191. package/dist/liveActivity.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"async.js","sourceRoot":"","sources":["../../src/internal/async.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,UAAU,cAAc,CAAC,OAAoB,EAAE,KAAmB;IACtE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,KAAK,GAAI,WAA4E,CAAC,GAAG,CAAC;IAChG,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,CAAC,CAAc,EAAQ,EAAE;QACvC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAC/B,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,CAAC,CAAC,CAAC,CAAC;YACX,MAAM;QACR,CAAC;QACD,CAAC,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,UAAU,CAAC,MAAM,CAAC;AAC3B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB;IACpD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACpB,OAAO,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC,CAAC;QACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"async.js","sourceRoot":"","sources":["../../src/internal/async.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,UAAU,cAAc,CAAC,OAAoB,EAAE,KAAmB;IACtE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,MAAM,KAAK,GAAI,WAA4E,CAAC,GAAG,CAAC;IAChG,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,OAAO,GAAG,CAAC,CAAc,EAAQ,EAAE;QACvC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAC/B,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;QACjC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,CAAC,CAAC,CAAC,CAAC;YACX,MAAM;QACR,CAAC;QACD,CAAC,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,UAAU,CAAC,MAAM,CAAC;AAC3B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,KAAK,CAAC,EAAU,EAAE,MAAoB;IACpD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACnC,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;YACpB,OAAO,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QACD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;QACF,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC,CAAC;QACF,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,MAAoB;IACjD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,CAAC,MAAM,IAAI,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC"}
@@ -5,8 +5,24 @@ export declare const MAX_PRIVATE_PING_MESSAGE_LENGTH = 120;
5
5
  export declare const MAX_PUBLIC_PING_MESSAGE_LENGTH = 160;
6
6
  export declare const MAX_PING_TITLE_LENGTH = 40;
7
7
  export declare function assertMaxLength(value: unknown, max: number, field: string): void;
8
+ /**
9
+ * Bytes a JSON string takes as PHP's default json_encode() writes it, which is
10
+ * what the server's 8 KB cap measures: every non-ASCII UTF-16 unit becomes a
11
+ * six-byte `\uXXXX` escape and `/` becomes `\/`. Non-Latin text is several
12
+ * times larger there than its UTF-8 size.
13
+ */
14
+ export declare function serverJsonByteLength(json: string): number;
8
15
  export declare function assertStructuredData(data: unknown): void;
9
16
  export declare function assertActionNumber(n: unknown): void;
17
+ /**
18
+ * Normalize an idempotency key the way the server reads it
19
+ * (`RequestIdempotency::keyFrom`): trimmed, then 1–255 characters. It travels
20
+ * as the `Idempotency-Key` header, so it must also be printable ASCII (space
21
+ * included): fetch throws on a character outside Latin-1, which surfaced as a
22
+ * retryable `network_error` that every retry repeated, and a Latin-1 byte above
23
+ * 0x7E reaches the server as invalid UTF-8.
24
+ */
25
+ export declare function normalizeIdempotencyKey(value: unknown, field?: string): string;
10
26
  export declare function requireNonEmpty(value: unknown, field: string): void;
11
27
  /** Validate before uppercasing so Unicode case expansion cannot become a code. */
12
28
  export declare function normalizeRedeemCode(value: unknown): string;
@@ -1 +1 @@
1
- {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../src/internal/guards.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,eAAO,MAAM,cAAc,QAAW,CAAC;AACvC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,+BAA+B,MAAM,CAAC;AACnD,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAClD,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAOxC,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAchF;AAmBD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAqBxD;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAOnD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAInE;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAK1D;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIxE"}
1
+ {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../src/internal/guards.ts"],"names":[],"mappings":"AAEA,8FAA8F;AAC9F,eAAO,MAAM,cAAc,QAAW,CAAC;AACvC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,+BAA+B,MAAM,CAAC;AACnD,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAClD,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAOxC,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAchF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASzD;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAqBxD;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAOnD;AAMD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAmB,GAAG,MAAM,CAYxF;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAInE;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAK1D;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIxE"}
@@ -27,7 +27,7 @@ export function assertMaxLength(value, max, field) {
27
27
  * six-byte `\uXXXX` escape and `/` becomes `\/`. Non-Latin text is several
28
28
  * times larger there than its UTF-8 size.
29
29
  */
30
- function serverJsonByteLength(json) {
30
+ export function serverJsonByteLength(json) {
31
31
  let bytes = 0;
32
32
  for (let index = 0; index < json.length; index += 1) {
33
33
  const code = json.charCodeAt(index);
@@ -71,6 +71,27 @@ export function assertActionNumber(n) {
71
71
  throw new PingRoomError('action_number must be an integer 1–16.', { code: 'invalid_request' });
72
72
  }
73
73
  }
74
+ const MAX_IDEMPOTENCY_KEY_LENGTH = 255;
75
+ /** PHP trim()'s default set: what the server strips from both ends of a key. */
76
+ const SERVER_TRIM = /^[ \t\n\r\0\x0B]+|[ \t\n\r\0\x0B]+$/g;
77
+ /**
78
+ * Normalize an idempotency key the way the server reads it
79
+ * (`RequestIdempotency::keyFrom`): trimmed, then 1–255 characters. It travels
80
+ * as the `Idempotency-Key` header, so it must also be printable ASCII (space
81
+ * included): fetch throws on a character outside Latin-1, which surfaced as a
82
+ * retryable `network_error` that every retry repeated, and a Latin-1 byte above
83
+ * 0x7E reaches the server as invalid UTF-8.
84
+ */
85
+ export function normalizeIdempotencyKey(value, field = 'idempotencyKey') {
86
+ if (typeof value !== 'string') {
87
+ throw new PingRoomError(`\`${field}\` must be a string.`, { code: 'invalid_request' });
88
+ }
89
+ const key = value.replace(SERVER_TRIM, '');
90
+ if (key.length < 1 || key.length > MAX_IDEMPOTENCY_KEY_LENGTH || !/^[\x20-\x7E]+$/.test(key)) {
91
+ throw new PingRoomError(`\`${field}\` must be 1–${MAX_IDEMPOTENCY_KEY_LENGTH} printable ASCII characters (letters, digits, punctuation or spaces).`, { code: 'invalid_request' });
92
+ }
93
+ return key;
94
+ }
74
95
  export function requireNonEmpty(value, field) {
75
96
  if (typeof value !== 'string' || value.trim() === '') {
76
97
  throw new PingRoomError(`\`${field}\` is required.`, { code: 'invalid_request' });
@@ -1 +1 @@
1
- {"version":3,"file":"guards.js","sourceRoot":"","sources":["../../src/internal/guards.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guards.js","sourceRoot":"","sources":["../../src/internal/guards.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import type { PingInput } from '../types.js';
2
+ /**
3
+ * Minimum spacing between the starts of two handoff-wait polls. The route is
4
+ * throttled at 30 requests a minute per human; a short or zero `timeout`
5
+ * would otherwise let one loop exhaust it.
6
+ */
7
+ export declare const MIN_HANDOFF_POLL_INTERVAL_MS = 2100;
8
+ /**
9
+ * Minimum spacing between the starts of two `listen()` polls after an empty
10
+ * answer. `notifications/wait` is throttled at 30 requests a minute per human,
11
+ * shared with every other listener on the account (the CLI, other agents); a
12
+ * zero hold or a proxy that answers early would otherwise spin the loop and
13
+ * keep that bucket exhausted. Same floor as the handoff wait.
14
+ */
15
+ export declare const MIN_LISTEN_POLL_INTERVAL_MS = 2100;
16
+ /**
17
+ * Minimum spacing between the starts of two approval or question wait polls.
18
+ * The server normally holds each one, but a proxy or a zero hold that answers
19
+ * `pending` at once would otherwise spin the loop and keep that route's
20
+ * 30-per-minute bucket for the human exhausted. Same floor as the handoff wait and the
21
+ * CLI (2100 ms keeps one loop under 30 a minute).
22
+ */
23
+ export declare const MIN_WAIT_POLL_INTERVAL_MS = 2100;
24
+ export declare function enc(segment: string): string;
25
+ /**
26
+ * Lift the SDK-only `idempotencyKey` out of an input and into the
27
+ * `Idempotency-Key` header the server reads. The server trims the key and
28
+ * accepts 1–255 characters; as a header it must also be printable ASCII.
29
+ */
30
+ export declare function splitIdempotencyKey<T extends {
31
+ idempotencyKey?: string;
32
+ }>(input: T): {
33
+ body: Omit<T, 'idempotencyKey'>;
34
+ headers?: Record<string, string>;
35
+ };
36
+ /** `onRetry`, when given, must be callable: a TypeError mid-outage would hide the real failure. */
37
+ export declare function assertOnRetry(onRetry: unknown): void;
38
+ export declare function dropUndefined<T extends Record<string, unknown>>(obj: T): Partial<T>;
39
+ export declare function assertPing(ping: PingInput): void;
40
+ export declare function waitTimeoutMs(seconds: number | undefined): number;
41
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../src/internal/request.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAM7C;;;;GAIG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,OAA+B,CAAC;AACxE;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,OAA+B,CAAC;AAEtE,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ3C;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,EACvE,KAAK,EAAE,CAAC,GACP;IAAE,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAMvE;AAED,mGAAmG;AACnG,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAIpD;AAED,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAQnF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAQhD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAEjE"}
@@ -0,0 +1,78 @@
1
+ // Request building shared by the PingRoom namespaces: path segments, bodies,
2
+ // the Idempotency-Key header, ping input checks and long-poll timing.
3
+ import { PingRoomError } from '../errors.js';
4
+ import { assertActionNumber, assertMaxLength, assertStructuredData, MAX_PING_TITLE_LENGTH, MAX_PUBLIC_PING_MESSAGE_LENGTH, normalizeIdempotencyKey, requireNonEmpty, } from './guards.js';
5
+ /** Extra wall-clock allowance over the server's hold window for long-poll calls. */
6
+ const LONG_POLL_BUFFER_MS = 10_000;
7
+ const DEFAULT_WAIT_SECONDS = 20;
8
+ /**
9
+ * Minimum spacing between the starts of two handoff-wait polls. The route is
10
+ * throttled at 30 requests a minute per human; a short or zero `timeout`
11
+ * would otherwise let one loop exhaust it.
12
+ */
13
+ export const MIN_HANDOFF_POLL_INTERVAL_MS = 2100;
14
+ /**
15
+ * Minimum spacing between the starts of two `listen()` polls after an empty
16
+ * answer. `notifications/wait` is throttled at 30 requests a minute per human,
17
+ * shared with every other listener on the account (the CLI, other agents); a
18
+ * zero hold or a proxy that answers early would otherwise spin the loop and
19
+ * keep that bucket exhausted. Same floor as the handoff wait.
20
+ */
21
+ export const MIN_LISTEN_POLL_INTERVAL_MS = MIN_HANDOFF_POLL_INTERVAL_MS;
22
+ /**
23
+ * Minimum spacing between the starts of two approval or question wait polls.
24
+ * The server normally holds each one, but a proxy or a zero hold that answers
25
+ * `pending` at once would otherwise spin the loop and keep that route's
26
+ * 30-per-minute bucket for the human exhausted. Same floor as the handoff wait and the
27
+ * CLI (2100 ms keeps one loop under 30 a minute).
28
+ */
29
+ export const MIN_WAIT_POLL_INTERVAL_MS = MIN_HANDOFF_POLL_INTERVAL_MS;
30
+ export function enc(segment) {
31
+ // encodeURIComponent leaves `.` and `..` alone, and URL parsing then
32
+ // resolves them as dot segments: `live.get('AB12', '..')` would request the
33
+ // room itself. No id is empty or a dot segment, so refuse them.
34
+ if (segment === '' || segment === '.' || segment === '..') {
35
+ throw new PingRoomError('A path parameter may not be empty, `.` or `..`.', { code: 'invalid_request' });
36
+ }
37
+ return encodeURIComponent(segment);
38
+ }
39
+ /**
40
+ * Lift the SDK-only `idempotencyKey` out of an input and into the
41
+ * `Idempotency-Key` header the server reads. The server trims the key and
42
+ * accepts 1–255 characters; as a header it must also be printable ASCII.
43
+ */
44
+ export function splitIdempotencyKey(input) {
45
+ const { idempotencyKey, ...body } = input;
46
+ if (idempotencyKey === undefined) {
47
+ return { body };
48
+ }
49
+ return { body, headers: { 'Idempotency-Key': normalizeIdempotencyKey(idempotencyKey) } };
50
+ }
51
+ /** `onRetry`, when given, must be callable: a TypeError mid-outage would hide the real failure. */
52
+ export function assertOnRetry(onRetry) {
53
+ if (onRetry !== undefined && typeof onRetry !== 'function') {
54
+ throw new PingRoomError('`onRetry` must be a function.', { code: 'invalid_request' });
55
+ }
56
+ }
57
+ export function dropUndefined(obj) {
58
+ const out = {};
59
+ for (const [k, v] of Object.entries(obj)) {
60
+ if (v !== undefined) {
61
+ out[k] = v;
62
+ }
63
+ }
64
+ return out;
65
+ }
66
+ export function assertPing(ping) {
67
+ requireNonEmpty(ping.message, 'message');
68
+ // The invite code does not expose room visibility. Permit the public-room
69
+ // ceiling locally; Laravel applies 120 for private and 160 for public rooms.
70
+ assertMaxLength(ping.message, MAX_PUBLIC_PING_MESSAGE_LENGTH, 'message');
71
+ assertMaxLength(ping.title, MAX_PING_TITLE_LENGTH, 'title');
72
+ assertActionNumber(ping.action_number);
73
+ assertStructuredData(ping.data);
74
+ }
75
+ export function waitTimeoutMs(seconds) {
76
+ return (seconds ?? DEFAULT_WAIT_SECONDS) * 1000 + LONG_POLL_BUFFER_MS;
77
+ }
78
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../../src/internal/request.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,sEAAsE;AAEtE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,qBAAqB,EACrB,8BAA8B,EAC9B,uBAAuB,EACvB,eAAe,GAChB,MAAM,aAAa,CAAC;AAGrB,oFAAoF;AACpF,MAAM,mBAAmB,GAAG,MAAM,CAAC;AACnC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC;;;;GAIG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AACjD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,4BAA4B,CAAC;AACxE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,4BAA4B,CAAC;AAEtE,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,qEAAqE;IACrE,4EAA4E;IAC5E,gEAAgE;IAChE,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QAC1D,MAAM,IAAI,aAAa,CAAC,iDAAiD,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC1G,CAAC;IACD,OAAO,kBAAkB,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAQ;IAER,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,EAAE,GAAG,KAAK,CAAC;IAC1C,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,CAAC;IAClB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,iBAAiB,EAAE,uBAAuB,CAAC,cAAc,CAAC,EAAE,EAAE,CAAC;AAC3F,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QAC3D,MAAM,IAAI,aAAa,CAAC,+BAA+B,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;IACxF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAoC,GAAM;IACrE,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACpB,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,GAAiB,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAe;IACxC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACzC,0EAA0E;IAC1E,6EAA6E;IAC7E,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,8BAA8B,EAAE,SAAS,CAAC,CAAC;IACzE,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,qBAAqB,EAAE,OAAO,CAAC,CAAC;IAC5D,kBAAkB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACvC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAA2B;IACvD,OAAO,CAAC,OAAO,IAAI,oBAAoB,CAAC,GAAG,IAAI,GAAG,mBAAmB,CAAC;AACxE,CAAC"}
@@ -0,0 +1,65 @@
1
+ import { PingRoomError } from '../errors.js';
2
+ /** First retry delay after a transient failure, in ms. */
3
+ export declare const TRANSIENT_BACKOFF_BASE_MS = 1000;
4
+ /**
5
+ * The smallest backoff base, whatever a caller asks for. A persistent failure
6
+ * would otherwise retry in a tight loop (a base of 0 never grows at all)
7
+ * against wait routes limited to 30 requests a minute each.
8
+ */
9
+ export declare const MIN_TRANSIENT_BACKOFF_MS = 250;
10
+ /** Ceiling on the exponential backoff (a server Retry-After may ask for longer). */
11
+ export declare const TRANSIENT_BACKOFF_CAP_MS = 30000;
12
+ /**
13
+ * How long past the last-seen `expires_at` a wait keeps retrying an
14
+ * unreachable API. The server reports `expired` once expires_at passes, so
15
+ * after this the only outcome left is one that cannot be read. Covers clock
16
+ * skew. Same bound as the CLI.
17
+ */
18
+ export declare const WAIT_EXPIRY_GRACE_MS: number;
19
+ /** The bound on an unbroken outage while no response has carried `expires_at`. */
20
+ export declare const WAIT_UNKNOWN_EXPIRY_OUTAGE_MS: number;
21
+ /**
22
+ * A failure a read loop may retry: a dropped connection, a request deadline,
23
+ * a 429 (each wait route has its own 30/min bucket per human), or a 5xx.
24
+ * Everything else — 401/403/404/422, a refused redirect, a malformed response —
25
+ * is permanent and must surface to the caller.
26
+ */
27
+ export declare function isTransientError(error: unknown): error is PingRoomError;
28
+ /**
29
+ * Delay before retrying after the `failures`-th consecutive transient failure:
30
+ * exponential from `baseMs`, capped at 30 s. A server `retryAfter` replaces the
31
+ * exponential step, but is never waited for less than `baseMs`. A `baseMs`
32
+ * below {@link MIN_TRANSIENT_BACKOFF_MS} is raised to it.
33
+ */
34
+ export declare function transientDelayMs(error: PingRoomError, failures: number, baseMs?: number): number;
35
+ export interface PollUntilSettledOptions<T> {
36
+ /** One bounded long-poll. Its errors are retried only when transient. */
37
+ poll: () => Promise<unknown>;
38
+ /** Check the response's minimal shape; throw `invalid_response` otherwise. */
39
+ validate: (value: unknown) => T;
40
+ /** True once the validated value is terminal. */
41
+ settled: (value: T) => boolean;
42
+ signal?: AbortSignal;
43
+ /** Minimum time between the starts of two polls, in ms. Default 0. */
44
+ minIntervalMs?: number;
45
+ /** Read `expires_at` (an ISO time) from a validated response; it bounds an outage. */
46
+ expiresAt?: (value: T) => unknown;
47
+ /** Names what is waited on in the give-up message, e.g. `approval a1`. */
48
+ what: string;
49
+ /** Called before each retry of a transient failure; `attempt` counts consecutive failures from 1. */
50
+ onRetry?: (error: PingRoomError, attempt: number) => void;
51
+ }
52
+ /**
53
+ * The shared human-in-the-loop wait loop. Polls until a validated response is
54
+ * terminal; retries transient failures with backoff; rejects with the
55
+ * signal's reason once it aborts. A response that fails validation is never
56
+ * returned as an outcome.
57
+ *
58
+ * The retries are bounded even without a signal. Once only failures have come
59
+ * back since the last valid response, and the last-seen `expires_at` plus
60
+ * {@link WAIT_EXPIRY_GRACE_MS} has passed (or, before any `expires_at` was
61
+ * seen, after {@link WAIT_UNKNOWN_EXPIRY_OUTAGE_MS} of unbroken failures), the
62
+ * wait rejects with `unavailable`, the last failure as its `cause`.
63
+ */
64
+ export declare function pollUntilSettled<T>(options: PollUntilSettledOptions<T>): Promise<T>;
65
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/internal/retry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG7C,0DAA0D;AAC1D,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAG/C;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,QAAa,CAAC;AAC/C,kFAAkF;AAClF,eAAO,MAAM,6BAA6B,QAAc,CAAC;AAEzD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAQvE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAkC,GACzC,MAAM,CAUR;AAED,MAAM,WAAW,uBAAuB,CAAC,CAAC;IACxC,yEAAyE;IACzE,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC,CAAC;IAChC,iDAAiD;IACjD,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sFAAsF;IACtF,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;IAClC,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC;IACb,qGAAqG;IACrG,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAeD;;;;;;;;;;;GAWG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAkDzF"}
@@ -0,0 +1,126 @@
1
+ import { PingRoomError } from '../errors.js';
2
+ import { sleep, throwIfAborted } from './async.js';
3
+ /** First retry delay after a transient failure, in ms. */
4
+ export const TRANSIENT_BACKOFF_BASE_MS = 1000;
5
+ /**
6
+ * The smallest backoff base, whatever a caller asks for. A persistent failure
7
+ * would otherwise retry in a tight loop (a base of 0 never grows at all)
8
+ * against wait routes limited to 30 requests a minute each.
9
+ */
10
+ export const MIN_TRANSIENT_BACKOFF_MS = 250;
11
+ /** Ceiling on the exponential backoff (a server Retry-After may ask for longer). */
12
+ export const TRANSIENT_BACKOFF_CAP_MS = 30_000;
13
+ /** setTimeout fires immediately above this, so no delay may exceed it. */
14
+ const MAX_TIMER_MS = 2_147_483_647;
15
+ /**
16
+ * How long past the last-seen `expires_at` a wait keeps retrying an
17
+ * unreachable API. The server reports `expired` once expires_at passes, so
18
+ * after this the only outcome left is one that cannot be read. Covers clock
19
+ * skew. Same bound as the CLI.
20
+ */
21
+ export const WAIT_EXPIRY_GRACE_MS = 5 * 60_000;
22
+ /** The bound on an unbroken outage while no response has carried `expires_at`. */
23
+ export const WAIT_UNKNOWN_EXPIRY_OUTAGE_MS = 15 * 60_000;
24
+ /**
25
+ * A failure a read loop may retry: a dropped connection, a request deadline,
26
+ * a 429 (each wait route has its own 30/min bucket per human), or a 5xx.
27
+ * Everything else — 401/403/404/422, a refused redirect, a malformed response —
28
+ * is permanent and must surface to the caller.
29
+ */
30
+ export function isTransientError(error) {
31
+ return (error instanceof PingRoomError
32
+ && (error.code === 'network_error'
33
+ || error.code === 'timeout'
34
+ || error.status === 429
35
+ || error.status >= 500));
36
+ }
37
+ /**
38
+ * Delay before retrying after the `failures`-th consecutive transient failure:
39
+ * exponential from `baseMs`, capped at 30 s. A server `retryAfter` replaces the
40
+ * exponential step, but is never waited for less than `baseMs`. A `baseMs`
41
+ * below {@link MIN_TRANSIENT_BACKOFF_MS} is raised to it.
42
+ */
43
+ export function transientDelayMs(error, failures, baseMs = TRANSIENT_BACKOFF_BASE_MS) {
44
+ const requested = Number.isFinite(baseMs) && baseMs >= 0 ? baseMs : TRANSIENT_BACKOFF_BASE_MS;
45
+ const base = Math.max(MIN_TRANSIENT_BACKOFF_MS, requested);
46
+ const retryAfter = error.retryAfter;
47
+ if (typeof retryAfter === 'number' && Number.isFinite(retryAfter) && retryAfter >= 0) {
48
+ return Math.min(MAX_TIMER_MS, Math.max(retryAfter * 1000, base));
49
+ }
50
+ // Enough doublings to pass the cap from any base; also keeps 2 ** step finite.
51
+ const step = Math.min(Math.max(0, failures - 1), 30);
52
+ return Math.min(MAX_TIMER_MS, Math.max(base, TRANSIENT_BACKOFF_CAP_MS), base * 2 ** step);
53
+ }
54
+ function timeOf(value) {
55
+ const at = typeof value === 'string' ? Date.parse(value) : NaN;
56
+ return Number.isFinite(at) ? at : null;
57
+ }
58
+ function unavailable(last, since, what) {
59
+ const reason = last.status > 0 ? `HTTP ${last.status}` : (last.code ?? 'network error');
60
+ return new PingRoomError(`PingRoom unavailable since ${new Date(since).toISOString()} (${reason}); stopped waiting for ${what}.`, { code: 'unavailable', cause: last });
61
+ }
62
+ /**
63
+ * The shared human-in-the-loop wait loop. Polls until a validated response is
64
+ * terminal; retries transient failures with backoff; rejects with the
65
+ * signal's reason once it aborts. A response that fails validation is never
66
+ * returned as an outcome.
67
+ *
68
+ * The retries are bounded even without a signal. Once only failures have come
69
+ * back since the last valid response, and the last-seen `expires_at` plus
70
+ * {@link WAIT_EXPIRY_GRACE_MS} has passed (or, before any `expires_at` was
71
+ * seen, after {@link WAIT_UNKNOWN_EXPIRY_OUTAGE_MS} of unbroken failures), the
72
+ * wait rejects with `unavailable`, the last failure as its `cause`.
73
+ */
74
+ export async function pollUntilSettled(options) {
75
+ const { poll, validate, settled, signal, minIntervalMs = 0, expiresAt, what, onRetry } = options;
76
+ let failures = 0;
77
+ let failingSince = null;
78
+ // expires_at plus grace, from the last valid response that carried one.
79
+ let deadline = null;
80
+ for (;;) {
81
+ throwIfAborted(signal);
82
+ const startedAt = Date.now();
83
+ let value;
84
+ try {
85
+ value = validate(await poll());
86
+ }
87
+ catch (error) {
88
+ throwIfAborted(signal);
89
+ if (!isTransientError(error)) {
90
+ throw error;
91
+ }
92
+ failures += 1;
93
+ failingSince ??= Date.now();
94
+ const remaining = (deadline ?? failingSince + WAIT_UNKNOWN_EXPIRY_OUTAGE_MS) - Date.now();
95
+ if (remaining <= 0) {
96
+ throw unavailable(error, failingSince, what);
97
+ }
98
+ onRetry?.(error, failures);
99
+ // A fast failure still consumes a poll. Keep retries spaced even when a
100
+ // pending response between failures resets the exponential backoff.
101
+ const delay = Math.max(transientDelayMs(error, failures), minIntervalMs - (Date.now() - startedAt));
102
+ // Do not retry earlier than the interval or Retry-After just to fit the
103
+ // outage bound. Wait out the budget, then stop without another request.
104
+ if (delay > remaining) {
105
+ await sleep(remaining, signal);
106
+ throwIfAborted(signal);
107
+ throw unavailable(error, failingSince, what);
108
+ }
109
+ await sleep(delay, signal);
110
+ continue;
111
+ }
112
+ failures = 0;
113
+ failingSince = null;
114
+ const expiry = expiresAt ? timeOf(expiresAt(value)) : null;
115
+ if (expiry !== null) {
116
+ deadline = expiry + WAIT_EXPIRY_GRACE_MS;
117
+ }
118
+ if (settled(value)) {
119
+ return value;
120
+ }
121
+ // An injected transport can resolve pending polls immediately. Yield
122
+ // between them so timer-driven cancellation and deadlines can run.
123
+ await sleep(Math.max(0, minIntervalMs - (Date.now() - startedAt)), signal);
124
+ }
125
+ }
126
+ //# sourceMappingURL=retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/internal/retry.ts"],"names":[],"mappings":"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"}
@@ -12,4 +12,13 @@ export declare function assertSecureUrl(rawUrl: string, allowInsecure: boolean):
12
12
  * errors, so a message copied from one must not reach a caller's logs as-is.
13
13
  */
14
14
  export declare function redactUrlInMessage(message: string, url: URL, rawUrl: string): string;
15
+ /**
16
+ * Every request goes out with `redirect: 'manual'` (Cloudflare Workers throw
17
+ * on `'error'`), so a redirect comes back as a response instead of being
18
+ * followed. Refuse it: a 307/308 would replay pairing secrets, uploaded bytes
19
+ * or a webhook body to whatever origin `Location` names. Browsers report a
20
+ * manual redirect as an `opaqueredirect` with status 0; other runtimes return
21
+ * the 3xx itself.
22
+ */
23
+ export declare function refuseRedirect(res: Response): void;
15
24
  //# sourceMappingURL=url.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/internal/url.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,GAAG,CAsB3E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAUpF"}
1
+ {"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../../src/internal/url.ts"],"names":[],"mappings":"AAEA,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,GAAG,CAsB3E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAUpF;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAelD"}
@@ -48,4 +48,29 @@ export function redactUrlInMessage(message, url, rawUrl) {
48
48
  }
49
49
  return out;
50
50
  }
51
+ /**
52
+ * Every request goes out with `redirect: 'manual'` (Cloudflare Workers throw
53
+ * on `'error'`), so a redirect comes back as a response instead of being
54
+ * followed. Refuse it: a 307/308 would replay pairing secrets, uploaded bytes
55
+ * or a webhook body to whatever origin `Location` names. Browsers report a
56
+ * manual redirect as an `opaqueredirect` with status 0; other runtimes return
57
+ * the 3xx itself.
58
+ */
59
+ export function refuseRedirect(res) {
60
+ if (res.type === 'opaqueredirect' || (res.status >= 300 && res.status < 400)) {
61
+ // Release the body, best effort. A custom `fetch` such as node-fetch hands
62
+ // back a Node Readable with no cancel(); calling it threw a TypeError that
63
+ // surfaced as a retryable network_error instead of this refusal.
64
+ try {
65
+ res.body?.cancel?.()?.catch?.(() => { });
66
+ }
67
+ catch {
68
+ // Ignored: the refusal below is what matters.
69
+ }
70
+ throw new PingRoomError('Refusing to follow a redirect; credentials and request bodies are never forwarded.', {
71
+ code: 'redirect_refused',
72
+ status: res.status,
73
+ });
74
+ }
75
+ }
51
76
  //# sourceMappingURL=url.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/internal/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,UAAU,cAAc,CAAC,QAAgB;IAC7C,MAAM,CAAC,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACvE,OAAO,CAAC,KAAK,WAAW,IAAI,CAAC,KAAK,WAAW,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAC3F,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,aAAsB;IACpE,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,0EAA0E;QAC1E,2CAA2C;QAC3C,MAAM,IAAI,aAAa,CAAC,+DAA+D,EAAE;YACvF,IAAI,EAAE,aAAa;SACpB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,GAAG,CAAC;IACb,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,aAAa,IAAI,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAChF,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,IAAI,aAAa,CACrB,sDAAsD,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,IAAI,KAAK;QAClF,sEAAsE,EACxE,EAAE,IAAI,EAAE,cAAc,EAAE,CACzB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe,EAAE,GAAQ,EAAE,MAAc;IAC1E,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,MAAM,aAAa,CAAC;IAC5C,IAAI,GAAG,GAAG,OAAO,CAAC;IAClB,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,IAAI,IAAI;YAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACjD,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7E,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"url.js","sourceRoot":"","sources":["../../src/internal/url.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { PingRoomError } from '../errors.js';
2
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
3
+ export declare function isNonEmptyString(value: unknown): value is string;
4
+ export declare function isNullableString(value: unknown): value is string | null;
5
+ /** UUIDs are case-insensitive; a caller may hold an id in either case. */
6
+ export declare function sameId(received: unknown, expected: string): boolean;
7
+ export declare function invalidWaitResponse(subject: string, expectedId: string, value: unknown, stateField: string): never;
8
+ /**
9
+ * A 2xx the PingRoom API never sends: a gateway's or captive portal's page, or
10
+ * a URL that is not PingRoom's. The body is kept short, for inspection only.
11
+ */
12
+ export declare function invalidSuccessResponse(status: number, expected: string, body: unknown): PingRoomError;
13
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/internal/validate.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEhE;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,GAAG,IAAI,CAEvE;AAED,0EAA0E;AAC1E,wBAAgB,MAAM,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,KAAK,CASlH;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,aAAa,CAKrG"}
@@ -0,0 +1,36 @@
1
+ // Shape checks for server responses the wait loops and Agent Inbox trust.
2
+ import { PingRoomError } from '../errors.js';
3
+ export function isRecord(value) {
4
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
5
+ }
6
+ export function isNonEmptyString(value) {
7
+ return typeof value === 'string' && value.trim() !== '';
8
+ }
9
+ export function isNullableString(value) {
10
+ return value === null || typeof value === 'string';
11
+ }
12
+ /** UUIDs are case-insensitive; a caller may hold an id in either case. */
13
+ export function sameId(received, expected) {
14
+ return typeof received === 'string' && received.toLowerCase() === expected.toLowerCase();
15
+ }
16
+ export function invalidWaitResponse(subject, expectedId, value, stateField) {
17
+ throw new PingRoomError(`PingRoom returned an invalid ${subject} wait response.`, {
18
+ code: 'invalid_response',
19
+ body: {
20
+ expected_id: expectedId,
21
+ received_id: isRecord(value) && typeof value['id'] === 'string' ? value['id'] : null,
22
+ [stateField]: isRecord(value) && typeof value[stateField] === 'string' ? value[stateField] : null,
23
+ },
24
+ });
25
+ }
26
+ /**
27
+ * A 2xx the PingRoom API never sends: a gateway's or captive portal's page, or
28
+ * a URL that is not PingRoom's. The body is kept short, for inspection only.
29
+ */
30
+ export function invalidSuccessResponse(status, expected, body) {
31
+ return new PingRoomError(`PingRoom returned an invalid response (HTTP ${status}): expected ${expected}.`, {
32
+ code: 'invalid_response',
33
+ body: typeof body === 'string' ? body.slice(0, 512) : body,
34
+ });
35
+ }
36
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/internal/validate.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAE1E,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC;AACrD,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,MAAM,CAAC,QAAiB,EAAE,QAAgB;IACxD,OAAO,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC;AAC3F,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAAe,EAAE,UAAkB,EAAE,KAAc,EAAE,UAAkB;IACzG,MAAM,IAAI,aAAa,CAAC,gCAAgC,OAAO,iBAAiB,EAAE;QAChF,IAAI,EAAE,kBAAkB;QACxB,IAAI,EAAE;YACJ,WAAW,EAAE,UAAU;YACvB,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;YACpF,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,UAAU,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI;SAClG;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAc,EAAE,QAAgB,EAAE,IAAa;IACpF,OAAO,IAAI,aAAa,CAAC,+CAA+C,MAAM,eAAe,QAAQ,GAAG,EAAE;QACxG,IAAI,EAAE,kBAAkB;QACxB,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI;KAC3D,CAAC,CAAC;AACL,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"linkPing.d.ts","sourceRoot":"","sources":["../src/linkPing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,aAAa;IAC5B,2FAA2F;IAC3F,GAAG,EAAE,MAAM,CAAC;IACZ,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAKD,wFAAwF;AACxF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAmB3D"}
1
+ {"version":3,"file":"linkPing.d.ts","sourceRoot":"","sources":["../src/linkPing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,aAAa;IAC5B,2FAA2F;IAC3F,GAAG,EAAE,MAAM,CAAC;IACZ,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAUD,wFAAwF;AACxF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAmB3D"}
package/dist/linkPing.js CHANGED
@@ -13,6 +13,10 @@
13
13
  */
14
14
  const URL_MAX = 2048;
15
15
  const LABEL_MAX = 26;
16
+ /** Count code points, as the server's mb_strlen does — an emoji is one character, not two. */
17
+ function characterLength(value) {
18
+ return Array.from(value).length;
19
+ }
16
20
  /** Build the `data` fragment for a link ping. Spread it into a ping's `data` object. */
17
21
  export function linkPing(input) {
18
22
  let parsed;
@@ -25,10 +29,10 @@ export function linkPing(input) {
25
29
  if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
26
30
  throw new TypeError('linkPing: url must be an absolute http(s) URL');
27
31
  }
28
- if (input.url.length > URL_MAX) {
32
+ if (characterLength(input.url) > URL_MAX) {
29
33
  throw new TypeError(`linkPing: url must be at most ${URL_MAX} characters`);
30
34
  }
31
- if (input.buttonLabel !== undefined && input.buttonLabel.length > LABEL_MAX) {
35
+ if (input.buttonLabel !== undefined && characterLength(input.buttonLabel) > LABEL_MAX) {
32
36
  throw new TypeError(`linkPing: buttonLabel must be at most ${LABEL_MAX} characters`);
33
37
  }
34
38
  const out = { url: input.url };
@@ -1 +1 @@
1
- {"version":3,"file":"linkPing.js","sourceRoot":"","sources":["../src/linkPing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,MAAM,OAAO,GAAG,IAAI,CAAC;AACrB,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,wFAAwF;AACxF,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChE,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,EAAE,CAAC;QAC/B,MAAM,IAAI,SAAS,CAAC,iCAAiC,OAAO,aAAa,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;QAC5E,MAAM,IAAI,SAAS,CAAC,yCAAyC,SAAS,aAAa,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,GAAG,GAAiB,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC;IAC7C,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;QAAE,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC;IAC1E,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"linkPing.js","sourceRoot":"","sources":["../src/linkPing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,MAAM,OAAO,GAAG,IAAI,CAAC;AACrB,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB,8FAA8F;AAC9F,SAAS,eAAe,CAAC,KAAa;IACpC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;AAClC,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,SAAS,CAAC,kCAAkC,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAChE,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,EAAE,CAAC;QACzC,MAAM,IAAI,SAAS,CAAC,iCAAiC,OAAO,aAAa,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,IAAI,eAAe,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,SAAS,EAAE,CAAC;QACtF,MAAM,IAAI,SAAS,CAAC,yCAAyC,SAAS,aAAa,CAAC,CAAC;IACvF,CAAC;IACD,MAAM,GAAG,GAAiB,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC;IAC7C,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;QAAE,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,WAAW,CAAC;IAC1E,OAAO,GAAG,CAAC;AACb,CAAC"}