Skip to content
This repository was archived by the owner on Jun 6, 2024. It is now read-only.
Open
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
7 changes: 5 additions & 2 deletions bin/cloudworker.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ program
.option('-d, --debug', 'Debug', false)
.option('-s, --kv-set [variable.key=value]', 'Binds variable to a local implementation of Workers KV and sets key to value', collect, [])
.option('-f, --kv-file [variable=path]', 'Set the filepath for value peristence for the local implementation of Workers KV', collect, [])
.option('-b, --bind [variable=value]', 'Binds variable to the value provided', collect, [])
.option('-a, --bind-file [variable=path]', 'Binds variable to the contents of the given file', collect, [])
.option('-w, --wasm [variable=path]', 'Binds variable to wasm located at path', collect, [])
.option('-c, --enable-cache', 'Enables cache <BETA>', false)
.option('-r, --watch', 'Watch the worker script and restart the worker when changes are detected', false)
Expand All @@ -44,8 +46,9 @@ function run (file, wasmBindings) {
console.log('Starting up...')
const fullpath = path.resolve(process.cwd(), file)
const script = utils.read(fullpath)
const bindings = utils.extractKVBindings(program.kvSet.concat(program.set), program.kvFile)
Object.assign(bindings, wasmBindings)
const kvBindings = utils.extractKVBindings(program.kvSet.concat(program.set), program.kvFile)
const bindings = utils.extractBindings(program.bind, program.bindFile)
Object.assign(bindings, kvBindings, wasmBindings)

// Add a warning log for deprecation
if (program.set.length > 0) console.warn('Warning: Flag --set is now deprecated, please use --kv-set instead')
Expand Down
68 changes: 68 additions & 0 deletions lib/__tests__/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,72 @@ describe('utils', () => {

cb()
})

test('extractKVBindings throws on invalid filepath format', async () => {
expect(() => { utils.extractKVBindings([], ['invalid format']) }).toThrow()
})

test('extractKVBindings handles files and sets', async (cb) => {
const kv = new KeyValueStore(path.resolve('test-kv.json'))
kv.put('hello', 'world')

const bindings = utils.extractKVBindings(['test.this=great'], ['test=test-kv.json'])
const {test} = bindings

fs.unlinkSync(kv.path)
expect(await test.get('hello')).toEqual('world')
expect(await test.get('this')).toEqual('great')

cb()
})

test('extractBindings throws on invalid format', async () => {
expect(() => { utils.extractBindings(['invalid format'], []) }).toThrow()
})

test('extractBindings parses properly', async () => {
const bindings = utils.extractBindings(['foo=bar', 'baz=qux'], [])
expect(bindings.foo).toEqual('bar')
expect(bindings.baz).toEqual('qux')
})

test('extractBindings allows = in value', async () => {
const bindings = utils.extractBindings(['foo="const bar=\'abc\';"'], [])
expect(bindings.foo).toEqual('"const bar=\'abc\';"')
})

test('extractBindings handles file as binding value', async () => {
const content = JSON.stringify({
foo: 'abc',
bar: 12345,
baz: {
qux: ['a', 'b', 'c', 'd'],
plugh: [ { id: 987 }, { id: 876 } ],
},
})
const path = '/tmp/__cloudworker_test.json'
await fs.writeFileSync(path, content)

const bindings = utils.extractBindings([], [`__DATA=${path}`])
expect(bindings.__DATA).toEqual(content)

await fs.unlinkSync(path)
})

test('extractBindings thows on invalid file format', async () => {
expect(() => { utils.extractBindings([], ['invalid file format']) }).toThrow()
})

test('extractBindings throw on nonexistent path', async () => {
expect(() => { utils.extractBindings([], ['foo=/tmp/__cloudworker_fake_file.json']) }).toThrow()
})

test('parseWasmFlags throws on invalid format', async () => {
expect(() => { utils.parseWasmFlags(['invalid format']) }).toThrow()
})

test('parseWasmFlags parses properly', async () => {
const bindings = utils.parseWasmFlags(['foo=bar'])
expect(bindings.foo).toEqual('bar')
})
})
87 changes: 54 additions & 33 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,82 @@ module.exports.read = f => fs.readFileSync(f).toString('utf-8')
module.exports.extractKVBindings = (kvSetFlags, kvFileFlags) => {
const bindings = {}

const filepaths = extractKVPaths(kvFileFlags)
const filepaths = parseFlags('kv-file', kvFileFlags)

for (const [variable, path] of Object.entries(filepaths)) {
bindings[variable] = new KeyValueStore(path)
}

for (const flag of kvSetFlags) {
const comps = flag.split('.')
if (comps.length < 2 || comps[1].split('=').length < 2) {
throw new Error('Invalid kv-set flag format. Expected format of [variable].[key]=[value]')
}

const variable = comps[0]
const kvFragment = comps.slice(1).join('.')
const kvComponents = kvFragment.split('=')
const key = kvComponents[0]
const value = kvComponents.slice(1).join('=')
const kvStores = parseFlags('kv-set', kvSetFlags, true)

for (const [variable, obj] of Object.entries(kvStores)) {
if (!bindings[variable]) {
bindings[variable] = new KeyValueStore(filepaths[variable])
}

bindings[variable].put(key, value)
for (const [key, value] of Object.entries(obj)) {
bindings[variable].put(key, value)
}
}

return bindings
}

const extractKVPaths = (kvFileFlags) => {
const paths = {}
module.exports.extractBindings = (bindingFlags, bindingFileFlags) => {
return Object.assign(
{},
parseFlags('bind', bindingFlags),
parseFlags('bind-file', bindingFileFlags, false, (variable, filepath) => {
if (!fs.existsSync(filepath)) {
throw new Error(`Invalid bind-file path "${filepath}"`)
}

if (!kvFileFlags) return paths
return fs.readFileSync(filepath).toString('utf-8')
})
)
}

for (const flag of kvFileFlags) {
const components = flag.split('=')
module.exports.parseWasmFlags = (wasmFlags) => parseFlags('wasm', wasmFlags)

if (flag.length < 2) {
throw new Error('Invalid kv-file flag format. Expected format of [variable]=[value]')
}
/**
* Parse flags into bindings.
*
* @param {string} type Type of binding being parsed.
* @param {Array<string>} flags Command line flags to parse.
* @param {boolean} objectVariable Whether the variable represents an object name and key
* @param {Function} handleVariable Function to call for custom variable/value handling.
*
* @returns {Object} bindings The variable bindings parsed from the flags.
*/
function parseFlags (type, flags = [], objectVariable = false, handleVariable = null) {
const bindings = {}

paths[components[0]] = components[1]
}
for (const flag of flags) {
if (objectVariable) {
const comps = flag.split('.')
if (comps.length < 2 || comps[1].split('=').length < 2) {
throw new Error(`Invalid ${type} flag format. Expected format of [variable].[key]=[value]`)
}

return paths
}
const variable = comps[0]
const kvFragment = comps.slice(1).join('.')
const kvComponents = kvFragment.split('=')
const key = kvComponents[0]
const value = kvComponents.slice(1).join('=')

module.exports.parseWasmFlags = (wasmFlags) => {
const bindings = {}
for (const flag of wasmFlags) {
const comps = flag.split('=')
if (comps.length !== 2) {
throw new Error('Invalid wasm flag format. Expected format of [variable=path]')
bindings[variable] = Object.assign({}, bindings[variable], { [key]: value })
} else {
const comps = flag.split('=')
if (comps.length < 2) {
throw new Error(`Invalid ${type} flag format. Expected format of [variable]=[value]`)
}

const variable = comps[0]
const value = comps.slice(1).join('=')
if (handleVariable) bindings[variable] = handleVariable(variable, value)
else bindings[variable] = value
}
const [variable, path] = comps
bindings[variable] = path
}

return bindings
}