Skip to content

Commit ccc89fe

Browse files
authored
Firefox & Safari support (#163)
1 parent 74a3fdc commit ccc89fe

37 files changed

+1827
-2286
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ module.exports = {
6464
comments: 200,
6565
},
6666
],
67-
'max-lines': ['error', 350],
67+
'max-lines': ['error', 375],
6868
// "newline-per-chained-call": ["error", { "ignoreChainWithDepth": 2 }],
6969
'no-bitwise': [
7070
'error',

.vscode/settings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"cSpell.words": [
33
"Hacky",
4+
"Ponyfill",
45
"bowser",
56
"browserstack",
67
"buffer",
@@ -69,5 +70,8 @@
6970
"webm",
7071
"zh"
7172
],
72-
"typescript.tsdk": "node_modules/typescript/lib"
73+
"typescript.tsdk": "node_modules/typescript/lib",
74+
"editor.formatOnSave": true,
75+
"prettier.packageManager": "yarn",
76+
"editor.defaultFormatter": "esbenp.prettier-vscode"
7377
}

README.md

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,19 @@
5050

5151
## Compatibility
5252

53-
| | .decrypt | .encrypt |
54-
| ------- | -------: | -------: |
55-
| Chrome |||
56-
| Safari |||
57-
| Edge |||
58-
| Firefox |||
53+
| | .decrypt | .encrypt | .saveZip |
54+
| -------- | -------: | -------: | -------: |
55+
| Chrome ||||
56+
| Edge >18 ||||
57+
| Safari | 🟡 | 🟡 | 🟡 |
58+
| Firefox | 🟡 | 🟡 | 🟡 |
59+
| Edge 18 ||||
60+
61+
✅ = Full support
62+
63+
🟡 = 32 MiB limit
64+
65+
❌ = No support
5966

6067
## Usage
6168

@@ -110,7 +117,10 @@ penumbra.encrypt(options: PenumbraEncryptionOptions, ...files: PenumbraFile[]):
110117
size = 4096 * 128;
111118
addEventListener('penumbra-progress', (e) => console.log(e.type, e.detail));
112119
addEventListener('penumbra-complete', (e) => console.log(e.type, e.detail));
113-
file = penumbra.encrypt(null, { stream: new Uint8Array(size), size });
120+
file = penumbra.encrypt(null, {
121+
stream: new Response(new Uint8Array(size)).body,
122+
size,
123+
});
114124
data = [];
115125
file.then(async ([encrypted]) => {
116126
console.log('encryption complete');
@@ -135,13 +145,12 @@ penumbra.decrypt(options: PenumbraDecryptionInfo, ...files: PenumbraEncryptedFil
135145
```
136146

137147
```ts
138-
const { intoStream } = self;
139148
const te = new TextEncoder();
140149
const td = new TextDecoder();
141150
const data = te.encode('test');
142151
const { byteLength: size } = data;
143152
const [encrypted] = await penumbra.encrypt(null, {
144-
stream: intoStream(data),
153+
stream: data,
145154
size,
146155
});
147156
const options = await penumbra.getDecryptionInfo(encrypted);

babel.config.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,5 @@ module.exports = {
1414
},
1515
],
1616
],
17-
plugins: [
18-
'@babel/plugin-proposal-class-properties',
19-
'@babel/plugin-proposal-object-rest-spread',
20-
],
17+
plugins: ['@babel/plugin-proposal-class-properties'],
2118
};

src/demo/demo.js renamed to demo/demo.js

Lines changed: 83 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
/* eslint-disable max-lines */
22
/* eslint-disable @typescript-eslint/explicit-function-return-type */
3-
/* eslint-disable max-lines */
43
const view = self;
54

65
const tests = [];
6+
const results = [];
77
let failures = 0;
88

99
/**
@@ -45,6 +45,12 @@ const onReady = async (
4545
[
4646
'penumbra.get() and penumbra.getTextOrURI() test (no credentials)',
4747
async () => {
48+
if (!self.TextEncoder) {
49+
console.warn(
50+
'skipping test due to lack of browser support for TextEncoder',
51+
);
52+
return false;
53+
}
4854
const cacheBuster = Math.random()
4955
.toString(10)
5056
.slice(2);
@@ -65,7 +71,7 @@ const onReady = async (
6571
} = await penumbra.getTextOrURI(await penumbra.get(NYT))[0];
6672
const test1Hash = await hash(
6773
'SHA-256',
68-
new TextEncoder().encode(test1Text),
74+
new self.TextEncoder().encode(test1Text),
6975
);
7076
const ref1Hash =
7177
'4933a43366fdda7371f02bb2a7e21b38f23db88a474b9abf9e33309cd15594d5';
@@ -259,6 +265,50 @@ const onReady = async (
259265
});
260266
},
261267
],
268+
[
269+
'preconnect',
270+
async (t) => {
271+
const measurePreconnects = () =>
272+
document.querySelectorAll('link[rel="preconnect"]').length;
273+
const start = measurePreconnects();
274+
const cleanup = penumbra.preconnect({
275+
url: 'https://s3-us-west-2.amazonaws.com/bencmbrook/NYT.txt.enc',
276+
filePrefix: 'NYT',
277+
mimetype: 'text/plain',
278+
decryptionOptions: {
279+
key: 'vScyqmJKqGl73mJkuwm/zPBQk0wct9eQ5wPE8laGcWM=',
280+
iv: '6lNU+2vxJw6SFgse',
281+
authTag: 'gadZhS1QozjEmfmHLblzbg==',
282+
},
283+
});
284+
const after = measurePreconnects();
285+
cleanup();
286+
t.assert(start < after);
287+
t.end();
288+
},
289+
],
290+
[
291+
'preload',
292+
async (t) => {
293+
const measurePreloads = () =>
294+
document.querySelectorAll('link[rel="preload"]').length;
295+
const start = measurePreloads();
296+
const cleanup = penumbra.preload({
297+
url: 'https://s3-us-west-2.amazonaws.com/bencmbrook/NYT.txt.enc',
298+
filePrefix: 'NYT',
299+
mimetype: 'text/plain',
300+
decryptionOptions: {
301+
key: 'vScyqmJKqGl73mJkuwm/zPBQk0wct9eQ5wPE8laGcWM=',
302+
iv: '6lNU+2vxJw6SFgse',
303+
authTag: 'gadZhS1QozjEmfmHLblzbg==',
304+
},
305+
});
306+
const after = measurePreloads();
307+
cleanup();
308+
t.assert(start < after);
309+
t.end();
310+
},
311+
],
262312
[
263313
'penumbra.getBlob()',
264314
async () => {
@@ -285,17 +335,18 @@ const onReady = async (
285335
[
286336
'penumbra.encrypt()',
287337
async () => {
288-
if (navigator.userAgent.toLowerCase().includes('firefox')) {
289-
console.error(
290-
'penumbra.encrypt() test skipped for Firefox. TODO: Fix penumbra.encrypt() in Firefox!',
338+
if (!self.TextEncoder || !self.TextDecoder) {
339+
console.warn(
340+
'skipping test due to lack of browser support for TextEncoder/TextDecoder',
291341
);
292-
return true;
342+
return false;
293343
}
294-
const te = new TextEncoder();
295-
const td = new TextDecoder();
344+
const te = new self.TextEncoder();
345+
const td = new self.TextDecoder();
296346
const input = 'test';
297-
const stream = te.encode(input);
298-
const { byteLength: size } = stream;
347+
const buffer = te.encode(input);
348+
const { byteLength: size } = buffer;
349+
const stream = new Response(buffer).body;
299350
const options = null;
300351
const file = {
301352
stream,
@@ -307,7 +358,9 @@ const onReady = async (
307358
const decryptedData = await new Response(
308359
decrypted.stream,
309360
).arrayBuffer();
310-
return td.decode(decryptedData) === input;
361+
const decryptedText = td.decode(decryptedData);
362+
console.log('decrypted text:', decryptedText);
363+
return decryptedText === input;
311364
},
312365
],
313366
[
@@ -317,6 +370,8 @@ const onReady = async (
317370
new Promise(async (resolve) => {
318371
const expectedReferenceHashes = [
319372
'318e197f7df584c339ec6d06490eb9cb3cdbb41c218809690d39d70d79dff48f',
373+
'6cbf553053fcfe8b6c5e17313ef4383fcef4bc0cf3df48c904ed5e7b05af04a6',
374+
'7559c3628a54a498b715edbbb9a0f16fc65e94eaaf185b41e91f6bddf1a8e02e',
320375
];
321376
let progressEventFiredAndWorking = false;
322377
let completeEventFired = false;
@@ -335,6 +390,7 @@ const onReady = async (
335390
allowDuplicates: true,
336391
saveBuffer: true,
337392
});
393+
338394
writer.write(
339395
...(await penumbra.get(
340396
{
@@ -467,19 +523,24 @@ const onReady = async (
467523
const getTestColor = (passed) => (passed ? 'limegreen' : 'crimson');
468524

469525
// eslint-disable-next-line no-restricted-syntax
470-
for await (const [name, test] of tests) {
471-
const passed = await test();
472-
failures += !passed;
473-
console.log(
474-
`%c${
475-
passed ? '✅ PASS' : '❌ FAIL'
476-
} %c${name} (%creturned ${JSON.stringify(passed)}%c)`,
477-
`font-size:larger;color:${getTestColor(passed)}`,
478-
'',
479-
'color:gray',
480-
'',
526+
for (const [name, test] of tests) {
527+
results.push(
528+
// eslint-disable-next-line no-loop-func
529+
test().then((passed) => {
530+
failures += !passed;
531+
console.log(
532+
`%c${
533+
passed ? '✅ PASS' : '❌ FAIL'
534+
} %c${name} (%creturned ${JSON.stringify(passed)}%c)`,
535+
`font-size:larger;color:${getTestColor(passed)}`,
536+
'',
537+
'color:gray',
538+
'',
539+
);
540+
}),
481541
);
482542
}
543+
await Promise.all(results);
483544
console.log(
484545
`%c${
485546
failures
File renamed without changes.

example/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ <h2>The data below is downloaded and decrypted</h2>
3636
<script src="jsAnimation.js"></script>
3737
<script src="files.js"></script>
3838
<script src="displayTable.js"></script>
39-
<script src="./build/penumbra.js" async defer></script>
39+
<script src="penumbra.js" async defer></script>
4040
<script src="index.js"></script>
4141
</body>
4242
</html>

karma.browserstack.js

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,39 @@ const { short } = require('git-rev-sync');
33
const getGlobalConfig = require('./karma.global');
44

55
module.exports = (config) => {
6-
// TODO add more browsers. this util is useful:
7-
// https://www.browserstack.com/automate/capabilities
86
const customLaunchers = {
97
bs_chrome_pc: {
108
base: 'BrowserStack',
119
browser: 'Chrome',
1210
browser_version: '85',
1311
os: 'Windows',
1412
os_version: '10',
13+
device: null,
14+
real_mobile: null,
1515
},
1616
bs_firefox_pc: {
17-
// Skips .encrypt/.saveZip tests
1817
base: 'BrowserStack',
1918
browser: 'Firefox',
2019
browser_version: '80',
2120
os: 'Windows',
2221
os_version: '10',
22+
device: null,
23+
real_mobile: null,
2324
},
24-
bs_safari_mac: {
25-
// Skips .encrypt/.saveZip tests
26-
base: 'BrowserStack',
27-
browser: 'Safari',
28-
browser_version: '13',
29-
os: 'OS X',
30-
os_version: 'Catalina',
31-
},
32-
bs_edge_pc: {
33-
base: 'BrowserStack',
34-
browser: 'Edge',
35-
browser_version: '85',
36-
os: 'Windows',
37-
os_version: '10',
38-
},
25+
/**
26+
* TODO: https://github.com/transcend-io/penumbra/issues/164
27+
* Uncomment this entry once BrowserStack supports Safari 14.
28+
* In the meantime Safari can be tested locally through `yarn start:demo`.
29+
*/
30+
// bs_safari_mac: {
31+
// base: 'BrowserStack',
32+
// browser: 'Safari',
33+
// browser_version: '14',
34+
// os: 'OS X',
35+
// os_version: 'Catalina',
36+
// device: null,
37+
// real_mobile: null,
38+
// },
3939
};
4040

4141
const globalConfig = getGlobalConfig(config);

package.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@transcend-io/penumbra",
3-
"version": "4.17.2",
3+
"version": "4.18.0",
44
"description": "Crypto streams for the browser.",
55
"main": "build/penumbra.js",
66
"types": "ts-build/src/index.d.ts",
@@ -15,11 +15,10 @@
1515
"lint:fix": "eslint . --ext .js,.ts,.tsx,.jsx --fix",
1616
"ts:lint": "tslint --project .",
1717
"####### Start #########": "",
18-
"start": "http-server build -a localhost",
19-
"start:example": "yarn build:example && http-server example",
20-
"start:demo": "http-server build",
18+
"start:example": "yarn build:example && http-server example/build/ -o",
19+
"start:demo": "yarn build:demo && http-server demo/build/ -o",
2120
"####### Testing #######": "",
22-
"test:interactive": "http-server build -a localhost",
21+
"test:interactive": "yarn start:demo",
2322
"test:local": "karma start karma.local.js",
2423
"test:local:rebuild": "yarn && karma start karma.local.js",
2524
"test:browserstack": "karma start karma.browserstack.js",
@@ -29,10 +28,11 @@
2928
"check:deps:interactive": "npm-check -u",
3029
"####### Build #######": "",
3130
"clean": "rimraf build && rimraf coverage",
32-
"build": "yarn clean && mkdir -p build && webpack --config webpack.prod.js && cp src/demo/* build/ && tsc && yarn build:markdown",
31+
"build": "yarn clean && mkdir -p build && webpack --config webpack.prod.js && tsc && yarn build:markdown",
3332
"build:markdown": "markdown-toc -i --bullets '—' README.md && sed -i -e 's/—/-/g' README.md && rm -f README.md-e",
3433
"build:watch": "tsc --watch",
35-
"build:example": "rimraf example/build && cp -a build example",
34+
"build:example": "rimraf example/build && yarn build && mkdir -p demo/build && cp -a example/. build/ && cp -a build/. example/build/",
35+
"build:demo": "rimraf demo/build && yarn build && mkdir -p demo/build && cp -a demo/. build/ && cp -a build/. demo/build/",
3636
"webpack:watch": "yarn clean && webpack --config webpack.dev.js --watch"
3737
},
3838
"repository": {
@@ -58,18 +58,19 @@
5858
],
5959
"homepage": "https://github.com/transcend-io/penumbra#readme",
6060
"dependencies": {
61-
"@transcend-io/conflux": "^3.0.6",
61+
"@transcend-io/conflux": "^3.2.0",
62+
"@transcend-io/remote-web-streams": "1.0.5",
6263
"comlink": "^4.2.0",
6364
"core-js": "^3.6.4",
6465
"crypto-browserify": "^3.12.0",
6566
"file-saver": "^2.0.2",
66-
"into-stream": "^5.1.1",
6767
"mime-types": "^2.1.26",
6868
"promise.allsettled": "^1.0.2",
6969
"remote-web-streams": "0.1.0",
7070
"streamsaver": "^2.0.4",
7171
"typedarray-to-buffer": "^3.1.5",
72-
"web-streams-node": "^0.4.0"
72+
"web-streams-node": "^0.4.0",
73+
"web-streams-polyfill": "^3.0.0"
7374
},
7475
"devDependencies": {
7576
"@babel/core": "^7.8.7",
@@ -85,7 +86,6 @@
8586
"@typescript-eslint/parser": "^2.23.0",
8687
"babel-eslint": "^10.1.0",
8788
"babel-loader": "^8.0.6",
88-
"bowser": "^2.9.0",
8989
"check-deps": "^1.4.1",
9090
"codecov": "^3.6.5",
9191
"eslint": "^6.8.0",

0 commit comments

Comments
 (0)