Socket
Socket
Sign inDemoInstall

@airtasker/spot

Package Overview
Dependencies
85
Maintainers
1
Versions
49
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.1.28 to 0.1.29

2

build/cli/src/commands/generate.js

@@ -73,3 +73,3 @@ "use strict";

Generate.examples = [
`$ api generate --language typescript --generator axios-client --out src/
`$ spot generate --language typescript --generator axios-client --out src/
Generated the following files:

@@ -76,0 +76,0 @@ - src/types.ts

@@ -51,3 +51,3 @@ "use strict";

Init.examples = [
`$ api init
`$ spot init
Generated the following files:

@@ -54,0 +54,0 @@ - api.ts

@@ -73,3 +73,3 @@ "use strict";

Generate.examples = [
`$ api generate --language typescript --generator axios-client --out src/
`$ spot generate --language typescript --generator axios-client --out src/
Generated the following files:

@@ -76,0 +76,0 @@ - src/types.ts

@@ -51,3 +51,3 @@ "use strict";

Init.examples = [
`$ api init
`$ spot init
Generated the following files:

@@ -54,0 +54,0 @@ - api.ts

@@ -44,2 +44,4 @@ "use strict";

};
case "date":
case "date-time":
case "string":

@@ -46,0 +48,0 @@ return {

import { Type } from "../../models";
import { OpenAPI3BaseSchemaType } from "./openapi3-schema";
export declare function rejectVoidOpenApi2SchemaType(type: Type, errorMessage: string): OpenAPI2SchemaType;
export declare function openApi2TypeSchema(type: Type): OpenAPI2SchemaType | null;
export declare type OpenAPI2SchemaType = OpenAPI2SchemaTypeObject | OpenAPI2SchemaTypeArray | OpenAPI2SchemaTypeAllOf | OpenAPI2SchemaTypeNull | OpenAPI2SchemaTypeString | OpenAPI2SchemaTypeNumber | OpenAPI2SchemaTypeInt | OpenAPI2SchemaTypeFloatDouble | OpenAPI2SchemaTypeInteger | OpenAPI2SchemaTypeBoolean | OpenAPI2SchemaTypeReference;
export declare type OpenAPI2SchemaType = OpenAPI2SchemaTypeObject | OpenAPI2SchemaTypeArray | OpenAPI2SchemaTypeAllOf | OpenAPI2SchemaTypeNull | OpenAPI2SchemaTypeString | OpenAPI2SchemaTypeDateTime | OpenAPI2SchemaTypeNumber | OpenAPI2SchemaTypeInt | OpenAPI2SchemaTypeFloatDouble | OpenAPI2SchemaTypeInteger | OpenAPI2SchemaTypeBoolean | OpenAPI2SchemaTypeReference;
export interface OpenAPI2BaseSchemaType {

@@ -42,7 +41,11 @@ discriminator?: {

}
export interface OpenAPI2SchemaTypeInt extends OpenAPI3BaseSchemaType {
export interface OpenAPI2SchemaTypeDateTime extends OpenAPI2BaseSchemaType {
type: "string";
format: "date" | "date-time";
}
export interface OpenAPI2SchemaTypeInt extends OpenAPI2BaseSchemaType {
type: "integer";
format: "int32" | "int64";
}
export interface OpenAPI2SchemaTypeFloatDouble extends OpenAPI3BaseSchemaType {
export interface OpenAPI2SchemaTypeFloatDouble extends OpenAPI2BaseSchemaType {
type: "number";

@@ -49,0 +52,0 @@ format: "float" | "double";

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const assert_never_1 = require("../../assert-never");
const compact = require("lodash/compact");
function rejectVoidOpenApi2SchemaType(type, errorMessage) {

@@ -12,2 +13,7 @@ const schemaType = openApi2TypeSchema(type);

exports.rejectVoidOpenApi2SchemaType = rejectVoidOpenApi2SchemaType;
function isStringConstantUnion(types) {
return types.reduce((acc, type) => {
return acc && type.kind === "string-constant";
}, true);
}
function openApi2TypeSchema(type) {

@@ -28,2 +34,12 @@ switch (type.kind) {

};
case "date":
return {
type: "string",
format: "date"
};
case "date-time":
return {
type: "string",
format: "date-time"
};
case "string":

@@ -97,2 +113,13 @@ return {

case "union":
const types = type.types.map(t => openApi2TypeSchema(t));
const withoutNullTypes = compact(types);
if (withoutNullTypes.length !== types.length) {
throw new Error(`Unsupported void type in union`);
}
if (isStringConstantUnion(type.types)) {
return {
type: "string",
enum: compact(type.types.map(t => (t.kind === "string-constant" ? t.value : null)))
};
}
// Please have a look at https://github.com/OAI/OpenAPI-Specification/issues/333

@@ -99,0 +126,0 @@ throw new Error(`Unions are not supported in OpenAPI 2`);

@@ -6,3 +6,3 @@ import { Type } from "../../models";

export declare function openApi3TypeSchema(type: Type): OpenAPI3SchemaType | null;
export declare type OpenAPI3SchemaType = OpenAPI3SchemaTypeObject | OpenAPI3SchemaTypeArray | OpenAPI3SchemaTypeOneOf | OpenAPI3SchemaTypeNull | OpenAPI3SchemaTypeString | OpenAPI3SchemaTypeNumber | OpenAPI3SchemaTypeInt | OpenAPI3SchemaTypeFloatDouble | OpenAPI3SchemaTypeInteger | OpenAPI3SchemaTypeBoolean | OpenAPI3SchemaTypeReference;
export declare type OpenAPI3SchemaType = OpenAPI3SchemaTypeObject | OpenAPI3SchemaTypeArray | OpenAPI3SchemaTypeOneOf | OpenAPI3SchemaTypeNull | OpenAPI3SchemaTypeString | OpenAPI3SchemaTypeDateTime | OpenAPI3SchemaTypeNumber | OpenAPI3SchemaTypeInt | OpenAPI3SchemaTypeFloatDouble | OpenAPI3SchemaTypeInteger | OpenAPI3SchemaTypeBoolean | OpenAPI3SchemaTypeReference;
export interface OpenAPI3BaseSchemaType {

@@ -60,2 +60,6 @@ nullable?: boolean;

}
export interface OpenAPI3SchemaTypeDateTime extends OpenAPI3BaseSchemaType {
type: "string";
format: "date" | "date-time";
}
export interface OpenAPI3SchemaTypeInt extends OpenAPI3BaseSchemaType {

@@ -62,0 +66,0 @@ type: "integer";

@@ -38,2 +38,7 @@ "use strict";

exports.openApiV3ContentTypeSchema = openApiV3ContentTypeSchema;
function isStringConstantUnion(types) {
return types.reduce((acc, type) => {
return acc && type.kind === "string-constant";
}, true);
}
function openApi3TypeSchema(type) {

@@ -56,2 +61,12 @@ switch (type.kind) {

};
case "date":
return {
type: "string",
format: "date"
};
case "date-time":
return {
type: "string",
format: "date-time"
};
case "string":

@@ -130,2 +145,8 @@ return {

}
if (isStringConstantUnion(type.types)) {
return {
type: "string",
enum: compact(type.types.map(t => (t.kind === "string-constant" ? t.value : null)))
};
}
return {

@@ -132,0 +153,0 @@ oneOf: withoutNullTypes

@@ -27,2 +27,4 @@ "use strict";

return type.value ? TRUE_TYPE_NODE : FALSE_TYPE_NODE;
case "date":
case "date-time":
case "string":

@@ -29,0 +31,0 @@ return STRING_TYPE_NODE;

@@ -63,2 +63,4 @@ "use strict";

return booleanConstantValidator(type.value, parameter);
case "date":
case "date-time":
case "string":

@@ -65,0 +67,0 @@ return stringValidator(parameter);

@@ -44,2 +44,4 @@ export declare function api(description: ApiDescription): (constructor: Function) => void;

export declare type Double = number;
export declare type Date = string;
export declare type DateTime = string;
export declare type Optional<T> = T | void;

@@ -44,2 +44,4 @@ "use strict";

};
case "date":
case "date-time":
case "string":

@@ -46,0 +48,0 @@ return {

import { Type } from "../../models";
import { OpenAPI3BaseSchemaType } from "./openapi3-schema";
export declare function rejectVoidOpenApi2SchemaType(type: Type, errorMessage: string): OpenAPI2SchemaType;
export declare function openApi2TypeSchema(type: Type): OpenAPI2SchemaType | null;
export declare type OpenAPI2SchemaType = OpenAPI2SchemaTypeObject | OpenAPI2SchemaTypeArray | OpenAPI2SchemaTypeAllOf | OpenAPI2SchemaTypeNull | OpenAPI2SchemaTypeString | OpenAPI2SchemaTypeNumber | OpenAPI2SchemaTypeInt | OpenAPI2SchemaTypeFloatDouble | OpenAPI2SchemaTypeInteger | OpenAPI2SchemaTypeBoolean | OpenAPI2SchemaTypeReference;
export declare type OpenAPI2SchemaType = OpenAPI2SchemaTypeObject | OpenAPI2SchemaTypeArray | OpenAPI2SchemaTypeAllOf | OpenAPI2SchemaTypeNull | OpenAPI2SchemaTypeString | OpenAPI2SchemaTypeDateTime | OpenAPI2SchemaTypeNumber | OpenAPI2SchemaTypeInt | OpenAPI2SchemaTypeFloatDouble | OpenAPI2SchemaTypeInteger | OpenAPI2SchemaTypeBoolean | OpenAPI2SchemaTypeReference;
export interface OpenAPI2BaseSchemaType {

@@ -42,7 +41,11 @@ discriminator?: {

}
export interface OpenAPI2SchemaTypeInt extends OpenAPI3BaseSchemaType {
export interface OpenAPI2SchemaTypeDateTime extends OpenAPI2BaseSchemaType {
type: "string";
format: "date" | "date-time";
}
export interface OpenAPI2SchemaTypeInt extends OpenAPI2BaseSchemaType {
type: "integer";
format: "int32" | "int64";
}
export interface OpenAPI2SchemaTypeFloatDouble extends OpenAPI3BaseSchemaType {
export interface OpenAPI2SchemaTypeFloatDouble extends OpenAPI2BaseSchemaType {
type: "number";

@@ -49,0 +52,0 @@ format: "float" | "double";

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const assert_never_1 = require("../../assert-never");
const compact = require("lodash/compact");
function rejectVoidOpenApi2SchemaType(type, errorMessage) {

@@ -12,2 +13,7 @@ const schemaType = openApi2TypeSchema(type);

exports.rejectVoidOpenApi2SchemaType = rejectVoidOpenApi2SchemaType;
function isStringConstantUnion(types) {
return types.reduce((acc, type) => {
return acc && type.kind === "string-constant";
}, true);
}
function openApi2TypeSchema(type) {

@@ -28,2 +34,12 @@ switch (type.kind) {

};
case "date":
return {
type: "string",
format: "date"
};
case "date-time":
return {
type: "string",
format: "date-time"
};
case "string":

@@ -97,2 +113,13 @@ return {

case "union":
const types = type.types.map(t => openApi2TypeSchema(t));
const withoutNullTypes = compact(types);
if (withoutNullTypes.length !== types.length) {
throw new Error(`Unsupported void type in union`);
}
if (isStringConstantUnion(type.types)) {
return {
type: "string",
enum: compact(type.types.map(t => (t.kind === "string-constant" ? t.value : null)))
};
}
// Please have a look at https://github.com/OAI/OpenAPI-Specification/issues/333

@@ -99,0 +126,0 @@ throw new Error(`Unions are not supported in OpenAPI 2`);

@@ -6,3 +6,3 @@ import { Type } from "../../models";

export declare function openApi3TypeSchema(type: Type): OpenAPI3SchemaType | null;
export declare type OpenAPI3SchemaType = OpenAPI3SchemaTypeObject | OpenAPI3SchemaTypeArray | OpenAPI3SchemaTypeOneOf | OpenAPI3SchemaTypeNull | OpenAPI3SchemaTypeString | OpenAPI3SchemaTypeNumber | OpenAPI3SchemaTypeInt | OpenAPI3SchemaTypeFloatDouble | OpenAPI3SchemaTypeInteger | OpenAPI3SchemaTypeBoolean | OpenAPI3SchemaTypeReference;
export declare type OpenAPI3SchemaType = OpenAPI3SchemaTypeObject | OpenAPI3SchemaTypeArray | OpenAPI3SchemaTypeOneOf | OpenAPI3SchemaTypeNull | OpenAPI3SchemaTypeString | OpenAPI3SchemaTypeDateTime | OpenAPI3SchemaTypeNumber | OpenAPI3SchemaTypeInt | OpenAPI3SchemaTypeFloatDouble | OpenAPI3SchemaTypeInteger | OpenAPI3SchemaTypeBoolean | OpenAPI3SchemaTypeReference;
export interface OpenAPI3BaseSchemaType {

@@ -60,2 +60,6 @@ nullable?: boolean;

}
export interface OpenAPI3SchemaTypeDateTime extends OpenAPI3BaseSchemaType {
type: "string";
format: "date" | "date-time";
}
export interface OpenAPI3SchemaTypeInt extends OpenAPI3BaseSchemaType {

@@ -62,0 +66,0 @@ type: "integer";

@@ -38,2 +38,7 @@ "use strict";

exports.openApiV3ContentTypeSchema = openApiV3ContentTypeSchema;
function isStringConstantUnion(types) {
return types.reduce((acc, type) => {
return acc && type.kind === "string-constant";
}, true);
}
function openApi3TypeSchema(type) {

@@ -56,2 +61,12 @@ switch (type.kind) {

};
case "date":
return {
type: "string",
format: "date"
};
case "date-time":
return {
type: "string",
format: "date-time"
};
case "string":

@@ -130,2 +145,8 @@ return {

}
if (isStringConstantUnion(type.types)) {
return {
type: "string",
enum: compact(type.types.map(t => (t.kind === "string-constant" ? t.value : null)))
};
}
return {

@@ -132,0 +153,0 @@ oneOf: withoutNullTypes

@@ -27,2 +27,4 @@ "use strict";

return type.value ? TRUE_TYPE_NODE : FALSE_TYPE_NODE;
case "date":
case "date-time":
case "string":

@@ -29,0 +31,0 @@ return STRING_TYPE_NODE;

@@ -63,2 +63,4 @@ "use strict";

return booleanConstantValidator(type.value, parameter);
case "date":
case "date-time":
case "string":

@@ -65,0 +67,0 @@ return stringValidator(parameter);

@@ -44,2 +44,4 @@ export declare function api(description: ApiDescription): (constructor: Function) => void;

export declare type Double = number;
export declare type Date = string;
export declare type DateTime = string;
export declare type Optional<T> = T | void;

@@ -56,3 +56,3 @@ import { ApiDescription, HttpContentType, HttpMethod } from "./lib";

}
export declare type Type = VoidType | NullType | BooleanType | BooleanConstantType | StringType | StringConstantType | NumberType | Int32Type | Int64Type | FloatType | DoubleType | IntegerConstantType | ObjectType | ArrayType | OptionalType | UnionType | TypeReference;
export declare type Type = VoidType | NullType | BooleanType | BooleanConstantType | StringType | StringConstantType | NumberType | Int32Type | Int64Type | FloatType | DoubleType | DateType | DateTimeType | IntegerConstantType | ObjectType | ArrayType | OptionalType | UnionType | TypeReference;
export declare const VOID: VoidType;

@@ -97,2 +97,4 @@ export interface VoidType {

export declare const DOUBLE: DoubleType;
export declare const DATE: DateType;
export declare const DATETIME: DateTimeType;
export interface Int32Type {

@@ -110,2 +112,8 @@ kind: "int32";

}
export interface DateType {
kind: "date";
}
export interface DateTimeType {
kind: "date-time";
}
export declare function objectType(properties: {

@@ -112,0 +120,0 @@ [key: string]: Type;

@@ -67,2 +67,8 @@ "use strict";

};
exports.DATE = {
kind: "date"
};
exports.DATETIME = {
kind: "date-time"
};
function objectType(properties) {

@@ -69,0 +75,0 @@ return {

@@ -19,2 +19,5 @@ "use strict";

async function parsePath(sourcePath) {
if (!isValidTypeScript(sourcePath)) {
throw panic_1.panic("TypeScript compilation error");
}
const api = parseRootFile(sourcePath);

@@ -155,1 +158,27 @@ const errors = validator_1.validate(api);

}
/**
* Check for TypeScript errors
*/
function isValidTypeScript(sourcePath) {
const program = ts.createProgram([sourcePath], {
target: ts.ScriptTarget.ESNext,
experimentalDecorators: true,
moduleResolution: ts.ModuleResolutionKind.NodeJs,
baseUrl: "./",
paths: {
"@airtasker/spot": ["./lib/src/lib"]
}
});
let diagnostics = ts.getPreEmitDiagnostics(program);
diagnostics.forEach(diagnostic => {
if (diagnostic.file) {
let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
console.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
}
else {
console.error(`${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`);
}
});
return diagnostics.length === 0;
}

@@ -78,2 +78,10 @@ "use strict";

};
case "Date":
return {
kind: "date"
};
case "DateTime":
return {
kind: "date-time"
};
default:

@@ -80,0 +88,0 @@ return {

@@ -72,2 +72,4 @@ "use strict";

case "boolean-constant":
case "date":
case "date-time":
case "string":

@@ -74,0 +76,0 @@ case "string-constant":

@@ -56,3 +56,3 @@ import { ApiDescription, HttpContentType, HttpMethod } from "./lib";

}
export declare type Type = VoidType | NullType | BooleanType | BooleanConstantType | StringType | StringConstantType | NumberType | Int32Type | Int64Type | FloatType | DoubleType | IntegerConstantType | ObjectType | ArrayType | OptionalType | UnionType | TypeReference;
export declare type Type = VoidType | NullType | BooleanType | BooleanConstantType | StringType | StringConstantType | NumberType | Int32Type | Int64Type | FloatType | DoubleType | DateType | DateTimeType | IntegerConstantType | ObjectType | ArrayType | OptionalType | UnionType | TypeReference;
export declare const VOID: VoidType;

@@ -97,2 +97,4 @@ export interface VoidType {

export declare const DOUBLE: DoubleType;
export declare const DATE: DateType;
export declare const DATETIME: DateTimeType;
export interface Int32Type {

@@ -110,2 +112,8 @@ kind: "int32";

}
export interface DateType {
kind: "date";
}
export interface DateTimeType {
kind: "date-time";
}
export declare function objectType(properties: {

@@ -112,0 +120,0 @@ [key: string]: Type;

@@ -67,2 +67,8 @@ "use strict";

};
exports.DATE = {
kind: "date"
};
exports.DATETIME = {
kind: "date-time"
};
function objectType(properties) {

@@ -69,0 +75,0 @@ return {

@@ -19,2 +19,5 @@ "use strict";

async function parsePath(sourcePath) {
if (!isValidTypeScript(sourcePath)) {
throw panic_1.panic("TypeScript compilation error");
}
const api = parseRootFile(sourcePath);

@@ -155,1 +158,27 @@ const errors = validator_1.validate(api);

}
/**
* Check for TypeScript errors
*/
function isValidTypeScript(sourcePath) {
const program = ts.createProgram([sourcePath], {
target: ts.ScriptTarget.ESNext,
experimentalDecorators: true,
moduleResolution: ts.ModuleResolutionKind.NodeJs,
baseUrl: "./",
paths: {
"@airtasker/spot": ["./lib/src/lib"]
}
});
let diagnostics = ts.getPreEmitDiagnostics(program);
diagnostics.forEach(diagnostic => {
if (diagnostic.file) {
let { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
console.error(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
}
else {
console.error(`${ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")}`);
}
});
return diagnostics.length === 0;
}

@@ -78,2 +78,10 @@ "use strict";

};
case "Date":
return {
kind: "date"
};
case "DateTime":
return {
kind: "date-time"
};
default:

@@ -80,0 +88,0 @@ return {

@@ -72,2 +72,4 @@ "use strict";

case "boolean-constant":
case "date":
case "date-time":
case "string":

@@ -74,0 +76,0 @@ case "string-constant":

@@ -1,1 +0,1 @@

{"version":"0.1.28","commands":{"generate":{"id":"generate","description":"Runs a generator on an API. Used to produce client libraries, server boilerplates and well-known API contract formats such as OpenAPI.","pluginName":"@airtasker/spot","pluginType":"core","aliases":[],"examples":["$ api generate --language typescript --generator axios-client --out src/\nGenerated the following files:\n- src/types.ts\n- src/validators.ts\n- src/client.ts\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"api":{"name":"api","type":"option","char":"a","description":"Path to a TypeScript API definition","required":true},"language":{"name":"language","type":"option","char":"l","description":"Language to generate"},"generator":{"name":"generator","type":"option","char":"g","description":"Generator to run"},"out":{"name":"out","type":"option","char":"o","description":"Directory in which to output generated files"}},"args":[]},"init":{"id":"init","description":"Generates the boilerplate for an API.","pluginName":"@airtasker/spot","pluginType":"core","aliases":[],"examples":["$ api init\nGenerated the following files:\n- api.ts\n- tsconfig.json\n- package.json\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]}}}
{"version":"0.1.29","commands":{"generate":{"id":"generate","description":"Runs a generator on an API. Used to produce client libraries, server boilerplates and well-known API contract formats such as OpenAPI.","pluginName":"@airtasker/spot","pluginType":"core","aliases":[],"examples":["$ spot generate --language typescript --generator axios-client --out src/\nGenerated the following files:\n- src/types.ts\n- src/validators.ts\n- src/client.ts\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"api":{"name":"api","type":"option","char":"a","description":"Path to a TypeScript API definition","required":true},"language":{"name":"language","type":"option","char":"l","description":"Language to generate"},"generator":{"name":"generator","type":"option","char":"g","description":"Generator to run"},"out":{"name":"out","type":"option","char":"o","description":"Directory in which to output generated files"}},"args":[]},"init":{"id":"init","description":"Generates the boilerplate for an API.","pluginName":"@airtasker/spot","pluginType":"core","aliases":[],"examples":["$ spot init\nGenerated the following files:\n- api.ts\n- tsconfig.json\n- package.json\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]}}}
{
"name": "@airtasker/spot",
"version": "0.1.28",
"version": "0.1.29",
"author": "Francois Wouts",

@@ -5,0 +5,0 @@ "bin": {

@@ -1,5 +0,4 @@

Spot
===
# Spot
**Spot** (*"Single Point Of Truth"*) is a concise, developer-friendly way to describe your API contract.
**Spot** (_"Single Point Of Truth"_) is a concise, developer-friendly way to describe your API contract.

@@ -11,6 +10,10 @@ Leveraging the TypeScript syntax, it lets you describe your API and generate any other API contract formats you need (OpenAPI, Swagger, JSON Schema, Pact, API Blueprint), client SDKs (TypeScript, Swift, Kotlin) or even server boilerplate (e.g. Express).

Example of an API definition file `api.ts` which defines a single `POST` endpoint to create a user:
```typescript
import { api, endpoint, request } from "@airtasker/spot";
import { api, endpoint, request, response } from "@airtasker/spot";
@api()
@api({
name: "My API",
description: "My really cool API"
})
class Api {

@@ -21,6 +24,4 @@ @endpoint({

})
createUser(
@request req: CreateUserRequest
): CreateUserResponse {
throw "contract";
createUser(@request req: CreateUserRequest): CreateUserResponse {
return response();
}

@@ -40,2 +41,3 @@ }

You can pass the definition above to a generator by simply running:
```sh

@@ -46,2 +48,3 @@ npx @airtasker/spot generate --api api.ts

This is work in progress as of 14 Nov 2018:
- [x] Functional TypeScript DSL

@@ -64,8 +67,11 @@ - [x] Support for multiple files (using import statements)

<!-- toc -->
* [Spot](#spot)
* [Usage](#usage)
* [Commands](#commands)
<!-- tocstop -->
# Usage
To get started and set up an API declaration in the current directory, run:
```

@@ -76,2 +82,3 @@ npx @airtasker/spot init

You can then run a generator with:
```

@@ -81,3 +88,293 @@ npx @airtasker/spot generate --api api.ts

## `@api`
Define an API. This is required and must only be defined once:
```TypeScript
import { api } from "@airtasker/spot";
@api({
name: "My API",
description: "My really cool API"
})
class MyAPI {}
```
| Field | Description |
| ------------- | ------------------------------------- |
| `name` | (**required**) Name of the API |
| `description` | (**required**) Description of the API |
## `@endpoint`
Define a HTTP endpoint for the API. An endpoint describes a particular HTTP action on a URL path:
```TypeScript
import { endpoint, header, pathParam, queryParam, request, response, specificError } from "@airtasker/spot";
class MyUserEndpoints {
// GET /users expects a mandatory `search_term` query parameter and returns a list of users.
@endpoint({
method: "GET",
path: "/users",
description: "Retrieve all users",
tags: ["Users"]
})
getUsers(@queryParam({ description: "Search term" }) search_term: Optional<string>): UserResponse[] {
return response();
}
// GET /users/:id returns a user by their unique identifier.
@endpoint({
method: "GET",
path: "/users/:id",
description: "Get user by id",
tags: ["Users"]
})
getUser(@pathParam({ description: "Unique user identifier" }) id: string): UserResponse {
return response();
}
// POST /users creates a user, expecting an authorization token to be present.
@endpoint({
method: "POST",
path: "/users",
description: "Create a user",
tags: ["Users"]
})
@specificError<ApiErrorResponse>({
name: "unauthorized",
statusCode: 401
})
createUser(
@request req: CreateUserRequest,
@header({
name: "Authorization",
description: "This is the authorization token"
})
authToken: Optional<string>
): CreateUserResponse {
return response();
}
}
interface User {
firstName: string;
lastName: string;
}
type UserResponse = User;
type UserListResponse = User[];
interface CreateUserRequest {
firstName: string;
lastName: string;
}
interface CreateUserResponse {
success: boolean;
}
interface ApiErrorResponse {
message: string;
}
```
| Field | Default | Description |
| -------------------- | ------------------ | -------------------------------------------------------- |
| `method` | | (**required**) [HTTP method](#suppported-http-methods) |
| `path` | | (**required**) URL path |
| `description` | `""` | Description of the endpoint |
| `requestContentType` | `application/json` | Content type of the request body |
| `successStatusCode` | `200` | HTTP status code for a successful response |
| `tags` | | List of tags used for endpoint grouping in documentation |
### `@request`
Define a request body for requests that require one. This is commonly used for `POST` and `PUT` requests and is not allowed for `GET` requests:
```TypeScript
class MyUserEndpoints {
//...
@endpoint({
method: "POST",
path: "/users"
})
createUser(
@request req: CreateUserRequest
): UserResponse {
return response();
}
@endpoint({
method: "PUT",
path: "/users/:id"
})
updateUser(
@pathParam({ description: "User unique identifier" }) id: string,
@request req: UpdateUserRequest
): UserResponse {
return response();
}
//...
}
interface CreateUserRequest {
firstName: string;
lastName: string;
}
interface UpdateUserRequest {
lastName: string;
}
```
### `@header`
Define a request header. `@header` can be used multiple times to define multiple headers:
```TypeScript
//...
@endpoint({
method: "POST",
path: "/users",
description: "Create a user"
})
createUser(
@request req: CreateUserRequest,
@header({
name: "Authorization",
description: "This is the authorization token"
})
authToken: Optional<string>
): CreateUserResponse {
return response();
}
//...
```
| Field | Description |
| ------------- | --------------------------------- |
| `name` | (**required**) Name of the header |
| `description` | Description of the header |
### `@pathParam`
Define path parameters that appear in the `path` provided in `@endpoint()`. For example if the path is `/users/:id`, the endpoint method must define a matching argument with `@pathParam() id: string`::
```TypeScript
//...
@endpoint({
method: "GET",
path: "/users/:id",
description: "Get user by id"
})
getUser(@pathParam({ description: "Unique user identifier" }) id: string): UserResponse {
return response();
}
//...
```
**Note**: the name of the argument must match the name of the path parameter.
| Field | Description |
| ------------- | --------------------------------- |
| `description` | Description of the path parameter |
### `@queryParam`
Define query parameters. `@queryParam` can be used multiple times to define multiple query parameters:
```TypeScript
//...
@endpoint({
method: "GET",
path: "/users",
description: "Retrieve all users"
})
getUsers(@queryParam({ description: "Search term" }) search_term: Optional<string>): UserResponse[] {
return response();
}
//...
```
**Note**: the name of the argument must match the name of the query parameter.
| Field | Description |
| ------------- | ---------------------------------- |
| `description` | Description of the query parameter |
### `@specificError<T>`
Define a known error for the endpoint. `@specificError` can be used multiple times to define multiple errors. `T` must be replaced with the response type when the error occurs, for example `@specificError<UnauthorizedErrorResponse>`:
```TypeScript
//...
@endpoint({
method: "POST",
path: "/users",
description: "Create a user"
})
@specificError<UnauthorizedErrorResponse>({
name: "unauthorized",
statusCode: 401
})
createUser(
//...
): CreateUserResponse {
return response();
}
//...
```
| Field | Description |
| ------------ | --------------------------------------------- |
| `name` | (**required**) Name of the error |
| `statusCode` | (**required**) HTTP status code for the error |
### `@genericError<T>`
Define a default error for the endpoint. This can only be used once for an `@endpoint`. `T` must be replaced with the response type when the error occurs, for example `@genericError<ApiErrorResponse>`:
```TypeScript
//...
@endpoint({
method: "POST",
path: "/users",
description: "Create a user"
})
@genericError<ApiErrorResponse>()
createUser(
//...
): CreateUserResponse {
return response();
}
//...
```
## Matcher Types
| Type | Description | Example |
| ------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| `string` | A string value | `name: string` |
| `number` | A number value | `numPencils: number` |
| `Int32` | A 32-bit integer | `age: Int32` |
| `Int64` | A 64-bit integer | `numAtoms: Int64` |
| `Float` | A 32-bit floating point number | `weight: Float` |
| `Double` | A 64-bit floating point number | `price: Double` |
| `boolean` | A boolean value | `isAdmin: boolean` |
| `Date` | [ISO-8601](https://www.iso.org/iso-8601-date-and-time-format.html) string representation of a date | `dateOfBirth: Date` |
| `DateTime` | [ISO-8601](https://www.iso.org/iso-8601-date-and-time-format.html) string representation of a date-time | `createdAt: DateTime` |
| Constant | An exact value | `role: "admin"` |
| `Optional<T>` | An optional value | `role: Optional<string>` |
| Union | One-of | `role: "admin" \| "member"`, `param: string \| number` |
| Array | Collection | `nicknames: string[]` |
| Object | An object matcher | `person: { firstName: string, lastName: string }` |
## Suppported HTTP Methods
`GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH`
# Commands
<!-- commands -->

@@ -104,3 +401,3 @@ * [`spot generate`](#spot-generate)

EXAMPLE
$ api generate --language typescript --generator axios-client --out src/
$ spot generate --language typescript --generator axios-client --out src/
Generated the following files:

@@ -112,3 +409,3 @@ - src/types.ts

_See code: [build/cli/src/commands/generate.js](https://github.com/airtasker/spot/blob/v0.1.28/build/cli/src/commands/generate.js)_
_See code: [build/cli/src/commands/generate.js](https://github.com/airtasker/spot/blob/v0.1.29/build/cli/src/commands/generate.js)_

@@ -144,3 +441,3 @@ ## `spot help [COMMAND]`

EXAMPLE
$ api init
$ spot init
Generated the following files:

@@ -152,3 +449,3 @@ - api.ts

_See code: [build/cli/src/commands/init.js](https://github.com/airtasker/spot/blob/v0.1.28/build/cli/src/commands/init.js)_
_See code: [build/cli/src/commands/init.js](https://github.com/airtasker/spot/blob/v0.1.29/build/cli/src/commands/init.js)_
<!-- commandsstop -->
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc