@zapier/zapier-sdk 0.4.1 → 0.5.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 (374) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/api/auth.d.ts +9 -0
  3. package/dist/api/auth.d.ts.map +1 -0
  4. package/dist/api/auth.js +25 -0
  5. package/dist/api/client.d.ts +9 -0
  6. package/dist/api/client.d.ts.map +1 -0
  7. package/dist/api/client.js +322 -0
  8. package/dist/api/debug.d.ts +13 -0
  9. package/dist/api/debug.d.ts.map +1 -0
  10. package/dist/api/debug.js +55 -0
  11. package/dist/api/index.d.ts +29 -0
  12. package/dist/api/index.d.ts.map +1 -0
  13. package/dist/api/index.js +41 -0
  14. package/dist/api/polling.d.ts +16 -0
  15. package/dist/api/polling.d.ts.map +1 -0
  16. package/dist/api/polling.js +45 -0
  17. package/dist/api/schemas.d.ts +2473 -0
  18. package/dist/api/schemas.d.ts.map +1 -0
  19. package/dist/api/schemas.js +355 -0
  20. package/dist/api/types.d.ts +75 -0
  21. package/dist/api/types.d.ts.map +1 -0
  22. package/dist/api/types.js +11 -0
  23. package/dist/auth.d.ts +34 -0
  24. package/dist/auth.d.ts.map +1 -0
  25. package/dist/auth.js +47 -0
  26. package/dist/constants.d.ts +10 -0
  27. package/dist/constants.d.ts.map +1 -0
  28. package/dist/constants.js +9 -0
  29. package/dist/index.cjs +2108 -1379
  30. package/dist/index.d.mts +2440 -620
  31. package/dist/index.d.ts +17 -892
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +20 -0
  34. package/dist/index.mjs +2093 -1362
  35. package/dist/plugins/api/index.d.ts +14 -0
  36. package/dist/plugins/api/index.d.ts.map +1 -0
  37. package/dist/plugins/api/index.js +21 -0
  38. package/dist/plugins/apps/index.d.ts +11 -0
  39. package/dist/plugins/apps/index.d.ts.map +1 -0
  40. package/dist/plugins/apps/index.js +91 -0
  41. package/dist/plugins/apps/types.d.ts +30 -0
  42. package/dist/plugins/apps/types.d.ts.map +1 -0
  43. package/dist/plugins/apps/types.js +2 -0
  44. package/dist/plugins/fetch/index.d.ts +21 -0
  45. package/dist/plugins/fetch/index.d.ts.map +1 -0
  46. package/dist/plugins/fetch/index.js +20 -0
  47. package/dist/plugins/findFirstAuthentication/index.d.ts +20 -0
  48. package/dist/plugins/findFirstAuthentication/index.d.ts.map +1 -0
  49. package/dist/plugins/findFirstAuthentication/index.js +24 -0
  50. package/dist/plugins/findFirstAuthentication/index.test.d.ts +2 -0
  51. package/dist/plugins/findFirstAuthentication/index.test.d.ts.map +1 -0
  52. package/dist/plugins/findFirstAuthentication/index.test.js +171 -0
  53. package/dist/plugins/findFirstAuthentication/schemas.d.ts +29 -0
  54. package/dist/plugins/findFirstAuthentication/schemas.d.ts.map +1 -0
  55. package/dist/plugins/findFirstAuthentication/schemas.js +18 -0
  56. package/dist/plugins/findUniqueAuthentication/index.d.ts +20 -0
  57. package/dist/plugins/findUniqueAuthentication/index.d.ts.map +1 -0
  58. package/dist/plugins/findUniqueAuthentication/index.js +31 -0
  59. package/dist/plugins/findUniqueAuthentication/index.test.d.ts +2 -0
  60. package/dist/plugins/findUniqueAuthentication/index.test.d.ts.map +1 -0
  61. package/dist/plugins/findUniqueAuthentication/index.test.js +152 -0
  62. package/dist/plugins/findUniqueAuthentication/schemas.d.ts +29 -0
  63. package/dist/plugins/findUniqueAuthentication/schemas.d.ts.map +1 -0
  64. package/dist/plugins/findUniqueAuthentication/schemas.js +18 -0
  65. package/dist/plugins/getAction/index.d.ts +23 -0
  66. package/dist/plugins/getAction/index.d.ts.map +1 -0
  67. package/dist/plugins/getAction/index.js +28 -0
  68. package/dist/plugins/getAction/index.test.d.ts +2 -0
  69. package/dist/plugins/getAction/index.test.d.ts.map +1 -0
  70. package/dist/plugins/getAction/index.test.js +186 -0
  71. package/dist/plugins/getAction/schemas.d.ts +23 -0
  72. package/dist/plugins/getAction/schemas.d.ts.map +1 -0
  73. package/dist/plugins/getAction/schemas.js +10 -0
  74. package/dist/plugins/getApp/index.d.ts +22 -0
  75. package/dist/plugins/getApp/index.d.ts.map +1 -0
  76. package/dist/plugins/getApp/index.js +39 -0
  77. package/dist/plugins/getApp/index.test.d.ts +2 -0
  78. package/dist/plugins/getApp/index.test.d.ts.map +1 -0
  79. package/dist/plugins/getApp/index.test.js +100 -0
  80. package/dist/plugins/getApp/schemas.d.ts +18 -0
  81. package/dist/plugins/getApp/schemas.d.ts.map +1 -0
  82. package/dist/plugins/getApp/schemas.js +10 -0
  83. package/dist/plugins/getAuthentication/index.d.ts +22 -0
  84. package/dist/plugins/getAuthentication/index.d.ts.map +1 -0
  85. package/dist/plugins/getAuthentication/index.js +41 -0
  86. package/dist/plugins/getAuthentication/index.test.d.ts +2 -0
  87. package/dist/plugins/getAuthentication/index.test.d.ts.map +1 -0
  88. package/dist/plugins/getAuthentication/index.test.js +205 -0
  89. package/dist/plugins/getAuthentication/schemas.d.ts +17 -0
  90. package/dist/plugins/getAuthentication/schemas.d.ts.map +1 -0
  91. package/dist/plugins/getAuthentication/schemas.js +11 -0
  92. package/dist/plugins/getProfile/index.d.ts +23 -0
  93. package/dist/plugins/getProfile/index.d.ts.map +1 -0
  94. package/dist/plugins/getProfile/index.js +29 -0
  95. package/dist/plugins/getProfile/schemas.d.ts +13 -0
  96. package/dist/plugins/getProfile/schemas.d.ts.map +1 -0
  97. package/dist/plugins/getProfile/schemas.js +5 -0
  98. package/dist/plugins/listActions/index.d.ts +28 -0
  99. package/dist/plugins/listActions/index.d.ts.map +1 -0
  100. package/dist/plugins/listActions/index.js +61 -0
  101. package/dist/plugins/listActions/index.test.d.ts +2 -0
  102. package/dist/plugins/listActions/index.test.d.ts.map +1 -0
  103. package/dist/plugins/listActions/index.test.js +467 -0
  104. package/dist/plugins/listActions/schemas.d.ts +29 -0
  105. package/dist/plugins/listActions/schemas.d.ts.map +1 -0
  106. package/dist/plugins/listActions/schemas.js +21 -0
  107. package/dist/plugins/listApps/index.d.ts +28 -0
  108. package/dist/plugins/listApps/index.d.ts.map +1 -0
  109. package/dist/plugins/listApps/index.js +62 -0
  110. package/dist/plugins/listApps/index.test.d.ts +2 -0
  111. package/dist/plugins/listApps/index.test.d.ts.map +1 -0
  112. package/dist/plugins/listApps/index.test.js +313 -0
  113. package/dist/plugins/listApps/schemas.d.ts +30 -0
  114. package/dist/plugins/listApps/schemas.d.ts.map +1 -0
  115. package/dist/plugins/listApps/schemas.js +23 -0
  116. package/dist/plugins/listAuthentications/index.d.ts +28 -0
  117. package/dist/plugins/listAuthentications/index.d.ts.map +1 -0
  118. package/dist/plugins/listAuthentications/index.js +77 -0
  119. package/dist/plugins/listAuthentications/index.test.d.ts +2 -0
  120. package/dist/plugins/listAuthentications/index.test.d.ts.map +1 -0
  121. package/dist/plugins/listAuthentications/index.test.js +564 -0
  122. package/dist/plugins/listAuthentications/schemas.d.ts +38 -0
  123. package/dist/plugins/listAuthentications/schemas.d.ts.map +1 -0
  124. package/dist/plugins/listAuthentications/schemas.js +28 -0
  125. package/dist/plugins/listInputFields/index.d.ts +28 -0
  126. package/dist/plugins/listInputFields/index.d.ts.map +1 -0
  127. package/dist/plugins/listInputFields/index.js +133 -0
  128. package/dist/plugins/listInputFields/index.test.d.ts +2 -0
  129. package/dist/plugins/listInputFields/index.test.d.ts.map +1 -0
  130. package/dist/plugins/listInputFields/index.test.js +325 -0
  131. package/dist/plugins/listInputFields/schemas.d.ts +38 -0
  132. package/dist/plugins/listInputFields/schemas.d.ts.map +1 -0
  133. package/dist/plugins/listInputFields/schemas.js +22 -0
  134. package/dist/plugins/registry/index.d.ts +11 -0
  135. package/dist/plugins/registry/index.d.ts.map +1 -0
  136. package/dist/plugins/registry/index.js +14 -0
  137. package/dist/plugins/request/index.d.ts +19 -0
  138. package/dist/plugins/request/index.d.ts.map +1 -0
  139. package/dist/plugins/request/index.js +62 -0
  140. package/dist/plugins/request/index.test.d.ts +2 -0
  141. package/dist/plugins/request/index.test.d.ts.map +1 -0
  142. package/dist/plugins/request/index.test.js +256 -0
  143. package/dist/plugins/request/schemas.d.ts +69 -0
  144. package/dist/plugins/request/schemas.d.ts.map +1 -0
  145. package/dist/plugins/request/schemas.js +42 -0
  146. package/dist/plugins/runAction/index.d.ts +28 -0
  147. package/dist/plugins/runAction/index.d.ts.map +1 -0
  148. package/dist/plugins/runAction/index.js +86 -0
  149. package/dist/plugins/runAction/index.test.d.ts +2 -0
  150. package/dist/plugins/runAction/index.test.d.ts.map +1 -0
  151. package/dist/plugins/runAction/index.test.js +320 -0
  152. package/dist/plugins/runAction/schemas.d.ts +37 -0
  153. package/dist/plugins/runAction/schemas.d.ts.map +1 -0
  154. package/dist/plugins/runAction/schemas.js +22 -0
  155. package/dist/resolvers/actionKey.d.ts +9 -0
  156. package/dist/resolvers/actionKey.d.ts.map +1 -0
  157. package/dist/resolvers/actionKey.js +19 -0
  158. package/dist/resolvers/actionType.d.ts +9 -0
  159. package/dist/resolvers/actionType.d.ts.map +1 -0
  160. package/dist/resolvers/actionType.js +22 -0
  161. package/dist/resolvers/appKey.d.ts +7 -0
  162. package/dist/resolvers/appKey.d.ts.map +1 -0
  163. package/dist/resolvers/appKey.js +5 -0
  164. package/dist/resolvers/authenticationId.d.ts +9 -0
  165. package/dist/resolvers/authenticationId.d.ts.map +1 -0
  166. package/dist/resolvers/authenticationId.js +33 -0
  167. package/dist/resolvers/index.d.ts +40 -0
  168. package/dist/resolvers/index.d.ts.map +1 -0
  169. package/dist/resolvers/index.js +91 -0
  170. package/dist/resolvers/inputs.d.ts +8 -0
  171. package/dist/resolvers/inputs.d.ts.map +1 -0
  172. package/dist/resolvers/inputs.js +14 -0
  173. package/dist/schemas/Action.d.ts +243 -0
  174. package/dist/schemas/Action.d.ts.map +1 -0
  175. package/dist/schemas/Action.js +34 -0
  176. package/dist/schemas/App.d.ts +26 -0
  177. package/dist/schemas/App.d.ts.map +1 -0
  178. package/dist/schemas/App.js +22 -0
  179. package/dist/schemas/Auth.d.ts +161 -0
  180. package/dist/schemas/Auth.d.ts.map +1 -0
  181. package/dist/schemas/Auth.js +41 -0
  182. package/dist/schemas/Field.d.ts +144 -0
  183. package/dist/schemas/Field.d.ts.map +1 -0
  184. package/dist/schemas/Field.js +105 -0
  185. package/dist/schemas/UserProfile.d.ts +163 -0
  186. package/dist/schemas/UserProfile.d.ts.map +1 -0
  187. package/dist/schemas/UserProfile.js +29 -0
  188. package/dist/sdk.d.ts +10 -0
  189. package/dist/sdk.d.ts.map +1 -0
  190. package/dist/sdk.js +94 -0
  191. package/dist/sdk.test.d.ts +2 -0
  192. package/dist/sdk.test.d.ts.map +1 -0
  193. package/dist/sdk.test.js +135 -0
  194. package/dist/types/domain.d.ts +36 -0
  195. package/dist/types/domain.d.ts.map +1 -0
  196. package/dist/types/domain.js +1 -0
  197. package/dist/types/domain.test.d.ts +2 -0
  198. package/dist/types/domain.test.d.ts.map +1 -0
  199. package/dist/types/domain.test.js +39 -0
  200. package/dist/types/errors.d.ts +143 -0
  201. package/dist/types/errors.d.ts.map +1 -0
  202. package/dist/types/errors.js +187 -0
  203. package/dist/types/events.d.ts +38 -0
  204. package/dist/types/events.d.ts.map +1 -0
  205. package/dist/types/events.js +7 -0
  206. package/dist/types/functions.d.ts +26 -0
  207. package/dist/types/functions.d.ts.map +1 -0
  208. package/dist/types/functions.js +4 -0
  209. package/dist/types/plugin.d.ts +61 -0
  210. package/dist/types/plugin.d.ts.map +1 -0
  211. package/dist/types/plugin.js +9 -0
  212. package/dist/types/properties.d.ts +22 -0
  213. package/dist/types/properties.d.ts.map +1 -0
  214. package/dist/types/properties.js +50 -0
  215. package/dist/types/sdk.d.ts +43 -0
  216. package/dist/types/sdk.d.ts.map +1 -0
  217. package/dist/types/sdk.js +4 -0
  218. package/dist/utils/array-utils.d.ts +31 -0
  219. package/dist/utils/array-utils.d.ts.map +1 -0
  220. package/dist/utils/array-utils.js +36 -0
  221. package/dist/utils/array-utils.test.d.ts +2 -0
  222. package/dist/utils/array-utils.test.d.ts.map +1 -0
  223. package/dist/utils/array-utils.test.js +107 -0
  224. package/dist/utils/domain-utils.d.ts +78 -0
  225. package/dist/utils/domain-utils.d.ts.map +1 -0
  226. package/dist/utils/domain-utils.js +218 -0
  227. package/dist/utils/domain-utils.test.d.ts +2 -0
  228. package/dist/utils/domain-utils.test.d.ts.map +1 -0
  229. package/dist/utils/domain-utils.test.js +192 -0
  230. package/dist/utils/function-utils.d.ts +45 -0
  231. package/dist/utils/function-utils.d.ts.map +1 -0
  232. package/dist/utils/function-utils.js +158 -0
  233. package/dist/utils/function-utils.test.d.ts +2 -0
  234. package/dist/utils/function-utils.test.d.ts.map +1 -0
  235. package/dist/utils/function-utils.test.js +110 -0
  236. package/dist/utils/pagination-utils.d.ts +37 -0
  237. package/dist/utils/pagination-utils.d.ts.map +1 -0
  238. package/dist/utils/pagination-utils.js +165 -0
  239. package/dist/utils/pagination-utils.test.d.ts +17 -0
  240. package/dist/utils/pagination-utils.test.d.ts.map +1 -0
  241. package/dist/utils/pagination-utils.test.js +461 -0
  242. package/dist/utils/schema-utils.d.ts +45 -0
  243. package/dist/utils/schema-utils.d.ts.map +1 -0
  244. package/dist/utils/schema-utils.js +65 -0
  245. package/dist/utils/validation.d.ts +4 -0
  246. package/dist/utils/validation.d.ts.map +1 -0
  247. package/dist/utils/validation.js +30 -0
  248. package/dist/utils/validation.test.d.ts +2 -0
  249. package/dist/utils/validation.test.d.ts.map +1 -0
  250. package/dist/utils/validation.test.js +43 -0
  251. package/package.json +12 -3
  252. package/src/api/client.ts +394 -171
  253. package/src/api/debug.ts +10 -1
  254. package/src/api/index.ts +0 -2
  255. package/src/api/polling.ts +28 -7
  256. package/src/api/schemas.ts +387 -0
  257. package/src/api/types.ts +72 -136
  258. package/src/constants.ts +10 -0
  259. package/src/index.ts +40 -19
  260. package/src/plugins/api/index.ts +47 -0
  261. package/src/plugins/apps/index.ts +25 -19
  262. package/src/plugins/apps/types.ts +7 -11
  263. package/src/plugins/fetch/index.ts +48 -40
  264. package/src/plugins/findFirstAuthentication/index.test.ts +206 -0
  265. package/src/plugins/findFirstAuthentication/index.ts +55 -0
  266. package/src/plugins/findFirstAuthentication/schemas.ts +41 -0
  267. package/src/plugins/findUniqueAuthentication/index.test.ts +197 -0
  268. package/src/plugins/findUniqueAuthentication/index.ts +72 -0
  269. package/src/plugins/findUniqueAuthentication/schemas.ts +42 -0
  270. package/src/plugins/getAction/index.test.ts +239 -0
  271. package/src/plugins/getAction/index.ts +57 -0
  272. package/src/plugins/getAction/schemas.ts +33 -0
  273. package/src/plugins/getApp/index.test.ts +127 -0
  274. package/src/plugins/getApp/index.ts +66 -0
  275. package/src/plugins/getApp/schemas.ts +38 -0
  276. package/src/plugins/getAuthentication/index.test.ts +284 -0
  277. package/src/plugins/getAuthentication/index.ts +86 -0
  278. package/src/plugins/getAuthentication/schemas.ts +31 -0
  279. package/src/plugins/getProfile/index.ts +55 -0
  280. package/src/plugins/getProfile/schemas.ts +26 -0
  281. package/src/plugins/listActions/index.test.ts +582 -0
  282. package/src/plugins/listActions/index.ts +115 -0
  283. package/src/plugins/listActions/schemas.ts +54 -0
  284. package/src/plugins/listApps/index.test.ts +357 -0
  285. package/src/plugins/listApps/index.ts +121 -0
  286. package/src/plugins/listApps/schemas.ts +49 -0
  287. package/src/plugins/listAuthentications/index.test.ts +709 -0
  288. package/src/plugins/listAuthentications/index.ts +136 -0
  289. package/src/plugins/listAuthentications/schemas.ts +60 -0
  290. package/src/plugins/listInputFields/index.test.ts +408 -0
  291. package/src/plugins/listInputFields/index.ts +204 -0
  292. package/src/plugins/listInputFields/schemas.ts +56 -0
  293. package/src/plugins/registry/index.ts +30 -0
  294. package/src/plugins/request/index.test.ts +329 -0
  295. package/src/plugins/request/index.ts +103 -0
  296. package/src/{functions → plugins}/request/schemas.ts +20 -9
  297. package/src/plugins/runAction/index.test.ts +387 -0
  298. package/src/plugins/runAction/index.ts +176 -0
  299. package/src/plugins/runAction/schemas.ts +53 -0
  300. package/src/resolvers/actionKey.ts +6 -4
  301. package/src/resolvers/actionType.ts +7 -2
  302. package/src/resolvers/appKey.ts +1 -1
  303. package/src/resolvers/authenticationId.ts +12 -3
  304. package/src/resolvers/inputs.ts +3 -1
  305. package/src/schemas/Action.ts +18 -12
  306. package/src/schemas/App.ts +11 -19
  307. package/src/schemas/Auth.ts +18 -13
  308. package/src/schemas/Field.ts +106 -11
  309. package/src/schemas/UserProfile.ts +43 -0
  310. package/src/sdk.test.ts +212 -0
  311. package/src/sdk.ts +132 -102
  312. package/src/types/domain.test.ts +50 -0
  313. package/src/types/domain.ts +43 -75
  314. package/src/types/errors.ts +275 -0
  315. package/src/types/functions.ts +27 -0
  316. package/src/types/optional-zapier-sdk-cli-login.d.ts +37 -0
  317. package/src/types/plugin.ts +105 -0
  318. package/src/types/properties.ts +4 -3
  319. package/src/types/sdk.ts +70 -48
  320. package/src/utils/array-utils.test.ts +131 -0
  321. package/src/utils/array-utils.ts +41 -0
  322. package/src/utils/domain-utils.test.ts +239 -0
  323. package/src/utils/domain-utils.ts +283 -0
  324. package/src/utils/function-utils.test.ts +141 -0
  325. package/src/utils/function-utils.ts +245 -0
  326. package/src/utils/pagination-utils.test.ts +620 -0
  327. package/src/utils/pagination-utils.ts +242 -0
  328. package/src/utils/validation.test.ts +50 -0
  329. package/src/utils/validation.ts +44 -0
  330. package/tsconfig.build.json +16 -2
  331. package/tsconfig.json +3 -1
  332. package/tsconfig.tsbuildinfo +1 -0
  333. package/tsup.config.ts +2 -0
  334. package/src/functions/bundleCode/index.ts +0 -78
  335. package/src/functions/bundleCode/info.ts +0 -9
  336. package/src/functions/bundleCode/schemas.ts +0 -30
  337. package/src/functions/findFirstAuthentication/index.ts +0 -24
  338. package/src/functions/findFirstAuthentication/info.ts +0 -9
  339. package/src/functions/findFirstAuthentication/schemas.ts +0 -50
  340. package/src/functions/findUniqueAuthentication/index.ts +0 -35
  341. package/src/functions/findUniqueAuthentication/info.ts +0 -9
  342. package/src/functions/findUniqueAuthentication/schemas.ts +0 -50
  343. package/src/functions/generateTypes/index.ts +0 -363
  344. package/src/functions/generateTypes/info.ts +0 -9
  345. package/src/functions/generateTypes/schemas.ts +0 -31
  346. package/src/functions/getAction/index.ts +0 -33
  347. package/src/functions/getAction/info.ts +0 -9
  348. package/src/functions/getAction/schemas.ts +0 -25
  349. package/src/functions/getApp/index.ts +0 -41
  350. package/src/functions/getApp/info.ts +0 -9
  351. package/src/functions/getApp/schemas.ts +0 -20
  352. package/src/functions/getAuthentication/index.ts +0 -50
  353. package/src/functions/getAuthentication/info.ts +0 -9
  354. package/src/functions/getAuthentication/schemas.ts +0 -29
  355. package/src/functions/listActions/index.ts +0 -149
  356. package/src/functions/listActions/info.ts +0 -9
  357. package/src/functions/listActions/schemas.ts +0 -30
  358. package/src/functions/listApps/index.ts +0 -60
  359. package/src/functions/listApps/info.ts +0 -9
  360. package/src/functions/listApps/schemas.ts +0 -32
  361. package/src/functions/listAuthentications/index.ts +0 -162
  362. package/src/functions/listAuthentications/info.ts +0 -9
  363. package/src/functions/listAuthentications/schemas.ts +0 -50
  364. package/src/functions/listFields/index.ts +0 -86
  365. package/src/functions/listFields/info.ts +0 -9
  366. package/src/functions/listFields/schemas.ts +0 -36
  367. package/src/functions/request/index.ts +0 -150
  368. package/src/functions/request/info.ts +0 -11
  369. package/src/functions/runAction/index.ts +0 -127
  370. package/src/functions/runAction/info.ts +0 -9
  371. package/src/functions/runAction/schemas.ts +0 -34
  372. package/src/plugins/apps/info.ts +0 -12
  373. package/src/plugins/fetch/types.ts +0 -2
  374. /package/src/{schema-utils.ts → utils/schema-utils.ts} +0 -0
@@ -0,0 +1,131 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { toArrayFromAsync } from "./array-utils";
3
+
4
+ describe("array-utils", () => {
5
+ describe("fromAsync", () => {
6
+ it("should convert async generator to array", async () => {
7
+ async function* generateNumbers() {
8
+ yield 1;
9
+ yield 2;
10
+ yield 3;
11
+ }
12
+
13
+ const result = await toArrayFromAsync(generateNumbers());
14
+ expect(result).toEqual([1, 2, 3]);
15
+ });
16
+
17
+ it("should handle empty async generator", async () => {
18
+ async function* generateNothing() {
19
+ // yield nothing
20
+ }
21
+
22
+ const result = await toArrayFromAsync(generateNothing());
23
+ expect(result).toEqual([]);
24
+ });
25
+
26
+ it("should handle async generator with async operations", async () => {
27
+ async function* generateWithDelay() {
28
+ for (let i = 1; i <= 3; i++) {
29
+ await new Promise((resolve) => setTimeout(resolve, 1)); // Small delay
30
+ yield i * 10;
31
+ }
32
+ }
33
+
34
+ const result = await toArrayFromAsync(generateWithDelay());
35
+ expect(result).toEqual([10, 20, 30]);
36
+ });
37
+
38
+ it("should handle async generator that yields different types", async () => {
39
+ async function* generateMixed() {
40
+ yield "hello";
41
+ yield 42;
42
+ yield { name: "test" };
43
+ yield [1, 2, 3];
44
+ }
45
+
46
+ const result = await toArrayFromAsync(generateMixed());
47
+ expect(result).toEqual(["hello", 42, { name: "test" }, [1, 2, 3]]);
48
+ });
49
+
50
+ it("should handle async generator with error", async () => {
51
+ async function* generateWithError() {
52
+ yield 1;
53
+ yield 2;
54
+ throw new Error("Test error");
55
+ }
56
+
57
+ await expect(toArrayFromAsync(generateWithError())).rejects.toThrow(
58
+ "Test error",
59
+ );
60
+ });
61
+
62
+ it("should handle async iterable from array with Symbol.asyncIterator", async () => {
63
+ const asyncIterable = {
64
+ async *[Symbol.asyncIterator]() {
65
+ for (const item of ["a", "b", "c"]) {
66
+ yield item;
67
+ }
68
+ },
69
+ };
70
+
71
+ const result = await toArrayFromAsync(asyncIterable);
72
+ expect(result).toEqual(["a", "b", "c"]);
73
+ });
74
+
75
+ it("should handle large async generator efficiently", async () => {
76
+ async function* generateLargeSequence() {
77
+ for (let i = 0; i < 1000; i++) {
78
+ yield i;
79
+ }
80
+ }
81
+
82
+ const result = await toArrayFromAsync(generateLargeSequence());
83
+ expect(result).toHaveLength(1000);
84
+ expect(result[0]).toBe(0);
85
+ expect(result[999]).toBe(999);
86
+ });
87
+
88
+ it("should handle async generator that yields promises (promises are auto-awaited)", async () => {
89
+ async function* generatePromises() {
90
+ yield Promise.resolve("first");
91
+ yield Promise.resolve("second");
92
+ }
93
+
94
+ const result = await toArrayFromAsync(generatePromises());
95
+ // for await automatically awaits promises, so we get resolved values
96
+ expect(result).toEqual(["first", "second"]);
97
+ });
98
+
99
+ it("should handle async generator that yields already resolved values", async () => {
100
+ async function* generateResolvedValues() {
101
+ yield await Promise.resolve("first");
102
+ yield await Promise.resolve("second");
103
+ }
104
+
105
+ const result = await toArrayFromAsync(generateResolvedValues());
106
+ // The resolved values are yielded directly
107
+ expect(result).toEqual(["first", "second"]);
108
+ });
109
+
110
+ it("should maintain order of async generator items", async () => {
111
+ async function* generateInOrder() {
112
+ for (let i = 1; i <= 5; i++) {
113
+ // Simulate varying delays
114
+ await new Promise((resolve) =>
115
+ setTimeout(resolve, Math.random() * 10),
116
+ );
117
+ yield `item-${i}`;
118
+ }
119
+ }
120
+
121
+ const result = await toArrayFromAsync(generateInOrder());
122
+ expect(result).toEqual([
123
+ "item-1",
124
+ "item-2",
125
+ "item-3",
126
+ "item-4",
127
+ "item-5",
128
+ ]);
129
+ });
130
+ });
131
+ });
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Array utility functions for working with async iterators and arrays
3
+ */
4
+
5
+ /**
6
+ * Creates an array from an async iterable, similar to Array.from() but for async iterables
7
+ *
8
+ * @param asyncIterable - An async iterable to convert to an array
9
+ * @returns Promise that resolves to an array containing all items from the async iterable
10
+ *
11
+ * @example
12
+ * async function* generateNumbers() {
13
+ * yield 1;
14
+ * yield 2;
15
+ * yield 3;
16
+ * }
17
+ *
18
+ * const numbers = await fromAsync(generateNumbers()); // [1, 2, 3]
19
+ *
20
+ * @example
21
+ * // With async generator that fetches data
22
+ * async function* fetchUsers() {
23
+ * for (let page = 1; page <= 3; page++) {
24
+ * const users = await fetchUsersPage(page);
25
+ * yield* users;
26
+ * }
27
+ * }
28
+ *
29
+ * const allUsers = await fromAsync(fetchUsers());
30
+ */
31
+ export async function toArrayFromAsync<T>(
32
+ asyncIterable: AsyncIterable<T>,
33
+ ): Promise<T[]> {
34
+ const result: T[] = [];
35
+
36
+ for await (const item of asyncIterable) {
37
+ result.push(item);
38
+ }
39
+
40
+ return result;
41
+ }
@@ -0,0 +1,239 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ groupVersionedAppKeysByType,
4
+ groupAppKeysByType,
5
+ } from "./domain-utils";
6
+
7
+ describe("domain-utils", () => {
8
+ describe("groupVersionedAppKeysByType", () => {
9
+ it("should reject UUID app keys", () => {
10
+ expect(() => {
11
+ groupVersionedAppKeysByType(["61e47557-af91-4b0c-a3e0-c28606357664"]);
12
+ }).toThrow(
13
+ "UUID app keys are not supported. Use app slug or implementation ID instead of: 61e47557-af91-4b0c-a3e0-c28606357664",
14
+ );
15
+ });
16
+
17
+ it("should categorize *API as selectedApi", () => {
18
+ const result = groupVersionedAppKeysByType(["FormatterCLIAPI"]);
19
+
20
+ expect(result).toEqual({
21
+ selectedApi: ["FormatterCLIAPI"],
22
+ slug: [],
23
+ });
24
+ });
25
+
26
+ it("should categorize various API types as selectedApi", () => {
27
+ const result = groupVersionedAppKeysByType([
28
+ "FormatterCLIAPI",
29
+ "SlackAPI",
30
+ "GmailRESTAPI",
31
+ "WebhookAPI",
32
+ "AnythingWithUppercase",
33
+ ]);
34
+
35
+ expect(result).toEqual({
36
+ selectedApi: [
37
+ "FormatterCLIAPI",
38
+ "SlackAPI",
39
+ "GmailRESTAPI",
40
+ "WebhookAPI",
41
+ "AnythingWithUppercase",
42
+ ],
43
+ slug: [],
44
+ });
45
+ });
46
+
47
+ it("should categorize lower-case strings as slugs, converting underscores to dashes", () => {
48
+ const result = groupVersionedAppKeysByType([
49
+ "slack",
50
+ "google-sheets",
51
+ "_100hires_ats",
52
+ "amazon_redshift",
53
+ ]);
54
+
55
+ expect(result).toEqual({
56
+ selectedApi: [],
57
+ slug: ["slack", "google-sheets", "100hires-ats", "amazon-redshift"],
58
+ });
59
+ });
60
+
61
+ it("should preserve version from API implementations", () => {
62
+ const result = groupVersionedAppKeysByType(["FormatterCLIAPI@1.0.0"]);
63
+
64
+ expect(result).toEqual({
65
+ selectedApi: ["FormatterCLIAPI@1.0.0"],
66
+ slug: [],
67
+ });
68
+ });
69
+
70
+ it("should preserve version from slugs", () => {
71
+ const result = groupVersionedAppKeysByType(["slack@2.1.0"]);
72
+
73
+ expect(result).toEqual({
74
+ selectedApi: [],
75
+ slug: ["slack@2.1.0"],
76
+ });
77
+ });
78
+
79
+ it("should handle mixed types correctly", () => {
80
+ const result = groupVersionedAppKeysByType([
81
+ "FormatterCLIAPI@1.0.0",
82
+ "slack",
83
+ "AnotherAPI",
84
+ "gmail@3.0.1",
85
+ ]);
86
+
87
+ expect(result).toEqual({
88
+ selectedApi: ["FormatterCLIAPI@1.0.0", "AnotherAPI"],
89
+ slug: ["slack", "gmail@3.0.1"],
90
+ });
91
+ });
92
+
93
+ it("should handle empty array", () => {
94
+ const result = groupVersionedAppKeysByType([]);
95
+
96
+ expect(result).toEqual({
97
+ selectedApi: [],
98
+ slug: [],
99
+ });
100
+ });
101
+
102
+ it("should reject uppercase and lowercase UUIDs", () => {
103
+ expect(() => {
104
+ groupVersionedAppKeysByType(["61E47557-AF91-4B0C-A3E0-C28606357664"]);
105
+ }).toThrow(
106
+ "UUID app keys are not supported. Use app slug or implementation ID instead of: 61E47557-AF91-4B0C-A3E0-C28606357664",
107
+ );
108
+
109
+ expect(() => {
110
+ groupVersionedAppKeysByType(["f47ac10b-58cc-4372-a567-0e02b2c3d479"]);
111
+ }).toThrow(
112
+ "UUID app keys are not supported. Use app slug or implementation ID instead of: f47ac10b-58cc-4372-a567-0e02b2c3d479",
113
+ );
114
+ });
115
+
116
+ it("should categorize partial UUIDs as slugs", () => {
117
+ const result = groupVersionedAppKeysByType([
118
+ "61e47557-af91-4b0c-a3e0", // too short
119
+ "61e47557-af91-4b0c-a3e0-c28606357664-extra", // too long
120
+ ]);
121
+
122
+ expect(result).toEqual({
123
+ selectedApi: [],
124
+ slug: [
125
+ "61e47557-af91-4b0c-a3e0",
126
+ "61e47557-af91-4b0c-a3e0-c28606357664-extra",
127
+ ],
128
+ });
129
+ });
130
+
131
+ it("should dedupe exact duplicate versioned keys", () => {
132
+ const result = groupVersionedAppKeysByType([
133
+ "slack@2.1.0",
134
+ "FormatterCLIAPI@1.0.0",
135
+ "slack@2.1.0", // exact duplicate
136
+ "FormatterCLIAPI@1.0.0", // exact duplicate
137
+ ]);
138
+
139
+ expect(result).toEqual({
140
+ selectedApi: ["FormatterCLIAPI@1.0.0"],
141
+ slug: ["slack@2.1.0"],
142
+ });
143
+ });
144
+ });
145
+
146
+ describe("groupAppKeysByType", () => {
147
+ it("should strip versions from API implementations", () => {
148
+ const result = groupAppKeysByType(["FormatterCLIAPI@1.0.0"]);
149
+
150
+ expect(result).toEqual({
151
+ selectedApi: ["FormatterCLIAPI"],
152
+ slug: [],
153
+ });
154
+ });
155
+
156
+ it("should strip versions from slugs", () => {
157
+ const result = groupAppKeysByType(["slack@2.1.0"]);
158
+
159
+ expect(result).toEqual({
160
+ selectedApi: [],
161
+ slug: ["slack"],
162
+ });
163
+ });
164
+
165
+ it("should handle mixed types and strip versions", () => {
166
+ const result = groupAppKeysByType([
167
+ "FormatterCLIAPI@1.0.0",
168
+ "slack",
169
+ "AnotherAPI@2.3.4",
170
+ "gmail@3.0.1",
171
+ ]);
172
+
173
+ expect(result).toEqual({
174
+ selectedApi: ["FormatterCLIAPI", "AnotherAPI"],
175
+ slug: ["slack", "gmail"],
176
+ });
177
+ });
178
+
179
+ it("should handle keys without versions", () => {
180
+ const result = groupAppKeysByType([
181
+ "FormatterCLIAPI",
182
+ "slack",
183
+ "AnotherAPI",
184
+ ]);
185
+
186
+ expect(result).toEqual({
187
+ selectedApi: ["FormatterCLIAPI", "AnotherAPI"],
188
+ slug: ["slack"],
189
+ });
190
+ });
191
+
192
+ it("should reject UUID app keys (delegated to versioned function)", () => {
193
+ expect(() => {
194
+ groupAppKeysByType(["61e47557-af91-4b0c-a3e0-c28606357664"]);
195
+ }).toThrow(
196
+ "UUID app keys are not supported. Use app slug or implementation ID instead of: 61e47557-af91-4b0c-a3e0-c28606357664",
197
+ );
198
+ });
199
+
200
+ it("should handle empty array", () => {
201
+ const result = groupAppKeysByType([]);
202
+
203
+ expect(result).toEqual({
204
+ selectedApi: [],
205
+ slug: [],
206
+ });
207
+ });
208
+
209
+ it("should dedupe multiple versions of same base key", () => {
210
+ const result = groupAppKeysByType([
211
+ "slack@1.0.0",
212
+ "FormatterCLIAPI@2.1.0",
213
+ "slack@1.2.3", // different version of slack
214
+ "FormatterCLIAPI@1.0.0", // different version of FormatterCLIAPI
215
+ "gmail",
216
+ ]);
217
+
218
+ expect(result).toEqual({
219
+ selectedApi: ["FormatterCLIAPI"], // only one instance despite multiple versions
220
+ slug: ["slack", "gmail"], // only one slack despite multiple versions
221
+ });
222
+ });
223
+
224
+ it("should dedupe exact duplicates after version stripping", () => {
225
+ const result = groupAppKeysByType([
226
+ "slack@1.0.0",
227
+ "slack", // no version
228
+ "slack@2.0.0", // different version
229
+ "FormatterCLIAPI",
230
+ "FormatterCLIAPI@1.5.0",
231
+ ]);
232
+
233
+ expect(result).toEqual({
234
+ selectedApi: ["FormatterCLIAPI"],
235
+ slug: ["slack"],
236
+ });
237
+ });
238
+ });
239
+ });
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Domain utility functions for working with app identifiers and data normalization
3
+ */
4
+
5
+ import type {
6
+ Action,
7
+ App,
8
+ Authentication,
9
+ Implementation,
10
+ ImplementationMeta,
11
+ Service,
12
+ } from "../api/types";
13
+ import type { ActionItem, AppItem, AuthenticationItem } from "../types/domain";
14
+
15
+ /**
16
+ * Splits a versioned key to extract the base key and version
17
+ *
18
+ * @param versionedKey - Versioned key in format "KeyName@version" (e.g., "SlackCLIAPI@1.21.1")
19
+ * @returns Tuple of [baseKey, version] or [versionedKey, undefined] if no @ symbol
20
+ *
21
+ * @example
22
+ * splitVersionedKey("SlackCLIAPI@1.21.1") // ["SlackCLIAPI", "1.21.1"]
23
+ * splitVersionedKey("SomeAPI") // ["SomeAPI", undefined]
24
+ */
25
+ export function splitVersionedKey(
26
+ versionedKey: string,
27
+ ): [string, string | undefined] {
28
+ const parts = versionedKey.split("@");
29
+ if (parts.length >= 2) {
30
+ const baseKey = parts[0];
31
+ const version = parts.slice(1).join("@"); // Handle edge case of multiple @ symbols
32
+ return [baseKey, version];
33
+ }
34
+ return [versionedKey, undefined];
35
+ }
36
+
37
+ /**
38
+ * Converts an API App to an AppItem with normalized field names
39
+ *
40
+ * @param app - Raw App from API
41
+ * @returns Normalized AppItem
42
+ */
43
+ export function normalizeAppItem(app: App): AppItem {
44
+ // Extract API name and version from current_implementation_id
45
+ const [apiName, appVersion] = app.current_implementation_id
46
+ ? splitVersionedKey(app.current_implementation_id)
47
+ : [app.current_implementation_id || "", undefined];
48
+
49
+ return {
50
+ title: app.name,
51
+ key: app.slug || apiName,
52
+ current_implementation_id: app.current_implementation_id || "",
53
+ version: appVersion, // Extract version separately
54
+ };
55
+ }
56
+
57
+ /**
58
+ * Converts an Implementation to an AppItem with normalized field names
59
+ *
60
+ * @param implementation - Raw Implementation from API
61
+ * @returns Normalized AppItem
62
+ */
63
+ export function normalizeImplementationToAppItem(
64
+ implementation: Implementation,
65
+ ): AppItem {
66
+ // Extract API name and version from selected_api
67
+ const [selectedApi, appVersion] = implementation.selected_api
68
+ ? splitVersionedKey(implementation.selected_api)
69
+ : [implementation.selected_api || "", undefined];
70
+
71
+ return {
72
+ title: implementation.name || selectedApi,
73
+ key: selectedApi,
74
+ current_implementation_id: implementation.selected_api || "",
75
+ version: appVersion, // Extract version separately
76
+ };
77
+ }
78
+
79
+ /**
80
+ * Converts a lightweight ImplementationMeta to a slimmed AppItem
81
+ *
82
+ * @param implementationMeta - Raw ImplementationMeta from API
83
+ * @returns Normalized AppItem with essential fields only
84
+ */
85
+ export function normalizeImplementationMetaToAppItem(
86
+ implementationMeta: ImplementationMeta,
87
+ ): AppItem {
88
+ // Extract API name and version from the implementation ID
89
+ const [selectedApi, appVersion] = splitVersionedKey(implementationMeta.id);
90
+
91
+ return {
92
+ title: implementationMeta.name,
93
+ key: selectedApi,
94
+ current_implementation_id: implementationMeta.id, // Keep the full versioned ID
95
+ version: appVersion, // Extract version separately
96
+ slug: implementationMeta.slug,
97
+ };
98
+ }
99
+
100
+ export function normalizeServiceToAppItem(service: Service): AppItem {
101
+ const [selectedApi, appVersion] = service.current_implementation_id
102
+ ? splitVersionedKey(service.current_implementation_id)
103
+ : ["", undefined];
104
+
105
+ return {
106
+ title: service.name,
107
+ key: selectedApi,
108
+ current_implementation_id: service.current_implementation_id,
109
+ version: appVersion,
110
+ description: service.description,
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Converts an API Authentication to an AuthenticationItem with normalized field names
116
+ *
117
+ * @param auth - Raw Authentication from API
118
+ * @param options - Additional fields to include
119
+ * @param options.app_key - selected_api from implementations endpoint
120
+ * @param options.version - Version extracted from selected_api
121
+ * @returns Normalized AuthenticationItem
122
+ */
123
+ export function normalizeAuthenticationItem(
124
+ auth: Authentication,
125
+ options: { app_key?: string; version?: string } = {},
126
+ ): AuthenticationItem {
127
+ // Extract app key and version from selected_api if not provided
128
+ let appKey = options.app_key;
129
+ let version = options.version;
130
+
131
+ if (auth.selected_api) {
132
+ const [extractedAppKey, extractedVersion] = splitVersionedKey(
133
+ auth.selected_api,
134
+ );
135
+
136
+ // Use extracted app key if not provided in options
137
+ if (!appKey) {
138
+ appKey = extractedAppKey;
139
+ }
140
+
141
+ // Use extracted version if not provided in options
142
+ if (!version) {
143
+ version = extractedVersion;
144
+ }
145
+ }
146
+
147
+ const {
148
+ selected_api: selectedApi,
149
+ customuser_id: userId,
150
+ ...restOfAuth
151
+ } = auth;
152
+
153
+ return {
154
+ ...restOfAuth, // Pass through all other API response fields except selected_api
155
+ implementation_id: selectedApi, // Rename selected_api to implementation_id
156
+ title: auth.title || (auth as any).label || undefined, // Coerce title from label if missing
157
+ is_expired: auth.is_stale, // Map is_stale to is_expired
158
+ expired_at: auth.marked_stale_at, // Map marked_stale_at to expired_at
159
+ app_key: appKey, // App key from implementations endpoint or parsed from selected_api
160
+ version, // Version from selected_api or provided
161
+ user_id: userId, // Map customuser_id to user_id
162
+ };
163
+ }
164
+
165
+ export function normalizeActionItem(action: Action): ActionItem {
166
+ const { name, type, selected_api: appKey, ...restOfAction } = action;
167
+ return {
168
+ ...restOfAction,
169
+ app_key: appKey || "",
170
+ action_type: type,
171
+ title: name, // Map name to title
172
+ type: "action",
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Groups app keys by their type based on format patterns
178
+ *
179
+ * @param appKeys Array of app key strings to categorize
180
+ * @returns Object with arrays of keys grouped by type: selectedApi (CLI APIs), slug (everything else)
181
+ *
182
+ * @example
183
+ * groupVersionedAppKeysByType([
184
+ * 'FormatterCLIAPI@1.0.0',
185
+ * 'slack',
186
+ * 'AnotherAPI'
187
+ * ])
188
+ * // Returns: {
189
+ * // selectedApi: ['FormatterCLIAPI@1.0.0', 'AnotherAPI'],
190
+ * // slug: ['slack']
191
+ * // }
192
+ */
193
+ export function groupVersionedAppKeysByType(appKeys: string[]): {
194
+ selectedApi: string[];
195
+ slug: string[];
196
+ } {
197
+ const result = {
198
+ selectedApi: [] as string[],
199
+ slug: [] as string[],
200
+ };
201
+
202
+ // Use Sets to track seen keys for deduplication
203
+ const seenSelectedApi = new Set<string>();
204
+ const seenSlugs = new Set<string>();
205
+
206
+ for (const key of appKeys) {
207
+ // Split by @ to get base name for classification (but preserve full key in results)
208
+ const [keyWithoutVersion, version] = splitVersionedKey(key);
209
+
210
+ // Check if it's a UUID (canonical ID) - not supported
211
+ const uuidRegex =
212
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
213
+ if (uuidRegex.test(keyWithoutVersion)) {
214
+ throw new Error(
215
+ `UUID app keys are not supported. Use app slug or implementation ID instead of: ${key}`,
216
+ );
217
+ }
218
+
219
+ // Check if it's a snake_case string
220
+ if (isSnakeCasedSlug(keyWithoutVersion)) {
221
+ const dashified = dashifySnakeCasedSlug(keyWithoutVersion);
222
+ const slugWithVersion = version ? `${dashified}@${version}` : dashified;
223
+ if (!seenSlugs.has(slugWithVersion)) {
224
+ seenSlugs.add(slugWithVersion);
225
+ result.slug.push(slugWithVersion); // Preserve full key including version
226
+ }
227
+ continue;
228
+ }
229
+
230
+ // Check if it's a slug (lowercase and dashes)
231
+ if (keyWithoutVersion.match(/^[a-z0-9]+(?:-[a-z0-9]+)*$/)) {
232
+ seenSlugs.add(key);
233
+ result.slug.push(key);
234
+ continue;
235
+ }
236
+
237
+ // Everything else is a selected_api
238
+ if (!seenSelectedApi.has(key)) {
239
+ seenSelectedApi.add(key);
240
+ result.selectedApi.push(key); // Preserve full key including version
241
+ }
242
+ }
243
+
244
+ return result;
245
+ }
246
+
247
+ export function groupAppKeysByType(appKeys: string[]): {
248
+ selectedApi: string[];
249
+ slug: string[];
250
+ } {
251
+ const grouped = groupVersionedAppKeysByType(appKeys);
252
+
253
+ // Extract base keys and dedupe by using Set
254
+ return {
255
+ selectedApi: [
256
+ ...new Set(grouped.selectedApi.map((key) => key.split("@")[0])),
257
+ ],
258
+ slug: [...new Set(grouped.slug.map((key) => key.split("@")[0]))],
259
+ };
260
+ }
261
+
262
+ function isSnakeCasedSlug(slug: string): boolean {
263
+ // Allow leading underscore for slugs starting with a number.
264
+ if (slug.match(/^_[0-9]/)) {
265
+ slug = slug.slice(1);
266
+ }
267
+
268
+ return !!slug.match(/^[a-z0-9]+(?:_[a-z0-9]+)*$/);
269
+ }
270
+
271
+ function dashifySnakeCasedSlug(slug: string): string {
272
+ // Only dashify if it's a valid snake_cased slug.
273
+ if (!isSnakeCasedSlug(slug)) {
274
+ return slug;
275
+ }
276
+
277
+ // Allow one leading underscore for slugs starting with a number.
278
+ if (slug.startsWith("_")) {
279
+ slug = slug.slice(1);
280
+ }
281
+
282
+ return slug.replace(/_/g, "-");
283
+ }