Skip to content

Commit

Permalink
feat(crypto): added a hook for utilizing the crypto.subtle api
Browse files Browse the repository at this point in the history
- temporarily lock semantic-release to v22
  • Loading branch information
cecilia-sanare committed Jan 14, 2024
1 parent 69d6542 commit 14d464c
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 19 deletions.
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
- name: Build
run: bun run build

- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with:
name: build
path: dist
Expand All @@ -85,20 +85,21 @@ jobs:
needs: [lint, test, build]
steps:
- uses: actions/checkout@v4
- run: git --version
- uses: oven-sh/setup-bun@v1
with:
bun-version: latest

- name: Install Packages
run: bun install --frozen-lockfile

- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: build
path: dist

- name: Deploy
run: bunx semantic-release
run: bunx semantic-release@^22
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
16 changes: 16 additions & 0 deletions .swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"sourceMaps": "inline",
"jsc": {
"target": "es2022",
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": {
"runtime": "automatic"
}
}
}
}
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@ export function MySimpleInput({ className: externalClassName, value: externalVal
}
```

## `useSubtleCrypto`

```tsx
import { useSubtleCrypto } from '@rain-cafe/react-utils';

export type ProfileProps = {
email?: string;
};

export function Profile({ email }: ProfileProps) {
const hashedEmail = useSubtleCrypto('SHA-256', email);

return <img src={`https://gravatar.com/avatar/${hashedEmail}.jpg`} />;
}
```

[_**Want to Contribute?**_](/CONTRIBUTING.md)

[npm-version-image]: https://img.shields.io/npm/v/@rain-cafe/react-utils.svg
Expand Down
Binary file modified bun.lockb
Binary file not shown.
26 changes: 11 additions & 15 deletions jest.config.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import type { JestConfigWithTsJest } from 'ts-jest';
import type { Config } from 'jest';
import { readFileSync } from 'fs';

const jestConfig: JestConfigWithTsJest = {
// Changed sourcemaps to inline resolving issues with https://github.com/swc-project/swc/issues/3854
const config = JSON.parse(readFileSync(`${__dirname}/.swcrc`, 'utf-8'));

export default {
roots: ['<rootDir>/src'],
testEnvironment: 'jsdom',
testEnvironment: '@happy-dom/jest-environment',

transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
tsconfig: 'tsconfig.json',
},
],
'^.+\\.(t|j)sx?$': ['@swc/jest', config],
},

collectCoverageFrom: ['<rootDir>/src/**/*'],

coveragePathIgnorePatterns: ['__tests__'],

setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect'],
};

export default jestConfig;
setupFilesAfterEnv: ['@inrupt/jest-jsdom-polyfills', '@testing-library/jest-dom/extend-expect'],
extensionsToTreatAsEsm: ['.ts', '.tsx'],
} satisfies Config;
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
"classnames": "^2.5.1"
},
"devDependencies": {
"@happy-dom/jest-environment": "^13.0.6",
"@inrupt/jest-jsdom-polyfills": "^3.0.2",
"@swc/jest": "^0.2.29",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
Expand All @@ -39,7 +42,6 @@
"eslint-plugin-react": "^7.33.1",
"eslint-plugin-unused-imports": "^3.0.0",
"jest": "^29.6.2",
"jest-environment-jsdom": "^29.6.2",
"microbundle": "^0.15.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
29 changes: 29 additions & 0 deletions src/hooks/__tests__/use-subtle-crypto.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { render, waitFor } from '@testing-library/react';
import Chance from 'chance';
import { useSubtleCrypto } from '../use-subtle-crypto';
import { hash } from '../../utils/subtle-crypto';

const chance = new Chance();

describe('Crypto Hooks', () => {
describe('hook(useSubtleCrypto)', () => {
type ExampleComponentProps = {
value: string;
};

function ExampleComponent({ value }: ExampleComponentProps) {
const hashedValue = useSubtleCrypto('SHA-256', value);

return <div data-testid="example">{hashedValue}</div>;
}

it('should cache the value', async () => {
const value = chance.string();
const expectedValue = await hash('SHA-256', value);

const component = render(<ExampleComponent value={value} />);

await waitFor(() => expect(component.getByText(expectedValue)).toBeTruthy());
});
});
});
19 changes: 19 additions & 0 deletions src/hooks/use-subtle-crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useEffect, useState } from 'react';
import { Algorithms, hash } from '../utils/subtle-crypto';

/**
* Returns a hashed version of the value provided
* @param algorithm the hashing algorithm to use
* @param value the value to hash
* @returns the hashed value
* @see https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#browser_compatibility
*/
export function useSubtleCrypto(algorithm: Algorithms, value?: string | null): string | undefined {
const [hashedValue, setHashedValue] = useState<string>();

useEffect(() => {
hash(algorithm, value).then(setHashedValue);
}, [algorithm, value]);

return hashedValue;
}
25 changes: 25 additions & 0 deletions src/utils/__tests__/subtle-crypto.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Algorithms, hash } from '../../utils/subtle-crypto';

describe('Crypto Utils', () => {
describe('fn(hash)', () => {
it.each([
['SHA-1', 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3'],
['SHA-256', '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08'],
['SHA-384', '768412320f7b0aa5812fce428dc4706b3cae50e02a64caa16a782249bfe8efc4b7ef1ccb126255d196047dfedf17a0a9'],
[
'SHA-512',
'ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff',
],
])('should support %s', async (algorithm: Algorithms, expectedValue: string) => {
await expect(hash(algorithm, 'test')).resolves.toEqual(expectedValue);
});

it('should support being provided null', async () => {
await expect(hash('SHA-256', null)).resolves.toEqual(null);
});

it('should support being provided undefined', async () => {
await expect(hash('SHA-256', undefined)).resolves.toEqual(null);
});
});
});
16 changes: 16 additions & 0 deletions src/utils/subtle-crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export type Algorithms = 'SHA-1' | 'SHA-256' | 'SHA-384' | 'SHA-512';

export async function hash(algorithm: Algorithms, message?: string): Promise<string> {
if (!message) return null;

const msgBuffer = new TextEncoder().encode(message);

// hash the message
const hashBuffer = await crypto.subtle.digest(algorithm, msgBuffer);

// convert ArrayBuffer to Array
const hashArray = Array.from(new Uint8Array(hashBuffer));

// convert bytes to hex string
return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
}

0 comments on commit 14d464c

Please sign in to comment.