Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/function/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,10 +284,10 @@ If you still need the rendered markup, call `page.content()` inside the function

```js
const compressed = await microlink.compress(({ page }) => page.title())
// 'br#...' on Node.js (brotli), 'lz#...' as fallback
// 'br#...' when brotli is available, 'gz#...' in Chrome
```

Supported prefixes: `lz#` (lz-string), `br#` (brotli), `gz#` (gzip).
Supported prefixes: `br#` (brotli), `gz#` (gzip).

### Optimization checklist

Expand Down
3 changes: 1 addition & 2 deletions packages/function/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@
"serverless"
],
"dependencies": {
"@microlink/mql": "workspace:*",
"lz-ts": "~1.1.2"
"@microlink/mql": "workspace:*"
},
"devDependencies": {
"@rollup/plugin-commonjs": "latest",
Expand Down
35 changes: 24 additions & 11 deletions packages/function/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,30 @@

const mql = require('@microlink/mql')

let toCompress

try {
const { promisify } = require('util')
const { brotliCompress } = require('zlib')
const compress = promisify(brotliCompress)
toCompress = async code =>
`br#${(await compress(code.toString())).toString('base64url')}`
} catch {
const { compressToURI } = require('lz-ts')
toCompress = code => Promise.resolve(`lz#${compressToURI(code.toString())}`)
const format = (() => {
try {
// eslint-disable-next-line no-new
new CompressionStream('brotli')
return 'brotli'
} catch {
return 'gzip'
}
})()

const toBase64url = bytes => {
if (typeof Buffer === 'function') { return Buffer.from(bytes).toString('base64url') }
let binary = ''
for (const byte of bytes) binary += String.fromCharCode(byte)
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
}

const toCompress = async code => {
const stream = new Blob([code.toString()])
.stream()
.pipeThrough(new CompressionStream(format))
const bytes = new Uint8Array(await new Response(stream).arrayBuffer())
const alias = format === 'brotli' ? 'br' : 'gz'
return `${alias}#${toBase64url(bytes)}`
}

const fn = (code, mqlOpts, gotOpts) => {
Expand Down
51 changes: 29 additions & 22 deletions packages/function/test/compress.mjs
Original file line number Diff line number Diff line change
@@ -1,49 +1,56 @@
import { brotliDecompress } from 'zlib'
import { brotliDecompress, gunzip } from 'zlib'
import { createRequire } from 'module'
import { promisify } from 'util'
import test from 'ava'

const require = createRequire(import.meta.url)
const { decompressFromURI, compressToURI } = require('lz-ts')
const fn = require('@microlink/function')

const decompress = promisify(brotliDecompress)
const unzip = promisify(gunzip)
const code = '({ page }) => page.title()'

test('compress is exported', t => {
t.is(typeof fn.compress, 'function')
})

test('node.js selects brotli compression', async t => {
test('selects brotli when CompressionStream supports it', async t => {
const compressed = await fn.compress(code)
t.true(compressed.startsWith('br#'))
})

test('brotli roundtrip produces original code', async t => {
const compressed = await fn.compress(code)
const payload = compressed.slice(3)
const decompressed = (await decompress(Buffer.from(payload, 'base64url'))).toString()
const decompressed = (
await decompress(Buffer.from(payload, 'base64url'))
).toString()
t.is(decompressed, code)
})

test('falls back to lz-string when zlib is unavailable', async t => {
const { execFileSync } = await import('child_process')
const result = execFileSync(process.execPath, ['-e', `
const Module = require('module')
const originalLoad = Module._load
Module._load = function (id, parent, isMain) {
if (id === 'zlib') throw new Error('not available')
return originalLoad(id, parent, isMain)
test('falls back to gzip when brotli is unavailable', async t => {
const { execFileSync } = require('child_process')
const result = execFileSync(
process.execPath,
[
'-e',
`
const OriginalCS = globalThis.CompressionStream
globalThis.CompressionStream = class {
constructor (format) {
if (format === 'brotli') throw new TypeError('unsupported')
return new OriginalCS(format)
}
}
const fn = require(${JSON.stringify(require.resolve('@microlink/function'))})
fn.compress('({ page }) => page.title()').then(r => process.stdout.write(r))
`], { encoding: 'utf8' })
t.true(result.startsWith('lz#'))
})

test('lz roundtrip produces original code', t => {
const compressed = `lz#${compressToURI(code)}`
t.true(compressed.startsWith('lz#'))
const payload = compressed.slice(3)
t.is(decompressFromURI(payload), code)
fn.compress(${JSON.stringify(code)}).then(r => process.stdout.write(r))
`
],
{ encoding: 'utf8' }
)
t.true(result.startsWith('gz#'))
const decompressed = (
await unzip(Buffer.from(result.slice(3), 'base64url'))
).toString()
t.is(decompressed, code)
})