Skip to content

Commit

Permalink
feat: iso-web supports signals and crypto
Browse files Browse the repository at this point in the history
  • Loading branch information
hugomrdias committed Sep 5, 2023
1 parent d9e2cf7 commit 0a0da99
Show file tree
Hide file tree
Showing 13 changed files with 320 additions and 5 deletions.
3 changes: 2 additions & 1 deletion .github/release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"packages/iso-did": {},
"packages/iso-signatures": {},
"packages/iso-filecoin": {},
"packages/iso-passkeys": {}
"packages/iso-passkeys": {},
"packages/iso-web": {}
}
}
3 changes: 2 additions & 1 deletion .github/release-please-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"packages/iso-filecoin": "2.1.0",
"packages/iso-passkeys": "0.2.1",
"packages/iso-did": "1.3.2",
"packages/iso-signatures": "0.1.9"
"packages/iso-signatures": "0.1.9",
"packages/iso-web": "0.0.1"
}
34 changes: 34 additions & 0 deletions .github/workflows/iso-web.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: iso-web
env:
CI: true
FORCE_COLOR: 1
on:
push:
branches:
- main
paths:
- 'packages/iso-web/**'
- '.github/workflows/iso-web.yml'
- 'pnpm-lock.yaml'
pull_request:
paths:
- 'packages/iso-web/**'
- '.github/workflows/iso-web.yml'
- 'pnpm-lock.yaml'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: pnpm/action-setup@v2.4.0
with:
version: 8
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'pnpm'
- run: pnpm install
- run: pnpm run build
- run: pnpm -r --filter iso-web run lint
- run: pnpm -r --filter iso-web run test
- run: pnpm -r --filter iso-web exec depcheck
6 changes: 4 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ jobs:
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-filecoin') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-passkeys') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-did') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-signatures')
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-signatures') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-web')
runs-on: ubuntu-latest
permissions:
contents: read
Expand All @@ -56,5 +57,6 @@ jobs:
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-filecoin') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-passkeys') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-did') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-signatures')
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-signatures') ||
contains(fromJson(needs.release.outputs.paths_released), 'packages/iso-web')
uses: './.github/workflows/reusable-docs.yml'
104 changes: 104 additions & 0 deletions packages/iso-web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"name": "iso-web",
"version": "0.0.1",
"description": "Isomorphic Web APIs",
"author": "Hugo Dias <hugomrdias@gmail.com> (hugodias.me)",
"license": "MIT",
"repository": {
"url": "hugomrdias/iso-repo",
"directory": "packages/iso-web"
},
"homepage": "https://github.com/hugomrdias/iso-repo/tree/master/packages/iso-web",
"bugs": {
"url": "https://github.com/hugomrdias/iso-repo/issues"
},
"keywords": [
"web apis",
"crypto",
"webcrypto",
"abortcontroller",
"abortsignal"
],
"type": "module",
"main": "src/index.js",
"types": "dist/src/index.d.ts",
"browser": {
"crypto": false,
"./src/crypto.js": "./src/crypto-browser.js"
},
"exports": {
".": {
"types": "./dist/src/index.d.ts",
"default": "./src/index.js"
},
"./crypto": {
"types": "./dist/src/crypto.d.ts",
"node": "./src/crypto.js",
"react-native": "./src/crypto-browser.js",
"default": "./src/crypto-browser.js"
},
"./signals": {
"types": "./dist/src/signals.d.ts",
"default": "./src/signals.js"
}
},
"typesVersions": {
"*": {
"crypto": [
"dist/src/crypto"
],
"signals": [
"dist/src/signals"
]
}
},
"files": [
"src",
"dist/src/*.d.ts",
"dist/src/*.d.ts.map"
],
"scripts": {
"lint": "tsc --build && eslint '**/*.{js,ts}' && prettier --check '**/*.{js,ts,yml,json}' --ignore-path ../../.gitignore",
"test": "tsc --build && pnpm run test:node && pnpm run test:browser",
"test:node": "playwright-test 'test/**/!(*.browser).test.js' --mode node",
"test:browser": "playwright-test 'test/**/!(*.node).test.js'"
},
"devDependencies": {
"@types/assert": "^1.5.6",
"@types/mocha": "^10.0.1",
"@types/node": "^20.4.3",
"assert": "^2.0.0",
"delay": "^6.0.0",
"hd-scripts": "^7.0.0",
"mocha": "^10.2.0",
"playwright-test": "^12.2.0",
"typescript": "5.1.6"
},
"publishConfig": {
"provenance": true
},
"eslintConfig": {
"extends": [
"../../node_modules/hd-scripts/eslint/index.js"
],
"parserOptions": {
"project": "./tsconfig.json"
},
"env": {
"mocha": true
},
"ignorePatterns": [
"dist"
]
},
"depcheck": {
"specials": [
"bin"
],
"ignores": [
"@types/*",
"hd-scripts",
"assert"
]
}
}
13 changes: 13 additions & 0 deletions packages/iso-web/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# iso-web [![NPM Version](https://img.shields.io/npm/v/iso-web.svg)](https://www.npmjs.com/package/iso-web) [![License](https://img.shields.io/npm/l/iso-web.svg)](https://github.com/hugomrdias/iso-repo/blob/main/license) [![iso-web](https://github.com/hugomrdias/iso-repo/actions/workflows/iso-web.yml/badge.svg)](https://github.com/hugomrdias/iso-repo/actions/workflows/iso-web.yml)

> Isomorphic web apis.
## Install

```bash
pnpm install iso-web
```

## License

MIT © [Hugo Dias](http://hugodias.me)
14 changes: 14 additions & 0 deletions packages/iso-web/src/crypto-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const webcrypto = globalThis.crypto

/**
* Secure PRNG - Random bytes from webcrypto
*
* @param {number} length
*/
export function randomBytes(length = 32) {
if (globalThis.crypto) {
return globalThis.crypto.getRandomValues(new Uint8Array(length))
} else {
throw new Error("The environment doesn't have randomBytes function")
}
}
21 changes: 21 additions & 0 deletions packages/iso-web/src/crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Isomorphic web crypto and random bytes exports
*
* @module
*/
import crypto from 'crypto'

export const webcrypto = /** @type {Crypto} */ (crypto.webcrypto)

/**
* Secure PRNG - Random bytes from webcrypto
*
* @param {number} length
*/
export function randomBytes(length = 32) {
if (crypto.webcrypto) {
return crypto.webcrypto.getRandomValues(new Uint8Array(length))
} else {
throw new Error("The environment doesn't have randomBytes function")
}
}
2 changes: 2 additions & 0 deletions packages/iso-web/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * as Crypto from './crypto.js'
export * as Signals from './signals.js'
31 changes: 31 additions & 0 deletions packages/iso-web/src/signals.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Isomorphic signals
*
* @module
*/

/**
* Combines an array of AbortSignals into a single signal that is aborted when any signal is
*
* @param {Iterable<AbortSignal>} signals
* @returns {AbortSignal}
*/
export function anySignal(signals) {
const controller = new AbortController()

for (const signal of signals) {
if ('aborted' in signal && 'reason' in signal) {
if (signal.aborted) {
controller.abort(signal.reason)
return signal
}

signal.addEventListener('abort', () => controller.abort(signal.reason), {
signal: controller.signal,
once: true,
})
}
}

return controller.signal
}
75 changes: 75 additions & 0 deletions packages/iso-web/test/signals.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { assert, test } from 'playwright-test/taps'
import { anySignal } from '../src/signals.js'
import delay from 'delay'

test('should abort from any signal', async (t) => {
const controllers = Array.from({ length: 5 }).map(() => new AbortController())
const signals = controllers.map((c) => c.signal)
const signal = anySignal(signals)

assert.equal(signal.aborted, false)

controllers[0].abort()

assert.equal(signal.aborted, true)
assert.equal(signals[0].aborted, true)
assert.equal(signals[1].aborted, false)
assert.equal(signals[2].aborted, false)
})

test('should ignore non signal', async (t) => {
const controllers = Array.from({ length: 5 }).map(() => new AbortController())
const signals = controllers.map((c) => c.signal)
// @ts-ignore
const signal = anySignal([...signals, { reason: 'test' }])

assert.equal(signal.aborted, false)

controllers[0].abort()

assert.equal(signal.aborted, true)
})

test('should abort only once', async (t) => {
const controllers = Array.from({ length: 5 }).map(() => new AbortController())
const signals = controllers.map((c) => c.signal)
const signal = anySignal(signals)

let count = 0
signal.addEventListener('abort', () => {
count++
})

for (const controller of controllers) {
controller.abort()
}

for (const controller of controllers) {
controller.abort()
}

await delay(100)

assert.equal(count, 1)

assert.equal(signal.aborted, true)
})

test('should abort with an AbortSignal.timeout', async (t) => {
const controllers = Array.from({ length: 5 }).map(() => new AbortController())
const signals = controllers.map((c) => c.signal)
const signal = anySignal([...signals, AbortSignal.timeout(100)])

await delay(101)

assert.equal(signal.reason.name, 'TimeoutError')
assert.equal(signal.aborted, true)
})

test('should abort if any signal already aborted', (t) => {
const controllers = Array.from({ length: 5 }).map(() => new AbortController())
const signals = controllers.map((c) => c.signal)
const signal = anySignal([...signals, AbortSignal.abort('test')])

assert.equal(signal.aborted, true)
})
16 changes: 16 additions & 0 deletions packages/iso-web/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"lib": ["ESNext", "DOM"],
"emitDeclarationOnly": true
},
"include": ["src", "scripts", "test", "package.json"],
"exclude": ["node_modules", "dist", "out"],
"typedocOptions": {
"entryPoints": ["src/signals.js", "src/crypto.js"],
"includeVersion": true,
"excludeExternals": true,
"internalModule": "<internal>"
}
}
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"packages/iso-filecoin",
"packages/iso-passkeys",
"packages/iso-did",
"packages/iso-signatures"
"packages/iso-signatures",
"packages/iso-web"
],
"excludeExternals": true,
"darkHighlightTheme": "github-dark",
Expand Down

0 comments on commit 0a0da99

Please sign in to comment.