@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.
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -1
- package/dist/constants.js.map +1 -1
- package/dist/database-lifecycle/destructiveMigrations.d.ts +1 -0
- package/dist/database-lifecycle/destructiveMigrations.d.ts.map +1 -1
- package/dist/database-lifecycle/destructiveMigrations.js +21 -1
- package/dist/database-lifecycle/destructiveMigrations.js.map +1 -1
- package/dist/database-lifecycle/destructiveMigrations.test.js +22 -0
- package/dist/database-lifecycle/destructiveMigrations.test.js.map +1 -1
- package/dist/database-lifecycle/index.d.ts +6 -1
- package/dist/database-lifecycle/index.d.ts.map +1 -1
- package/dist/database-lifecycle/index.js +40 -8
- package/dist/database-lifecycle/index.js.map +1 -1
- package/dist/database-lifecycle/index.test.js +16 -1
- package/dist/database-lifecycle/index.test.js.map +1 -1
- package/dist/database-lifecycle/physicalInstanceResourceKey.d.ts +23 -0
- package/dist/database-lifecycle/physicalInstanceResourceKey.d.ts.map +1 -0
- package/dist/database-lifecycle/physicalInstanceResourceKey.js +45 -0
- package/dist/database-lifecycle/physicalInstanceResourceKey.js.map +1 -0
- package/dist/database-lifecycle/physicalInstanceResourceKey.test.d.ts +2 -0
- package/dist/database-lifecycle/physicalInstanceResourceKey.test.d.ts.map +1 -0
- package/dist/database-lifecycle/physicalInstanceResourceKey.test.js +58 -0
- package/dist/database-lifecycle/physicalInstanceResourceKey.test.js.map +1 -0
- package/dist/database-lifecycle/physicalPoolProvisioningResourceKey.test.d.ts +2 -0
- package/dist/database-lifecycle/physicalPoolProvisioningResourceKey.test.d.ts.map +1 -0
- package/dist/database-lifecycle/physicalPoolProvisioningResourceKey.test.js +19 -0
- package/dist/database-lifecycle/physicalPoolProvisioningResourceKey.test.js.map +1 -0
- package/dist/dbfs/types.d.ts +2 -0
- package/dist/dbfs/types.d.ts.map +1 -1
- package/dist/dbfs/types.js +4 -0
- package/dist/dbfs/types.js.map +1 -1
- package/dist/socket/protocol.d.ts +11 -3
- package/dist/socket/protocol.d.ts.map +1 -1
- package/dist/socket/protocol.js.map +1 -1
- package/dist/socket/socket.d.ts +12 -0
- package/dist/socket/socket.d.ts.map +1 -1
- package/dist/socket/socket.js +13 -1
- package/dist/socket/socket.js.map +1 -1
- package/dist/types/ai/index.d.ts +7 -0
- package/dist/types/ai/index.d.ts.map +1 -1
- package/dist/types/ai/index.js.map +1 -1
- package/dist/types/audit/ocsf.d.ts +10 -0
- package/dist/types/audit/ocsf.d.ts.map +1 -1
- package/dist/types/audit/ocsf.js +1 -0
- package/dist/types/audit/ocsf.js.map +1 -1
- package/dist/types/codeSecurity/index.d.ts +16 -0
- package/dist/types/codeSecurity/index.d.ts.map +1 -1
- package/dist/types/codeSecurity/index.js +23 -0
- package/dist/types/codeSecurity/index.js.map +1 -1
- package/dist/types/codeSecurity/index.test.js +48 -0
- package/dist/types/codeSecurity/index.test.js.map +1 -1
- package/dist/types/event/index.d.ts +13 -2
- package/dist/types/event/index.d.ts.map +1 -1
- package/dist/types/event/index.js +11 -0
- package/dist/types/event/index.js.map +1 -1
- package/dist/types/group/index.d.ts +3 -1
- package/dist/types/group/index.d.ts.map +1 -1
- package/dist/types/group/index.js +3 -1
- package/dist/types/group/index.js.map +1 -1
- package/dist/types/group/index.test.d.ts +2 -0
- package/dist/types/group/index.test.d.ts.map +1 -0
- package/dist/types/group/index.test.js +13 -0
- package/dist/types/group/index.test.js.map +1 -0
- package/dist/types/policyGate/errors.d.ts +78 -0
- package/dist/types/policyGate/errors.d.ts.map +1 -0
- package/dist/types/policyGate/errors.js +56 -0
- package/dist/types/policyGate/errors.js.map +1 -0
- package/dist/types/policyGate/index.d.ts +19 -5
- package/dist/types/policyGate/index.d.ts.map +1 -1
- package/dist/types/policyGate/index.js +51 -4
- package/dist/types/policyGate/index.js.map +1 -1
- package/dist/types/publishPipeline/displayNames.js +6 -6
- package/dist/types/publishPipeline/displayNames.js.map +1 -1
- package/dist/types/publishPipeline/displayNames.test.js +5 -5
- package/dist/types/publishPipeline/displayNames.test.js.map +1 -1
- package/dist/types/publishPipeline/index.d.ts +3 -3
- package/dist/types/publishPipeline/index.d.ts.map +1 -1
- package/dist/types/publishPipeline/plan.d.ts +1 -11
- package/dist/types/publishPipeline/plan.d.ts.map +1 -1
- package/dist/types/publishPipeline/plan.js +2 -57
- package/dist/types/publishPipeline/plan.js.map +1 -1
- package/dist/types/publishPipeline/plan.test.js +9 -95
- package/dist/types/publishPipeline/plan.test.js.map +1 -1
- package/dist/types/response/PostApplicationCloneResponseBody.d.ts +12 -0
- package/dist/types/response/PostApplicationCloneResponseBody.d.ts.map +1 -0
- package/dist/types/response/PostApplicationCloneResponseBody.js +3 -0
- package/dist/types/response/PostApplicationCloneResponseBody.js.map +1 -0
- package/dist/types/response/index.d.ts +1 -0
- package/dist/types/response/index.d.ts.map +1 -1
- package/dist/types/response/index.js +1 -0
- package/dist/types/response/index.js.map +1 -1
- package/dist/types/reviewPolicy/index.d.ts +6 -1
- package/dist/types/reviewPolicy/index.d.ts.map +1 -1
- package/dist/types/reviewPolicy/index.js +2 -1
- package/dist/types/reviewPolicy/index.js.map +1 -1
- package/dist/utils/attachment-upload.d.ts +31 -0
- package/dist/utils/attachment-upload.d.ts.map +1 -1
- package/dist/utils/attachment-upload.js +64 -1
- package/dist/utils/attachment-upload.js.map +1 -1
- package/dist/utils/attachment-upload.test.js +42 -0
- package/dist/utils/attachment-upload.test.js.map +1 -1
- package/dist/utils/git-url.d.ts.map +1 -1
- package/dist/utils/git-url.js +29 -6
- package/dist/utils/git-url.js.map +1 -1
- package/dist/utils/git-url.test.js +60 -0
- package/dist/utils/git-url.test.js.map +1 -1
- package/dist/utils/normalizeRoleName.js +2 -2
- package/dist/utils/normalizeRoleName.js.map +1 -1
- package/dist/utils/normalizeRoleName.test.d.ts +2 -0
- package/dist/utils/normalizeRoleName.test.d.ts.map +1 -0
- package/dist/utils/normalizeRoleName.test.js +25 -0
- package/dist/utils/normalizeRoleName.test.js.map +1 -0
- package/dist/utils/response-dto.test.d.ts +2 -0
- package/dist/utils/response-dto.test.d.ts.map +1 -0
- package/dist/utils/response-dto.test.js +28 -0
- package/dist/utils/response-dto.test.js.map +1 -0
- package/dist-esm/constants.d.ts +1 -0
- package/dist-esm/constants.d.ts.map +1 -1
- package/dist-esm/constants.js +1 -0
- package/dist-esm/constants.js.map +1 -1
- package/dist-esm/database-lifecycle/destructiveMigrations.d.ts +1 -0
- package/dist-esm/database-lifecycle/destructiveMigrations.d.ts.map +1 -1
- package/dist-esm/database-lifecycle/destructiveMigrations.js +21 -2
- package/dist-esm/database-lifecycle/destructiveMigrations.js.map +1 -1
- package/dist-esm/database-lifecycle/destructiveMigrations.test.js +23 -1
- package/dist-esm/database-lifecycle/destructiveMigrations.test.js.map +1 -1
- package/dist-esm/database-lifecycle/index.d.ts +6 -1
- package/dist-esm/database-lifecycle/index.d.ts.map +1 -1
- package/dist-esm/database-lifecycle/index.js +36 -7
- package/dist-esm/database-lifecycle/index.js.map +1 -1
- package/dist-esm/database-lifecycle/index.test.js +17 -2
- package/dist-esm/database-lifecycle/index.test.js.map +1 -1
- package/dist-esm/database-lifecycle/physicalInstanceResourceKey.d.ts +23 -0
- package/dist-esm/database-lifecycle/physicalInstanceResourceKey.d.ts.map +1 -0
- package/dist-esm/database-lifecycle/physicalInstanceResourceKey.js +42 -0
- package/dist-esm/database-lifecycle/physicalInstanceResourceKey.js.map +1 -0
- package/dist-esm/database-lifecycle/physicalInstanceResourceKey.test.d.ts +2 -0
- package/dist-esm/database-lifecycle/physicalInstanceResourceKey.test.d.ts.map +1 -0
- package/dist-esm/database-lifecycle/physicalInstanceResourceKey.test.js +56 -0
- package/dist-esm/database-lifecycle/physicalInstanceResourceKey.test.js.map +1 -0
- package/dist-esm/database-lifecycle/physicalPoolProvisioningResourceKey.test.d.ts +2 -0
- package/dist-esm/database-lifecycle/physicalPoolProvisioningResourceKey.test.d.ts.map +1 -0
- package/dist-esm/database-lifecycle/physicalPoolProvisioningResourceKey.test.js +17 -0
- package/dist-esm/database-lifecycle/physicalPoolProvisioningResourceKey.test.js.map +1 -0
- package/dist-esm/dbfs/types.d.ts +2 -0
- package/dist-esm/dbfs/types.d.ts.map +1 -1
- package/dist-esm/dbfs/types.js +3 -1
- package/dist-esm/dbfs/types.js.map +1 -1
- package/dist-esm/socket/protocol.d.ts +11 -3
- package/dist-esm/socket/protocol.d.ts.map +1 -1
- package/dist-esm/socket/protocol.js.map +1 -1
- package/dist-esm/socket/socket.d.ts +12 -0
- package/dist-esm/socket/socket.d.ts.map +1 -1
- package/dist-esm/socket/socket.js +12 -0
- package/dist-esm/socket/socket.js.map +1 -1
- package/dist-esm/types/ai/index.d.ts +7 -0
- package/dist-esm/types/ai/index.d.ts.map +1 -1
- package/dist-esm/types/ai/index.js.map +1 -1
- package/dist-esm/types/audit/ocsf.d.ts +10 -0
- package/dist-esm/types/audit/ocsf.d.ts.map +1 -1
- package/dist-esm/types/audit/ocsf.js +1 -0
- package/dist-esm/types/audit/ocsf.js.map +1 -1
- package/dist-esm/types/codeSecurity/index.d.ts +16 -0
- package/dist-esm/types/codeSecurity/index.d.ts.map +1 -1
- package/dist-esm/types/codeSecurity/index.js +22 -0
- package/dist-esm/types/codeSecurity/index.js.map +1 -1
- package/dist-esm/types/codeSecurity/index.test.js +49 -1
- package/dist-esm/types/codeSecurity/index.test.js.map +1 -1
- package/dist-esm/types/event/index.d.ts +13 -2
- package/dist-esm/types/event/index.d.ts.map +1 -1
- package/dist-esm/types/event/index.js +11 -0
- package/dist-esm/types/event/index.js.map +1 -1
- package/dist-esm/types/group/index.d.ts +3 -1
- package/dist-esm/types/group/index.d.ts.map +1 -1
- package/dist-esm/types/group/index.js +3 -1
- package/dist-esm/types/group/index.js.map +1 -1
- package/dist-esm/types/group/index.test.d.ts +2 -0
- package/dist-esm/types/group/index.test.d.ts.map +1 -0
- package/dist-esm/types/group/index.test.js +11 -0
- package/dist-esm/types/group/index.test.js.map +1 -0
- package/dist-esm/types/policyGate/errors.d.ts +78 -0
- package/dist-esm/types/policyGate/errors.d.ts.map +1 -0
- package/dist-esm/types/policyGate/errors.js +51 -0
- package/dist-esm/types/policyGate/errors.js.map +1 -0
- package/dist-esm/types/policyGate/index.d.ts +19 -5
- package/dist-esm/types/policyGate/index.d.ts.map +1 -1
- package/dist-esm/types/policyGate/index.js +33 -4
- package/dist-esm/types/policyGate/index.js.map +1 -1
- package/dist-esm/types/publishPipeline/displayNames.js +6 -6
- package/dist-esm/types/publishPipeline/displayNames.js.map +1 -1
- package/dist-esm/types/publishPipeline/displayNames.test.js +5 -5
- package/dist-esm/types/publishPipeline/displayNames.test.js.map +1 -1
- package/dist-esm/types/publishPipeline/index.d.ts +3 -3
- package/dist-esm/types/publishPipeline/index.d.ts.map +1 -1
- package/dist-esm/types/publishPipeline/plan.d.ts +1 -11
- package/dist-esm/types/publishPipeline/plan.d.ts.map +1 -1
- package/dist-esm/types/publishPipeline/plan.js +2 -56
- package/dist-esm/types/publishPipeline/plan.js.map +1 -1
- package/dist-esm/types/publishPipeline/plan.test.js +10 -96
- package/dist-esm/types/publishPipeline/plan.test.js.map +1 -1
- package/dist-esm/types/response/PostApplicationCloneResponseBody.d.ts +12 -0
- package/dist-esm/types/response/PostApplicationCloneResponseBody.d.ts.map +1 -0
- package/dist-esm/types/response/PostApplicationCloneResponseBody.js +2 -0
- package/dist-esm/types/response/PostApplicationCloneResponseBody.js.map +1 -0
- package/dist-esm/types/response/index.d.ts +1 -0
- package/dist-esm/types/response/index.d.ts.map +1 -1
- package/dist-esm/types/response/index.js +1 -0
- package/dist-esm/types/response/index.js.map +1 -1
- package/dist-esm/types/reviewPolicy/index.d.ts +6 -1
- package/dist-esm/types/reviewPolicy/index.d.ts.map +1 -1
- package/dist-esm/types/reviewPolicy/index.js +2 -1
- package/dist-esm/types/reviewPolicy/index.js.map +1 -1
- package/dist-esm/utils/attachment-upload.d.ts +31 -0
- package/dist-esm/utils/attachment-upload.d.ts.map +1 -1
- package/dist-esm/utils/attachment-upload.js +60 -0
- package/dist-esm/utils/attachment-upload.js.map +1 -1
- package/dist-esm/utils/attachment-upload.test.js +43 -1
- package/dist-esm/utils/attachment-upload.test.js.map +1 -1
- package/dist-esm/utils/git-url.d.ts.map +1 -1
- package/dist-esm/utils/git-url.js +29 -6
- package/dist-esm/utils/git-url.js.map +1 -1
- package/dist-esm/utils/git-url.test.js +60 -0
- package/dist-esm/utils/git-url.test.js.map +1 -1
- package/dist-esm/utils/normalizeRoleName.js +2 -2
- package/dist-esm/utils/normalizeRoleName.js.map +1 -1
- package/dist-esm/utils/normalizeRoleName.test.d.ts +2 -0
- package/dist-esm/utils/normalizeRoleName.test.d.ts.map +1 -0
- package/dist-esm/utils/normalizeRoleName.test.js +23 -0
- package/dist-esm/utils/normalizeRoleName.test.js.map +1 -0
- package/dist-esm/utils/response-dto.test.d.ts +2 -0
- package/dist-esm/utils/response-dto.test.d.ts.map +1 -0
- package/dist-esm/utils/response-dto.test.js +26 -0
- package/dist-esm/utils/response-dto.test.js.map +1 -0
- package/package.json +5 -2
- package/src/constants.ts +1 -0
- package/src/database-lifecycle/destructiveMigrations.test.ts +27 -1
- package/src/database-lifecycle/destructiveMigrations.ts +22 -2
- package/src/database-lifecycle/index.test.ts +18 -1
- package/src/database-lifecycle/index.ts +41 -7
- package/src/database-lifecycle/physicalInstanceResourceKey.test.ts +66 -0
- package/src/database-lifecycle/physicalInstanceResourceKey.ts +51 -0
- package/src/database-lifecycle/physicalPoolProvisioningResourceKey.test.ts +22 -0
- package/src/dbfs/types.ts +5 -0
- package/src/socket/protocol.ts +10 -2
- package/src/socket/socket.ts +12 -0
- package/src/types/ai/index.ts +7 -0
- package/src/types/audit/ocsf.ts +12 -0
- package/src/types/codeSecurity/index.test.ts +58 -1
- package/src/types/codeSecurity/index.ts +49 -0
- package/src/types/event/index.ts +13 -2
- package/src/types/group/index.test.ts +13 -0
- package/src/types/group/index.ts +4 -2
- package/src/types/policyGate/errors.ts +66 -0
- package/src/types/policyGate/index.ts +44 -6
- package/src/types/publishPipeline/displayNames.test.ts +5 -5
- package/src/types/publishPipeline/displayNames.ts +6 -6
- package/src/types/publishPipeline/index.ts +3 -3
- package/src/types/publishPipeline/plan.test.ts +12 -105
- package/src/types/publishPipeline/plan.ts +3 -62
- package/src/types/response/PostApplicationCloneResponseBody.ts +12 -0
- package/src/types/response/index.ts +1 -0
- package/src/types/reviewPolicy/index.ts +7 -1
- package/src/utils/attachment-upload.test.ts +48 -1
- package/src/utils/attachment-upload.ts +78 -0
- package/src/utils/git-url.test.ts +65 -0
- package/src/utils/git-url.ts +29 -6
- package/src/utils/normalizeRoleName.test.ts +27 -0
- package/src/utils/normalizeRoleName.ts +2 -2
- package/src/utils/response-dto.test.ts +29 -0
package/src/utils/git-url.ts
CHANGED
|
@@ -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
|
-
|
|
9
|
-
|
|
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-]
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
5
|
-
.replace(
|
|
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
|
+
});
|