From 8a688ad80563dcf4460a466086c33f153c618ff2 Mon Sep 17 00:00:00 2001 From: Daniel Rutkovsky Date: Thu, 8 Jun 2023 11:02:11 +0200 Subject: [PATCH] Initial version of swift generator --- .gitignore | 12 + LICENCE.md | 20 + README.md | 46 +- Tests/Package.swift | 27 + Tests/Tests/gen-swiftTests/tests.swift | 236 +++++++++ Tests/download.sh | 20 + Tests/test.ridl | 73 +++ Tests/test.sh | 13 + _examples/ClientExample/.gitignore | 2 + _examples/ClientExample/Makefile | 21 + _examples/ClientExample/Package.swift | 19 + _examples/ClientExample/README.md | 17 + .../Sources/ClientExample/main.swift | 19 + _examples/ClientExample/server/index.ts | 41 ++ _examples/ClientExample/server/package.json | 17 + _examples/ClientExample/server/tsconfig.json | 61 +++ _examples/ClientExample/server/yarn.lock | 473 ++++++++++++++++++ _examples/ClientExample/service.ridl | 35 ++ _examples/Makefile | 9 + client.go.tmpl | 123 +++++ clientHelpers.go.tmpl | 57 +++ codingKey.go.tmpl | 19 + fieldName.go.tmpl | 13 + help.go.tmpl | 6 + main.go.tmpl | 80 +++ methodInputs.go.tmpl | 13 + methodOutputs.go.tmpl | 19 + type.go.tmpl | 24 + types.go.tmpl | 73 +++ 29 files changed, 1587 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 LICENCE.md create mode 100644 Tests/Package.swift create mode 100644 Tests/Tests/gen-swiftTests/tests.swift create mode 100755 Tests/download.sh create mode 100644 Tests/test.ridl create mode 100755 Tests/test.sh create mode 100644 _examples/ClientExample/.gitignore create mode 100644 _examples/ClientExample/Makefile create mode 100644 _examples/ClientExample/Package.swift create mode 100644 _examples/ClientExample/README.md create mode 100644 _examples/ClientExample/Sources/ClientExample/main.swift create mode 100644 _examples/ClientExample/server/index.ts create mode 100644 _examples/ClientExample/server/package.json create mode 100644 _examples/ClientExample/server/tsconfig.json create mode 100644 _examples/ClientExample/server/yarn.lock create mode 100644 _examples/ClientExample/service.ridl create mode 100644 _examples/Makefile create mode 100644 client.go.tmpl create mode 100644 clientHelpers.go.tmpl create mode 100644 codingKey.go.tmpl create mode 100644 fieldName.go.tmpl create mode 100644 help.go.tmpl create mode 100644 main.go.tmpl create mode 100644 methodInputs.go.tmpl create mode 100644 methodOutputs.go.tmpl create mode 100644 type.go.tmpl create mode 100644 types.go.tmpl diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aaf9511 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.DS_Store +/.build +/Packages +/*.xcodeproj +xcuserdata/ +DerivedData/ +.netrc +.build +.swiftpm +Package.resolved +client.swift +node_modules \ No newline at end of file diff --git a/LICENCE.md b/LICENCE.md new file mode 100644 index 0000000..eebc93d --- /dev/null +++ b/LICENCE.md @@ -0,0 +1,20 @@ +Copyright (c) 2019-present https://github.com/webrpc authors + +MIT License + +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. \ No newline at end of file diff --git a/README.md b/README.md index ecdac4e..a42e7d9 100644 --- a/README.md +++ b/README.md @@ -1 +1,45 @@ -# gen-swift \ No newline at end of file +# gen-swift + +This repo contains the templates used by the `webrpc-gen` cli to code-generate +webrpc Swift client code. + +This generator, from a webrpc schema/design file will code-generate: + +1. Client -- a Swift client to speak to a webrpc server using the +provided schema. This client is compatible with any webrpc server language (ie. Go, nodejs, etc.). + +## Dependencies + +In order to support `any` type in webrpc, we use [AnyCodable](https://github.com/Flight-School/AnyCodable). +This is a dependency of the generated code, so you must add it to your project. + +## Usage + +``` +webrpc-gen -schema=example.ridl -target=swift -server -client -out=./example.gen.swift +``` + +or + +``` +webrpc-gen -schema=example.ridl -target=github.com/webrpc/gen-swift@v0.11.2 -server -client -out=./example.get.swift +``` + +or + +``` +webrpc-gen -schema=example.ridl -target=./local-templates-on-disk -server -client -out=./example.gen.swift +``` + +As you can see, the `-target` supports default `swift`, any git URI, or a local folder :) + +### Set custom template variables +Change any of the following values by passing `-option="Value"` CLI flag to `webrpc-gen`. + +| webrpc-gen -option | Description | Default value | +|----------------------|----------------------------|----------------------------| +| `-client` | generate client code | unset (`false`) | + +## LICENSE + +[MIT LICENSE](./LICENSE) \ No newline at end of file diff --git a/Tests/Package.swift b/Tests/Package.swift new file mode 100644 index 0000000..c365dc4 --- /dev/null +++ b/Tests/Package.swift @@ -0,0 +1,27 @@ +// swift-tools-version: 5.8 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "gen-swift", + platforms: [.macOS(.v13), .iOS(.v15)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "gen-swift", + targets: ["gen-swift"]), + + ], + dependencies: [.package(url: "https://github.com/Flight-School/AnyCodable", from: "0.6.7")], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "gen-swift", + dependencies: ["AnyCodable"]), + .testTarget( + name: "gen-swiftTests", + dependencies: ["gen-swift"]), + ] +) diff --git a/Tests/Tests/gen-swiftTests/tests.swift b/Tests/Tests/gen-swiftTests/tests.swift new file mode 100644 index 0000000..8106bab --- /dev/null +++ b/Tests/Tests/gen-swiftTests/tests.swift @@ -0,0 +1,236 @@ +import XCTest +@testable import gen_swift + +final class gen_swiftTests: XCTestCase { + + private let client = TestApiClient(hostname: "http://localhost:9988") + + func testEmpty() async throws { + await XCTAssertNoThrow(try await client.getEmpty(), "getEmpty() should get empty type successfully") + } + + func testError() async throws { + await XCTAssertThrowsError(try await client.getError(), "getError() should throw error") + } + + func testOne() async throws { + let response = try await client.getOne() + await XCTAssertNoThrow(try await client.sendOne(one: response), + "getOne() should receive simple type and send it back via sendOne() successfully") + + } + + func testMulti() async throws { + let (one, two, three) = try await client.getMulti() + await XCTAssertNoThrow(try await client.sendMulti(one: one, two: two, three: three), + "getMulti() should receive simple type and send it back via sendMulti() successfully") + } + + func testComplex() async throws { + let response = try await client.getComplex() + await XCTAssertNoThrow(try await client.sendComplex(complex: response), + "getComplex() should receive complex type and send it back via sendComplex() successfully") + } + + func testCustomErrors() async throws { + let errors: [WebrpcError] = [ + .init( + error: "WebrpcEndpoint", + code: 0, + message: "endpoint error", + cause: "failed to read file: unexpected EOF", + status: 400, + errorKind: .webrpcEndpointError + ), + .init( + error: "Unauthorized", + code: 1, + message: "unauthorized", + cause: "failed to verify JWT token", + status: 401, + errorKind: .unauthorizedError + ), + .init( + error: "ExpiredToken", + code: 2, + message: "expired token", + cause: nil, + status: 401, + errorKind: .expiredTokenError + ), + .init( + error: "InvalidToken", + code: 3, + message: "invalid token", + cause: nil, + status: 401, + errorKind: .invalidTokenError + ), + .init( + error: "Deactivated", + code: 4, + message: "account deactivated", + cause: nil, + status: 403, + errorKind: .deactivatedError + ), + .init( + error: "ConfirmAccount", + code: 5, + message: "confirm your email", + cause: nil, + status: 403, + errorKind: .confirmAccountError + ), + .init( + error: "AccessDenied", + code: 6, + message: "access denied", + cause: nil, + status: 403, + errorKind: .accessDeniedError + ), + .init( + error: "MissingArgument", + code: 7, + message: "missing argument", + cause: nil, + status: 400, + errorKind: .missingArgumentError + ), + .init( + error: "UnexpectedValue", + code: 8, + message: "unexpected value", + cause: nil, + status: 400, + errorKind: .unexpectedValueError + ), + .init( + error: "RateLimited", + code: 100, + message: "too many requests", + cause: "1000 req/min exceeded", + status: 429, + errorKind: .rateLimitedError + ), + .init( + error: "DatabaseDown", + code: 101, + message: "service outage", + cause: nil, + status: 503, + errorKind: .databaseDownError + ), + .init( + error: "ElasticDown", + code: 102, + message: "search is degraded", + cause: nil, + status: 503, + errorKind: .elasticDownError + ), + .init( + error: "NotImplemented", + code: 103, + message: "not implemented", + cause: nil, + status: 501, + errorKind: .notImplementedError + ), + .init( + error: "UserNotFound", + code: 200, + message: "user not found", + cause: nil, + status: 400, + errorKind: .userNotFoundError + ), + .init( + error: "UserBusy", + code: 201, + message: "user busy", + cause: nil, + status: 400, + errorKind: .userBusyError + ), + .init( + error: "InvalidUsername", + code: 202, + message: "invalid username", + cause: nil, + status: 400, + errorKind: .invalidUsernameError + ), + .init( + error: "FileTooBig", + code: 300, + message: "file is too big (max 1GB)", + cause: nil, + status: 400, + errorKind: .fileTooBigError + ), + .init( + error: "FileInfected", + code: 301, + message: "file is infected", + cause: nil, + status: 400, + errorKind: .fileInfectedError + ), + .init( + error: "FileType", + code: 302, + message: "unsupported file type", + cause: ".wav is not supported", + status: 400, + errorKind: .fileTypeError + ) + ] + for error in errors { + do { + try await client.getSchemaError(code: error.code) + XCTFail("Expected to throw \(error)") + } catch let err as WebrpcError { + XCTAssertEqual(error.code, err.code) + XCTAssertEqual(error.error, err.error) + XCTAssertEqual(error.message, err.message) + XCTAssertEqual(error.status, err.status) + XCTAssertEqual(error.cause, err.cause) + XCTAssertEqual(error.kind, err.kind) + } catch let err { + XCTFail("Expected to throw \(error) but got \(err) instead") + } + } + } +} + +extension XCTest { + func XCTAssertThrowsError( + _ expression: @autoclosure () async throws -> T, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line, + _ errorHandler: (_ error: Error) -> Void = { _ in } + ) async { + do { + _ = try await expression() + XCTFail(message(), file: file, line: line) + } catch { + errorHandler(error) + } + } + + func XCTAssertNoThrow( + _ expression: @autoclosure () async throws -> T, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line + ) async { + do { + _ = try await expression() + } catch { + XCTFail(message(), file: file, line: line) + } + } +} diff --git a/Tests/download.sh b/Tests/download.sh new file mode 100755 index 0000000..96b715c --- /dev/null +++ b/Tests/download.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -e + +VERSION="${1}" +DIR="${2}" +[[ -z "$VERSION" || -z "$DIR" ]] && { echo "Usage: $0 "; exit 1; } + +mkdir -p "$DIR" + +# Download webrpc binaries if not available locally +OS="$(basename $(uname -o | tr A-Z a-z))" +ARCH="$(uname -m | sed 's/x86_64/amd64/')" +if [[ ! -f "$DIR/webrpc-gen" ]]; then + curl -o "$DIR/webrpc-gen" -fLJO "https://github.com/webrpc/webrpc/releases/download/$VERSION/webrpc-gen.$OS-$ARCH" + chmod +x "$DIR/webrpc-gen" +fi +if [[ ! -f "$DIR/webrpc-test" ]]; then + curl -o "$DIR/webrpc-test" -fLJO "https://github.com/webrpc/webrpc/releases/download/$VERSION/webrpc-test.$OS-$ARCH" + chmod +x "$DIR/webrpc-test" +fi diff --git a/Tests/test.ridl b/Tests/test.ridl new file mode 100644 index 0000000..326b7bf --- /dev/null +++ b/Tests/test.ridl @@ -0,0 +1,73 @@ +webrpc = v1 + +name = Test +version = v0.10.0 + +service TestApi + - GetEmpty() + - GetError() + + - GetOne() => (one: Simple) + - SendOne(one: Simple) + + - GetMulti() => (one: Simple, two: Simple, three: Simple) + - SendMulti(one: Simple, two: Simple, three: Simple) + + - GetComplex() => (complex: Complex) + - SendComplex(complex: Complex) + + # added in v0.11.0 + - GetSchemaError(code: int) + +struct Simple + - id: int + - name: string + +struct User + - id: uint64 + + json = id + + go.field.name = ID + + go.tag.db = id + + - username: string + + json = USERNAME + + go.tag.db = username + + - role: string + + go.tag.db = - + +enum Status: uint32 + - AVAILABLE + - NOT_AVAILABLE + +struct Complex + - meta: map + - metaNestedExample: map> + - namesList: []string + - numsList: []int64 + - doubleArray: [][]string + - listOfMaps: []map + - listOfUsers: []User + - mapOfUsers: map + - user: User + - enum: Status + +error 1 Unauthorized "unauthorized" HTTP 401 +error 2 ExpiredToken "expired token" HTTP 401 +error 3 InvalidToken "invalid token" HTTP 401 +error 4 Deactivated "account deactivated" HTTP 403 +error 5 ConfirmAccount "confirm your email" HTTP 403 +error 6 AccessDenied "access denied" HTTP 403 +error 7 MissingArgument "missing argument" HTTP 400 +error 8 UnexpectedValue "unexpected value" HTTP 400 +error 100 RateLimited "too many requests" HTTP 429 +error 101 DatabaseDown "service outage" HTTP 503 +error 102 ElasticDown "search is degraded" HTTP 503 +error 103 NotImplemented "not implemented" HTTP 501 +error 200 UserNotFound "user not found" +error 201 UserBusy "user busy" +error 202 InvalidUsername "invalid username" +error 300 FileTooBig "file is too big (max 1GB)" +error 301 FileInfected "file is infected" +error 302 FileType "unsupported file type" + diff --git a/Tests/test.sh b/Tests/test.sh new file mode 100755 index 0000000..f296ceb --- /dev/null +++ b/Tests/test.sh @@ -0,0 +1,13 @@ +#!/bin/bash +set -e + +webrpc-test -version +webrpc-test -print-schema > ./test.ridl +webrpc-gen -schema=./test.ridl -target=../ -client -out=./Sources/gen-swift/client.swift + +webrpc-test -server -port=9988 -timeout=5s & + +# Wait until http://localhost:9988 is available, up to 10s. +for (( i=0; i<100; i++ )); do nc -z localhost 9988 && break || sleep 0.1; done + +swift test diff --git a/_examples/ClientExample/.gitignore b/_examples/ClientExample/.gitignore new file mode 100644 index 0000000..3b987e5 --- /dev/null +++ b/_examples/ClientExample/.gitignore @@ -0,0 +1,2 @@ +*.gen.ts +node_modules \ No newline at end of file diff --git a/_examples/ClientExample/Makefile b/_examples/ClientExample/Makefile new file mode 100644 index 0000000..ed190e8 --- /dev/null +++ b/_examples/ClientExample/Makefile @@ -0,0 +1,21 @@ +all: + @awk -F'[ :]' '!/^all:/ && /^([A-z_-]+):/ {print "make " $$1}' Makefile + +bootstrap: + rm -rf server/node_modules + cd server && yarn + +generate: generate-server generate-client + +generate-server: + webrpc-gen -schema=service.ridl -target=typescript -server -out=./server/server.gen.ts + +generate-client: + webrpc-gen -schema=service.ridl -target=../../ -client -out=./Sources/ClientExample/client.swift + swift build -c release + +run-server: + yarn --cwd ./server start + +run-client: + ./.build/release/ClientExample \ No newline at end of file diff --git a/_examples/ClientExample/Package.swift b/_examples/ClientExample/Package.swift new file mode 100644 index 0000000..cc31b05 --- /dev/null +++ b/_examples/ClientExample/Package.swift @@ -0,0 +1,19 @@ +// swift-tools-version: 5.8 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "ClientExample", + platforms: [.macOS(.v13), .iOS(.v15)], + products: [ + .executable(name: "ClientExample", targets: ["ClientExample"]), + + ], + dependencies: [.package(url: "https://github.com/Flight-School/AnyCodable", from: "0.6.7")], + targets: [ + .executableTarget( + name: "ClientExample", + dependencies: ["AnyCodable"]), + ] +) diff --git a/_examples/ClientExample/README.md b/_examples/ClientExample/README.md new file mode 100644 index 0000000..616093a --- /dev/null +++ b/_examples/ClientExample/README.md @@ -0,0 +1,17 @@ +webrpc node-ts +============== + +* Server: Nodejs (TypeScript) +* Client: CLI (Swift) + +example of generating a webrpc client from [service.ridl](./service.ridl) schema. + +## Usage + +1. Install nodejs, yarn, swift and webrpc-gen +1. $ `make bootstrap` -- runs yarn on ./server +1. $ `make generate` -- generates both server and client code +1. $ `make run-server` +1. $ `make run-client` + +Or you can just open the Package.swift and run the client from Xcode. \ No newline at end of file diff --git a/_examples/ClientExample/Sources/ClientExample/main.swift b/_examples/ClientExample/Sources/ClientExample/main.swift new file mode 100644 index 0000000..323822e --- /dev/null +++ b/_examples/ClientExample/Sources/ClientExample/main.swift @@ -0,0 +1,19 @@ +import Foundation + +print("Startig client...") +print("WebRPCVersion: \(WebRPCVersion)") +print("WebRPCSchemaVersion: \(WebRPCSchemaVersion)") +print("WebRPCSchemaHash: \(WebRPCSchemaHash)") +print("") + +let client = ExampleServiceClient(hostname: "http://localhost:3000") + +do { + print("Sending ping...") + try await client.ping() + + print("Getting user 1...") + print(try await client.getUser(userID: 1)) +} catch { + print(error) +} \ No newline at end of file diff --git a/_examples/ClientExample/server/index.ts b/_examples/ClientExample/server/index.ts new file mode 100644 index 0000000..b5ee253 --- /dev/null +++ b/_examples/ClientExample/server/index.ts @@ -0,0 +1,41 @@ +import express from 'express' +import * as proto from './server.gen' +import { createExampleServiceApp } from './server.gen' + +const app = express() + +app.use((req, res, next) => { + res.setHeader('Access-Control-Allow-Origin', '*') + res.setHeader('Access-Control-Allow-Methods', 'POST, GET, OPTIONS') + res.setHeader('Access-Control-Allow-Headers', 'Content-Type') + + if (req.method === 'OPTIONS') { + res.status(200).end() + return + } + + next() +}) + +const exampleServiceApp = createExampleServiceApp({ + Ping: () => { + return {} + }, + + GetUser: () => ({ + code: 1, + user: { + id: 1, + USERNAME: 'webrpcfan', + role: proto.Kind.ADMIN, + meta: {}, + createdAt: new Date().toISOString(), + }, + }), +}) + +app.use(exampleServiceApp) + +app.listen(3000, () => { + console.log('> Listening on port 3000') +}) diff --git a/_examples/ClientExample/server/package.json b/_examples/ClientExample/server/package.json new file mode 100644 index 0000000..355e56f --- /dev/null +++ b/_examples/ClientExample/server/package.json @@ -0,0 +1,17 @@ +{ + "name": "node-ts-server", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "express": "^4.17.3" + }, + "devDependencies": { + "@types/express": "^4.16.1", + "ts-node": "^8.1.0", + "typescript": "^3.4.3" + }, + "scripts": { + "start": "./node_modules/.bin/ts-node ." + } +} \ No newline at end of file diff --git a/_examples/ClientExample/server/tsconfig.json b/_examples/ClientExample/server/tsconfig.json new file mode 100644 index 0000000..fae13d9 --- /dev/null +++ b/_examples/ClientExample/server/tsconfig.json @@ -0,0 +1,61 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */, + "lib": [ + "es2016" + ] /* Specify library files to be included in the compilation. */, + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + } +} diff --git a/_examples/ClientExample/server/yarn.lock b/_examples/ClientExample/server/yarn.lock new file mode 100644 index 0000000..5690b50 --- /dev/null +++ b/_examples/ClientExample/server/yarn.lock @@ -0,0 +1,473 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/body-parser@*": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c" + integrity sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.32" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28" + integrity sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg== + dependencies: + "@types/node" "*" + +"@types/express-serve-static-core@*": + version "4.16.4" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.4.tgz#56bb8be4559401d68af4a3624ae9dd3166103e60" + integrity sha512-x/8h6FHm14rPWnW2HP5likD/rsqJ3t/77OWx2PLxym0hXbeBWQmcPyHmwX+CtCQpjIfgrUdEoDFcLPwPZWiqzQ== + dependencies: + "@types/node" "*" + "@types/range-parser" "*" + +"@types/express@^4.16.1": + version "4.16.1" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.1.tgz#d756bd1a85c34d87eaf44c888bad27ba8a4b7cf0" + integrity sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "*" + "@types/serve-static" "*" + +"@types/mime@*": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" + integrity sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw== + +"@types/node@*": + version "12.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.0.0.tgz#d11813b9c0ff8aaca29f04cbc12817f4c7d656e5" + integrity sha512-Jrb/x3HT4PTJp6a4avhmJCDEVrPdqLfl3e8GGMbpkGGdwAV5UGlIs4vVEfsHHfylZVOKZWpOqmqFH8CbfOZ6kg== + +"@types/range-parser@*": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" + integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== + +"@types/serve-static@*": + version "1.13.2" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48" + integrity sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q== + dependencies: + "@types/express-serve-static-core" "*" + "@types/mime" "*" + +accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +arg@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0" + integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +body-parser@1.19.2: + version "1.19.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e" + integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "1.8.1" + iconv-lite "0.4.24" + on-finished "~2.3.0" + qs "6.9.7" + raw-body "2.4.3" + type-is "~1.6.18" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +diff@^3.1.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +express@^4.17.3: + version "4.17.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" + integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.19.2" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.4.2" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "~1.1.2" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.9.7" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.17.2" + serve-static "1.14.2" + setprototypeof "1.2.0" + statuses "~1.5.0" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +finalhandler@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +http-errors@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" + integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== + dependencies: + depd "~1.1.2" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses ">= 1.5.0 < 2" + toidentifier "1.0.1" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +inherits@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +make-error@^1.1.1: + version "1.3.5" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" + integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@~2.1.24: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + dependencies: + mime-db "1.40.0" + +mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +qs@6.9.7: + version "6.9.7" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" + integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== + +range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c" + integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g== + dependencies: + bytes "3.1.2" + http-errors "1.8.1" + iconv-lite "0.4.24" + unpipe "1.0.0" + +safe-buffer@5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +send@0.17.2: + version "0.17.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820" + integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "1.8.1" + mime "1.6.0" + ms "2.1.3" + on-finished "~2.3.0" + range-parser "~1.2.1" + statuses "~1.5.0" + +serve-static@1.14.2: + version "1.14.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa" + integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.17.2" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +source-map-support@^0.5.6: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +ts-node@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.1.0.tgz#8c4b37036abd448577db22a061fd7a67d47e658e" + integrity sha512-34jpuOrxDuf+O6iW1JpgTRDFynUZ1iEqtYruBqh35gICNjN8x+LpVcPAcwzLPi9VU6mdA3ym+x233nZmZp445A== + dependencies: + arg "^4.1.0" + diff "^3.1.0" + make-error "^1.1.1" + source-map-support "^0.5.6" + yn "^3.0.0" + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typescript@^3.4.3: + version "3.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99" + integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + +yn@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.0.tgz#fcbe2db63610361afcc5eb9e0ac91e976d046114" + integrity sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg== diff --git a/_examples/ClientExample/service.ridl b/_examples/ClientExample/service.ridl new file mode 100644 index 0000000..a121481 --- /dev/null +++ b/_examples/ClientExample/service.ridl @@ -0,0 +1,35 @@ +webrpc = v1 + +name = node-ts +version = v1.0.0 + + +enum Kind: uint32 + - USER + - ADMIN + + +struct User + - ID: uint64 + + json = id + + - username: string + + json = USERNAME + + - role: Kind + + - meta: map + + - internalID: uint64 + + json = - + + - createdAt?: timestamp + + +struct Page + - num: uint32 + + +service ExampleService + - Ping() => () + - GetUser(userID: uint64) => (code: uint32, user: User) diff --git a/_examples/Makefile b/_examples/Makefile new file mode 100644 index 0000000..65d719e --- /dev/null +++ b/_examples/Makefile @@ -0,0 +1,9 @@ +.PHONY: all generate diff + +all: generate diff + +generate: + cd ClientExample && make generate + +diff: + git diff --color --ignore-all-space --ignore-blank-lines --exit-code . \ No newline at end of file diff --git a/client.go.tmpl b/client.go.tmpl new file mode 100644 index 0000000..294d272 --- /dev/null +++ b/client.go.tmpl @@ -0,0 +1,123 @@ +{{define "client"}} + +{{- $typeMap := .TypeMap -}} + +{{- if .Services -}} +// MARK: - Client + +{{- range $service := .Services}} +{{- range $_, $method := $service.Methods -}} +{{- if gt (len $method.Inputs) 0 -}} +private struct {{$method.Name}}Request: Codable { +{{- range $_, $input := $method.Inputs}} + let {{$input.Name}}: {{template "type" dict "Type" $input.Type "TypeMap" $typeMap}}{{if $input.Optional}}?{{end}} +{{- end}} +} +{{- end -}} + +{{- if gt (len $method.Outputs) 0 }} +private struct {{$method.Name}}Response: Codable { +{{- range $_, $output := $method.Outputs}} + let {{$output.Name}}: {{template "type" dict "Type" $output.Type "TypeMap" $typeMap}}{{if $output.Optional}}?{{end}} +{{- end}} +} +{{end}} +{{end -}} + +public class {{.Name}}Client { + private let hostname: String + private let apiPath = "/rpc/{{.Name}}/" + private let urlSession: URLSession + + private lazy var jsonDecoder: JSONDecoder = { + let decoder = JSONDecoder() + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'" + decoder.dateDecodingStrategy = .formatted(dateFormatter) + return decoder + }() + + private struct Empty: Codable {} + + public init(hostname: String, urlSession: URLSession = URLSession.shared) { + self.hostname = hostname + self.urlSession = urlSession + } + + private func getURL(for service: String) -> String { + "\(hostname)\(apiPath)\(service)" + } + + private func sendRequest(url: String, headers: [String: String]?, data: Data? = nil) async throws { + let _: Empty? = try await sendRequest(url: url, headers: headers, data: data) + } + + @discardableResult + private func sendRequest( + url: String, + headers: [String: String]?, + data: Data? = nil) async throws -> T { + var request = URLRequest(url: URL(string: url)!) + request.httpMethod = "POST" + headers?.forEach { + request.setValue($0.value, forHTTPHeaderField: $0.key) + } + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.httpBody = data + let (data, response) = try await URLSession.shared.data(for: request) + + guard let httpResponse = response as? HTTPURLResponse else { + throw WebrpcError(error: "Unknown error", code: -999, message: nil, cause: nil, status: -999, errorKind: .unknownError) + } + + switch httpResponse.statusCode { + case 200..<300: + let decoded = try jsonDecoder.decode(T.self, from: data) + return decoded + default: + if let decoded = try? jsonDecoder.decode(WebrpcError.self, from: data) { + throw decoded + } + + throw WebrpcError(error: "Unknown error", code: -999, message: nil, cause: nil, status: httpResponse.statusCode, errorKind: .unknownError) + } + } + } + +extension {{.Name}}Client: {{.Name}} { + {{- range $_, $method := .Methods}} + {{ $generateHeaders := "false" -}} + public func {{firstLetterToLower $method.Name}}({{template "methodInputs" dict "Method" $method "TypeMap" $typeMap "GenerateHeaders" $generateHeaders}}) async throws {{- if gt (len $method.Outputs) 0}}-> {{end}}{{template "methodOutputs" dict "Method" $method "TypeMap" $typeMap}} { + try await {{firstLetterToLower $method.Name}}( + {{- range $i, $input := $method.Inputs}} + {{- if $i}}, {{end}}{{$input.Name}}: {{$input.Name}} + {{- end -}} + {{- if (gt (len $method.Inputs) 0)}}, {{end}}headers: nil) + } + + {{$generateHeaders := "true" -}} + public func {{firstLetterToLower $method.Name}}({{template "methodInputs" dict "Method" $method "TypeMap" $typeMap "GenerateHeaders" $generateHeaders}}) async throws {{- if gt (len $method.Outputs) 0}}-> {{end}}{{template "methodOutputs" dict "Method" $method "TypeMap" $typeMap}} { + {{- if (gt (len $method.Inputs) 0)}} + let request = {{$method.Name}}Request( + {{- range $i, $input := $method.Inputs}} + {{- if $i}}, {{end}}{{$input.Name}}: {{$input.Name}} + {{- end -}} + ) + {{- end}} + {{if (gt (len $method.Outputs) 0)}}let response: {{$method.Name}}Response = {{ end -}}try await sendRequest(url: getURL(for: "{{.Name}}"), headers: headers{{ternary (gt (.Inputs | len) 0) ", data: try JSONEncoder().encode(request)" "" -}}) + {{- if (eq (len $method.Outputs) 1)}} + return {{range $i, $output := $method.Outputs -}} + response.{{$output.Name}} + {{- end -}} + {{- end}} + {{- if (gt (len $method.Outputs) 1)}} + return ({{- range $i, $output := $method.Outputs}} + {{- if $i}}, {{end}}{{$output.Name}}: response.{{$output.Name}} + {{- end -}}) + {{- end}} + } + {{end}} +} +{{end -}} +{{end -}} +{{end}} diff --git a/clientHelpers.go.tmpl b/clientHelpers.go.tmpl new file mode 100644 index 0000000..feefac1 --- /dev/null +++ b/clientHelpers.go.tmpl @@ -0,0 +1,57 @@ +{{define "clientHelpers"}} +{{- $webrpcErrors := .WebrpcErrors -}} +{{- $schemaErrors := .SchemaErrors -}} + +// MARK: - Errors + +public struct WebrpcError: Codable, Error { + let error: String + let code: Int + let message: String? + let cause: String? + let status: Int + let kind: ErrorKind + + enum CodingKeys: String, CodingKey { + case error + case code + case message = "msg" + case cause + case status + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + error = try container.decode(String.self, forKey: .error) + code = try container.decode(Int.self, forKey: .code) + message = try? container.decodeIfPresent(String.self, forKey: .message) + cause = try? container.decodeIfPresent(String.self, forKey: .cause) + status = try container.decode(Int.self, forKey: .status) + + if let errorKind = ErrorKind(rawValue: code) { + kind = errorKind + } else { + kind = .unknownError + } + } + + internal init(error: String, code: Int, message: String?, cause: String?, status: Int, errorKind: ErrorKind) { + self.error = error + self.code = code + self.message = message + self.cause = cause + self.status = status + self.kind = errorKind + } + + public enum ErrorKind: Int { + {{- range $_, $error := $webrpcErrors}} + case {{camelCase $error.Name}}Error = {{$error.Code}} + {{- end }} + {{- range $_, $error := $schemaErrors}} + case {{camelCase $error.Name}}Error = {{$error.Code}} + {{- end }} + case unknownError = -999 + } +} +{{end}} diff --git a/codingKey.go.tmpl b/codingKey.go.tmpl new file mode 100644 index 0000000..f38b85c --- /dev/null +++ b/codingKey.go.tmpl @@ -0,0 +1,19 @@ +{{- define "codingKey" -}} +{{- /* Prints field name while prioritizing "json" field meta */ -}} + +{{- $field := .Field -}} +{{- $fieldName := $field.Name -}} + +{{- range $meta := $field.Meta -}} + {{- if exists $meta "json" -}} + {{- $fieldName = get $meta "json" | split "," | first -}} + {{- end -}} +{{- end -}} + +{{- if in (toLower $fieldName) "associatedtype" "class" "deinit" "enum" "extension" "fileprivate" "func" "import" "init" "inout" "internal" "let" "open" "operator" "private" "precedencegroup" "protocol" "public" "rethrows" "static" "struct" "subscript" "typealias" "var" "break" "case" "catch" "continue" "default" "defer" "do" "else" "fallthrough" "for" "guard" "if" "in" "repeat" "return" "throw" "switch" "where" "while" "Any" "as" "await" "catch" "false" "is" "nil" "rethrows" "self" "Self" "super" "throw" "throws" "true" "try" -}} + {{- $fieldName := (printf "`%v`" $fieldName) -}} +{{- end -}} + +{{- $fieldName -}} + +{{- end -}} \ No newline at end of file diff --git a/fieldName.go.tmpl b/fieldName.go.tmpl new file mode 100644 index 0000000..6fee5cd --- /dev/null +++ b/fieldName.go.tmpl @@ -0,0 +1,13 @@ +{{- define "fieldName" -}} + +{{- $field := .Field -}} +{{- $fieldName := $field.Name -}} + +{{- if in $fieldName "associatedtype" "class" "deinit" "enum" "extension" "fileprivate" "func" "import" "init" "inout" "internal" "let" "open" "operator" "private" "precedencegroup" "protocol" "public" "rethrows" "static" "struct" "subscript" "typealias" "var" "break" "case" "catch" "continue" "default" "defer" "do" "else" "fallthrough" "for" "guard" "if" "in" "repeat" "return" "throw" "switch" "where" "while" "Any" "as" "await" "catch" "false" "is" "nil" "rethrows" "self" "Self" "super" "throw" "throws" "true" "try" -}} + {{- (printf "`%v`" $fieldName) -}} +{{- else -}} + {{- (camelCase $fieldName) -}} +{{- end -}} + +{{- end -}} + diff --git a/help.go.tmpl b/help.go.tmpl new file mode 100644 index 0000000..d799f05 --- /dev/null +++ b/help.go.tmpl @@ -0,0 +1,6 @@ +{{- define "help" -}} + {{- range $k, $v := . -}} + {{- stderrPrintf " -%v=%v\n" $k $v -}} + {{- end -}} + {{- stderrPrintf "See https://github.com/webrpc/gen-swift for more info.\n" -}} +{{- end -}} diff --git a/main.go.tmpl b/main.go.tmpl new file mode 100644 index 0000000..f48f4b5 --- /dev/null +++ b/main.go.tmpl @@ -0,0 +1,80 @@ +{{- define "main" -}} + +{{- /* Options with default values. */ -}} +{{- $opts := dict -}} +{{- set $opts "client" (ternary (in .Opts.client "" "true") true false) -}} +{{- set $opts "server" (ternary (in .Opts.server "" "true") true false) -}} + +{{- /* Print help on -help. */ -}} +{{- if exists .Opts "help" -}} + {{- template "help" $opts -}} + {{- exit 0 -}} +{{- end -}} + +{{- /* Print help on unsupported option. */ -}} +{{- range $k, $v := .Opts }} + {{- if not (exists $opts $k) -}} + {{- stderrPrintf "-%v=%q is not supported target option\n\nUsage:\n" $k $v -}} + {{- template "help" $opts -}} + {{- exit 1 -}} + {{- end -}} +{{- end -}} + +{{- if ne .WebrpcVersion "v1" -}} + {{- stderrPrintf "%s generator error: unsupported webrpc version %s\n" .WebrpcTarget .WebrpcVersion -}} + {{- exit 1 -}} +{{- end -}} + +{{- if not (minVersion .WebrpcGenVersion "v0.7.0") -}} + {{- stderrPrintf "%s generator error: unsupported webrpc-gen version %s, please update\n" .WebrpcTarget .WebrpcGenVersion -}} + {{- exit 1 -}} +{{- end -}} + +{{- /* Map webrpc core types to JS. */ -}} +{{- $typeMap := dict }} +{{- set $typeMap "null" "nil" -}} +{{- set $typeMap "any" "AnyCodable" -}} +{{- set $typeMap "byte" "String" -}} +{{- set $typeMap "bool" "Bool" -}} +{{- set $typeMap "uint" "UInt" -}} +{{- set $typeMap "uint8" "UInt8" -}} +{{- set $typeMap "uint16" "UInt16" -}} +{{- set $typeMap "uint32" "UInt32" -}} +{{- set $typeMap "uint64" "UInt64" -}} +{{- set $typeMap "int" "Int" -}} +{{- set $typeMap "int8" "Int8" -}} +{{- set $typeMap "int16" "Int16" -}} +{{- set $typeMap "int32" "Int32" -}} +{{- set $typeMap "int64" "Int64" -}} +{{- set $typeMap "float32" "Float" -}} +{{- set $typeMap "float64" "Double" -}} +{{- set $typeMap "string" "String" -}} +{{- set $typeMap "timestamp" "Date" -}} + +/* swiftlint:disable */ +// {{.SchemaName}} {{.SchemaVersion}} {{.SchemaHash}} +// -- +// Code generated by webrpc-gen@{{.WebrpcGenVersion}} with {{.WebrpcTarget}} generator. DO NOT EDIT. +// +// {{.WebrpcGenCommand}} + +import AnyCodable +import Foundation + +// WebRPC description and code-gen version +public let WebRPCVersion = "{{.WebrpcVersion}}" + +// Schema version of your RIDL schema +public let WebRPCSchemaVersion = "{{.SchemaVersion}}" + +// Schema hash generated from your RIDL schema +public let WebRPCSchemaHash = "{{.SchemaHash}}" + +{{template "types" dict "Types" .Types "Services" .Services "TypeMap" $typeMap}} + +{{if $opts.client}} + {{- template "client" dict "Services" .Services "TypeMap" $typeMap}} + {{- template "clientHelpers" dict "WebrpcErrors" .WebrpcErrors "SchemaErrors" .Errors }} +{{- end}} + +{{- end}} diff --git a/methodInputs.go.tmpl b/methodInputs.go.tmpl new file mode 100644 index 0000000..a6e40cb --- /dev/null +++ b/methodInputs.go.tmpl @@ -0,0 +1,13 @@ +{{- define "methodInputs" -}} + +{{- $method := .Method -}} +{{- $typeMap := .TypeMap -}} +{{- $generateHeaders := .GenerateHeaders -}} + +{{- range $i, $input := $method.Inputs}} + {{- if $i}}, {{end}}{{$input.Name}}: {{template "type" dict "Type" $input.Type "TypeMap" $typeMap}}{{if .Optional}}?{{end}} +{{- end -}} +{{- if eq $generateHeaders "true" -}} +{{if (gt (len $method.Inputs) 0)}}, {{end}}headers: [String: String]? +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/methodOutputs.go.tmpl b/methodOutputs.go.tmpl new file mode 100644 index 0000000..a799ea4 --- /dev/null +++ b/methodOutputs.go.tmpl @@ -0,0 +1,19 @@ +{{- define "methodOutputs" -}} + +{{- $method := .Method -}} +{{- $typeMap := .TypeMap -}} + +{{- if eq (len $method.Outputs) 1 -}} +{{- range $i, $output := $method.Outputs -}} + {{template "type" dict "Type" $output.Type "TypeMap" $typeMap}} +{{- end -}} +{{- end -}} + +{{- if gt (len $method.Outputs) 1 -}} +( +{{- range $i, $output := $method.Outputs}} + {{- if $i}}, {{end}}{{$output.Name}}: {{template "type" dict "Type" $output.Type "TypeMap" $typeMap}} +{{- end -}} +) +{{- end -}} +{{- end}} \ No newline at end of file diff --git a/type.go.tmpl b/type.go.tmpl new file mode 100644 index 0000000..d980aef --- /dev/null +++ b/type.go.tmpl @@ -0,0 +1,24 @@ +{{- define "type" -}} + +{{- $type := .Type -}} +{{- $typeMap := .TypeMap -}} + +{{- if isMapType $type -}} + + [{{template "type" dict "Type" (mapKeyType $type) "TypeMap" $typeMap}}: {{template "type" dict "Type" (mapValueType $type) "TypeMap" $typeMap}}] + +{{- else if isListType $type -}} + + [{{template "type" dict "Type" (listElemType $type) "TypeMap" $typeMap}}] + + +{{- else if isCoreType $type -}} + + {{get $typeMap $type}} + +{{- else -}} + + {{$type}} + +{{- end -}} +{{- end -}} \ No newline at end of file diff --git a/types.go.tmpl b/types.go.tmpl new file mode 100644 index 0000000..e1aaf51 --- /dev/null +++ b/types.go.tmpl @@ -0,0 +1,73 @@ +/Users/danielrutkovsky/Developer/gen-typescript/types.go.tmpl{{define "types"}} + +{{- $typeMap := .TypeMap -}} +{{- $types := .Types -}} +{{- $services := .Services -}} + +// MARK: - Types + +{{- if $types -}} +{{range $_i, $type := $types -}} + +{{if isEnumType $type }} +public enum {{$type.Name}}: String, Codable { +{{- range $_, $field := $type.Fields}} + case {{camelCase $field.Name}} = "{{$field.Name}}" +{{- end}} +} +{{end -}} + +{{- if isStructType $type }} +public struct {{$type.Name}}: Codable { + {{- range $_, $field := $type.Fields}} + {{- $isExportable := true -}} + {{- range $meta := $field.Meta -}} + {{- if exists $meta "json" -}} + {{- if eq (printf "%v" (get $meta "json")) "-" -}} + {{- $isExportable = false}} + {{- end -}} + {{- end -}} + {{- end }} + {{- if $isExportable }} + let {{template "fieldName" dict "Field" .}}: {{template "type" dict "Type" $field.Type "TypeMap" $typeMap}}{{if .Optional}}?{{end}} + {{- end -}} + {{- end}} + + {{- if $type.Fields}} + + enum CodingKeys: String, CodingKey { + {{- range $_, $field := $type.Fields}} + {{- $isExportable := true -}} + {{- range $meta := $field.Meta -}} + {{- if exists $meta "json" -}} + {{- if eq (printf "%v" (get $meta "json")) "-" -}} + {{- $isExportable = false}} + {{- end -}} + {{- end -}} + {{- end }} + {{- if $isExportable }} + case {{template "fieldName" dict "Field" .}} = "{{template "codingKey" dict "Field" .}}" + {{- end -}} + {{- end}} + } + + {{- end}} +} +{{end -}} +{{end -}} +{{end -}} + +{{if $services}} +{{- range $_, $service := $services}} +public protocol {{$service.Name}} { +{{- range $_, $method := $service.Methods}} + {{ $generateHeaders := "false" -}} + func {{firstLetterToLower $method.Name}}({{template "methodInputs" dict "Method" $method "TypeMap" $typeMap "GenerateHeaders" $generateHeaders}}) async throws {{- if gt (len $method.Outputs) 0}}-> {{end}}{{template "methodOutputs" dict "Method" $method "TypeMap" $typeMap}} + {{ $generateHeaders := "true" -}} + func {{firstLetterToLower $method.Name}}({{template "methodInputs" dict "Method" $method "TypeMap" $typeMap "GenerateHeaders" $generateHeaders}}) async throws {{- if gt (len $method.Outputs) 0}}-> {{end}}{{template "methodOutputs" dict "Method" $method "TypeMap" $typeMap}} +{{- end }} +} +{{- end -}} +{{end -}} + +{{end}}