
Research
Supply Chain Attack on Axios Pulls Malicious Dependency from npm
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.
Convert strings between snake_case, camelCase, and kebab-case. Convert object keys and array values with ease.
Convert strings from
snake_casetocamelCaseorkebab-caseand vice versa. Transform array values and objects keys, including nested. Uprade your legacy API to camelCase with no fuss. :herb:
The library aims to quickly wrap older APIs so you can use convenient and consistent camelCase on the Client while keeping snake_case on the BE & HTTP. Whenever API refactoring is not possible or desirable KeCaSn comes to rescue!
$ yarn add kecasn
or
$ npm install kecasn
import {pipe, fromCamelCase, toSnakeCase} from "kecasn"
const snakifyStr = pipe(fromCamelCase, toSnakeCase)
snakifyStr("postTitle:asc")
// "post_title:asc"
convertData(snakifyStr, {values: true})(["TypeScript"])
// ["type-script"]
convertData(snakifyStr, {keys: true})({post_tags: ["TypeScript"]})
// {postTags: ["TypeScript"]}
Many older APIs still consume and produce data in snake_case format. Tolerating the problem is not an option as from the fetching layer snake_case quickly spreads to form field names, validators, etc. You'll end up having case conversions all over the place and you'll have to constantly think which case to use where. Full API rewrite is a great but very expensive option.
An alternative (and proposed) approach is to lock snake_case and all related conversions in a single
place: in the API handling layer. Unless your req/resp data is huge (so it's processing becomes expensive)
this approach will result in more consistent and readable code. Kebab-oriented converters may be useful for CSS-in-JS libraries.
Think of converting (React/Vue/...) component props to CSS rules.
lib/api/fetchers.ts
import {pipe, fromSnakeCase, fromCamelCase, toSnakeCase, toCamelCase, convertData} from "kecasn"
// string -> string
const snakifyStr = pipe(fromCamelCase, toSnakeCase) // Until JS natively supports `|>` pipeline operator
const camelizeStr = pipe(fromSnakeCase, toCamelCase) // ...
// unknown -> unknown
const snakifyKeys = convertData(snakifyStr, {keys: true}) // values are not converted
const snakifyValues = convertData(snakifyStr, {values: true}) // keys are not converted
const camelizeKeys = convertData(camelizeStr, {keys: true}) // values are not converted
export const fetchPosts = async (query : FetchPostsQuery) : Promise<FetchPostsResult> => {
const result = await fetchAPI(["SEARCH", "/api/posts"], {
body: { // FE -> BE
fields: snakifyValues(query.fields), // ["postTitle"] -> ["post_title"]
where: snakifyKeys(query.where), // {postTags: ["TypeScript"]} -> {post_tags: ["TypeScript"]}
order: snakifyValues(query.order), // ["postTitle:asc"] -> ["post_title:asc"]
page: query.page || 1,
limit: query.limit || 10
},
})
return {
...result, // BE -> FE
models: camelizeKeys(result.data), // [{post_title: "Some Title"}] -> [{postTitle: "Some Title"}]
}
}
Note that camelizeData and snakifyData are unable to guess the output type: renaming object props
is a type change in general. It's not a problem whenever unknown is isolated within a strictly typed
function (like the above fetchPosts).
Let's iterate over the above example one more time. The following call:
const posts = await fetchPosts({
fields: ["id", "postTitle"],
where: {postTags: ["TypeScript"]},
order: ["postTitle:asc"],
limit: 2
})
will cause the following HTTP exchange:
-> SEARCH /api/posts
{
"fields": ["id", post_title"], // values are snakified!
"where": {post_tags: ["TypeScript"]}, // keys are snakified, values are unchanged!
"order": ["post_title:asc"], // values are snakified!
"limit": 2
}
<- 200 OK
{
"models": [
{"id": 1, "post_title": "First Post"},
{"id": 2, "post_title": "Second Post"}
]
"total": 24
}
and the following value of posts variable:
[
{id: 1, postTitle: "First Post"}, // keys are camelized, values are unchanged!
{id: 2, postTitle: "Second Post"} // keys are camelized, values are unchanged!
]
API consists of several low-level functions like toCamelCase or fromSnakeCase and one high-level function convertData.
Low-level case-casting functions work with (accept or return) words i.e. space separated strings.
This common format is necesary to reduce the number of converting functions (explained below).
Each toXyz function accepts words and produces a cased string.
toCamelCase : (s : string) => stringtoCamelCase("foo bar") // "fooBar" -- words are formatted (to camelCase)
toCamelCase("foo:bar") // "foo:bar" -- /
toSnakeCase : (s : string) => stringtoSnakeCase("foo bar") // "foo_bar" -- words are formatted (to snake_case)
toSnakeCase("foo bar") // "foo:bar" -- /
toKebabCase : (s : string) => stringtoKebabCase("foo bar") // "foo-bar" -- words are formatted (to kebab-case)
toKebabCase("foo:bar") // "foo:bar" -- /
Each fromXyz function expects a cased string and produces words.
fromCamelCase : (s : string) => stringfromCamelCase("fooBar") // "foo bar" -- camelCase is parsed (to words)
fromCamelCase("foo_bar") // "foo:bar" -- /
fromSnakeCase : (s : string) => stringfromSnakeCase("foo_bar_baz") // "foo bar baz" -- snake_case is parsed (to words)
fromSnakeCase("foo:bar") // "foo:bar" -- /
fromKebabCase : (s : string) => stringfromKebabCase("some-css-rule") // "some css rule" -- kebab-case is parsed (to words)
fromKebabCase("some:css") // "some:css" -- /
pipe : <X, Y, Z>(fn1 : (x : X) => Y, fn2 : (y : Y) => Z) => (x : X) : ZJust a tiny helper function to compose fn1 and fn2. pipe(fn1, fn2) is the same as fn2(fn1).
Useful to avoid extra declarations and typings in the absence of native JS operator(s).
convertData : (convertStr : ConvertStr, options : Options = {}) => (x : unknown) => unknownWhere ConvertStr and Options are:
type ConvertStr = (s : string) => string
type Options = {keys ?: false, values ?: false} // both default to `false`
Example:
const uppercase = (x : string) : string => x.toUpperCase()
convertData(uppercase, {values: true})("fooBar") // "FOOBAR"
convertData(uppercase, {values: true})(["fooBar"]) // ["FOOBAR"]
convertData(uppercase, {values: true})({my_tags: ["fooBar"]}) // {my_tags: ["FOOBAR"]}
convertData(uppercase, {keys: true})({my_tags: ["fooBar"]}) // {MYTAGS: ["fooBar"]}
More realistic example was given above.
fromSepCase, toSepCaseUndocumented (but exported) functions used to build fromSnakeCase, toKebabCase etc.
Use them if you need a different separator.
fromCamel, toCamel, fromSnake, toSnake is an objectively better design than camelToSnake, snakeToCamel
Compare the following possible design (which was chosen):
fromX, toX, fromY, toY, fromZ, toZ
---
Number of functions Z1 equals:
2 * N where N is the № of supported cases
N=2 => 2 * 2 = 4
N=3 => 2 * 3 = 6
N=4 => 2 * 4 = 8
N=5 => 2 * 5 = 10
and the following (which was rejected):
fromXtoY, fromXtoZ, fromYtoX, fromYtoZ, fromZtoX, fromZtoY
---
Number of functions Z2 equals:
N ^ 2 - N where N is the № of supported cases
N = 2 => 2 ^ 2 - 2 = 2 (for N = 2 we get just 2 functions camelToSnake, snakeToCamel)
N = 3 => 3 ^ 2 - 3 = 6 (equals Z1)
N = 4 => 4 ^ 2 - 4 = 12 (and it starts to...)
N = 5 => 5 ^ 2 - 5 = 20 (proliferate... Just 5 supported cases require twice as much functions!)
For now we support just snake, kebab and camel so both approaches are equivalent (in terms
of function №). But you got the point.
FAQs
Convert strings between snake_case, camelCase, and kebab-case. Convert object keys and array values with ease.
We found that kecasn demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
A supply chain attack on Axios introduced a malicious dependency, plain-crypto-js@4.2.1, published minutes earlier and absent from the project’s GitHub releases.

Research
Malicious versions of the Telnyx Python SDK on PyPI delivered credential-stealing malware via a multi-stage supply chain attack.

Security News
TeamPCP is partnering with ransomware group Vect to turn open source supply chain attacks on tools like Trivy and LiteLLM into large-scale ransomware operations.