4.11.0 • Published 2 months ago

@squared-functions/cloud v4.11.0

Weekly downloads
1
License
MIT
Repository
github
Last release
2 months ago

squared-functions 0.5

These are some of the available options when creating archives or copying files with squared 2.1.

// NOTE: format: zip | tar | gz/tgz | compress: gz | br

squared.settings.outputArchiveFormat = 'tar'; // Default: "zip"

squared.saveAs('archive1', { // OR: archive1.gz
    format: 'gz',
    assets: [
        {
            pathname: 'app/src/main/res/drawable',
            filename: 'ic_launcher_background.xml',
            uri: 'http://localhost:3000/common/images/ic_launcher_background.xml',
            compress: [{ format: 'gz', level: 9 }, { format: 'br' }]
        }
    ],

    // All attributes are optional (case-sensitive except extension)
    exclusions: {
        glob: ['**/*.zip'],
        pathname: ['app/build', 'app/libs'],
        filename: ['ic_launcher_foreground.xml'],
        extension: ['iml', 'pro'],
        pattern: ['output', /grad.+?\./i, '\\.git']
    }
});

Image conversion can be achieved using the "commands" array property in a FileAsset object. The supported formats are:

  • png - r/w
  • jpeg - r/w
  • webp - r/w
  • bmp - r/w
  • gif - r
  • tiff - r

NOTE: WebP support requires manual NPM installation of the binaries.

  • dwebp - r
  • cwebp - w

npm install dwebp-bin && npm install cwebp-bin

// All commands are optional except "format". Outer groupings and inner brackets are required.

+ <format>

- @|%
- ( minSize(n,0) , maxSize(n,*) )
- ( width(n|auto) x height(n|auto) [bilinear|bicubic|hermite|bezier]? ^(cover|contain|scale)?[left|center|right|top|middle|bottom]? #background-color? )
- ( left(+|-n) , top(+|-n) | cropWidth(n) x cropHeight(n) )
- { ...rotate(n) #background-color? }
- | opacity(0.0-1.0) OR jpeg_webp_quality(0-100)[photo|picture|drawing|icon|text]?[0-100]?| // cwebp: -preset -near_lossless
- !method // no arguments (e.g. jimp: dither565|greyscale|invert|normalize|opaque|sepia)

@ - replace
% - smaller

Placing an @ symbol (png@) after the format will remove the original file from the package. Using the % symbol (png%) instead will choose the smaller of the two files. You can also use these commands with the setting "convertImages" in the Android framework as a string with the "+" chain format.

// NOTE: Multiple transformations per asset use the ':' as the separator when using "data-chrome-file"

webp(50000,*)(800x600[bezier]^contain[right|bottom]#FFFFFF)(-50,50|200x200){45,135,215,315#FFFFFF}|0.5||100[photo][75]|!opaque!greyscale
const options = {
    assets: [
        {
            pathname: 'images',
            filename: 'pencil.png',
            mimeType: 'image/png',
            commands: ['jpeg'],
            uri: 'http://localhost:3000/common/images/pencil.png'
        },
        {
            pathname: 'images',
            filename: 'pencil.png',
            mimeType: 'image/png',
            commands: ['bmp@(50000,100000)'],
            uri: 'http://localhost:3000/common/images/pencil.png'
        }
    ]
};

TinyPNG is used for compression and supports only PNG and JPEG.

Gulp

Tasks can be performed with Gulp to take advantage of their pre-built plugin repository. Gulp is the final stage preceding archiving or copying after file content has been downloaded and transformed.

// squared.settings.json

{
  "gulp": {
    "minify": "./gulpfile.js"
    "beautify": "./gulpfile.js",
    "compress": "./gulpfile.android.js"
  }
}

// chrome
{
  "selector": "head > script:nth-of-type(1)",
  "type": "js",
  "tasks": [
    "minify",
    "beautify"
  ]
}

// android
const options = {
    assets: [
        {
            pathname: 'images',
            filename: 'pencil.png',
            mimeType: 'image/png',
            tasks: ['compress'],
            uri: 'http://localhost:3000/common/images/pencil.png'
        }
    ]
};
<!-- chrome -->
<script src="/common/system.js" data-chrome-tasks="minify+beautify"></script>

<!-- android -->
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/12005/harbour1.jpg" data-android-tasks="compress" />
// gulpfile.js

const gulp = require('gulp');
const uglify = require('gulp-uglify');
 
gulp.task('minify', () => {
  return gulp.src('*')
    .pipe(uglify())
    .pipe(gulp.dest('./'));
});

// NOTE: SRC (temp) and DEST (original) always read and write to the current directory

Renaming files with Gulp is not recommended. It is better to use the "saveAs" or "filename" attributes when the asset is part of the HTML page.

CHROME: Saving web page assets

Bundling options are available with these HTML tag names.

  • saveAs: html + script + link
  • exportAs: script + style
  • exclude: script + link + style

Files with the same path and filename will automatically create a bundle assuming there are no conflicts in call ordering.

{
  "selector": "head > script:nth-of-type(2), head > script:nth-of-type(3)",
  "type": "js",
  "saveAs": "js/modules2.js"
}

JS and CSS files can be bundled together with the "saveAs" or "exportAs" action. Multiple transformations per asset can be chained using the "+" symbol. Whitespace can be used between anything for readability.

Separator : ::

Same (1-2): ~
Chain  (2): +
Option (3): |
* preserve - Prevent unused styles from being deleted (css)
* inline - Content is extracted and rendered inline with <script> or <style> (js/css)
* compress
    - png TinyPNG service for PNG or JPEG
    - gz  Gzip
    - br  Brotli
* base64 - Content is rendered inline with base64 encoding (image)
<link data-chrome-file="saveAs:css/prod.css::beautify::preserve|inline" rel="stylesheet" href="css/dev.css" />
<style data-chrome-file="exportAs:css/prod.css::minify+beautify::compress[gz]">
    body {
        font: 1em/1.4 Helvetica, Arial, sans-serif;
        background-color: #fafafa;
    }
</style>
<script data-chrome-file="saveAs:js/bundle1.js::minify" src="/dist/squared.js"></script>
<script data-chrome-file="saveAs:js/bundle1.js::minify" src="/dist/squared.base.js"></script>
<script data-chrome-file="saveAs:js/bundle2.js" src="/dist/chrome.framework.js"></script>

Bundling with inline commands using a 1-2-1 format may cause the generated bundle to execute incorrectly. Other configuration methods will create a new file when it finds any conflicts. The advantages of bundling this way gives you the ability to debug source code inside <script> elements.

Raw assets: saveTo command

You can use images commands with saveTo (directory) on any element when the image is the primary display output. Encoding with base64 is also available using the "::base64" commmand as the third argument. Transformations are given arbitrary filenames and the original file is preserved.

<!-- NOTE: img | video | audio | source | track | object | embed | iframe -->

<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/12005/harbour1.jpg"
     data-chrome-file="saveTo: ../images/harbour :: png(10000,75000)(800x600[bezier]^contain[right|bottom]) :: compress|base64" />

<!-- "saveTo:~::~::base64" -->

Built-In plugins

JS and CSS files can be optimized further using these settings:

  • beautify
  • minify
  • es5 (Babel)
  • es5-minify (UglifyJS)
  • custom name

You can also define your own optimizations in squared.settings.json:

These particular plugins can be configured using a plain object literal. Manual installation is required when using any of these packages npm run install-chrome. Transpiling with Babel is also configurable with a .babelrc file in the base folder for any presets and additional settings. Other non-builtin minifiers can similarly be applied and chained by defining a custom string-based synchronous function.

chrome -> html | js | css -> npm package name -> custom name
  • Function object
  • file relative to serve.js
  • function closure
// squared.settings.json

{
  "chrome": {
    "html": { // built-in minifier
      "posthtml": {
        "transform": {
          "plugins": [
            ["posthtml-doctype", { "doctype": "HTML 5" }], // Plugins have be installed with NPM manually
            ["posthtml-include", { "root": "./", "encoding": "utf-8" }]
          ]
        },
        "transform-output": {
          "directives": [
            { "name": "?php", "start": "<", "end": ">" }
          ]
        }
      },
      "prettier": {
        "beautify": {
          "parser": "html",
          "printWidth": 120,
          "tabWidth": 4
        }
      }
    },
    "js": { // custom function (chrome -> eval_function: true)
      "terser": {
        "minify-example": "function(context, value, output) { return context.minify(value, output).code; }", // "minify-example-output" creates scoped variable "output"
        "minify-example-output": {
          "keep_classnames": true
        }
      },
      "@babel/core": {
        "es5-example": "./es5.js" // startsWith('./ | ../')
      },
      "rollup": {
        "bundle-es6": {
          "plugins": [
            ["@rollup/plugin-json", { compact: true }]
          ],
          "external": ["lodash"]
        },
        "bundle-es6-output": "./rollup.output.config.json" // supplemental JSON configuration settings use the "-output" suffix
      }
    },
    "css": {
      "postcss": {
        "transform": {
          "plugins": ["autoprefixer", "cssnano"] // Plugins have be installed with NPM manually
        }
      },
      "node-sass": { // npm i node-sass
        "sass-example": "function(context, value) { return context.renderSync({ data: value }, functions: {}); }" // first transpiler in chain
      }
    }
  }
}
// es5.js

function (context, value, output /* optional: "@babel/core-output" */) {
    const options = { presets: ['@babel/preset-env'] }; // <https://babeljs.io/docs/en/options>
    return context.transformSync(value, output).code;
}

The same concept can be used inline anywhere using a <script> tag with the type attribute set to "text/template". The script template will be completely removed from the final output.

// "es5-example" is a custom name (chrome -> eval_text_template: true)

<script type="text/template" data-chrome-template="js::@babel/core::es5-example">
function (context, value, output, input /* optional */) {
    const options = { ...output, presets: ['@babel/preset-env'], sourceMaps: true };
    const result = context.transformSync(value, options);
    if (result) {
        if (result.map) {
            input.nextMap('babel', result.map, result.code);
        }
        return result.code;
    }
}
</script>

Here is the equivalent YAML settings and when available has higher precedence than JSON settings.

Modifying content attributes

There are possible scenarios when a transformation may cause an asset type to change into another format.

<!-- before -->
<link id="sass-example" rel="alternate" type="text/plain" href="css/dev.sass" />
{
  "selector": null,
  "type": "css",
  "filename": "prod.css",
  "attributes": [
    {
      "name": "id"
    },
    {
      "name": "rel",
      "value": "stylesheet"
    },
    {
      "name": "type",
      "value": "text/css"
    },
    {
      "name": "title",
      "value": ""
    },
    {
      "name": "disabled",
      "value": null
    }
  ],
  "process": [
    "node-sass"
  ]
}
<!-- after -->
<link rel="stylesheet" type="text/css" title="" disabled href="css/prod.css" />

External configuration

JSON (json/js) configuration is optional and is provided for those who prefer to separate the bundling and transformations from the HTML. Any assets inside the configuration file will override any settings either inline or from JavaScript. You can also use the equivalent in YAML (yml/yaml) for configuring as well.

interface FileModifiers {
    preserve?: boolean; // type: css
    inline?: boolean; // type: js | css
    base64?: boolean; // type: image
    compress?: { format: string, level?: number }[];
}

interface OutputModifiers {
    tasks?: string[];
    watch?: boolean | { interval?: number | expires?: string }; // type: js | css | image (expires: 1h 1m 1s)
    attributes?: { name: string, value?: string }[];
    cloudStorage?: CloudService[];
    ignore?: boolean;
    exclude?: boolean;
}

interface AssetCommand extends FileModifiers, OutputModifiers {
    selector?: string;
    type?: string;
    saveAs?: string; // type: js | css
    exportAs?: string; // type: js | css
    saveTo?: string; // type: image | video | audio (transforms create multiple files and are given a UUID filename)
    pathname?: string; // alias for "saveTo"
    filename?: string; // type: html | ...image
    process?: string[]; // type: js | css
    commands?: string[]; // type: image
    template?: {
        module?: string;
        identifier?: string;
        value?: string;
    };
}
squared.saveAs('bundle.zip', { configUri: 'http://localhost:3000/chrome/bundle.yml' });

Here is the equivalent page using only inline commands with "data-chrome-file" and "data-chrome-tasks".

Cloud storage

Manual installation of the SDK is required and an account with a cloud storage provider.

* Amazon AWS
  - npm install aws-sdk
  - S3: https://aws.amazon.com/free (5GB)

* Microsoft
  - npm install @azure/storage-blob
  - Azure: https://azure.microsoft.com/en-us/free (5GB)

* Google
  - npm install @google-cloud/storage
  - GCS: https://cloud.google.com/free (5GB)

Other providers will be integrated similarly except for credential verification.

// NOTE: Optional fields are supported by all services

{
  "selector": "#picture1",
  "type": "image",
  "commands": [
    "png(100x200){90,180,270}" // Uploaded with UUID filename
  ],
  "cloudStorage": [
    {
      "service": "s3",
      "bucket": "squared-001",
      "accessKeyId": "**********", // Using settings (optional)
      "secretAccessKey": "**********", // Using settings (optional)
      "active": true, // Rewrites "src" to cloud storage location (optional)
      "localStorage": false, // Removes all files from archive or local disk (optional)
      "uploadAll": true, // Include transforms (optional)
      "filename": "picture1.webp" // Bucket filename (optional)
    },
    {
      "service": "azure",
      "container": "squared-002",
      "accountName": "**********",
      "accountKey": "**********",
      "apiEndpoint": "http://squaredjs.azureedge.net/squared-002" // e.g. CDN (optional)
    },
    {
      "service": "gcs",
      "bucket": "squared-003",
      "keyFilename": "**********", // Path to JSON credentials
      "settings": "main" // Load host configuration at instantiation (optional)
    }
  ]
}

Inline commands are not supported. Serving CSS files from cloud storage or CDN requires every image inside the file to be hosted with an absolute URL.

squared.saveAs('index.zip', {
    configUri: 'http://localhost:3000/chrome/bundle.yml',
    saveAs: {
        image: { // Non-element images using url() method
            cloudStorage: [{
                service: 's3',
                bucket: 'squared-001',
                active: true,
                settings: 'main'
            }]
        }
    }
});

Options: Production / saveAs

The entire page can similarly be transformed as a group using the "saveAs" attribute in options. Cloud storage can be used for all assets except HTML using the same configuration as element selectors.

squared.saveAs('index.zip', {
    removeUnusedStyles: false, // Use only when you are not switching classnames with JavaScript
    productionRelease: false, // Ignore local url rewriting and load assets using absolute paths
    preserveCrossOrigin: false, // Ignore downloading a local copy of assets hosted on other domains

    // All attributes are optional except "filename" for <script> and <link>.
    saveAs: {
        html: { filename: 'index.html', format: 'beautify', attributes: [{ name: 'lang', value: 'en' }] },
        script: { pathname: '../js', filename: 'bundle.js', format: 'es5+es5-minify' },
        link: { pathname: 'css', filename: 'bundle.css', preserve: true, inline: true },
        image: { base64: true },
        base64: { format: 'png' }
    }
}); 

Setting the active cloud storage filename to a JS/CSS bundle will have no effect since it is possible more than one bundle will be created.

Asset exclusion

You can exclude unnecessary processing files using the dataset attribute in <script|link|style> tags.

<script data-chrome-file="exclude" src="/dist/squared.js"></script>
<script data-chrome-file="exclude" src="/dist/squared.base.js"></script>
<script data-chrome-file="exclude" src="/dist/chrome.framework.js"></script>
<script data-chrome-file="exclude">
    squared.setFramework(chrome);
    squared.save();
</script>

You can similarly prevent an asset from being downloaded or transformed using the "ignore" command.

<iframe src="https://www.google.com/maps" data-chrome-file="ignore" />

LICENSE

MIT

4.11.0

2 months ago

4.10.5

3 months ago

4.10.4

3 months ago

4.10.3

4 months ago

4.10.2

5 months ago

4.10.1

5 months ago

4.9.20

10 months ago

4.10.0

9 months ago

4.8.45

11 months ago

4.9.19

11 months ago

4.9.18

11 months ago

4.9.8

1 year ago

4.9.9

1 year ago

3.10.0

1 year ago

4.8.41

1 year ago

4.8.40

1 year ago

4.8.43

1 year ago

4.9.11

1 year ago

4.8.42

1 year ago

4.9.10

1 year ago

4.9.13

1 year ago

4.8.44

1 year ago

4.9.12

1 year ago

4.9.15

1 year ago

4.9.14

1 year ago

4.9.17

1 year ago

4.9.16

1 year ago

4.8.34

1 year ago

4.8.35

1 year ago

4.8.38

1 year ago

4.8.39

1 year ago

4.9.7

1 year ago

4.9.6

1 year ago

4.9.5

1 year ago

3.9.3

1 year ago

3.9.27

1 year ago

4.8.32

1 year ago

4.8.33

1 year ago

4.9.4

1 year ago

4.8.31

1 year ago

4.9.3

1 year ago

4.9.0

1 year ago

4.9.2

1 year ago

4.9.1

1 year ago

3.8.14

1 year ago

3.8.15

1 year ago

3.9.2

1 year ago

3.9.1

1 year ago

3.9.0

1 year ago

4.8.90

1 year ago

4.8.92

1 year ago

4.8.91

1 year ago

4.8.94

1 year ago

4.8.93

1 year ago

4.8.96

1 year ago

4.8.95

1 year ago

4.8.50

1 year ago

4.8.85

1 year ago

4.8.87

1 year ago

4.8.86

1 year ago

4.8.89

1 year ago

4.8.88

1 year ago

4.8.76

1 year ago

4.8.75

1 year ago

4.8.78

1 year ago

4.8.77

1 year ago

4.8.79

1 year ago

4.8.25

1 year ago

4.8.27

1 year ago

4.8.26

1 year ago

4.8.29

1 year ago

4.8.28

1 year ago

4.8.30

1 year ago

4.8.36

1 year ago

4.8.37

1 year ago

3.8.12

1 year ago

3.8.13

1 year ago

3.8.10

1 year ago

3.8.11

1 year ago

4.8.9

1 year ago

4.8.8

1 year ago

4.8.5

1 year ago

4.8.4

1 year ago

4.8.7

1 year ago

4.8.6

1 year ago

4.8.1

1 year ago

4.8.0

2 years ago

4.8.3

1 year ago

4.8.2

1 year ago

4.7.0

2 years ago

4.7.5

2 years ago

4.7.2

2 years ago

4.7.1

2 years ago

4.7.4

2 years ago

4.7.3

2 years ago

3.8.0

2 years ago

4.6.0

2 years ago

3.8.9

1 year ago

3.8.4

2 years ago

3.8.3

2 years ago

3.8.2

2 years ago

3.8.1

2 years ago

3.8.8

1 year ago

3.8.7

1 year ago

3.8.6

1 year ago

3.8.5

2 years ago

4.5.0

2 years ago

4.5.2

2 years ago

4.5.1

2 years ago

4.5.7

2 years ago

4.5.4

2 years ago

4.5.3

2 years ago

4.5.6

2 years ago

4.5.5

2 years ago

4.8.21

1 year ago

4.8.20

1 year ago

4.8.23

1 year ago

4.8.22

1 year ago

4.8.24

1 year ago

4.8.10

1 year ago

4.8.12

1 year ago

4.8.11

1 year ago

4.8.14

1 year ago

4.8.13

1 year ago

4.8.16

1 year ago

4.8.15

1 year ago

4.8.18

1 year ago

4.8.17

1 year ago

4.8.19

1 year ago

3.7.5

2 years ago

3.7.4

2 years ago

3.7.3

2 years ago

3.7.2

2 years ago

3.7.7

2 years ago

3.7.6

2 years ago

4.4.1

2 years ago

4.4.0

2 years ago

4.4.3

2 years ago

4.4.2

2 years ago

4.4.5

2 years ago

4.4.4

2 years ago

4.3.4

2 years ago

4.3.3

2 years ago

4.3.9

2 years ago

4.3.6

2 years ago

4.3.5

2 years ago

4.3.8

2 years ago

4.3.7

2 years ago

4.3.13

2 years ago

4.3.12

2 years ago

4.3.11

2 years ago

4.3.10

2 years ago

4.3.14

2 years ago

4.3.2

2 years ago

4.3.1

2 years ago

4.3.0

2 years ago

4.2.3

2 years ago

4.2.2

2 years ago

4.2.4

2 years ago

4.2.1

2 years ago

4.2.0

2 years ago

3.6.13

2 years ago

3.6.12

2 years ago

3.7.1

2 years ago

3.7.0

2 years ago

3.6.11

2 years ago

4.1.4

2 years ago

4.1.3

2 years ago

4.1.2

2 years ago

4.1.1

2 years ago

4.0.5

2 years ago

4.0.4

2 years ago

4.0.7

2 years ago

4.0.6

2 years ago

4.0.3

2 years ago

3.6.9

2 years ago

3.6.10

2 years ago

4.1.0

2 years ago

4.0.1

2 years ago

4.0.2

2 years ago

3.6.8

2 years ago

2.4.1

2 years ago

2.4.0

2 years ago

2.4.2

2 years ago

3.6.2

2 years ago

3.6.1

2 years ago

3.6.0

2 years ago

4.0.0

2 years ago

3.6.6

2 years ago

3.6.5

2 years ago

3.6.4

2 years ago

3.6.3

2 years ago

3.6.7

2 years ago

3.5.3

2 years ago

3.5.2

2 years ago

3.5.1

2 years ago

3.5.0

2 years ago

3.4.0

2 years ago

3.4.4

2 years ago

3.4.3

2 years ago

3.4.2

2 years ago

3.4.1

2 years ago

3.4.6

2 years ago

3.4.5

2 years ago

3.3.1

2 years ago

3.3.0

2 years ago

3.3.3

2 years ago

3.3.2

2 years ago

3.2.0

2 years ago

2.3.6

2 years ago

3.1.3

2 years ago

3.1.2

2 years ago

3.1.1

2 years ago

3.1.0

2 years ago

3.1.5

2 years ago

3.1.4

2 years ago

3.0.8

2 years ago

2.3.8

2 years ago

2.3.7

2 years ago

2.3.9

2 years ago

1.3.4

2 years ago

2.3.5

3 years ago

3.0.7

3 years ago

3.0.6

3 years ago

2.3.4

3 years ago

2.3.3

3 years ago

3.0.4

3 years ago

3.0.3

3 years ago

3.0.2

3 years ago

3.0.1

3 years ago

3.0.5

3 years ago

1.3.3

3 years ago

1.2.14

3 years ago

2.3.0

3 years ago

2.3.2

3 years ago

2.3.1

3 years ago

2.2.3

3 years ago

2.2.2

3 years ago

3.0.0

3 years ago

1.3.2

3 years ago

1.3.1

3 years ago

1.3.0

3 years ago

1.2.13

3 years ago

2.2.1

3 years ago

2.2.0

3 years ago

1.2.12

3 years ago

2.1.3

3 years ago

1.2.11

3 years ago

2.1.2

3 years ago

2.1.1

3 years ago

2.1.0

3 years ago

2.0.9

3 years ago

1.2.10

3 years ago

2.0.10

3 years ago

1.2.9

3 years ago

2.0.8

3 years ago

2.0.7

3 years ago

2.0.6

3 years ago

2.0.5

3 years ago

1.2.8

3 years ago

2.0.4

3 years ago

1.2.7

3 years ago

2.0.3

3 years ago

2.0.2

3 years ago

2.0.1

3 years ago

2.0.0

3 years ago

1.1.9

3 years ago

1.2.6

3 years ago

1.2.5

3 years ago

1.1.8

3 years ago

1.2.4

3 years ago

1.1.7

3 years ago

1.2.3

3 years ago

1.1.6

3 years ago

1.2.2

3 years ago

1.2.1

3 years ago

1.1.5

3 years ago

1.2.0

3 years ago

1.1.4

3 years ago

1.1.3

3 years ago

1.1.2

3 years ago

1.1.1

3 years ago

1.1.0

3 years ago

1.0.7

3 years ago

0.15.3

3 years ago

1.0.6

3 years ago

0.15.2

3 years ago

1.0.5

3 years ago

0.15.1

3 years ago

0.15.0

3 years ago

1.0.4

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.3

3 years ago

1.0.0

3 years ago

0.14.4

3 years ago

0.14.3

3 years ago

0.14.2

3 years ago

0.14.1

3 years ago

0.14.0

3 years ago

0.13.3

3 years ago

0.13.2

3 years ago

0.13.0

3 years ago

0.13.1

3 years ago

0.12.0

3 years ago

0.11.0

3 years ago

0.10.1

3 years ago

0.10.0

3 years ago

0.9.11

3 years ago

0.9.10

3 years ago

0.9.9

3 years ago

0.9.8

3 years ago

0.9.7

3 years ago

0.9.6

3 years ago

0.9.5

3 years ago

0.9.4

3 years ago

0.9.3

3 years ago

0.9.2

3 years ago

0.9.1

3 years ago

0.9.0

3 years ago

0.8.1

3 years ago

0.8.0

3 years ago

0.7.5

3 years ago

0.7.4

3 years ago

0.7.3

3 years ago

0.7.2

3 years ago

0.7.1

3 years ago

0.7.0

3 years ago

0.6.3

3 years ago

0.6.2

3 years ago

0.6.1

3 years ago

0.6.0

3 years ago

0.5.4

3 years ago

0.5.5

3 years ago

0.5.3

3 years ago

0.5.2

3 years ago

0.5.1

3 years ago

0.5.0

3 years ago