Repository: cloudflare/serverless-cloudflare-workers
Branch: master
Commit: e93ec702290b
Files: 27
Total size: 68.1 KB
Directory structure:
gitextract_3btj622v/
├── .github/
│ └── workflows/
│ └── semgrep.yml
├── .gitignore
├── LICENSE.md
├── README.md
├── deploy/
│ ├── cloudflareDeploy.js
│ ├── cloudflareDeployFunction.js
│ └── lib/
│ ├── logResponse.js
│ ├── multiscript.js
│ ├── singlescript.js
│ └── workerScript.js
├── index.js
├── invoke/
│ ├── cloudflareInvoke.js
│ └── lib/
│ └── invoke.js
├── package.json
├── provider/
│ ├── cloudflareProvider.js
│ ├── credentials.js
│ └── sdk.js
├── remove/
│ ├── cloudflareRemove.js
│ └── lib/
│ └── singlescript.js
├── shared/
│ ├── accountType.js
│ ├── duplicate.js
│ ├── multiscript.js
│ └── validate.js
├── test/
│ └── shared/
│ ├── duplicate_test.js
│ └── multiscript_test.js
└── utils/
├── index.js
└── webpack.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/semgrep.yml
================================================
on:
pull_request: {}
workflow_dispatch: {}
push:
branches:
- main
- master
schedule:
- cron: '0 0 * * *'
name: Semgrep config
jobs:
semgrep:
name: semgrep/ci
runs-on: ubuntu-20.04
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
SEMGREP_URL: https://cloudflare.semgrep.dev
SEMGREP_APP_URL: https://cloudflare.semgrep.dev
SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version
container:
image: returntocorp/semgrep
steps:
- uses: actions/checkout@v3
- run: semgrep ci
================================================
FILE: .gitignore
================================================
.nyc_output
coverage/
node_modules/
.npmrc
*.DS_Store
================================================
FILE: LICENSE.md
================================================
Copyright 2018 Cloudflare
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================
FILE: README.md
================================================
# serverless-cloudflare-workers
Serverless plugin for [Cloudflare Workers](https://developers.cloudflare.com/workers/)
## Documentation
https://serverless.com/framework/docs/providers/cloudflare/guide/quick-start/
### Bundling with Webpack
You can have the plugin automatically bundle your code into one file using [webpack](https://webpack.js.org/). This is a great solution if you are fine with a no frills bundling.
You can use a single global webpack config to bundle your assets. And this webpack config will be built during the packaging time, before individual functions are prepared. To use this, add `webpackConfig` to your service section in serverless config, with value as the path to the webpack config.
```yaml
service:
name: service-name
webpackConfig: webpack.config #webpack config path without js extension from root folder.
config:
accountId: ${env:CLOUDFLARE_ACCOUNT_ID}
zoneId: ${env:CLOUDFLARE_ZONE_ID}
```
You can also add a function level webpack configuration in addition to a global webpack configuration. This helps you to process bundling different for an individual function than the global webpack config explained earlier. To use this, set the webpack config path to the function level `webpack` variable. Setting function level `webpack` variable to `true` will force webpack to bundle the function script with a default web pack configuration. Setting `webpack` key to `false` will turn off webpack for the function. (i.e the function script will not be fetched from dist folder)
Simply add `webpack: true | <config path>` to your config block.
```yaml
functions:
myfunction:
name: myfunction
webpack: true #or the web pack config path for this function
script: handlers/myfunctionhandler
events:
- http:
url: example.com/myfunction
method: GET
```
### Environment Variables
While Cloudflare Workers doesn't exactly offer environment vairables, we can bind global variables to values, essentially giving the same capabilities. In your function configuration, add key value pairs in `environment`
```yaml
functions:
myFunction:
environment:
MYKEY: value_of_my_key
ANOTHER_KEY_OF_MINE: sweet_child_o_mine
```
Then in your script, you can reference `MYKEY` to access the value.
You can also add an environment block under `provider`. These will get added to every function. If a function defines the same variable, the function defintion will overwrite the provider block definition.
```yaml
provider:
name: cloudflare
environment:
MYKEY: value_of_my_key
ANOTHER_KEY_OF_MINE: sweet_child_o_mine
```
### Using Cloudflare KV Storage
The plugin can create and bind a [KV Storage](https://developers.cloudflare.com/workers/kv/) namespace for your function by simpling adding a resources section.
The following will create a namespace called `BEST_NAMESPACE` and bind the variable `TEST` to that namespace inside `myfunction`.
```yaml
functions:
myfunction:
name: myfunction
webpack: true
script: handlers/myfunctionhandler
resources:
kv:
- variable: TEST
namespace: BEST_NAMESPACE
events:
- http:
url: example.com/myfunction
method: GET
```
### Web Assembly
The plugin can upload and bind WASM to execute in your worker. The easiest way to do this is to use the --template cloudflare-workers-rust when generating a project. The template includes a Rust create folder setup with wasm-pack, a webpack script for adding the generated javascript into your project, and the yml file settings to upload the wasm file itself.
```yaml
functions:
myfunction:
name: myfunction
webpack: true
script: handlers/myfunctionhandler
resources:
wasm:
- variable: WASM
filename: rust/pkg/wasm_bg.wasm
events:
- http:
url: example.com/myfunction
method: GET
```
================================================
FILE: deploy/cloudflareDeploy.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const BB = require("bluebird");
const ms = require("./lib/multiscript");
const ss = require("./lib/singlescript");
const accountType = require("../shared/accountType");
const logs = require("./lib/logResponse");
const utils = require("../utils");
const validate = require("../shared/validate");
const duplicate = require("../shared/duplicate");
class CloudflareDeploy {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options || {};
this.provider = this.serverless.getProvider("cloudflare");
Object.assign(this, accountType, ms, utils, ss, logs, validate);
const startTime = Date.now();
this.hooks = {
"deploy:deploy": () =>
BB.bind(this)
.then(this.checkAccountType)
.then(async isMultiScript => {
this.serverless.cli.log('Starting Serverless Cloudflare-Worker deployment.');
if (isMultiScript && await duplicate.checkIfDuplicateRoutes(this.serverless, this.provider)) {
return BB.reject("Duplicate routes pointing to different script");
}
if (this.getInvalidScriptNames()) {
return BB.reject(
"Worker names can contain lowercase letters, numbers, underscores, and dashes. They cannot start with dashes."
);
}
if (isMultiScript) {
return this.multiScriptDeployAll()
} else {
const functionObject = this.getFunctionObjectForSingleScript();
return this.deploySingleScript(functionObject);
}
})
.then(this.logDeployResponse)
.then(k => this.serverless.cli.log(`Finished deployment in ${(Date.now() - startTime) / 1000} seconds.`))
.then(k => this.serverless.cli.log('Finished Serverless Cloudflare-Worker deployment.'))
};
}
}
module.exports = CloudflareDeploy;
================================================
FILE: deploy/cloudflareDeployFunction.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const BB = require("bluebird");
const ms = require("./lib/multiscript");
const ss = require("./lib/singlescript");
const validateFunctionName = require("../shared/validate");
const accountType = require("../shared/accountType");
const utils = require("../utils");
const logs = require("./lib/logResponse");
const duplicate = require("../shared/duplicate");
class CloudflareDeployFunction {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options || {};
this.provider = this.serverless.getProvider("cloudflare");
Object.assign(this, validateFunctionName, accountType, ms, utils, ss, logs);
let functionNamesForDeploy = [];
this.hooks = {
"deploy:function:deploy": () =>
BB.bind(this)
.then(this.validateFunctionName)
.then( (functions) => {
functionNamesForDeploy = functions;
return this.checkAccountType;
})
.then(async isMultiScript => {
if (isMultiScript && await duplicate.checkIfDuplicateRoutes(this.serverless, this.provider)) {
return BB.reject("Duplicate routes pointing to different script");
}
if (this.getInvalidScriptNames()) {
return BB.reject(
"Worker names can contain lowercase letters, numbers, underscores, and dashes. They cannot start with dashes."
);
}
if (isMultiScript) {
return this.multiScriptDeployAll(functionNamesForDeploy);
} else {
const functionObject = this.getFunctionObjectForSingleScript();
return this.deploySingleScript(functionObject);
}
})
.then(this.logDeployResponse)
};
}
}
module.exports = CloudflareDeployFunction;
================================================
FILE: deploy/lib/logResponse.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
module.exports = {
logDeployResponse({ workerScriptResponse, routesResponse, namespaceResponses, isMultiScript }) {
if (isMultiScript) {
this.aggregateWorkerResponse(this.serverless.cli, workerScriptResponse);
this.aggregateRoutesResponse(this.serverless.cli, routesResponse);
} else {
this.parseWorkerResponse(this.serverless.cli, workerScriptResponse);
this.parseRoutesResponse(this.serverless.cli, routesResponse);
}
}
};
================================================
FILE: deploy/lib/multiscript.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const BB = require("bluebird");
const webpack = require("../../utils/webpack");
const ms = require("../../shared/multiscript");
module.exports = {
async deployScriptToCloudflare(functionObject) {
return BB.bind(this)
.then(async () => {
if (functionObject.webpack) {
await webpack.pack(this.serverless, functionObject);
}
// deploy script, routes, and namespaces
const namespaceResponse = await ms.deployNamespaces(this.provider.config.accountId, functionObject);
const workerScriptResponse = await ms.deployWorker(this.provider.config.accountId, this.serverless, functionObject);
const routesResponse = await ms.deployRoutes(this.provider.config.zoneId, functionObject);
return {
workerScriptResponse,
routesResponse,
namespaceResponse
};
});
},
async deployScript(scriptName) {
const startScriptTime = Date.now();
const functionObject = this.getFunctionObject(scriptName);
this.serverless.cli.log(`deploying script: ${scriptName}`);
const {
workerScriptResponse,
routesResponse: rResponse,
namespaceResponse,
} = await this.deployScriptToCloudflare(functionObject, scriptName);
this.serverless.cli.log(`Finished deployment ${scriptName} in ${(Date.now() - startScriptTime) / 1000} seconds`);
return { workerResponse: workerScriptResponse, routesResponse: rResponse, namespaceResponse }
},
/**
* Deploy functions passed in or all functions if no functions are submitted
*
* @param {Array[string]} functions
*/
async multiScriptDeployAll(functions = null) {
functions = functions || this.serverless.service.getAllFunctions();
if (typeof (functions) === 'undefined' || functions === null) {
throw new Error("Incorrect template being used for a MultiScript user ");
}
let workerResponse = [];
let routesResponse = [];
let namespaceResponses = [];
// Build global webpack if available
await webpack.packGlobalWebpack(this.serverless)
this.serverless.cli.log('Starting deployment');
// scriptName is really the key of the function map
for (const name of functions) {
const result = await this.deployScript(name);
workerResponse.push(result.workerResponse)
routesResponse.push(result.routesResponse)
namespaceResponses.push(result.namespaceResponse)
}
return {
workerScriptResponse: workerResponse,
routesResponse,
namespaceResponses,
isMultiScript: true
};
}
};
================================================
FILE: deploy/lib/singlescript.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const sdk = require("../../provider/sdk");
const { generateCode, generateWASM } = require("./workerScript");
const BB = require("bluebird");
const webpack = require("../../utils/webpack");
const cf = require("cloudflare-workers-toolkit");
const ms = require("../../shared/multiscript");
module.exports = {
async singleServeRoutesAPI({ pattern, zoneId }) {
const payload = { pattern, enabled: true };
return await sdk.cfApiCall({
url: `https://api.cloudflare.com/client/v4/zones/${zoneId}/workers/filters`,
method: `POST`,
contentType: `application/json`,
body: JSON.stringify(payload)
});
},
async getRoutesSingleScript(zoneId) {
return sdk.cfApiCall({
url: `https://api.cloudflare.com/client/v4/zones/${zoneId}/workers/filters`,
method: `GET`
});
},
collectRoutes(events) {
return events.map(event => {
if (event.http) {
return event.http.url;
}
})
},
/**
* Deploys a single script zone which can only have one script per zone.
* Because of this the name field is omitted.
* Also, zoneId must be used since it is required to deploy a single script.
* @param {*} functionObject
*/
async deploySingleScript(functionObject) {
return await BB.bind(this).then(async () => {
const { zoneId } = this.provider.config;
const singleScriptRoutes = this.collectRoutes(functionObject.events);
let workerScriptResponse;
let routesResponse = [];
// Build global webpack if available
await webpack.packGlobalWebpack(this.serverless)
// If a local webpack config defined, do that too
if (functionObject.webpack) {
await webpack.pack(this.serverless, functionObject);
}
const scriptContents = generateCode(this.serverless, functionObject);
cf.setAccountId(this.provider.config.accountId);
const namespaceResponse = await ms.deployNamespaces(this.provider.config.accountId, functionObject);
let bindings = await ms.getBindings(this.provider, functionObject)
const response = await cf.workers.deploy({
accountId: this.provider.config.accountId,
zoneId,
script: scriptContents,
wasm: generateWASM(functionObject),
bindings
});
workerScriptResponse = response;
for (const pattern of singleScriptRoutes) {
this.serverless.cli.log(`deploying route: ${pattern}`);
const rResponse = await this.singleServeRoutesAPI({
pattern,
zoneId
});
routesResponse.push(rResponse);
}
return {
namespaceResponse,
workerScriptResponse,
routesResponse,
isMultiScript: false
};
});
}
};
================================================
FILE: deploy/lib/workerScript.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const path = require("path");
const fs = require("fs");
const webpack = require("../../utils/webpack");
const generateCode = (serverless, functionObject) => {
let { script } = functionObject;
const rootPath = webpack.getAssetPathPrefix(serverless, functionObject)
if (path.extname(script) != ".js") {
script = `${rootPath}${script}.js`
}
return fs.readFileSync(script).toString();
};
/**
* Builds the list of wasm objects for script deployment
* @param {} functionObject
*/
const generateWASM = (functionObject) => {
let wasm = [];
if (functionObject && functionObject.resources && functionObject.resources.wasm) {
functionObject.resources.wasm.map((w) => {
wasm.push(w.file);
})
}
return wasm;
}
module.exports = {
generateCode,
generateWASM
};
================================================
FILE: index.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const CloudflareDeploy = require("./deploy/cloudflareDeploy");
const CloudflareDeployFunction = require("./deploy/cloudflareDeployFunction");
const CloudflareRemove = require("./remove/cloudflareRemove");
const CloudflareProvider = require("./provider/cloudflareProvider");
const CloudflareInvoke = require("./invoke/cloudflareInvoke");
class CloudflareIndex {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.serverless.pluginManager.addPlugin(CloudflareProvider);
this.serverless.pluginManager.addPlugin(CloudflareDeploy);
this.serverless.pluginManager.addPlugin(CloudflareDeployFunction);
this.serverless.pluginManager.addPlugin(CloudflareRemove);
this.serverless.pluginManager.addPlugin(CloudflareInvoke);
}
}
module.exports = CloudflareIndex;
================================================
FILE: invoke/cloudflareInvoke.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
"use strict";
const BB = require("bluebird");
const sdk = require("../provider/sdk");
const validateFunctionName = require("../shared/validate");
const accountType = require("../shared/accountType");
const utils = require("../utils");
const invoke = require("./lib/invoke");
class CloudflareInvoke {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options || {};
this.provider = this.serverless.getProvider("cloudflare");
Object.assign(this, utils, validateFunctionName, accountType, invoke);
this.hooks = {
"invoke:invoke": () =>
BB.bind(this)
.then(this.validateFunctionName)
.then(this.bakeEvent)
.then(async payload => {
return await sdk.invokeApiCall(payload);
})
.then(resp => {
console.log(resp);
})
};
}
}
module.exports = CloudflareInvoke;
================================================
FILE: invoke/lib/invoke.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
module.exports = {
bakeEvent() {
const functionObject = this.getFunctionObject();
const { events } = functionObject;
let bakedEvent = { url: "", headers: {}, method: "GET" };
events.forEach(event => {
const { http } = event;
Object.keys(http).forEach(httpEvent => {
if (httpEvent === "url") {
let urlString = http[httpEvent];
if (!urlString.startsWith("http://") && !urlString.startsWith("https://")) {
urlString = "https://" + urlString;
}
if (this.options.querystring) {
urlString += "?" + this.options.querystring;
}
bakedEvent.url = urlString;
} else if (httpEvent == "headers") {
Object.keys(http[httpEvent]).forEach(key => {
const value = http[httpEvent][key];
bakedEvent["headers"][key] = value;
});
} else {
bakedEvent[httpEvent] = http[httpEvent];
}
});
});
return bakedEvent;
}
};
================================================
FILE: package.json
================================================
{
"name": "serverless-cloudflare-workers",
"version": "1.2.0",
"description": "serverless cloudflare workers ",
"main": "index.js",
"scripts": {
"test": "nyc --reporter=html --reporter=text mocha --recursive",
"fmt": "eslint --fix ./deploy ./invoke ./provider ./remove ./index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/cloudflare/serverless-cloudflare-workers.git"
},
"keywords": [
"serverless",
"serverless",
"framework",
"serverless",
"applications",
"workers",
"cloudflare"
],
"dependencies": {
"bluebird": "^3.4.7",
"cloudflare-workers-toolkit": "^0.1.0",
"fs-extra": "^7.0.1",
"node-fetch": "^2.3.0",
"webpack": "^4.25.1"
},
"author": "serverless.com",
"license": "MIT",
"bugs": {
"url": "https://github.com/cloudflare/serverless-cloudflare-workers/issues"
},
"homepage": "https://github.com/cloudflare/serverless-cloudflare-workers#readme",
"devDependencies": {
"eslint": "^5.13.0",
"eslint-config-prettier": "^2.9.0",
"eslint-plugin-prettier": "^2.6.2",
"mocha": "^5.2.0",
"nyc": "^13.3.0",
"proxyquire": "^2.1.0"
}
}
================================================
FILE: provider/cloudflareProvider.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
"use strict";
const sdk = require("./sdk");
const providerName = "cloudflare";
class CloudflareProvider {
static getProviderName() {
return providerName;
}
constructor(serverless) {
this.serverless = serverless;
this.provider = this;
this.serverless.setProvider(providerName, this);
this.config = this.serverless.service.provider.config || serverless.service.serviceObject.config;
}
}
module.exports = CloudflareProvider;
================================================
FILE: provider/credentials.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
"use strict";
const ENV_PARAMS = ["CLOUDFLARE_AUTH_KEY", "CLOUDFLARE_AUTH_EMAIL"];
const REQUIRED_CREDENTIALS = ENV_PARAMS.map(s => {
// ["auth_key", "email"]
const a = s.split("CLOUDFLARE_")[1];
return a.toLowerCase();
});
function get() {
const envProps = {};
ENV_PARAMS.forEach(envName => {
if (process.env[envName]) {
envProps[envName.split("CLOUDFLARE_")[1].toLowerCase()] =
process.env[envName];
}
});
return envProps;
}
module.exports = {
get,
REQUIRED_CREDENTIALS
};
================================================
FILE: provider/sdk.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const fetch = require('node-fetch');
const cfApiCall = async ({ url, method, contentType = null, body = null }) => {
if (url.substring(0, 8) !== "https://") {
url = `https://api.cloudflare.com/client/v4${url}`;
}
let options = {
headers: {
"X-Auth-Email": process.env.CLOUDFLARE_AUTH_EMAIL,
"X-Auth-Key": process.env.CLOUDFLARE_AUTH_KEY
},
method: method
};
if (contentType) {
options["headers"]["Content-Type"] = contentType;
}
if (body) {
options["body"] = body;
}
return await fetch(url, options).then(responseBody => responseBody.json());
};
const invokeApiCall = async ({
url,
method,
contentType = null,
body = null,
headers
}) => {
let options = {
method,
headers
};
if (contentType) {
options["headers"]["Content-Type"] = contentType;
}
if (body) {
options["body"] = body;
}
return await fetch(url, options).then(responseBody => responseBody.text());
};
module.exports = {
cfApiCall,
invokeApiCall
};
================================================
FILE: remove/cloudflareRemove.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const BB = require("bluebird");
const ss = require("./lib/singlescript");
const accountType = require("../shared/accountType");
const cf = require("cloudflare-workers-toolkit");
const utils = require("../utils");
class CloudflareRemove {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.provider = this.serverless.getProvider("cloudflare");
// TODO: refactor out Object assigns. they lead to difficult code
Object.assign(this, accountType, utils);
this.hooks = {
"remove:remove": () => {
if (this.options.function || this.options.f) {
throw new Error("This does not support -f yet.")
}
BB.bind(this)
.then(this.remove)
.then(resp => console.log("Removed"))
}
};
}
async remove() {
const {
accountId,
zoneId,
routes: singleScriptEnabled
} = this.provider.config;
const functionKeys = this.serverless.service.getAllFunctions();
const multiscriptEnabled = await this.checkAccountType();
if (multiscriptEnabled) {
if (functionKeys === undefined || functionKeys === null) {
throw new Error(
"Incorrect template being used for a MultiScript user "
);
}
const { success, result, errors } = await cf.routes.getRoutes({zoneId});
let allRoutes = {};
if (success) {
result.forEach(r => {
try {
allRoutes[r.script].push(r.id);
} catch (err) {
allRoutes[r.script] = [r.id];
}
});
} else if (errors) {
throw new Error(errors);
}
const promises = [];
functionKeys.forEach(functionKey => {
let serviceName = this.getFunctionObject(functionKey).name;
allRoutes[serviceName].forEach(routeId => {
promises.push(cf.routes.remove({zoneId, routeId}));
});
promises.push(cf.workers.remove({accountId, name: serviceName}));
});
await Promise.all(promises);
} else {
await Promise.all([cf.workers.remove({zoneId: this.provider.config.zoneId}), ss.removeRoutes(zoneId)]);
}
this.serverless.cli.log("removed routes + scripts");
return true;
}
}
module.exports = CloudflareRemove;
================================================
FILE: remove/lib/singlescript.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const sdk = require("../../provider/sdk");
module.exports = {
async removeRoutes(zoneId) {
const { success, errors, result } = await sdk.cfApiCall({
url: `https://api.cloudflare.com/client/v4/zones/${zoneId}/workers/filters`,
contentType: `application/json`
});
if (success) {
let promises = [];
result.forEach(filter => {
promises.push(
sdk.cfApiCall({
url: `https://api.cloudflare.com/client/v4/zones/${zoneId}/workers/filters/${
filter["id"]
}`,
method: `DELETE`
})
);
});
return await Promise.all(promises);
}
throw new Error(errors);
}
};
================================================
FILE: shared/accountType.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const BB = require("bluebird");
const credentials = require("../provider/credentials");
const cf = require("cloudflare-workers-toolkit");
module.exports = {
async checkAccountType() {
const zoneId = this.provider.config.zoneId;
return await BB.bind(this)
.then(this.checkAllEnvironmentVariables)
.then(function() {
return cf.workers.getSettings({zoneId});
})
.then(this.checkIfMultiScript)
},
checkAllEnvironmentVariables() {
const envCreds = credentials.get();
const requiredCredentials = credentials.REQUIRED_CREDENTIALS;
requiredCredentials.forEach(requiredCredential => {
if (!envCreds[requiredCredential]) {
return BB.reject(
`Missing mandatory environment variable: CLOUDFLARE_${requiredCredential.toUpperCase()}.`
);
}
});
},
checkIfMultiScript({ success, errors, result }) {
if (!success) {
return BB.reject(JSON.stringify(errors));
}
const { multiscript, enabled } = result;
if (!enabled) {
return BB.reject(
"Workers are not enabled for this account, please upgrade your account at https://cloudflare.com"
);
}
return multiscript;
}
};
================================================
FILE: shared/duplicate.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const cf = require("cloudflare-workers-toolkit");
module.exports = {
/**
* @param {*} serverless
* @param {*} provider
*/
async checkIfDuplicateRoutes(serverless, provider) {
// for any worker that we are uploading, we check its routes in the yml file and
// check if there are exact same routes in our cloudflare account which point to
// different script name
if (typeof(provider) == 'undefined' || !provider.config) {
throw("No config found.")
}
const { zoneId } = provider.config;
if (!zoneId) {
throw("You must specify a Zone ID CLOUDFLARE_ZONE_ID");
}
const response = await cf.routes.getRoutes({zoneId});
const { result } = response;
// check for all the workers we are uploading
const foundDuplicate = result.some(filters => {
const { pattern, script } = filters;
const functions = serverless.service.getAllFunctions();
for (const scriptName of functions) {
const functionObject = serverless.service.getFunction(scriptName);
const routes = functionObject.events.map(function(event) {
if (event.http) {
return event.http.url;
}
})
//let uploadedName = functionObject.name || scriptName;
return routes.some(r => {
return r === pattern && functionObject.name !== script;
});
}
});
return foundDuplicate;
}
}
================================================
FILE: shared/multiscript.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const cf = require("cloudflare-workers-toolkit");
const path = require("path");
const { generateCode, generateWASM } = require("../deploy/lib/workerScript");
module.exports = {
getRoutes(events) {
return events.map(function (event) {
if (event.http) {
return event.http.url;
}
});
},
/**
* Parses the resources and environment config to build bindings for the worker. async because it has to get namespaces for the CF id
* @param {*} provider
* @param {*} functionObject
*/
async getBindings(provider, functionObject) {
let bindings = [];
let resources = functionObject.resources;
if (resources && resources.kv) { // do nothing if there is no kv config
const namespaces = await cf.storage.getNamespaces();
let namespaceBindings = resources.kv.map(function (store) {
return {
name: store.variable,
type: 'kv_namespace',
namespace_id: namespaces.find(function (ns) {
return ns.title === store.namespace;
}).id
}
});
bindings = bindings.concat(namespaceBindings);
}
if (resources && resources.wasm) {
let wasmBindings = resources.wasm.map(function(wasm) {
return {
name: wasm.variable,
type: 'wasm_module',
part: path.basename(wasm.file, path.extname(wasm.file))
}
});
bindings = bindings.concat(wasmBindings);
}
// Get Environment Variables
let envVars = Object.assign({}, provider.environment);
envVars = Object.assign(envVars, functionObject.environment);
for (const key in envVars) {
bindings.push({
name: key,
type: 'secret_text',
text: envVars[key]
});
}
return bindings;
},
/**
* Deploys the Worker Script in functionObject from the yml file
* @param {*} accountId
* @param {*} service
* @param {*} functionObject
*/
async deployWorker(accountId, serverless, functionObject) {
const { service } = serverless;
cf.setAccountId(accountId);
const contents = generateCode(serverless, functionObject);
let bindings = await this.getBindings(service.provider, functionObject);
let t = await cf.workers.deploy({
accountId,
name: functionObject.name,
script: contents,
wasm: generateWASM(functionObject),
bindings
})
return t;
},
/**
* Deploys the namespaces in function Object listed under resources->storage
* @param {*} accountId
* @param {*} functionObject
*/
async deployNamespaces(accountId, functionObject) {
let responses = [];
if (functionObject.resources && functionObject.resources.kv) {
for (const store of functionObject.resources.kv) {
let result = await cf.storage.createNamespace({
accountId,
name: store.namespace
});
if (cf.storage.isDuplicateNamespaceError(result)) {
result.success = true;
}
responses.push(result);
}
}
return responses;
},
/**
* Deploys all routes found in functionObject.events
* @param {*} zoneId
* @param {*} functionObject
*/
async deployRoutes(zoneId, functionObject) {
const allRoutes = this.getRoutes(functionObject.events);
let routeResponses = [];
for (const pattern of allRoutes) {
const response = await cf.routes.deploy({ path: pattern, scriptName: functionObject.name, zoneId });
routeResponses.push(response)
}
return routeResponses;
}
}
================================================
FILE: shared/validate.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const BB = require("bluebird");
module.exports = {
validateFunctionName() {
return BB.bind(this)
.then(this.checkIfFuntionParamPresent)
.then(this.checkFunctionName);
},
checkIfFuntionParamPresent() {
let funParam = this.options.function;
if (funParam === undefined) {
funParam = this.options.f;
}
if (funParam === undefined || funParam === null || funParam === "") {
return BB.reject("Invoke function with -f or --function");
}
return funParam;
},
/**
* Ensures that funParam is an array of functions and ensures there are config entries for each.
*/
checkFunctionName(funParam) {
const functions = this.serverless.service.getAllFunctions();
if (!Array.isArray(funParam)) {
funParam = Array(funParam)
}
for (let func of funParam) {
if (functions.indexOf(func) === -1) {
return BB.reject(`The specified function: ${func} was not found in your configuration file.`);
}
}
return funParam;
},
isValidScriptName(sname) {
const re = new RegExp("^[a-z0-9_][A-Za-z0-9-_]*$");
if (re.exec(sname)) {
return true;
}
return false;
},
getInvalidScriptNames() {
const functions = this.serverless.service.getAllFunctions();
const notValidScriptNames = functions.find(scriptName => {
return !this.isValidScriptName(scriptName);
});
return notValidScriptNames;
}
};
================================================
FILE: test/shared/duplicate_test.js
================================================
const proxyquire = require('proxyquire')
const assert = require('assert');
const validate = proxyquire("../../shared/duplicate", {
'cloudflare-workers-toolkit': {
"routes": {
'getRoutes': function() {
return Promise.resolve({
"result": [{
pattern: "route1",
script: "existing"
}]
})
}
}
}
});
describe("checkIfDuplicateRoutes", function() {
it("should fail gracefully if no config is provided", async function() {
try {
await validate.checkIfDuplicateRoutes();
} catch (e) {
assert.equal(e, "No config found.");
}
});
const PROVIDER = {
config: {
accountId: 12,
zoneId: 13
}
}
const FUNCTIONS = ["first", "second"];
const SERVERLESS = {
service: {
getAllFunctions: function() {
return FUNCTIONS;
},
getFunction: function(name) {
return {
webpack: false,
name: name,
script: `test/handlers/${name}`,
events: [ { http: {url: "route1", method: 'GET'}} ]
}
}
}
}
it("should detect duplicates in the same yml file", async function() {
const result = await validate.checkIfDuplicateRoutes(SERVERLESS, PROVIDER);
assert.equal(result, true)
});
});
================================================
FILE: test/shared/multiscript_test.js
================================================
const proxyquire = require('proxyquire')
const assert = require('assert');
const ms = proxyquire("../../shared/multiscript", {
"cloudflare-workers-toolkit": {
}
});
const EVENTS = [{
'http': {
url: 'somedomain.com/route1',
method: 'GET'
}
}, {
'http': {
url: 'somedomain.com/route2',
method: 'GET'
}
}];
describe("getRoutes", function() {
it("pulls routes out of the event config", function() {
const routes = ms.getRoutes(EVENTS);
assert.deepEqual(routes, ['somedomain.com/route1', 'somedomain.com/route2'])
});
});
================================================
FILE: utils/index.js
================================================
/**
* Copyright (c) 2018, Cloudflare. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
module.exports = {
getFunctionObject(paramName) {
let funParam = paramName || this.options.function;
if (typeof funParam === "undefined") {
funParam = this.options.f;
}
if (funParam) {
return this.serverless.service.getFunction(funParam);
} else return null;
},
getFunctionObjectForSingleScript() {
const [functionName] = this.serverless.service.getAllFunctions();
return this.getFunctionObject(functionName);
},
parseWorkerResponse(serverlessConsole, apiResponse) {
let {
success: workerDeploySuccess,
result: workerResult,
errors: workerErrors
} = apiResponse;
const { id, size } = workerResult || {};
if (workerDeploySuccess) {
serverlessConsole.log(
`✅ Script Deployed. Name: ${id}, Size: ${(size / 1024).toFixed(2)}K`
);
} else {
serverlessConsole.log(`❌ Fatal Error, Script Not Deployed!`);
workerErrors.forEach(err => {
let { code, message } = err;
serverlessConsole.log(
`--> Error Code:${code}\n--> Error Message: "${message}"`
);
});
}
return { workerDeploySuccess, workerResult, workerErrors };
},
aggregateWorkerResponse(serverlessConsole, apiResponse) {
let status = [];
apiResponse.forEach(resp => {
status.push(this.parseWorkerResponse(serverlessConsole, resp));
});
return status;
},
parseRoutesResponse(serverlessConsole, apiResponse) {
let status = [];
apiResponse.forEach(resp => {
let {
success: routeSuccess,
result: routeResult,
errors: routeErrors
} = resp;
if (routeSuccess || !this.routeContainsFatalErrors(routeErrors)) {
serverlessConsole.log(`✅ Routes Deployed `);
} else {
serverlessConsole.log(`❌ Fatal Error, Routes Not Deployed!`);
routeErrors.forEach(err => {
let { code, message } = err;
serverlessConsole.log(
`--> Error Code:${code}\n--> Error Message: "${message}"`
);
});
}
status.push({ routeSuccess, routeResult, routeErrors });
});
return status;
},
aggregateRoutesResponse(serverlessConsole, apiResponse) {
let status = [];
apiResponse.forEach(resp => {
status.push(this.parseRoutesResponse(serverlessConsole, resp));
});
return status;
},
routeContainsFatalErrors(errors) {
// suppress 10020 duplicate routes error
// no need to show error when they are simply updating their script
return errors.some(e => e.code !== 10020);
}
};
================================================
FILE: utils/webpack.js
================================================
const path = require("path");
const BB = require("bluebird");
const webpack = BB.promisify(require("webpack"));
const fse = require("fs-extra");
function getDefaultWebPackConfig(serverless, functionObject) {
const outputPath = path.join(
serverless.config.servicePath,
functionObject.script
);
return {
config: {
entry: {
out: outputPath
},
output: {
filename: `${functionObject.script}.js`,
path: path.join(serverless.config.servicePath, "dist")
},
devtool: "cheap-module-source-map",
target: "webworker",
mode: "production"
},
outputPath
};
}
async function build(config) {
try {
let result = await webpack(config);
let errors = result.compilation.errors;
if (Array.isArray(errors) && errors) {
errors.forEach(error => {
console.log(`Webpack Error: ${error}`);
});
}
} catch (error) {
// failed to webpack
console.log(`Webpack Error: ${error}`);
}
}
module.exports = {
pack: async function (serverless, functionObject, webpackConfigFile = null) {
serverless.cli.log(`bundling: ${functionObject.script}`);
const startTime = Date.now()
let config = null, outputPath = '';
//If webpack config file is provided use that
if (webpackConfigFile && typeof webpackConfigFile === 'string') {
console.log(`Building web pack with config ${webpackConfigFile}.js`)
config = require(path.resolve("./" + webpackConfigFile));
} else if (typeof (functionObject.webpack) === 'boolean' && functionObject.webpack) {
//webpack is set to true in function object. Let's generate bundle for just this function
console.log(`Building web pack with config default config for ${functionObject.script}`);
({ config, outputPath } = getDefaultWebPackConfig(serverless, functionObject));
} else if (typeof (functionObject.webpack) === 'string' && functionObject.webpack) {
//function has individual webpack config. Build that
console.log(`Building web pack with config file ${functionObject.webpack} for ${functionObject.script}`);
({ config } = getDefaultWebPackConfig(serverless, functionObject));
config = Object.assign(config, require(path.resolve("./" + functionObject.webpack)));
}
if (!config) {
//No config found, probably did a global webpack build. Exit
return
}
await build(config)
console.log(`Webpack finished in ${(Date.now() - startTime) / 1000} seconds`)
if (outputPath) {
fse.removeSync(outputPath);
}
},
packGlobalWebpack: async function (serverless) {
// Let's see if there is global webpack that we need to build
const globalWebPackConfig = serverless.service.serviceObject.webpackConfig;
if (globalWebPackConfig && typeof globalWebPackConfig === 'string') {
await this.pack(serverless, { script: 'Global web pack config' }, globalWebPackConfig);
}
},
getAssetPathPrefix: function (serverless, functionObject) {
// If web pack is used and not explicitly turned off for this script, append dist in-front of the path
return (serverless.service.serviceObject.webpackConfig || functionObject.webpack) &&
functionObject.webpack !== false ?
'./dist/' : '';
}
};
gitextract_3btj622v/
├── .github/
│ └── workflows/
│ └── semgrep.yml
├── .gitignore
├── LICENSE.md
├── README.md
├── deploy/
│ ├── cloudflareDeploy.js
│ ├── cloudflareDeployFunction.js
│ └── lib/
│ ├── logResponse.js
│ ├── multiscript.js
│ ├── singlescript.js
│ └── workerScript.js
├── index.js
├── invoke/
│ ├── cloudflareInvoke.js
│ └── lib/
│ └── invoke.js
├── package.json
├── provider/
│ ├── cloudflareProvider.js
│ ├── credentials.js
│ └── sdk.js
├── remove/
│ ├── cloudflareRemove.js
│ └── lib/
│ └── singlescript.js
├── shared/
│ ├── accountType.js
│ ├── duplicate.js
│ ├── multiscript.js
│ └── validate.js
├── test/
│ └── shared/
│ ├── duplicate_test.js
│ └── multiscript_test.js
└── utils/
├── index.js
└── webpack.js
SYMBOL INDEX (51 symbols across 19 files)
FILE: deploy/cloudflareDeploy.js
class CloudflareDeploy (line 29) | class CloudflareDeploy {
method constructor (line 30) | constructor(serverless, options) {
FILE: deploy/cloudflareDeployFunction.js
class CloudflareDeployFunction (line 29) | class CloudflareDeployFunction {
method constructor (line 30) | constructor(serverless, options) {
FILE: deploy/lib/logResponse.js
method logDeployResponse (line 20) | logDeployResponse({ workerScriptResponse, routesResponse, namespaceRespo...
FILE: deploy/lib/multiscript.js
method deployScriptToCloudflare (line 24) | async deployScriptToCloudflare(functionObject) {
method deployScript (line 46) | async deployScript(scriptName) {
method multiScriptDeployAll (line 66) | async multiScriptDeployAll(functions = null) {
FILE: deploy/lib/singlescript.js
method singleServeRoutesAPI (line 28) | async singleServeRoutesAPI({ pattern, zoneId }) {
method getRoutesSingleScript (line 38) | async getRoutesSingleScript(zoneId) {
method collectRoutes (line 45) | collectRoutes(events) {
method deploySingleScript (line 59) | async deploySingleScript(functionObject) {
FILE: index.js
class CloudflareIndex (line 25) | class CloudflareIndex {
method constructor (line 26) | constructor(serverless, options) {
FILE: invoke/cloudflareInvoke.js
class CloudflareInvoke (line 27) | class CloudflareInvoke {
method constructor (line 28) | constructor(serverless, options) {
FILE: invoke/lib/invoke.js
method bakeEvent (line 20) | bakeEvent() {
FILE: provider/cloudflareProvider.js
class CloudflareProvider (line 23) | class CloudflareProvider {
method getProviderName (line 24) | static getProviderName() {
method constructor (line 28) | constructor(serverless) {
FILE: provider/credentials.js
constant ENV_PARAMS (line 21) | const ENV_PARAMS = ["CLOUDFLARE_AUTH_KEY", "CLOUDFLARE_AUTH_EMAIL"];
constant REQUIRED_CREDENTIALS (line 22) | const REQUIRED_CREDENTIALS = ENV_PARAMS.map(s => {
function get (line 27) | function get() {
FILE: remove/cloudflareRemove.js
class CloudflareRemove (line 25) | class CloudflareRemove {
method constructor (line 26) | constructor(serverless, options) {
method remove (line 45) | async remove() {
FILE: remove/lib/singlescript.js
method removeRoutes (line 22) | async removeRoutes(zoneId) {
FILE: shared/accountType.js
method checkAccountType (line 24) | async checkAccountType() {
method checkAllEnvironmentVariables (line 34) | checkAllEnvironmentVariables() {
method checkIfMultiScript (line 45) | checkIfMultiScript({ success, errors, result }) {
FILE: shared/duplicate.js
method checkIfDuplicateRoutes (line 26) | async checkIfDuplicateRoutes(serverless, provider) {
FILE: shared/multiscript.js
method getRoutes (line 24) | getRoutes(events) {
method getBindings (line 37) | async getBindings(provider, functionObject) {
method deployWorker (line 92) | async deployWorker(accountId, serverless, functionObject) {
method deployNamespaces (line 115) | async deployNamespaces(accountId, functionObject) {
method deployRoutes (line 139) | async deployRoutes(zoneId, functionObject) {
FILE: shared/validate.js
method validateFunctionName (line 22) | validateFunctionName() {
method checkIfFuntionParamPresent (line 27) | checkIfFuntionParamPresent() {
method checkFunctionName (line 43) | checkFunctionName(funParam) {
method isValidScriptName (line 57) | isValidScriptName(sname) {
method getInvalidScriptNames (line 65) | getInvalidScriptNames() {
FILE: test/shared/multiscript_test.js
constant EVENTS (line 10) | const EVENTS = [{
FILE: utils/index.js
method getFunctionObject (line 20) | getFunctionObject(paramName) {
method getFunctionObjectForSingleScript (line 29) | getFunctionObjectForSingleScript() {
method parseWorkerResponse (line 33) | parseWorkerResponse(serverlessConsole, apiResponse) {
method aggregateWorkerResponse (line 57) | aggregateWorkerResponse(serverlessConsole, apiResponse) {
method parseRoutesResponse (line 65) | parseRoutesResponse(serverlessConsole, apiResponse) {
method aggregateRoutesResponse (line 91) | aggregateRoutesResponse(serverlessConsole, apiResponse) {
method routeContainsFatalErrors (line 101) | routeContainsFatalErrors(errors) {
FILE: utils/webpack.js
function getDefaultWebPackConfig (line 6) | function getDefaultWebPackConfig(serverless, functionObject) {
function build (line 29) | async function build(config) {
Condensed preview — 27 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (73K chars).
[
{
"path": ".github/workflows/semgrep.yml",
"chars": 591,
"preview": "\non:\n pull_request: {}\n workflow_dispatch: {}\n push: \n branches:\n - main\n - master\n schedule:\n - cro"
},
{
"path": ".gitignore",
"chars": 54,
"preview": ".nyc_output\ncoverage/\nnode_modules/\n.npmrc\n*.DS_Store\n"
},
{
"path": "LICENSE.md",
"chars": 1445,
"preview": "Copyright 2018 Cloudflare\n\nRedistribution and use in source and binary forms, with or without modification, are permitte"
},
{
"path": "README.md",
"chars": 3928,
"preview": "# serverless-cloudflare-workers\n\nServerless plugin for [Cloudflare Workers](https://developers.cloudflare.com/workers/)\n"
},
{
"path": "deploy/cloudflareDeploy.js",
"chars": 3438,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "deploy/cloudflareDeployFunction.js",
"chars": 3373,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "deploy/lib/logResponse.js",
"chars": 2001,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "deploy/lib/multiscript.js",
"chars": 4115,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "deploy/lib/singlescript.js",
"chars": 4274,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "deploy/lib/workerScript.js",
"chars": 2332,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "index.js",
"chars": 2364,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "invoke/cloudflareInvoke.js",
"chars": 2446,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "invoke/lib/invoke.js",
"chars": 2546,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "package.json",
"chars": 1182,
"preview": "{\n \"name\": \"serverless-cloudflare-workers\",\n \"version\": \"1.2.0\",\n \"description\": \"serverless cloudflare workers \",\n "
},
{
"path": "provider/cloudflareProvider.js",
"chars": 1989,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "provider/credentials.js",
"chars": 2053,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "provider/sdk.js",
"chars": 2551,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "remove/cloudflareRemove.js",
"chars": 3815,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "remove/lib/singlescript.js",
"chars": 2227,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "shared/accountType.js",
"chars": 2741,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "shared/duplicate.js",
"chars": 2966,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "shared/multiscript.js",
"chars": 5081,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "shared/validate.js",
"chars": 2969,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "test/shared/duplicate_test.js",
"chars": 1298,
"preview": "const proxyquire = require('proxyquire')\nconst assert = require('assert');\n\nconst validate = proxyquire(\"../../shared/d"
},
{
"path": "test/shared/multiscript_test.js",
"chars": 565,
"preview": "const proxyquire = require('proxyquire')\nconst assert = require('assert');\n\nconst ms = proxyquire(\"../../shared/multisc"
},
{
"path": "utils/index.js",
"chars": 4114,
"preview": "/**\n * Copyright (c) 2018, Cloudflare. All rights reserved.\n *\n * Redistribution and use in source and binary forms, wit"
},
{
"path": "utils/webpack.js",
"chars": 3283,
"preview": "const path = require(\"path\");\nconst BB = require(\"bluebird\");\nconst webpack = BB.promisify(require(\"webpack\"));\nconst fs"
}
]
About this extraction
This page contains the full source code of the cloudflare/serverless-cloudflare-workers GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 27 files (68.1 KB), approximately 15.5k tokens, and a symbol index with 51 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.