diff --git a/.gitignore b/.gitignore index 291a652..5bbd280 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,17 @@ -test-server \ No newline at end of file +sdkgen2 + +.DS_Store + +node_modules + +lib/core/metadata.js +lib/core/MetadataBlog.js + +website/translated_docs +website/build/ +website/yarn.lock +website/node_modules +website/i18n/* + +/sdkgen +/sdkgen.dwarf diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9e1b30d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +sudo: required + +language: node_js +node_js: + - '8' + +services: + - docker + +script: + - git config --global user.name "${GH_NAME}" + - git config --global user.email "${GH_EMAIL}" + - echo "machine github.com login ${GH_NAME} password ${GH_TOKEN}" > ~/.netrc + - cd website + - yarn install && GIT_USER="${GH_NAME}" yarn run publish-gh-pages + - cd .. + - if [[ "$TRAVIS_TAG" ]] ; then + docker image build . -t cubos/sdkgen -t cubos/sdkgen:$TRAVIS_TAG; + docker image build ./target-node -t cubos/sdkgen/target-node -t cubos/sdkgen/target-node:$TRAVIS_TAG; + docker image build ./target-web -t cubos/sdkgen/target-web -t cubos/sdkgen/target-web:$TRAVIS_TAG; + docker image build ./target-android -t cubos/sdkgen/target-android -t cubos/sdkgen/target-android:$TRAVIS_TAG; + docker image build ./target-node -t cubos/sdkgen/target-node -t cubos/sdkgen/target-node:$TRAVIS_TAG; + else + docker image build .; + fi + +deploy: + provider: script + skip_cleanup: true + script: + - docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD" && docker push cubos/sdkgen:$TRAVIS_TAG + on: + branch: master + tags: true diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..dde5728 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@cubos.io. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/Dockerfile b/Dockerfile index 5f45474..ee435c8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,10 @@ -FROM crystallang/crystal +FROM crystallang/crystal:0.27.0 +ADD . /tmp/ +WORKDIR /tmp +RUN crystal tool format --check +RUN crystal spec +RUN crystal build main.cr -o sdkgen + +FROM crystallang/crystal:0.27.0 CMD ["bash"] -WORKDIR /root -ADD *.cr /tmp/ -RUN crystal build /tmp/main.cr -o sdkgen2 && rm /tmp/*.cr -RUN cp sdkgen2 /usr/bin \ No newline at end of file +COPY --from=0 /tmp/sdkgen /usr/bin diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8f36511 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Cubos + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..75e11eb --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# sdkgen + +![Zé Polvinho](assets/ze_polvinho_sdkgen_small.png) + +The sdkgen is a tool that aims on solving client-server communication and data layout sharing between server, web, android and ios using a description language also called sdkgen. + +[![Build Status](https://travis-ci.org/cubos/sdkgen.svg?branch=master)](https://travis-ci.org/cubos/sdkgen) + +## How to use it + +`crystal run main.cr -- api.sdkgen -o api.ts -t typescript_nodeserver` + + +## TODO + +- [ ] Improve README +- [ ] Create and host documentation page +- [ ] Make it database neutral +- [x] Move associated tooling related to package publishing to travis diff --git a/assets/ze_polvinho_sdkgen.png b/assets/ze_polvinho_sdkgen.png new file mode 100644 index 0000000..a14a872 Binary files /dev/null and b/assets/ze_polvinho_sdkgen.png differ diff --git a/assets/ze_polvinho_sdkgen_small.png b/assets/ze_polvinho_sdkgen_small.png new file mode 100644 index 0000000..44fb5a7 Binary files /dev/null and b/assets/ze_polvinho_sdkgen_small.png differ diff --git a/ast_to_s.cr b/ast_to_s.cr deleted file mode 100644 index 474e141..0000000 --- a/ast_to_s.cr +++ /dev/null @@ -1,108 +0,0 @@ -require "./ast" - -class StringPrimitiveType - def to_s - "string" - end -end - -class IntPrimitiveType - def to_s - "int" - end -end - -class UIntPrimitiveType - def to_s - "uint" - end -end - -class FloatPrimitiveType - def to_s - "float" - end -end - -class DatePrimitiveType - def to_s - "date" - end -end - -class DateTimePrimitiveType - def to_s - "datetime" - end -end - -class BoolPrimitiveType - def to_s - "bool" - end -end - -class BytesPrimitiveType - def to_s - "bytes" - end -end - -class VoidPrimitiveType - def to_s - "void" - end -end - -class OptionalType - def to_s - "#{@base.to_s}?" - end -end - -class ApiDescription - def to_s - custom_types.map(&.to_s).join("\n") + "\n" + - operations.map(&.to_s).join("\n") - end -end - -class Field - def to_s - str = "#{name}: #{type.to_s}" - str += " !secret" if secret - str - end -end - -class CustomType - def to_s - "type #{name} {\n" + - fields.map{|f| " #{f.to_s}\n" }.join + - "}\n" - end -end - -class CustomTypeReference - def to_s - name - end -end - -class GetOperation - def to_s - "get #{name}(#{args.map(&.to_s).join(", ")}): #{return_type.to_s}" - end -end - -class FunctionOperation - def to_s - "function #{name}(#{args.map(&.to_s).join(", ")}): #{return_type.to_s}" - end -end - -class SubscribeOperation - def to_s - "subscribe #{name}(#{args.map(&.to_s).join(", ")}): #{return_type.to_s}" - end -end diff --git a/docs/doc1.md b/docs/doc1.md new file mode 100644 index 0000000..37fa73f --- /dev/null +++ b/docs/doc1.md @@ -0,0 +1,29 @@ +--- +id: doc1 +title: Latin-ish +sidebar_label: Example Page +--- + +Check the [documentation](https://docusaurus.io) for how to use Docusaurus. + +## Lorem + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien. + +## Mauris In Code + +``` +Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut. +``` + +## Nulla + +Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra. + +## Orci + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum. + +## Phasellus + +Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis. diff --git a/docs/doc2.md b/docs/doc2.md new file mode 100644 index 0000000..20c0c95 --- /dev/null +++ b/docs/doc2.md @@ -0,0 +1,7 @@ +--- +id: doc2 +title: document number 2 +--- + +This is a link to [another document.](doc3.md) +This is a link to an [external page.](http://www.example.com) diff --git a/docs/doc3.md b/docs/doc3.md new file mode 100644 index 0000000..bcb9054 --- /dev/null +++ b/docs/doc3.md @@ -0,0 +1,13 @@ +--- +id: doc3 +title: This is document number 3 +--- +Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ac euismod odio, eu consequat dui. Nullam molestie consectetur risus id imperdiet. Proin sodales ornare turpis, non mollis massa ultricies id. Nam at nibh scelerisque, feugiat ante non, dapibus tortor. Vivamus volutpat diam quis tellus elementum bibendum. Praesent semper gravida velit quis aliquam. Etiam in cursus neque. Nam lectus ligula, malesuada et mauris a, bibendum faucibus mi. Phasellus ut interdum felis. Phasellus in odio pulvinar, porttitor urna eget, fringilla lectus. Aliquam sollicitudin est eros. Mauris consectetur quam vitae mauris interdum hendrerit. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + +Duis et egestas libero, imperdiet faucibus ipsum. Sed posuere eget urna vel feugiat. Vivamus a arcu sagittis, fermentum urna dapibus, congue lectus. Fusce vulputate porttitor nisl, ac cursus elit volutpat vitae. Nullam vitae ipsum egestas, convallis quam non, porta nibh. Morbi gravida erat nec neque bibendum, eu pellentesque velit posuere. Fusce aliquam erat eu massa eleifend tristique. + +Sed consequat sollicitudin ipsum eget tempus. Integer a aliquet velit. In justo nibh, pellentesque non suscipit eget, gravida vel lacus. Donec odio ante, malesuada in massa quis, pharetra tristique ligula. Donec eros est, tristique eget finibus quis, semper non nisl. Vivamus et elit nec enim ornare placerat. Sed posuere odio a elit cursus sagittis. + +Phasellus feugiat purus eu tortor ultrices finibus. Ut libero nibh, lobortis et libero nec, dapibus posuere eros. Sed sagittis euismod justo at consectetur. Nulla finibus libero placerat, cursus sapien at, eleifend ligula. Vivamus elit nisl, hendrerit ac nibh eu, ultrices tempus dui. Nam tellus neque, commodo non rhoncus eu, gravida in risus. Nullam id iaculis tortor. + +Nullam at odio in sem varius tempor sit amet vel lorem. Etiam eu hendrerit nisl. Fusce nibh mauris, vulputate sit amet ex vitae, congue rhoncus nisl. Sed eget tellus purus. Nullam tempus commodo erat ut tristique. Cras accumsan massa sit amet justo consequat eleifend. Integer scelerisque vitae tellus id consectetur. diff --git a/docs/exampledoc4.md b/docs/exampledoc4.md new file mode 100644 index 0000000..6f94ffe --- /dev/null +++ b/docs/exampledoc4.md @@ -0,0 +1,6 @@ +--- +id: doc4 +title: Other Document +--- + +this is another document diff --git a/docs/exampledoc5.md b/docs/exampledoc5.md new file mode 100644 index 0000000..92e2d0d --- /dev/null +++ b/docs/exampledoc5.md @@ -0,0 +1,6 @@ +--- +id: doc5 +title: Fifth Document +--- + +Another one diff --git a/ext/vscode/.vscode/launch.json b/ext/vscode/.vscode/launch.json new file mode 100644 index 0000000..8384213 --- /dev/null +++ b/ext/vscode/.vscode/launch.json @@ -0,0 +1,13 @@ +// A launch configuration that launches the extension inside a new window +{ + "version": "0.1.0", + "configurations": [ + { + "name": "Launch Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceRoot}" ] + } + ] +} \ No newline at end of file diff --git a/ext/vscode/CHANGELOG.md b/ext/vscode/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/ext/vscode/README.md b/ext/vscode/README.md new file mode 100644 index 0000000..e4c5d2c --- /dev/null +++ b/ext/vscode/README.md @@ -0,0 +1 @@ +Language support for the .sdkgen description language \ No newline at end of file diff --git a/ext/vscode/language-configuration.json b/ext/vscode/language-configuration.json new file mode 100644 index 0000000..e1a6c25 --- /dev/null +++ b/ext/vscode/language-configuration.json @@ -0,0 +1,31 @@ +{ + "comments": { + // symbol used for single line comment. Remove this entry if your language does not support line comments + "lineComment": "//", + // symbols used for start and end a block comment. Remove this entry if your language does not support block comments + "blockComment": [ "/*", "*/" ] + }, + // symbols used as brackets + "brackets": [ + ["{", "}"], + ["[", "]"], + ["(", ")"] + ], + // symbols that are auto closed when typing + "autoClosingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"] + ], + // symbols that that can be used to surround a selection + "surroundingPairs": [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ["\"", "\""], + ["'", "'"], + ["/*", "*/"] + ] +} \ No newline at end of file diff --git a/ext/vscode/package.json b/ext/vscode/package.json new file mode 100644 index 0000000..a27de3f --- /dev/null +++ b/ext/vscode/package.json @@ -0,0 +1,30 @@ +{ + "name": "sdkgen", + "displayName": "sdkgen", + "description": "sdkgen language support", + "version": "0.0.10", + "publisher": "cubos", + "engines": { + "vscode": "^1.10.0" + }, + "categories": [ + "Languages", "Snippets" + ], + "contributes": { + "languages": [{ + "id": "sdkgen", + "aliases": ["sdkgen", "sdkgen"], + "extensions": [".sdkgen"], + "configuration": "./language-configuration.json" + }], + "grammars": [{ + "language": "sdkgen", + "scopeName": "source.sdkgen", + "path": "./syntaxes/sdkgen.tmLanguage.json" + }], + "snippets": [{ + "language": "sdkgen", + "path": "./snippets/sdkgen.json" + }] + } +} diff --git a/ext/vscode/snippets/sdkgen.json b/ext/vscode/snippets/sdkgen.json new file mode 100644 index 0000000..297cf9a --- /dev/null +++ b/ext/vscode/snippets/sdkgen.json @@ -0,0 +1,60 @@ +{ + "error declaration": { + "prefix": "erd", + "body": "error ${1:ErrorName}", + "description": "Produce a error declaration with a name." + }, + "enum declaration:": { + "prefix": "ed", + "body": [ + "enum {", + " ${1:enumValue}", + " ${2:enumValue}", + " ${3:enumValue}", + "}" + ], + "description": "Produce an enum declaration." + }, + "enum type declaration": { + "prefix": "etd", + "body": [ + "type ${1:TypeName} enum {", + " ${2:enumValue}", + " ${3:enumValue}", + " ${4:enumValue}", + "${0}}" + ], + "description": "Produce an enum type declaration with three kinds of enum." + }, + "function declaration": { + "prefix": "fd", + "body": "function ${1:functionName}(${2:parameterName}${3:: ${4:parameterType}})${5:: ${6:returnType}}", + "description": "Produce a generic function with name, parameter, parameter type and return type." + }, + "get function declaration": { + "prefix": "gfd", + "body": "get ${1:functionName}(${2:parameterName}${3:: ${4:parameterType}})${5:: ${6:returnType}}", + "description": "Produce a get function with name, parameter, parameter type and return type." + }, + "import declaration": { + "prefix": "impd", + "body": "import \"${1:importModulePath}\"", + "description": "Produce a get function with relative path." + }, + "type declaration": { + "prefix": "td", + "body": [ + "type ${1:TypeName} {", + " ${2:propertyName}${3:: ${4:parameterType}}", + " ${5:propertyName}${6:: ${7:parameterType}}", + "${0}}" + + ], + "description": "Produce a type declaration with a name, property name and a property type." + }, + "url declaration": { + "prefix": "ud", + "body": "\\$url = \"${1:urlName}\"", + "description": "Produce a url variable." + } +} \ No newline at end of file diff --git a/ext/vscode/syntaxes/sdkgen.tmLanguage.json b/ext/vscode/syntaxes/sdkgen.tmLanguage.json new file mode 100644 index 0000000..d3264bf --- /dev/null +++ b/ext/vscode/syntaxes/sdkgen.tmLanguage.json @@ -0,0 +1,184 @@ +{ + "name": "sdkgen", + "patterns": [ + { + "include": "#any" + }, { + "name": "meta.variable", + "begin": "(\\$[a-zA-Z0-9]+) =", + "beginCaptures": { + "1": {"name": "variable.other"} + }, + "end": "\n", + "patterns": [ + { + "include": "#literal" + } + ] + }, { + "name": "meta.import", + "begin": "(import) ", + "beginCaptures": { + "1": {"name": "keyword.control"} + }, + "end": "\n", + "patterns": [ + { + "include": "#literal" + } + ] + }, { + "name": "meta.error", + "match": "(error) ([A-Z][a-zA-Z0-9]*)", + "captures": { + "1": {"name": "keyword.control"}, + "2": {"name": "entity.name.type"} + } + }, { + "name": "meta.type", + "begin": "(type) ([A-Z][a-zA-Z0-9]*) ", + "beginCaptures": { + "1": {"name": "keyword.control"}, + "2": {"name": "entity.name.type"} + }, + "end": "\n", + "patterns": [ + { + "include": "#type" + } + ] + }, { + "name": "meta.operation", + "begin": "(get|function) ([a-zA-Z0-9]+)", + "beginCaptures": { + "1": {"name": "keyword.control"}, + "2": {"name": "entity.name.function"} + }, + "end": "\n", + "patterns": [ + { + "name": "meta.operation.args", + "begin": "\\(", + "end": "\\)", + "patterns": [ + { + "include": "#fields" + } + ] + }, { + "include": "#type" + } + ] + } + ], + "repository": { + "any": { + "patterns": [ + { + "name": "comment.line", + "match": "//(.*?)\n" + }, + { + "name": "comment.block", + "begin": "/\\*", + "end": "\\*/" + } + ] + }, + "fields": { + "patterns": [ + { + "include": "#any" + }, { + "name": "meta.field", + "begin": "(\\w+):", + "beginCaptures": { + "1": {"name": "variable.parameter"} + }, + "end": "(?=\n|,|;|\\)|\\})", + "patterns": [ + { + "include": "#type" + } + ] + }, { + "name": "meta.spread", + "match": "(\\.\\.\\.)\\s?([A-Z][a-zA-Z0-9]*)", + "captures": { + "1": {"name": "markup.bold"}, + "2": {"name": "entity.name.type"} + } + } + ] + }, + "type": { + "patterns": [ + { + "include": "#any" + }, { + "name": "keyword.type", + "match": "\\b(bool|int|uint|float|string|datetime|date|bytes|void|money|cpf|cnpj|email|phone|cep|latlng|url|uuid|hex|base64|safehtml|xml)\\b" + }, { + "name": "markup.bold", + "match": "(\\?|\\[\\])" + }, { + "name": "entity.name.type", + "match": "\\b([A-Z][a-zA-Z0-9]*)\\b" + }, { + "name": "constant.numeric", + "match": "(![a-zA-Z0-9]+)\\b" + }, { + "name": "meta.type", + "begin": "\\{", + "beginCaptures": { + "1": {"name": "keyword.control"}, + "2": {"name": "entity.name.type"} + }, + "end": "\\}", + "patterns": [ + { + "include": "#fields" + } + ] + }, { + "name": "meta.enum", + "begin": "(enum) \\{", + "beginCaptures": { + "1": {"name": "keyword.control"}, + "2": {"name": "entity.name.type"} + }, + "end": "\\}", + "patterns": [ + { + "include": "#any" + }, { + "name": "variable.parameter", + "match": "(\\w+)" + } + ] + } + ] + }, + "literal": { + "patterns": [ + { + "include": "#any" + }, { + "name": "string.quoted.double.untitled", + "begin": "\"", + "end": "\"", + "patterns": [ + { + "name": "constant.character.escape.untitled", + "match": "\\." + } + ] + }, { + "name": "keyword.literal", + "match": "\\b(true|false)\\b" + } + ] + } + }, + "scopeName": "source.sdkgen" +} \ No newline at end of file diff --git a/ext/vscode/vsc-extension-quickstart.md b/ext/vscode/vsc-extension-quickstart.md new file mode 100644 index 0000000..968cbd8 --- /dev/null +++ b/ext/vscode/vsc-extension-quickstart.md @@ -0,0 +1,27 @@ +# Welcome to your VS Code Extension + +## What's in the folder +* This folder contains all of the files necessary for your extension +* `package.json` - this is the manifest file in which you declare your language support and define +the location of the grammar file that has been copied into you extension. +* `syntaxes/sdkgen.tmLanguage.json` - this is the Text mate grammar file that is used for tokenization +* `language-configuration.json` - this the language configuration, defining the tokens that are used for +comments and brackets. + +## Get up and running straight away +* Make sure the language configuration settings in `language-configuration.json` are accurate +* press `F5` to open a new window with your extension loaded +* create a new file with a file name suffix matching your language +* verify that syntax highlight works and that the language configuration settings are working + +## Make changes +* you can relaunch the extension from the debug toolbar after making changes to the files listed above +* you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes + +## Add more language features +* To add features such as intellisense, hovers and validators check out the VS Code extenders documentation at +https://code.visualstudio.com/docs + +## Install your extension +* To start using your extension with Visual Studio Code copy it into the `/.vscode/extensions` folder and restart Code. +* To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension. \ No newline at end of file diff --git a/language.md b/language.md index 68a213e..68fcb72 100644 --- a/language.md +++ b/language.md @@ -71,7 +71,6 @@ Operations are represented as functions called by the client and executed by the - `get`: This operation will fetch some data from the server into the client, it is meant to be a read-only operation. This type of operation can support cache. A connection failure by default should a non blocking warning to the end user. This can be safely retried on failure. - `function`: This operation will do some action and optionally return some information about it. It is not cacheable, but supports "do eventually" semantics. On a connection failure, a more intrusive error is shown to the end user by default. There will be no automatic retries. -- `subscribe`: This request is always stream based and will keep a connection open receiving events from the server. Examples: @@ -81,10 +80,6 @@ Examples: // Sends a message, returns nothing function sendMessage(target: string, message: Message): void - // Subscribes for new messages - // The "return type" is the event - subscribe messages(since: date): Message - ## Marks A mark can be added after a field or an operation parameter to add some special meaning. It starts with an exclamation and is followed by some word. Supported marks are: diff --git a/lexer.cr b/lexer.cr deleted file mode 100644 index 2cb1f65..0000000 --- a/lexer.cr +++ /dev/null @@ -1,278 +0,0 @@ -class Lexer - - class LexerException < Exception - end - - def initialize(@filename : String) - @raw = File.read(filename) - @start = 0 - @pos = 0 - @line = 1 - @col = 0 - end - - private def current_char - @raw[@pos]? - end - - private def current_char! - @raw[@pos] - end - - private def peek_next_char - @raw[@pos+1]? - end - - private def next_char - @pos += 1 - current_char - end - - def next_token - skip - start_token - - return nil unless current_char - - return make_token(CurlyOpenSymbolToken.new) if literal_match("{") - return make_token(CurlyCloseSymbolToken.new) if literal_match("}") - return make_token(ParensOpenSymbolToken.new) if literal_match("(") - return make_token(ParensCloseSymbolToken.new) if literal_match(")") - return make_token(OptionalSymbolToken.new) if literal_match("?") - return make_token(ArraySymbolToken.new) if literal_match("[]") - return make_token(ColonSymbolToken.new) if literal_match(":") - return make_token(ExclamationMarkSymbolToken.new) if literal_match("!") - return make_token(CommaSymbolToken.new) if literal_match(",") - return make_token(EqualSymbolToken.new) if literal_match("=") - - return make_token(TypeKeywordToken.new) if literal_match("type") - return make_token(GetKeywordToken.new) if literal_match("get") - return make_token(FunctionKeywordToken.new) if literal_match("function") - return make_token(SubscribeKeywordToken.new) if literal_match("subscribe") - return make_token(ErrorKeywordToken.new) if literal_match("error") - return make_token(PrimitiveTypeToken.new("bool")) if literal_match("bool") - return make_token(PrimitiveTypeToken.new("int")) if literal_match("int") - return make_token(PrimitiveTypeToken.new("uint")) if literal_match("uint") - return make_token(PrimitiveTypeToken.new("float")) if literal_match("float") - return make_token(PrimitiveTypeToken.new("string")) if literal_match("string") - return make_token(PrimitiveTypeToken.new("datetime")) if literal_match("datetime") - return make_token(PrimitiveTypeToken.new("date")) if literal_match("date") - return make_token(PrimitiveTypeToken.new("bytes")) if literal_match("bytes") - return make_token(PrimitiveTypeToken.new("void")) if literal_match("void") - - if current_char!.letter? - while current_char && (current_char!.letter? || current_char!.number? || current_char! == '_') - next_char - end - return make_token(IdentifierToken.new(@raw[@start...@pos])) - end - - if current_char! == '$' - next_char - while current_char && (current_char!.letter? || current_char!.number? || current_char! == '_') - next_char - end - return make_token(GlobalOptionToken.new(@raw[@start+1...@pos])) - end - - if current_char! == '"' - return string_match - end - - until [' ', '\t', '\r', '\n'].includes? current_char - next_char - end - - raise LexerException.new("Invalid token at #{@filename}:#{@line}:#{@col}: \"#{@raw[@start...@pos]}\"") - end - - private def skip - while true - if [' ', '\t', '\r', '\n'].includes? current_char - next_char - next - end - - if current_char == '/' && peek_next_char == '/' - while true - break if next_char == '\n' - end - next_char - next - end - - break - end - end - - private def start_token - while @start < @pos - if @raw[@start] == '\n' - @line += 1 - @col = 0 - else - @col += 1 - end - @start += 1 - end - end - - private def make_token(token) - token.filename = @filename - token.line = @line - token.col = @col - token - end - - private def literal_match(str : String) - str.each_char.with_index.each do |c, i| - return false unless @raw[@pos+i]? == c - end - @pos += str.size - end - - private def string_match - unless current_char != "\"" - raise LexerException.new("BUG: Expected string start here") - end - - next_char - contents = String::Builder.new; - while true - c = current_char - unless c - raise LexerException.new("Unexpected end of input inside string literal at #{@filename}:#{@line}:#{@col}") - end - if c == '"' - next_char - break - end - if c == '\\' - next_char - case current_char - when 'n' - contents << '\n' - when 'r' - contents << '\r' - when 't' - contents << '\t' - when '"' - contents << '\"' - when '\\' - contents << '\\' - when nil - raise LexerException.new("Unexpected end of input on escape sequence inside string literal at #{@filename}:#{@line}:#{@col}") - else - contents << current_char! - end - next_char - next - end - contents << c - next_char - end - return make_token(StringLiteralToken.new(contents.to_s)) - end -end - -class Token - property! filename : String - property! line : Int32 - property! col : Int32 - - def try_ident - self - end - - def location - "#{filename}:#{line}:#{col}" - end -end - -class TypeKeywordToken < Token - def try_ident - IdentifierToken.new("type") - end -end - -class GetKeywordToken < Token - def try_ident - IdentifierToken.new("get") - end -end - -class FunctionKeywordToken < Token - def try_ident - IdentifierToken.new("function") - end -end - -class SubscribeKeywordToken < Token - def try_ident - IdentifierToken.new("subscribe") - end -end - -class ErrorKeywordToken < Token - def try_ident - IdentifierToken.new("error") - end -end - -class PrimitiveTypeToken < Token - property name : String - def initialize(@name) - end - - def try_ident - IdentifierToken.new(@name) - end -end - -class IdentifierToken < Token - property name : String - def initialize(@name) - end -end - -class GlobalOptionToken < Token - property name : String - def initialize(@name) - end -end - -class StringLiteralToken < Token - property str : String - def initialize(@str) - end -end - -class EqualSymbolToken < Token -end - -class ExclamationMarkSymbolToken < Token -end - -class CurlyOpenSymbolToken < Token -end - -class CurlyCloseSymbolToken < Token -end - -class ParensOpenSymbolToken < Token -end - -class ParensCloseSymbolToken < Token -end - -class ColonSymbolToken < Token -end - -class OptionalSymbolToken < Token -end - -class ArraySymbolToken < Token -end - -class CommaSymbolToken < Token -end \ No newline at end of file diff --git a/main.cr b/main.cr index 3e431b1..bf43b40 100644 --- a/main.cr +++ b/main.cr @@ -1,43 +1 @@ -require "./lexer" -require "./parser" -require "./ast_to_s" -require "./target_java_android" -require "./target_typescript_server" -require "./target_typescript_web" -require "option_parser" -require "file_utils" - -is_server = false -destination = "" -sources = [] of String - -OptionParser.parse! do |parser| - parser.banner = "Usage: salute [arguments]" - parser.on("-s", "--server", "Generates server-side code") { is_server = true } - parser.on("-o NAME", "--output=NAME", "Specifies the output file") { |name| destination = name } - parser.on("-h", "--help", "Show this help") { puts parser } - parser.unknown_args {|args| sources = args } -end - -if sources.size == 0 - STDERR.puts "You must specify one source file" - exit -elsif sources.size > 1 - STDERR.puts "You must specify only one source file" - exit -end - -source = sources[0] - -lexer = Lexer.new(source) -parser = Parser.new(lexer) -ast = parser.parse -ast.check - -if destination == "" - STDERR.puts "You must specify an output file" - exit -end - -FileUtils.mkdir_p(File.dirname(destination)) -Target.process(ast, destination, is_server: is_server) +require "./src/main.cr" diff --git a/parser.cr b/parser.cr deleted file mode 100644 index e166702..0000000 --- a/parser.cr +++ /dev/null @@ -1,227 +0,0 @@ -require "./lexer" -require "./ast" - -class Parser - - class ParserException < Exception - end - - @token : Token | Nil - - def initialize(@lexer : Lexer) - @token = @lexer.next_token - end - - def parse - api = AST::ApiDescription.new - while @token - case multi_expect(TypeKeywordToken, GetKeywordToken, FunctionKeywordToken, SubscribeKeywordToken, GlobalOptionToken, ErrorKeywordToken) - when TypeKeywordToken - api.custom_types << parse_custom_type_definition - when GetKeywordToken, FunctionKeywordToken, SubscribeKeywordToken - api.operations << parse_operation - when GlobalOptionToken - parse_option(api.options) - when ErrorKeywordToken - next_token - token = expect IdentifierToken - next_token - api.errors << token.name - end - end - api - end - - def next_token - @token = @lexer.next_token - end - - macro multi_expect(*token_types) - token = @token - unless token - raise ParserException.new "Expected #{{{token_types.map{|t| t.stringify.gsub(/Token$/, "")}.join(" or ")}}}, but found end of file" - end - - result = nil - - {% for token_type in token_types %} - {% if token_type.stringify == "IdentifierToken" %} - token = token.try_ident - {% end %} - if !result && token.is_a?({{token_type}}) - result = token - end - {% end %} - - unless result - raise ParserException.new "Expected #{{{token_types.map{|t| t.stringify.gsub(/Token$/, "")}.join(" or ")}}} at #{token.location}, but found #{token.class.to_s.gsub(/Token$/, "")}" - end - - result - end - - macro expect(token_type) - token = @token - unless token - raise ParserException.new "Expected #{{{token_type.stringify.gsub(/Token$/, "")}}}, but found end of file" - end - {% if token_type.stringify == "IdentifierToken" %} - token = token.try_ident - {% end %} - unless token.is_a?({{token_type}}) - raise ParserException.new "Expected #{{{token_type.stringify.gsub(/Token$/, "")}}} at #{token.location}, but found #{token.class.to_s.sub(/Token$/, "")}" - end - token - end - - def parse_custom_type_definition - expect TypeKeywordToken - next_token - - t = AST::CustomType.new - name_token = expect(IdentifierToken) - unless name_token.name[0].uppercase? - raise ParserException.new "A custom type name must start with an uppercase letter, but found '#{name_token.name}' at #{name_token.location}" - end - t.name = name_token.name - next_token - - expect CurlyOpenSymbolToken - next_token - - while true - case token = multi_expect(IdentifierToken, CurlyCloseSymbolToken) - when IdentifierToken - t.fields << parse_field - when CurlyCloseSymbolToken - next_token - return t - end - end - end - - def parse_operation - op = nil - case token = multi_expect(GetKeywordToken, FunctionKeywordToken, SubscribeKeywordToken) - when GetKeywordToken - op = AST::GetOperation.new - when FunctionKeywordToken - op = AST::FunctionOperation.new - when SubscribeKeywordToken - op = AST::SubscribeOperation.new - else - raise "never" - end - - next_token - op.name = expect(IdentifierToken).name - next_token - expect ParensOpenSymbolToken - next_token - - while true - case token = multi_expect(IdentifierToken, ParensCloseSymbolToken, CommaSymbolToken) - when IdentifierToken - op.args << parse_field - when ParensCloseSymbolToken - next_token - break - when CommaSymbolToken - next_token - next - end - end - - if @token.is_a? ColonSymbolToken - expect ColonSymbolToken - next_token - op.return_type = parse_type - else - op.return_type = AST::VoidPrimitiveType.new - end - - op - end - - def parse_option(options) - var = expect GlobalOptionToken - next_token - expect EqualSymbolToken - next_token - - case var.name - when "url" - token = expect StringLiteralToken - next_token - options.url = token.str - else - raise ParserException.new("Unknown option $#{var.name} at #{var.location}") - end - end - - def parse_field - field = AST::Field.new - field.name = expect(IdentifierToken).name - next_token - expect ColonSymbolToken - next_token - field.type = parse_type(allow_void: false) - - while @token.is_a?(ExclamationMarkSymbolToken) - next_token - case (token = expect(IdentifierToken)).name - when "secret" - field.secret = true - else - raise ParserException.new "Unknown field mark !#{token.name} at #{token.location}" - end - next_token - end - - field - end - - def parse_type(allow_void = true) - result = case token = multi_expect(PrimitiveTypeToken, IdentifierToken) - when IdentifierToken - unless token.name[0].uppercase? - raise ParserException.new "Expected a type but found '#{token.name}', at #{token.location}" - end - AST::CustomTypeReference.new(token.name) - when PrimitiveTypeToken - case token.name - when "string"; AST::StringPrimitiveType.new - when "int"; AST::IntPrimitiveType.new - when "uint"; AST::UIntPrimitiveType.new - when "date"; AST::DatePrimitiveType.new - when "datetime"; AST::DateTimePrimitiveType.new - when "float"; AST::FloatPrimitiveType.new - when "bool"; AST::BoolPrimitiveType.new - when "bytes"; AST::BytesPrimitiveType.new - when "void" - if allow_void - AST::VoidPrimitiveType.new - else - raise ParserException.new "Can't use 'void' here, at #{token.location}" - end - else - raise "BUG! Should handle primitive #{token.name}" - end - else - raise "never" - end - next_token - - while @token.is_a? ArraySymbolToken - next_token - result = AST::ArrayType.new(result) - end - - if @token.is_a?(OptionalSymbolToken) - next_token - result = AST::OptionalType.new(result) - end - - result - end -end diff --git a/spec/lexer_spec.cr b/spec/lexer_spec.cr new file mode 100644 index 0000000..42d1923 --- /dev/null +++ b/spec/lexer_spec.cr @@ -0,0 +1,331 @@ +require "spec" +require "../src/syntax/lexer" + +describe Lexer do + it_lexes "", [] of Token + + it_lexes "type", [ + TypeKeywordToken.new, + ] + + it_doesnt_lex "23", "Unexpected character '2' at -:1:1" + + it_doesnt_lex "2a", "Unexpected character '2' at -:1:1" + + it_lexes "type2", [ + IdentifierToken.new("type2"), + ] + + it_lexes "aaa", [ + IdentifierToken.new("aaa"), + ] + + it_lexes "...aaa", [ + SpreadSymbolToken.new, + IdentifierToken.new("aaa"), + ] + + it_lexes "a b c", [ + IdentifierToken.new("a"), + IdentifierToken.new("b"), + IdentifierToken.new("c"), + ] + + it_doesnt_lex "aa_bb", "Unexpected character '_' at -:1:3" + + it_lexes "type type", [ + TypeKeywordToken.new, + TypeKeywordToken.new, + ] + + it_lexes "enum", [ + EnumKeywordToken.new, + ] + + it_lexes "error", [ + ErrorKeywordToken.new, + ] + + it_lexes "import", [ + ImportKeywordToken.new, + ] + + it_lexes "get", [ + GetKeywordToken.new, + ] + + it_lexes "Get", [ + IdentifierToken.new("Get"), + ] + + it_lexes "function", [ + FunctionKeywordToken.new, + ] + + it_lexes "enuma", [ + IdentifierToken.new("enuma"), + ] + + it_lexes "errorh", [ + IdentifierToken.new("errorh"), + ] + + %w[enum type error import get function].each do |kw| + it_lexes kw, [ + IdentifierToken.new(kw), + ] + end + + Lexer::PRIMITIVES.each do |primitive| + it_lexes primitive, [ + PrimitiveTypeToken.new(primitive), + ] + + it_lexes primitive, [ + IdentifierToken.new(primitive), + ] + + it_lexes primitive + "a", [ + IdentifierToken.new(primitive + "a"), + ] + end + + it_lexes "err", [ + IdentifierToken.new("err"), + ] + + it_lexes "{", [ + CurlyOpenSymbolToken.new, + ] + + it_lexes "{{", [ + CurlyOpenSymbolToken.new, + CurlyOpenSymbolToken.new, + ] + + it_lexes "}{", [ + CurlyCloseSymbolToken.new, + CurlyOpenSymbolToken.new, + ] + + it_lexes " } { ", [ + CurlyCloseSymbolToken.new, + CurlyOpenSymbolToken.new, + ] + + it_lexes "({!:?,=})", [ + ParensOpenSymbolToken.new, + CurlyOpenSymbolToken.new, + ExclamationMarkSymbolToken.new, + ColonSymbolToken.new, + OptionalSymbolToken.new, + CommaSymbolToken.new, + EqualSymbolToken.new, + CurlyCloseSymbolToken.new, + ParensCloseSymbolToken.new, + ] + + it_lexes " [][] ", [ + ArraySymbolToken.new, + ArraySymbolToken.new, + ] + + it_lexes "nice[]", [ + IdentifierToken.new("nice"), + ArraySymbolToken.new, + ] + + it_lexes "nice\n[]", [ + IdentifierToken.new("nice"), + ArraySymbolToken.new, + ] + + it_doesnt_lex "[", "Unexpected end of file" + + it_lexes "type Str string", [ + TypeKeywordToken.new, + IdentifierToken.new("Str"), + PrimitiveTypeToken.new("string"), + ] + + it_lexes "$url", [ + GlobalOptionToken.new("url"), + ] + + it_lexes "$F", [ + GlobalOptionToken.new("F"), + ] + + it_lexes "$x123", [ + GlobalOptionToken.new("x123"), + ] + + it_lexes "$ah[]?", [ + GlobalOptionToken.new("ah"), + ArraySymbolToken.new, + OptionalSymbolToken.new, + ] + + it_doesnt_lex "$", "Unexpected end of file" + + it_doesnt_lex "$_a", "Unexpected character '_'" + + it_doesnt_lex "$ a", "Unexpected character ' '" + + it_lexes "\"ab\"", [ + StringLiteralToken.new("ab"), + ] + + it_lexes "\"\"", [ + StringLiteralToken.new(""), + ] + + it_lexes "\"aa\\nbb\"", [ + StringLiteralToken.new("aa\nbb"), + ] + + it_lexes "\"aa\\bb\"", [ + StringLiteralToken.new("aabb"), + ] + + it_lexes "\"'\"", [ + StringLiteralToken.new("'"), + ] + + it_lexes "\"\\n\\t\\\"\"", [ + StringLiteralToken.new("\n\t\""), + ] + + it_lexes "\"/* */\"", [ + StringLiteralToken.new("/* */"), + ] + + it_lexes "//hmmm", [] of Token + + it_lexes "x//hmmm", [ + IdentifierToken.new("x"), + ] + + it_lexes "a//hmmm\nb", [ + IdentifierToken.new("a"), + IdentifierToken.new("b"), + ] + + it_lexes "a // hmmm \n b", [ + IdentifierToken.new("a"), + IdentifierToken.new("b"), + ] + + it_lexes "// héýça\n z", [ + IdentifierToken.new("z"), + ] + + # New-line tests + it_doesnt_lex "2", "Unexpected character '2' at -:1:1" + it_doesnt_lex "\n2", "Unexpected character '2' at -:2:1" + it_doesnt_lex "//\n2", "Unexpected character '2' at -:2:1" + it_doesnt_lex "//\n 2", "Unexpected character '2' at -:2:2" + it_doesnt_lex "//x\n2", "Unexpected character '2' at -:2:1" + it_doesnt_lex "//x\n 2", "Unexpected character '2' at -:2:2" + it_doesnt_lex "/*\n*/3", "Unexpected character '3' at -:2:3" + it_doesnt_lex "/*\n\n\n\n*/ 2", "Unexpected character '2' at -:5:4" + it_doesnt_lex "/*a*/\n2", "Unexpected character '2' at -:2:1" + it_doesnt_lex "/*a*/\n 2", "Unexpected character '2' at -:2:2" + + # Add multi-line comments tests + it_doesnt_lex "/*\n", "Unexpected end of file" + it_doesnt_lex "/* *", "Unexpected end of file" + it_doesnt_lex "/* \tae\n\n", "Unexpected end of file" + it_doesnt_lex "/*", "Unexpected end of file" + it_doesnt_lex "\/*", "Unexpected end of file" + it_doesnt_lex "/* dsvibwi", "Unexpected end of file" + it_doesnt_lex "/* cdibweic *", "Unexpected end of file" + it_doesnt_lex "/* cdibweic *a", "Unexpected end of file" + it_doesnt_lex "/* * /", "Unexpected end of file" + it_doesnt_lex "/* * /", "Unexpected end of file" + it_doesnt_lex "/* * /", "Unexpected end of file" + it_doesnt_lex "/*/", "Unexpected end of file" + it_doesnt_lex "/* /", "Unexpected end of file" + it_doesnt_lex "/* * /", "Unexpected end of file" + it_doesnt_lex "/* * * * /", "Unexpected end of file" + it_doesnt_lex "/* *a/", "Unexpected end of file" + + it_lexes "/*\n*/", [] of Token + it_lexes "/* * * * * */", [] of Token + it_lexes "/* * ***_ */", [] of Token + it_lexes "/**/", [] of Token + it_lexes "/***/", [] of Token + it_lexes "/****/", [] of Token + it_lexes "/*****/", [] of Token + it_lexes "/******/", [] of Token + it_lexes "/*******/", [] of Token + it_lexes "/********/", [] of Token + it_lexes "/****aas ********/", [] of Token + it_lexes "/*******a ********/", [] of Token + it_lexes "/**********/", [] of Token + it_lexes "/************/", [] of Token + it_lexes "/*************/", [] of Token + it_lexes "/**************/", [] of Token + it_lexes "/***************/", [] of Token + it_lexes "/*a */", [] of Token + it_lexes "/*a \n*/", [] of Token + it_lexes "/*a \n\n\n\n\n*/", [] of Token + it_lexes "/**a*/", [] of Token + it_lexes "/*a**/", [] of Token + it_lexes "/* *\/", [] of Token + + it_lexes "/*a*/b/*c*/", [ + IdentifierToken.new("b"), + ] + + it_lexes "/* đðđ\n */u", [ + IdentifierToken.new("u"), + ] + + it_lexes "c/* a*/", [ + IdentifierToken.new("c"), + ] + + it_lexes "/* bce */a", [ + IdentifierToken.new("a"), + ] + + it_lexes "b/* baed */c", [ + IdentifierToken.new("b"), + IdentifierToken.new("c"), + ] + + it_lexes "/* \n\nb */a", [ + IdentifierToken.new("a"), + ] + + it_lexes "/* *\/a", [ + IdentifierToken.new("a"), + ] +end + +def it_lexes(code, expected_tokens) + it "lexes '#{code}' as [#{expected_tokens.map(&.to_s).join(" ")}]" do + lexer = Lexer.new(code) + + tokens = [] of Token + while token = lexer.next_token + tokens << token + end + tokens.map_with_index! do |token, i| + expected_tokens[i]?.is_a?(IdentifierToken) ? token.try_ident : token + end + + tokens.should eq expected_tokens + end +end + +def it_doesnt_lex(code, message) + it "doesn't lex '#{code}'" do + lexer = Lexer.new(code) + expect_raises(Lexer::LexerException, message) do + while lexer.next_token + end + end + end +end diff --git a/spec/parser_spec.cr b/spec/parser_spec.cr new file mode 100644 index 0000000..73a98db --- /dev/null +++ b/spec/parser_spec.cr @@ -0,0 +1,130 @@ +require "spec" +require "../src/syntax/parser" +require "../src/syntax/ast_to_s" + +describe Parser do + Lexer::PRIMITIVES.each do |p| + it "handles primitive type '#{p}'" do + check_parses <<-END + type Foo { + foo: #{p} + } + END + end + end + + (Lexer::PRIMITIVES + %w[type get function enum import error void]).each do |kw| + it "handles '#{kw}' on the name of a field" do + check_parses <<-END + type Foo { + #{kw}: int + } + END + end + end + + it "handles arrays and optionals" do + check_parses <<-END + type Foo { + aa: string[] + bbb: int?[]?? + cccc: int[][][] + ddddd: uint[][][]??[]???[][] + } + END + end + + it "handles errors" do + check_parses <<-END + error Foo + error Bar + error FooBar + END + end + + it "handles simple get operations" do + Lexer::PRIMITIVES.each do |primitive| + check_parses <<-END + get foo(): #{primitive} + get bar(): #{primitive}? + get baz(): #{primitive}[] + END + end + end + + it "handles options on the top" do + check_parses <<-END + $url = "api.cubos.io/sdkgenspec" + END + end + + it "handles combinations of all parts" do + check_parses <<-END + $url = "api.cubos.io/sdkgenspec" + + error Foo + error Bar + + type Baz { + a: string? + b: int + } + + get baz(): Baz + END + end + + it "fails when field happens twice" do + check_doesnt_parse(<<-END, "redeclare" + type Baz { + a: int + b: bool + a: int + } + END + ) + + check_doesnt_parse(<<-END, "redeclare" + type Baz { + b: int + xx: bool + xx: int + } + END + ) + + check_doesnt_parse(<<-END, "redeclare" + function foo(a: string, a: int) + END + ) + end + + it "handles spreads in structs" do + check_parses <<-END + type Foo { + ...Bar + ...Baz + aa: string + } + END + end +end + +def clear(code) + code = code.strip + code = code.gsub /\/\/.*/, "" + code = code.gsub /\n\s+/, "\n" + code = code.gsub /\n+/, "\n" + code +end + +def check_parses(code) + parser = Parser.new(IO::Memory.new(code)) + ast = parser.parse + clear(ast.to_s).should eq clear(code) +end + +def check_doesnt_parse(code, message) + parser = Parser.new(IO::Memory.new(code)) + expect_raises(Parser::ParserException, message) { parser.parse } +end diff --git a/spec/semantic_spec.cr b/spec/semantic_spec.cr new file mode 100644 index 0000000..fd09014 --- /dev/null +++ b/spec/semantic_spec.cr @@ -0,0 +1,26 @@ +require "spec" +require "../src/syntax/parser" +require "../src/semantic/ast_semantic" + +describe Semantic do + it "fails when type definition appears twice" do + expect_raises(Exception, "defined") do + parse <<-END + type X { + + } + + type X { + + } + END + end + end +end + +def parse(code) + parser = Parser.new(IO::Memory.new(code)) + ast = parser.parse + ast.semantic + ast +end diff --git a/ast.cr b/src/ast.cr similarity index 57% rename from ast.cr rename to src/ast.cr index f3d9072..11ca358 100644 --- a/ast.cr +++ b/src/ast.cr @@ -1,11 +1,8 @@ - module AST abstract class Type end abstract class PrimitiveType < Type - def check(ast) - end end class StringPrimitiveType < PrimitiveType @@ -35,40 +32,93 @@ module AST class VoidPrimitiveType < PrimitiveType end + class MoneyPrimitiveType < PrimitiveType + end + + class CpfPrimitiveType < PrimitiveType + end + + class CnpjPrimitiveType < PrimitiveType + end + + class EmailPrimitiveType < PrimitiveType + end + + class PhonePrimitiveType < PrimitiveType + end + + class CepPrimitiveType < PrimitiveType + end + + class LatLngPrimitiveType < PrimitiveType + end + + class UrlPrimitiveType < PrimitiveType + end + + class UuidPrimitiveType < PrimitiveType + end + + class HexPrimitiveType < PrimitiveType + end + + class Base64PrimitiveType < PrimitiveType + end + + class SafeHtmlPrimitiveType < PrimitiveType + end + + class XmlPrimitiveType < PrimitiveType + end + class OptionalType < Type property base - def initialize(@base : Type) - end - def check(ast) - @base.check(ast) + def initialize(@base : Type) end end class ArrayType < Type property base + def initialize(@base : Type) end + end - def check(ast) - @base.check(ast) + class EnumType < Type + property values = [] of String + end + + class StructType < Type + property fields = [] of Field + property spreads = [] of TypeReference + end + + class TypeDefinition + property! name : String + property! type : Type + end + + class TypeReference < Type + property name + + def initialize(@name : String) end end class ApiDescription - property custom_types = [] of CustomType + property type_definitions = [] of TypeDefinition property operations = [] of Operation property options = Options.new property errors = [] of String - - def check - custom_types.each &.check(self) - operations.each &.check(self) - end end class Options property url = "" + property useRethink = true + property strict = false + property syntheticDefaultImports = true + property retryRequest = true end class Field @@ -81,50 +131,15 @@ module AST end end - class CustomType - property! name : String - property fields = [] of Field - - def check(ast) - fields.each &.check(ast) - end - end - - class CustomTypeReference < Type - property name - def initialize(@name : String) - end - - def check(ast) - unless ast.custom_types.find {|t| t.name == name } - raise "Could not find type '#{name}'" - end - end - end - abstract class Operation property! name : String property args = [] of Field property! return_type : Type - - def check(ast) - args.each &.check(ast) - end - - def fnName - name - end end class GetOperation < Operation - def fnName - return_type.is_a?(BoolPrimitiveType) ? name : "get" + name[0].upcase + name[1..-1] - end end class FunctionOperation < Operation end - - class SubscribeOperation < Operation - end -end \ No newline at end of file +end diff --git a/src/codegen_types/array.cr b/src/codegen_types/array.cr new file mode 100644 index 0000000..06cff87 --- /dev/null +++ b/src/codegen_types/array.cr @@ -0,0 +1,63 @@ +require "../utils" + +module AST + class ArrayType + def typescript_decode(expr) + inner = base.typescript_decode("e") + inner = "(#{inner})" if inner[0] == '{' + "#{expr}.map((e: any) => #{inner})" + end + + def typescript_encode(expr) + inner = base.typescript_encode("e") + inner = "(#{inner})" if inner[0] == '{' + "#{expr}.map(e => #{inner})" + end + + def typescript_native_type + if base.is_a? AST::OptionalType + "(#{base.typescript_native_type})[]" + else + base.typescript_native_type + "[]" + end + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"array\");\n" + i = random_var + io << "for (let #{i} = 0; #{i} < #{expr}.length; ++#{i}) {\n" + io << ident base.typescript_expect("#{expr}[#{i}]") + io << "}\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || !(#{expr} instanceof Array)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "} else {\n" + i = random_var + io << ident "for (let #{i} = 0; #{i} < #{expr}.length; ++#{i}) {\n" + io << ident ident base.typescript_check_encoded("#{expr}[#{i}]", "#{descr} + \"[\" + #{i} + \"]\"") + io << ident "}\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || !(#{expr} instanceof Array)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "} else {\n" + i = random_var + io << ident "for (let #{i} = 0; #{i} < #{expr}.length; ++#{i}) {\n" + io << ident ident base.typescript_check_decoded("#{expr}[#{i}]", "#{descr} + \"[\" + #{i} + \"]\"") + io << ident "}\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/base64.cr b/src/codegen_types/base64.cr new file mode 100644 index 0000000..dd6e8a6 --- /dev/null +++ b/src/codegen_types/base64.cr @@ -0,0 +1,40 @@ +module AST + class Base64PrimitiveType + def typescript_decode(expr) + "#{expr}" + end + + def typescript_encode(expr) + "#{expr}" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + io << "expect(#{expr}).toMatch(/^(?:[A-Za-z0-9+\/]{4}\\n?)*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)$/);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^(?:[A-Za-z0-9+\/]{4}\\n?)*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)$/)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^(?:[A-Za-z0-9+\/]{4}\\n?)*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)$/)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/bool.cr b/src/codegen_types/bool.cr new file mode 100644 index 0000000..8dcdade --- /dev/null +++ b/src/codegen_types/bool.cr @@ -0,0 +1,39 @@ +module AST + class BoolPrimitiveType + def typescript_decode(expr) + "!!#{expr}" + end + + def typescript_encode(expr) + "!!#{expr}" + end + + def typescript_native_type + "boolean" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"boolean\");\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} !== true && #{expr} !== false) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} !== true && #{expr} !== false) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/bytes.cr b/src/codegen_types/bytes.cr new file mode 100644 index 0000000..f5532db --- /dev/null +++ b/src/codegen_types/bytes.cr @@ -0,0 +1,39 @@ +module AST + class BytesPrimitiveType + def typescript_decode(expr) + "Buffer.from(#{expr}, \"base64\")" + end + + def typescript_encode(expr) + "#{expr}.toString(\"base64\")" + end + + def typescript_native_type + "Buffer" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeInstanceOf(Buffer);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + # io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^(?:[A-Za-z0-9+\/]{4}\\n?)*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)$/)) {\n" + # io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + # io << " typeCheckerError(err, ctx);\n" + # io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || !(#{expr} instanceof Buffer)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/cep.cr b/src/codegen_types/cep.cr new file mode 100644 index 0000000..a6d68e1 --- /dev/null +++ b/src/codegen_types/cep.cr @@ -0,0 +1,40 @@ +module AST + class CepPrimitiveType + def typescript_decode(expr) + "#{expr}.replace(/(..)(...)(...)/, \"$1.$2-$3\")" + end + + def typescript_encode(expr) + "#{expr}.replace(/[^0-9]/g, \"\").padStart(8, \"0\")" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + io << "expect({expr}.replace(/[^0-9]/g, \"\").length).toBe(8);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || #{expr}.replace(/[^0-9]/g, \"\").length !== 8) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || #{expr}.replace(/[^0-9]/g, \"\").length !== 8) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/cnpj.cr b/src/codegen_types/cnpj.cr new file mode 100644 index 0000000..3fa6ad3 --- /dev/null +++ b/src/codegen_types/cnpj.cr @@ -0,0 +1,40 @@ +module AST + class CnpjPrimitiveType + def typescript_decode(expr) + "#{expr}.replace(/(..)(...)(...)(....)(..)/, \"$1.$2.$3/$4-$5\")" + end + + def typescript_encode(expr) + "#{expr}.replace(/[^0-9]/g, \"\").padStart(14, \"0\")" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + io << "expect({expr}.replace(/[^0-9]/g, \"\").length).toBe(14);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || #{expr}.replace(/[^0-9]/g, \"\").length !== 14) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || #{expr}.replace(/[^0-9]/g, \"\").length !== 14) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/cpf.cr b/src/codegen_types/cpf.cr new file mode 100644 index 0000000..b6b3051 --- /dev/null +++ b/src/codegen_types/cpf.cr @@ -0,0 +1,40 @@ +module AST + class CpfPrimitiveType + def typescript_decode(expr) + "#{expr}.replace(/(...)(...)(...)(..)/, \"$1.$2.$3-$4\")" + end + + def typescript_encode(expr) + "#{expr}.replace(/[^0-9]/g, \"\").padStart(11, \"0\")" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + io << "expect({expr}.replace(/[^0-9]/g, \"\").length).toBe(11);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || #{expr}.replace(/[^0-9]/g, \"\").length !== 11) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || #{expr}.replace(/[^0-9]/g, \"\").length !== 11) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/date.cr b/src/codegen_types/date.cr new file mode 100644 index 0000000..f5d5964 --- /dev/null +++ b/src/codegen_types/date.cr @@ -0,0 +1,43 @@ +module AST + class DatePrimitiveType + def typescript_decode(expr) + "new Date(parseInt(#{expr}.split(\"-\")[0], 10), parseInt(#{expr}.split(\"-\")[1], 10) - 1, parseInt(#{expr}.split(\"-\")[2], 10))" + end + + def typescript_encode(expr) + "typeof(#{expr}) === \"string\" ? new Date(new Date(#{expr}).getTime() - new Date(#{expr}).getTimezoneOffset() * 60000).toISOString().split(\"T\")[0] : new Date(#{expr}.getTime() - #{expr}.getTimezoneOffset() * 60000).toISOString().split(\"T\")[0]" + end + + def typescript_native_type + "Date" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeInstanceOf(Date);\n" + io << "expect(#{expr}.getHours()).toBe(0);\n" + io << "expect(#{expr}.getMinutes()).toBe(0);\n" + io << "expect(#{expr}.getSeconds()).toBe(0);\n" + io << "expect(#{expr}.getMilliseconds()).toBe(0);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^[0-9]{4}-[01][0-9]-[0123][0-9]$/)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || !(#{expr} instanceof Date || ((#{expr} as any).match && (#{expr} as any).match(/^[0-9]{4}-[01][0-9]-[0123][0-9]/)))) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/datetime.cr b/src/codegen_types/datetime.cr new file mode 100644 index 0000000..b343e0b --- /dev/null +++ b/src/codegen_types/datetime.cr @@ -0,0 +1,39 @@ +module AST + class DateTimePrimitiveType + def typescript_decode(expr) + "new Date(#{expr} + \"Z\")" + end + + def typescript_encode(expr) + "(typeof #{expr} === \"string\" && (#{expr} as any).match(/^[0-9]{4}-[01][0-9]-[0123][0-9]T[012][0-9]:[0123456][0-9]:[0123456][0-9](\\.[0-9]{1,6})?Z?$/) ? (#{expr} as any).replace(\"Z\", \"\") : #{expr}.toISOString().replace(\"Z\", \"\"))" + end + + def typescript_native_type + "Date" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeInstanceOf(Date);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^[0-9]{4}-[01][0-9]-[0123][0-9]T[012][0-9]:[0123456][0-9]:[0123456][0-9](\\.[0-9]{1,6})?$/)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || !(#{expr} instanceof Date || ((#{expr} as any).match && (#{expr} as any).match(/^[0-9]{4}-[01][0-9]-[0123][0-9]T[012][0-9]:[0123456][0-9]:[0123456][0-9](\\.[0-9]{1,6})?Z?$/)))) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/email.cr b/src/codegen_types/email.cr new file mode 100644 index 0000000..a569b81 --- /dev/null +++ b/src/codegen_types/email.cr @@ -0,0 +1,40 @@ +module AST + class EmailPrimitiveType + def typescript_decode(expr) + "#{expr}.normalize()" + end + + def typescript_encode(expr) + "#{expr}.normalize()" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + io << "expect(#{expr}).toMatch(/^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])$/);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])$/)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])$/)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/enum.cr b/src/codegen_types/enum.cr new file mode 100644 index 0000000..4a2b82a --- /dev/null +++ b/src/codegen_types/enum.cr @@ -0,0 +1,51 @@ +module AST + class EnumType + def typescript_decode(expr) + "#{expr}" + end + + def typescript_encode(expr) + "#{expr}" + end + + def typescript_native_type + name + end + + def typescript_definition + "export type #{name} = #{values.map(&.inspect).join(" | ")};" + # String::Builder.build do |io| + # io << "export enum #{name} {\n" + # values.each do |value| + # io << ident "#{value} = \"#{value}\",\n" + # end + # io << "}" + # end + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + io << "expect(#{expr}).toMatch(/#{values.join("|")}/);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{values.inspect}.includes(#{expr})) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{values.inspect}.includes(#{expr})) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/float.cr b/src/codegen_types/float.cr new file mode 100644 index 0000000..32aacf5 --- /dev/null +++ b/src/codegen_types/float.cr @@ -0,0 +1,39 @@ +module AST + class FloatPrimitiveType + def typescript_decode(expr) + "#{expr}" + end + + def typescript_encode(expr) + "#{expr}" + end + + def typescript_native_type + "number" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"number\");\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"number\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"number\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/hex.cr b/src/codegen_types/hex.cr new file mode 100644 index 0000000..050f41f --- /dev/null +++ b/src/codegen_types/hex.cr @@ -0,0 +1,40 @@ +module AST + class HexPrimitiveType + def typescript_decode(expr) + "#{expr}" + end + + def typescript_encode(expr) + "#{expr}" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + io << "expect(#{expr}).toMatch(/^([0-9a-f]{2})*$/);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^([0-9a-f]{2})*$/)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^([0-9a-f]{2})*$/)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/int.cr b/src/codegen_types/int.cr new file mode 100644 index 0000000..b3a245b --- /dev/null +++ b/src/codegen_types/int.cr @@ -0,0 +1,40 @@ +module AST + class IntPrimitiveType + def typescript_decode(expr) + "#{expr} | 0" + end + + def typescript_encode(expr) + "#{expr} | 0" + end + + def typescript_native_type + "number" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"number\");\n" + io << "expect(#{expr} - (#{expr} | 0)).toBe(0);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"number\" || (#{expr} | 0) !== #{expr}) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"number\" || (#{expr} | 0) !== #{expr}) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/latlng.cr b/src/codegen_types/latlng.cr new file mode 100644 index 0000000..22a346c --- /dev/null +++ b/src/codegen_types/latlng.cr @@ -0,0 +1,36 @@ +module AST + class LatLngPrimitiveType + def typescript_decode(expr) + "{lat: #{expr}.lat, lng: #{expr}.lng}" + end + + def typescript_encode(expr) + "{lat: #{expr}.lat, lng: #{expr}.lng}" + end + + def typescript_native_type + "{lat: number, lng: number}" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"object\");\n" + io << "expect(#{expr}.lat).toBeTypeOf(\"number\");\n" + io << "expect(#{expr}.lng).toBeTypeOf(\"number\");\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"object\" || typeof #{expr}.lat !== \"number\" || typeof #{expr}.lng !== \"number\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + typescript_check_encoded(expr, descr) + end + end +end diff --git a/src/codegen_types/money.cr b/src/codegen_types/money.cr new file mode 100644 index 0000000..c224541 --- /dev/null +++ b/src/codegen_types/money.cr @@ -0,0 +1,41 @@ +module AST + class MoneyPrimitiveType + def typescript_decode(expr) + "#{expr} | 0" + end + + def typescript_encode(expr) + "#{expr} | 0" + end + + def typescript_native_type + "number" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"number\");\n" + io << "expect(#{expr} - (#{expr} | 0)).toBe(0);\n" + io << "expect(#{expr}).toBeGreaterThanOrEqual(0);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"number\" || (#{expr} | 0) !== #{expr} || #{expr} < 0) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"number\" || (#{expr} | 0) !== #{expr} || #{expr} < 0) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/optional.cr b/src/codegen_types/optional.cr new file mode 100644 index 0000000..1a93b91 --- /dev/null +++ b/src/codegen_types/optional.cr @@ -0,0 +1,45 @@ +module AST + class OptionalType + def typescript_decode(expr) + "#{expr} === null || #{expr} === undefined ? null : #{base.typescript_decode(expr)}" + end + + def typescript_encode(expr) + "#{expr} === null || #{expr} === undefined ? null : #{base.typescript_encode(expr)}" + end + + def typescript_native_type + "#{base.typescript_native_type} | null" + end + + def typescript_expect(expr) + String.build do |io| + x = random_var + io << "const #{x} = #{expr};\n" + io << "if (#{x} !== null && #{x} !== undefined) {\n" + io << ident base.typescript_expect(x) + io << "}\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + x = random_var + io << "const #{x} = #{expr};\n" + io << "if (#{x} !== null && #{x} !== undefined) {\n" + io << ident base.typescript_check_encoded(x, descr) + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + x = random_var + io << "const #{x} = #{expr};\n" + io << "if (#{x} !== null && #{x} !== undefined) {\n" + io << ident base.typescript_check_decoded(x, descr) + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/phone.cr b/src/codegen_types/phone.cr new file mode 100644 index 0000000..1f2a05b --- /dev/null +++ b/src/codegen_types/phone.cr @@ -0,0 +1,41 @@ +module AST + class PhonePrimitiveType + def typescript_decode(expr) + "#{expr}" + end + + def typescript_encode(expr) + "#{expr}.replace(/[^0-9+]/g, \"\")" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + io << "expect(#{expr}.replace(/[^0-9+]/g, \"\").length).toBeGreaterThanOrEqual(5);\n" + io << "expect(#{expr}[0]).toBe(\"+\");\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || #{expr}.replace(/[^0-9+]/g, \"\").length < 5 || #{expr}[0] !== \"+\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || #{expr}.replace(/[^0-9+]/g, \"\").length < 5 || #{expr}[0] !== \"+\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/reference.cr b/src/codegen_types/reference.cr new file mode 100644 index 0000000..40707c8 --- /dev/null +++ b/src/codegen_types/reference.cr @@ -0,0 +1,27 @@ +module AST + class TypeReference + def typescript_decode(expr) + type.typescript_decode(expr) + end + + def typescript_encode(expr) + type.typescript_encode(expr) + end + + def typescript_native_type + type.typescript_native_type + end + + def typescript_expect(expr) + type.typescript_expect(expr) + end + + def typescript_check_encoded(expr, descr) + type.typescript_check_encoded(expr, descr) + end + + def typescript_check_decoded(expr, descr) + type.typescript_check_decoded(expr, descr) + end + end +end diff --git a/src/codegen_types/safehtml.cr b/src/codegen_types/safehtml.cr new file mode 100644 index 0000000..429aa49 --- /dev/null +++ b/src/codegen_types/safehtml.cr @@ -0,0 +1,39 @@ +module AST + class SafeHtmlPrimitiveType + def typescript_decode(expr) + "#{expr}.normalize()" + end + + def typescript_encode(expr) + "#{expr}.normalize()" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/string.cr b/src/codegen_types/string.cr new file mode 100644 index 0000000..a99b193 --- /dev/null +++ b/src/codegen_types/string.cr @@ -0,0 +1,41 @@ +module AST + class StringPrimitiveType + def typescript_decode(expr) + "#{expr}" + # "#{expr}.normalize()" + end + + def typescript_encode(expr) + "#{expr}" + # "#{expr}.normalize()" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/struct.cr b/src/codegen_types/struct.cr new file mode 100644 index 0000000..4e91e67 --- /dev/null +++ b/src/codegen_types/struct.cr @@ -0,0 +1,70 @@ +module AST + class StructType + def typescript_decode(expr) + String::Builder.build do |io| + io << "{\n" + fields.each do |field| + io << ident "#{field.name}: #{field.type.typescript_decode("#{expr}.#{field.name}")},\n" + end + io << "}" + end + end + + def typescript_encode(expr) + String::Builder.build do |io| + io << "{\n" + fields.each do |field| + io << ident "#{field.name}: #{field.type.typescript_encode("#{expr}.#{field.name}")},\n" + end + io << "}" + end + end + + def typescript_native_type + name + end + + def typescript_definition + String.build do |io| + io << "export interface #{name} {\n" + fields.each do |field| + io << " #{field.name}: #{field.type.typescript_native_type};\n" + end + io << "}" + end + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"object\");\n" + fields.each do |field| + io << field.type.typescript_expect("#{expr}.#{field.name}") + end + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + fields.each do |field| + io << field.type.typescript_check_encoded("#{expr}.#{field.name}", "#{descr} + \".#{field.name}\"") + end + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + fields.each do |field| + io << field.type.typescript_check_decoded("#{expr}.#{field.name}", "#{descr} + \".#{field.name}\"") + end + end + end + end +end diff --git a/src/codegen_types/uint.cr b/src/codegen_types/uint.cr new file mode 100644 index 0000000..db132ee --- /dev/null +++ b/src/codegen_types/uint.cr @@ -0,0 +1,41 @@ +module AST + class UIntPrimitiveType + def typescript_decode(expr) + "#{expr} | 0" + end + + def typescript_encode(expr) + "#{expr} | 0" + end + + def typescript_native_type + "number" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"number\");\n" + io << "expect(#{expr} - (#{expr} | 0)).toBe(0);\n" + io << "expect(#{expr}).toBeGreaterThanOrEqual(0);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"number\" || (#{expr} | 0) !== #{expr} || #{expr} < 0) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"number\" || (#{expr} | 0) !== #{expr} || #{expr} < 0) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/url.cr b/src/codegen_types/url.cr new file mode 100644 index 0000000..7d9bf64 --- /dev/null +++ b/src/codegen_types/url.cr @@ -0,0 +1,39 @@ +module AST + class UrlPrimitiveType + def typescript_decode(expr) + "#{expr}.normalize()" + end + + def typescript_encode(expr) + "#{expr}.normalize()" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/uuid.cr b/src/codegen_types/uuid.cr new file mode 100644 index 0000000..a0790ef --- /dev/null +++ b/src/codegen_types/uuid.cr @@ -0,0 +1,40 @@ +module AST + class UuidPrimitiveType + def typescript_decode(expr) + "#{expr}" + end + + def typescript_encode(expr) + "#{expr}" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + io << "expect(#{expr}).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\" || !#{expr}.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/)) {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/codegen_types/void.cr b/src/codegen_types/void.cr new file mode 100644 index 0000000..d49a596 --- /dev/null +++ b/src/codegen_types/void.cr @@ -0,0 +1,27 @@ +module AST + class VoidPrimitiveType + def typescript_decode(expr) + "undefined" + end + + def typescript_encode(expr) + "null" + end + + def typescript_native_type + "void" + end + + def typescript_expect(expr) + "" + end + + def typescript_check_encoded(expr, descr) + "" + end + + def typescript_check_decoded(expr, descr) + "" + end + end +end diff --git a/src/codegen_types/xml.cr b/src/codegen_types/xml.cr new file mode 100644 index 0000000..7e2743a --- /dev/null +++ b/src/codegen_types/xml.cr @@ -0,0 +1,39 @@ +module AST + class XmlPrimitiveType + def typescript_decode(expr) + "#{expr}.normalize()" + end + + def typescript_encode(expr) + "#{expr}.normalize()" + end + + def typescript_native_type + "string" + end + + def typescript_expect(expr) + String.build do |io| + io << "expect(#{expr}).toBeTypeOf(\"string\");\n" + end + end + + def typescript_check_encoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + + def typescript_check_decoded(expr, descr) + String.build do |io| + io << "if (#{expr} === null || #{expr} === undefined || typeof #{expr} !== \"string\") {\n" + io << " const err = new Error(\"Invalid Type at '\" + #{descr} + \"', expected #{self.class.name}, got '\" + #{expr} + \"'\");\n" + io << " typeCheckerError(err, ctx);\n" + io << "}\n" + end + end + end +end diff --git a/src/main.cr b/src/main.cr new file mode 100644 index 0000000..035f5ba --- /dev/null +++ b/src/main.cr @@ -0,0 +1,58 @@ +require "./syntax/parser" +require "./semantic/ast_semantic" +require "./target/java_android" +require "./target/swift_ios" +require "./target/typescript_nodeserver" +require "./target/typescript_nodeclient" +require "./target/typescript_servertest" +require "./target/typescript_web" +require "option_parser" +require "file_utils" +require "colorize" + +is_server = false +destination = "" +target_name = "" +sources = [] of String + +OptionParser.parse! do |parser| + parser.banner = "Usage: salute [arguments]" + parser.on("-o NAME", "--output=NAME", "Specifies the output file") { |name| destination = name } + parser.on("-t TARGET", "--target=TARGET", "Specifies the target platform") { |target| target_name = target } + parser.on("-h", "--help", "Show this help") { puts parser } + parser.unknown_args { |args| sources = args } +end + +if sources.size == 0 + STDERR.puts "You must specify one source file" + exit 1 +elsif sources.size > 1 + STDERR.puts "You must specify only one source file" + exit 1 +end + +source = sources[0] + +begin + parser = Parser.new(source) + ast = parser.parse + ast.semantic + + if destination == "" + STDERR.puts "You must specify an output file" + exit 1 + end + + if target_name == "" + STDERR.puts "You must specify a target" + exit 1 + end + + FileUtils.mkdir_p(File.dirname(destination)) + Target.process(ast, destination, target_name) +rescue ex : Lexer::LexerException | Parser::ParserException | Semantic::SemanticException + STDERR.puts (ex.message || "Invalid source").colorize.light_red + exit 1 +rescue ex : Exception + raise ex +end diff --git a/src/semantic/apply_struct_spreads.cr b/src/semantic/apply_struct_spreads.cr new file mode 100644 index 0000000..b6c4d85 --- /dev/null +++ b/src/semantic/apply_struct_spreads.cr @@ -0,0 +1,30 @@ +require "./visitor" + +module Semantic + class ApplyStructSpreads < Visitor + # Here we may visit the same struct multiple times + # We must make sure we only process each one once + @processed = Set(AST::StructType).new + + def visit(t : AST::StructType) + return if @processed.includes? t + @processed << t + + super + + t.spreads.map(&.type).each do |other| + unless other.is_a? AST::StructType + raise SemanticException.new("A spread operator in a struct can't refer to something that is not a struct, in '#{t.name}'.") + end + visit other # recursion! + + other.fields.each do |other_field| + if t.fields.find { |f| f.name == other_field.name } + raise SemanticException.new("The field '#{other_field.name}' happens on both '#{t.name}' and '#{other.name}'.") + end + t.fields << other_field + end + end + end + end +end diff --git a/src/semantic/ast_semantic.cr b/src/semantic/ast_semantic.cr new file mode 100644 index 0000000..ba870eb --- /dev/null +++ b/src/semantic/ast_semantic.cr @@ -0,0 +1,81 @@ +require "./visitor" +require "./match_type_definitions" +require "./check_no_recursive_types" +require "./check_dont_return_secret" +require "./check_naming_for_getters_returning_bool" +require "./check_empty_enum" +require "./give_struct_and_enum_names" +require "./collect_struct_and_enum_types" +require "./check_multiple_declaration" +require "./apply_struct_spreads" + +module Semantic + class SemanticException < Exception + end +end + +module AST + class ApiDescription + property struct_types = [] of AST::StructType + property enum_types = [] of AST::EnumType + + def semantic + errors << "Fatal" + errors << "Connection" + errors << "Serialization" + error_types_enum = AST::EnumType.new + error_types_enum.values = errors + error_types_enum_def = AST::TypeDefinition.new + error_types_enum_def.name = "ErrorType" + error_types_enum_def.type = error_types_enum + type_definitions << error_types_enum_def + + op = AST::FunctionOperation.new + op.name = "ping" + op.return_type = AST::StringPrimitiveType.new + operations << op + + op = AST::FunctionOperation.new + op.name = "setPushToken" + op.args = [AST::Field.new] + op.args[0].name = "token" + op.args[0].type = AST::StringPrimitiveType.new + op.return_type = AST::VoidPrimitiveType.new + operations << op + + Semantic::CheckMultipleDeclaration.visit(self) + Semantic::MatchTypeDefinitions.visit(self) + Semantic::CheckNoRecursiveTypes.visit(self) + Semantic::CheckDontReturnSecret.visit(self) + Semantic::CheckNamingForGettersReturningBool.visit(self) + Semantic::GiveStructAndEnumNames.visit(self) + Semantic::CheckEmptyEnum.visit(self) + Semantic::CollectStructAndEnumTypes.visit(self) + Semantic::ApplyStructSpreads.visit(self) + end + end + + class TypeReference + property! type : Type + end + + class StructType + property! name : String + end + + class EnumType + property! name : String + end + + abstract class Operation + def pretty_name + name + end + end + + class GetOperation < Operation + def pretty_name + return_type.is_a?(BoolPrimitiveType) ? name : "get" + name[0].upcase + name[1..-1] + end + end +end diff --git a/src/semantic/check_dont_return_secret.cr b/src/semantic/check_dont_return_secret.cr new file mode 100644 index 0000000..c504c79 --- /dev/null +++ b/src/semantic/check_dont_return_secret.cr @@ -0,0 +1,29 @@ +require "./visitor" + +module Semantic + class CheckDontReturnSecret < Visitor + @inreturn = false + @path = [] of String + + def visit(op : AST::Operation) + @inreturn = true + @path.push op.name + "()" + visit op.return_type + @path.pop + @inreturn = false + end + + def visit(ref : AST::TypeReference) + visit ref.type + end + + def visit(field : AST::Field) + @path.push field.name + if @inreturn && field.secret + raise SemanticException.new("Can't return a secret value at #{@path.join(".")}") + end + super + @path.pop + end + end +end diff --git a/src/semantic/check_empty_enum.cr b/src/semantic/check_empty_enum.cr new file mode 100644 index 0000000..194b70e --- /dev/null +++ b/src/semantic/check_empty_enum.cr @@ -0,0 +1,12 @@ +require "./visitor" + +module Semantic + class CheckEmptyEnum < Visitor + def visit(t : AST::EnumType) + super + if t.values.size == 0 + raise SemanticException.new("Enum '#{t.name}' is empty") + end + end + end +end diff --git a/src/semantic/check_multiple_declaration.cr b/src/semantic/check_multiple_declaration.cr new file mode 100644 index 0000000..ed403d8 --- /dev/null +++ b/src/semantic/check_multiple_declaration.cr @@ -0,0 +1,24 @@ +require "./visitor" + +module Semantic + class CheckMultipleDeclaration < Visitor + @names = Set(String).new + @op_names = Set(String).new + + def visit(definition : AST::TypeDefinition) + if @names.includes? definition.name + raise SemanticException.new("Type '#{definition.name}' is defined multiple times") + end + @names << definition.name + super + end + + def visit(op : AST::Operation) + if @op_names.includes? op.pretty_name + raise SemanticException.new("Function '#{op.pretty_name}' is declared multiples times") + end + @op_names << op.pretty_name + super + end + end +end diff --git a/src/semantic/check_naming_for_getters_returning_bool.cr b/src/semantic/check_naming_for_getters_returning_bool.cr new file mode 100644 index 0000000..9af565b --- /dev/null +++ b/src/semantic/check_naming_for_getters_returning_bool.cr @@ -0,0 +1,16 @@ +require "./visitor" + +module Semantic + class CheckNamingForGettersReturningBool < Visitor + def visit(op : AST::GetOperation) + super + is_bool = op.return_type.is_a? AST::BoolPrimitiveType + has_bool_name = op.name =~ /^(is|has|can|may|should)/ + if is_bool && !has_bool_name + raise SemanticException.new("Get operation '#{op.name}' returns bool but isn't named accordingly") + elsif !is_bool && has_bool_name + raise SemanticException.new("Get operation '#{op.name}' doesn't return bool but its name suggest it does") + end + end + end +end diff --git a/src/semantic/check_no_recursive_types.cr b/src/semantic/check_no_recursive_types.cr new file mode 100644 index 0000000..30ec185 --- /dev/null +++ b/src/semantic/check_no_recursive_types.cr @@ -0,0 +1,26 @@ +require "./visitor" + +module Semantic + class CheckNoRecursiveTypes < Visitor + @path = [] of String + + def visit(definition : AST::TypeDefinition) + @path = [definition.name] + super + end + + def visit(field : AST::Field) + @path.push field.name + super + @path.pop + end + + def visit(ref : AST::TypeReference) + if ref.name == @path[0] + raise SemanticException.new("Detected type recursion: #{@path.join(".")}") + end + visit ref.type + super + end + end +end diff --git a/src/semantic/collect_struct_and_enum_types.cr b/src/semantic/collect_struct_and_enum_types.cr new file mode 100644 index 0000000..5f99808 --- /dev/null +++ b/src/semantic/collect_struct_and_enum_types.cr @@ -0,0 +1,15 @@ +require "./visitor" + +module Semantic + class CollectStructAndEnumTypes < Visitor + def visit(t : AST::StructType) + @ast.struct_types << t + super + end + + def visit(t : AST::EnumType) + @ast.enum_types << t + super + end + end +end diff --git a/src/semantic/give_struct_and_enum_names.cr b/src/semantic/give_struct_and_enum_names.cr new file mode 100644 index 0000000..8c546ae --- /dev/null +++ b/src/semantic/give_struct_and_enum_names.cr @@ -0,0 +1,33 @@ +require "./visitor" + +module Semantic + class GiveStructAndEnumNames < Visitor + @path = [] of String + @names = Hash(String, Array(String)).new + + def visit(definition : AST::TypeDefinition) + @path = [definition.name] + super + end + + def visit(operation : AST::Operation) + @path = [operation.name] + super + end + + def visit(field : AST::Field) + @path.push field.name + super + @path.pop + end + + def visit(t : AST::StructType | AST::EnumType) + t.name = @path.map { |s| s[0].upcase + s[1..-1] }.join("") + if @names.has_key? t.name + raise SemanticException.new("The name of the type '#{@path.join(".")}' will conflict with '#{@names[t.name].join(".")}'") + end + @names[t.name] = @path.dup + super + end + end +end diff --git a/src/semantic/match_type_definitions.cr b/src/semantic/match_type_definitions.cr new file mode 100644 index 0000000..e3103a4 --- /dev/null +++ b/src/semantic/match_type_definitions.cr @@ -0,0 +1,14 @@ +require "./visitor" + +module Semantic + class MatchTypeDefinitions < Visitor + def visit(ref : AST::TypeReference) + definition = @ast.type_definitions.find { |t| t.name == ref.name } + unless definition + raise SemanticException.new("Could not find type '#{ref.name}'") + end + ref.type = definition.type + super + end + end +end diff --git a/src/semantic/visitor.cr b/src/semantic/visitor.cr new file mode 100644 index 0000000..18011e8 --- /dev/null +++ b/src/semantic/visitor.cr @@ -0,0 +1,46 @@ +require "../ast" + +module Semantic + class Visitor + def self.visit(ast : AST::ApiDescription) + new(ast).visit(ast) + end + + def initialize(@ast : AST::ApiDescription) + end + + def visit(node : AST::ApiDescription) + node.type_definitions.each { |e| visit e } + node.operations.each { |e| visit e } + end + + def visit(op : AST::Operation) + op.args.each { |arg| visit arg } + visit op.return_type + end + + def visit(field : AST::Field) + visit field.type + end + + def visit(definition : AST::TypeDefinition) + visit definition.type + end + + def visit(t : AST::StructType) + t.fields.each { |field| visit field } + t.spreads.each { |ref| visit ref } + end + + def visit(t : AST::ArrayType) + visit t.base + end + + def visit(t : AST::OptionalType) + visit t.base + end + + def visit(t : AST::PrimitiveType | AST::EnumType | AST::TypeReference) + end + end +end diff --git a/src/syntax/ast_to_s.cr b/src/syntax/ast_to_s.cr new file mode 100644 index 0000000..29a920b --- /dev/null +++ b/src/syntax/ast_to_s.cr @@ -0,0 +1,265 @@ +require "../ast" + +module AST + class StringPrimitiveType + def to_s(io) + io << "string" + end + end + + class IntPrimitiveType + def to_s(io) + io << "int" + end + end + + class UIntPrimitiveType + def to_s(io) + io << "uint" + end + end + + class FloatPrimitiveType + def to_s(io) + io << "float" + end + end + + class DatePrimitiveType + def to_s(io) + io << "date" + end + end + + class DateTimePrimitiveType + def to_s(io) + io << "datetime" + end + end + + class BoolPrimitiveType + def to_s(io) + io << "bool" + end + end + + class BytesPrimitiveType + def to_s(io) + io << "bytes" + end + end + + class VoidPrimitiveType + def to_s(io) + io << "void" + end + end + + class MoneyPrimitiveType + def to_s(io) + io << "money" + end + end + + class CpfPrimitiveType + def to_s(io) + io << "cpf" + end + end + + class CnpjPrimitiveType + def to_s(io) + io << "cnpj" + end + end + + class EmailPrimitiveType + def to_s(io) + io << "email" + end + end + + class PhonePrimitiveType + def to_s(io) + io << "phone" + end + end + + class CepPrimitiveType + def to_s(io) + io << "cep" + end + end + + class LatLngPrimitiveType + def to_s(io) + io << "latlng" + end + end + + class UrlPrimitiveType + def to_s(io) + io << "url" + end + end + + class UuidPrimitiveType + def to_s(io) + io << "uuid" + end + end + + class HexPrimitiveType + def to_s(io) + io << "hex" + end + end + + class Base64PrimitiveType + def to_s(io) + io << "base64" + end + end + + class SafeHtmlPrimitiveType + def to_s(io) + io << "safehtml" + end + end + + class XmlPrimitiveType + def to_s(io) + io << "xml" + end + end + + class OptionalType + def to_s(io) + @base.to_s(io) + io << "?" + end + end + + class ArrayType + def to_s(io) + @base.to_s(io) + io << "[]" + end + end + + class ApiDescription + def to_s(io) + anyopt = false + if options.url != "" + io << "$url = " + options.url.inspect(io) + io << "\n" + anyop = true + end + if options.useRethink != true + io << "$useRethink = " + options.useRethink.inspect(io) + io << "\n" + anyop = true + end + if options.retryRequest != true + io << "$retryRequest = " + options.retryRequest.inspect(io) + io << "\n" + anyop = true + end + if options.strict != false + io << "$strict = " + options.strict.inspect(io) + io << "\n" + anyop = true + end + if options.syntheticDefaultImports != true + io << "$syntheticDefaultImports = " + options.syntheticDefaultImports.inspect(io) + io << "\n" + anyop = true + end + io << "\n" if anyop && errors.size != 0 + errors.each do |err| + io << "error " << err << "\n" + end + io << "\n" if errors.size != 0 && type_definitions.size != 0 + type_definitions.each_with_index do |tdef, i| + tdef.to_s(io) + io << "\n" + end + io << "\n" if type_definitions.size != 0 && operations.size != 0 + operations.each do |op| + op.to_s(io) + io << "\n" + end + end + end + + class Field + def to_s(io) + io << name << ": " + type.to_s(io) + io << " !secret" if secret + end + end + + class TypeDefinition + def to_s(io) + io << "type " << name << " " + type.to_s(io) + end + end + + class StructType + def to_s(io) + io << "{\n" + spreads.each do |ref| + io << " ..." << ref.name << "\n" + end + fields.each do |field| + io << " " + field.to_s(io) + io << "\n" + end + io << "}" + end + end + + class TypeReference + def to_s(io) + io << name + end + end + + class GetOperation + def to_s(io) + io << "get " << name << "(" + args.each_with_index do |arg, i| + arg.to_s(io) + io << ", " if i != args.size - 1 + end + io << ")" + unless return_type.is_a? VoidPrimitiveType + io << ": " + return_type.to_s(io) + end + end + end + + class FunctionOperation + def to_s + io << "function " << name << "(" + args.each_with_index do |arg, i| + arg.to_s(io) + io << ", " if i != args.size - 1 + end + io << ")" + unless return_type.is_a? VoidPrimitiveType + io << ": " + return_type.to_s(io) + end + end + end +end diff --git a/src/syntax/lexer.cr b/src/syntax/lexer.cr new file mode 100644 index 0000000..c866af0 --- /dev/null +++ b/src/syntax/lexer.cr @@ -0,0 +1,227 @@ +require "./token" + +class Lexer + PRIMITIVES = %w[ + bool int uint float string date datetime bytes + money cpf cnpj email phone cep latlng url + uuid hex base64 safehtml xml + ] + + property filename : String + + class LexerException < Exception + end + + @start_pos = 0 + @start_line = 1 + @start_column = 1 + @line = 1 + @column = 1 + + def initialize(string : String, filename : String? = nil) + @filename = filename || "-" + @reader = Char::Reader.new(string) + end + + private def pos + @reader.pos + end + + private def current_char + @reader.current_char + end + + private def next_char + @reader.next_char + @column += 1 + @reader.current_char + end + + private def peek_next_char + @reader.peek_next_char + end + + private def substring(start_pos, end_pos) + reader = Char::Reader.new(@reader.string) + reader.pos = start_pos + String.build do |io| + while reader.pos <= end_pos + io << reader.current_char + reader.next_char + end + end + end + + def next_token + @start_pos = @reader.pos + @start_line = @line + @start_column = @column + token = nil + + case current_char + when '\0' + return nil + when ' ', '\r', '\t' + next_char + return next_token + when '\n' + next_char + @column = 1 + @line += 1 + return next_token + when '/' + case next_char + when '/' + while true + case next_char + when '\0' + return nil + when '\n' + next_char + @column = 1 + @line += 1 + return next_token + end + end + when '*' + while true + case next_char + when '\n' + @column = 0 + @line += 1 + when '*' + while next_char == '*' + end + case current_char + when '\n' + @column = 0 + @line += 1 + when '\0' + break + when '/' + next_char + return next_token + end + when '\0' + break + end + end + end + when '{' + next_char + token = CurlyOpenSymbolToken.new + when '}' + next_char + token = CurlyCloseSymbolToken.new + when '(' + next_char + token = ParensOpenSymbolToken.new + when ')' + next_char + token = ParensCloseSymbolToken.new + when '?' + next_char + token = OptionalSymbolToken.new + when ':' + next_char + token = ColonSymbolToken.new + when '!' + next_char + token = ExclamationMarkSymbolToken.new + when ',' + next_char + token = CommaSymbolToken.new + when '=' + next_char + token = EqualSymbolToken.new + when '[' + case next_char + when ']' + next_char + token = ArraySymbolToken.new + end + when '.' + case next_char + when '.' + case next_char + when '.' + next_char + token = SpreadSymbolToken.new + end + end + when '$' + next_char + if current_char.ascii_letter? + while current_char.ascii_letter? || current_char.ascii_number? + next_char + end + + token = GlobalOptionToken.new(substring(@start_pos + 1, pos - 1)) + end + when '"' + builder = String::Builder.new + while true + case next_char + when '\0' + break + when '\\' + case next_char + when '\0' + break + when 'n' ; builder << '\n' + when 't' ; builder << '\t' + when '"' ; builder << '"' + when '\\'; builder << '\\' + else + builder << current_char + end + when '"' + next_char + token = StringLiteralToken.new(builder.to_s) + break + else + builder << current_char + end + end + else + if current_char.ascii_letter? + next_char + while current_char.ascii_letter? || current_char.ascii_number? + next_char + end + + str = substring(@start_pos, pos - 1) + + token = case str + when "error" ; ErrorKeywordToken.new + when "enum" ; EnumKeywordToken.new + when "type" ; TypeKeywordToken.new + when "import" ; ImportKeywordToken.new + when "get" ; GetKeywordToken.new + when "function"; FunctionKeywordToken.new + when "true" ; TrueKeywordToken.new + when "false" ; FalseKeywordToken.new + else + if PRIMITIVES.includes? str + PrimitiveTypeToken.new(str) + else + IdentifierToken.new(str) + end + end + end + end + + if token + token.filename = @filename + token.line = @start_line + token.column = @start_column + return token + else + if current_char != '\0' + raise LexerException.new("Unexpected character #{current_char.inspect} at #{@filename}:#{@line}:#{@column}") + else + raise LexerException.new("Unexpected end of file at #{@filename}") + end + end + end +end diff --git a/src/syntax/parser.cr b/src/syntax/parser.cr new file mode 100644 index 0000000..71c9769 --- /dev/null +++ b/src/syntax/parser.cr @@ -0,0 +1,344 @@ +require "./lexer" +require "../ast" + +class Parser + class ParserException < Exception + end + + @lexers = [] of Lexer + @token : Token | Nil + + def initialize(filename : String) + @lexers << Lexer.new(File.read(filename), filename) + read_next_token + end + + def initialize(io : IO) + @lexers << Lexer.new(io.gets_to_end) + read_next_token + end + + private def read_next_token + while @lexers.size > 0 + @token = @lexers.last.next_token + if @token + return + else + @lexers.pop + end + end + end + + private def current_filename + @lexers.last.filename if @lexers.size > 0 + end + + def parse + api = AST::ApiDescription.new + while @token + case multi_expect(ImportKeywordToken, TypeKeywordToken, GetKeywordToken, FunctionKeywordToken, GlobalOptionToken, ErrorKeywordToken) + when ImportKeywordToken + read_next_token + token = expect StringLiteralToken + source = File.expand_path(token.str + ".sdkgen", File.dirname(current_filename.not_nil!)) + @lexers << Lexer.new(File.read(source), source) + read_next_token + when TypeKeywordToken + api.type_definitions << parse_type_definition + when GetKeywordToken, FunctionKeywordToken + api.operations << parse_operation + when GlobalOptionToken + parse_option(api.options) + when ErrorKeywordToken + read_next_token + token = expect IdentifierToken + read_next_token + api.errors << token.name + end + end + api + end + + macro multi_expect(*token_types) + token = @token + unless token + raise ParserException.new "Expected #{{{token_types.map { |t| t.stringify.gsub(/Token$/, "") }.join(" or ")}}}, but found end of file" + end + + result = nil + + {% for token_type in token_types %} + {% if token_type.stringify == "IdentifierToken" %} + token = token.try_ident + {% end %} + if !result && token.is_a?({{token_type}}) + result = token + end + {% end %} + + unless result + raise ParserException.new "Expected #{{{token_types.map { |t| t.stringify.gsub(/Token$/, "") }.join(" or ")}}} at #{token.location}, but found #{token}" + end + + result + end + + macro expect(token_type) + token = @token + unless token + raise ParserException.new "Expected #{{{token_type.stringify.gsub(/Token$/, "")}}}, but found end of file" + end + {% if token_type.stringify == "IdentifierToken" %} + token = token.try_ident + {% end %} + unless token.is_a?({{token_type}}) + raise ParserException.new "Expected #{{{token_type.stringify.gsub(/Token$/, "")}}} at #{token.location}, but found #{token}" + end + token + end + + def parse_enum + expect EnumKeywordToken + read_next_token + + e = AST::EnumType.new + + expect CurlyOpenSymbolToken + read_next_token + + while true + case token = multi_expect(IdentifierToken, CurlyCloseSymbolToken) + when IdentifierToken + e.values << token.name + read_next_token + when CurlyCloseSymbolToken + read_next_token + return e + end + end + end + + def parse_type_definition + expect TypeKeywordToken + read_next_token + + t = AST::TypeDefinition.new + name_token = expect(IdentifierToken) + unless name_token.name[0].uppercase? + raise ParserException.new "The custom type name must start with an uppercase letter, but found '#{name_token.name}' at #{name_token.location}" + end + t.name = name_token.name + read_next_token + + t.type = parse_type + t + end + + def parse_struct + expect CurlyOpenSymbolToken + read_next_token + + t = AST::StructType.new + field_names = Set(String).new + + while true + case token = multi_expect(IdentifierToken, CurlyCloseSymbolToken, SpreadSymbolToken) + when IdentifierToken + f = parse_field + if field_names.includes? f.name + raise ParserException.new "Cannot redeclare field '#{f.name}'" + end + field_names << f.name + t.fields << f + when SpreadSymbolToken + read_next_token + token = expect IdentifierToken + unless token.name[0].uppercase? + raise ParserException.new "Expected a type name but found '#{token.name}', at #{token.location}" + end + t.spreads << AST::TypeReference.new(token.name) + read_next_token + when CurlyCloseSymbolToken + read_next_token + return t + end + end + end + + def parse_operation + op = nil + case token = multi_expect(GetKeywordToken, FunctionKeywordToken) + when GetKeywordToken + op = AST::GetOperation.new + when FunctionKeywordToken + op = AST::FunctionOperation.new + else + raise "never" + end + + read_next_token + op.name = expect(IdentifierToken).name + ref_deprecated_location_token = @token.not_nil! + read_next_token + arg_names = Set(String).new + + if @token.is_a? ParensOpenSymbolToken + read_next_token + while true + case token = multi_expect(IdentifierToken, ParensCloseSymbolToken, CommaSymbolToken) + when IdentifierToken + f = parse_field + if arg_names.includes? f.name + raise ParserException.new "Cannot redeclare argument '#{f.name}'" + end + arg_names << f.name + op.args << f + when ParensCloseSymbolToken + read_next_token + break + when CommaSymbolToken + read_next_token + next + end + end + else + STDERR.puts "DEPRECATED: Should use '()' even for functions without arguments. See '#{op.name}' at #{ref_deprecated_location_token.location}.".colorize.light_yellow + end + + if @token.is_a? ColonSymbolToken + expect ColonSymbolToken + read_next_token + op.return_type = parse_type + else + op.return_type = AST::VoidPrimitiveType.new + end + + op + end + + def parse_option(options) + var = expect GlobalOptionToken + read_next_token + expect EqualSymbolToken + read_next_token + + case var.name + when "url" + token = expect StringLiteralToken + read_next_token + options.url = token.str + when "useRethink" + case token = multi_expect(TrueKeywordToken, FalseKeywordToken) + when TrueKeywordToken + options.useRethink = true + when FalseKeywordToken + options.useRethink = false + end + read_next_token + when "retryRequest" + case token = multi_expect(TrueKeywordToken, FalseKeywordToken) + when TrueKeywordToken + options.retryRequest = true + when FalseKeywordToken + options.retryRequest = false + end + read_next_token + when "strict" + case token = multi_expect(TrueKeywordToken, FalseKeywordToken) + when TrueKeywordToken + options.strict = true + when FalseKeywordToken + options.strict = false + end + read_next_token + when "syntheticDefaultImports" + case token = multi_expect(TrueKeywordToken, FalseKeywordToken) + when TrueKeywordToken + options.syntheticDefaultImports = true + when FalseKeywordToken + options.syntheticDefaultImports = false + end + read_next_token + else + raise ParserException.new("Unknown option $#{var.name} at #{var.location}") + end + end + + def parse_field + field = AST::Field.new + field.name = expect(IdentifierToken).name + read_next_token + expect ColonSymbolToken + read_next_token + field.type = parse_type + + while @token.is_a?(ExclamationMarkSymbolToken) + read_next_token + case (token = expect(IdentifierToken)).name + when "secret" + field.secret = true + else + raise ParserException.new "Unknown field mark !#{token.name} at #{token.location}" + end + read_next_token + end + + field + end + + def parse_type + case token = multi_expect(CurlyOpenSymbolToken, EnumKeywordToken, PrimitiveTypeToken, IdentifierToken) + when CurlyOpenSymbolToken + result = parse_struct + when EnumKeywordToken + result = parse_enum + when IdentifierToken + unless token.name[0].uppercase? + raise ParserException.new "Expected a type but found '#{token.name}', at #{token.location}" + end + result = AST::TypeReference.new(token.name) + read_next_token + when PrimitiveTypeToken + result = case token.name + when "string" ; AST::StringPrimitiveType.new + when "int" ; AST::IntPrimitiveType.new + when "uint" ; AST::UIntPrimitiveType.new + when "date" ; AST::DatePrimitiveType.new + when "datetime"; AST::DateTimePrimitiveType.new + when "float" ; AST::FloatPrimitiveType.new + when "bool" ; AST::BoolPrimitiveType.new + when "bytes" ; AST::BytesPrimitiveType.new + when "money" ; AST::MoneyPrimitiveType.new + when "cpf" ; AST::CpfPrimitiveType.new + when "cnpj" ; AST::CnpjPrimitiveType.new + when "email" ; AST::EmailPrimitiveType.new + when "phone" ; AST::PhonePrimitiveType.new + when "cep" ; AST::CepPrimitiveType.new + when "latlng" ; AST::LatLngPrimitiveType.new + when "url" ; AST::UrlPrimitiveType.new + when "uuid" ; AST::UuidPrimitiveType.new + when "hex" ; AST::HexPrimitiveType.new + when "base64" ; AST::Base64PrimitiveType.new + when "safehtml"; AST::SafeHtmlPrimitiveType.new + when "xml" ; AST::XmlPrimitiveType.new + else + raise "BUG! Should handle primitive #{token.name}" + end + read_next_token + else + raise "never" + end + + while @token.is_a? ArraySymbolToken || @token.is_a? OptionalSymbolToken + case @token + when ArraySymbolToken + result = AST::ArrayType.new(result) + when OptionalSymbolToken + result = AST::OptionalType.new(result) + end + read_next_token + end + + result + end +end diff --git a/src/syntax/token.cr b/src/syntax/token.cr new file mode 100644 index 0000000..f68893c --- /dev/null +++ b/src/syntax/token.cr @@ -0,0 +1,148 @@ +class Token + property! filename : String + property! line : Int32 + property! column : Int32 + + def try_ident + self + end + + def location + "#{filename}:#{line}:#{column}" + end + + def to_s(io) + io << self.class.name.sub("Token", "") + vars = [] of String + {% for ivar in @type.instance_vars.map(&.id).reject { |s| %w[filename line column].map(&.id).includes? s } %} + vars << @{{ivar.id}} + {% end %} + vars.inspect(io) if vars.size > 0 + end + + def inspect(io) + to_s(io) + end + + def ==(other : Token) + return false if other.class != self.class + return true + end +end + +class ImportKeywordToken < Token + def try_ident + IdentifierToken.new("import") + end +end + +class TypeKeywordToken < Token + def try_ident + IdentifierToken.new("type") + end +end + +class EnumKeywordToken < Token + def try_ident + IdentifierToken.new("enum") + end +end + +class GetKeywordToken < Token + def try_ident + IdentifierToken.new("get") + end +end + +class FunctionKeywordToken < Token + def try_ident + IdentifierToken.new("function") + end +end + +class ErrorKeywordToken < Token + def try_ident + IdentifierToken.new("error") + end +end + +class TrueKeywordToken < Token + def try_ident + IdentifierToken.new("true") + end +end + +class FalseKeywordToken < Token + def try_ident + IdentifierToken.new("false") + end +end + +class PrimitiveTypeToken < Token + property name : String + def_equals name + + def initialize(@name) + end + + def try_ident + IdentifierToken.new(@name) + end +end + +class IdentifierToken < Token + property name : String + def_equals name + + def initialize(@name) + end +end + +class GlobalOptionToken < Token + property name : String + def_equals name + + def initialize(@name) + end +end + +class StringLiteralToken < Token + property str : String + def_equals str + + def initialize(@str) + end +end + +class EqualSymbolToken < Token +end + +class ExclamationMarkSymbolToken < Token +end + +class CurlyOpenSymbolToken < Token +end + +class CurlyCloseSymbolToken < Token +end + +class ParensOpenSymbolToken < Token +end + +class ParensCloseSymbolToken < Token +end + +class ColonSymbolToken < Token +end + +class OptionalSymbolToken < Token +end + +class ArraySymbolToken < Token +end + +class CommaSymbolToken < Token +end + +class SpreadSymbolToken < Token +end diff --git a/src/target/java.cr b/src/target/java.cr new file mode 100644 index 0000000..73fe92c --- /dev/null +++ b/src/target/java.cr @@ -0,0 +1,264 @@ +require "./target" +require "random/secure" + +abstract class JavaTarget < Target + def mangle(ident) + if %w[ + boolean class if int byte do for while void float double long char synchronized + instanceof extends implements interface abstract static public private protected + final import package throw throws catch finally try new null else return continue + break goto switch default case in + Object Class + ].includes? ident + "_" + ident + else + ident + end + end + + def native_type_not_primitive(t : AST::PrimitiveType) + case t + when AST::StringPrimitiveType ; "String" + when AST::IntPrimitiveType ; "Integer" + when AST::UIntPrimitiveType ; "Integer" + when AST::FloatPrimitiveType ; "Double" + when AST::DatePrimitiveType ; "Calendar" + when AST::DateTimePrimitiveType; "Calendar" + when AST::BoolPrimitiveType ; "Boolean" + when AST::BytesPrimitiveType ; "byte[]" + when AST::VoidPrimitiveType ; "void" + else + raise "BUG! Should handle primitive #{t.class}" + end + end + + def native_type_not_primitive(t : AST::Type) + native_type(t) + end + + def native_type(t : AST::PrimitiveType) + case t + when AST::StringPrimitiveType ; "String" + when AST::IntPrimitiveType ; "int" + when AST::UIntPrimitiveType ; "int" + when AST::FloatPrimitiveType ; "double" + when AST::DatePrimitiveType ; "Calendar" + when AST::DateTimePrimitiveType; "Calendar" + when AST::BoolPrimitiveType ; "boolean" + when AST::BytesPrimitiveType ; "byte[]" + when AST::VoidPrimitiveType ; "void" + else + raise "BUG! Should handle primitive #{t.class}" + end + end + + def native_type(t : AST::OptionalType) + native_type_not_primitive(t.base) + end + + def native_type(t : AST::ArrayType) + "ArrayList<#{native_type_not_primitive(t.base)}>" + end + + def native_type(t : AST::StructType | AST::EnumType) + mangle t.name + end + + def native_type(ref : AST::TypeReference) + native_type ref.type + end + + def generate_struct_type(t) + String.build do |io| + io << "public static class #{mangle t.name} implements Parcelable, Comparable<#{mangle t.name}> {\n" + t.fields.each do |field| + io << ident "public #{native_type field.type} #{mangle field.name};\n" + end + io << ident <<-END + +public int compareTo(#{mangle t.name} other) { + return toJSON().toString().compareTo(other.toJSON().toString()); +} + +public JSONObject toJSON() { + try { + return new JSONObject() {{ + +END + t.fields.each do |field| + io << ident ident ident ident "put(\"#{field.name}\", #{type_to_json field.type, mangle field.name});\n" + end + io << ident <<-END + }}; + } catch (JSONException e) { + e.printStackTrace(); + return new JSONObject(); + } +} + +public static JSONArray toJSONArray(List<#{mangle t.name}> list) { + JSONArray array = null; + if (list != null && !list.isEmpty()) { + array = new JSONArray(); + for (int i=0; i fromJSONArray(final JSONArray jsonArray) { + ArrayList<#{mangle t.name}> list = null; + if (jsonArray != null && jsonArray.length() > 0) { + list = new ArrayList<#{mangle t.name}>(); + for (int i = 0; i < jsonArray.length(); i++) { + try { + JSONObject obj = jsonArray.getJSONObject(i); + list.add(fromJSON(obj)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return list; +} + +public #{mangle t.name}() { +} + +protected #{mangle t.name}(final JSONObject json) { + try { + +END + t.fields.each do |field| + io << ident ident ident "#{mangle field.name} = #{type_from_json field.type, "json", field.name.inspect};\n" + end + io << ident <<-END + + } catch (JSONException e) { + e.printStackTrace(); + } +} + +protected #{mangle t.name}(Parcel in) { + try { + final JSONObject json = new JSONObject(in.readString()); + +END + t.fields.each do |field| + io << ident ident ident "#{mangle field.name} = #{type_from_json field.type, "json", field.name.inspect};\n" + end + io << ident <<-END + } catch (JSONException e) { + e.printStackTrace(); + } +} + +@Override +public void writeToParcel(Parcel dest, int flags) { + dest.writeString(toJSON().toString()); +} + +@Override +public int describeContents() { + return 0; +} + +public static final Parcelable.Creator<#{mangle t.name}> CREATOR = new Parcelable.Creator<#{mangle t.name}>() { + @Override + public #{mangle t.name} createFromParcel(Parcel in) { + return new #{mangle t.name}(in); + } + + @Override + public #{mangle t.name}[] newArray(int size) { + return new #{mangle t.name}[size]; + } +}; + +END + io << "}" + end + end + + def generate_enum_type(t) + String.build do |io| + io << "public enum #{mangle t.name} {\n" + t.values.each do |value| + io << ident "#{mangle value},\n" + end + io << "}" + end + end + + def type_from_json(t : AST::Type, obj : String, name : String) + case t + when AST::StringPrimitiveType + "#{obj}.getString(#{name})" + when AST::IntPrimitiveType, AST::UIntPrimitiveType + "#{obj}.getInt(#{name})" + when AST::FloatPrimitiveType + "#{obj}.getDouble(#{name})" + when AST::BoolPrimitiveType + "#{obj}.getBoolean(#{name})" + when AST::DatePrimitiveType + "DateHelpers.decodeDate(#{obj}.getString(#{name}))" + when AST::DateTimePrimitiveType + "DateHelpers.decodeDateTime(#{obj}.getString(#{name}))" + when AST::BytesPrimitiveType + "Base64.decode(#{obj}.getString(#{name}), Base64.DEFAULT)" + when AST::VoidPrimitiveType + "null" + when AST::OptionalType + "#{obj}.isNull(#{name}) ? null : #{type_from_json(t.base, obj, name)}" + when AST::ArrayType + i = "i" + Random::Secure.hex[0, 5] + ary = "ary" + Random::Secure.hex[0, 5] + "new #{native_type t}() {{ final JSONArray #{ary} = #{obj}.getJSONArray(#{name}); for (int #{i} = 0; #{i} < #{ary}.length(); ++#{i}) {final int x#{i} = #{i}; add(#{type_from_json(t.base, "#{ary}", "x#{i}")});} }}" + when AST::StructType + "#{mangle t.name}.fromJSON(#{obj}.getJSONObject(#{name}))" + when AST::EnumType + "#{t.values.map { |v| "#{obj}.getString(#{name}).equals(#{v.inspect}) ? #{mangle t.name}.#{mangle v} : " }.join}null" + when AST::TypeReference + type_from_json(t.type, obj, name) + else + raise "Unknown type" + end + end + + def type_to_json(t : AST::Type, src : String) + case t + when AST::StringPrimitiveType, AST::IntPrimitiveType, AST::UIntPrimitiveType, AST::FloatPrimitiveType, AST::BoolPrimitiveType + "#{src}" + when AST::DatePrimitiveType + "DateHelpers.encodeDate(#{src})" + when AST::DateTimePrimitiveType + "DateHelpers.encodeDateTime(#{src})" + when AST::BytesPrimitiveType + "Base64.encodeToString(#{src}, Base64.DEFAULT)" + when AST::VoidPrimitiveType + "JSONObject.NULL" + when AST::OptionalType + "#{src} == null ? JSONObject.NULL : #{type_to_json(t.base, src)}" + when AST::ArrayType + el = "el" + Random::Secure.hex[0, 5] + "new JSONArray() {{ for (final #{native_type t.base} #{el} : #{src}) put(#{type_to_json t.base, "#{el}"}); }}" + when AST::StructType + "#{src}.toJSON()" + when AST::EnumType + "#{t.values.map { |v| "#{src} == #{mangle t.name}.#{mangle v} ? #{v.inspect} : " }.join}\"\"" + when AST::TypeReference + type_to_json(t.type, src) + else + raise "Unknown type" + end + end +end diff --git a/src/target/java_android.cr b/src/target/java_android.cr new file mode 100644 index 0000000..06d4d59 --- /dev/null +++ b/src/target/java_android.cr @@ -0,0 +1,938 @@ +require "./java" + +class JavaAndroidTarget < JavaTarget + def gen + @io << <<-END + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Application; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager; +import android.graphics.Point; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.Parcel; +import android.os.Parcelable; +import android.provider.Settings; +import android.util.Base64; +import android.util.Log; +import android.view.Display; +import android.view.WindowManager; + +import com.anupcowkur.reservoir.Reservoir; +import com.anupcowkur.reservoir.ReservoirGetCallback; +import com.anupcowkur.reservoir.ReservoirPutCallback; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.net.SocketTimeoutException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; + +import okhttp3.Call; +import okhttp3.ConnectionPool; +import okhttp3.Dispatcher; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.Interceptor; + +public class API { + public interface GlobalRequestCallback { + public void onResult(final String method, final Error error, final JSONObject result, final Callback callback); + }; + + static public Calls calls = new Calls(); + static public Application application; + static public boolean useStaging = false; + static public Context serviceContext = null; + static public GlobalRequestCallback globalCallback = new GlobalRequestCallback() { + @Override + public void onResult(final String method, final Error error, final JSONObject result, final Callback callback) { + callback.onResult(error, result); + } + }; + + static public void initialize(Application application) { + Internal.initialize(application); + } + + static public int Default = 0; + static public int Loading = 1; + static public int Cache = 2; + +END + + # INIT CREATING API'S CALLS INTERFACE + @io << <<-END +public interface APICalls { + +END + + @ast.operations.each do |op| + args = op.args.map { |arg| "final #{native_type arg.type} #{mangle arg.name}" } + args << "final #{callback_type op.return_type} callback" + @io << ident(String.build do |io| + io << "public void #{mangle op.pretty_name}(#{args.join(", ")});\n" + end) + end + + @io << <<-END +} +END + # END CREATING API'S CALLS INTERFACE + + @ast.struct_types.each do |t| + @io << ident generate_struct_type(t) + @io << "\n\n" + end + + @ast.enum_types.each do |t| + @io << ident generate_enum_type(t) + @io << "\n\n" + end + + # INIT CREATING CALLS + @io << <<-END + + public static class Calls implements APICalls { + +END + + @ast.operations.each do |op| + args = op.args.map { |arg| "final #{native_type arg.type} #{mangle arg.name}" } + args << "final #{callback_type op.return_type} callback" + @io << ident(String.build do |io| + io << "@Override \n" + io << "public void #{mangle op.pretty_name}(#{args.join(", ")}) {\n" + io << " #{mangle op.pretty_name}(#{(op.args.map { |arg| mangle arg.name } + ["0", "callback"]).join(", ")});\n" + io << "}" + end) + @io << "\n\n" + args = args[0..-2] + ["final int flags", args[-1]] + @io << ident(String.build do |io| + io << "public void #{mangle op.pretty_name}(#{args.join(", ")}) {\n" + io << ident(String.build do |io| + if op.args.size == 0 + io << "final JSONObject args = new JSONObject();" + else + io << <<-END +final JSONObject args; +try { + args = new JSONObject() {{ + +END + op.args.each do |arg| + io << ident ident "put(\"#{arg.name}\", #{type_to_json arg.type, mangle arg.name});\n" + end + io << <<-END + }}; +} catch (final JSONException e) { + e.printStackTrace(); + globalCallback.onResult(#{op.pretty_name.inspect}, new Error() {{type = ErrorType.Fatal; message = e.getMessage();}}, null, new Callback() { + @Override + public void onResult(final Error error,final JSONObject result) { + +END + if op.return_type.is_a? AST::VoidPrimitiveType + io << <<-END + callback.onResult(error); + +END + else + io << <<-END + if (error != null) { + callback.onResult(error, null); + } else { + try { + callback.onResult(null, #{type_from_json op.return_type, "result", "result".inspect}); + } catch (final JSONException e) { + e.printStackTrace(); + callback.onResult(new Error() {{type = ErrorType.Fatal; message = e.getMessage();}}, null); + } + } +END + end + io << <<-END + + } + }); + return; +} + +END + end + io << <<-END + +Internal.RequestCallback reqCallback = new Internal.RequestCallback() { + @Override + public void onResult(final Error error, final JSONObject result) { + +END + if op.return_type.is_a? AST::VoidPrimitiveType + io << <<-END + globalCallback.onResult(#{op.pretty_name.inspect}, error, null, new Callback() { + @Override + public void onResult(final Error error,final JSONObject result) { + callback.onResult(error); + } + }); + + +END + else + io << <<-END + globalCallback.onResult(#{op.pretty_name.inspect}, error, result, new Callback() { + @Override + public void onResult(final Error error,final JSONObject result) { + if (error != null) { + callback.onResult(error, null); + } else { + try { + callback.onResult(null, #{type_from_json op.return_type, "result", "result".inspect}); + } catch (final JSONException e) { + e.printStackTrace(); + callback.onResult(new Error() {{type = ErrorType.Fatal; message = e.getMessage();}}, null); + } + } + } + }); +END + end + io << <<-END + } +}; +Internal.initialize(); +if ((flags & API.Loading) != 0) { + reqCallback = Internal.withLoading(reqCallback); +} +if ((flags & API.Cache) != 0) { + String signature = "#{mangle op.pretty_name}:" + Internal.hash(args.toString()); + final Internal.RequestCallback reqCallbackPure = reqCallback; + final Internal.RequestCallback reqCallbackSaveCache = Internal.withSavingOnCache(signature, reqCallback); + Reservoir.getAsync(signature, String.class, new ReservoirGetCallback() { + @Override + public void onSuccess(String json) { + try { + JSONObject data = new JSONObject(json); + Calendar time = Internal.decodeDateTime(data.getString("time")); + callback.cacheAge = (int)((new GregorianCalendar().getTimeInMillis() - time.getTimeInMillis()) / 1000); + callback.repeatWithoutCacheRunnable = new Runnable() { + @Override + public void run() { + Internal.makeRequest(#{mangle(op.pretty_name).inspect}, args, new Internal.RequestCallback() { + @Override + public void onResult(Error error, JSONObject result) { + callback.cacheAge = 0; + reqCallbackSaveCache.onResult(error, result); + } + }); + } + }; + reqCallbackPure.onResult(null, data); + } catch (JSONException e) { + Internal.makeRequest(#{mangle(op.pretty_name).inspect}, args, reqCallbackSaveCache); + } + } + + @Override + public void onFailure(Exception e) { + Internal.makeRequest(#{mangle(op.pretty_name).inspect}, args, reqCallbackSaveCache); + } + }); +} else { + Internal.makeRequest(#{mangle(op.pretty_name).inspect}, args, reqCallback); +} + +END + end) + io << "}" + end) + @io << "\n\n" + end + + @io << <<-END + +} + +END + + # END CREATING CALLS + + @io << <<-END + public static class Error { + public ErrorType type; + public String message; + } + + public static class BaseCallback { + public int cacheAge = 0; + Runnable repeatWithoutCacheRunnable; + + protected void repeatWithoutCache() { + repeatWithoutCacheRunnable.run(); + } + } + + public abstract static class Callback extends BaseCallback { + public abstract void onResult(final Error error, final T result); + } + + public abstract static class VoidCallback extends BaseCallback { + public abstract void onResult(final Error error); + } + + static public void getDeviceId(final Callback callback) { + SharedPreferences pref = Internal.context().getSharedPreferences("api", Context.MODE_PRIVATE); + if (pref.contains("deviceId")) + callback.onResult(null, pref.getString("deviceId", null)); + else { + calls.ping(API.Default, new Callback() { + @Override + public void onResult(Error error, String result) { + if (error != null) + callback.onResult(error, null); + else + getDeviceId(callback); + } + }); + } + } + + static public void setHttpClientInterceptor(Interceptor interceptor) { + Internal.initialize(); + Internal.interceptor = interceptor; + Internal.createHttpClient(); + } + + static public OkHttpClient getHttpClient() { + Internal.initialize(); + return Internal.getHttpClient(); + } + + static public void setHttpClient(OkHttpClient newClient) { + Internal.setHttpClient(newClient); + } + + static public void setApiUrl(String url) { + Internal.forcedUrl = url; + } + + static public String getApiUrl() { + return Internal.forcedUrl != null ? Internal.forcedUrl : "https://" + Internal.baseUrl + (API.useStaging ? "-staging" : ""); + } + + private static class DateHelpers { + static SimpleDateFormat dateTimeFormat; + static SimpleDateFormat dateFormat; + + static { + dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US); + dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + dateTimeFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + } + + static String encodeDateTime(Calendar cal) { + return dateTimeFormat.format(cal.getTime()); + } + + static String encodeDate(Calendar cal) { + return dateFormat.format(cal.getTime()); + } + + static Calendar toCalendar(Date date){ + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + return cal; + } + + static Calendar decodeDateTime(String str) { + try { + return toCalendar(dateTimeFormat.parse(str)); + } catch (ParseException e) { + e.printStackTrace(); + return null; + } + } + + static Calendar decodeDate(String str) { + try { + return toCalendar(dateFormat.parse(str)); + } catch (ParseException e) { + e.printStackTrace(); + return null; + } + } + } + + private static class Internal { + static String forcedUrl = null; + static String baseUrl = #{@ast.options.url.inspect}; + static OkHttpClient http = null; + static ConnectionPool connectionPool; + static SecureRandom random = new SecureRandom(); + static boolean initialized = false; + static SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US); + static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + static Application application; + static Interceptor interceptor = null; + + static { + dateTimeFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + //dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + createHttpClient(); + } + + static OkHttpClient getHttpClient() { + if (http == null) { + createHttpClient(); + } + + return http; + } + + static void setHttpClient(OkHttpClient newClient) { + http = newClient; + } + + static void createHttpClient() { + if (http != null) { + OkHttpClient.Builder builder = http.newBuilder(); + if (interceptor != null) + builder.addNetworkInterceptor(interceptor); + + http = builder.build(); + return; + } + + connectionPool = new ConnectionPool(100, 45, TimeUnit.SECONDS); + + TrustManagerFactory trustManagerFactory; + try { + trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + } catch (NoSuchAlgorithmException | KeyStoreException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new IllegalStateException("Unexpected default trust managers:" + + Arrays.toString(trustManagers)); + } + X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; + + SSLSocketFactory sslSocketFactory; + try { + sslSocketFactory = new TLSSocketFactory(); + } catch (KeyManagementException | NoSuchAlgorithmException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + + Dispatcher dispatcher = new Dispatcher(); + dispatcher.setMaxRequests(200); + dispatcher.setMaxRequestsPerHost(200); + + OkHttpClient.Builder builder = new OkHttpClient.Builder() + .connectionPool(connectionPool) + .dispatcher(dispatcher) + .sslSocketFactory(sslSocketFactory, trustManager) + .connectTimeout(45, TimeUnit.SECONDS); + + if (interceptor != null) + builder.addNetworkInterceptor(interceptor); + + http = builder.build(); + } + + static void initialize() { + if (initialized) return; + initialized = true; + try { + Reservoir.init(context(), 10 * 1024 * 1024 /* 10 MB */); + } catch (IOException e) { + e.printStackTrace(); + } + } + + static void initialize(Application app) { + if (application == null && app != null) { + application = app; + } + initialize(); + } + + static Context context() { + if (application == null) { + try { + Class activityThreadClass = + Class.forName("android.app.ActivityThread"); + Method method = activityThreadClass.getMethod("currentApplication"); + Application app = (Application)method.invoke(null, (Object[]) null); + if (app == null) { + if (API.serviceContext != null) + return API.serviceContext; + else + throw new RuntimeException("Failed to get Application, use API.serviceContext to provide a Context"); + } + return app; + } catch (ClassNotFoundException | NoSuchMethodException | + IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException("Failed to get application from android.app.ActivityThread"); + } + } else { + return application; + } + } + + static Activity getCurrentActivity() { + try { + Class activityThreadClass = Class.forName("android.app.ActivityThread"); + java.lang.Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null); + Field activitiesField = activityThreadClass.getDeclaredField("mActivities"); + activitiesField.setAccessible(true); + + @SuppressWarnings("unchecked") + Map activities = (Map) activitiesField.get(activityThread); + + if (activities == null) + return null; + + for (java.lang.Object activityRecord : activities.values()) { + Class activityRecordClass = activityRecord.getClass(); + Field pausedField = activityRecordClass.getDeclaredField("paused"); + pausedField.setAccessible(true); + if (!pausedField.getBoolean(activityRecord)) { + Field activityField = activityRecordClass.getDeclaredField("activity"); + activityField.setAccessible(true); + return (Activity) activityField.get(activityRecord); + } + } + + return null; + } catch (ClassNotFoundException | NoSuchMethodException | NoSuchFieldException | + IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException("Failed to get current activity from android.app.ActivityThread"); + } + } + + static String language() { + Locale loc = Locale.getDefault(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return loc.toLanguageTag(); + } + + final char SEP = '-'; + String language = loc.getLanguage(); + String region = loc.getCountry(); + String variant = loc.getVariant(); + + if (language.equals("no") && region.equals("NO") && variant.equals("NY")) { + language = "nn"; + region = "NO"; + variant = ""; + } + + if (language.isEmpty() || !language.matches("\\\\p{Alpha}{2,8}")) { + language = "und"; + } else if (language.equals("iw")) { + language = "he"; + } else if (language.equals("in")) { + language = "id"; + } else if (language.equals("ji")) { + language = "yi"; + } + + if (!region.matches("\\\\p{Alpha}{2}|\\\\p{Digit}{3}")) { + region = ""; + } + + if (!variant.matches("\\\\p{Alnum}{5,8}|\\\\p{Digit}\\\\p{Alnum}{3}")) { + variant = ""; + } + + StringBuilder bcp47Tag = new StringBuilder(language); + if (!region.isEmpty()) { + bcp47Tag.append(SEP).append(region); + } + if (!variant.isEmpty()) { + bcp47Tag.append(SEP).append(variant); + } + + return bcp47Tag.toString(); + } + + @SuppressLint("HardwareIds") + static JSONObject device() throws JSONException { + JSONObject device = new JSONObject(); + device.put("type", "android"); + device.put("fingerprint", "" + Settings.Secure.getString(context().getContentResolver(), Settings.Secure.ANDROID_ID)); + device.put("platform", new JSONObject() {{ + put("version", Build.VERSION.RELEASE); + put("sdkVersion", Build.VERSION.SDK_INT); + put("brand", Build.BRAND); + put("model", Build.MODEL); + }}); + try { + device.put("version", context().getPackageManager().getPackageInfo(context().getPackageName(), 0).versionName); + } catch (PackageManager.NameNotFoundException e) { + device.put("version", "unknown"); + } + device.put("language", language()); + device.put("screen", new JSONObject() {{ + WindowManager manager = (WindowManager) context().getSystemService(Context.WINDOW_SERVICE); + Display display = manager.getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + put("width", size.x); + put("height", size.y); + }}); + device.put("timezone", Calendar.getInstance().getTimeZone().getID()); + SharedPreferences pref = context().getSharedPreferences("api", Context.MODE_PRIVATE); + if (pref.contains("deviceId")) + device.put("id", pref.getString("deviceId", null)); + return device; + } + + final private static char[] hexArray = "0123456789abcdef".toCharArray(); + static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 2]; + for ( int j = 0; j < bytes.length; j++ ) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = hexArray[v >>> 4]; + hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + } + return new String(hexChars); + } + + static String randomBytesHex(int len) { + byte[] bytes = new byte[len]; + random.nextBytes(bytes); + return bytesToHex(bytes); + } + + static String hash(String message) { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + return null; + } + return bytesToHex(digest.digest(message.getBytes())); + } + + interface RequestCallback { + void onResult(Error error, JSONObject result); + } + + static RequestCallback withLoading(final RequestCallback callback) { + final ProgressDialog[] progress = new ProgressDialog[] {null}; + final Timer timer = new Timer(); + final TimerTask task = new TimerTask() { + @Override + public void run() { + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + Activity currentActivity = getCurrentActivity(); + if (currentActivity != null) { + progress[0] = ProgressDialog.show(currentActivity, "Aguarde", "Carregando...", true, true); + } + } + }); + } + }; + timer.schedule(task, 800); + return new RequestCallback() { + @Override + public void onResult(Error error, JSONObject result) { + timer.cancel(); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + if (progress[0] != null) + progress[0].dismiss(); + } + }); + callback.onResult(error, result); + } + }; + } + + static RequestCallback withSavingOnCache(final String signature, final RequestCallback callback) { + return new RequestCallback() { + @Override + public void onResult(Error error, final JSONObject result) { + if (error == null) { + Reservoir.putAsync(signature, new JSONObject() {{ + try { + put("time", encodeDateTime(new GregorianCalendar())); + put("result", result.getJSONObject("result")); + } catch (JSONException e) { + e.printStackTrace(); + } + }}.toString(), new ReservoirPutCallback() { + @Override + public void onSuccess() {} + @Override + public void onFailure(Exception e) {} + }); + } + callback.onResult(error, result); + } + }; + } + + static void makeRequest(String name, JSONObject args, final RequestCallback callback) { + initialize(); + + JSONObject body = new JSONObject(); + try { + body.put("id", randomBytesHex(8)); + body.put("device", device()); + body.put("name", name); + body.put("args", args); + body.put("staging", API.useStaging); + } catch (final JSONException e) { + e.printStackTrace(); + callback.onResult(new Error() {{type = ErrorType.Fatal; message = e.getMessage();}}, null); + } + + final Request request = new Request.Builder() + .url(forcedUrl != null ? forcedUrl : "https://" + baseUrl + (API.useStaging ? "-staging" : "") + "/" + name) + .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), body.toString())) + .build(); + + final boolean shouldReceiveResponse[] = new boolean[] {true}; + final int sentCount[] = new int[] {0}; + final Timer timer = new Timer(); + final TimerTask task = new TimerTask() { + @Override + public void run() { + sentCount[0] += 1; + if (sentCount[0] >= 22 || (sentCount[0] * 2000) >= http.connectTimeoutMillis()) { + if (!shouldReceiveResponse[0]) return; + shouldReceiveResponse[0] = false; + timer.cancel(); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + callback.onResult(new Error() {{type = ErrorType.Connection; message = "Erro de conexão, tente novamente mais tarde.";}}, null); + } + }); + return; + } + + if (sentCount[0] >= #{@ast.options.retryRequest ? "22" : "2"}) { + return; + } + + if (sentCount[0] % 4 == 0) { + createHttpClient(); + } + + http.newCall(request).enqueue(new okhttp3.Callback() { + @Override + public void onFailure(Call call, final IOException e) { + if (!shouldReceiveResponse[0] || e instanceof SocketTimeoutException) return; + shouldReceiveResponse[0] = false; + timer.cancel(); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + e.printStackTrace(); + callback.onResult(new Error() {{type = ErrorType.Fatal; message = e.getMessage();}}, null); + } + }); + } + + @Override + public void onResponse(Call call, final Response response) throws IOException { + if (!shouldReceiveResponse[0]) return; + shouldReceiveResponse[0] = false; + timer.cancel(); + if (response.code() == 502) { + Log.e("API", "HTTP " + response.code()); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + callback.onResult(new Error() {{type = ErrorType.Fatal; message = "Erro Fatal (502) - Tente novamente";}}, null); + } + }); + return; + } + final String stringBody = response.body().string(); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + try { + JSONObject body = new JSONObject(stringBody); + + SharedPreferences pref = context().getSharedPreferences("api", Context.MODE_PRIVATE); + pref.edit().putString("deviceId", body.getString("deviceId")).apply(); + + if (!body.getBoolean("ok")) { + JSONObject jsonError = body.getJSONObject("error"); + Error error = new Error(); + error.type = #{type_from_json(@ast.enum_types.find { |e| e.name == "ErrorType" }.not_nil!, "jsonError", "type".inspect)}; + error.message = jsonError.getString("message"); + Log.e("API Error", jsonError.getString("type") + " - " + error.message); + callback.onResult(error, null); + } else { + callback.onResult(null, body); + } + } catch (final JSONException e) { + e.printStackTrace(); + callback.onResult(new Error() {{type = ErrorType.Fatal; message = e.getMessage();}}, null); + } + } + }); + } + }); + } + }; + timer.scheduleAtFixedRate(task, 0, 2000); + } + + static Calendar toCalendar(Date date){ + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + return cal; + } + + static Calendar decodeDateTime(String str) { + try { + return toCalendar(dateTimeFormat.parse(str)); + } catch (ParseException e) { + e.printStackTrace(); + return null; + } + } + + static Calendar decodeDate(String str) { + try { + return toCalendar(dateFormat.parse(str)); + } catch (ParseException e) { + e.printStackTrace(); + return null; + } + } + + static String encodeDateTime(Calendar cal) { + return dateTimeFormat.format(cal.getTime()); + } + + static String encodeDate(Calendar cal) { + return dateFormat.format(cal.getTime()); + } + + static private class TLSSocketFactory extends SSLSocketFactory { + private SSLSocketFactory internalSSLSocketFactory; + + TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, null, null); + internalSSLSocketFactory = context.getSocketFactory(); + } + + @Override + public String[] getDefaultCipherSuites() { + return internalSSLSocketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return internalSSLSocketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket() throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket()); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort)); + } + + private Socket enableTLSOnSocket(Socket socket) { + if (socket != null && (socket instanceof SSLSocket)) { + ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"}); + } + return socket; + } + } + } +} +END + end + + def callback_type(t : AST::Type) + t.is_a?(AST::VoidPrimitiveType) ? "VoidCallback" : "Callback<#{native_type_not_primitive(t)}>" + end +end + +Target.register(JavaAndroidTarget, target_name: "java_android") diff --git a/src/target/swift.cr b/src/target/swift.cr new file mode 100644 index 0000000..5369ff9 --- /dev/null +++ b/src/target/swift.cr @@ -0,0 +1,212 @@ +require "./target" + +abstract class SwiftTarget < Target + def native_type(t : AST::PrimitiveType) + case t + when AST::StringPrimitiveType ; "String" + when AST::IntPrimitiveType ; "Int" + when AST::UIntPrimitiveType ; "UInt" + when AST::FloatPrimitiveType ; "Double" + when AST::DatePrimitiveType ; "Date" + when AST::DateTimePrimitiveType; "Date" + when AST::BoolPrimitiveType ; "Bool" + when AST::BytesPrimitiveType ; "Data" + when AST::VoidPrimitiveType ; "NoReply" + else + raise "BUG! Should handle primitive #{t.class}" + end + end + + def native_type(t : AST::OptionalType) + native_type(t.base) + "?" + end + + def native_type(t : AST::ArrayType) + "[#{native_type(t.base)}]" + end + + def native_type(t : AST::StructType | AST::EnumType) + "API." + t.name + end + + def native_type(ref : AST::TypeReference) + native_type ref.type + end + + def generate_property_copy(t : AST::Type, path : String) + case t + when AST::StringPrimitiveType ; path + when AST::IntPrimitiveType ; path + when AST::UIntPrimitiveType ; path + when AST::FloatPrimitiveType ; path + when AST::DatePrimitiveType ; path + when AST::DateTimePrimitiveType; path + when AST::BoolPrimitiveType ; path + when AST::BytesPrimitiveType ; path + when AST::ArrayType ; "#{path}.map { #{generate_property_copy(t.base, "$0")} }" + when AST::EnumType ; "#{path}.copy" + when AST::StructType ; "#{path}.copy" + when AST::OptionalType ; "#{path} != nil ? #{generate_property_copy(t.base, path + "!")} : nil" + when AST::TypeReference ; generate_property_copy(t.type, path) + else + raise "BUG! Should handle primitive #{t.class}" + end + end + + def generate_struct_type(t) + String.build do |io| + io << "class #{t.name}: Codable {\n" + t.fields.each do |field| + io << ident "var #{field.name}: #{native_type field.type}\n" + end + io << ident "\nvar copy: #{t.name} {\n" + io << ident ident "return #{t.name}(\n" + io << ident ident ident t.fields.map { |field| "#{field.name}: #{generate_property_copy(field.type, field.name)}" }.join(",\n") + io << ident ident "\n)\n" + io << ident "}\n" + t.spreads.map(&.type.as(AST::StructType)).map { |spread| + io << ident "\nvar as#{spread.name}: #{spread.name} {\n" + io << ident ident "return #{spread.name}(\n" + io << ident ident ident spread.fields.map { |field| "#{field.name}: #{field.name}" }.join(",\n") + io << ident ident "\n)\n" + io << ident "}\n" + } + io << ident <<-END + + +init() { + +END + t.fields.each do |field| + io << ident ident "#{field.name} = #{default_value field.type}\n" + end + io << ident <<-END +} + +init(#{t.fields.map { |f| "#{f.name}: #{native_type f.type}" }.join(", ")}) { + +END + t.fields.each do |field| + io << ident ident "self.#{field.name} = #{field.name}\n" + end + io << ident <<-END +} + +init(json: [String: Any]) throws { + let jsonData = try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted) + let decodedSelf = try decoder.decode(#{t.name}.self, from: jsonData)\n + +END + t.fields.each do |field| + io << ident ident "#{field.name} = decodedSelf.#{field.name}\n" + end + io << ident <<-END +} + +func toJSON() -> [String: Any] { + var json = [String: Any]() + +END + t.fields.each do |field| + io << ident ident "json[\"#{field.name}\"] = #{type_to_json field.type, field.name}\n" + end + io << ident <<-END + return json +} + +END + io << "}" + end + end + + def generate_enum_type(t) + String.build do |io| + if t.name == "ErrorType" + io << "enum #{t.name}: String, Error, Codable {\n" + else + io << "enum #{t.name}: String, CaseIterable, DisplayableValue, Codable {\n" + end + + t.values.each do |value| + io << ident "case #{value} = #{value.inspect}\n" + end + if t.name != "ErrorType" + io << ident "\nvar copy: #{t.name} {\n" + io << ident ident "return #{t.name}(rawValue: self.rawValue)!\n" + io << ident "}\n" + io << ident "\nstatic func valuesDictionary() -> [String: #{t.name}] {\n" + io << ident ident "var dictionary: [String: #{t.name}] = [:]\n" + io << ident ident "for enumCase in self.allCases {\n" + io << ident ident ident "dictionary[enumCase.displayableValue] = enumCase\n" + io << ident ident "}\n" + io << ident ident "return dictionary\n" + io << ident "}\n" + + io << ident "\nstatic func allDisplayableValues() -> [String] {\n" + io << ident ident "var displayableValues: [String] = []\n" + io << ident ident "for enumCase in self.allCases {\n" + io << ident ident ident "displayableValues.append(enumCase.displayableValue)\n" + io << ident ident "}\n" + io << ident ident "return displayableValues.sorted()\n" + io << ident "}\n" + end + io << "}" + end + end + + def default_value(t : AST::Type) + case t + when AST::StringPrimitiveType + "\"\"" + when AST::IntPrimitiveType, AST::UIntPrimitiveType, AST::FloatPrimitiveType + "0" + when AST::BoolPrimitiveType + "false" + when AST::DatePrimitiveType, AST::DateTimePrimitiveType + "Date()" + when AST::BytesPrimitiveType + "Data()" + when AST::VoidPrimitiveType + "nil" + when AST::OptionalType + "nil" + when AST::ArrayType + "[]" + when AST::StructType + "#{t.name}()" + when AST::EnumType + "#{t.name}.#{t.values[0]}" + when AST::TypeReference + default_value(t.type) + else + raise "Unknown type" + end + end + + def type_to_json(t : AST::Type, src : String) + case t + when AST::StringPrimitiveType, AST::IntPrimitiveType, AST::UIntPrimitiveType, AST::FloatPrimitiveType, AST::BoolPrimitiveType + "#{src}" + when AST::DatePrimitiveType + "apiInternal.encodeDate(date: #{src})" + when AST::DateTimePrimitiveType + "apiInternal.encodeDateTime(date: #{src})" + when AST::BytesPrimitiveType + "#{src}.base64EncodedString()" + when AST::VoidPrimitiveType + "nil" + when AST::OptionalType + "#{src} != nil ? #{type_to_json(t.base, src + "!")} : nil" + when AST::ArrayType + "#{src}.map({ return #{type_to_json t.base, "$0"} })" + when AST::StructType + "#{src}.toJSON()" + when AST::EnumType + "#{src}.rawValue" + when AST::TypeReference + type_to_json(t.type, src) + else + raise "Unknown type" + end + end +end diff --git a/src/target/swift_ios.cr b/src/target/swift_ios.cr new file mode 100644 index 0000000..48c2bc6 --- /dev/null +++ b/src/target/swift_ios.cr @@ -0,0 +1,316 @@ +require "./swift" + +class SwiftIosTarget < SwiftTarget + def gen + @io << <<-END +import Alamofire +import KeychainSwift + +protocol ApiCallsLogic: class {\n +END + + @ast.operations.each do |op| + args = op.args.map { |arg| "#{arg.name}: #{native_type arg.type}" } + if op.return_type.is_a? AST::VoidPrimitiveType + args << "callback: ((_ result: API.ApiInternal.Result) -> Void)?" + else + ret = op.return_type + args << "callback: ((_ result: API.ApiInternal.Result<#{native_type ret}>) -> Void)?" + end + + @io << ident "@discardableResult func #{op.pretty_name}(#{args.join(", ")}) -> DataRequest\n" + end + + @io << "}\n\n" # CloseAPICallsProtocol + @io << <<-END + +class API { + static var customUrl: String? + static var useStaging = false + static var globalCallback: (_ method: String, _ result: ApiInternal.Result, _ callback: ((ApiInternal.Result) -> Void)?) -> Void = { _, result, callback in + callback?(result) + } + + static var isEnabledAssertion = true + static var calls: ApiCallsLogic = Calls() + static var apiInternal = ApiInternal() + + static var decoder: JSONDecoder = { + let currentDecoder = JSONDecoder() + currentDecoder.dateDecodingStrategy = .custom({ (decoder) -> Date in + let container = try decoder.singleValueContainer() + let dateStr = try container.decode(String.self) + + var date: Date? = apiInternal.decodeDate(str: dateStr) ?? apiInternal.decodeDateTime(str: dateStr) + + guard let unwrapDate = date else { + throw DecodingError.dataCorruptedError(in: container, debugDescription: "Cannot decode date string \\(dateStr)") + } + + return unwrapDate + }) + + return currentDecoder + }() + + // MARK: Struct and Enums + struct NoReply: Codable {}\n\n +END + # ApiCalls + @io << ident(String.build do |io| + io << "public class Calls: ApiCallsLogic {\n" + @ast.operations.each do |op| + args = op.args.map { |arg| "#{arg.name}: #{native_type arg.type}" } + + if op.return_type.is_a? AST::VoidPrimitiveType + args << "callback: ((_ result: ApiInternal.Result) -> Void)?" + else + ret = op.return_type + args << "callback: ((_ result: ApiInternal.Result<#{native_type ret}>) -> Void)?" + end + io << ident(String.build do |io| + io << "@discardableResult public func #{op.pretty_name}(#{args.join(", ")}) -> DataRequest {\n" + io << ident(String.build do |io| + if op.args.size != 0 + io << "var args = [String: Any]()\n" + else + io << "let args = [String: Any]()\n" + end + op.args.each do |arg| + io << "args[\"#{arg.name}\"] = #{type_to_json arg.type, arg.name}\n" + end + io << "\n" + io << "return API.apiInternal.makeRequest(#{op.pretty_name.inspect}, args, callback: callback)\n" + end) # end of function body indentation. + io << "}" + end) # end of function indentation. + io << "\n\n" + end + io << "}\n" + end) # end of Calls indentation. + @io << "\n" + + @ast.struct_types.each do |t| + @io << ident generate_struct_type(t) + @io << "\n\n" + end + + @ast.enum_types.each do |t| + @io << ident generate_enum_type(t) + @io << "\n\n" + end + + @io << <<-END + + class ApiInternal { + var baseUrl = #{@ast.options.url.inspect} + + // MARK: ApiInternal Inner classes + enum Result { + case success(T) + case failure(Error) + } + + class Error: Codable { + var type: API.ErrorType + var message: String + + init(type: API.ErrorType, message: String) { + self.type = type + self.message = message + } + } + + class HttpResponse: Codable { + var ok: Bool + var deviceId: String? + var result: T? + var error: Error? + + required init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + ok = try values.decode(Bool.self, forKey: .ok) + deviceId = try? values.decode(String.self, forKey: .deviceId) + result = (try? values.decode(T.self, forKey: .result)) ?? (API.NoReply() as? T) + error = try? values.decode(Error.self, forKey: .error) + } + } + + // MARK: ApiInternal Date Handler + let dateAndTimeFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .gregorian) + formatter.timeZone = TimeZone(abbreviation: "UTC") + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS" + return formatter + }() + + let dateFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.calendar = Calendar(identifier: .gregorian) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.dateFormat = "yyyy-MM-dd" + return formatter + }() + + func decodeDate(str: String) -> Date? { + return dateFormatter.date(from: str) + } + + func encodeDate(date: Date) -> String { + return dateFormatter.string(from: date) + } + + func decodeDateTime(str: String) -> Date? { + return dateAndTimeFormatter.date(from: str) + } + + func encodeDateTime(date: Date) -> String { + return dateAndTimeFormatter.string(from: date) + } + + // MARK: ApiInternal Device Handler + func device() -> [String: Any] { + var device = [String: Any]() + device["platform"] = "ios" + device["fingerprint"] = phoneFingerprint() + device["platformVersion"] = "iOS " + UIDevice.current.systemVersion + " on " + UIDevice.current.model + if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { + device["version"] = version + } else { + device["version"] = "unknown" + } + + device["language"] = Locale.preferredLanguages[0] + device["timezone"] = TimeZone.current.identifier + if let currentId = deviceId { + device["id"] = currentId + } + + return device + } + + func randomBytesHex(len: Int) -> String { + var randomBytes = [UInt8](repeating: 0, count: len) + let _ = SecRandomCopyBytes(kSecRandomDefault, len, &randomBytes) + return randomBytes.map({String(format: "%02hhx", $0)}).joined(separator: "") + } + + var deviceId: String? { + return UserDefaults.standard.value(forKey: "device-id") as? String + } + + func saveDeviceId(_ id: String) { + UserDefaults.standard.setValue(id, forKey: "device-id") + UserDefaults.standard.synchronize() + } + + func phoneFingerprint() -> String { + let keychain = KeychainSwift() + guard let phoneFingerprint = keychain.get("phoneFingerprint") else { + let newPhoneFingerprint = randomBytesHex(len: 32) + keychain.set(newPhoneFingerprint, forKey: "phoneFingerprint", withAccess: .accessibleAlwaysThisDeviceOnly) + return newPhoneFingerprint + } + + return phoneFingerprint + } + + // MARK: ApiInternal request functions + func globalCallback(result: Result, method: String, callback: ((Result) -> Void)?) { + + //Convert result to result of type Any + let anyResult: Result + switch result { + case .success(let value): + anyResult = Result.success(value as Any?) + case .failure(let error): + anyResult = Result.failure(error) + } + + API.globalCallback(method, anyResult) { result in + //Convert result to result of type T. + let tResult: Result + switch result { + case .success(let value): + + if let tValue = value as? T { + tResult = Result.success(tValue) + } else { + tResult = Result.failure(Error(type: API.ErrorType.Serialization,message: "Erro ao mapear os dados, tente novamente mais tarde")) + } + + case .failure(let error): + tResult = Result.failure(error) + } + + callback?(tResult) + } + } + + @discardableResult + func makeRequest(_ name: String, _ args: [String: Any], callback: ((Result) -> Void)?) -> DataRequest { + let api = SessionManager.default + + let body = [ + "id": randomBytesHex(len: 8), + "device": device(), + "name": name, + "args": args, + "staging": API.useStaging + ] as [String : Any] + + let url = API.customUrl ?? "https://\\(baseUrl)\\(API.useStaging ? "-staging" : "")" + return api.request( + "\\(url)/\\(name)", + method: .post, + parameters: body, + encoding: JSONEncoding.default + ).responseData { [weak self] response in + guard let this = self else { return } + + let responseResult: Result + switch response.result { + case .success(let data): + + do { + let response = try API.decoder.decode(HttpResponse.self, from: data) + if let deviceId = response.deviceId { this.saveDeviceId(deviceId) } + if let result = response.result, response.ok { + responseResult = Result.success(result) + } else if let error = response.error { + responseResult = Result.failure(error) + } else { + responseResult = Result.failure(Error(type: API.ErrorType.Serialization, message: "Algo de errado está acontecendo em nosso servidor, tente novamente mais tarde")) + } + + } catch let error { + if API.isEnabledAssertion { assert(false, "Erro ao serializar dados, error: \\(error)") } + responseResult = Result.failure(Error(type: API.ErrorType.Serialization, message: "Erro ao carregar seus dados, tente novamente mais tarde")) + } + + case .failure: + responseResult = Result.failure(Error(type: API.ErrorType.Connection, message: "Erro de Conexão, tente novamente mais tarde")) + } + + this.globalCallback(result: responseResult, method: name, callback: callback) + } + } + } +} +protocol DisplayableValue: RawRepresentable { + var displayableValue: String { get } +} + +extension DisplayableValue where RawValue == String { + var displayableValue: String { + return self.rawValue + } +} + +END + end +end + +Target.register(SwiftIosTarget, target_name: "swift_ios") diff --git a/src/target/target.cr b/src/target/target.cr new file mode 100644 index 0000000..d4e051c --- /dev/null +++ b/src/target/target.cr @@ -0,0 +1,31 @@ +require "../ast" +require "../codegen_types/**" + +abstract class Target + @@targets = {} of String => Target.class + + def initialize(@output : String, @ast : AST::ApiDescription) + @io = IO::Memory.new + end + + def write + @io.rewind + File.write(@output, @io) + end + + abstract def gen + + def self.register(target, target_name) + @@targets[target_name] = target + end + + def self.process(ast, output, target_name) + target = @@targets[target_name]? + unless target + raise "Target '#{target_name}' is not supported" + end + t = target.new(output, ast) + t.gen + t.write + end +end diff --git a/src/target/typescript_nodeclient.cr b/src/target/typescript_nodeclient.cr new file mode 100644 index 0000000..367e586 --- /dev/null +++ b/src/target/typescript_nodeclient.cr @@ -0,0 +1,131 @@ +require "./target" + +class TypeScriptNodeClient < Target + def gen + @io << <<-END +import * as https from "https"; +import * as http from "http"; +import { randomBytes } from "crypto"; +import { URL } from "url"; + +END + + @ast.struct_types.each do |t| + @io << t.typescript_definition + @io << "\n\n" + end + + @ast.enum_types.each do |t| + @io << t.typescript_definition + @io << "\n\n" + end + + @io << <<-END +export class ApiClient { + deviceId: string | null = null; + fingerprint = randomBytes(8).toString("hex"); + + constructor(private baseUrl = #{("https://" + @ast.options.url).inspect}, private useStaging = false) {} + + +END + + @ast.operations.each do |op| + args = op.args.map { |arg| "#{arg.name}: #{arg.type.typescript_native_type}" } + @io << " async #{op.pretty_name}(#{args.join(", ")}): Promise<#{op.return_type.typescript_native_type}> {\n" + if op.args.size > 0 + @io << " const args = {\n" + op.args.each do |arg| + @io << " #{arg.name}: #{arg.type.typescript_encode(arg.name)}," + @io << "\n" + end + @io << " };\n" + end + + @io << " " + @io << "const ret = " unless op.return_type.is_a? AST::VoidPrimitiveType + @io << "await this.makeRequest({name: #{op.pretty_name.inspect}, #{op.args.size > 0 ? "args" : "args: {}"}});\n" + @io << " return " + op.return_type.typescript_decode("ret") + ";\n" unless op.return_type.is_a? AST::VoidPrimitiveType + @io << " }\n\n" + end + + @io << <<-END + private device() { + const device: any = { + type: "node", + fingerprint: this.fingerprint, + language: null, + screen: null, + platform: null, + version: null, + timezone: null + }; + if (this.deviceId) + device.id = this.deviceId; + return device; + } + + private async makeRequest({name, args}: {name: string, args: any}) { + const deviceData = this.device(); + const body = { + id: randomBytes(8).toString("hex"), + device: deviceData, + name: name, + args: args + }; + + const url = new URL(this.baseUrl + (this.useStaging ? "-staging" : "") + "/" + name); + const options = { + hostname: url.hostname, + path: url.pathname, + method: "POST", + port: url.port + }; + + return new Promise((resolve, reject) => { + const request = (url.protocol === "http:" ? http.request : https.request) + const req = request(options, resp => { + let data = ""; + resp.on("data", (chunk) => { + data += chunk; + }); + resp.on("end", () => { + try { + const response = JSON.parse(data); + + try { + this.deviceId = response.deviceId; + if (response.ok) { + resolve(response.result); + } else { + reject(response.error); + } + } catch (e) { + console.error(e); + reject({type: "Fatal", message: e.toString()}); + } + } catch (e) { + console.error(e); + reject({type: "BadFormattedResponse", message: `Response couldn't be parsed as JSON (${data}):\\n${e.toString()}`}); + } + }); + + }); + + req.on("error", (e) => { + console.error(`problem with request: ${e.message}`); + reject({type: "Fatal", message: e.toString()}); + }); + + // write data to request body + req.write(JSON.stringify(body)); + req.end(); + }); + } +} + +END + end +end + +Target.register(TypeScriptNodeClient, target_name: "typescript_nodeclient") diff --git a/src/target/typescript_nodeserver.cr b/src/target/typescript_nodeserver.cr new file mode 100644 index 0000000..5abd115 --- /dev/null +++ b/src/target/typescript_nodeserver.cr @@ -0,0 +1,561 @@ +require "json" +require "./target" + +class TypeScriptServerTarget < Target + def gen + if @ast.options.syntheticDefaultImports + @io << <<-END +import http from "http"; +import crypto from "crypto"; +import os from "os"; +import url from "url"; +import Raven from "raven"; + +END + else + @io << <<-END +import * as http from "http"; +import * as crypto from "crypto"; +import * as os from "os"; +import * as url from "url"; +const Raven = require("raven"); + +END + end + + unless @ast.options.useRethink + @io << <<-END + +interface DBDevice { + id: string + ip: string + type: "android" | "ios" | "web" + platform: any + fingerprint: string + screen: {width: number, height: number} + version: string + language: string + lastActiveAt?: Date + push?: string + timezone?: string | null +} + +interface DBApiCall { + id: string + name: string + args: any + executionId: string + running: boolean + device: DBDevice + date: Date + duration: number + host: string + ok: boolean + result: any + error: {type: string, message: string} | null +} + +END + end + + @io << <<-END + +let captureError: (e: Error, req?: http.IncomingMessage, extra?: any) => void = () => {}; +export function setCaptureErrorFn(fn: (e: Error, req?: http.IncomingMessage, extra?: any) => void) { + captureError = fn; +} + +let sentryUrl: string | null = null +export function setSentryUrl(url: string) { + sentryUrl = url; +} + +function typeCheckerError(e: Error, ctx: Context) { + #{@ast.options.strict ? "throw e;" : "setTimeout(() => captureError(e, ctx.req, ctx.call), 1000);"} +} + +function padNumber(value: number, length: number) { + return value.toString().padStart(length, "0"); +} + +function toDateTimeString(date: Date) { + return `${ + padNumber(date.getFullYear(), 4) + }-${ + padNumber(date.getMonth() + 1, 2) + }-${ + padNumber(date.getDate(), 2) + } ${ + padNumber(date.getHours(), 2) + }:${ + padNumber(date.getMinutes(), 2) + }:${ + padNumber(date.getSeconds(), 2) + }`; +} + +END + + @io << "export const cacheConfig: {\n" + @ast.operations.each do |op| + args = ["ctx: Context"] + op.args.map { |arg| "#{arg.name}: #{arg.type.typescript_native_type}" } + @io << " " << op.pretty_name << "?: (#{args.join(", ")}) => Promise<{key: any, expirationSeconds: number | null, version: number}>;\n" + end + @io << "} = {};\n\n" + + @ast.struct_types.each do |t| + @io << t.typescript_definition + @io << "\n\n" + end + + @ast.enum_types.each do |t| + @io << t.typescript_definition + @io << "\n\n" + end + + @io << "export const fn: {\n" + @ast.operations.each do |op| + args = ["ctx: Context"] + op.args.map { |arg| "#{arg.name}: #{arg.type.typescript_native_type}" } + @io << " " << op.pretty_name << ": (#{args.join(", ")}) => Promise<#{op.return_type.typescript_native_type}>;\n" + end + @io << "} = {\n" + @ast.operations.each do |op| + @io << " " << op.pretty_name << ": () => { throw \"not implemented\"; },\n" + end + @io << "};\n\n" + + @io << "const fnExec: {[name: string]: (ctx: Context, args: any) => Promise} = {\n" + @ast.operations.each do |op| + @io << " " << op.pretty_name << ": async (ctx: Context, args: any) => {\n" + op.args.each do |arg| + @io << ident ident arg.type.typescript_check_encoded("args.#{arg.name}", "\"#{op.pretty_name}.args.#{arg.name}\"") + @io << ident ident "const #{arg.name} = #{arg.type.typescript_decode("args.#{arg.name}")};" + @io << "\n" + end + @io << "\n" + @io << ident ident "let cacheKey: string | null = null, decodedKey: string | null = null, cacheExpirationSeconds: number | null = null, cacheVersion: number | null = null;\n" + @io << ident ident "if (cacheConfig.#{op.pretty_name}) {\n" + @io << ident ident ident "try {\n" + @io << ident ident ident ident "const {key, expirationSeconds, version} = await cacheConfig.#{op.pretty_name}(#{(["ctx"] + op.args.map(&.name)).join(", ")});\n" + @io << ident ident ident ident "if (!key) throw \"\";\n" + @io << ident ident ident ident "cacheKey = crypto.createHash(\"sha256\").update(JSON.stringify(key)+ \"-#{op.pretty_name}\").digest(\"hex\").substr(0, 100); decodedKey = JSON.stringify(key); cacheExpirationSeconds = expirationSeconds; cacheVersion = version;\n" + @io << ident ident ident ident "const cache = await hook.getCache(cacheKey, version);console.log(JSON.stringify(cache));\n" + @io << ident ident ident ident "if (cache && (!cache.expirationDate || cache.expirationDate > new Date())) return cache.ret;\n" + @io << ident ident ident "} catch(e) {console.log(JSON.stringify(e));}\n" + @io << ident ident "}\n" + @io << ident ident "const ret = await fn.#{op.pretty_name}(#{(["ctx"] + op.args.map(&.name)).join(", ")});\n" + @io << ident ident op.return_type.typescript_check_decoded("ret", "\"#{op.pretty_name}.ret\"") + @io << ident ident "const encodedRet = " + op.return_type.typescript_encode("ret") + ";\n" + @io << ident ident "if (cacheKey !== null && cacheVersion !== null) hook.setCache(cacheKey, cacheExpirationSeconds ? new Date(new Date().getTime() + (cacheExpirationSeconds * 1000)) : null, cacheVersion, decodedKey!, \"#{op.pretty_name}\", encodedRet);\n" + @io << ident ident "return encodedRet" + @io << ident "},\n" + end + @io << "};\n\n" + + @io << "const clearForLogging: {[name: string]: (call: DBApiCall) => void} = {\n" + @ast.operations.each do |op| + cmds_args = String.build { |io| emit_clear_for_logging(io, op, "call.args") } + + if cmds_args != "" + @io << " " << op.pretty_name << ": async (call: DBApiCall) => {\n" + @io << ident ident cmds_args + @io << ident "},\n" + end + end + @io << "};\n\n" + + @ast.struct_types.each do |t| + @io << "export function transform#{t.name}ToJson(x: #{t.typescript_native_type}) {\n" + @io << ident "return " + t.typescript_encode("x") + ";\n" + @io << "}\n\n" + end + + @ast.struct_types.each do |t| + @io << "export function transformJsonTo#{t.name}(x: string) {\n" + @io << ident "const y = JSON.parse(x);\n" + @io << ident "return " + t.typescript_decode("y") + ";\n" + @io << "}\n\n" + end + + @ast.errors.each do |error| + @io << "export class #{error} extends Error {\n" + @io << ident "_type = #{error.inspect};\n" + @io << ident "constructor(public _msg: string) {\n" + @io << ident ident "super(_msg ? \"#{error}: \" + _msg : #{error.inspect});\n" + @io << ident "}\n" + @io << "}\n\n" + end + + @io << "export const err = {\n" + @ast.errors.each do |error| + @io << ident "#{error}: (message: string = \"\") => { throw new #{error}(message); },\n" + end + @io << "};\n\n" + + @io << <<-END +////////////////////////////////////////////////////// + +const httpHandlers: { + [signature: string]: (body: string, res: http.ServerResponse, req: http.IncomingMessage) => void +} = {} + +export function handleHttp(method: "GET" | "POST" | "PUT" | "DELETE", path: string, func: (body: string, res: http.ServerResponse, req: http.IncomingMessage) => void) { + httpHandlers[method + path] = func; +} + +export function handleHttpPrefix(method: "GET" | "POST" | "PUT" | "DELETE", path: string, func: (body: string, res: http.ServerResponse, req: http.IncomingMessage) => void) { + httpHandlers["prefix " + method + path] = func; +} + +export interface Context { + call: DBApiCall; + device: DBDevice; + req: http.IncomingMessage; + startTime: Date; + staging: boolean; +} + +function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export let server: http.Server; + +export const hook: { + onHealthCheck: () => Promise + onDevice: (id: string, deviceInfo: any) => Promise + onReceiveCall: (call: DBApiCall) => Promise + afterProcessCall: (call: DBApiCall) => Promise + setCache: (cacheKey: string, expirationDate: Date | null, version: number, decodedKey: string, fnName: string, ret: any) => Promise + getCache: (cacheKey: string, version: number) => Promise<{expirationDate: Date | null, ret: any} | null> +} = { + onHealthCheck: async () => true, + onDevice: async () => {}, + onReceiveCall: async () => {}, + afterProcessCall: async () => {}, + setCache: async () => {}, + getCache: async () => null +}; + +export function start(port: number = 8000) { + if (server) return; + server = http.createServer((req, res) => { + req.on("error", (err) => { + console.error(err); + }); + + res.on("error", (err) => { + console.error(err); + }); + + res.setHeader("Access-Control-Allow-Origin", "*"); + res.setHeader("Access-Control-Allow-Methods", "PUT, POST, GET, OPTIONS"); + res.setHeader("Access-Control-Allow-Headers", "Content-Type"); + res.setHeader("Access-Control-Max-Age", "86400"); + res.setHeader("Content-Type", "application/json"); + + let body = ""; + req.on("data", (chunk: any) => body += chunk.toString()); + req.on("end", () => { + if (req.method === "OPTIONS") { + res.writeHead(200); + res.end(); + return; + } + const ip = req.headers["x-real-ip"] as string || (req.headers["x-fowarded-for"] as string || ""); + const signature = req.method! + url.parse(req.url || "").pathname; + if (httpHandlers[signature]) { + console.log(`${toDateTimeString(new Date())} http ${signature}`); + httpHandlers[signature](body, res, req); + return; + } + for (let target in httpHandlers) { + if (("prefix " + signature).startsWith(target)) { + console.log(`${toDateTimeString(new Date())} http ${target}`); + httpHandlers[target](body, res, req); + return; + } + } + + switch (req.method) { + case "HEAD": { + res.writeHead(200); + res.end(); + break; + } + case "GET": { + hook.onHealthCheck().then(ok => { + res.writeHead(ok ? 200 : 500); + res.write(JSON.stringify({ok})); + res.end(); + }, error => { + console.error(error); + res.writeHead(500); + res.write(JSON.stringify({ok: false})); + res.end(); + }); + break; + } + case "POST": { + (async () => { + const request = JSON.parse(body); + request.device.ip = ip; + request.device.lastActiveAt = new Date(); + const context: Context = { + call: null as any, + req: req, + device: request.device, + startTime: new Date, + staging: request.staging || false + }; + const startTime = process.hrtime(); + + const {id, ...deviceInfo} = context.device; + + if (!context.device.id) + context.device.id = crypto.randomBytes(20).toString("hex"); + + await hook.onDevice(context.device.id, deviceInfo); + + const executionId = crypto.randomBytes(20).toString("hex"); + + let call: DBApiCall = { + id: `${request.id}-${context.device.id}`, + name: request.name, + args: JSON.parse(JSON.stringify(request.args)), + executionId: executionId, + running: true, + device: context.device, + date: context.startTime, + duration: 0, + host: os.hostname(), + ok: true, + result: null as any, + error: null as {type: string, message: string}|null + }; + + context.call = call; + + if (clearForLogging[call.name]) + clearForLogging[call.name](call); + + try { + call = await hook.onReceiveCall(call) || call; + } catch (e) { + call.ok = false; + call.error = { + type: "Fatal", + message: e.toString() + }; + call.running = false; + } + + if (call.running) { + try { + const func = fnExec[request.name]; + if (func) { + call.result = await func(context, request.args); + } else { + console.error(JSON.stringify(Object.keys(fnExec))); + throw "Function does not exist: " + request.name; + } + } catch (err) { + console.error(err); + call.ok = false; + if (#{@ast.errors.to_json}.includes(err._type)) { + call.error = { + type: err._type, + message: err._msg + }; + } else { + call.error = { + type: "Fatal", + message: err.toString() + }; + setTimeout(() => captureError(err, req, { + call + }), 1); + } + } + call.running = false; + const deltaTime = process.hrtime(startTime); + call.duration = deltaTime[0] + deltaTime[1] * 1e-9; + + await hook.afterProcessCall(call); + } + + const response = { + id: call.id, + ok: call.ok, + executed: call.executionId === executionId, + deviceId: call.device.id, + startTime: call.date, + duration: call.duration, + host: call.host, + result: call.result, + error: call.error + }; + + // res.writeHead(!response.error ? 200 : response.error.type === "Fatal" ? 500 : 400); + res.writeHead(200); + res.write(JSON.stringify(response)); + res.end(); + + console.log( + `${toDateTimeString(new Date())} ` + + `${call.id} [${call.duration.toFixed(6)}s] ` + + `${call.name}() -> ${call.ok ? "OK" : call.error ? call.error.type : "???"}` + ); + })().catch(err => { + console.error(err); + if (!res.headersSent) + res.writeHead(500); + res.end(); + }); + break; + } + default: { + res.writeHead(500); + res.end(); + } + } + }); + }); + + if ((server as any).keepAliveTimeout) + (server as any).keepAliveTimeout = 0; + + if (!process.env.TEST && !process.env.DEBUGGING && sentryUrl) { + Raven.config(sentryUrl).install(); + captureError = (e, req, extra) => Raven.captureException(e, { + req, + extra, + fingerprint: [(e.message || e.toString()).replace(/[0-9]+/g, "X").replace(/"[^"]*"/g, "X")] + }); + } + + if (process.env.DEBUGGING && !process.env.NOLOCALTUNNEL) { + port = (Math.random() * 50000 + 10000) | 0; + } + + if (!process.env.TEST) { + server.listen(port, () => { + const addr = server.address(); + const addrString = typeof addr === "string" ? addr : `${addr.address}:${addr.port}`; + console.log(`Listening on ${addrString}`); + }); + } + + if (process.env.DEBUGGING && !process.env.NOLOCALTUNNEL) { + const subdomain = require("crypto").createHash("sha256").update(process.argv[1]).digest("hex").substr(0, 8); + require("localtunnel")(port, {subdomain}, (err: Error | null, tunnel: any) => { + if (err) throw err; + console.log("Tunnel URL:", tunnel.url); + }); + } +} + +fn.ping = async (ctx: Context) => "pong"; +END + + if @ast.options.useRethink + @io << <<-END +fn.setPushToken = async (ctx: Context, token: string) => { + await r.table("devices").get(ctx.device.id).update({push: token}); +}; +END + end + + if @ast.options.useRethink + @io << <<-END + + +import r from "../rethinkdb"; + +hook.onHealthCheck = async () => { + return await r.expr(true); +}; + +hook.onDevice = async (id, deviceInfo) => { + if (await r.table("devices").get(id).eq(null)) { + await r.table("devices").insert({ + id: id, + date: r.now(), + ...deviceInfo + }); + } else { + r.table("devices").get(id).update(deviceInfo).run(); + } +}; + +hook.onReceiveCall = async (call) => { + for (let i = 0; i < 1500; ++i) { + const priorCall = await r.table("api_calls").get(call.id); + if (priorCall === null) { + const res = await r.table("api_calls").insert(call); + if (res.inserted > 0) + return; + else + continue; + } + if (!priorCall.running) { + return priorCall; + } + if (priorCall.executionId === call.executionId) { + return; + } + + await sleep(100); + } + + throw "CallExecutionTimeout: Timeout while waiting for execution somewhere else (is the original container that received this request dead?)"; +}; + +hook.afterProcessCall = async (call) => { + r.table("api_calls").get(call.id).update(call).run(); +}; + +END + end + end + + @i = 0 + + def emit_clear_for_logging(io : IO, t : AST::Type | AST::Operation | AST::Field, path : String) + case t + when AST::Operation + t.args.each do |field| + emit_clear_for_logging(io, field, "#{path}.#{field.name}") + end + when AST::StructType + t.fields.each do |field| + emit_clear_for_logging(io, field, "#{path}.#{field.name}") + end + when AST::Field + if t.secret + io << "#{path} = \"\";\n" + else + emit_clear_for_logging(io, t.type, path) + end + when AST::TypeReference + emit_clear_for_logging(io, t.type, path) + when AST::OptionalType + cmd = String.build { |io| emit_clear_for_logging(io, t.base, path) } + if cmd != "" + io << "if (#{path}) {\n" << ident(cmd) << "}\n" + end + when AST::ArrayType + var = ('i' + @i).to_s + @i += 1 + cmd = String.build { |io| emit_clear_for_logging(io, t.base, "#{path}[#{var}]") } + @i -= 1 + if cmd != "" + io << "for (let #{var} = 0; #{var} < #{path}.length; ++#{var}) {\n" << ident(cmd) << "}\n" + end + when AST::BytesPrimitiveType + io << "#{path} = `<${#{path}.length} bytes>`;\n" + end + end +end + +Target.register(TypeScriptServerTarget, target_name: "typescript_nodeserver") diff --git a/src/target/typescript_servertest.cr b/src/target/typescript_servertest.cr new file mode 100644 index 0000000..be28535 --- /dev/null +++ b/src/target/typescript_servertest.cr @@ -0,0 +1,90 @@ +require "./target" + +class TypeScriptServerTestTarget < Target + def gen + if @ast.options.syntheticDefaultImports + @io << <<-END +import http from "http"; + +END + else + @io << <<-END +import * as http from "http"; + +END + end + + @io << <<-END +export interface Context { + call: DBApiCall; + device: DBDevice; + req: http.IncomingMessage; + startTime: Date; + staging: boolean; +} + +/* istanbul ignore next */ +expect.extend({ + toBeTypeOf(received, argument) { + const type = Array.isArray(received) ? "array" : typeof received; + return { + message: () => `expected ${received} to be type ${argument}`, + pass: type === argument + }; + } +}); + +declare global { + namespace jest { + interface Matchers { + toBeTypeOf(type: string): void + } + } +} + + +END + @io << "type ApiFn = {\n" + @ast.operations.each do |op| + args = ["ctx: Context"] + op.args.map { |arg| "#{arg.name}: #{arg.type.typescript_native_type}" } + @io << " " << op.pretty_name << ": (#{args.join(", ")}) => Promise<#{op.return_type.typescript_native_type}>;\n" + end + @io << "};\n\n" + @ast.struct_types.each do |t| + @io << t.typescript_definition + @io << "\n\n" + end + + @ast.enum_types.each do |t| + @io << t.typescript_definition + @io << "\n\n" + end + + @io << <<-END +export function wrapApiFn(fn: ApiFn) { + return {\n +END + @ast.operations.each do |op| + args = ["ctx: Context"] + op.args.map { |arg| "#{arg.name}: #{arg.type.typescript_native_type}" } + @io << " " << op.pretty_name << ": async (#{args.join(", ")}) => {\n" + op.args.each do |arg| + @io << ident ident ident arg.type.typescript_expect(arg.name) + end + @io << " " + @io << "const ret = " unless op.return_type.is_a? AST::VoidPrimitiveType + @io << "await fn.#{op.pretty_name}(#{(["ctx"] + op.args.map(&.name)).join(", ")});\n" + unless op.return_type.is_a? AST::VoidPrimitiveType + @io << ident ident ident op.return_type.typescript_expect("ret") + @io << " return ret;\n" + end + @io << " },\n" + end + @io << <<-END + }; +} + +END + end +end + +Target.register(TypeScriptServerTestTarget, target_name: "typescript_servertest") diff --git a/src/target/typescript_web.cr b/src/target/typescript_web.cr new file mode 100644 index 0000000..3c53abe --- /dev/null +++ b/src/target/typescript_web.cr @@ -0,0 +1,183 @@ +require "./target" + +class TypeScriptWebTarget < Target + def gen + @io << <<-END +import {UAParser} from "ua-parser-js"; + +let baseUrl = #{@ast.options.url.inspect}; +let useStaging = false; +let msgNotConnect = "Not connected or server not found"; + +export function setMsgNotConnect(msg: string) { + msgNotConnect = msg; +} + +export function setStaging(use: boolean) { + useStaging = !!use; +} + +export function setBaseUrl(url: string) { + baseUrl = url; +} + +END + + @ast.struct_types.each do |t| + @io << t.typescript_definition + @io << "\n\n" + end + + @ast.enum_types.each do |t| + @io << t.typescript_definition + @io << "\n\n" + end + + @ast.operations.each do |op| + args = op.args.map { |arg| "#{arg.name}: #{arg.type.typescript_native_type}" } + @io << "export async function #{op.pretty_name}(#{args.join(", ")}): Promise<#{op.return_type.typescript_native_type}> {\n" + if op.args.size > 0 + @io << " const args = {\n" + op.args.each do |arg| + @io << ident ident "#{arg.name}: #{arg.type.typescript_encode(arg.name)}," + @io << "\n" + end + @io << " };\n" + end + + @io << " " + @io << "const ret = " unless op.return_type.is_a? AST::VoidPrimitiveType + @io << "await makeRequest({name: #{op.pretty_name.inspect}, #{op.args.size > 0 ? "args" : "args: {}"}});\n" + @io << ident "return " + op.return_type.typescript_decode("ret") + ";" + @io << "\n" + @io << "}\n\n" + end + + @io << <<-END +////////////////////////////////////////////////////// + +let fallbackDeviceId: string | null = null; + +function setDeviceId(deviceId: string) { + fallbackDeviceId = deviceId; + try { + localStorage.setItem("deviceId", deviceId); + } catch (e) {} +} + +function getDeviceId() { + try { + return localStorage.getItem("deviceId"); + } catch (e) {} + return fallbackDeviceId; +} + +async function device() { + const parser = new UAParser(); + parser.setUA(navigator.userAgent); + const agent = parser.getResult(); + const me = document.currentScript as HTMLScriptElement; + const device: any = { + type: "web", + platform: { + browser: agent.browser.name, + browserVersion: agent.browser.version, + os: agent.os.name, + osVersion: agent.os.version + }, + screen: { + width: screen.width, + height: screen.height + }, + version: me ? me.src : "", + language: navigator.language, + timezone: typeof Intl === "object" ? Intl.DateTimeFormat().resolvedOptions().timeZone : null + }; + const deviceId = getDeviceId(); + if (deviceId) + device.id = deviceId; + return device; +} + +function randomBytesHex(len: number) { + let hex = ""; + for (let i = 0; i < 2 * len; ++i) + hex += "0123456789abcdef"[Math.floor(Math.random()*16)]; + return hex; +} + +export interface ListenerTypes { + fail: (e: Error, name: string, args: any) => void; + fatal: (e: Error, name: string, args: any) => void; + success: (res: string, name: string, args: any) => void; +} + +type HookArray = Array; +export type Listenables = keyof ListenerTypes; +export type ListenersDict = { [k in Listenables]: Array }; + +const listenersDict: ListenersDict = { + fail: [], + fatal: [], + success: [], +}; + +export function addEventListener(listenable: Listenables, hook: ListenerTypes[typeof listenable]) { + const listeners: HookArray = listenersDict[listenable]; + listeners.push(hook); +} + +export function removeEventListener(listenable: Listenables, hook: ListenerTypes[typeof listenable]) { + const listeners: HookArray = listenersDict[listenable]; + listenersDict[listenable] = listeners.filter(h => h !== hook) as any; +} + +async function makeRequest({name, args}: {name: string, args: any}) { + const deviceData = await device(); + return new Promise((resolve, reject) => { + const req = new XMLHttpRequest(); + req.open("POST", (baseUrl.startsWith("http") ? "" : "https://") + baseUrl + (useStaging ? "-staging" : "") + "/" + name); + const body = { + id: randomBytesHex(8), + device: deviceData, + name: name, + args: args + }; + req.onreadystatechange = () => { + if (req.readyState !== 4) return; + try { + const response = JSON.parse(req.responseText); + + try { + setDeviceId(response.deviceId); + if (response.ok) { + resolve(response.result); + listenersDict["success"].forEach(hook => hook(response.result, name, args)); + } else { + reject(response.error); + listenersDict["fail"].forEach(hook => hook(response.error, name, args)); + } + } catch (e) { + console.error(e); + reject({type: "Fatal", message: e.toString()}); + listenersDict["fatal"].forEach(hook => hook(e, name, args)); + } + } catch (e) { + console.error(e); + if (!req.responseText) { + reject({type: "NotConnect", message: msgNotConnect}); + } else { + reject({type: "BadFormattedResponse", message: `Response couldn't be parsed as JSON (${req.responseText}):\\n${e.toString()}`}); + } + listenersDict["fatal"].forEach(hook => hook(e, name, args)); + } + }; + req.send(JSON.stringify(body)); + }); +} + +END + end +end + +Target.register(TypeScriptWebTarget, target_name: "typescript_web") diff --git a/src/utils.cr b/src/utils.cr new file mode 100644 index 0000000..5f14996 --- /dev/null +++ b/src/utils.cr @@ -0,0 +1,16 @@ +module RandomGen + @@gen = Random::PCG32.new(17u64) + + def self.random_u + @@gen.next_u + end +end + +def random_var + "x#{RandomGen.random_u}" +end + +def ident(code) + return "" if code.strip == "" + code.split("\n").map { |line| " " + line }.join("\n").gsub(/\n\s+$/m, "\n") +end diff --git a/target-android/.idea/misc.xml b/target-android/.idea/misc.xml index fbb6828..ba7052b 100644 --- a/target-android/.idea/misc.xml +++ b/target-android/.idea/misc.xml @@ -1,8 +1,5 @@ - - - - - - - - - - - - - - + diff --git a/target-android/.project b/target-android/.project new file mode 100644 index 0000000..0747141 --- /dev/null +++ b/target-android/.project @@ -0,0 +1,17 @@ + + + target-android + Project target-android created by Buildship. + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/target-android/.settings/org.eclipse.buildship.core.prefs b/target-android/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..b888301 --- /dev/null +++ b/target-android/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +#Wed Dec 13 10:40:10 BRT 2017 +connection.project.dir= diff --git a/target-android/Dockerfile b/target-android/Dockerfile index 7bdd3c3..d76d321 100644 --- a/target-android/Dockerfile +++ b/target-android/Dockerfile @@ -1,5 +1,6 @@ FROM registry.cubos.io/cubos/android-builder WORKDIR /root ADD . /root/ +RUN ./gradlew assembleDebug ADD build_and_publish.sh / RUN chmod +x /build_and_publish.sh \ No newline at end of file diff --git a/target-android/api/.classpath b/target-android/api/.classpath new file mode 100644 index 0000000..8d8d85f --- /dev/null +++ b/target-android/api/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/target-android/api/.project b/target-android/api/.project new file mode 100644 index 0000000..f90ee76 --- /dev/null +++ b/target-android/api/.project @@ -0,0 +1,23 @@ + + + api + Project api created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/target-android/api/.settings/org.eclipse.buildship.core.prefs b/target-android/api/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 0000000..963d2e5 --- /dev/null +++ b/target-android/api/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +#Wed Dec 13 10:40:10 BRT 2017 +connection.project.dir=.. diff --git a/target-android/api/build.gradle b/target-android/api/build.gradle index 5f6c335..7493518 100644 --- a/target-android/api/build.gradle +++ b/target-android/api/build.gradle @@ -2,12 +2,12 @@ apply plugin: 'com.android.library' apply plugin: 'maven-publish' android { - compileSdkVersion 25 - buildToolsVersion "25.0.2" + compileSdkVersion 26 + buildToolsVersion "26.0.2" defaultConfig { minSdkVersion 16 - targetSdkVersion 25 + targetSdkVersion 26 versionCode 1 versionName "1.0" } @@ -22,13 +22,19 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.squareup.okhttp3:okhttp:3.6.0' + compile 'com.squareup.okhttp3:okhttp:3.8.0' + compile 'com.anupcowkur:reservoir:3.1.0' } publishing { repositories { maven { - url 'https://mymavenrepo.com/repo/4XBKk8BMCyMc24joS8Od/' +// url 'https://mymavenrepo.com/repo/4XBKk8BMCyMc24joS8Od/' + url 'https://maven.cubos.io/upload/' + credentials { + username "cubos" + password "4XBKk8BMCyMc24joS8Od" + } } } @@ -39,6 +45,35 @@ publishing { artifactId 'sdkgen2' version '1.0.10' artifact 'build/outputs/aar/api-release.aar' + + pom.withXml { + // Creating additional node for dependencies + def dependenciesNode = asNode().appendNode('dependencies') + + // Defining configuration names from which dependencies will be taken (debugCompile or releaseCompile and compile) + def configurationNames = ["releaseCompile", 'compile'] + + configurationNames.each { configurationName -> + configurations[configurationName].allDependencies.each { + if (it.group != null && it.name != null) { + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', it.group) + dependencyNode.appendNode('artifactId', it.name) + dependencyNode.appendNode('version', it.version) + + // If there are any exclusions in dependency + if (it.excludeRules.size() > 0) { + def exclusionsNode = dependencyNode.appendNode('exclusions') + it.excludeRules.each { rule -> + def exclusionNode = exclusionsNode.appendNode('exclusion') + exclusionNode.appendNode('groupId', rule.group) + exclusionNode.appendNode('artifactId', rule.module) + } + } + } + } + } + } } } } diff --git a/target-android/build.gradle b/target-android/build.gradle index 1ea4bd0..127bad0 100644 --- a/target-android/build.gradle +++ b/target-android/build.gradle @@ -3,9 +3,11 @@ buildscript { repositories { jcenter() + mavenCentral() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' + classpath 'com.android.tools.build:gradle:3.0.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -15,6 +17,8 @@ buildscript { allprojects { repositories { jcenter() + mavenCentral() + google() } } diff --git a/target-android/build_and_publish.sh b/target-android/build_and_publish.sh index 4b6e488..c3fecf2 100644 --- a/target-android/build_and_publish.sh +++ b/target-android/build_and_publish.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -NAME=$(basename `git rev-parse --show-toplevel` | sed -e 's/-api//g') -VERSION=1.0.$2 +NAME=$2 +VERSION=1.0.$3 LOCATION=/root rm -rf $LOCATION/api/src/main/java/io/cubos/api diff --git a/target-android/build_and_publish2.sh b/target-android/build_and_publish2.sh new file mode 100644 index 0000000..1f33fe9 --- /dev/null +++ b/target-android/build_and_publish2.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +NAME=$2 +VERSION=$3 +LOCATION=/root + +rm -rf $LOCATION/api/src/main/java/io/cubos/api +mkdir -p $LOCATION/api/src/main/java/io/cubos/api/$NAME +cp $1 $LOCATION/tmp.java +cd $LOCATION + +(echo "package io.cubos.api."$NAME";"; cat tmp.java) > api/src/main/java/io/cubos/api/$NAME/API.java + +CURRENT_NAME=$(cat api/src/main/res/values/strings.xml | grep app_name | sed -e 's/<[^>]*>//g' | xargs echo -n) +CURRENT_VERSION=$(cat api/build.gradle | grep "version " | sed 's/version//g' | xargs echo -n) +sed -i -e 's/'$CURRENT_NAME'/'$NAME'/g' api/src/main/java/io/cubos/api/$NAME/API.java +sed -i -e 's/'$CURRENT_NAME'/'$NAME'/g' api/src/main/AndroidManifest.xml +sed -i -e 's/'$CURRENT_NAME'/'$NAME'/g' api/src/main/res/values/strings.xml +sed -i -e 's/'$CURRENT_NAME'/'$NAME'/g' api/build.gradle +sed -i -e 's/version '"'"$CURRENT_VERSION"'"'/version '"'"$VERSION"'"'/g' api/build.gradle + +./gradlew assembleRelease publish diff --git a/target-android/gradle/wrapper/gradle-wrapper.properties b/target-android/gradle/wrapper/gradle-wrapper.properties index 04f6041..3a75351 100644 --- a/target-android/gradle/wrapper/gradle-wrapper.properties +++ b/target-android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip diff --git a/target-node/.dockerignore b/target-node/.dockerignore new file mode 100644 index 0000000..868bd46 --- /dev/null +++ b/target-node/.dockerignore @@ -0,0 +1,4 @@ +node_modules +api.js +api.js.map +api.d.ts \ No newline at end of file diff --git a/target-node/.gitignore b/target-node/.gitignore new file mode 100644 index 0000000..868bd46 --- /dev/null +++ b/target-node/.gitignore @@ -0,0 +1,4 @@ +node_modules +api.js +api.js.map +api.d.ts \ No newline at end of file diff --git a/target-node/.npmignore b/target-node/.npmignore new file mode 100644 index 0000000..e69de29 diff --git a/target-node/.yarnrc b/target-node/.yarnrc new file mode 100644 index 0000000..d51da20 --- /dev/null +++ b/target-node/.yarnrc @@ -0,0 +1,3 @@ +registry "https://npm.cubos.io/" +email tech@cubos.io +username cubos diff --git a/target-node/Dockerfile b/target-node/Dockerfile new file mode 100644 index 0000000..f13e6a8 --- /dev/null +++ b/target-node/Dockerfile @@ -0,0 +1,8 @@ +FROM node +WORKDIR /root/stage/ +ADD package.json /root/stage/ +ADD yarn.lock /root/stage/ +RUN yarn install +ADD . /root/stage/ +ADD build_and_publish.sh / +RUN chmod +x /build_and_publish.sh diff --git a/target-node/api.ts b/target-node/api.ts new file mode 100644 index 0000000..e69de29 diff --git a/target-node/build_and_publish.sh b/target-node/build_and_publish.sh new file mode 100644 index 0000000..17752f1 --- /dev/null +++ b/target-node/build_and_publish.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +NAME=$2 +VERSION=1.0.$3 + +cp $1 /root/stage/api.ts +cd /root/stage + +export PATH=$(npm bin):$PATH +json -I -f package.json -e 'this.name="@cubos/'$NAME'"' +npm version $VERSION || true +tsc +babel api.js -o api.js +npm publish diff --git a/target-node/build_and_publish2.sh b/target-node/build_and_publish2.sh new file mode 100644 index 0000000..484b5cf --- /dev/null +++ b/target-node/build_and_publish2.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +NAME=$2 +VERSION=$3 + +cp $1 /root/stage/api.ts +cd /root/stage + +export PATH=$(npm bin):$PATH +json -I -f package.json -e 'this.name="@cubos/'$NAME'"' +npm version $VERSION || true +tsc +babel api.js -o api.js +npm publish diff --git a/target-node/package.json b/target-node/package.json new file mode 100644 index 0000000..03e153a --- /dev/null +++ b/target-node/package.json @@ -0,0 +1,30 @@ +{ + "name": "@cubos/api", + "version": "0.0", + "dependencies": { + "@types/node": "^8.5.2", + "@types/raven": "^2.1.3", + "babel-runtime": "^6.26.0", + "localtunnel": "^1.8.3", + "raven": "^2.3.0", + "request": "^2.83.0" + }, + "main": "api.js", + "types": "api.d.ts", + "license": "UNLICENSED", + "babel": { + "plugins": [ + "transform-runtime" + ], + "presets": [ + "babel-preset-env" + ] + }, + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-preset-env": "^1.6.1", + "json": "^9.0.4", + "typescript": "^2.6.2" + } +} diff --git a/target-node/tsconfig.json b/target-node/tsconfig.json new file mode 100644 index 0000000..5a881b0 --- /dev/null +++ b/target-node/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "declaration": true, + "alwaysStrict": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "noImplicitAny": true, + "noImplicitThis": true, + "sourceMap": true, + "target": "ES2017", + "strictNullChecks": true, + "module": "commonjs", + "lib": ["es2017"] + }, + "include": [ + "*.ts" + ], + "exclude": [] +} diff --git a/target-node/yarn.lock b/target-node/yarn.lock new file mode 100644 index 0000000..934ffa0 --- /dev/null +++ b/target-node/yarn.lock @@ -0,0 +1,1866 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/events@*": + version "1.1.0" + resolved "https://npm.cubos.io/@types%2fevents/-/events-1.1.0.tgz#93b1be91f63c184450385272c47b6496fd028e02" + +"@types/node@*", "@types/node@^8.5.2": + version "8.5.2" + resolved "https://npm.cubos.io/@types%2fnode/-/node-8.5.2.tgz#83b8103fa9a2c2e83d78f701a9aa7c9539739aa5" + +"@types/raven@^2.1.3": + version "2.1.3" + resolved "https://npm.cubos.io/@types%2fraven/-/raven-2.1.3.tgz#b241ccb42aba90d8b8863741dad29e01508bd37c" + dependencies: + "@types/events" "*" + "@types/node" "*" + +abbrev@1: + version "1.1.1" + resolved "https://npm.cubos.io/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +ajv@^4.9.1: + version "4.11.8" + resolved "https://npm.cubos.io/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ajv@^5.1.0: + version "5.5.2" + resolved "https://npm.cubos.io/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://npm.cubos.io/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://npm.cubos.io/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +anymatch@^1.3.0: + version "1.3.2" + resolved "https://npm.cubos.io/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + dependencies: + micromatch "^2.1.5" + normalize-path "^2.0.0" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://npm.cubos.io/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +are-we-there-yet@~1.1.2: + version "1.1.4" + resolved "https://npm.cubos.io/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.1.0" + resolved "https://npm.cubos.io/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://npm.cubos.io/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://npm.cubos.io/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://npm.cubos.io/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://npm.cubos.io/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://npm.cubos.io/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://npm.cubos.io/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://npm.cubos.io/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.2.1, aws4@^1.6.0: + version "1.6.0" + resolved "https://npm.cubos.io/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +babel-cli@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-cli/-/babel-cli-6.26.0.tgz#502ab54874d7db88ad00b887a06383ce03d002f1" + dependencies: + babel-core "^6.26.0" + babel-polyfill "^6.26.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + commander "^2.11.0" + convert-source-map "^1.5.0" + fs-readdir-recursive "^1.0.0" + glob "^7.1.2" + lodash "^4.17.4" + output-file-sync "^1.1.2" + path-is-absolute "^1.0.1" + slash "^1.0.0" + source-map "^0.5.6" + v8flags "^2.1.1" + optionalDependencies: + chokidar "^1.6.1" + +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8" + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.0" + debug "^2.6.8" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.7" + slash "^1.0.0" + source-map "^0.5.6" + +babel-generator@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.6" + trim-right "^1.0.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://npm.cubos.io/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.26.0" + resolved "https://npm.cubos.io/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://npm.cubos.io/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://npm.cubos.io/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://npm.cubos.io/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://npm.cubos.io/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://npm.cubos.io/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-async-to-generator@^6.22.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.26.0" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-runtime@^6.23.0: + version "6.23.0" + resolved "https://npm.cubos.io/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz#88490d446502ea9b8e7efb0fe09ec4d99479b1ee" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-polyfill@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" + dependencies: + babel-runtime "^6.26.0" + core-js "^2.5.0" + regenerator-runtime "^0.10.5" + +babel-preset-env@^1.6.1: + version "1.6.1" + resolved "https://npm.cubos.io/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48" + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^2.1.2" + invariant "^2.2.2" + semver "^5.3.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://npm.cubos.io/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://npm.cubos.io/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://npm.cubos.io/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +block-stream@*: + version "0.0.9" + resolved "https://npm.cubos.io/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +boom@2.x.x: + version "2.10.1" + resolved "https://npm.cubos.io/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +boom@4.x.x: + version "4.3.1" + resolved "https://npm.cubos.io/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" + dependencies: + hoek "4.x.x" + +boom@5.x.x: + version "5.2.0" + resolved "https://npm.cubos.io/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" + dependencies: + hoek "4.x.x" + +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://npm.cubos.io/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://npm.cubos.io/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +browserslist@^2.1.2: + version "2.11.0" + resolved "https://npm.cubos.io/browserslist/-/browserslist-2.11.0.tgz#50350d6873a82ebe0f3ae5483658c571ae5f9d7d" + dependencies: + caniuse-lite "^1.0.30000784" + electron-to-chromium "^1.3.30" + +camelcase@^1.2.1: + version "1.2.1" + resolved "https://npm.cubos.io/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +caniuse-lite@^1.0.30000784: + version "1.0.30000784" + resolved "https://npm.cubos.io/caniuse-lite/-/caniuse-lite-1.0.30000784.tgz#129ced74e9a1280a441880b6cd2bce30ef59e6c0" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://npm.cubos.io/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://npm.cubos.io/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chokidar@^1.6.1: + version "1.7.0" + resolved "https://npm.cubos.io/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +cliui@^3.0.3: + version "3.2.0" + resolved "https://npm.cubos.io/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://npm.cubos.io/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://npm.cubos.io/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://npm.cubos.io/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +commander@^2.11.0: + version "2.12.2" + resolved "https://npm.cubos.io/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://npm.cubos.io/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://npm.cubos.io/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +convert-source-map@^1.5.0: + version "1.5.1" + resolved "https://npm.cubos.io/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" + +cookie@0.3.1: + version "0.3.1" + resolved "https://npm.cubos.io/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + +core-js@^2.4.0, core-js@^2.5.0: + version "2.5.3" + resolved "https://npm.cubos.io/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://npm.cubos.io/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://npm.cubos.io/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +cryptiles@3.x.x: + version "3.1.2" + resolved "https://npm.cubos.io/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" + dependencies: + boom "5.x.x" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://npm.cubos.io/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +debug@2.6.8: + version "2.6.8" + resolved "https://npm.cubos.io/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" + dependencies: + ms "2.0.0" + +debug@^2.2.0, debug@^2.6.8: + version "2.6.9" + resolved "https://npm.cubos.io/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +decamelize@^1.0.0: + version "1.2.0" + resolved "https://npm.cubos.io/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +deep-extend@~0.4.0: + version "0.4.2" + resolved "https://npm.cubos.io/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://npm.cubos.io/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://npm.cubos.io/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://npm.cubos.io/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +electron-releases@^2.1.0: + version "2.1.0" + resolved "https://npm.cubos.io/electron-releases/-/electron-releases-2.1.0.tgz#c5614bf811f176ce3c836e368a0625782341fd4e" + +electron-to-chromium@^1.3.30: + version "1.3.30" + resolved "https://npm.cubos.io/electron-to-chromium/-/electron-to-chromium-1.3.30.tgz#9666f532a64586651fc56a72513692e820d06a80" + dependencies: + electron-releases "^2.1.0" + +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://npm.cubos.io/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://npm.cubos.io/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://npm.cubos.io/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://npm.cubos.io/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +extend@~3.0.0, extend@~3.0.1: + version "3.0.1" + resolved "https://npm.cubos.io/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://npm.cubos.io/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://npm.cubos.io/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://npm.cubos.io/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fast-deep-equal@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://npm.cubos.io/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://npm.cubos.io/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://npm.cubos.io/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://npm.cubos.io/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://npm.cubos.io/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://npm.cubos.io/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +form-data@~2.3.1: + version "2.3.1" + resolved "https://npm.cubos.io/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +fs-readdir-recursive@^1.0.0: + version "1.1.0" + resolved "https://npm.cubos.io/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.1.3" + resolved "https://npm.cubos.io/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.39" + +fstream-ignore@^1.0.5: + version "1.0.5" + resolved "https://npm.cubos.io/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: + version "1.0.11" + resolved "https://npm.cubos.io/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://npm.cubos.io/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://npm.cubos.io/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://npm.cubos.io/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob@^7.0.5, glob@^7.1.2: + version "7.1.2" + resolved "https://npm.cubos.io/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^9.18.0: + version "9.18.0" + resolved "https://npm.cubos.io/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + +graceful-fs@^4.1.2, graceful-fs@^4.1.4: + version "4.1.11" + resolved "https://npm.cubos.io/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://npm.cubos.io/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://npm.cubos.io/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +har-validator@~5.0.3: + version "5.0.3" + resolved "https://npm.cubos.io/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + dependencies: + ajv "^5.1.0" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://npm.cubos.io/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +hawk@3.1.3, hawk@~3.1.3: + version "3.1.3" + resolved "https://npm.cubos.io/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hawk@~6.0.2: + version "6.0.2" + resolved "https://npm.cubos.io/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" + dependencies: + boom "4.x.x" + cryptiles "3.x.x" + hoek "4.x.x" + sntp "2.x.x" + +hoek@2.x.x: + version "2.16.3" + resolved "https://npm.cubos.io/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +hoek@4.x.x: + version "4.2.0" + resolved "https://npm.cubos.io/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://npm.cubos.io/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://npm.cubos.io/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://npm.cubos.io/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.3: + version "2.0.3" + resolved "https://npm.cubos.io/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@~1.3.0: + version "1.3.5" + resolved "https://npm.cubos.io/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +invariant@^2.2.2: + version "2.2.2" + resolved "https://npm.cubos.io/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://npm.cubos.io/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://npm.cubos.io/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://npm.cubos.io/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://npm.cubos.io/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://npm.cubos.io/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://npm.cubos.io/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://npm.cubos.io/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-number@^2.1.0: + version "2.1.0" + resolved "https://npm.cubos.io/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://npm.cubos.io/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://npm.cubos.io/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://npm.cubos.io/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://npm.cubos.io/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +js-tokens@^3.0.0, js-tokens@^3.0.2: + version "3.0.2" + resolved "https://npm.cubos.io/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://npm.cubos.io/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://npm.cubos.io/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://npm.cubos.io/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://npm.cubos.io/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://npm.cubos.io/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://npm.cubos.io/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://npm.cubos.io/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +json5@^0.5.1: + version "0.5.1" + resolved "https://npm.cubos.io/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +json@^9.0.4: + version "9.0.6" + resolved "https://npm.cubos.io/json/-/json-9.0.6.tgz#7972c2a5a48a42678db2730c7c2c4ee6e4e24585" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://npm.cubos.io/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://npm.cubos.io/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://npm.cubos.io/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://npm.cubos.io/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +localtunnel@^1.8.3: + version "1.8.3" + resolved "https://npm.cubos.io/localtunnel/-/localtunnel-1.8.3.tgz#dcc5922fd85651037d4bde24fd93248d0b24eb05" + dependencies: + debug "2.6.8" + openurl "1.1.1" + request "2.81.0" + yargs "3.29.0" + +lodash@^4.17.4: + version "4.17.4" + resolved "https://npm.cubos.io/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +loose-envify@^1.0.0: + version "1.3.1" + resolved "https://npm.cubos.io/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +lsmod@1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/lsmod/-/lsmod-1.0.0.tgz#9a00f76dca36eb23fa05350afe1b585d4299e64b" + +micromatch@^2.1.5: + version "2.3.11" + resolved "https://npm.cubos.io/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +mime-db@~1.30.0: + version "1.30.0" + resolved "https://npm.cubos.io/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7: + version "2.1.17" + resolved "https://npm.cubos.io/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" + dependencies: + mime-db "~1.30.0" + +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://npm.cubos.io/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://npm.cubos.io/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://npm.cubos.io/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +"mkdirp@>=0.5 0", mkdirp@^0.5.1: + version "0.5.1" + resolved "https://npm.cubos.io/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +ms@2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +nan@^2.3.0: + version "2.8.0" + resolved "https://npm.cubos.io/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" + +node-pre-gyp@^0.6.39: + version "0.6.39" + resolved "https://npm.cubos.io/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" + dependencies: + detect-libc "^1.0.2" + hawk "3.1.3" + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://npm.cubos.io/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-path@^2.0.0, normalize-path@^2.0.1: + version "2.1.1" + resolved "https://npm.cubos.io/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://npm.cubos.io/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://npm.cubos.io/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1, oauth-sign@~0.8.2: + version "0.8.2" + resolved "https://npm.cubos.io/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://npm.cubos.io/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://npm.cubos.io/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +once@^1.3.0, once@^1.3.3: + version "1.4.0" + resolved "https://npm.cubos.io/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +openurl@1.1.1: + version "1.1.1" + resolved "https://npm.cubos.io/openurl/-/openurl-1.1.1.tgz#3875b4b0ef7a52c156f0db41d4609dbb0f94b387" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://npm.cubos.io/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://npm.cubos.io/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://npm.cubos.io/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.4: + version "0.1.4" + resolved "https://npm.cubos.io/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +output-file-sync@^1.1.2: + version "1.1.2" + resolved "https://npm.cubos.io/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" + dependencies: + graceful-fs "^4.1.4" + mkdirp "^0.5.1" + object-assign "^4.1.0" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://npm.cubos.io/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://npm.cubos.io/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://npm.cubos.io/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://npm.cubos.io/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://npm.cubos.io/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +private@^0.1.6, private@^0.1.7: + version "0.1.8" + resolved "https://npm.cubos.io/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://npm.cubos.io/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://npm.cubos.io/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +qs@~6.4.0: + version "6.4.0" + resolved "https://npm.cubos.io/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +qs@~6.5.1: + version "6.5.1" + resolved "https://npm.cubos.io/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + +randomatic@^1.1.3: + version "1.1.7" + resolved "https://npm.cubos.io/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +raven@^2.3.0: + version "2.3.0" + resolved "https://npm.cubos.io/raven/-/raven-2.3.0.tgz#96f15346bdaa433b3b6d47130804506155833d69" + dependencies: + cookie "0.3.1" + lsmod "1.0.0" + stack-trace "0.0.9" + timed-out "4.0.1" + uuid "3.0.0" + +rc@^1.1.7: + version "1.2.2" + resolved "https://npm.cubos.io/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4: + version "2.3.3" + resolved "https://npm.cubos.io/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://npm.cubos.io/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +regenerate@^1.2.1: + version "1.3.3" + resolved "https://npm.cubos.io/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f" + +regenerator-runtime@^0.10.5: + version "0.10.5" + resolved "https://npm.cubos.io/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://npm.cubos.io/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://npm.cubos.io/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://npm.cubos.io/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + dependencies: + is-equal-shallow "^0.1.3" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://npm.cubos.io/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://npm.cubos.io/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://npm.cubos.io/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://npm.cubos.io/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://npm.cubos.io/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://npm.cubos.io/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request@2.81.0: + version "2.81.0" + resolved "https://npm.cubos.io/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +request@^2.83.0: + version "2.83.0" + resolved "https://npm.cubos.io/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + +rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: + version "2.6.2" + resolved "https://npm.cubos.io/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://npm.cubos.io/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +semver@^5.3.0: + version "5.4.1" + resolved "https://npm.cubos.io/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://npm.cubos.io/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://npm.cubos.io/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slash@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +sntp@1.x.x: + version "1.0.9" + resolved "https://npm.cubos.io/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +sntp@2.x.x: + version "2.1.0" + resolved "https://npm.cubos.io/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" + dependencies: + hoek "4.x.x" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://npm.cubos.io/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + dependencies: + source-map "^0.5.6" + +source-map@^0.5.6: + version "0.5.7" + resolved "https://npm.cubos.io/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +sshpk@^1.7.0: + version "1.13.1" + resolved "https://npm.cubos.io/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +stack-trace@0.0.9: + version "0.0.9" + resolved "https://npm.cubos.io/stack-trace/-/stack-trace-0.0.9.tgz#a8f6eaeca90674c333e7c43953f275b451510695" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://npm.cubos.io/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://npm.cubos.io/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +stringstream@~0.0.4, stringstream@~0.0.5: + version "0.0.5" + resolved "https://npm.cubos.io/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://npm.cubos.io/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://npm.cubos.io/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +tar-pack@^3.4.0: + version "3.4.1" + resolved "https://npm.cubos.io/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar@^2.2.1: + version "2.2.1" + resolved "https://npm.cubos.io/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +timed-out@4.0.1: + version "4.0.1" + resolved "https://npm.cubos.io/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://npm.cubos.io/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +tough-cookie@~2.3.0, tough-cookie@~2.3.3: + version "2.3.3" + resolved "https://npm.cubos.io/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" + dependencies: + punycode "^1.4.1" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://npm.cubos.io/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://npm.cubos.io/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://npm.cubos.io/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +typescript@^2.6.2: + version "2.6.2" + resolved "https://npm.cubos.io/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4" + +uid-number@^0.0.6: + version "0.0.6" + resolved "https://npm.cubos.io/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +user-home@^1.1.1: + version "1.1.1" + resolved "https://npm.cubos.io/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://npm.cubos.io/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +uuid@3.0.0: + version "3.0.0" + resolved "https://npm.cubos.io/uuid/-/uuid-3.0.0.tgz#6728fc0459c450d796a99c31837569bdf672d728" + +uuid@^3.0.0, uuid@^3.1.0: + version "3.1.0" + resolved "https://npm.cubos.io/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + +v8flags@^2.1.1: + version "2.1.1" + resolved "https://npm.cubos.io/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" + dependencies: + user-home "^1.1.1" + +verror@1.10.0: + version "1.10.0" + resolved "https://npm.cubos.io/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +wide-align@^1.1.0: + version "1.1.2" + resolved "https://npm.cubos.io/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + dependencies: + string-width "^1.0.2" + +window-size@^0.1.2: + version "0.1.4" + resolved "https://npm.cubos.io/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://npm.cubos.io/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://npm.cubos.io/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +y18n@^3.2.0: + version "3.2.1" + resolved "https://npm.cubos.io/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yargs@3.29.0: + version "3.29.0" + resolved "https://npm.cubos.io/yargs/-/yargs-3.29.0.tgz#1aab9660eae79d8b8f675bcaeeab6ee34c2cf69c" + dependencies: + camelcase "^1.2.1" + cliui "^3.0.3" + decamelize "^1.0.0" + os-locale "^1.4.0" + window-size "^0.1.2" + y18n "^3.2.0" diff --git a/target-web/.dockerignore b/target-web/.dockerignore index 868bd46..b512c09 100644 --- a/target-web/.dockerignore +++ b/target-web/.dockerignore @@ -1,4 +1 @@ -node_modules -api.js -api.js.map -api.d.ts \ No newline at end of file +node_modules \ No newline at end of file diff --git a/target-web/.gitignore b/target-web/.gitignore index 868bd46..b512c09 100644 --- a/target-web/.gitignore +++ b/target-web/.gitignore @@ -1,4 +1 @@ -node_modules -api.js -api.js.map -api.d.ts \ No newline at end of file +node_modules \ No newline at end of file diff --git a/target-web/build_and_publish.sh b/target-web/build_and_publish.sh index 6742a96..f6fd206 100644 --- a/target-web/build_and_publish.sh +++ b/target-web/build_and_publish.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash +set -e -NAME=$(basename `git rev-parse --show-toplevel`) -VERSION=1.0.$2 +NAME=$2 +VERSION=1.0.$3 cp $1 /root/stage/api.ts cd /root/stage @@ -9,6 +10,11 @@ cd /root/stage export PATH=$(npm bin):$PATH json -I -f package.json -e 'this.name="@cubos/'$NAME'"' npm version $VERSION || true +npm dist-tag add @cubos/$NAME@$CI_COMMIT_REF_NAME || true +echo "[tsc] START" tsc +echo "[tsc] END" +echo "[babel] START" babel api.js -o api.js +echo "[babel] END" npm publish diff --git a/target-web/build_and_publish2.sh b/target-web/build_and_publish2.sh new file mode 100644 index 0000000..a1f20c5 --- /dev/null +++ b/target-web/build_and_publish2.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -e + +NAME=$2 +VERSION=$3 + +cp $1 /root/stage/api.ts +cd /root/stage + +export PATH=$(npm bin):$PATH +json -I -f package.json -e 'this.name="@cubos/'$NAME'"' +npm version $VERSION || true +npm dist-tag add @cubos/$NAME@$CI_COMMIT_REF_NAME || true +echo "[tsc] START" +tsc +echo "[tsc] END" +echo "[babel] START" +babel api.js -o api.js +echo "[babel] END" +npm publish diff --git a/target-web/package.json b/target-web/package.json index 203e89a..848c99d 100644 --- a/target-web/package.json +++ b/target-web/package.json @@ -2,13 +2,14 @@ "name": "@cubos/api", "version": "0.0", "dependencies": { - "@types/ua-parser-js": "^0.7.30", - "babel-runtime": "^6.23.0", - "moment": "^2.17.1", - "ua-parser-js": "^0.7.12" + "@types/node": "^8.0.29", + "@types/ua-parser-js": "^0.7.32", + "babel-runtime": "^6.26.0", + "ua-parser-js": "^0.7.14" }, "main": "api.js", "types": "api.d.ts", + "license": "UNLICENSED", "babel": { "plugins": [ "transform-runtime" @@ -20,12 +21,12 @@ ] }, "devDependencies": { - "babel-cli": "^6.23.0", + "babel-cli": "^6.26.0", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-es2015": "^6.22.0", "babel-preset-es2016": "^6.22.0", "babel-preset-es2017": "^6.22.0", "json": "^9.0.4", - "typescript": "^2.2.1" + "typescript": "^2.5.2" } } diff --git a/target-web/yarn.lock b/target-web/yarn.lock index 46f8607..54550f7 100644 --- a/target-web/yarn.lock +++ b/target-web/yarn.lock @@ -2,17 +2,21 @@ # yarn lockfile v1 -"@types/ua-parser-js@^0.7.30": - version "0.7.30" - resolved "https://npm.cubos.io/@types%2fua-parser-js/-/ua-parser-js-0.7.30.tgz#9096e5552ff02f8d018a17efac69b4dc75083f8b" +"@types/node@^8.0.29": + version "8.0.29" + resolved "https://npm.cubos.io/@types%2fnode/-/node-8.0.29.tgz#4d6b33df66b15611b40452a581c40d9c94341ba1" + +"@types/ua-parser-js@^0.7.32": + version "0.7.32" + resolved "https://npm.cubos.io/@types%2fua-parser-js/-/ua-parser-js-0.7.32.tgz#8827d451d6702307248073b5d98aa9293d02b5e5" abbrev@1: version "1.1.0" resolved "https://npm.cubos.io/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f" ajv@^4.9.1: - version "4.11.4" - resolved "https://npm.cubos.io/ajv/-/ajv-4.11.4.tgz#ebf3a55d4b132ea60ff5847ae85d2ef069960b45" + version "4.11.8" + resolved "https://npm.cubos.io/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" dependencies: co "^4.6.0" json-stable-stringify "^1.0.1" @@ -33,15 +37,15 @@ anymatch@^1.3.0: micromatch "^2.1.5" aproba@^1.0.3: - version "1.1.1" - resolved "https://npm.cubos.io/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab" + version "1.1.2" + resolved "https://npm.cubos.io/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1" are-we-there-yet@~1.1.2: - version "1.1.2" - resolved "https://npm.cubos.io/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" + version "1.1.4" + resolved "https://npm.cubos.io/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" dependencies: delegates "^1.0.0" - readable-stream "^2.0.0 || ^1.1.13" + readable-stream "^2.0.6" arr-diff@^2.0.0: version "2.0.0" @@ -50,8 +54,8 @@ arr-diff@^2.0.0: arr-flatten "^1.0.1" arr-flatten@^1.0.1: - version "1.0.1" - resolved "https://npm.cubos.io/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" + version "1.1.0" + resolved "https://npm.cubos.io/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" array-unique@^0.2.1: version "0.2.1" @@ -65,14 +69,14 @@ asn1@~0.2.3: version "0.2.3" resolved "https://npm.cubos.io/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + assert-plus@^0.2.0: version "0.2.0" resolved "https://npm.cubos.io/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" -assert-plus@^1.0.0: - version "1.0.0" - resolved "https://npm.cubos.io/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - async-each@^1.0.0: version "1.0.1" resolved "https://npm.cubos.io/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -89,24 +93,24 @@ aws4@^1.2.1: version "1.6.0" resolved "https://npm.cubos.io/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" -babel-cli@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-cli/-/babel-cli-6.23.0.tgz#52ff946a2b0f64645c35e7bd5eea267aa0948c0f" +babel-cli@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-cli/-/babel-cli-6.26.0.tgz#502ab54874d7db88ad00b887a06383ce03d002f1" dependencies: - babel-core "^6.23.0" - babel-polyfill "^6.23.0" - babel-register "^6.23.0" - babel-runtime "^6.22.0" - commander "^2.8.1" - convert-source-map "^1.1.0" + babel-core "^6.26.0" + babel-polyfill "^6.26.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + commander "^2.11.0" + convert-source-map "^1.5.0" fs-readdir-recursive "^1.0.0" - glob "^7.0.0" - lodash "^4.2.0" - output-file-sync "^1.1.0" - path-is-absolute "^1.0.0" + glob "^7.1.2" + lodash "^4.17.4" + output-file-sync "^1.1.2" + path-is-absolute "^1.0.1" slash "^1.0.0" - source-map "^0.5.0" - v8flags "^2.0.10" + source-map "^0.5.6" + v8flags "^2.1.1" optionalDependencies: chokidar "^1.6.1" @@ -118,143 +122,151 @@ babel-code-frame@^6.22.0: esutils "^2.0.2" js-tokens "^3.0.0" -babel-core@^6.23.0: - version "6.23.1" - resolved "https://npm.cubos.io/babel-core/-/babel-core-6.23.1.tgz#c143cb621bb2f621710c220c5d579d15b8a442df" +babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: - babel-code-frame "^6.22.0" - babel-generator "^6.23.0" - babel-helpers "^6.23.0" + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8" + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" babel-messages "^6.23.0" - babel-register "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.1" - babel-types "^6.23.0" - babylon "^6.11.0" - convert-source-map "^1.1.0" - debug "^2.1.1" - json5 "^0.5.0" - lodash "^4.2.0" - minimatch "^3.0.2" - path-is-absolute "^1.0.0" - private "^0.1.6" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.0" + debug "^2.6.8" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.7" slash "^1.0.0" - source-map "^0.5.0" + source-map "^0.5.6" -babel-generator@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-generator/-/babel-generator-6.23.0.tgz#6b8edab956ef3116f79d8c84c5a3c05f32a74bc5" +babel-generator@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5" dependencies: babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" detect-indent "^4.0.0" jsesc "^1.3.0" - lodash "^4.2.0" - source-map "^0.5.0" + lodash "^4.17.4" + source-map "^0.5.6" trim-right "^1.0.1" -babel-helper-builder-binary-assignment-operator-visitor@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.22.0.tgz#29df56be144d81bdeac08262bfa41d2c5e91cdcd" +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" dependencies: - babel-helper-explode-assignable-expression "^6.22.0" + babel-helper-explode-assignable-expression "^6.24.1" babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" -babel-helper-call-delegate@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-helper-call-delegate/-/babel-helper-call-delegate-6.22.0.tgz#119921b56120f17e9dae3f74b4f5cc7bcc1b37ef" +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" dependencies: - babel-helper-hoist-variables "^6.22.0" + babel-helper-hoist-variables "^6.24.1" babel-runtime "^6.22.0" - babel-traverse "^6.22.0" - babel-types "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helper-define-map@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-helper-define-map/-/babel-helper-define-map-6.23.0.tgz#1444f960c9691d69a2ced6a205315f8fd00804e7" +babel-helper-define-map@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz#7a9747f258d8947d32d515f6aa1c7bd02204a080" dependencies: - babel-helper-function-name "^6.23.0" + babel-helper-function-name "^6.24.1" babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-types "^6.24.1" lodash "^4.2.0" -babel-helper-explode-assignable-expression@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.22.0.tgz#c97bf76eed3e0bae4048121f2b9dae1a4e7d0478" +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" dependencies: babel-runtime "^6.22.0" - babel-traverse "^6.22.0" - babel-types "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helper-function-name@^6.22.0, babel-helper-function-name@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-helper-function-name/-/babel-helper-function-name-6.23.0.tgz#25742d67175c8903dbe4b6cb9d9e1fcb8dcf23a6" +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" dependencies: - babel-helper-get-function-arity "^6.22.0" + babel-helper-get-function-arity "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helper-get-function-arity@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.22.0.tgz#0beb464ad69dc7347410ac6ade9f03a50634f5ce" +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" -babel-helper-hoist-variables@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.22.0.tgz#3eacbf731d80705845dd2e9718f600cfb9b4ba72" +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" -babel-helper-optimise-call-expression@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.23.0.tgz#f3ee7eed355b4282138b33d02b78369e470622f5" +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" dependencies: babel-runtime "^6.22.0" - babel-types "^6.23.0" + babel-types "^6.24.1" -babel-helper-regex@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-helper-regex/-/babel-helper-regex-6.22.0.tgz#79f532be1647b1f0ee3474b5f5c3da58001d247d" +babel-helper-regex@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz#d36e22fab1008d79d88648e32116868128456ce8" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" lodash "^4.2.0" -babel-helper-remap-async-to-generator@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.22.0.tgz#2186ae73278ed03b8b15ced089609da981053383" +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" dependencies: - babel-helper-function-name "^6.22.0" + babel-helper-function-name "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.22.0" - babel-traverse "^6.22.0" - babel-types "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helper-replace-supers@^6.22.0, babel-helper-replace-supers@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-helper-replace-supers/-/babel-helper-replace-supers-6.23.0.tgz#eeaf8ad9b58ec4337ca94223bacdca1f8d9b4bfd" +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" dependencies: - babel-helper-optimise-call-expression "^6.23.0" + babel-helper-optimise-call-expression "^6.24.1" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-helpers@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-helpers/-/babel-helpers-6.23.0.tgz#4f8f2e092d0b6a8808a4bde79c27f1e2ecf0d992" +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" dependencies: babel-runtime "^6.22.0" - babel-template "^6.23.0" + babel-template "^6.24.1" babel-messages@^6.23.0: version "6.23.0" @@ -280,11 +292,11 @@ babel-plugin-syntax-trailing-function-commas@^6.22.0: version "6.22.0" resolved "https://npm.cubos.io/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" -babel-plugin-transform-async-to-generator@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.22.0.tgz#194b6938ec195ad36efc4c33a971acf00d8cd35e" +babel-plugin-transform-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" dependencies: - babel-helper-remap-async-to-generator "^6.22.0" + babel-helper-remap-async-to-generator "^6.24.1" babel-plugin-syntax-async-functions "^6.8.0" babel-runtime "^6.22.0" @@ -300,36 +312,36 @@ babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-block-scoping@^6.22.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.23.0.tgz#e48895cf0b375be148cd7c8879b422707a053b51" +babel-plugin-transform-es2015-block-scoping@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz#76c295dc3a4741b1665adfd3167215dcff32a576" dependencies: babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" lodash "^4.2.0" -babel-plugin-transform-es2015-classes@^6.22.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.23.0.tgz#49b53f326202a2fd1b3bbaa5e2edd8a4f78643c1" +babel-plugin-transform-es2015-classes@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" dependencies: - babel-helper-define-map "^6.23.0" - babel-helper-function-name "^6.23.0" - babel-helper-optimise-call-expression "^6.23.0" - babel-helper-replace-supers "^6.23.0" + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-plugin-transform-es2015-computed-properties@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.22.0.tgz#7c383e9629bba4820c11b0425bdd6290f7f057e7" +babel-plugin-transform-es2015-computed-properties@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" dependencies: babel-runtime "^6.22.0" - babel-template "^6.22.0" + babel-template "^6.24.1" babel-plugin-transform-es2015-destructuring@^6.22.0: version "6.23.0" @@ -337,12 +349,12 @@ babel-plugin-transform-es2015-destructuring@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-duplicate-keys@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.22.0.tgz#672397031c21610d72dd2bbb0ba9fb6277e1c36b" +babel-plugin-transform-es2015-duplicate-keys@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-for-of@^6.22.0: version "6.23.0" @@ -350,13 +362,13 @@ babel-plugin-transform-es2015-for-of@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-function-name@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.22.0.tgz#f5fcc8b09093f9a23c76ac3d9e392c3ec4b77104" +babel-plugin-transform-es2015-function-name@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" dependencies: - babel-helper-function-name "^6.22.0" + babel-helper-function-name "^6.24.1" babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-literals@^6.22.0: version "6.22.0" @@ -364,63 +376,63 @@ babel-plugin-transform-es2015-literals@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-modules-amd@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.22.0.tgz#bf69cd34889a41c33d90dfb740e0091ccff52f21" +babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" dependencies: - babel-plugin-transform-es2015-modules-commonjs "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.22.0" + babel-template "^6.24.1" -babel-plugin-transform-es2015-modules-commonjs@^6.22.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.23.0.tgz#cba7aa6379fb7ec99250e6d46de2973aaffa7b92" +babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz#d3e310b40ef664a36622200097c6d440298f2bfe" dependencies: - babel-plugin-transform-strict-mode "^6.22.0" + babel-plugin-transform-strict-mode "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-types "^6.24.1" -babel-plugin-transform-es2015-modules-systemjs@^6.22.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.23.0.tgz#ae3469227ffac39b0310d90fec73bfdc4f6317b0" +babel-plugin-transform-es2015-modules-systemjs@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" dependencies: - babel-helper-hoist-variables "^6.22.0" + babel-helper-hoist-variables "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" + babel-template "^6.24.1" -babel-plugin-transform-es2015-modules-umd@^6.22.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.23.0.tgz#8d284ae2e19ed8fe21d2b1b26d6e7e0fcd94f0f1" +babel-plugin-transform-es2015-modules-umd@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" dependencies: - babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" + babel-template "^6.24.1" -babel-plugin-transform-es2015-object-super@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.22.0.tgz#daa60e114a042ea769dd53fe528fc82311eb98fc" +babel-plugin-transform-es2015-object-super@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" dependencies: - babel-helper-replace-supers "^6.22.0" + babel-helper-replace-supers "^6.24.1" babel-runtime "^6.22.0" -babel-plugin-transform-es2015-parameters@^6.22.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.23.0.tgz#3a2aabb70c8af945d5ce386f1a4250625a83ae3b" +babel-plugin-transform-es2015-parameters@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" dependencies: - babel-helper-call-delegate "^6.22.0" - babel-helper-get-function-arity "^6.22.0" + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" babel-runtime "^6.22.0" - babel-template "^6.23.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" -babel-plugin-transform-es2015-shorthand-properties@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.22.0.tgz#8ba776e0affaa60bff21e921403b8a652a2ff723" +babel-plugin-transform-es2015-shorthand-properties@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-spread@^6.22.0: version "6.22.0" @@ -428,13 +440,13 @@ babel-plugin-transform-es2015-spread@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-sticky-regex@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.22.0.tgz#ab316829e866ee3f4b9eb96939757d19a5bc4593" +babel-plugin-transform-es2015-sticky-regex@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" dependencies: - babel-helper-regex "^6.22.0" + babel-helper-regex "^6.24.1" babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" babel-plugin-transform-es2015-template-literals@^6.22.0: version "6.22.0" @@ -448,27 +460,27 @@ babel-plugin-transform-es2015-typeof-symbol@^6.22.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-es2015-unicode-regex@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.22.0.tgz#8d9cc27e7ee1decfe65454fb986452a04a613d20" +babel-plugin-transform-es2015-unicode-regex@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" dependencies: - babel-helper-regex "^6.22.0" + babel-helper-regex "^6.24.1" babel-runtime "^6.22.0" regexpu-core "^2.0.0" -babel-plugin-transform-exponentiation-operator@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.22.0.tgz#d57c8335281918e54ef053118ce6eb108468084d" +babel-plugin-transform-exponentiation-operator@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" dependencies: - babel-helper-builder-binary-assignment-operator-visitor "^6.22.0" + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" babel-plugin-syntax-exponentiation-operator "^6.8.0" babel-runtime "^6.22.0" -babel-plugin-transform-regenerator@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.22.0.tgz#65740593a319c44522157538d690b84094617ea6" +babel-plugin-transform-regenerator@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz#b8da305ad43c3c99b4848e4fe4037b770d23c418" dependencies: - regenerator-transform "0.9.8" + regenerator-transform "0.9.11" babel-plugin-transform-runtime@^6.23.0: version "6.23.0" @@ -476,122 +488,166 @@ babel-plugin-transform-runtime@^6.23.0: dependencies: babel-runtime "^6.22.0" -babel-plugin-transform-strict-mode@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.22.0.tgz#e008df01340fdc87e959da65991b7e05970c8c7c" +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://npm.cubos.io/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" dependencies: babel-runtime "^6.22.0" - babel-types "^6.22.0" + babel-types "^6.24.1" -babel-polyfill@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d" +babel-polyfill@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" dependencies: - babel-runtime "^6.22.0" - core-js "^2.4.0" - regenerator-runtime "^0.10.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + regenerator-runtime "^0.10.5" babel-preset-es2015@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-preset-es2015/-/babel-preset-es2015-6.22.0.tgz#af5a98ecb35eb8af764ad8a5a05eb36dc4386835" + version "6.24.1" + resolved "https://npm.cubos.io/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939" dependencies: babel-plugin-check-es2015-constants "^6.22.0" babel-plugin-transform-es2015-arrow-functions "^6.22.0" babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoping "^6.22.0" - babel-plugin-transform-es2015-classes "^6.22.0" - babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.24.1" + babel-plugin-transform-es2015-classes "^6.24.1" + babel-plugin-transform-es2015-computed-properties "^6.24.1" babel-plugin-transform-es2015-destructuring "^6.22.0" - babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-duplicate-keys "^6.24.1" babel-plugin-transform-es2015-for-of "^6.22.0" - babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-function-name "^6.24.1" babel-plugin-transform-es2015-literals "^6.22.0" - babel-plugin-transform-es2015-modules-amd "^6.22.0" - babel-plugin-transform-es2015-modules-commonjs "^6.22.0" - babel-plugin-transform-es2015-modules-systemjs "^6.22.0" - babel-plugin-transform-es2015-modules-umd "^6.22.0" - babel-plugin-transform-es2015-object-super "^6.22.0" - babel-plugin-transform-es2015-parameters "^6.22.0" - babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-plugin-transform-es2015-modules-systemjs "^6.24.1" + babel-plugin-transform-es2015-modules-umd "^6.24.1" + babel-plugin-transform-es2015-object-super "^6.24.1" + babel-plugin-transform-es2015-parameters "^6.24.1" + babel-plugin-transform-es2015-shorthand-properties "^6.24.1" babel-plugin-transform-es2015-spread "^6.22.0" - babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.24.1" babel-plugin-transform-es2015-template-literals "^6.22.0" babel-plugin-transform-es2015-typeof-symbol "^6.22.0" - babel-plugin-transform-es2015-unicode-regex "^6.22.0" - babel-plugin-transform-regenerator "^6.22.0" + babel-plugin-transform-es2015-unicode-regex "^6.24.1" + babel-plugin-transform-regenerator "^6.24.1" babel-preset-es2016@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-preset-es2016/-/babel-preset-es2016-6.22.0.tgz#b061aaa3983d40c9fbacfa3743b5df37f336156c" + version "6.24.1" + resolved "https://npm.cubos.io/babel-preset-es2016/-/babel-preset-es2016-6.24.1.tgz#f900bf93e2ebc0d276df9b8ab59724ebfd959f8b" dependencies: - babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.24.1" babel-preset-es2017@^6.22.0: - version "6.22.0" - resolved "https://npm.cubos.io/babel-preset-es2017/-/babel-preset-es2017-6.22.0.tgz#de2f9da5a30c50d293fb54a0ba15d6ddc573f0f2" + version "6.24.1" + resolved "https://npm.cubos.io/babel-preset-es2017/-/babel-preset-es2017-6.24.1.tgz#597beadfb9f7f208bcfd8a12e9b2b29b8b2f14d1" dependencies: babel-plugin-syntax-trailing-function-commas "^6.22.0" - babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-async-to-generator "^6.24.1" -babel-register@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-register/-/babel-register-6.23.0.tgz#c9aa3d4cca94b51da34826c4a0f9e08145d74ff3" +babel-register@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" dependencies: - babel-core "^6.23.0" - babel-runtime "^6.22.0" - core-js "^2.4.0" + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" home-or-tmp "^2.0.0" - lodash "^4.2.0" + lodash "^4.17.4" mkdirp "^0.5.1" - source-map-support "^0.4.2" + source-map-support "^0.4.15" -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0: +babel-runtime@^6.18.0, babel-runtime@^6.22.0: version "6.23.0" resolved "https://npm.cubos.io/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" dependencies: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-template@^6.22.0, babel-template@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-template/-/babel-template-6.23.0.tgz#04d4f270adbb3aa704a8143ae26faa529238e638" +babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1: + version "6.25.0" + resolved "https://npm.cubos.io/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071" dependencies: babel-runtime "^6.22.0" - babel-traverse "^6.23.0" - babel-types "^6.23.0" - babylon "^6.11.0" + babel-traverse "^6.25.0" + babel-types "^6.25.0" + babylon "^6.17.2" lodash "^4.2.0" -babel-traverse@^6.22.0, babel-traverse@^6.23.0, babel-traverse@^6.23.1: - version "6.23.1" - resolved "https://npm.cubos.io/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48" +babel-template@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.25.0: + version "6.25.0" + resolved "https://npm.cubos.io/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1" dependencies: babel-code-frame "^6.22.0" babel-messages "^6.23.0" babel-runtime "^6.22.0" - babel-types "^6.23.0" - babylon "^6.15.0" + babel-types "^6.25.0" + babylon "^6.17.2" debug "^2.2.0" globals "^9.0.0" invariant "^2.2.0" lodash "^4.2.0" -babel-types@^6.19.0, babel-types@^6.22.0, babel-types@^6.23.0: - version "6.23.0" - resolved "https://npm.cubos.io/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf" +babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0: + version "6.25.0" + resolved "https://npm.cubos.io/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e" dependencies: babel-runtime "^6.22.0" esutils "^2.0.2" lodash "^4.2.0" to-fast-properties "^1.0.1" -babylon@^6.11.0, babylon@^6.15.0: - version "6.16.1" - resolved "https://npm.cubos.io/babylon/-/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3" +babel-types@^6.26.0: + version "6.26.0" + resolved "https://npm.cubos.io/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" -balanced-match@^0.4.1: - version "0.4.2" - resolved "https://npm.cubos.io/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" +babylon@^6.17.2: + version "6.17.4" + resolved "https://npm.cubos.io/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://npm.cubos.io/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://npm.cubos.io/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" bcrypt-pbkdf@^1.0.0: version "1.0.1" @@ -615,11 +671,11 @@ boom@2.x.x: dependencies: hoek "2.x.x" -brace-expansion@^1.0.0: - version "1.1.6" - resolved "https://npm.cubos.io/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://npm.cubos.io/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" dependencies: - balanced-match "^0.4.1" + balanced-match "^1.0.0" concat-map "0.0.1" braces@^1.8.2: @@ -630,15 +686,11 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" -buffer-shims@^1.0.0: - version "1.0.0" - resolved "https://npm.cubos.io/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" - caseless@~0.12.0: version "0.12.0" resolved "https://npm.cubos.io/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" -chalk@^1.1.0: +chalk@^1.1.0, chalk@^1.1.3: version "1.1.3" resolved "https://npm.cubos.io/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" dependencies: @@ -649,8 +701,8 @@ chalk@^1.1.0: supports-color "^2.0.0" chokidar@^1.6.1: - version "1.6.1" - resolved "https://npm.cubos.io/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" + version "1.7.0" + resolved "https://npm.cubos.io/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" dependencies: anymatch "^1.3.0" async-each "^1.0.0" @@ -677,11 +729,9 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@^2.8.1: - version "2.9.0" - resolved "https://npm.cubos.io/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" - dependencies: - graceful-readlink ">= 1.0.0" +commander@^2.11.0: + version "2.11.0" + resolved "https://npm.cubos.io/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" concat-map@0.0.1: version "0.0.1" @@ -691,14 +741,18 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: version "1.1.0" resolved "https://npm.cubos.io/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" -convert-source-map@^1.1.0: - version "1.4.0" - resolved "https://npm.cubos.io/convert-source-map/-/convert-source-map-1.4.0.tgz#e3dad195bf61bfe13a7a3c73e9876ec14a0268f3" +convert-source-map@^1.5.0: + version "1.5.0" + resolved "https://npm.cubos.io/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" core-js@^2.4.0: version "2.4.1" resolved "https://npm.cubos.io/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" +core-js@^2.5.0: + version "2.5.1" + resolved "https://npm.cubos.io/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" + core-util-is@~1.0.0: version "1.0.2" resolved "https://npm.cubos.io/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -715,21 +769,21 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -debug@^2.1.1, debug@^2.2.0: - version "2.6.1" - resolved "https://npm.cubos.io/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" +debug@^2.2.0: + version "2.6.8" + resolved "https://npm.cubos.io/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" dependencies: - ms "0.7.2" + ms "2.0.0" -debug@~2.2.0: - version "2.2.0" - resolved "https://npm.cubos.io/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" +debug@^2.6.8: + version "2.6.9" + resolved "https://npm.cubos.io/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: - ms "0.7.1" + ms "2.0.0" deep-extend@~0.4.0: - version "0.4.1" - resolved "https://npm.cubos.io/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" + version "0.4.2" + resolved "https://npm.cubos.io/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" delayed-stream@~1.0.0: version "1.0.0" @@ -772,8 +826,8 @@ expand-range@^1.8.1: fill-range "^2.1.0" extend@~3.0.0: - version "3.0.0" - resolved "https://npm.cubos.io/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" + version "3.0.1" + resolved "https://npm.cubos.io/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" extglob@^0.3.1: version "0.3.2" @@ -786,8 +840,8 @@ extsprintf@1.0.2: resolved "https://npm.cubos.io/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" filename-regex@^2.0.0: - version "2.0.0" - resolved "https://npm.cubos.io/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" + version "2.0.1" + resolved "https://npm.cubos.io/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" fill-range@^2.1.0: version "2.2.3" @@ -814,8 +868,8 @@ forever-agent@~0.6.1: resolved "https://npm.cubos.io/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" form-data@~2.1.1: - version "2.1.2" - resolved "https://npm.cubos.io/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4" + version "2.1.4" + resolved "https://npm.cubos.io/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" dependencies: asynckit "^0.4.0" combined-stream "^1.0.5" @@ -830,13 +884,13 @@ fs.realpath@^1.0.0: resolved "https://npm.cubos.io/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" fsevents@^1.0.0: - version "1.1.1" - resolved "https://npm.cubos.io/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff" + version "1.1.2" + resolved "https://npm.cubos.io/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4" dependencies: nan "^2.3.0" - node-pre-gyp "^0.6.29" + node-pre-gyp "^0.6.36" -fstream-ignore@~1.0.5: +fstream-ignore@^1.0.5: version "1.0.5" resolved "https://npm.cubos.io/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" dependencies: @@ -844,18 +898,18 @@ fstream-ignore@~1.0.5: inherits "2" minimatch "^3.0.0" -fstream@^1.0.0, fstream@^1.0.2, fstream@~1.0.10: - version "1.0.10" - resolved "https://npm.cubos.io/fstream/-/fstream-1.0.10.tgz#604e8a92fe26ffd9f6fae30399d4984e1ab22822" +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: + version "1.0.11" + resolved "https://npm.cubos.io/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" dependencies: graceful-fs "^4.1.2" inherits "~2.0.0" mkdirp ">=0.5 0" rimraf "2" -gauge@~2.7.1: - version "2.7.3" - resolved "https://npm.cubos.io/gauge/-/gauge-2.7.3.tgz#1c23855f962f17b3ad3d0dc7443f304542edfe09" +gauge@~2.7.3: + version "2.7.4" + resolved "https://npm.cubos.io/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" dependencies: aproba "^1.0.3" console-control-strings "^1.0.0" @@ -867,8 +921,8 @@ gauge@~2.7.1: wide-align "^1.1.0" getpass@^0.1.1: - version "0.1.6" - resolved "https://npm.cubos.io/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" + version "0.1.7" + resolved "https://npm.cubos.io/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" dependencies: assert-plus "^1.0.0" @@ -885,34 +939,30 @@ glob-parent@^2.0.0: dependencies: is-glob "^2.0.0" -glob@^7.0.0, glob@^7.0.5: - version "7.1.1" - resolved "https://npm.cubos.io/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" +glob@^7.0.5, glob@^7.1.2: + version "7.1.2" + resolved "https://npm.cubos.io/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.2" + minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" -globals@^9.0.0: - version "9.16.0" - resolved "https://npm.cubos.io/globals/-/globals-9.16.0.tgz#63e903658171ec2d9f51b1d31de5e2b8dc01fb80" +globals@^9.0.0, globals@^9.18.0: + version "9.18.0" + resolved "https://npm.cubos.io/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" graceful-fs@^4.1.2, graceful-fs@^4.1.4: version "4.1.11" resolved "https://npm.cubos.io/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://npm.cubos.io/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - har-schema@^1.0.5: version "1.0.5" resolved "https://npm.cubos.io/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" -har-validator@~4.2.0: +har-validator@~4.2.1: version "4.2.1" resolved "https://npm.cubos.io/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" dependencies: @@ -964,7 +1014,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: +inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.3: version "2.0.3" resolved "https://npm.cubos.io/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -972,7 +1022,7 @@ ini@~1.3.0: version "1.3.4" resolved "https://npm.cubos.io/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" -invariant@^2.2.0: +invariant@^2.2.0, invariant@^2.2.2: version "2.2.2" resolved "https://npm.cubos.io/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" dependencies: @@ -984,13 +1034,13 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.0.2: - version "1.1.4" - resolved "https://npm.cubos.io/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" +is-buffer@^1.1.5: + version "1.1.5" + resolved "https://npm.cubos.io/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" is-dotfile@^1.0.0: - version "1.0.2" - resolved "https://npm.cubos.io/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + version "1.0.3" + resolved "https://npm.cubos.io/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" is-equal-shallow@^0.1.3: version "0.1.3" @@ -1024,12 +1074,18 @@ is-glob@^2.0.0, is-glob@^2.0.1: dependencies: is-extglob "^1.0.0" -is-number@^2.0.2, is-number@^2.1.0: +is-number@^2.1.0: version "2.1.0" resolved "https://npm.cubos.io/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" dependencies: kind-of "^3.0.2" +is-number@^3.0.0: + version "3.0.0" + resolved "https://npm.cubos.io/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + is-posix-bracket@^0.1.0: version "0.1.1" resolved "https://npm.cubos.io/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" @@ -1056,15 +1112,9 @@ isstream@~0.1.2: version "0.1.2" resolved "https://npm.cubos.io/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -jodid25519@^1.0.0: - version "1.0.2" - resolved "https://npm.cubos.io/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" - dependencies: - jsbn "~0.1.0" - -js-tokens@^3.0.0: - version "3.0.1" - resolved "https://npm.cubos.io/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" +js-tokens@^3.0.0, js-tokens@^3.0.2: + version "3.0.2" + resolved "https://npm.cubos.io/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" jsbn@~0.1.0: version "0.1.1" @@ -1092,7 +1142,7 @@ json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://npm.cubos.io/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" -json5@^0.5.0: +json5@^0.5.1: version "0.5.1" resolved "https://npm.cubos.io/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" @@ -1105,20 +1155,27 @@ jsonify@~0.0.0: resolved "https://npm.cubos.io/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" jsprim@^1.2.2: - version "1.3.1" - resolved "https://npm.cubos.io/jsprim/-/jsprim-1.3.1.tgz#2a7256f70412a29ee3670aaca625994c4dcff252" + version "1.4.0" + resolved "https://npm.cubos.io/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" dependencies: + assert-plus "1.0.0" extsprintf "1.0.2" json-schema "0.2.3" verror "1.3.6" kind-of@^3.0.2: - version "3.1.0" - resolved "https://npm.cubos.io/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" + version "3.2.2" + resolved "https://npm.cubos.io/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://npm.cubos.io/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" dependencies: - is-buffer "^1.0.2" + is-buffer "^1.1.5" -lodash@^4.2.0: +lodash@^4.17.4, lodash@^4.2.0: version "4.17.4" resolved "https://npm.cubos.io/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -1146,21 +1203,21 @@ micromatch@^2.1.5: parse-glob "^3.0.4" regex-cache "^0.4.2" -mime-db@~1.26.0: - version "1.26.0" - resolved "https://npm.cubos.io/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" +mime-db@~1.27.0: + version "1.27.0" + resolved "https://npm.cubos.io/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" mime-types@^2.1.12, mime-types@~2.1.7: - version "2.1.14" - resolved "https://npm.cubos.io/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" + version "2.1.15" + resolved "https://npm.cubos.io/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" dependencies: - mime-db "~1.26.0" + mime-db "~1.27.0" -minimatch@^3.0.0, minimatch@^3.0.2: - version "3.0.3" - resolved "https://npm.cubos.io/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" +minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://npm.cubos.io/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: - brace-expansion "^1.0.0" + brace-expansion "^1.1.7" minimist@0.0.8: version "0.0.8" @@ -1170,59 +1227,54 @@ minimist@^1.2.0: version "1.2.0" resolved "https://npm.cubos.io/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -"mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.1: +"mkdirp@>=0.5 0", mkdirp@^0.5.1: version "0.5.1" resolved "https://npm.cubos.io/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" -moment@^2.17.1: - version "2.17.1" - resolved "https://npm.cubos.io/moment/-/moment-2.17.1.tgz#fed9506063f36b10f066c8b59a144d7faebe1d82" - -ms@0.7.1: - version "0.7.1" - resolved "https://npm.cubos.io/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" - -ms@0.7.2: - version "0.7.2" - resolved "https://npm.cubos.io/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" +ms@2.0.0: + version "2.0.0" + resolved "https://npm.cubos.io/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" nan@^2.3.0: - version "2.5.1" - resolved "https://npm.cubos.io/nan/-/nan-2.5.1.tgz#d5b01691253326a97a2bbee9e61c55d8d60351e2" - -node-pre-gyp@^0.6.29: - version "0.6.33" - resolved "https://npm.cubos.io/node-pre-gyp/-/node-pre-gyp-0.6.33.tgz#640ac55198f6a925972e0c16c4ac26a034d5ecc9" - dependencies: - mkdirp "~0.5.1" - nopt "~3.0.6" - npmlog "^4.0.1" - rc "~1.1.6" - request "^2.79.0" - rimraf "~2.5.4" - semver "~5.3.0" - tar "~2.2.1" - tar-pack "~3.3.0" - -nopt@~3.0.6: - version "3.0.6" - resolved "https://npm.cubos.io/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + version "2.6.2" + resolved "https://npm.cubos.io/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45" + +node-pre-gyp@^0.6.36: + version "0.6.36" + resolved "https://npm.cubos.io/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" + dependencies: + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "^2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://npm.cubos.io/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" dependencies: abbrev "1" + osenv "^0.1.4" normalize-path@^2.0.1: - version "2.0.1" - resolved "https://npm.cubos.io/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a" + version "2.1.1" + resolved "https://npm.cubos.io/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" -npmlog@^4.0.1: - version "4.0.2" - resolved "https://npm.cubos.io/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" +npmlog@^4.0.2: + version "4.1.2" + resolved "https://npm.cubos.io/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" - gauge "~2.7.1" + gauge "~2.7.3" set-blocking "~2.0.0" number-is-nan@^1.0.0: @@ -1244,9 +1296,9 @@ object.omit@^2.0.0: for-own "^0.1.4" is-extendable "^0.1.1" -once@^1.3.0, once@~1.3.3: - version "1.3.3" - resolved "https://npm.cubos.io/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" +once@^1.3.0, once@^1.3.3: + version "1.4.0" + resolved "https://npm.cubos.io/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" dependencies: wrappy "1" @@ -1254,11 +1306,18 @@ os-homedir@^1.0.0: version "1.0.2" resolved "https://npm.cubos.io/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" -os-tmpdir@^1.0.1: +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: version "1.0.2" resolved "https://npm.cubos.io/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" -output-file-sync@^1.1.0: +osenv@^0.1.4: + version "0.1.4" + resolved "https://npm.cubos.io/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +output-file-sync@^1.1.2: version "1.1.2" resolved "https://npm.cubos.io/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" dependencies: @@ -1275,7 +1334,7 @@ parse-glob@^3.0.4: is-extglob "^1.0.0" is-glob "^2.0.0" -path-is-absolute@^1.0.0: +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://npm.cubos.io/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -1287,7 +1346,7 @@ preserve@^0.2.0: version "0.2.0" resolved "https://npm.cubos.io/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" -private@^0.1.6: +private@^0.1.6, private@^0.1.7: version "0.1.7" resolved "https://npm.cubos.io/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" @@ -1299,48 +1358,36 @@ punycode@^1.4.1: version "1.4.1" resolved "https://npm.cubos.io/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" -qs@~6.3.0: - version "6.3.2" - resolved "https://npm.cubos.io/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" +qs@~6.4.0: + version "6.4.0" + resolved "https://npm.cubos.io/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" randomatic@^1.1.3: - version "1.1.6" - resolved "https://npm.cubos.io/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + version "1.1.7" + resolved "https://npm.cubos.io/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" dependencies: - is-number "^2.0.2" - kind-of "^3.0.2" + is-number "^3.0.0" + kind-of "^4.0.0" -rc@~1.1.6: - version "1.1.7" - resolved "https://npm.cubos.io/rc/-/rc-1.1.7.tgz#c5ea564bb07aff9fd3a5b32e906c1d3a65940fea" +rc@^1.1.7: + version "1.2.1" + resolved "https://npm.cubos.io/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" dependencies: deep-extend "~0.4.0" ini "~1.3.0" minimist "^1.2.0" strip-json-comments "~2.0.1" -"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.2: - version "2.2.3" - resolved "https://npm.cubos.io/readable-stream/-/readable-stream-2.2.3.tgz#9cf49463985df016c8ae8813097a9293a9b33729" +readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4: + version "2.3.3" + resolved "https://npm.cubos.io/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" dependencies: - buffer-shims "^1.0.0" core-util-is "~1.0.0" - inherits "~2.0.1" + inherits "~2.0.3" isarray "~1.0.0" process-nextick-args "~1.0.6" - string_decoder "~0.10.x" - util-deprecate "~1.0.1" - -readable-stream@~2.1.4: - version "2.1.5" - resolved "https://npm.cubos.io/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" - dependencies: - buffer-shims "^1.0.0" - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - string_decoder "~0.10.x" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" util-deprecate "~1.0.1" readdirp@^2.0.0: @@ -1356,13 +1403,17 @@ regenerate@^1.2.1: version "1.3.2" resolved "https://npm.cubos.io/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260" -regenerator-runtime@^0.10.0: - version "0.10.3" - resolved "https://npm.cubos.io/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz#8c4367a904b51ea62a908ac310bf99ff90a82a3e" +regenerator-runtime@^0.10.0, regenerator-runtime@^0.10.5: + version "0.10.5" + resolved "https://npm.cubos.io/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + +regenerator-runtime@^0.11.0: + version "0.11.0" + resolved "https://npm.cubos.io/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1" -regenerator-transform@0.9.8: - version "0.9.8" - resolved "https://npm.cubos.io/regenerator-transform/-/regenerator-transform-0.9.8.tgz#0f88bb2bc03932ddb7b6b7312e68078f01026d6c" +regenerator-transform@0.9.11: + version "0.9.11" + resolved "https://npm.cubos.io/regenerator-transform/-/regenerator-transform-0.9.11.tgz#3a7d067520cb7b7176769eb5ff868691befe1283" dependencies: babel-runtime "^6.18.0" babel-types "^6.19.0" @@ -1393,6 +1444,10 @@ regjsparser@^0.1.4: dependencies: jsesc "~0.5.0" +remove-trailing-separator@^1.0.1: + version "1.0.2" + resolved "https://npm.cubos.io/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511" + repeat-element@^1.1.2: version "1.1.2" resolved "https://npm.cubos.io/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" @@ -1407,9 +1462,9 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@^2.79.0: - version "2.80.0" - resolved "https://npm.cubos.io/request/-/request-2.80.0.tgz#8cc162d76d79381cdefdd3505d76b80b60589bd0" +request@^2.81.0: + version "2.81.0" + resolved "https://npm.cubos.io/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: aws-sign2 "~0.6.0" aws4 "^1.2.1" @@ -1418,7 +1473,7 @@ request@^2.79.0: extend "~3.0.0" forever-agent "~0.6.1" form-data "~2.1.1" - har-validator "~4.2.0" + har-validator "~4.2.1" hawk "~3.1.3" http-signature "~1.1.0" is-typedarray "~1.0.0" @@ -1427,19 +1482,24 @@ request@^2.79.0: mime-types "~2.1.7" oauth-sign "~0.8.1" performance-now "^0.2.0" - qs "~6.3.0" + qs "~6.4.0" + safe-buffer "^5.0.1" stringstream "~0.0.4" tough-cookie "~2.3.0" - tunnel-agent "~0.4.1" + tunnel-agent "^0.6.0" uuid "^3.0.0" -rimraf@2, rimraf@~2.5.1, rimraf@~2.5.4: - version "2.5.4" - resolved "https://npm.cubos.io/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" +rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: + version "2.6.1" + resolved "https://npm.cubos.io/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" dependencies: glob "^7.0.5" -semver@~5.3.0: +safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://npm.cubos.io/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +semver@^5.3.0: version "5.3.0" resolved "https://npm.cubos.io/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -1465,19 +1525,19 @@ sntp@1.x.x: dependencies: hoek "2.x.x" -source-map-support@^0.4.2: - version "0.4.11" - resolved "https://npm.cubos.io/source-map-support/-/source-map-support-0.4.11.tgz#647f939978b38535909530885303daf23279f322" +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://npm.cubos.io/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" dependencies: - source-map "^0.5.3" + source-map "^0.5.6" -source-map@^0.5.0, source-map@^0.5.3: +source-map@^0.5.6: version "0.5.6" resolved "https://npm.cubos.io/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" sshpk@^1.7.0: - version "1.11.0" - resolved "https://npm.cubos.io/sshpk/-/sshpk-1.11.0.tgz#2d8d5ebb4a6fab28ffba37fa62a90f4a3ea59d77" + version "1.13.1" + resolved "https://npm.cubos.io/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" dependencies: asn1 "~0.2.3" assert-plus "^1.0.0" @@ -1486,11 +1546,10 @@ sshpk@^1.7.0: optionalDependencies: bcrypt-pbkdf "^1.0.0" ecc-jsbn "~0.1.1" - jodid25519 "^1.0.0" jsbn "~0.1.0" tweetnacl "~0.14.0" -string-width@^1.0.1: +string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://npm.cubos.io/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" dependencies: @@ -1498,9 +1557,11 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://npm.cubos.io/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://npm.cubos.io/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" stringstream@~0.0.4: version "0.0.5" @@ -1520,20 +1581,20 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://npm.cubos.io/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -tar-pack@~3.3.0: - version "3.3.0" - resolved "https://npm.cubos.io/tar-pack/-/tar-pack-3.3.0.tgz#30931816418f55afc4d21775afdd6720cee45dae" +tar-pack@^3.4.0: + version "3.4.0" + resolved "https://npm.cubos.io/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" dependencies: - debug "~2.2.0" - fstream "~1.0.10" - fstream-ignore "~1.0.5" - once "~1.3.3" - readable-stream "~2.1.4" - rimraf "~2.5.1" - tar "~2.2.1" - uid-number "~0.0.6" - -tar@~2.2.1: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar@^2.2.1: version "2.2.1" resolved "https://npm.cubos.io/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" dependencies: @@ -1541,9 +1602,9 @@ tar@~2.2.1: fstream "^1.0.2" inherits "2" -to-fast-properties@^1.0.1: - version "1.0.2" - resolved "https://npm.cubos.io/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" +to-fast-properties@^1.0.1, to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://npm.cubos.io/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" tough-cookie@~2.3.0: version "2.3.2" @@ -1555,23 +1616,25 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://npm.cubos.io/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -tunnel-agent@~0.4.1: - version "0.4.3" - resolved "https://npm.cubos.io/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://npm.cubos.io/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://npm.cubos.io/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" -typescript@^2.2.1: - version "2.2.1" - resolved "https://npm.cubos.io/typescript/-/typescript-2.2.1.tgz#4862b662b988a4c8ff691cc7969622d24db76ae9" +typescript@^2.5.2: + version "2.5.2" + resolved "https://npm.cubos.io/typescript/-/typescript-2.5.2.tgz#038a95f7d9bbb420b1bf35ba31d4c5c1dd3ffe34" -ua-parser-js@^0.7.12: - version "0.7.12" - resolved "https://npm.cubos.io/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb" +ua-parser-js@^0.7.14: + version "0.7.14" + resolved "https://npm.cubos.io/ua-parser-js/-/ua-parser-js-0.7.14.tgz#110d53fa4c3f326c121292bbeac904d2e03387ca" -uid-number@~0.0.6: +uid-number@^0.0.6: version "0.0.6" resolved "https://npm.cubos.io/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" @@ -1584,12 +1647,12 @@ util-deprecate@~1.0.1: resolved "https://npm.cubos.io/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" uuid@^3.0.0: - version "3.0.1" - resolved "https://npm.cubos.io/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + version "3.1.0" + resolved "https://npm.cubos.io/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" -v8flags@^2.0.10: - version "2.0.11" - resolved "https://npm.cubos.io/v8flags/-/v8flags-2.0.11.tgz#bca8f30f0d6d60612cc2c00641e6962d42ae6881" +v8flags@^2.1.1: + version "2.1.1" + resolved "https://npm.cubos.io/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" dependencies: user-home "^1.1.1" @@ -1600,10 +1663,10 @@ verror@1.3.6: extsprintf "1.0.2" wide-align@^1.1.0: - version "1.1.0" - resolved "https://npm.cubos.io/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" + version "1.1.2" + resolved "https://npm.cubos.io/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" dependencies: - string-width "^1.0.1" + string-width "^1.0.2" wrappy@1: version "1.0.2" diff --git a/target.cr b/target.cr deleted file mode 100644 index f8e6899..0000000 --- a/target.cr +++ /dev/null @@ -1,41 +0,0 @@ -require "./ast" -require "./lexer" -require "./parser" - -abstract class Target - @@targets = {} of {String, Bool} => Target.class - - def initialize(@output : String, @ast : AST::ApiDescription) - @io = IO::Memory.new - end - - def write - @io.rewind - File.write(@output, @io) - end - - abstract def gen - - def self.register(target, language, is_server = false) - @@targets[{language, is_server}] = target - end - - def self.process(ast, output, is_server = false) - match = output.match(/\.(\w+)$/) - unless match - raise "Unrecognized extension for '#{output}'" - end - language = match[1] - target = @@targets[{language, is_server}]? - unless target - raise "Language extension '.#{language}' is not supported" - end - t = target.new(output, ast) - t.gen - t.write - end - - def ident(code) - code.split("\n").map {|line| " " + line}.join("\n").gsub(/\n\s+$/, "\n") - end -end \ No newline at end of file diff --git a/target_java.cr b/target_java.cr deleted file mode 100644 index 1b64b9a..0000000 --- a/target_java.cr +++ /dev/null @@ -1,175 +0,0 @@ -require "./target" - -abstract class JavaTarget < Target - def ident(code) - super super code - end - - def native_type_not_primitive(t : AST::PrimitiveType) - case t - when AST::StringPrimitiveType; "String" - when AST::IntPrimitiveType; "Integer" - when AST::UIntPrimitiveType; "Integer" - when AST::FloatPrimitiveType; "Double" - when AST::DatePrimitiveType; "Date" - when AST::DateTimePrimitiveType; "Date" - when AST::BoolPrimitiveType; "Boolean" - when AST::BytesPrimitiveType; "byte[]" - when AST::VoidPrimitiveType; "void" - else - raise "BUG! Should handle primitive #{t.class}" - end - end - - def native_type_not_primitive(t : AST::Type) - native_type(t) - end - - def native_type(t : AST::PrimitiveType) - case t - when AST::StringPrimitiveType; "String" - when AST::IntPrimitiveType; "int" - when AST::UIntPrimitiveType; "int" - when AST::FloatPrimitiveType; "double" - when AST::DatePrimitiveType; "Calendar" - when AST::DateTimePrimitiveType; "Calendar" - when AST::BoolPrimitiveType; "boolean" - when AST::BytesPrimitiveType; "byte[]" - when AST::VoidPrimitiveType; "void" - else - raise "BUG! Should handle primitive #{t.class}" - end - end - - def native_type(t : AST::OptionalType) - native_type_not_primitive(t.base) - end - - def native_type(t : AST::ArrayType) - "ArrayList<#{native_type_not_primitive(t.base)}>" - end - - def native_type(t : AST::CustomTypeReference) - t.name - end - - def generate_custom_type_interface(custom_type) - String.build do |io| - io << "public static class #{custom_type.name} {\n" - custom_type.fields.each do |field| - io << ident "public #{native_type field.type} #{field.name};\n" - end - io << ident <<-END - -public JSONObject toJSON() { - try { - return new JSONObject() {{ - -END - custom_type.fields.each do |field| - io << ident ident ident ident "put(\"#{field.name}\", #{type_to_json field.type, field.name});\n" - end - io << ident <<-END - }}; - } catch (JSONException e) { - e.printStackTrace(); - return new JSONObject(); - } -} - -public static #{custom_type.name} fromJSON(final JSONObject json) { - try { - return new #{custom_type.name}() {{ - -END - custom_type.fields.each do |field| - io << ident ident ident ident "#{field.name} = #{type_from_json field.type, get_field_from_json_object(field.type, "json", field.name.inspect)};\n" - end - io << ident <<-END - - }}; - } catch (JSONException e) { - e.printStackTrace(); - return new #{custom_type.name}(); - } -} - -END - io << "}" - end - end - - def operation_type(op : AST::Operation) - "#{operation_args(op)} => Promise<#{operation_ret(op)}>" - end - - def get_field_from_json_object(t : AST::Type, src : String, name : String) - case t - when AST::StringPrimitiveType, AST::DatePrimitiveType, AST::DateTimePrimitiveType, AST::BytesPrimitiveType - "#{src}.getString(#{name})" - when AST::IntPrimitiveType, AST::UIntPrimitiveType - "#{src}.getInt(#{name})" - when AST::FloatPrimitiveType - "#{src}.getDouble(#{name})" - when AST::BoolPrimitiveType - "#{src}.getBoolean(#{name})" - when AST::VoidPrimitiveType - "#{src}.get(#{name})" - when AST::OptionalType - "#{src}.isNull(#{name}) ? null : #{get_field_from_json_object(t.base, src, name)}" - when AST::ArrayType - "#{src}.getJSONArray(#{name})" - when AST::CustomTypeReference - "#{src}.getJSONObject(#{name})" - else - raise "Unknown type" - end - end - - def type_from_json(t : AST::Type, src : String) - case t - when AST::StringPrimitiveType, AST::IntPrimitiveType, AST::UIntPrimitiveType, AST::FloatPrimitiveType, AST::BoolPrimitiveType - "#{src}" - when AST::DatePrimitiveType - "Internal.decodeDate(#{src})" - when AST::DateTimePrimitiveType - "Internal.decodeDateTime(#{src})" - when AST::BytesPrimitiveType - "Base64.decode(#{src}, Base64.DEFAULT)" - when AST::VoidPrimitiveType - "null" - when AST::OptionalType - "#{src} == null ? null : #{type_from_json(t.base, src)}" - when AST::ArrayType - "new #{native_type t}() {{ JSONArray ary = #{src}; for (int i = 0; i < ary.length(); ++i) add(#{type_from_json(t.base, get_field_from_json_object(t.base, "ary", "i"))}); }}" - when AST::CustomTypeReference - ct = @ast.custom_types.find {|x| x.name == t.name }.not_nil! - "#{ct.name}.fromJSON(#{src})" - else - raise "Unknown type" - end - end - - def type_to_json(t : AST::Type, src : String) - case t - when AST::StringPrimitiveType, AST::IntPrimitiveType, AST::UIntPrimitiveType, AST::FloatPrimitiveType, AST::BoolPrimitiveType - "#{src}" - when AST::DatePrimitiveType - "Internal.encodeDate(#{src})" - when AST::DateTimePrimitiveType - "Internal.encodeDateTime(#{src})" - when AST::BytesPrimitiveType - "Base64.encodeToString(#{src}, Base64.DEFAULT)" - when AST::VoidPrimitiveType - "null" - when AST::OptionalType - "#{src} == null ? null : #{type_to_json(t.base, src)}" - when AST::ArrayType - "new JSONArray() {{ for (#{native_type t.base} el : #{src}) put(#{type_to_json t.base, "el"}); }}" - when AST::CustomTypeReference - "#{src}.toJSON()" - else - raise "Unknown type" - end - end -end diff --git a/target_java_android.cr b/target_java_android.cr deleted file mode 100644 index 5c57594..0000000 --- a/target_java_android.cr +++ /dev/null @@ -1,355 +0,0 @@ -require "./target_java" - -class JavaAndroidTarget < JavaTarget - def gen - @io << <<-END - -import android.app.Application; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.os.Build; -import android.provider.Settings; -import android.util.Base64; -import android.util.Log; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.math.BigInteger; -import java.security.SecureRandom; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.TimeZone; - -import okhttp3.Call; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; - -public class API { - -END - - @ast.custom_types.each do |custom_type| - @io << ident generate_custom_type_interface(custom_type) - @io << "\n\n" - end - - @ast.operations.each do |op| - args = op.args.map {|arg| "final #{native_type arg.type} #{arg.name}" } - args << "final #{callback_type op.return_type} callback" - @io << ident(String.build do |io| - io << "static public void #{op.fnName}(#{args.join(", ")}) {\n" - io << ident(String.build do |io| - if op.args.size == 0 - io << "JSONObject args = new JSONObject();" - else - io << <<-END -JSONObject args; -try { - args = new JSONObject() {{ - -END - op.args.each do |arg| - io << ident ident "put(\"#{arg.name}\", #{type_to_json arg.type, arg.name});\n" - end - io << <<-END - }}; -} catch (JSONException e) { - e.printStackTrace(); - callback.onFinished(); - callback.onError("bug", e.getMessage()); - return; -} -END - end - io << <<-END - -Internal.makeRequest(#{op.fnName.inspect}, args, new Internal.RequestCallback() { - @Override - public void onResult(final JSONObject result) { - callback.onFinished(); - -END - if op.return_type.is_a? AST::VoidPrimitiveType - io << <<-END - callback.onResult(); - -END - else - io << <<-END - try { - callback.onResult(#{type_from_json op.return_type, get_field_from_json_object(op.return_type, "result", "result".inspect)}); - } catch (JSONException e) { - e.printStackTrace(); - callback.onError("bug", e.getMessage()); - } -END - end - io << <<-END - } - - @Override - public void onError(String type, String message) { - callback.onFinished(); - callback.onError(type, message); - } - - @Override - public void onFailure(String message) { - callback.onFinished(); - callback.onError("Connection", message); - } -}); -END - end) - io << "}" - end) - @io << "\n\n" - end - - @io << <<-END - - public interface Callback { - void onFinished(); - void onResult(T result); - void onError(String type, String message); - } - - public interface IntCallback { - void onFinished(); - void onResult(int result); - void onError(String type, String message); - } - - public interface DoubleCallback { - void onFinished(); - void onResult(double result); - void onError(String type, String message); - } - - public interface BooleanCallback { - void onFinished(); - void onResult(boolean result); - void onError(String type, String message); - } - - public interface VoidCallback { - void onFinished(); - void onResult(); - void onError(String type, String message); - } - - private static class Internal { - private static final String baseUrl = #{@ast.options.url.inspect}; - private static final OkHttpClient http = new OkHttpClient(); - private static final SecureRandom random = new SecureRandom(); - - private static Context context() { - try { - final Class activityThreadClass = - Class.forName("android.app.ActivityThread"); - final Method method = activityThreadClass.getMethod("currentApplication"); - Application app = (Application)method.invoke(null, (Object[]) null); - if (app == null) - throw new RuntimeException(""); - return app; - } catch (final ClassNotFoundException | NoSuchMethodException | - IllegalArgumentException | InvocationTargetException | IllegalAccessException e) { - throw new RuntimeException(""); - } - } - - private static String language() { - Locale loc = Locale.getDefault(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return loc.toLanguageTag(); - } - - final char SEP = '-'; - String language = loc.getLanguage(); - String region = loc.getCountry(); - String variant = loc.getVariant(); - - if (language.equals("no") && region.equals("NO") && variant.equals("NY")) { - language = "nn"; - region = "NO"; - variant = ""; - } - - if (language.isEmpty() || !language.matches("\\\\p{Alpha}{2,8}")) { - language = "und"; - } else if (language.equals("iw")) { - language = "he"; - } else if (language.equals("in")) { - language = "id"; - } else if (language.equals("ji")) { - language = "yi"; - } - - if (!region.matches("\\\\p{Alpha}{2}|\\\\p{Digit}{3}")) { - region = ""; - } - - if (!variant.matches("\\\\p{Alnum}{5,8}|\\\\p{Digit}\\\\p{Alnum}{3}")) { - variant = ""; - } - - StringBuilder bcp47Tag = new StringBuilder(language); - if (!region.isEmpty()) { - bcp47Tag.append(SEP).append(region); - } - if (!variant.isEmpty()) { - bcp47Tag.append(SEP).append(variant); - } - - return bcp47Tag.toString(); - } - - private static JSONObject device() throws JSONException { - JSONObject device = new JSONObject(); - device.put("platform", "android"); - device.put("fingerprint", "" + Settings.Secure.ANDROID_ID); - device.put("platformVersion", "Android " + Build.VERSION.RELEASE + "(API " + Build.VERSION.SDK_INT + ") on " + Build.BRAND + " " + Build.MODEL); - try { - device.put("version", context().getPackageManager().getPackageInfo(context().getPackageName(), 0).versionName); - } catch (PackageManager.NameNotFoundException e) { - device.put("version", "unknown"); - } - device.put("language", language()); - SharedPreferences pref = context().getSharedPreferences("api", Context.MODE_PRIVATE); - if (pref.contains("deviceId")) - device.put("id", pref.getString("deviceId", null)); - return device; - } - - private static String randomBytesHex(int len) { - String str = new BigInteger(8 * len, random).toString(16); - while (str.length() < len) str = "0" + str; - return str; - } - - private interface RequestCallback { - void onResult(JSONObject result); - void onError(String type, String message); - void onFailure(String message); - } - - private static void makeRequest(String name, JSONObject args, final RequestCallback callback) { - JSONObject body = new JSONObject(); - try { - body.put("id", randomBytesHex(16)); - body.put("device", device()); - body.put("name", name); - body.put("args", args); - } catch (JSONException e) { - e.printStackTrace(); - callback.onError("bug", e.getMessage()); - } - - Request request = new Request.Builder() - .url("https://" + baseUrl + "/" + name) - .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), body.toString())) - .build(); - - http.newCall(request).enqueue(new okhttp3.Callback() { - @Override - public void onFailure(Call call, IOException e) { - e.printStackTrace(); - callback.onFailure(e.getMessage()); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - if (response.code() >= 500) { - Log.e("API Fatal", response.body().string()); - callback.onFailure("HTTP " + response.code()); - return; - } - - try { - JSONObject body = new JSONObject(response.body().string()); - if (!body.getBoolean("ok")) { - String type = body.getJSONObject("error").getString("type"); - String message = body.getJSONObject("error").getString("message"); - callback.onError(type, message); - } else { - callback.onResult(body); - } - } catch (JSONException e) { - e.printStackTrace(); - callback.onError("bug", e.getMessage()); - } - } - }); - } - - static SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US); - static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.US); - - static { - dateTimeFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - } - - static Calendar toCalendar(Date date){ - Calendar cal = Calendar.getInstance(); - cal.setTime(date); - return cal; - } - - static Calendar decodeDateTime(String str) { - try { - return toCalendar(dateTimeFormat.parse(str)); - } catch (ParseException e) { - e.printStackTrace(); - return null; - } - } - - static Calendar decodeDate(String str) { - try { - return toCalendar(dateFormat.parse(str)); - } catch (ParseException e) { - e.printStackTrace(); - return null; - } - } - - static String encodeDateTime(Calendar cal) { - return dateTimeFormat.format(cal.getTime()); - } - - static String encodeDate(Calendar cal) { - return dateFormat.format(cal.getTime()); - } - } -} -END - end - - def callback_type(t : AST::Type) - case t - when AST::IntPrimitiveType; "IntCallback" - when AST::UIntPrimitiveType; "IntCallback" - when AST::FloatPrimitiveType; "DoubleCallback" - when AST::BoolPrimitiveType; "BooleanCallback" - when AST::VoidPrimitiveType; "VoidCallback" - else - "Callback<#{native_type_not_primitive(t)}>" - end - end -end - -Target.register(JavaAndroidTarget, language: "java", is_server: false) diff --git a/target_typescript.cr b/target_typescript.cr deleted file mode 100644 index 7e64f92..0000000 --- a/target_typescript.cr +++ /dev/null @@ -1,124 +0,0 @@ -require "./target" - -abstract class TypeScriptTarget < Target - def native_type(t : AST::PrimitiveType) - case t - when AST::StringPrimitiveType; "string" - when AST::IntPrimitiveType; "number" - when AST::UIntPrimitiveType; "number" - when AST::FloatPrimitiveType; "number" - when AST::DatePrimitiveType; "Date" - when AST::DateTimePrimitiveType; "Date" - when AST::BoolPrimitiveType; "boolean" - when AST::BytesPrimitiveType; "Buffer" - when AST::VoidPrimitiveType; "null" - else - raise "BUG! Should handle primitive #{t.class}" - end - end - - def native_type(t : AST::OptionalType) - native_type(t.base) + " | null" - end - - def native_type(t : AST::ArrayType) - native_type(t.base) + "[]" - end - - def native_type(t : AST::CustomTypeReference) - t.name - end - - def generate_custom_type_interface(custom_type) - String.build do |io| - io << "export interface #{custom_type.name} {\n" - custom_type.fields.each do |field| - io << ident "#{field.name}: #{native_type field.type};\n" - end - io << "}" - end - end - - def operation_ret(op : AST::GetOperation | AST::FunctionOperation) - op.return_type.is_a?(AST::VoidPrimitiveType) ? "void" : native_type op.return_type - end - - def operation_ret(op : AST::SubscribeOperation) - "null" - end - - def operation_args(op : AST::Operation) - args = op.args.map {|arg| "#{arg.name}: #{native_type arg.type}" } - if op.is_a? SubscribeOperation - args << "callback: (result: #{native_type op.return_type}) => null" - end - - "(#{args.join(", ")})" - end - - def operation_type(op : AST::Operation) - "#{operation_args(op)} => Promise<#{operation_ret(op)}>" - end - - def type_from_json(t : AST::Type, src : String) - case t - when AST::StringPrimitiveType, AST::IntPrimitiveType, AST::UIntPrimitiveType, AST::FloatPrimitiveType, AST::BoolPrimitiveType - "#{src}" - when AST::DatePrimitiveType - "moment(#{src}, \"YYYY-MM-DD\").toDate()" - when AST::DateTimePrimitiveType - "moment.utc(#{src}, \"YYYY-MM-DDTHH:mm:ss.SSS\").toDate()" - when AST::BytesPrimitiveType - "Buffer.from(#{src}, \"base64\")" - when AST::VoidPrimitiveType - "undefined" - when AST::OptionalType - "#{src} === null || #{src} === undefined ? null : #{type_from_json(t.base, src)}" - when AST::ArrayType - t.base.is_a?(AST::CustomTypeReference) ? "#{src}.map(e => (#{type_from_json(t.base, "e")}))" : "#{src}.map(e => #{type_from_json(t.base, "e")})" - when AST::CustomTypeReference - String::Builder.build do |io| - io << "{\n" - ct = @ast.custom_types.find {|x| x.name == t.name }.not_nil! - ct.fields.each do |field| - io << ident "#{field.name}: #{type_from_json(field.type, "#{src}.#{field.name}")}," - io << "\n" - end - io << "}" - end - else - raise "Unknown type" - end - end - - def type_to_json(t : AST::Type, src : String) - case t - when AST::StringPrimitiveType, AST::IntPrimitiveType, AST::UIntPrimitiveType, AST::FloatPrimitiveType, AST::BoolPrimitiveType - "#{src}" - when AST::DatePrimitiveType - "moment(#{src}).format(\"YYYY-MM-DD\")" - when AST::DateTimePrimitiveType - "moment.utc(#{src}).format(\"YYYY-MM-DDTHH:mm:ss.SSS\")" - when AST::BytesPrimitiveType - "#{src}.toString(\"base64\")" - when AST::VoidPrimitiveType - "null" - when AST::OptionalType - "#{src} === null || #{src} === undefined ? null : #{type_to_json(t.base, src)}" - when AST::ArrayType - t.base.is_a?(AST::CustomTypeReference) ? "#{src}.map(e => (#{type_to_json(t.base, "e")}))" : "#{src}.map(e => #{type_to_json(t.base, "e")})" - when AST::CustomTypeReference - String::Builder.build do |io| - io << "{\n" - ct = @ast.custom_types.find {|x| x.name == t.name }.not_nil! - ct.fields.each do |field| - io << ident "#{field.name}: #{type_to_json(field.type, "#{src}.#{field.name}")}," - io << "\n" - end - io << "}" - end - else - raise "Unknown type" - end - end -end diff --git a/target_typescript_server.cr b/target_typescript_server.cr deleted file mode 100644 index 7c6ca60..0000000 --- a/target_typescript_server.cr +++ /dev/null @@ -1,244 +0,0 @@ -require "./target_typescript" - -class TypeScriptServerTarget < TypeScriptTarget - def gen - @io << <<-END -import http from "http"; -import crypto from "crypto"; -import os from "os"; -import moment from "moment"; -import r from "../rethinkdb"; - - -END - - @io << "export const fn: {\n" - @ast.operations.each do |op| - @io << " " << op.fnName << ": " << operation_type(op) << ";\n" - end - @io << "} = {\n"; - @ast.operations.each do |op| - @io << " " << op.fnName << ": () => { throw \"not implemented\"; },\n" - end - @io << "};\n\n" - - @ast.custom_types.each do |custom_type| - @io << generate_custom_type_interface(custom_type) - @io << "\n\n" - end - - @io << "const fnExec: {[name: string]: (ctx: Context, args: any) => Promise} = {\n" - @ast.operations.each do |op| - @io << " " << op.fnName << ": async (ctx: Context, args: any) => {\n" - op.args.each do |arg| - @io << ident ident "const #{arg.name} = #{type_from_json(arg.type, "args.#{arg.name}")};" - @io << "\n" - end - @io << " const ret = await fn.#{op.fnName}(#{(["ctx"] + op.args.map(&.name)).join(", ")});\n" - @io << ident ident "return " + type_to_json(op.return_type, "ret") + ";" - @io << "\n" - @io << " },\n" - end - @io << "};\n\n" - - @io << "export const err = {\n" - @ast.errors.each do |error| - @io << " #{error}: (message: string = \"\") => { throw {type: #{error.inspect}, message}; },\n" - end - @io << "};\n\n" - - @io << <<-END -////////////////////////////////////////////////////// - -export interface Context { - device: DBDevice; - startTime: Date; -} - -function sleep(ms: number) { - return new Promise(resolve => setTimeout(resolve, ms)); -} - -export function start(port: number) { - const server = http.createServer((req, res) => { - req.on("error", (err) => { - console.error(err); - }); - - res.on("error", (err) => { - console.error(err); - }); - - res.setHeader("Access-Control-Allow-Origin", "*"); - res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); - res.setHeader("Access-Control-Allow-Headers", "Content-Type"); - res.setHeader("Access-Control-Max-Age", "86400"); - - switch (req.method) { - case "GET": { - res.writeHead(200); - res.write(`{"ok": true}`); - res.end(); - break; - } - case "POST": { - let data = ""; - req.on("data", chunk => data += chunk.toString()); - req.on("end", () => { - try { - const request = JSON.parse(data); - const context: Context = { - device: request.device, - startTime: new Date - }; - const startTime = process.hrtime(); - - (async () => { - const deviceInfo = { - platform: request.device.platform, - platformVersion: request.device.platformVersion, - version: request.device.version, - language: request.device.language - }; - - if (!context.device.id) { - context.device.id = crypto.randomBytes(32).toString("hex"); - - await r.table("devices").insert({ - id: context.device.id, - ...deviceInfo - }); - } else { - await r.table("devices").get(context.device.id).update(deviceInfo); - } - - const executionId = crypto.randomBytes(32).toString("hex"); - - let call: DBApiCall = { - id: `${context.device.id}_${request.id}`, - name: request.name, - args: request.args, - executionId: executionId, - running: true, - device: context.device, - date: context.startTime, - duration: 0, - host: os.hostname(), - ok: true, - result: null as any, - error: null as {type: string, message: string}|null - }; - - async function tryLock(): Promise { - const priorCall = await r.table("api_calls").get(call.id); - if (priorCall === null) { - await r.table("api_calls").insert(call); - return await tryLock(); - } - if (!priorCall.running) { - call = priorCall; - return true; - } - if (priorCall.executionId === executionId) { - return true; - } - return false; - } - - for (let i = 0; i < 30; ++i) { - if (tryLock()) break; - await sleep(100); - } - - if (call.running) { - if (call.executionId !== executionId) { - call.ok = false; - call.error = { - type: "CallExecutionTimeout", - message: "Timeout while waiting for execution somewhere else" - }; - } else { - try { - call.result = await fnExec[call.name](context, call.args); - } catch (err) { - call.ok = false; - if (err.type) { - call.error = { - type: err.type, - message: err.message - }; - } else { - call.error = { - type: "bug", - message: err.toString() - }; - } - } - } - } - - const deltaTime = process.hrtime(startTime); - call.duration = deltaTime[0] + deltaTime[1] * 1e-9; - - const response = { - id: call.id, - ok: call.ok, - executed: call.executionId === executionId, - deviceId: call.device.id, - startTime: call.date, - duration: call.duration, - host: call.host, - result: call.result, - error: call.error - }; - - console.log({ - request, - response - }); - - res.writeHead(200); - res.write(JSON.stringify(response)); - res.end(); - - await r.table("api_calls").get(call.id).update(call); - })().catch(err => { - console.error(err); - res.writeHead(500); - res.end(); - }); - } catch (err) { - console.error(err); - res.writeHead(400); - res.end(); - } - }); - break; - } - default: { - res.writeHead(400); - res.end(); - } - } - }); - - server.listen(port, () => { - console.log(`Listening on ${server.address().address}:${server.address().port}`); - }); -} - - -END - end - - def operation_args(op : AST::Operation) - args = ["ctx: Context"] + op.args.map {|arg| "#{arg.name}: #{native_type arg.type}" } - if op.is_a? SubscribeOperation - args << "callback: (result: #{native_type op.return_type}) => void" - end - - "(#{args.join(", ")})" - end -end - -Target.register(TypeScriptServerTarget, language: "ts", is_server: true) diff --git a/target_typescript_web.cr b/target_typescript_web.cr deleted file mode 100644 index 59233d9..0000000 --- a/target_typescript_web.cr +++ /dev/null @@ -1,98 +0,0 @@ -require "./target_typescript" - -class TypeScriptWebTarget < TypeScriptTarget - def gen - @io << <<-END -import * as moment from "moment"; -import {UAParser} from "ua-parser-js"; - -const baseUrl = #{@ast.options.url.inspect}; - - -END - - @ast.custom_types.each do |custom_type| - @io << generate_custom_type_interface(custom_type) - @io << "\n\n" - end - - @ast.operations.each do |op| - @io << "export async function #{op.fnName}#{operation_args(op)}: Promise<#{operation_ret(op)}> {\n" - if op.args.size > 0 - @io << " const args = {\n" - op.args.each do |arg| - @io << ident ident "#{arg.name}: #{type_to_json(arg.type, arg.name)}," - @io << "\n" - end - @io << " };\n" - end - - @io << " " - @io << "const ret: #{native_type op.return_type} = " unless op.return_type.is_a? AST::VoidPrimitiveType - @io << "await makeRequest({name: #{op.fnName.inspect}, #{op.args.size > 0 ? "args" : "args: {}"}});\n" - @io << ident "return " + type_from_json(op.return_type, "ret") + ";" - @io << "\n" - @io << "}\n\n" - end - - @io << <<-END -////////////////////////////////////////////////////// - -function device() { - const parser = new UAParser(); - parser.setUA(navigator.userAgent); - const agent = parser.getResult(); - const me = document.currentScript as HTMLScriptElement; - const device: any = { - platform: "web", - platformVersion: `${agent.browser.name} ${agent.browser.version} on ${agent.os.name} ${agent.os.version}`, - version: me ? me.src : "", - language: navigator.language - }; - const deviceId = localStorage.getItem("deviceId"); - if (deviceId) - device.id = deviceId; - return device; -} - -function randomBytesHex(len: number) { - let hex = ""; - for (let i = 0; i < 2 * len; ++i) - hex += "0123456789abcdef"[Math.floor(Math.random()*16)]; - return hex; -} - -async function makeRequest({name, args}: {name: string, args: any}) { - return new Promise((resolve, reject) => { - const req = new XMLHttpRequest(); - req.open("POST", "https://" + baseUrl + "/" + name); - const body = { - id: randomBytesHex(16), - device: device(), - name: name, - args: args - }; - req.onreadystatechange = () => { - if (req.readyState !== 4) return; - try { - const response = JSON.parse(req.responseText); - localStorage.setItem("deviceId", response.deviceId); - if (response.ok) { - resolve(response.result); - } else { - reject(response.error); - } - } catch (e) { - console.error(e); - reject({type: "ServerError", message: e.toString()}); - } - }; - req.send(JSON.stringify(body)); - }); -} - -END - end -end - -Target.register(TypeScriptWebTarget, language: "ts", is_server: false) diff --git a/test.api b/test.api deleted file mode 100644 index 7e8f9f7..0000000 --- a/test.api +++ /dev/null @@ -1,36 +0,0 @@ - -// primitive types: int, uint, float, string, bytes, date, datetime, bool - -$db = "test" - -type Image { - url: string - width: int - height: int -} - -type User { - name: string - avatar: Image -} - -type Message { - date: date - message: string - image: bytes? -} - -type CardData { - cardNumber: string !secret - holder: string - month: uint - year: uint - ccv: string !secret -} - -get currentUser(): User? -get lastMessage(user: User): Message -function logIn(user: string, pass: string !secret): User - -// subscribe messages(since: date): Message - diff --git a/website/blog/2016-03-11-blog-post.md b/website/blog/2016-03-11-blog-post.md new file mode 100755 index 0000000..cf2ba29 --- /dev/null +++ b/website/blog/2016-03-11-blog-post.md @@ -0,0 +1,18 @@ +--- +title: Blog Title +author: Blog Author +authorURL: http://twitter.com/ +authorFBID: 100002976521003 +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien. + + + +Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut. + +Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra. + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum. + +Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis. diff --git a/website/blog/2017-04-10-blog-post-two.md b/website/blog/2017-04-10-blog-post-two.md new file mode 100644 index 0000000..3ab4637 --- /dev/null +++ b/website/blog/2017-04-10-blog-post-two.md @@ -0,0 +1,18 @@ +--- +title: New Blog Post +author: Blog Author +authorURL: http://twitter.com/ +authorFBID: 100002976521003 +--- + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus elementum massa eget nulla aliquet sagittis. Proin odio tortor, vulputate ut odio in, ultrices ultricies augue. Cras ornare ultrices lorem malesuada iaculis. Etiam sit amet libero tempor, pulvinar mauris sed, sollicitudin sapien. + + + +Mauris vestibulum ullamcorper nibh, ut semper purus pulvinar ut. Donec volutpat orci sit amet mauris malesuada, non pulvinar augue aliquam. Vestibulum ultricies at urna ut suscipit. Morbi iaculis, erat at imperdiet semper, ipsum nulla sodales erat, eget tincidunt justo dui quis justo. Pellentesque dictum bibendum diam at aliquet. Sed pulvinar, dolor quis finibus ornare, eros odio facilisis erat, eu rhoncus nunc dui sed ex. Nunc gravida dui massa, sed ornare arcu tincidunt sit amet. Maecenas efficitur sapien neque, a laoreet libero feugiat ut. + +Nulla facilisi. Maecenas sodales nec purus eget posuere. Sed sapien quam, pretium a risus in, porttitor dapibus erat. Sed sit amet fringilla ipsum, eget iaculis augue. Integer sollicitudin tortor quis ultricies aliquam. Suspendisse fringilla nunc in tellus cursus, at placerat tellus scelerisque. Sed tempus elit a sollicitudin rhoncus. Nulla facilisi. Morbi nec dolor dolor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras et aliquet lectus. Pellentesque sit amet eros nisi. Quisque ac sapien in sapien congue accumsan. Nullam in posuere ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacinia leo a nibh fringilla pharetra. + +Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin venenatis lectus dui, vel ultrices ante bibendum hendrerit. Aenean egestas feugiat dui id hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur in tellus laoreet, eleifend nunc id, viverra leo. Proin vulputate non dolor vel vulputate. Curabitur pretium lobortis felis, sit amet finibus lorem suscipit ut. Sed non mollis risus. Duis sagittis, mi in euismod tincidunt, nunc mauris vestibulum urna, at euismod est elit quis erat. Phasellus accumsan vitae neque eu placerat. In elementum arcu nec tellus imperdiet, eget maximus nulla sodales. Curabitur eu sapien eget nisl sodales fermentum. + +Phasellus pulvinar ex id commodo imperdiet. Praesent odio nibh, sollicitudin sit amet faucibus id, placerat at metus. Donec vitae eros vitae tortor hendrerit finibus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque vitae purus dolor. Duis suscipit ac nulla et finibus. Phasellus ac sem sed dui dictum gravida. Phasellus eleifend vestibulum facilisis. Integer pharetra nec enim vitae mattis. Duis auctor, lectus quis condimentum bibendum, nunc dolor aliquam massa, id bibendum orci velit quis magna. Ut volutpat nulla nunc, sed interdum magna condimentum non. Sed urna metus, scelerisque vitae consectetur a, feugiat quis magna. Donec dignissim ornare nisl, eget tempor risus malesuada quis. diff --git a/website/blog/2017-09-25-testing-rss.md b/website/blog/2017-09-25-testing-rss.md new file mode 100644 index 0000000..b7ff812 --- /dev/null +++ b/website/blog/2017-09-25-testing-rss.md @@ -0,0 +1,11 @@ +--- +title: Adding RSS Support - RSS Truncation Test +author: Eric Nakagawa +authorURL: http://twitter.com/ericnakagawa +authorFBID: 661277173 +--- +1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 + +This should be truncated. + +This line should never render in XML. diff --git a/website/blog/2017-09-26-adding-rss.md b/website/blog/2017-09-26-adding-rss.md new file mode 100644 index 0000000..eeb4f04 --- /dev/null +++ b/website/blog/2017-09-26-adding-rss.md @@ -0,0 +1,10 @@ +--- +title: Adding RSS Support +author: Eric Nakagawa +authorURL: http://twitter.com/ericnakagawa +authorFBID: 661277173 +--- + +This is a test post. + +A whole bunch of other information. diff --git a/website/blog/2017-10-24-new-version-1.0.0.md b/website/blog/2017-10-24-new-version-1.0.0.md new file mode 100644 index 0000000..60761c0 --- /dev/null +++ b/website/blog/2017-10-24-new-version-1.0.0.md @@ -0,0 +1,8 @@ +--- +title: New Version 1.0.0 +author: Eric Nakagawa +authorURL: http://twitter.com/ericnakagawa +authorFBID: 661277173 +--- + +This blog post will test file name parsing issues when periods are present. diff --git a/website/core/Footer.js b/website/core/Footer.js new file mode 100644 index 0000000..b43f1d6 --- /dev/null +++ b/website/core/Footer.js @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require('react'); + +class Footer extends React.Component { + docUrl(doc, language) { + const baseUrl = this.props.config.baseUrl; + return baseUrl + 'docs/' + (language ? language + '/' : '') + doc; + } + + pageUrl(doc, language) { + const baseUrl = this.props.config.baseUrl; + return baseUrl + (language ? language + '/' : '') + doc; + } + + render() { + const currentYear = new Date().getFullYear(); + return ( + + ); + } +} + +module.exports = Footer; diff --git a/website/package.json b/website/package.json new file mode 100644 index 0000000..6b6365d --- /dev/null +++ b/website/package.json @@ -0,0 +1,14 @@ +{ + "scripts": { + "examples": "docusaurus-examples", + "start": "docusaurus-start", + "build": "docusaurus-build", + "publish-gh-pages": "docusaurus-publish", + "write-translations": "docusaurus-write-translations", + "version": "docusaurus-version", + "rename-version": "docusaurus-rename-version" + }, + "devDependencies": { + "docusaurus": "^1.3.1" + } +} diff --git a/website/pages/en/help.js b/website/pages/en/help.js new file mode 100755 index 0000000..6434d77 --- /dev/null +++ b/website/pages/en/help.js @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require('react'); + +const CompLibrary = require('../../core/CompLibrary.js'); +const Container = CompLibrary.Container; +const GridBlock = CompLibrary.GridBlock; + +const siteConfig = require(process.cwd() + '/siteConfig.js'); + +function docUrl(doc, language) { + return siteConfig.baseUrl + 'docs/' + (language ? language + '/' : '') + doc; +} + +class Help extends React.Component { + render() { + let language = this.props.language || ''; + const supportLinks = [ + { + content: `Learn more using the [documentation on this site.](${docUrl( + 'doc1.html', + language + )})`, + title: 'Browse Docs', + }, + { + content: 'Ask questions about the documentation and project', + title: 'Join the community', + }, + { + content: "Find out what's new with this project", + title: 'Stay up to date', + }, + ]; + + return ( +
+ +
+
+

Need help?

+
+

This project is maintained by a dedicated group of people.

+ +
+
+
+ ); + } +} + +module.exports = Help; diff --git a/website/pages/en/index.js b/website/pages/en/index.js new file mode 100755 index 0000000..d6db81e --- /dev/null +++ b/website/pages/en/index.js @@ -0,0 +1,219 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require('react'); + +const CompLibrary = require('../../core/CompLibrary.js'); +const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */ +const Container = CompLibrary.Container; +const GridBlock = CompLibrary.GridBlock; + +const siteConfig = require(process.cwd() + '/siteConfig.js'); + +function imgUrl(img) { + return siteConfig.baseUrl + 'img/' + img; +} + +function docUrl(doc, language) { + return siteConfig.baseUrl + 'docs/' + (language ? language + '/' : '') + doc; +} + +function pageUrl(page, language) { + return siteConfig.baseUrl + (language ? language + '/' : '') + page; +} + +class Button extends React.Component { + render() { + return ( + + ); + } +} + +Button.defaultProps = { + target: '_self', +}; + +const SplashContainer = props => ( +
+
+
{props.children}
+
+
+); + +const Logo = props => ( +
+ +
+); + +const ProjectTitle = props => ( +

+ {siteConfig.title} + {siteConfig.tagline} +

+); + +const PromoSection = props => ( +
+
+
{props.children}
+
+
+); + +class HomeSplash extends React.Component { + render() { + let language = this.props.language || ''; + return ( + + +
+ + + + + + +
+
+ ); + } +} + +const Block = props => ( + + + +); + +const Features = props => ( + + {[ + { + content: 'This is the content of my feature', + image: imgUrl('docusaurus.svg'), + imageAlign: 'top', + title: 'Feature One', + }, + { + content: 'The content of my second feature', + image: imgUrl('docusaurus.svg'), + imageAlign: 'top', + title: 'Feature Two', + }, + ]} + +); + +const FeatureCallout = props => ( +
+

Feature Callout

+ These are features of this project +
+); + +const LearnHow = props => ( + + {[ + { + content: 'Talk about learning how to use this', + image: imgUrl('docusaurus.svg'), + imageAlign: 'right', + title: 'Learn How', + }, + ]} + +); + +const TryOut = props => ( + + {[ + { + content: 'Talk about trying this out', + image: imgUrl('docusaurus.svg'), + imageAlign: 'left', + title: 'Try it Out', + }, + ]} + +); + +const Description = props => ( + + {[ + { + content: 'This is another description of how this project is useful', + image: imgUrl('docusaurus.svg'), + imageAlign: 'right', + title: 'Description', + }, + ]} + +); + +const Showcase = props => { + if ((siteConfig.users || []).length === 0) { + return null; + } + const showcase = siteConfig.users + .filter(user => { + return user.pinned; + }) + .map((user, i) => { + return ( + + {user.caption} + + ); + }); + + return ( +
+

{"Who's Using This?"}

+

This project is used by all these people

+
{showcase}
+ +
+ ); +}; + +class Index extends React.Component { + render() { + let language = this.props.language || ''; + + return ( +
+ +
+ + + + + + +
+
+ ); + } +} + +module.exports = Index; diff --git a/website/pages/en/users.js b/website/pages/en/users.js new file mode 100644 index 0000000..c113bf5 --- /dev/null +++ b/website/pages/en/users.js @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require('react'); + +const CompLibrary = require('../../core/CompLibrary.js'); +const Container = CompLibrary.Container; + +const siteConfig = require(process.cwd() + '/siteConfig.js'); + +class Users extends React.Component { + render() { + if ((siteConfig.users || []).length === 0) { + return null; + } + const editUrl = siteConfig.repoUrl + '/edit/master/website/siteConfig.js'; + const showcase = siteConfig.users.map((user, i) => { + return ( + + {user.caption} + + ); + }); + + return ( +
+ +
+
+

Who's Using This?

+

This project is used by many folks

+
+
{showcase}
+

Are you using this project?

+ + Add your company + +
+
+
+ ); + } +} + +module.exports = Users; diff --git a/website/sidebars.json b/website/sidebars.json new file mode 100644 index 0000000..7a1d054 --- /dev/null +++ b/website/sidebars.json @@ -0,0 +1,10 @@ +{ + "docs": { + "Docusaurus": ["doc1"], + "First Category": ["doc2"], + "Second Category": ["doc3"] + }, + "docs-other": { + "First Category": ["doc4", "doc5"] + } +} diff --git a/website/siteConfig.js b/website/siteConfig.js new file mode 100644 index 0000000..87d9144 --- /dev/null +++ b/website/siteConfig.js @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// See https://docusaurus.io/docs/site-config.html for all the possible +// site configuration options. + +/* List of projects/orgs using your project for the users page */ +const users = [ + { + caption: 'User1', + // You will need to prepend the image path with your baseUrl + // if it is not '/', like: '/test-site/img/docusaurus.svg'. + image: '/img/docusaurus.svg', + infoLink: 'https://www.facebook.com', + pinned: true, + }, +]; + +const siteConfig = { + title: 'sdkgen' /* title for your website */, + tagline: 'Integrate all plataforms in a blink', + url: 'https://cubos.github.io' /* your website url */, + baseUrl: '/sdkgen/' /* base url for your project */, + // For github.io type URLs, you would set the url and baseUrl like: + // url: 'https://facebook.github.io', + // baseUrl: '/test-site/', + + // Used for publishing and more + projectName: 'sdkgen', + organizationName: 'cubos', + // For top-level user or org sites, the organization is still the same. + // e.g., for the https://JoelMarcey.github.io site, it would be set like... + // organizationName: 'JoelMarcey' + + // For no header links in the top nav bar -> headerLinks: [], + headerLinks: [ + {doc: 'doc1', label: 'Docs'}, + {doc: 'doc4', label: 'API'}, + {page: 'help', label: 'Help'}, + {blog: true, label: 'Blog'}, + ], + + // If you have users set above, you add it here: + users, + + /* path to images for header/footer */ + headerIcon: 'img/docusaurus.svg', + footerIcon: 'img/docusaurus.svg', + favicon: 'img/favicon.png', + + /* colors for website */ + colors: { + primaryColor: '#2E8555', + secondaryColor: '#205C3B', + }, + + /* custom fonts for website */ + /*fonts: { + myFont: [ + "Times New Roman", + "Serif" + ], + myOtherFont: [ + "-apple-system", + "system-ui" + ] + },*/ + + // This copyright info is used in /core/Footer.js and blog rss/atom feeds. + copyright: + 'Copyright © ' + + new Date().getFullYear() + + ' Your Name or Your Company Name', + + highlight: { + // Highlight.js theme to use for syntax highlighting in code blocks + theme: 'default', + }, + + // Add custom scripts here that would be placed in