specli 0.0.11 → 0.0.13
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/README.md +216 -325
- package/bin/cli.sh +27 -0
- package/dist/{src/ai → ai}/tools.d.ts +0 -1
- package/dist/ai/tools.js +161 -0
- package/dist/ai/tools.test.d.ts +1 -0
- package/dist/ai/tools.test.js +56 -0
- package/dist/{src/cli → cli}/auth-requirements.d.ts +0 -1
- package/dist/cli/auth-requirements.js +65 -0
- package/dist/cli/auth-requirements.test.d.ts +1 -0
- package/dist/cli/auth-requirements.test.js +16 -0
- package/dist/{src/cli → cli}/auth-schemes.d.ts +0 -1
- package/dist/cli/auth-schemes.js +112 -0
- package/dist/cli/auth-schemes.test.d.ts +1 -0
- package/dist/cli/auth-schemes.test.js +56 -0
- package/dist/{src/cli → cli}/capabilities.d.ts +0 -1
- package/dist/cli/capabilities.js +41 -0
- package/dist/cli/capabilities.test.d.ts +1 -0
- package/dist/cli/capabilities.test.js +84 -0
- package/dist/{src/cli → cli}/command-id.d.ts +0 -1
- package/dist/cli/command-id.js +8 -0
- package/dist/cli/command-id.test.d.ts +1 -0
- package/dist/cli/command-id.test.js +27 -0
- package/dist/{src/cli → cli}/command-index.d.ts +0 -1
- package/dist/cli/command-index.js +9 -0
- package/dist/{src/cli → cli}/command-model.d.ts +0 -1
- package/dist/cli/command-model.js +53 -0
- package/dist/cli/command-model.test.d.ts +1 -0
- package/dist/cli/command-model.test.js +40 -0
- package/dist/{src/cli → cli}/compile.d.ts +0 -1
- package/dist/cli/compile.js +79 -0
- package/dist/{src/cli → cli}/crypto.d.ts +0 -1
- package/dist/cli/crypto.js +9 -0
- package/dist/{src/cli → cli}/derive-name.d.ts +0 -1
- package/dist/cli/derive-name.js +96 -0
- package/dist/{src/cli → cli}/exec.d.ts +0 -1
- package/dist/cli/exec.js +50 -0
- package/dist/{src/cli → cli}/main.d.ts +0 -1
- package/dist/cli/main.js +177 -0
- package/dist/{src/cli → cli}/naming.d.ts +0 -1
- package/dist/cli/naming.js +191 -0
- package/dist/cli/naming.test.d.ts +1 -0
- package/dist/cli/naming.test.js +75 -0
- package/dist/{src/cli → cli}/operations.d.ts +0 -1
- package/dist/cli/operations.js +100 -0
- package/dist/cli/operations.test.d.ts +1 -0
- package/dist/cli/operations.test.js +51 -0
- package/dist/{src/cli → cli}/params.d.ts +0 -1
- package/dist/cli/params.js +36 -0
- package/dist/cli/params.test.d.ts +1 -0
- package/dist/cli/params.test.js +62 -0
- package/dist/{src/cli → cli}/pluralize.d.ts +0 -1
- package/dist/cli/pluralize.js +38 -0
- package/dist/{src/cli → cli}/positional.d.ts +0 -1
- package/dist/cli/positional.js +35 -0
- package/dist/cli/positional.test.d.ts +1 -0
- package/dist/cli/positional.test.js +60 -0
- package/dist/{src/cli → cli}/request-body.d.ts +0 -1
- package/dist/cli/request-body.js +44 -0
- package/dist/cli/request-body.test.d.ts +1 -0
- package/dist/cli/request-body.test.js +31 -0
- package/dist/{src/cli → cli}/runtime/argv.d.ts +0 -1
- package/dist/cli/runtime/argv.js +15 -0
- package/dist/{src/cli → cli}/runtime/auth/resolve.d.ts +0 -1
- package/dist/cli/runtime/auth/resolve.js +39 -0
- package/dist/{src/cli → cli}/runtime/body-flags.d.ts +0 -1
- package/dist/cli/runtime/body-flags.js +117 -0
- package/dist/cli/runtime/body-flags.test.d.ts +1 -0
- package/dist/cli/runtime/body-flags.test.js +192 -0
- package/dist/{src/cli → cli}/runtime/body.d.ts +0 -1
- package/dist/cli/runtime/body.js +16 -0
- package/dist/{src/cli → cli}/runtime/collect.d.ts +0 -1
- package/dist/cli/runtime/collect.js +3 -0
- package/dist/{src/cli → cli}/runtime/compat.d.ts +0 -1
- package/dist/cli/runtime/compat.js +78 -0
- package/dist/{src/cli → cli}/runtime/context.d.ts +0 -1
- package/dist/cli/runtime/context.js +44 -0
- package/dist/{src/cli → cli}/runtime/execute.d.ts +0 -1
- package/dist/cli/runtime/execute.js +106 -0
- package/dist/{src/cli → cli}/runtime/generated.d.ts +0 -1
- package/dist/cli/runtime/generated.js +168 -0
- package/dist/{src/cli → cli}/runtime/headers.d.ts +0 -1
- package/dist/cli/runtime/headers.js +30 -0
- package/dist/{src/cli → cli}/runtime/index.d.ts +0 -1
- package/dist/cli/runtime/index.js +3 -0
- package/dist/{src/cli → cli}/runtime/profile/secrets.d.ts +0 -1
- package/dist/cli/runtime/profile/secrets.js +53 -0
- package/dist/{src/cli → cli}/runtime/profile/store.d.ts +0 -1
- package/dist/cli/runtime/profile/store.js +63 -0
- package/dist/{src/cli → cli}/runtime/request.d.ts +0 -1
- package/dist/cli/runtime/request.js +283 -0
- package/dist/cli/runtime/request.test.d.ts +1 -0
- package/dist/cli/runtime/request.test.js +332 -0
- package/dist/{src/cli → cli}/runtime/server-url.d.ts +0 -1
- package/dist/cli/runtime/server-url.js +28 -0
- package/dist/{src/cli → cli}/runtime/template.d.ts +0 -1
- package/dist/cli/runtime/template.js +22 -0
- package/dist/cli/runtime/validate/ajv.d.ts +2 -0
- package/dist/cli/runtime/validate/ajv.js +11 -0
- package/dist/{src/cli → cli}/runtime/validate/coerce.d.ts +0 -1
- package/dist/cli/runtime/validate/coerce.js +63 -0
- package/dist/cli/runtime/validate/coerce.test.d.ts +1 -0
- package/dist/cli/runtime/validate/coerce.test.js +75 -0
- package/dist/{src/cli → cli}/runtime/validate/error.d.ts +0 -1
- package/dist/cli/runtime/validate/error.js +19 -0
- package/dist/{src/cli → cli}/runtime/validate/index.d.ts +0 -1
- package/dist/cli/runtime/validate/index.js +4 -0
- package/dist/{src/cli → cli}/runtime/validate/schema.d.ts +0 -1
- package/dist/cli/runtime/validate/schema.js +38 -0
- package/dist/{src/cli → cli}/schema-shape.d.ts +0 -1
- package/dist/cli/schema-shape.js +34 -0
- package/dist/{src/cli → cli}/schema.d.ts +0 -1
- package/dist/cli/schema.js +31 -0
- package/dist/{src/cli → cli}/server.d.ts +0 -1
- package/dist/cli/server.js +130 -0
- package/dist/cli/server.test.d.ts +1 -0
- package/dist/cli/server.test.js +49 -0
- package/dist/{src/cli → cli}/spec-id.d.ts +0 -1
- package/dist/cli/spec-id.js +8 -0
- package/dist/{src/cli → cli}/spec-loader.d.ts +0 -1
- package/dist/cli/spec-loader.js +40 -0
- package/dist/{src/cli → cli}/stable-json.d.ts +0 -1
- package/dist/cli/stable-json.js +29 -0
- package/dist/{src/cli → cli}/strings.d.ts +0 -1
- package/dist/cli/strings.js +20 -0
- package/dist/{src/cli → cli}/types.d.ts +0 -1
- package/dist/cli/types.js +3 -0
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +51 -2324
- package/dist/compiled.d.ts +2 -0
- package/{src/compiled.ts → dist/compiled.js} +8 -11
- package/dist/macros/env.d.ts +10 -0
- package/dist/macros/env.js +22 -0
- package/dist/macros/spec.d.ts +5 -0
- package/dist/macros/spec.js +16 -0
- package/package.json +17 -25
- package/bin/specli.js +0 -26
- package/cli.ts +0 -77
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -53
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -2032
- package/dist/index.js.map +0 -48
- package/dist/src/ai/tools.d.ts.map +0 -1
- package/dist/src/ai/tools.js +0 -1656
- package/dist/src/ai/tools.js.map +0 -45
- package/dist/src/cli/auth-requirements.d.ts.map +0 -1
- package/dist/src/cli/auth-requirements.js +0 -66
- package/dist/src/cli/auth-requirements.js.map +0 -10
- package/dist/src/cli/auth-schemes.d.ts.map +0 -1
- package/dist/src/cli/auth-schemes.js +0 -116
- package/dist/src/cli/auth-schemes.js.map +0 -11
- package/dist/src/cli/capabilities.d.ts.map +0 -1
- package/dist/src/cli/capabilities.js +0 -45
- package/dist/src/cli/capabilities.js.map +0 -10
- package/dist/src/cli/command-id.d.ts.map +0 -1
- package/dist/src/cli/command-id.js +0 -18
- package/dist/src/cli/command-id.js.map +0 -11
- package/dist/src/cli/command-index.d.ts.map +0 -1
- package/dist/src/cli/command-index.js +0 -15
- package/dist/src/cli/command-index.js.map +0 -10
- package/dist/src/cli/command-model.d.ts.map +0 -1
- package/dist/src/cli/command-model.js +0 -274
- package/dist/src/cli/command-model.js.map +0 -18
- package/dist/src/cli/compile.d.ts.map +0 -1
- package/dist/src/cli/compile.js +0 -146
- package/dist/src/cli/compile.js.map +0 -11
- package/dist/src/cli/crypto.d.ts.map +0 -1
- package/dist/src/cli/crypto.js +0 -15
- package/dist/src/cli/crypto.js.map +0 -10
- package/dist/src/cli/derive-name.d.ts.map +0 -1
- package/dist/src/cli/derive-name.js +0 -70
- package/dist/src/cli/derive-name.js.map +0 -10
- package/dist/src/cli/exec.d.ts.map +0 -1
- package/dist/src/cli/exec.js +0 -2077
- package/dist/src/cli/exec.js.map +0 -49
- package/dist/src/cli/main.d.ts.map +0 -1
- package/dist/src/cli/main.js +0 -2032
- package/dist/src/cli/main.js.map +0 -48
- package/dist/src/cli/naming.d.ts.map +0 -1
- package/dist/src/cli/naming.js +0 -216
- package/dist/src/cli/naming.js.map +0 -12
- package/dist/src/cli/operations.d.ts.map +0 -1
- package/dist/src/cli/operations.js +0 -103
- package/dist/src/cli/operations.js.map +0 -10
- package/dist/src/cli/params.d.ts.map +0 -1
- package/dist/src/cli/params.js +0 -79
- package/dist/src/cli/params.js.map +0 -12
- package/dist/src/cli/pluralize.d.ts.map +0 -1
- package/dist/src/cli/pluralize.js +0 -43
- package/dist/src/cli/pluralize.js.map +0 -10
- package/dist/src/cli/positional.d.ts.map +0 -1
- package/dist/src/cli/positional.js +0 -39
- package/dist/src/cli/positional.js.map +0 -10
- package/dist/src/cli/request-body.d.ts.map +0 -1
- package/dist/src/cli/request-body.js +0 -82
- package/dist/src/cli/request-body.js.map +0 -12
- package/dist/src/cli/runtime/argv.d.ts.map +0 -1
- package/dist/src/cli/runtime/argv.js +0 -22
- package/dist/src/cli/runtime/argv.js.map +0 -10
- package/dist/src/cli/runtime/auth/resolve.d.ts.map +0 -1
- package/dist/src/cli/runtime/auth/resolve.js +0 -38
- package/dist/src/cli/runtime/auth/resolve.js.map +0 -10
- package/dist/src/cli/runtime/body-flags.d.ts.map +0 -1
- package/dist/src/cli/runtime/body-flags.js +0 -86
- package/dist/src/cli/runtime/body-flags.js.map +0 -10
- package/dist/src/cli/runtime/body.d.ts.map +0 -1
- package/dist/src/cli/runtime/body.js +0 -40
- package/dist/src/cli/runtime/body.js.map +0 -11
- package/dist/src/cli/runtime/collect.d.ts.map +0 -1
- package/dist/src/cli/runtime/collect.js +0 -9
- package/dist/src/cli/runtime/collect.js.map +0 -10
- package/dist/src/cli/runtime/compat.d.ts.map +0 -1
- package/dist/src/cli/runtime/compat.js +0 -62
- package/dist/src/cli/runtime/compat.js.map +0 -10
- package/dist/src/cli/runtime/context.d.ts.map +0 -1
- package/dist/src/cli/runtime/context.js +0 -936
- package/dist/src/cli/runtime/context.js.map +0 -32
- package/dist/src/cli/runtime/execute.d.ts.map +0 -1
- package/dist/src/cli/runtime/execute.js +0 -670
- package/dist/src/cli/runtime/execute.js.map +0 -22
- package/dist/src/cli/runtime/generated.d.ts.map +0 -1
- package/dist/src/cli/runtime/generated.js +0 -869
- package/dist/src/cli/runtime/generated.js.map +0 -23
- package/dist/src/cli/runtime/headers.d.ts.map +0 -1
- package/dist/src/cli/runtime/headers.js +0 -36
- package/dist/src/cli/runtime/headers.js.map +0 -10
- package/dist/src/cli/runtime/index.d.ts.map +0 -1
- package/dist/src/cli/runtime/index.js +0 -1808
- package/dist/src/cli/runtime/index.js.map +0 -46
- package/dist/src/cli/runtime/profile/secrets.d.ts.map +0 -1
- package/dist/src/cli/runtime/profile/secrets.js +0 -51
- package/dist/src/cli/runtime/profile/secrets.js.map +0 -11
- package/dist/src/cli/runtime/profile/store.d.ts.map +0 -1
- package/dist/src/cli/runtime/profile/store.js +0 -102
- package/dist/src/cli/runtime/profile/store.js.map +0 -11
- package/dist/src/cli/runtime/request.d.ts.map +0 -1
- package/dist/src/cli/runtime/request.js +0 -571
- package/dist/src/cli/runtime/request.js.map +0 -21
- package/dist/src/cli/runtime/server-url.d.ts.map +0 -1
- package/dist/src/cli/runtime/server-url.js +0 -55
- package/dist/src/cli/runtime/server-url.js.map +0 -11
- package/dist/src/cli/runtime/template.d.ts.map +0 -1
- package/dist/src/cli/runtime/template.js +0 -29
- package/dist/src/cli/runtime/template.js.map +0 -10
- package/dist/src/cli/runtime/validate/ajv.d.ts +0 -3
- package/dist/src/cli/runtime/validate/ajv.d.ts.map +0 -1
- package/dist/src/cli/runtime/validate/ajv.js +0 -17
- package/dist/src/cli/runtime/validate/ajv.js.map +0 -10
- package/dist/src/cli/runtime/validate/coerce.d.ts.map +0 -1
- package/dist/src/cli/runtime/validate/coerce.js +0 -60
- package/dist/src/cli/runtime/validate/coerce.js.map +0 -10
- package/dist/src/cli/runtime/validate/error.d.ts.map +0 -1
- package/dist/src/cli/runtime/validate/error.js +0 -21
- package/dist/src/cli/runtime/validate/error.js.map +0 -10
- package/dist/src/cli/runtime/validate/index.d.ts.map +0 -1
- package/dist/src/cli/runtime/validate/index.js +0 -122
- package/dist/src/cli/runtime/validate/index.js.map +0 -13
- package/dist/src/cli/runtime/validate/schema.d.ts.map +0 -1
- package/dist/src/cli/runtime/validate/schema.js +0 -36
- package/dist/src/cli/runtime/validate/schema.js.map +0 -10
- package/dist/src/cli/schema-shape.d.ts.map +0 -1
- package/dist/src/cli/schema-shape.js +0 -41
- package/dist/src/cli/schema-shape.js.map +0 -10
- package/dist/src/cli/schema.d.ts.map +0 -1
- package/dist/src/cli/schema.js +0 -38
- package/dist/src/cli/schema.js.map +0 -10
- package/dist/src/cli/server.d.ts.map +0 -1
- package/dist/src/cli/server.js +0 -64
- package/dist/src/cli/server.js.map +0 -11
- package/dist/src/cli/spec-id.d.ts.map +0 -1
- package/dist/src/cli/spec-id.js +0 -21
- package/dist/src/cli/spec-id.js.map +0 -11
- package/dist/src/cli/spec-loader.d.ts.map +0 -1
- package/dist/src/cli/spec-loader.js +0 -110
- package/dist/src/cli/spec-loader.js.map +0 -15
- package/dist/src/cli/stable-json.d.ts.map +0 -1
- package/dist/src/cli/stable-json.js +0 -35
- package/dist/src/cli/stable-json.js.map +0 -10
- package/dist/src/cli/strings.d.ts.map +0 -1
- package/dist/src/cli/strings.js +0 -16
- package/dist/src/cli/strings.js.map +0 -10
- package/dist/src/cli/types.d.ts.map +0 -1
- package/dist/src/cli/types.js +0 -9
- package/dist/src/cli/types.js.map +0 -10
- package/index.ts +0 -1
- package/src/ai/tools.ts +0 -211
- package/src/cli/auth-requirements.ts +0 -91
- package/src/cli/auth-schemes.ts +0 -187
- package/src/cli/capabilities.ts +0 -88
- package/src/cli/command-id.ts +0 -16
- package/src/cli/command-index.ts +0 -19
- package/src/cli/command-model.ts +0 -128
- package/src/cli/compile.ts +0 -101
- package/src/cli/crypto.ts +0 -9
- package/src/cli/derive-name.ts +0 -101
- package/src/cli/exec.ts +0 -72
- package/src/cli/main.ts +0 -231
- package/src/cli/naming.ts +0 -224
- package/src/cli/operations.ts +0 -152
- package/src/cli/params.ts +0 -71
- package/src/cli/pluralize.ts +0 -41
- package/src/cli/positional.ts +0 -75
- package/src/cli/request-body.ts +0 -94
- package/src/cli/runtime/argv.ts +0 -14
- package/src/cli/runtime/auth/resolve.ts +0 -59
- package/src/cli/runtime/body-flags.ts +0 -176
- package/src/cli/runtime/body.ts +0 -24
- package/src/cli/runtime/collect.ts +0 -6
- package/src/cli/runtime/compat.ts +0 -89
- package/src/cli/runtime/context.ts +0 -62
- package/src/cli/runtime/execute.ts +0 -147
- package/src/cli/runtime/generated.ts +0 -242
- package/src/cli/runtime/headers.ts +0 -37
- package/src/cli/runtime/index.ts +0 -3
- package/src/cli/runtime/profile/secrets.ts +0 -83
- package/src/cli/runtime/profile/store.ts +0 -100
- package/src/cli/runtime/request.ts +0 -390
- package/src/cli/runtime/server-url.ts +0 -45
- package/src/cli/runtime/template.ts +0 -26
- package/src/cli/runtime/validate/ajv.ts +0 -13
- package/src/cli/runtime/validate/coerce.ts +0 -71
- package/src/cli/runtime/validate/error.ts +0 -29
- package/src/cli/runtime/validate/index.ts +0 -4
- package/src/cli/runtime/validate/schema.ts +0 -54
- package/src/cli/schema-shape.ts +0 -36
- package/src/cli/schema.ts +0 -76
- package/src/cli/server.ts +0 -88
- package/src/cli/spec-id.ts +0 -12
- package/src/cli/spec-loader.ts +0 -58
- package/src/cli/stable-json.ts +0 -35
- package/src/cli/strings.ts +0 -21
- package/src/cli/types.ts +0 -59
- package/src/macros/env.ts +0 -21
- package/src/macros/spec.ts +0 -17
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { InvalidArgumentError } from "commander";
|
|
2
|
+
export function coerceValue(raw, type) {
|
|
3
|
+
if (type === "string" || type === "unknown")
|
|
4
|
+
return raw;
|
|
5
|
+
if (type === "boolean") {
|
|
6
|
+
// Commander boolean options are handled without a value; keep for completeness.
|
|
7
|
+
if (raw === "true")
|
|
8
|
+
return true;
|
|
9
|
+
if (raw === "false")
|
|
10
|
+
return false;
|
|
11
|
+
throw new InvalidArgumentError(`Expected boolean, got '${raw}'`);
|
|
12
|
+
}
|
|
13
|
+
if (type === "integer") {
|
|
14
|
+
const n = Number.parseInt(raw, 10);
|
|
15
|
+
if (!Number.isFinite(n))
|
|
16
|
+
throw new InvalidArgumentError(`Expected integer, got '${raw}'`);
|
|
17
|
+
return n;
|
|
18
|
+
}
|
|
19
|
+
if (type === "number") {
|
|
20
|
+
const n = Number(raw);
|
|
21
|
+
if (!Number.isFinite(n))
|
|
22
|
+
throw new InvalidArgumentError(`Expected number, got '${raw}'`);
|
|
23
|
+
return n;
|
|
24
|
+
}
|
|
25
|
+
// For now, accept objects as JSON strings.
|
|
26
|
+
if (type === "object") {
|
|
27
|
+
try {
|
|
28
|
+
return JSON.parse(raw);
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
throw new InvalidArgumentError(`Expected JSON object, got '${raw}'. Use --data/--file for complex bodies.`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Arrays should usually be passed as repeatable flags or comma-separated,
|
|
35
|
+
// but allow JSON arrays too.
|
|
36
|
+
if (type === "array") {
|
|
37
|
+
return coerceArrayInput(raw, "string");
|
|
38
|
+
}
|
|
39
|
+
return raw;
|
|
40
|
+
}
|
|
41
|
+
export function coerceArrayInput(raw, itemType) {
|
|
42
|
+
const trimmed = raw.trim();
|
|
43
|
+
if (!trimmed)
|
|
44
|
+
return [];
|
|
45
|
+
if (trimmed.startsWith("[")) {
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = JSON.parse(trimmed);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new InvalidArgumentError(`Expected JSON array, got '${raw}'`);
|
|
52
|
+
}
|
|
53
|
+
if (!Array.isArray(parsed)) {
|
|
54
|
+
throw new InvalidArgumentError(`Expected JSON array, got '${raw}'`);
|
|
55
|
+
}
|
|
56
|
+
return parsed.map((v) => coerceValue(String(v), itemType));
|
|
57
|
+
}
|
|
58
|
+
return trimmed
|
|
59
|
+
.split(",")
|
|
60
|
+
.map((s) => s.trim())
|
|
61
|
+
.filter(Boolean)
|
|
62
|
+
.map((s) => coerceValue(s, itemType));
|
|
63
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { coerceArrayInput, coerceValue } from "./coerce.js";
|
|
3
|
+
describe("coerceValue", () => {
|
|
4
|
+
test("returns string as-is for string type", () => {
|
|
5
|
+
expect(coerceValue("hello", "string")).toBe("hello");
|
|
6
|
+
});
|
|
7
|
+
test("returns string as-is for unknown type", () => {
|
|
8
|
+
expect(coerceValue("hello", "unknown")).toBe("hello");
|
|
9
|
+
});
|
|
10
|
+
test("parses integer type", () => {
|
|
11
|
+
expect(coerceValue("42", "integer")).toBe(42);
|
|
12
|
+
expect(coerceValue("-10", "integer")).toBe(-10);
|
|
13
|
+
expect(coerceValue("0", "integer")).toBe(0);
|
|
14
|
+
});
|
|
15
|
+
test("throws for invalid integer", () => {
|
|
16
|
+
expect(() => coerceValue("abc", "integer")).toThrow("Expected integer");
|
|
17
|
+
});
|
|
18
|
+
test("truncates decimal for integer type (parseInt behavior)", () => {
|
|
19
|
+
// parseInt("12.5", 10) returns 12 - this is expected JS behavior
|
|
20
|
+
expect(coerceValue("12.5", "integer")).toBe(12);
|
|
21
|
+
});
|
|
22
|
+
test("parses number type", () => {
|
|
23
|
+
expect(coerceValue("42", "number")).toBe(42);
|
|
24
|
+
expect(coerceValue("3.14", "number")).toBe(3.14);
|
|
25
|
+
expect(coerceValue("-0.5", "number")).toBe(-0.5);
|
|
26
|
+
});
|
|
27
|
+
test("throws for invalid number", () => {
|
|
28
|
+
expect(() => coerceValue("abc", "number")).toThrow("Expected number");
|
|
29
|
+
});
|
|
30
|
+
test("parses boolean type", () => {
|
|
31
|
+
expect(coerceValue("true", "boolean")).toBe(true);
|
|
32
|
+
expect(coerceValue("false", "boolean")).toBe(false);
|
|
33
|
+
});
|
|
34
|
+
test("throws for invalid boolean", () => {
|
|
35
|
+
expect(() => coerceValue("yes", "boolean")).toThrow("Expected boolean");
|
|
36
|
+
expect(() => coerceValue("1", "boolean")).toThrow("Expected boolean");
|
|
37
|
+
});
|
|
38
|
+
test("parses object type as JSON", () => {
|
|
39
|
+
expect(coerceValue('{"a":1}', "object")).toEqual({ a: 1 });
|
|
40
|
+
});
|
|
41
|
+
test("throws for invalid object JSON", () => {
|
|
42
|
+
expect(() => coerceValue("not json", "object")).toThrow("Expected JSON object");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe("coerceArrayInput", () => {
|
|
46
|
+
test("parses comma-separated values", () => {
|
|
47
|
+
expect(coerceArrayInput("a,b,c", "string")).toEqual(["a", "b", "c"]);
|
|
48
|
+
});
|
|
49
|
+
test("trims whitespace in comma-separated values", () => {
|
|
50
|
+
expect(coerceArrayInput("a, b, c", "string")).toEqual(["a", "b", "c"]);
|
|
51
|
+
});
|
|
52
|
+
test("parses JSON array", () => {
|
|
53
|
+
expect(coerceArrayInput('["a","b","c"]', "string")).toEqual([
|
|
54
|
+
"a",
|
|
55
|
+
"b",
|
|
56
|
+
"c",
|
|
57
|
+
]);
|
|
58
|
+
});
|
|
59
|
+
test("coerces array items to specified type", () => {
|
|
60
|
+
expect(coerceArrayInput("1,2,3", "integer")).toEqual([1, 2, 3]);
|
|
61
|
+
expect(coerceArrayInput('["1","2","3"]', "integer")).toEqual([1, 2, 3]);
|
|
62
|
+
});
|
|
63
|
+
test("returns empty array for empty string", () => {
|
|
64
|
+
expect(coerceArrayInput("", "string")).toEqual([]);
|
|
65
|
+
expect(coerceArrayInput(" ", "string")).toEqual([]);
|
|
66
|
+
});
|
|
67
|
+
test("throws for invalid JSON array", () => {
|
|
68
|
+
expect(() => coerceArrayInput("[invalid", "string")).toThrow("Expected JSON array");
|
|
69
|
+
});
|
|
70
|
+
test("treats non-array JSON as comma-separated string", () => {
|
|
71
|
+
// If it doesn't start with '[', it's treated as comma-separated
|
|
72
|
+
// '{"a":1}' doesn't start with '[', so it's treated as a single value
|
|
73
|
+
expect(coerceArrayInput('{"a":1}', "string")).toEqual(['{"a":1}']);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export function formatAjvErrors(errors) {
|
|
2
|
+
if (!errors?.length)
|
|
3
|
+
return "Invalid input";
|
|
4
|
+
return errors
|
|
5
|
+
.map((e) => {
|
|
6
|
+
const path = e.instancePath || e.schemaPath || "";
|
|
7
|
+
if (e.keyword === "required" &&
|
|
8
|
+
e.params &&
|
|
9
|
+
typeof e.params === "object" &&
|
|
10
|
+
"missingProperty" in e.params) {
|
|
11
|
+
const missing = String(e.params.missingProperty);
|
|
12
|
+
const where = e.instancePath || "/";
|
|
13
|
+
return `${where} missing required property '${missing}'`.trim();
|
|
14
|
+
}
|
|
15
|
+
const msg = e.message || "invalid";
|
|
16
|
+
return `${path} ${msg}`.trim();
|
|
17
|
+
})
|
|
18
|
+
.join("\n");
|
|
19
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function deriveValidationSchemas(action) {
|
|
2
|
+
// We validate only simple containers for now.
|
|
3
|
+
// Deep style/encoding differences for OpenAPI params are out of scope for v1.
|
|
4
|
+
const query = { type: "object", properties: {}, required: [] };
|
|
5
|
+
const header = { type: "object", properties: {}, required: [] };
|
|
6
|
+
const cookie = { type: "object", properties: {}, required: [] };
|
|
7
|
+
for (const p of action.params) {
|
|
8
|
+
if (p.kind !== "flag")
|
|
9
|
+
continue;
|
|
10
|
+
const target = p.in === "query"
|
|
11
|
+
? query
|
|
12
|
+
: p.in === "header"
|
|
13
|
+
? header
|
|
14
|
+
: p.in === "cookie"
|
|
15
|
+
? cookie
|
|
16
|
+
: undefined;
|
|
17
|
+
if (!target)
|
|
18
|
+
continue;
|
|
19
|
+
const schema = p.schema ?? (p.type === "unknown" ? {} : { type: p.type });
|
|
20
|
+
target.properties[p.name] = schema;
|
|
21
|
+
if (p.required) {
|
|
22
|
+
if (!target.required)
|
|
23
|
+
target.required = [];
|
|
24
|
+
target.required.push(p.name);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (!query.required?.length)
|
|
28
|
+
delete query.required;
|
|
29
|
+
if (!header.required?.length)
|
|
30
|
+
delete header.required;
|
|
31
|
+
if (!cookie.required?.length)
|
|
32
|
+
delete cookie.required;
|
|
33
|
+
return {
|
|
34
|
+
querySchema: Object.keys(query.properties).length ? query : undefined,
|
|
35
|
+
headerSchema: Object.keys(header.properties).length ? header : undefined,
|
|
36
|
+
cookieSchema: Object.keys(cookie.properties).length ? cookie : undefined,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -2,4 +2,3 @@ export type ParamType = "string" | "number" | "integer" | "boolean" | "array" |
|
|
|
2
2
|
export declare function getSchemaType(schema: unknown): ParamType;
|
|
3
3
|
export declare function getSchemaFormat(schema: unknown): string | undefined;
|
|
4
4
|
export declare function getSchemaEnumStrings(schema: unknown): string[] | undefined;
|
|
5
|
-
//# sourceMappingURL=schema-shape.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function getSchemaType(schema) {
|
|
2
|
+
if (!schema || typeof schema !== "object")
|
|
3
|
+
return "unknown";
|
|
4
|
+
const t = schema.type;
|
|
5
|
+
if (t === "string")
|
|
6
|
+
return "string";
|
|
7
|
+
if (t === "number")
|
|
8
|
+
return "number";
|
|
9
|
+
if (t === "integer")
|
|
10
|
+
return "integer";
|
|
11
|
+
if (t === "boolean")
|
|
12
|
+
return "boolean";
|
|
13
|
+
if (t === "array")
|
|
14
|
+
return "array";
|
|
15
|
+
if (t === "object")
|
|
16
|
+
return "object";
|
|
17
|
+
return "unknown";
|
|
18
|
+
}
|
|
19
|
+
export function getSchemaFormat(schema) {
|
|
20
|
+
if (!schema || typeof schema !== "object")
|
|
21
|
+
return undefined;
|
|
22
|
+
const f = schema.format;
|
|
23
|
+
return typeof f === "string" ? f : undefined;
|
|
24
|
+
}
|
|
25
|
+
export function getSchemaEnumStrings(schema) {
|
|
26
|
+
if (!schema || typeof schema !== "object")
|
|
27
|
+
return undefined;
|
|
28
|
+
const e = schema.enum;
|
|
29
|
+
if (!Array.isArray(e))
|
|
30
|
+
return undefined;
|
|
31
|
+
// We only surface string enums for now (enough for flag docs + completion).
|
|
32
|
+
const values = e.filter((v) => typeof v === "string");
|
|
33
|
+
return values.length ? values : undefined;
|
|
34
|
+
}
|
|
@@ -27,4 +27,3 @@ export type SchemaOutput = {
|
|
|
27
27
|
export type MinimalSchemaOutput = Pick<SchemaOutput, "schemaVersion" | "openapi" | "spec" | "capabilities" | "commands">;
|
|
28
28
|
export declare function buildSchemaOutput(loaded: LoadedSpec, operations: NormalizedOperation[], planned: PlannedOperation[] | undefined, servers: ServerInfo[], authSchemes: AuthScheme[], commands: CommandModel | undefined, commandsIndex: import("./command-index.js").CommandsIndex | undefined, capabilities: Capabilities): SchemaOutput;
|
|
29
29
|
export declare function toMinimalSchemaOutput(output: SchemaOutput): MinimalSchemaOutput;
|
|
30
|
-
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function buildSchemaOutput(loaded, operations, planned, servers, authSchemes, commands, commandsIndex, capabilities) {
|
|
2
|
+
return {
|
|
3
|
+
schemaVersion: 1,
|
|
4
|
+
openapi: {
|
|
5
|
+
version: loaded.doc.openapi,
|
|
6
|
+
title: loaded.doc.info?.title,
|
|
7
|
+
infoVersion: loaded.doc.info?.version,
|
|
8
|
+
},
|
|
9
|
+
spec: {
|
|
10
|
+
id: loaded.id,
|
|
11
|
+
fingerprint: loaded.fingerprint,
|
|
12
|
+
source: loaded.source,
|
|
13
|
+
},
|
|
14
|
+
capabilities,
|
|
15
|
+
servers,
|
|
16
|
+
authSchemes,
|
|
17
|
+
operations,
|
|
18
|
+
planned,
|
|
19
|
+
commands,
|
|
20
|
+
commandsIndex,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function toMinimalSchemaOutput(output) {
|
|
24
|
+
return {
|
|
25
|
+
schemaVersion: output.schemaVersion,
|
|
26
|
+
openapi: output.openapi,
|
|
27
|
+
spec: output.spec,
|
|
28
|
+
capabilities: output.capabilities,
|
|
29
|
+
commands: output.commands,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { getSchemaEnumStrings } from "./schema-shape.js";
|
|
2
|
+
const HTTP_METHODS = [
|
|
3
|
+
"get",
|
|
4
|
+
"post",
|
|
5
|
+
"put",
|
|
6
|
+
"patch",
|
|
7
|
+
"delete",
|
|
8
|
+
"options",
|
|
9
|
+
"head",
|
|
10
|
+
"trace",
|
|
11
|
+
];
|
|
12
|
+
function extractVariableNames(url) {
|
|
13
|
+
const names = [];
|
|
14
|
+
const re = /\{([^}]+)\}/g;
|
|
15
|
+
while (true) {
|
|
16
|
+
const match = re.exec(url);
|
|
17
|
+
if (!match)
|
|
18
|
+
break;
|
|
19
|
+
names.push(match[1] ?? "");
|
|
20
|
+
}
|
|
21
|
+
return names.map((n) => n.trim()).filter(Boolean);
|
|
22
|
+
}
|
|
23
|
+
function parseServer(raw) {
|
|
24
|
+
const s = raw;
|
|
25
|
+
if (!s || typeof s !== "object")
|
|
26
|
+
return undefined;
|
|
27
|
+
if (typeof s.url !== "string")
|
|
28
|
+
return undefined;
|
|
29
|
+
const variableNames = extractVariableNames(s.url);
|
|
30
|
+
const variables = [];
|
|
31
|
+
const rawVars = s.variables &&
|
|
32
|
+
typeof s.variables === "object" &&
|
|
33
|
+
!Array.isArray(s.variables)
|
|
34
|
+
? s.variables
|
|
35
|
+
: {};
|
|
36
|
+
for (const name of variableNames) {
|
|
37
|
+
const v = rawVars[name];
|
|
38
|
+
const def = v?.default;
|
|
39
|
+
const desc = v?.description;
|
|
40
|
+
variables.push({
|
|
41
|
+
name,
|
|
42
|
+
default: typeof def === "string" ? def : undefined,
|
|
43
|
+
enum: getSchemaEnumStrings(v),
|
|
44
|
+
description: typeof desc === "string" ? desc : undefined,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
url: s.url,
|
|
49
|
+
description: typeof s.description === "string" ? s.description : undefined,
|
|
50
|
+
variables,
|
|
51
|
+
variableNames,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function mergeServers(a, b) {
|
|
55
|
+
const variableNames = [...a.variableNames];
|
|
56
|
+
for (const n of b.variableNames) {
|
|
57
|
+
if (!variableNames.includes(n))
|
|
58
|
+
variableNames.push(n);
|
|
59
|
+
}
|
|
60
|
+
const byName = new Map();
|
|
61
|
+
for (const v of a.variables)
|
|
62
|
+
byName.set(v.name, { ...v });
|
|
63
|
+
for (const v of b.variables) {
|
|
64
|
+
const existing = byName.get(v.name);
|
|
65
|
+
if (!existing) {
|
|
66
|
+
byName.set(v.name, { ...v });
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
byName.set(v.name, {
|
|
70
|
+
name: v.name,
|
|
71
|
+
default: existing.default ?? v.default,
|
|
72
|
+
enum: existing.enum ?? v.enum,
|
|
73
|
+
description: existing.description ?? v.description,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
url: a.url,
|
|
78
|
+
description: a.description ?? b.description,
|
|
79
|
+
variableNames,
|
|
80
|
+
variables: variableNames
|
|
81
|
+
.map((n) => byName.get(n))
|
|
82
|
+
.filter(Boolean),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export function listServers(doc) {
|
|
86
|
+
const rawServers = [];
|
|
87
|
+
// OpenAPI 3.x allows servers at the root, per-path-item, and per-operation.
|
|
88
|
+
if (Array.isArray(doc.servers))
|
|
89
|
+
rawServers.push(...doc.servers);
|
|
90
|
+
const paths = doc.paths;
|
|
91
|
+
if (paths && typeof paths === "object" && !Array.isArray(paths)) {
|
|
92
|
+
for (const rawPathItem of Object.values(paths)) {
|
|
93
|
+
if (!rawPathItem || typeof rawPathItem !== "object")
|
|
94
|
+
continue;
|
|
95
|
+
const pathItem = rawPathItem;
|
|
96
|
+
if (Array.isArray(pathItem.servers))
|
|
97
|
+
rawServers.push(...pathItem.servers);
|
|
98
|
+
for (const method of HTTP_METHODS) {
|
|
99
|
+
const op = pathItem[method];
|
|
100
|
+
if (!op || typeof op !== "object")
|
|
101
|
+
continue;
|
|
102
|
+
const opObj = op;
|
|
103
|
+
if (Array.isArray(opObj.servers))
|
|
104
|
+
rawServers.push(...opObj.servers);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const out = [];
|
|
109
|
+
const byUrl = new Map();
|
|
110
|
+
for (const raw of rawServers) {
|
|
111
|
+
const parsed = parseServer(raw);
|
|
112
|
+
if (!parsed)
|
|
113
|
+
continue;
|
|
114
|
+
const existing = byUrl.get(parsed.url);
|
|
115
|
+
if (!existing) {
|
|
116
|
+
byUrl.set(parsed.url, parsed);
|
|
117
|
+
out.push(parsed);
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const merged = mergeServers(existing, parsed);
|
|
121
|
+
byUrl.set(parsed.url, merged);
|
|
122
|
+
const idx = out.findIndex((s) => s.url === parsed.url);
|
|
123
|
+
if (idx !== -1)
|
|
124
|
+
out[idx] = merged;
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
export function getDefaultServerUrl(doc) {
|
|
129
|
+
return listServers(doc)[0]?.url;
|
|
130
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { listServers } from "./server.js";
|
|
3
|
+
describe("listServers", () => {
|
|
4
|
+
test("extracts server variables from template", () => {
|
|
5
|
+
const doc = {
|
|
6
|
+
openapi: "3.0.3",
|
|
7
|
+
servers: [
|
|
8
|
+
{
|
|
9
|
+
url: "https://{region}.api.example.com/{basePath}",
|
|
10
|
+
variables: {
|
|
11
|
+
region: {
|
|
12
|
+
default: "us",
|
|
13
|
+
enum: ["us", "eu"],
|
|
14
|
+
},
|
|
15
|
+
basePath: {
|
|
16
|
+
default: "v1",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
};
|
|
22
|
+
const servers = listServers(doc);
|
|
23
|
+
expect(servers).toHaveLength(1);
|
|
24
|
+
expect(servers[0]?.variableNames).toEqual(["region", "basePath"]);
|
|
25
|
+
expect(servers[0]?.variables.map((v) => v.name)).toEqual([
|
|
26
|
+
"region",
|
|
27
|
+
"basePath",
|
|
28
|
+
]);
|
|
29
|
+
expect(servers[0]?.variables[0]?.enum).toEqual(["us", "eu"]);
|
|
30
|
+
});
|
|
31
|
+
test("includes servers defined on paths and operations", () => {
|
|
32
|
+
const doc = {
|
|
33
|
+
openapi: "3.0.3",
|
|
34
|
+
paths: {
|
|
35
|
+
"/v1/forecast": {
|
|
36
|
+
servers: [{ url: "https://api.a.example.com" }],
|
|
37
|
+
get: {
|
|
38
|
+
servers: [{ url: "https://api.b.example.com" }],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
const servers = listServers(doc);
|
|
44
|
+
expect(servers.map((s) => s.url)).toEqual([
|
|
45
|
+
"https://api.a.example.com",
|
|
46
|
+
"https://api.b.example.com",
|
|
47
|
+
]);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import SwaggerParser from "@apidevtools/swagger-parser";
|
|
2
|
+
import { sha256Hex } from "./crypto.js";
|
|
3
|
+
import { parseYamlContent } from "./runtime/compat.js";
|
|
4
|
+
import { getSpecId } from "./spec-id.js";
|
|
5
|
+
import { stableStringify } from "./stable-json.js";
|
|
6
|
+
function isProbablyUrl(input) {
|
|
7
|
+
return /^https?:\/\//i.test(input);
|
|
8
|
+
}
|
|
9
|
+
function parseSpecText(text) {
|
|
10
|
+
const trimmed = text.trimStart();
|
|
11
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
12
|
+
return JSON.parse(text);
|
|
13
|
+
}
|
|
14
|
+
return parseYamlContent(text);
|
|
15
|
+
}
|
|
16
|
+
export async function loadSpec(options) {
|
|
17
|
+
const { spec, embeddedSpecText } = options;
|
|
18
|
+
let source;
|
|
19
|
+
let inputForParser;
|
|
20
|
+
if (typeof embeddedSpecText === "string") {
|
|
21
|
+
source = "embedded";
|
|
22
|
+
inputForParser = parseSpecText(embeddedSpecText);
|
|
23
|
+
}
|
|
24
|
+
else if (spec) {
|
|
25
|
+
source = isProbablyUrl(spec) ? "url" : "file";
|
|
26
|
+
inputForParser = spec;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
throw new Error("Missing spec. Provide --spec <url|path> or build with an embedded spec.");
|
|
30
|
+
}
|
|
31
|
+
const doc = (await SwaggerParser.dereference(
|
|
32
|
+
// biome-ignore lint/suspicious/noExplicitAny: unknown
|
|
33
|
+
inputForParser));
|
|
34
|
+
if (!doc || typeof doc !== "object" || typeof doc.openapi !== "string") {
|
|
35
|
+
throw new Error("Loaded spec is not a valid OpenAPI document");
|
|
36
|
+
}
|
|
37
|
+
const fingerprint = await sha256Hex(stableStringify(doc));
|
|
38
|
+
const id = getSpecId({ doc, fingerprint });
|
|
39
|
+
return { source, id, fingerprint, doc };
|
|
40
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function stableStringify(value, options) {
|
|
2
|
+
const visiting = new WeakSet();
|
|
3
|
+
return JSON.stringify(sort(value, visiting), null, options?.space);
|
|
4
|
+
}
|
|
5
|
+
function sort(value, visiting) {
|
|
6
|
+
if (value === null)
|
|
7
|
+
return null;
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
if (visiting.has(value))
|
|
10
|
+
return { __specli_circular: true };
|
|
11
|
+
visiting.add(value);
|
|
12
|
+
const out = value.map((v) => sort(v, visiting));
|
|
13
|
+
visiting.delete(value);
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
if (typeof value === "object") {
|
|
17
|
+
if (visiting.has(value))
|
|
18
|
+
return { __specli_circular: true };
|
|
19
|
+
visiting.add(value);
|
|
20
|
+
const obj = value;
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const key of Object.keys(obj).sort()) {
|
|
23
|
+
out[key] = sort(obj[key], visiting);
|
|
24
|
+
}
|
|
25
|
+
visiting.delete(value);
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function kebabCase(input) {
|
|
2
|
+
const trimmed = input.trim();
|
|
3
|
+
if (!trimmed)
|
|
4
|
+
return "";
|
|
5
|
+
// Convert spaces/underscores/dots to dashes, split camelCase.
|
|
6
|
+
return trimmed
|
|
7
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1-$2")
|
|
8
|
+
.replace(/[\s_.:/]+/g, "-")
|
|
9
|
+
.replace(/[^a-zA-Z0-9-]/g, "-")
|
|
10
|
+
.replace(/-+/g, "-")
|
|
11
|
+
.replace(/^-|-$/g, "")
|
|
12
|
+
.toLowerCase();
|
|
13
|
+
}
|
|
14
|
+
export function titleCase(input) {
|
|
15
|
+
return input
|
|
16
|
+
.split(/\s+/g)
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.map((w) => w[0]?.toUpperCase() + w.slice(1))
|
|
19
|
+
.join(" ");
|
|
20
|
+
}
|
package/dist/cli.d.ts
CHANGED