diff --git a/.gitignore b/.gitignore index e2d971b26..a300d2358 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ dev-portal/node_modules/* !dev-portal/node_modules/swagger-ui/ dev-portal/node_modules/swagger-ui/* +!dev-portal/node_modules/swagger-ui/package.json !dev-portal/node_modules/swagger-ui/dist/ # built artifacts @@ -27,7 +28,8 @@ dev-portal/node_modules/swagger-ui/* # misc npm-debug.log .DS_Store -packaged.yaml +packaged*.yaml cognito.js .idea .vscode +*.iml \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 7c3fd1a90..e53711bc6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,15 @@ language: node_js node_js: - - "node" + # we can't use latest because 11.11 breaks jest's env variable mock + - "11.10.1" cache: npm before_install: + # install clis in parallel +# - pip install --user awscli +# pip install --user aws-sam-cli +# https://github.com/travis-ci/travis-ci/issues/3139 - cd dev-portal # install everything in dev portal - npm install diff --git a/README.md b/README.md index 9a1b7598a..4f452ea78 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ## Introduction [![Build Status](https://travis-ci.org/awslabs/aws-api-gateway-developer-portal.svg?branch=master)](https://travis-ci.org/awslabs/aws-api-gateway-developer-portal) -The Amazon API Gateway Serverless Developer Portal is an application that you use for developer engagement by making your API Gateway APIs available to your customers through self-service discovery of those APIs. Your customers can use the developer portal to browse API documentation, register for – and immediately receive – their own API key that can be used to build applications, test published APIs, and monitor their own API usage. +The Amazon API Gateway Serverless Developer Portal is an application that you use for developer engagement by making your API Gateway APIs available to your customers through self-service discovery of those APIs. Your customers can use the developer portal to browse API documentation, register for – and immediately receive – their own API key that can be used to build applications, test published APIs, monitor their own API usage, generate SDKs, and submit feedback on your APIs design. For more information about Amazon API Gateway, visit the API Gateway [product page](https://aws.amazon.com/api-gateway/). @@ -13,7 +13,7 @@ It also optionally supports subscription/unsubscription through a SaaS product o ## Setup There are 2 main ways to deploy the Developer Portal today: ### 1. Deploy with SAR -This deployment model is better if you want an easy way deploy the developer portal and use it as-is out of box. You can deploy the Serverless Developer Portal through SAR in a few clicks! See the [documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-developer-portal.html). +This deployment model is better if you want an easy way to deploy the developer portal and use it as-is out of box. You can deploy the Serverless Developer Portal through SAR in a few clicks! See the [documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-developer-portal.html). ### 2. Deploy with SAM This deployment model is better if you plan to customize the developer portal heavily and setup CI/CD on it. @@ -30,15 +30,15 @@ If you have previously set up a v1 developer portal (non-SAM deployed), you will #### Deploy Run: ->Replace the `your-lambda-artifacts-bucket-name` with a bucket that you manage and must already exist +>In the command below, replace the `your-lambda-artifacts-bucket-name` with the name of a bucket that you manage and that already exists. Then, run: ```bash sam package --template-file ./cloudformation/template.yaml --output-template-file ./cloudformation/packaged.yaml --s3-bucket your-lambda-artifacts-bucket-name ``` Then run: ->Replace `custom-prefix` in the command below with some prefix that is globally unique, like your org name or username and run +>In the command below, replace the `your-lambda-artifacts-bucket-name` with the name of a bucket that you manage and that already exists, and replace `custom-prefix` with some prefix that is globally unique, like your org name or username. Then, run: ```bash -sam deploy --template-file ./cloudformation/packaged.yaml --stack-name "dev-portal" --capabilities CAPABILITY_NAMED_IAM --parameter-overrides DevPortalSiteS3BucketName="custom-prefix-dev-portal-static-assets" ArtifactsS3BucketName="custom-prefix-dev-portal-artifacts" +sam deploy --template-file ./cloudformation/packaged.yaml --stack-name "dev-portal" --s3-bucket your-lambda-artifacts-bucket-name --capabilities CAPABILITY_NAMED_IAM --parameter-overrides DevPortalSiteS3BucketName="custom-prefix-dev-portal-static-assets" ArtifactsS3BucketName="custom-prefix-dev-portal-artifacts" CognitoDomainNameOrPrefix="custom-prefix" ``` The command will exit when the stack creation is successful. If you'd like to watch it create in real-time, you can log into the cloudformation console. @@ -49,27 +49,31 @@ To get the URL for the newly created developer portal instance, find the website aws cloudformation describe-stacks --query "Stacks[?StackName=='dev-portal'][Outputs[?OutputKey=='WebsiteURL']][][].OutputValue" ``` -You can override any of the parameters in the template using the `--parameter-overrides key="value"` format. This will be necessary if you intend to deploy several instances of the developer portal. You can see a full list of overridable parameters in `cloudformation/template.yaml` under the `Parameters` section. +You can override any of the parameters in the template using the `--parameter-overrides key="value"` format. This will be necessary if you intend to deploy several instances of the developer portal or customize some of the features. You can see a full list of overridable parameters in `cloudformation/template.yaml` under the `Parameters` section. -## Populate the Swagger catalog +## Registering Users +Users can self-register by clicking the 'Register' button in the developer portal. Cognito calls the `CognitoUserPoolsConfirmationStrategyFunction` to determine if the user is allowed to register themselves. By default, this function always accepts the user into the user pool, but you can customize the body of the function either in a local repository (followed by packaging and deploying) or in the lambda console. If you intend for the developer portal to be 'private' to some group of users (and not globally / freely accessible), you will need to write a lambda function that enforces your business logic for user registration. Documentation on this lambda function's use can be found [here](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html). +### Promoting a User to an Admin +Admin users can manage what APIs are visible to normal users and whether or not SDK generation is enabled (per api) for normal users. To promote a user to an admin, go to the Cognito console in the account the developer portal is in, select User Pools, then select the correct User Pool for the dev portal. From there, choose Users and groups, click on the users' name, choose Add to group, and select the group named `STACK-NAMEAdminsGroup`. This user is now an admin; if they're currently logged in, they will have to log out and back in to receive admin credentials. + +## Populate the API catalog By default the Developer Portal won't list any APIs. You will have to pick and choose which APIs to show. There are 2 types of APIs: ### Subscribable APIs For an API to be subscribable, they must be managed by Amazon API Gateway. The Developer Portal can let a user associate their API Key with these APIs (via the Subscribe button) so they can start calling and developing on these APIs. To list a subscribable API: -1. Associate that API & stage to a usage plan. -2. Export the API's Swagger (must export as JSON, with API GW extensions) from the stage -> Note: If you're using custom domains in API Gateway, you will need to rename the Swagger file in the format `apiId_stageName.json` and upload it to the `ArtifactsS3Bucket` (actual name provided as a parameter override on the CLI when deploying) in the `catalog` folder. An example might be named `d89n46zud1_production.json`. Note that this is case sensitive! -3. Upload the exported Swagger file to the `catalog` folder which will cause a `catalog.json` file to be generated automatically. This file should contain a mapping of usage plans to api-stage with the Swagger for that api-stage inline. -> Note: The `catalog.json` file will be automatically re-built every time a file is added or removed from the `catalog` folder. If you associate or disassociate a new api-stage to your usage plan, you will need to add or remove a Swagger file from the `catalog` folder in order for the `catalog.json` file to be current. +1. In API Gateway's console or CLI, associate that API & stage to a usage plan. +2. Log into the developer portal using an admin account and go to the Admin Panel tab. +3. In the "Displayed" column, click "False". ### Non-subscribable APIs -The Developer can also list APIs that are managed outside of Amazon API Gateway (eg. APIs hosted on-premise). The Developer Portal won't be able to associate an API Key with the API automatically however they can still test the APIs. +The Developer can also list APIs that are managed outside of Amazon API Gateway (e.g., APIs hosted on-premise). The Developer Portal won't be able to associate an API Key with the API automatically; however, customers can still test the APIs. To list a non-subscribable API: -1. Upload the Swagger file for your API to the `catalog` folder. (See above for additional notes and details). +1. Log into the developer portal using an admin account and go to the Admin Panel tab. +2. In the "Generic APIs" table, click "Add API", select an API specification file (Swagger or OAS3 in .json, .yaml, or .yml), and upload it. ### Testing your APIs @@ -90,146 +94,28 @@ If you chose `UseRoute53Nameservers=true`, after the deployment finishes, go to If you chose `UseRoute53Nameservers=false`, instead point your nameservers at the cloudfront distribution's URL. ### Add custom content and brand the Developer Portal -See [Customization section](#customization) - -### Add an approval workflow to register new users -See [Components section](#cognito-user-pool-confirmation-strategy-lambdacognito-user-pools-confirmation-strategy) +See [this page on customization](https://github.com/awslabs/aws-api-gateway-developer-portal/wiki/Customization) ## Updating to a new version -The Developer Portal follows the semantic versioning scheme (major.minor.patch). Changes to the minor or patch version are backwards compatible so you should feel safe to get the latest version. +The Developer Portal follows the semantic versioning scheme (major.minor.patch). Changes to the minor or patch version are backwards compatible so you should feel safe to get the latest version. For changes to major versions, please see [this page on updating](https://github.com/awslabs/aws-api-gateway-developer-portal/wiki/Upgrading/_edit). ### To update a SAM deployment: 1. Get the latest version from GitHub (Clone/Pull/Download). -2. When deploying follow the same steps as previous and use the same values for the parameters. The only difference is passing in a new value for "-StaticAssetRebuildToken". You can use any string for this as long as it is different than previously used. If you followed the instructions above and it is the first time you're updating, you can use any non-empty string (default value is ""). +2. When deploying follow the same steps as previous and use the same values for the parameters. The only difference is passing in a new value for the stack parameter StaticAssetRebuildToken. You can use any string for this as long as it is different than previously used. ### To update a SAR deployment -1. When deploying follow the same steps as previous and use the same values for the parameters. The only difference is passing in a new value for "-StaticAssetRebuildToken". You can use any string for this as long as it is different than previously used (default value is "defaultRebuildToken"). +1. When deploying follow the same steps as previous and use the same values for the parameters. The only difference is passing in a new value for the stack parameter StaticAssetRebuildToken. ## Components - -![Alt text](/images/highLevelDiagram.png?raw=true) - -### SAM Stack (template.yaml) - -All the components in the developer portal are managed by the SAM stack defined in template.yaml. New application components can be added to this template. Configuration values are fed to this template from the parameter overrides provided on the command line. If overrides are not provided, default values are used. - -### UI (/app) - -The UI is a simple React application hosted in an S3 bucket. The assets are uploaded to the S3 bucket by the static-asset-uploader lambda function. The client side code communicates with the application backend via an API Gateway proxy API. For more information on updating the UI, see `./dev-portal/README.md`. - -### Application Backend (/lambdas/backend) - -The application backend is a Lambda function built on the [aws-serverless-express](https://github.com/awslabs/aws-serverless-express) library. The backend is responsible for login/registration, API subscription/unsubscription, usage metrics, and handling product subscription redirects from AWS Marketplace. - -The backend function runs with escalated privileges (defined as LambdaExecutionRole in the CloudFormation template) and can be used to call other AWS services such as the API Gateway control plane or DynamoDB. - -All resources in the API require AWS SigV4 authentication (i.e. via Cognito) with the exception of /register and the marketplace redirection resource. - -By default, the backend implementation assumes a one-to-one association between authenticated users (Cognito identities) and API Gateway API Keys. A given user can be subscribed to multiple usage plans using the same API Key. - -### Cognito User Pool Confirmation Strategy (/lambda/cognito-user-pools-confirmation-strategy) - -This lambda function (right now) is called for every registration request, but always returns true. This is a placeholder function for you to edit with your own logic for approval. We expect there won't be a single approval/workflow logic that will work for everyone, so we created a placeholder Lambda function that you can easily extend to have your own logic. - -If you're consuming the dev portal via SAM, you should be able to change the contents of the lambdas/cognito-user-pools-confirmation-strategy/index.js file, then run a CloudFormation stack update with the new lambda body. - -### AWS Marketplace SNS Listener Function (Optional) (/lambda/listener) - -The listener Lambda function will be triggered when customers subscribe or unsubscribe to your product through the AWS Marketplace console. AWS Marketplace will generate a unique SNS Topic where events will be published for your product. This is configurable via 'marketplaceSubscriptionTopic' configuration in package.json. After changing this you will need to run 'npm run update-stack' and 'npm run subscribe-listener' to subscribe the listener function. - -From the listener function you can manage your Usage Plan Keys through API Gateway to grant/revoke access to your APIs as well as implement any other subscription/unsubscription business logic. If you have multiple marketplace products, you will need to subscribe the listener function to the SNS topic for each product. +For an overview of the components of the developer portal, please see [this page](https://github.com/awslabs/aws-api-gateway-developer-portal/wiki/Components). ## Debugging -You can trace and troubleshoot the Lambda functions using CloudWatch Logs. See this [blog post](https://aws.amazon.com/blogs/compute/techniques-and-tools-for-better-serverless-api-logging-with-amazon-api-gateway-and-aws-lambda/) for more information. - -## Customization -After deployment, you can overwrite certain files in the S3 bucket to update images, styling and the content of specific pages. All customizations live in the `custom-content` folder of the bucket defined by the `DevPortalSiteS3BucketName` parameter in the sam deploy command (default value "custom-prefix-dev-portal-static-assets"). - -> By default, on upload to the S3 bucket, the permissions are restricted. Make sure that "Everyone" has Read permissions to the files in the S3 bucket otherwise you might see some components not displaying properly. - -> By default, the easy customizations described below **won't be updated by subsequent deployments**. This makes it safe to deploy architectural changes to the Developer Portal without overwriting your branding and content changes. To override this behavior, see [Advanced Customization](#advanced-customization) below. - -#### Images - -You can update the logo that appears in the navbar, the image that appears on the Home page, and the images that appear for each api on the API details pages. - -> All images must be `.png`. - -- `/custom-content/nav-logo.png` - - The logo in that appears in the navbar. Replace it to use your own image. - -- `/custom-content/home-image.png` - - Primary image displayed on the Home page. Replace it to use your own image. - -- `/custom-content/api-logos/default.png` - - The default image used when a specific api image is not provided. Replace it to use your own image. - -- `/custom-content/api-logos/{apiId}_{stage}.png` - - A custom image for a given API and Stage. If provided will be displayed instead of the `default.png` - - e.g. `/custom-content/api-logos/s8df5s3dd_Prod.png` - - -#### Styling - -Replace the `/custom-content/styles.css` with your own CSS Styling. Note that this stylesheet is loaded **before** all other styles in the project. Be sure to make sure your styles do not collide. - -#### Content - -Content on the Home page, the Getting Started page can be modified by updating the markdown files in `/custom-content/content-fragments`. - -Each file begins with a [yaml front matter](https://jekyllrb.com/docs/front-matter/) block. This front matter is used to fill in data beyond the content of the page. - -```yaml ---- -title: Navbar Header # Display in the navbar -header: Main Page Header # Main headline on the page ---- - -Your content starts here. -``` - -The content of the page is rendered using [GitHub-flavoured markdown](https://github.github.com/gfm/). You can also nest HTML inside each markdown fragment if you need more complex layouts. - -```md -# My Content Header! - -Some content content... - -[](https://aws.amazon.com/api-gateway/) -``` - -The `Home` page takes the following front matter: - -- `title`: Text that appears in the navbar. -- `header`: Main headline on the Home page. -- `tagline`: Secondary headline on the Home page. -- `gettingStartedButton`: Text of the "Getting Started" button. -- `apiListButton`: Text of the "Our APIs" button. - -The `APIs` page takes the following front matter: -- `title`: Text that appears in the navbar. - -The `GettingStarted` page takes the following front matter: -- `title`: Text that appears in the navbar. - -### Advanced customization - -In addition to the easy customization described above, you can make changes in your cloned copy of the repository, version with git, and package & deploy with SAM. **You must include the `StaticAssetRebuildToken` as part of the deployment.** - -To pull in new versions of the dev portal, merge or rebase in the upstream changes. - -> By default, the easy customizations described above **won't be updated by subsequent deployments**. If you would prefer to overwrite all files in the s3 bucket on a deploy, pass the `StaticAssetRebuildMode=overwrite` argument to your `sam deploy` command in addition to the `StaticAssetRebuildToken`. See [Advanced Customization](#advanced-customization) below. - +You can trace and troubleshoot the Lambda functions using CloudWatch Logs. See [this blog post](https://aws.amazon.com/blogs/compute/techniques-and-tools-for-better-serverless-api-logging-with-amazon-api-gateway-and-aws-lambda/) for more information. ## Tear-down -Deleting developer portal should be as easy as deleting the cloudformation stack. This will empty the `ArtifactsS3Bucket` and `DevPortalSiteS3Bucket` s3 buckets, including any custom files! Note that this will not delete any api keys provisioned by the developer portal. If you would like to delete api keys provisioned through the developer portal but not those provisioned through other means, make sure to download a backup of the `Customers` DDB table, which will list the provisioned api keys. +Deleting the developer portal should be as easy as deleting the cloudformation stack. This will empty the `ArtifactsS3Bucket` and `DevPortalSiteS3Bucket` s3 buckets, including any custom files! Note that this will not delete any api keys provisioned by the developer portal. If you would like to delete api keys provisioned through the developer portal but not those provisioned through other means, make sure to download a backup of the `Customers` DDB table before deleting the cloudformation stack. This table lists the provisioned api keys that will need to be cleaned up afterwards. ## Marketplace SaaS Setup Instructions diff --git a/README_SAR.md b/README_SAR.md index 1d986c281..e128199f0 100644 --- a/README_SAR.md +++ b/README_SAR.md @@ -6,6 +6,7 @@ The Serverless Developer Portal is an application that you use for developer eng #### To deploy 1. Enter a name for ArtifactsS3BucketName: this will create an Amazon S3 bucket with that name for storing the catalog metadata. 1. Enter a name for DevPortalSiteS3BucketName: this will create an Amazon S3 bucket with that name for the web application code. +1. Enter a unique prefix for CognitoDomainNameOrPrefix; this will be used in the sign in page's URL. 1. You should leave all the other settings as-is unless you need to change them. For example, you can optionally enter a custom domain name in the CustomDomainName field. 1. Acknowledge that the app uses custom roles by checking the box "I acknowledge that this app creates custom IAM roles." 1. Choose Deploy. @@ -16,14 +17,14 @@ The Serverless Developer Portal is an application that you use for developer eng 1. Open the Outputs section. The URL for the developer portal is specified in the WebSiteURL property. #### Updating to a new version -The Developer Portal follows the semantic versioning scheme (major.minor.patch). Changes to the minor or patch version are backwards compatible so you should feel safe to get the latest version. +The Developer Portal follows the semantic versioning scheme (major.minor.patch). Changes to the minor or patch version are backwards compatible so you should feel safe to get the latest version. For changes to major versions, please see [this page on updating](https://github.com/awslabs/aws-api-gateway-developer-portal/wiki/Upgrading/_edit). To update: -1. When deploying follow the same steps as previous and use the same values for the parameters. The only difference is passing in a new value for "-StaticAssetRebuildToken". You can use any string for this as long as it is different than previously used (default value is "defaultRebuildToken"). +1. When deploying follow the same steps as previous and use the same values for the parameters. The only difference is passing in a new value for the stack parameter StaticAssetRebuildToken. You can use any string for this as long as it is different than the previously used string. #### Next steps 1. Publish an API on it for your customer to look at. Learn how to do that [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-developer-portal.html#apigateway-developer-portal-publish). -1. Customize, own, and brand the portal. Learn how to do that [here](https://github.com/awslabs/aws-api-gateway-developer-portal#customization). +1. Customize, own, and brand the portal. Learn how to do that [here](https://github.com/awslabs/aws-api-gateway-developer-portal/wiki/Customization). 1. Setup a custom domain on it your customers recognize your brand and the associated APIs. Learn how to do that [here](https://github.com/awslabs/aws-api-gateway-developer-portal#before-going-to-production). To learn more about the API Gateway Serverless Developer Portal, read the [documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-developer-portal.html) or visit the [GitHub repository](https://github.com/awslabs/aws-api-gateway-developer-portal). For more information about Amazon API Gateway, visit the API Gateway [product page](https://aws.amazon.com/api-gateway/). diff --git a/__tests__/cfn-integration-test.js b/__tests__/cfn-integration-test.js new file mode 100644 index 000000000..1b222aa6d --- /dev/null +++ b/__tests__/cfn-integration-test.js @@ -0,0 +1,112 @@ +const fs = require('fs') +const rp = require('request-promise') + +describe('template.yaml', () => { + // NOTE: These tests all assume that the CFN template has already been packaged *PER REGION*! + const cfnTimeout = 75, + // run the test with a timeout of slightly longer than double the CFN stack timeout + testTimeout = 1000*60*cfnTimeout*2 + 1 + const _console = console.log + + async function commonTest(region, stackMiddlefix) { + let unixTimestamp = Math.floor(new Date() / 1000), + stackName = `cfn-integ-${ stackMiddlefix }-${ unixTimestamp }`, + s3Params = { + // CFN, when reading the template from S3, requires the S3 bucket to be in the same region as the CFN stack... + Bucket: `dev-portal-integ-${ region }`, + Body: fs.readFileSync(`./cloudformation/packaged-${region}.yaml`), + Key: stackName + }, + cfnParams = { + StackName: stackName, + Capabilities: ['CAPABILITY_NAMED_IAM', 'CAPABILITY_AUTO_EXPAND'], + Parameters: [ + { + ParameterKey: 'DevPortalSiteS3BucketName', + ParameterValue: `integ-${ stackMiddlefix }-${ unixTimestamp }-dev-portal-test` + }, + { + ParameterKey: 'ArtifactsS3BucketName', + ParameterValue: `integ-${ stackMiddlefix }-${ unixTimestamp }-artifact-bucket` + }, + { + ParameterKey: 'DevPortalCustomersTableName', + ParameterValue: `Customers${ unixTimestamp }` + }, + { + ParameterKey: 'CognitoDomainNameOrPrefix', + ParameterValue: `integ-${ stackMiddlefix }-${ unixTimestamp }` + } + ], + // RoleARN: 'STRING_VALUE', + TimeoutInMinutes: cfnTimeout + } + + console.log('commonTest', region) + + const AWS = require('aws-sdk') + AWS.config.update({ region: region }) + + // pin versions of SDKs + const cfn = new AWS.CloudFormation({ region: region }), + s3 = new AWS.S3({ region: region }), + logger = function (input) { + _console(`${region}:${stackName}:${input}`) + } + + // Upload the packaged template to S3, then use the resulting URL in the CFN createStack call + // This is necessary because the file is too large to deliver in-line to CFN + cfnParams.TemplateURL =(await s3.upload(s3Params).promise()).Location + + logger('createStack call starting.') + await cfn.createStack(cfnParams).promise() + logger('createStack call succeeded.') + + logger('stackExists waiter starting.') + await cfn.waitFor('stackExists', { StackName: stackName }).promise() + logger('stackExists waiter succeeded.') + + logger('stackCreateComplete waiter starting.') + let devPortalUrl = + (await cfn.waitFor('stackCreateComplete', { StackName: stackName }).promise()).Stacks[0].Outputs + .find((output) => output.OutputKey === 'WebsiteURL').OutputValue + logger('stackCreateComplete waiter succeeded.') + + logger(`verifying that stack is available at ${devPortalUrl} .`) + let staticIndex = await rp(devPortalUrl) + + expect(staticIndex.includes('
0?("string"==typeof e||u.objectMode||Object.getPrototypeOf(e)===c.prototype||(e=function(t){return c.from(t)}(e)),r?u.endEmitted?t.emit("error",new Error("stream.unshift() after end event")):L(t,u,e,!0):u.ended?t.emit("error",new Error("stream.push() after EOF")):(u.reading=!1,u.decoder&&!n?(e=u.decoder.write(e),u.objectMode||0!==e.length?L(t,u,e,!1):N(t,u)):L(t,u,e,!1))):r||(u.reading=!1));return function(t){return!t.ended&&(t.needReadable||t.length>1,d=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,h=r?0:i-1,p=r?1:-1,g=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(a=isNaN(t)?1:0,u=c):(u=Math.floor(Math.log(t)/Math.LN2),t*(f=Math.pow(2,-u))<1&&(u--,f*=2),t+=u+l>=1?d/f:d*Math.pow(2,1-l),t*f>=2&&(u++,f/=2),u+l>=c?(a=0,u=c):u+l>=1?(a=(t*f-1)*Math.pow(2,o),u+=l):(a=t*Math.pow(2,l-1)*Math.pow(2,o),u=0));o>=8;e[n+h]=255&a,h+=p,a/=256,o-=8);for(u=u<>1,d=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,h=r?0:i-1,p=r?1:-1,g=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(a=isNaN(t)?1:0,u=c):(u=Math.floor(Math.log(t)/Math.LN2),t*(f=Math.pow(2,-u))<1&&(u--,f*=2),t+=u+l>=1?d/f:d*Math.pow(2,1-l),t*f>=2&&(u++,f/=2),u+l>=c?(a=0,u=c):u+l>=1?(a=(t*f-1)*Math.pow(2,o),u+=l):(a=t*Math.pow(2,l-1)*Math.pow(2,o),u=0));o>=8;e[n+h]=255&a,h+=p,a/=256,o-=8);for(u=u<s&&(r=s-u),o=r;o>=0;o--){for(var l=!0,m=0;mi&&(n=i):n=i;var o=t.length;if(o%2!==0)throw new TypeError("Invalid hex string");n>o/2&&(n=o/2);for(var a=0;ai)&&(r=i),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var o=!1;;)switch(n){case"hex":return b(this,e,t,r);case"utf8":case"utf-8":return v(this,e,t,r);case"ascii":return S(this,e,t,r);case"latin1":case"binary":return I(this,e,t,r);case"base64":return N(this,e,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return C(this,e,t,r);default:if(o)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),o=!0}},u.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var T=4096;function A(e,t,r){var n="";r=Math.min(e.length,r);for(var i=t;i