@superblocksteam/shared 0.9609.6 → 0.9611.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 (270) hide show
  1. package/dist/constants.d.ts +1 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +2 -1
  4. package/dist/constants.js.map +1 -1
  5. package/dist/database-lifecycle/destructiveMigrations.d.ts +1 -0
  6. package/dist/database-lifecycle/destructiveMigrations.d.ts.map +1 -1
  7. package/dist/database-lifecycle/destructiveMigrations.js +21 -1
  8. package/dist/database-lifecycle/destructiveMigrations.js.map +1 -1
  9. package/dist/database-lifecycle/destructiveMigrations.test.js +22 -0
  10. package/dist/database-lifecycle/destructiveMigrations.test.js.map +1 -1
  11. package/dist/database-lifecycle/index.d.ts +6 -1
  12. package/dist/database-lifecycle/index.d.ts.map +1 -1
  13. package/dist/database-lifecycle/index.js +40 -8
  14. package/dist/database-lifecycle/index.js.map +1 -1
  15. package/dist/database-lifecycle/index.test.js +16 -1
  16. package/dist/database-lifecycle/index.test.js.map +1 -1
  17. package/dist/database-lifecycle/physicalInstanceResourceKey.d.ts +23 -0
  18. package/dist/database-lifecycle/physicalInstanceResourceKey.d.ts.map +1 -0
  19. package/dist/database-lifecycle/physicalInstanceResourceKey.js +45 -0
  20. package/dist/database-lifecycle/physicalInstanceResourceKey.js.map +1 -0
  21. package/dist/database-lifecycle/physicalInstanceResourceKey.test.d.ts +2 -0
  22. package/dist/database-lifecycle/physicalInstanceResourceKey.test.d.ts.map +1 -0
  23. package/dist/database-lifecycle/physicalInstanceResourceKey.test.js +58 -0
  24. package/dist/database-lifecycle/physicalInstanceResourceKey.test.js.map +1 -0
  25. package/dist/database-lifecycle/physicalPoolProvisioningResourceKey.test.d.ts +2 -0
  26. package/dist/database-lifecycle/physicalPoolProvisioningResourceKey.test.d.ts.map +1 -0
  27. package/dist/database-lifecycle/physicalPoolProvisioningResourceKey.test.js +19 -0
  28. package/dist/database-lifecycle/physicalPoolProvisioningResourceKey.test.js.map +1 -0
  29. package/dist/dbfs/types.d.ts +2 -0
  30. package/dist/dbfs/types.d.ts.map +1 -1
  31. package/dist/dbfs/types.js +4 -0
  32. package/dist/dbfs/types.js.map +1 -1
  33. package/dist/socket/protocol.d.ts +11 -3
  34. package/dist/socket/protocol.d.ts.map +1 -1
  35. package/dist/socket/protocol.js.map +1 -1
  36. package/dist/socket/socket.d.ts +12 -0
  37. package/dist/socket/socket.d.ts.map +1 -1
  38. package/dist/socket/socket.js +13 -1
  39. package/dist/socket/socket.js.map +1 -1
  40. package/dist/types/ai/index.d.ts +7 -0
  41. package/dist/types/ai/index.d.ts.map +1 -1
  42. package/dist/types/ai/index.js.map +1 -1
  43. package/dist/types/audit/ocsf.d.ts +10 -0
  44. package/dist/types/audit/ocsf.d.ts.map +1 -1
  45. package/dist/types/audit/ocsf.js +1 -0
  46. package/dist/types/audit/ocsf.js.map +1 -1
  47. package/dist/types/codeSecurity/index.d.ts +16 -0
  48. package/dist/types/codeSecurity/index.d.ts.map +1 -1
  49. package/dist/types/codeSecurity/index.js +23 -0
  50. package/dist/types/codeSecurity/index.js.map +1 -1
  51. package/dist/types/codeSecurity/index.test.js +48 -0
  52. package/dist/types/codeSecurity/index.test.js.map +1 -1
  53. package/dist/types/event/index.d.ts +13 -2
  54. package/dist/types/event/index.d.ts.map +1 -1
  55. package/dist/types/event/index.js +11 -0
  56. package/dist/types/event/index.js.map +1 -1
  57. package/dist/types/group/index.d.ts +3 -1
  58. package/dist/types/group/index.d.ts.map +1 -1
  59. package/dist/types/group/index.js +3 -1
  60. package/dist/types/group/index.js.map +1 -1
  61. package/dist/types/group/index.test.d.ts +2 -0
  62. package/dist/types/group/index.test.d.ts.map +1 -0
  63. package/dist/types/group/index.test.js +13 -0
  64. package/dist/types/group/index.test.js.map +1 -0
  65. package/dist/types/policyGate/errors.d.ts +78 -0
  66. package/dist/types/policyGate/errors.d.ts.map +1 -0
  67. package/dist/types/policyGate/errors.js +56 -0
  68. package/dist/types/policyGate/errors.js.map +1 -0
  69. package/dist/types/policyGate/index.d.ts +19 -5
  70. package/dist/types/policyGate/index.d.ts.map +1 -1
  71. package/dist/types/policyGate/index.js +51 -4
  72. package/dist/types/policyGate/index.js.map +1 -1
  73. package/dist/types/publishPipeline/displayNames.js +6 -6
  74. package/dist/types/publishPipeline/displayNames.js.map +1 -1
  75. package/dist/types/publishPipeline/displayNames.test.js +5 -5
  76. package/dist/types/publishPipeline/displayNames.test.js.map +1 -1
  77. package/dist/types/publishPipeline/index.d.ts +3 -3
  78. package/dist/types/publishPipeline/index.d.ts.map +1 -1
  79. package/dist/types/publishPipeline/plan.d.ts +1 -11
  80. package/dist/types/publishPipeline/plan.d.ts.map +1 -1
  81. package/dist/types/publishPipeline/plan.js +2 -57
  82. package/dist/types/publishPipeline/plan.js.map +1 -1
  83. package/dist/types/publishPipeline/plan.test.js +9 -95
  84. package/dist/types/publishPipeline/plan.test.js.map +1 -1
  85. package/dist/types/response/PostApplicationCloneResponseBody.d.ts +12 -0
  86. package/dist/types/response/PostApplicationCloneResponseBody.d.ts.map +1 -0
  87. package/dist/types/response/PostApplicationCloneResponseBody.js +3 -0
  88. package/dist/types/response/PostApplicationCloneResponseBody.js.map +1 -0
  89. package/dist/types/response/index.d.ts +1 -0
  90. package/dist/types/response/index.d.ts.map +1 -1
  91. package/dist/types/response/index.js +1 -0
  92. package/dist/types/response/index.js.map +1 -1
  93. package/dist/types/reviewPolicy/index.d.ts +6 -1
  94. package/dist/types/reviewPolicy/index.d.ts.map +1 -1
  95. package/dist/types/reviewPolicy/index.js +2 -1
  96. package/dist/types/reviewPolicy/index.js.map +1 -1
  97. package/dist/utils/attachment-upload.d.ts +31 -0
  98. package/dist/utils/attachment-upload.d.ts.map +1 -1
  99. package/dist/utils/attachment-upload.js +64 -1
  100. package/dist/utils/attachment-upload.js.map +1 -1
  101. package/dist/utils/attachment-upload.test.js +42 -0
  102. package/dist/utils/attachment-upload.test.js.map +1 -1
  103. package/dist/utils/git-url.d.ts.map +1 -1
  104. package/dist/utils/git-url.js +29 -6
  105. package/dist/utils/git-url.js.map +1 -1
  106. package/dist/utils/git-url.test.js +60 -0
  107. package/dist/utils/git-url.test.js.map +1 -1
  108. package/dist/utils/normalizeRoleName.js +2 -2
  109. package/dist/utils/normalizeRoleName.js.map +1 -1
  110. package/dist/utils/normalizeRoleName.test.d.ts +2 -0
  111. package/dist/utils/normalizeRoleName.test.d.ts.map +1 -0
  112. package/dist/utils/normalizeRoleName.test.js +25 -0
  113. package/dist/utils/normalizeRoleName.test.js.map +1 -0
  114. package/dist/utils/response-dto.test.d.ts +2 -0
  115. package/dist/utils/response-dto.test.d.ts.map +1 -0
  116. package/dist/utils/response-dto.test.js +28 -0
  117. package/dist/utils/response-dto.test.js.map +1 -0
  118. package/dist-esm/constants.d.ts +1 -0
  119. package/dist-esm/constants.d.ts.map +1 -1
  120. package/dist-esm/constants.js +1 -0
  121. package/dist-esm/constants.js.map +1 -1
  122. package/dist-esm/database-lifecycle/destructiveMigrations.d.ts +1 -0
  123. package/dist-esm/database-lifecycle/destructiveMigrations.d.ts.map +1 -1
  124. package/dist-esm/database-lifecycle/destructiveMigrations.js +21 -2
  125. package/dist-esm/database-lifecycle/destructiveMigrations.js.map +1 -1
  126. package/dist-esm/database-lifecycle/destructiveMigrations.test.js +23 -1
  127. package/dist-esm/database-lifecycle/destructiveMigrations.test.js.map +1 -1
  128. package/dist-esm/database-lifecycle/index.d.ts +6 -1
  129. package/dist-esm/database-lifecycle/index.d.ts.map +1 -1
  130. package/dist-esm/database-lifecycle/index.js +36 -7
  131. package/dist-esm/database-lifecycle/index.js.map +1 -1
  132. package/dist-esm/database-lifecycle/index.test.js +17 -2
  133. package/dist-esm/database-lifecycle/index.test.js.map +1 -1
  134. package/dist-esm/database-lifecycle/physicalInstanceResourceKey.d.ts +23 -0
  135. package/dist-esm/database-lifecycle/physicalInstanceResourceKey.d.ts.map +1 -0
  136. package/dist-esm/database-lifecycle/physicalInstanceResourceKey.js +42 -0
  137. package/dist-esm/database-lifecycle/physicalInstanceResourceKey.js.map +1 -0
  138. package/dist-esm/database-lifecycle/physicalInstanceResourceKey.test.d.ts +2 -0
  139. package/dist-esm/database-lifecycle/physicalInstanceResourceKey.test.d.ts.map +1 -0
  140. package/dist-esm/database-lifecycle/physicalInstanceResourceKey.test.js +56 -0
  141. package/dist-esm/database-lifecycle/physicalInstanceResourceKey.test.js.map +1 -0
  142. package/dist-esm/database-lifecycle/physicalPoolProvisioningResourceKey.test.d.ts +2 -0
  143. package/dist-esm/database-lifecycle/physicalPoolProvisioningResourceKey.test.d.ts.map +1 -0
  144. package/dist-esm/database-lifecycle/physicalPoolProvisioningResourceKey.test.js +17 -0
  145. package/dist-esm/database-lifecycle/physicalPoolProvisioningResourceKey.test.js.map +1 -0
  146. package/dist-esm/dbfs/types.d.ts +2 -0
  147. package/dist-esm/dbfs/types.d.ts.map +1 -1
  148. package/dist-esm/dbfs/types.js +3 -1
  149. package/dist-esm/dbfs/types.js.map +1 -1
  150. package/dist-esm/socket/protocol.d.ts +11 -3
  151. package/dist-esm/socket/protocol.d.ts.map +1 -1
  152. package/dist-esm/socket/protocol.js.map +1 -1
  153. package/dist-esm/socket/socket.d.ts +12 -0
  154. package/dist-esm/socket/socket.d.ts.map +1 -1
  155. package/dist-esm/socket/socket.js +12 -0
  156. package/dist-esm/socket/socket.js.map +1 -1
  157. package/dist-esm/types/ai/index.d.ts +7 -0
  158. package/dist-esm/types/ai/index.d.ts.map +1 -1
  159. package/dist-esm/types/ai/index.js.map +1 -1
  160. package/dist-esm/types/audit/ocsf.d.ts +10 -0
  161. package/dist-esm/types/audit/ocsf.d.ts.map +1 -1
  162. package/dist-esm/types/audit/ocsf.js +1 -0
  163. package/dist-esm/types/audit/ocsf.js.map +1 -1
  164. package/dist-esm/types/codeSecurity/index.d.ts +16 -0
  165. package/dist-esm/types/codeSecurity/index.d.ts.map +1 -1
  166. package/dist-esm/types/codeSecurity/index.js +22 -0
  167. package/dist-esm/types/codeSecurity/index.js.map +1 -1
  168. package/dist-esm/types/codeSecurity/index.test.js +49 -1
  169. package/dist-esm/types/codeSecurity/index.test.js.map +1 -1
  170. package/dist-esm/types/event/index.d.ts +13 -2
  171. package/dist-esm/types/event/index.d.ts.map +1 -1
  172. package/dist-esm/types/event/index.js +11 -0
  173. package/dist-esm/types/event/index.js.map +1 -1
  174. package/dist-esm/types/group/index.d.ts +3 -1
  175. package/dist-esm/types/group/index.d.ts.map +1 -1
  176. package/dist-esm/types/group/index.js +3 -1
  177. package/dist-esm/types/group/index.js.map +1 -1
  178. package/dist-esm/types/group/index.test.d.ts +2 -0
  179. package/dist-esm/types/group/index.test.d.ts.map +1 -0
  180. package/dist-esm/types/group/index.test.js +11 -0
  181. package/dist-esm/types/group/index.test.js.map +1 -0
  182. package/dist-esm/types/policyGate/errors.d.ts +78 -0
  183. package/dist-esm/types/policyGate/errors.d.ts.map +1 -0
  184. package/dist-esm/types/policyGate/errors.js +51 -0
  185. package/dist-esm/types/policyGate/errors.js.map +1 -0
  186. package/dist-esm/types/policyGate/index.d.ts +19 -5
  187. package/dist-esm/types/policyGate/index.d.ts.map +1 -1
  188. package/dist-esm/types/policyGate/index.js +33 -4
  189. package/dist-esm/types/policyGate/index.js.map +1 -1
  190. package/dist-esm/types/publishPipeline/displayNames.js +6 -6
  191. package/dist-esm/types/publishPipeline/displayNames.js.map +1 -1
  192. package/dist-esm/types/publishPipeline/displayNames.test.js +5 -5
  193. package/dist-esm/types/publishPipeline/displayNames.test.js.map +1 -1
  194. package/dist-esm/types/publishPipeline/index.d.ts +3 -3
  195. package/dist-esm/types/publishPipeline/index.d.ts.map +1 -1
  196. package/dist-esm/types/publishPipeline/plan.d.ts +1 -11
  197. package/dist-esm/types/publishPipeline/plan.d.ts.map +1 -1
  198. package/dist-esm/types/publishPipeline/plan.js +2 -56
  199. package/dist-esm/types/publishPipeline/plan.js.map +1 -1
  200. package/dist-esm/types/publishPipeline/plan.test.js +10 -96
  201. package/dist-esm/types/publishPipeline/plan.test.js.map +1 -1
  202. package/dist-esm/types/response/PostApplicationCloneResponseBody.d.ts +12 -0
  203. package/dist-esm/types/response/PostApplicationCloneResponseBody.d.ts.map +1 -0
  204. package/dist-esm/types/response/PostApplicationCloneResponseBody.js +2 -0
  205. package/dist-esm/types/response/PostApplicationCloneResponseBody.js.map +1 -0
  206. package/dist-esm/types/response/index.d.ts +1 -0
  207. package/dist-esm/types/response/index.d.ts.map +1 -1
  208. package/dist-esm/types/response/index.js +1 -0
  209. package/dist-esm/types/response/index.js.map +1 -1
  210. package/dist-esm/types/reviewPolicy/index.d.ts +6 -1
  211. package/dist-esm/types/reviewPolicy/index.d.ts.map +1 -1
  212. package/dist-esm/types/reviewPolicy/index.js +2 -1
  213. package/dist-esm/types/reviewPolicy/index.js.map +1 -1
  214. package/dist-esm/utils/attachment-upload.d.ts +31 -0
  215. package/dist-esm/utils/attachment-upload.d.ts.map +1 -1
  216. package/dist-esm/utils/attachment-upload.js +60 -0
  217. package/dist-esm/utils/attachment-upload.js.map +1 -1
  218. package/dist-esm/utils/attachment-upload.test.js +43 -1
  219. package/dist-esm/utils/attachment-upload.test.js.map +1 -1
  220. package/dist-esm/utils/git-url.d.ts.map +1 -1
  221. package/dist-esm/utils/git-url.js +29 -6
  222. package/dist-esm/utils/git-url.js.map +1 -1
  223. package/dist-esm/utils/git-url.test.js +60 -0
  224. package/dist-esm/utils/git-url.test.js.map +1 -1
  225. package/dist-esm/utils/normalizeRoleName.js +2 -2
  226. package/dist-esm/utils/normalizeRoleName.js.map +1 -1
  227. package/dist-esm/utils/normalizeRoleName.test.d.ts +2 -0
  228. package/dist-esm/utils/normalizeRoleName.test.d.ts.map +1 -0
  229. package/dist-esm/utils/normalizeRoleName.test.js +23 -0
  230. package/dist-esm/utils/normalizeRoleName.test.js.map +1 -0
  231. package/dist-esm/utils/response-dto.test.d.ts +2 -0
  232. package/dist-esm/utils/response-dto.test.d.ts.map +1 -0
  233. package/dist-esm/utils/response-dto.test.js +26 -0
  234. package/dist-esm/utils/response-dto.test.js.map +1 -0
  235. package/package.json +5 -2
  236. package/src/constants.ts +1 -0
  237. package/src/database-lifecycle/destructiveMigrations.test.ts +27 -1
  238. package/src/database-lifecycle/destructiveMigrations.ts +22 -2
  239. package/src/database-lifecycle/index.test.ts +18 -1
  240. package/src/database-lifecycle/index.ts +41 -7
  241. package/src/database-lifecycle/physicalInstanceResourceKey.test.ts +66 -0
  242. package/src/database-lifecycle/physicalInstanceResourceKey.ts +51 -0
  243. package/src/database-lifecycle/physicalPoolProvisioningResourceKey.test.ts +22 -0
  244. package/src/dbfs/types.ts +5 -0
  245. package/src/socket/protocol.ts +10 -2
  246. package/src/socket/socket.ts +12 -0
  247. package/src/types/ai/index.ts +7 -0
  248. package/src/types/audit/ocsf.ts +12 -0
  249. package/src/types/codeSecurity/index.test.ts +58 -1
  250. package/src/types/codeSecurity/index.ts +49 -0
  251. package/src/types/event/index.ts +13 -2
  252. package/src/types/group/index.test.ts +13 -0
  253. package/src/types/group/index.ts +4 -2
  254. package/src/types/policyGate/errors.ts +66 -0
  255. package/src/types/policyGate/index.ts +44 -6
  256. package/src/types/publishPipeline/displayNames.test.ts +5 -5
  257. package/src/types/publishPipeline/displayNames.ts +6 -6
  258. package/src/types/publishPipeline/index.ts +3 -3
  259. package/src/types/publishPipeline/plan.test.ts +12 -105
  260. package/src/types/publishPipeline/plan.ts +3 -62
  261. package/src/types/response/PostApplicationCloneResponseBody.ts +12 -0
  262. package/src/types/response/index.ts +1 -0
  263. package/src/types/reviewPolicy/index.ts +7 -1
  264. package/src/utils/attachment-upload.test.ts +48 -1
  265. package/src/utils/attachment-upload.ts +78 -0
  266. package/src/utils/git-url.test.ts +65 -0
  267. package/src/utils/git-url.ts +29 -6
  268. package/src/utils/normalizeRoleName.test.ts +27 -0
  269. package/src/utils/normalizeRoleName.ts +2 -2
  270. package/src/utils/response-dto.test.ts +29 -0
@@ -5,10 +5,13 @@
5
5
  * Used by both the UI and the vite-plugin-file-sync AI agent tooling.
6
6
  */
7
7
 
8
- const HTTPS_GIT_URL_RE = /^https?:\/\/[^\s/$.?#]+(?:\.[^\s/$.?#]+)+\/[^\s/$.?#]+(\/[^\s/$.?#]+)+(\.git)?\/?$/i;
9
- const SSH_GIT_URL_RE = /^[\w-]+@[\w.-]+:[\w./-]+(\.git)?$/;
8
+ // Character classes below are bounded ({1,256} / {1,4096}) to avoid polynomial
9
+ // backtracking on adversarial input (SEC-78) — real hosts/paths are far shorter,
10
+ // so behavior is preserved for every valid git remote URL.
11
+ const HTTPS_GIT_URL_RE = /^https?:\/\/[^\s/$.?#]{1,256}(?:\.[^\s/$.?#]{1,256})+\/[^\s/$.?#]{1,256}(?:\/[^\s/$.?#]{1,256})+(?:\.git)?\/?$/i;
12
+ const SSH_GIT_URL_RE = /^[\w-]{1,256}@[\w.-]{1,256}:[\w./-]{1,4096}(?:\.git)?$/;
10
13
  // ssh:// protocol format: ssh://git@host/path
11
- const SSH_PROTOCOL_RE = /^ssh:\/\/[\w-]+@[\w.-]+(\/[\w./-]+)+(\.git)?\/?$/i;
14
+ const SSH_PROTOCOL_RE = /^ssh:\/\/[\w-]{1,256}@[\w.-]{1,256}\/[\w./-]{1,4096}(?:\.git)?\/?$/i;
12
15
 
13
16
  export function isValidGitUrl(url: string): boolean {
14
17
  const trimmed = url.trim();
@@ -69,7 +72,15 @@ export function gitRemoteUrlToWeb(url: string): string | null {
69
72
  }
70
73
 
71
74
  // SCP-style SSH: git@host:org/repo.git → https://host/org/repo
72
- const sshMatch = trimmed.match(/^[\w-]+@([\w.-]+):(.+?)(?:\.git)?\s*$/);
75
+ // Path capture is bounded ({1,4096}) so it cannot drive polynomial
76
+ // backtracking on adversarial input (SEC-78). The char class itself is
77
+ // unchanged (`.`) — paths may legitimately contain spaces (e.g. Azure
78
+ // DevOps project/repo names), and the numeric bound alone is enough to
79
+ // keep matching linear. `trimmed` (see top of function) never ends in
80
+ // whitespace, so the trailing `\s*` that used to sit before `$` was
81
+ // redundant (always zero-width) and only added its own O(n) rescan per
82
+ // lazy-capture step — dropped for a plain `$`.
83
+ const sshMatch = trimmed.match(/^[\w-]{1,256}@([\w.-]{1,256}):(.{1,4096}?)(?:\.git)?$/);
73
84
  if (sshMatch?.[1] && sshMatch[2]) {
74
85
  const host = sshMatch[1];
75
86
  const path = sshMatch[2];
@@ -212,7 +223,12 @@ export function sshGitUrlToHttps(url: string): string | null {
212
223
  const host = parsed.hostname;
213
224
  const path = parsed.pathname;
214
225
  if (host === 'ssh.dev.azure.com') {
215
- const azureMatch = path.match(/^\/v3\/([^/]+)\/([^/]+)\/(.+?)(?:\.git)?\/?$/);
226
+ // Path capture is bounded ({1,4096}) so the trailing `(?:\.git)?\/?$`
227
+ // cannot drive polynomial backtracking on adversarial input (SEC-78).
228
+ // The char class itself is unchanged (`.`) — Azure DevOps repo names
229
+ // may legitimately contain spaces, and the numeric bound alone is
230
+ // enough to keep matching linear.
231
+ const azureMatch = path.match(/^\/v3\/([^/]{1,256})\/([^/]{1,256})\/(.{1,4096}?)(?:\.git)?\/?$/);
216
232
  if (azureMatch) {
217
233
  return `https://dev.azure.com/${azureMatch[1]}/${azureMatch[2]}/_git/${azureMatch[3]}`;
218
234
  }
@@ -223,7 +239,14 @@ export function sshGitUrlToHttps(url: string): string | null {
223
239
  // Not a standard URL — try SCP-style SSH below.
224
240
  }
225
241
 
226
- const sshMatch = trimmed.match(/^[\w-]+@([\w.-]+):(.+?)(?:\.git)?\s*$/);
242
+ // Path capture is bounded ({1,4096}) so it cannot drive polynomial
243
+ // backtracking on adversarial input (SEC-78). The char class itself is
244
+ // unchanged (`.`) — paths may legitimately contain spaces, and the numeric
245
+ // bound alone is enough to keep matching linear. `trimmed` (see top of
246
+ // function) never ends in whitespace, so the trailing `\s*` that used to
247
+ // sit before `$` was redundant (always zero-width) and only added its own
248
+ // O(n) rescan per lazy-capture step — dropped for a plain `$`.
249
+ const sshMatch = trimmed.match(/^[\w-]{1,256}@([\w.-]{1,256}):(.{1,4096}?)(?:\.git)?$/);
227
250
  if (sshMatch?.[1] && sshMatch[2]) {
228
251
  const host = sshMatch[1];
229
252
  const path = sshMatch[2];
@@ -0,0 +1,27 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { normalizeRoleName } from './normalizeRoleName.js';
4
+
5
+ describe('normalizeRoleName', () => {
6
+ it('lowercases a simple name', () => {
7
+ expect(normalizeRoleName('Admin')).toBe('admin');
8
+ });
9
+
10
+ it('collapses internal non-alphanumeric runs to a single underscore', () => {
11
+ expect(normalizeRoleName('Team Lead')).toBe('team_lead');
12
+ expect(normalizeRoleName('a__b')).toBe('a_b');
13
+ });
14
+
15
+ it('strips leading and trailing non-alphanumeric characters', () => {
16
+ expect(normalizeRoleName(' Team Lead ')).toBe('team_lead');
17
+ expect(normalizeRoleName('!!!Role@@@')).toBe('role');
18
+ expect(normalizeRoleName('___')).toBe('');
19
+ });
20
+
21
+ it('handles a long non-alphanumeric run without catastrophic backtracking', () => {
22
+ const input = `${'!'.repeat(100_000)}role`;
23
+ const start = Date.now();
24
+ expect(normalizeRoleName(input)).toBe('role');
25
+ expect(Date.now() - start).toBeLessThan(1_000);
26
+ });
27
+ });
@@ -1,7 +1,7 @@
1
1
  export function normalizeRoleName(displayName: string): string {
2
2
  const key = displayName
3
3
  .trim() // remove leading/trailing whitespace
4
- .replace(/^[\W_]+|[\W_]+$/g, '') // remove leading/trailing non-alphanumeric characters
5
- .replace(/[\W_]+/g, '_'); // replace non-alphanumeric characters with underscores
4
+ .replace(/[\W_]+/g, '_') // collapse non-alphanumeric runs to a single underscore (linear)
5
+ .replace(/^_|_$/g, ''); // strip the single edge underscore left by the collapse step (no quantifier -> no polynomial shape)
6
6
  return key.toLowerCase();
7
7
  }
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { BadRequestError } from '../errors/index.js';
4
+ import { unwrapResponseDto } from './response-dto.js';
5
+
6
+ describe('unwrapResponseDto', () => {
7
+ it('turns a 400 response into a BadRequestError with the server message', async () => {
8
+ const response = Promise.resolve({
9
+ responseMeta: {
10
+ status: 400,
11
+ success: false,
12
+ message: 'Excluded application path ".git" cannot be uploaded',
13
+ error: {
14
+ code: 400,
15
+ message: 'Excluded application path ".git" cannot be uploaded'
16
+ }
17
+ },
18
+ data: null
19
+ });
20
+
21
+ const error = await unwrapResponseDto(response).catch((caughtError: unknown) => caughtError);
22
+
23
+ expect(error).toBeInstanceOf(BadRequestError);
24
+ expect(error).toMatchObject({
25
+ message: 'Excluded application path ".git" cannot be uploaded',
26
+ status: 400
27
+ });
28
+ });
29
+ });