GithubHelp home page GithubHelp logo

functionless / functionless Goto Github PK

View Code? Open in Web Editor NEW
328.0 8.0 14.0 21.21 MB

Build serverless AWS applications with type-safe Components and easy-to-follow Conventions.

Home Page: https://functionless.org

License: Apache License 2.0

JavaScript 0.43% TypeScript 99.43% Shell 0.02% CSS 0.12%
api api-rest aws aws-lambda graphql serverless typescript aws-cdk javascript

functionless's Introduction


Functionless

Build serverless AWS applications with type-safe Components and easy-to-follow Conventions.

MIT License Discord Twitter

๐Ÿ› ย  Functionless is in pre-release - come chat to us on Discord!


Overview

Website โ€ข API Docs โ€ข Getting Started

Functionless is a full-stack framework that enables you to easily build type-safe cloud applications on AWS serverless without writing CloudFormation or complex infrastructure configuration.

  • ๐Ÿช‚ย  Type-safe AWS cloud resources such as Rest APIs, GraphQL APIs, Lambda Functions, DynamoDB Tables, Step Functions, Event Bridge, and more.
  • ๐Ÿ‘จโ€๐Ÿ’ปย  Local development experience for AWS serverless.
  • ๐Ÿžย  Instant feedback with step-through debugger.
  • ๐Ÿง™ย  Architecture-aware CLI for operating, maintaining and testing cloud resources.
  • ๐Ÿ”ย  Guaranteed least-privilege automatically derived IAM Policies.
  • ๐ŸŽขย  NextJS-like file system conventions for CloudFormation Stacks and APIs.
  • ๐Ÿงฉย  Build and share custom re-usable cloud components.

Quick Start

# create a new project
npx create-functionless@latest
cd <project-name>

# deploy to AWS
npx fl deploy

# open my-function in the AWS console
npx fl ./src/my-function console

# run your application locally
npx fl local

Why Functionless?

Functionless re-imagines Infrastructure-as-Code (IaC) as Infrastructure-from-Code (IfC). Enjoy a streamlined developer experience for full stack developers without giving up control of your AWS infrastructure.

๐Ÿง  Intuitive

We provide guardrails to accelerate development of serverless applications. Use simple file system conventions to organize your cloud resources such as Stacks, APIs, Functions, Workflows, Databases, and more.

๐Ÿš€ Productive

Designed for instant feedback. Catch errors in real-time before deployment with type-safe Runtime APIs; test and debug locally; automate operational tasks with an application-aware CLI experience.

Type-safe Cloud Resources

๐Ÿ‘ฎโ€โ™€๏ธ Secure

Built with safety and security in mind. Our compiler automatically guarantees your serverless Resources are configured with IAM Policies that only have access to what they need - no more and no less.

๐Ÿ’ช Powerful

Build and share custom components and integrate them into your application. Each component exposes a Runtime and Operational interface for production-ready use.

functionless's People

Contributors

bakerlee avatar cfraz89 avatar dependabot[bot] avatar fredericbarthelet avatar functionless-upgrader[bot] avatar jayagl avatar mathisobadia avatar matthias-pichler-warrify avatar thantos avatar tvanhens avatar volkanunsal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

functionless's Issues

Kinesis Stream type-safe Integration class

Provide a wrapper class, Stream<T>, that models the Kinesis Stream Functionless integrations.

Must integrates with the following:

  1. AppSync
const stream = new Stream<string>(new aws_sqs.Stream(..))

new AppsyncResolver<{id: string}>($context => {
  stream.putRecord({
    Data: id,
    PartitionKey: id
  });
});
  1. Step Functions
new StepFunction(scope, id, (id: string) => {
  stream.sendMessage({
    Data: id,
    PartitionKey: id
  })
})
  1. Event Bridge
bus
  .when(scope, "rule", event => event.source === "lambda")
  .map(event => event.record)
  .pipe(stream)

Appsync: Support in-line access of properties on the result of a service call

Support the following access pattern:

return table.get(..).property

Currently, this mistakenly renders a Velocity Template where the RequestMappingTemplate ends with:

$util.toJson($request).property

It should actually translate to an expression the ResponseMappingTemplate:

#set($context.stash.return__val = $context.result.property)

Optimize `map`, `forEach` and `reduce` chains.

As of #1 , the logic for map, forEach and reduce chains (such as below) results in redundant use fo #foreach which excessively consumes the maximum iteration count of 1000.

This expression results in n = list.length * 3 iterations when it could be accomplished in n = list.length

list.map(..).map(..).forEach(..)

Event Bridge + Step Functions

Dependent on #36 and #39

Event bridge integrations with step functions. Start a step function integration using the typesafe pipe from the Functionless EventBus.

bus
   .when(...)
   .map(...)
   .pipe(myStepFunction)

bus
   .when(...)
   .pipe(myStepFunction)

Event bridge events send from step functions.

const createPerson = new ExpressStepFunction((arg: string): Person | undefined => {
   const person = {...};
   myBus({ "detail-type": "PersonCreated",  detail: person });
});

Support Typescript to generate IAM Policies

new IamPolicy.EventBridge(identity => {
    if(identity.eventBridge.sourceAccount === "..." && arnAllowList.includes(identity.resource)) {
        return IamPolicy.EventBridge.CreateRule & IamPolicy.EventBridge.DeleteRule;
    }
    return false;
})

or

new Policy((principal) => {
  if (principal.sourceAccountId === '...') {
    return bucket.grantRead(principal);
  } else {
    return denyAll()
  }
})

Appsync: Allow service calls in if-statements

Keywords

Appsync, conditional, if, branch

Description

Would like to enable branching with if-conditions at the top-level.

The following cases should be considered:

  1. the branches call the same integration
new AppsyncFunction(($context, arg) => {
  if (arg === "hello") {
    functionA(arg)
  } else {
    functionA(arg)
  }
});
  1. only one of the branches calls the integration
new AppsyncFunction(($context, arg) => {
  if (arg === "hello") {
    functionA(arg)
  }
});
  1. the branches call different integrations
new AppsyncFunction(($context, arg) => {
  if (arg === "hello") {
    functionA(arg)
  } else {
    functionB(arg)
  }
});

AppSync technically doesn't support branching like this, but we should be able to enable a limited form of it using $context.stash and the #return directive.

API Gateway mapping templates

Is it currently possible to use this for api gateway mapping templates as well? If not support for this would be nice!

Step Functions filter using a Map Task

When filtering lists, e.g. list.filter(..), we currently only support cases where the filter expression can be converted to JSON Path. We need to add support for using a Map Task which can call APIs, make Choices, etc.

Ex.

list.filter(item => {
  if(task(item)) {
    return false;
  } else {
    return true;
  }
})

List `filter` support

Filter immutably removes items from a list to assign to a new list or chain from.

Given a list of size N, return a list of size <N.

Filter is given a lambda that must return a boolean.

[1,2,3,4].filter(x => 2) // [3, 4]

Filter supports index and array parameters like map.

[1,2,3,4].filter((_, i, array) => i < array.length / 2) // [1, 2]

Filter should be able to collapse with map, reduce, and foreach.

Filter may be able to be collapsed too, TODO, explore this.

Filter may result in 0 items, causing reduce to fail when no initial value is given.

AppsyncFunction Interface

I love this project and I've been trying to use it in addition with graphql codegen to generate types automatically from the schema. While trying to do this I've had issues with the API for the AppsyncFunction class

The issue I see is that the name of the function parameter influences the result of the mapping template so if I use the code from the example app
This works

    this.getPerson = new AppsyncFunction<
      (id: string) => ProcessedPerson | null
    >((_$context, id) => {
      const person = this.personTable.getItem({
        key: {
          id: $util.dynamodb.toDynamoDB(id),
        },
        consistentRead: true,
      });

But this doesn't work because the name of the parameter is not the same as the name of the graphql query parameter

    this.getPerson = new AppsyncFunction<
      (id: string) => ProcessedPerson | null
    >((_$context, userId) => { // notice the argument name has changed
      const person = this.personTable.getItem({
        key: {
          id: $util.dynamodb.toDynamoDB(userId),
        },
        consistentRead: true,
      });

This means that no amount of code generation will help here as even if we successfully generate a type that looks like (id: string) => ProcessedPerson | null but don't use the right parameter name then you will not see a typescript error there.

Ideally I think that the interface for this function could look something like

AppsyncFunction<TArgs, TResult, TSource = undefined> {
    readonly decl: FunctionDecl<F>;
    constructor(fn: ($context: $Context<Source>, args: TArgs) => TResult);

Then the arguments would be used with args.id so slightly less good looking but with destructuring the difference is small, but this has the benefit of better type safety as using args.userId would result in a typescript error.

As an added benefit this would make using functionless with graphql codegen typescript resolver plugin much easier

Event Bridge + API Gateway

API Gateway integration with EventBridge.

Dependent on #39 and #23

EventBridge to APIg

Trigger an API (post/put/delete) from an event bridge event.

bus
   .when(...)
   .map(...)
   .pipe(myApiG.myPostOperation)

APIg to EventBridge

Send an event to a bus from a API Gateway operation.

new ApiIntegration<{
  body: BodyType,
  params: ParamsType,
  path: PathType
}>(($input) => {
  myBus({ source: "MyAPI", "detail-type": "AnEvent", detail: $input })
});

Generalize TypeScript plugin to allow sub-types of AppsyncResolver

See: #24 (comment)

Currently, if a developer writes a sub-type of AppsyncResolver, the transformer does not detect it as requiring transformation. We should update the compiler be smarter when detecting when a Function needs to be transformer.

Simplest solution is to update this code:
https://github.com/sam-goodwin/functionless/blob/be8d032c6e0f58658854e3bbc15901553a62a2c6/src/compile.ts#L96-L118

It looks for the FunctionlessKind: "AppsyncResolver" static member on the ts.ClassDeclaration to identify which code to apply the transform to. The logic is naive and does not detect members on the sub-type. Instead, we should use checker.getType(classDecl).getProperty("FunctionlessKind") which accounts for sub-types.

This would work for the immediate use-case of allowing a wrapper class over the top of AppsyncResolver that @mathisobadia is working on (#24 (comment)), but it relies on the constructor having the exact same shape (constructor(fn: ResolverFunction)). If it had a different shape, such as constructor(id: string, fn: ResolverFunction), then it would break.

Perhaps we should traverse through the constructor arguments and transform any arguments that are of typeResolverFunction?

Event Bridge + App Sync

To App Sync

Reference: https://aws.amazon.com/blogs/mobile/appsync-eventbridge/

note: not 100% sure what this should look like
there are more caveats than I would like...

const updatePerson = this.updateName = new AppsyncResolver(($context: AppsyncContext<{ id: string; name: string }>) => {...});
const api = new appsync.GraphqlApi(stack, "Api", {...});

const synesizedUpdateResolver = updatePerson.addResolver(api, {
  typeName: "Mutation",
  fieldName: "updateName",
});

bus
   .when(...)
   .map(event => event.detail) // to object or string
   // Triggers the graphql mutation using the event from the map function
   .pipe(synesizedUpdateResolver);

Questions:

  1. What is the right reference(s) from app sync to use in pipe?
  2. Can graphql support a single string input as opposed to named fields?
  3. Can we get the full schema in order to build the query to invoke app sync?

From GraphQL to Event Bridge

Reference: https://serverlessland.com/patterns/appsync-eventbridge-cdk

const awsBus = new aws_events.EventBus(this, 'bus');
const bus = new EventBus(awsBus);

new AppsyncResolver(($context: AppsyncContext<{ id: string; name: string }>) => {
    bus({ id, name });
    /// ... other things ...
});

Reflect support for constants outside of the reflect.

Reflect (including all reflect like functions, event bus when, app sync resolver, etc) should be able to reference constant values that are not within the reflect function itself.

  • Event Bridge - #93
  • AppSync
  • Step Functions
  • API Gateway
const ID_PREFIX = "my_prefix";

bus
   .when(event => event.id.startsWith(ID_PREFIX))
   .map(event => ({ id: `${ID_PREFIX}${event.detail.newId}` }))
   pipe(...)

$context code generation

I just realized I haven't tested the use of $context in Appsync VTL templates and it probably doesn't work since the plugin will detect it as either an argument or an external Reference. This needs to be fixed.

Lambda Json Payload

Currently, functionless turns a Lambda payload into a array of arguments in the callable Function.

new Function<(arg: string, arg2: string) => string>(...)

which actually represent a payload of the form

{
   arg: string;
   arg2: string;
}

This is problematic because

  1. Lambda supports any valid json, including strings and numbers as the payload
  2. All integrations with lambda need to use an array of args instead of the more natural object

Proposal:

Accept any type payload and output to the function as arbitrary json.

new Function<{ arg: string, arg2: string }, string>(...); 

Step Functions: gracefully handle empty blocks

The Step Function interpreter does not handle all cases when a BlockStmt is empty - it does handle cases like an empty catch (i think), but we need to enumerate the cases thoroughly.

Event Escape Hatches

Currently the Event Bridge integrations (#37) only works for Functionless's paradigms, but we should be able to support any existing event bridge rules or targets without our magic integrations.

Current

const bus = new EventBus(cdkBus)
    .when(event => event.source === "lambda")
    .map(event => event.source)
    .pipe(myFunctionlessFunction) // must be a Functionless function, event bus, or other future integrations

Supported

  • Any Bus - The EventBus class already wraps any event bus, including ones not owned by the current stack.

Note, the user could just use .rule property on the EventBusRule or the .eventInput property on the EventBusTransform.

Missing

  • Support any rule - bypass the .when(), but still make use of map and pipe.
  • Support any mapping - the need for this would be less clear as the output would not be type safe.
  • Support any target - pipe only supports integrations, but there are many targets

Proposal

Rules

Support a form which imports any rule instead of one made by .when();

const rule = EventBusRule.from(rule).when().pipe()

Targets

The EventBusTransform class can support a pipe override which accepts a function that contains the rule and the target input.

const transformed = new EventBus(bus).when(...).map(...);
transformed.pipe((rule, inputTarget) => {
    const connection = new events.Connection(this, 'Connection', {
     authorization: events.Authorization.apiKey('x-api-key', SecretValue.secretsManager('ApiSecretName')),
     description: 'Connection with API Key x-api-key',
   });

    return new events.ApiDestination(this, 'Destination', {
       event: inputTarget
       connection,
       endpoint: 'https://example.com',
       description: 'Calling example.com with API key x-api-key',
    });
})

Mappings

The rule pipe can support creation of arbitrary targets, and thus, input transforms.

const rule = new EventBus(bus).when(...);
rule.pipe((rule) => {
   const connection = new events.Connection(this, 'Connection', {
     authorization: events.Authorization.apiKey('x-api-key', SecretValue.secretsManager('ApiSecretName')),
     description: 'Connection with API Key x-api-key',
   });

    return new events.ApiDestination(this, 'Destination', {
       event: RuleTargetInput.fromObject({ something: EventField.fromPath("$.source") })
       connection,
       endpoint: 'https://example.com',
       description: 'Calling example.com with API key x-api-key',
    });
})

Step Functions: support ThrowStmt(CallExpr)

The throw statement in Step Functions is currently implemented such that the expr must always be a new <error class name>(), but Node also supports throw Error() - we should add support for ThrowStmt(CallExpr).

throw Error("cause")

Synthesizes to:

{
  "Type": "Fail",
  "Error": "Error",
  "Cause": '{"message":"cause"}'
}

Scala 3 functionless

Implement a scala 3 version of functionless. Instead of the typescript transformer, use a macro.

Then: look into integrating with spark/flink. Sadly, I don't think they support scala 3 yet, still scala 2.

Transformer throwing Errors kills the TypeScript language server

Currently, when the TypeScript transformer encounters a node it does not understand, we just throw an Error and display the syntax (text) that could not be processed. This is not too big of a deal when running tsc, but it kills the language server when editing in VS code.

Instead, the transformer should not throw and instead output a NullLiteral or ErrorNode to the AST.

For errors, we will rely on a language service plugin (see: #10) to add domain-specific diagnostics instead of throwing an error.

Build a wrapper class compatible with generated Resolver Classes

See: #24 (comment)

The AppsyncResolver class supports integration with generated resolver classes, but it is quite verbose, requiring the developer specify TArgs, TResult, TSource individually:

new AppsyncResolver<
  MutationResolvers["addPerson"]["args"],
  MutationResolvers["addPerson"]["result"],
  MutationResolvers["addPerson"]["parent"]
>

Instead, we want a class that accepts a single type parameter that accepts the Resolver type:

type ResolverBase = {
  args: ResolverArguments;
  parent: unknown;
  result: unknown;
};

class AppsyncResolverWrapper<
  ResolverType extends ResolverBase
> extends AppsyncResolver<
  ResolverType["args"],
  ResolverType["result"],
  ResolverType["parent"]
> {
  constructor(
    fn: ResolverFunction<
      ResolverType["args"],
      ResolverType["result"],
      ResolverType["parent"]
    >
  ) {
    super(fn);
  }
}

Because of the way our TypeScript transform works, we need to improve the plugin to be more intelligent so that developers can build their own wrapper classes: #30

Support `when` narrowing on event bridge.

The .when method in event bridge does not narrow the type of the map or pipe methods.

interface Detail {
   optional?: string;
}

bus
   .when(event => event.detail.optional !== undefined)
   .map<string>(event => event.detail.optional) // compiler error because optional could be undefined

AppSync: two separate AppsyncResolver's calling the same API causes error

If two separate AppsyncResolver functions (for the same graphql API) use the same API, they both attempt to create a Construct with the same name, leading to an error.

new AppsyncResolver(() => {
  table.putItem()
});

new AppsyncResolver(() => {
  table.putItem()
});

Error:

Error: There is already a Construct with name 'c8f14ae7c285b29c7558ec4a21c23804dabe8bf102_putItem_0Function' in DynamoDbDataSource [c8f14ae7c285b29c7558ec4a21c23804dabe8bf102]
    at Node.addChild (/Users/samgoodwin/workspaces/functionless/node_modules/constructs/src/construct.ts:401:13)
    at new Node (/Users/samgoodwin/workspaces/functionless/node_modules/constructs/src/construct.ts:69:17)
    at new Construct (/Users/samgoodwin/workspaces/functionless/node_modules/constructs/src/construct.ts:449:17)
    at new Resource (/Users/samgoodwin/workspaces/functionless/node_modules/aws-cdk-lib/core/lib/resource.ts:150:5)
    at new AppsyncFunction (/Users/samgoodwin/workspaces/functionless/node_modules/@aws-cdk/aws-appsync-alpha/lib/appsync-function.ts:130:5)
    at DynamoDbDataSource.createFunction (/Users/samgoodwin/workspaces/functionless/node_modules/@aws-cdk/aws-appsync-alpha/lib/data-source.ts:146:12)
    at createStage (/Users/samgoodwin/workspaces/functionless/src/appsync.ts:435:33)
    at /Users/samgoodwin/workspaces/functionless/src/appsync.ts:339:22
    at Array.map (<anonymous>)
    at synthesizeFunctions (/Users/samgoodwin/workspaces/functionless/src/appsync.ts:249:10)

Step Functions interpreter

Similar to AppsyncFunction, we should build interpreters for AWS Step Functions:

  1. new ExpressStepFunction
  2. new StandardStepFunction

The ExpressStepFunction represents a synchronous workflow that returns a value and has a limited subset of features available (see: https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html#welcome-workflows)

const getPerson = new ExpressStepFunction((arg: string): Person | undefined => {
  const person = personTable.get(arg);
  return {
    id: person.id.S,
    name: person.name.S
   };
})

The StandardStepFunction represents an asynchronous workflow that does not return a value - to retrieve the value, a caller must call getExecutionStatus.

Appsync Integration

It should be possible to call a Step Function from within an Appsync Resolver:

new AppsyncFunction(() => {
  // ExpressStepFunction returns synchronously
  const person = getPerson("arg");
})

Event Bridge + Dynamo

Event bridge events that invoke dynamo targets.

bus
   .when(...)
   .map(event => ({ key: { key: event.id }, item: event.detail }))
   .pipel(myEventSinkTable.putItem)

Event Sources

Add auto event bus rule generation from integrations with the ability.

Some AWS services like Lambda, Step Functions, and more automatically generate Rules on the default bus.

Add the ability to automatically create these rules and then send to all Functionless Event Bus target. They should support type safe event types based on the event.

myFunction.onSucccess().send(myMachine, event => event.detail.result);
myMachine.onStarted().send(...)

Where send is the outcome of #37 's final target design.

SQS Queue type-safe integration class

Provide a wrapper class, Queue<T>, that models the SQS Queue Functionless integrations.

Must integrates with the following:

  1. AppSync
const queue = new Queue<string>(new aws_sqs.Queue(..))

new AppsyncResolver<{id: string}>($context => {
  queue.sendMessage({
     MessageBody: id,
     MessageAttributes: ..
  });
});
  1. Step Functions
new StepFunction(scope, id, (id: string) => {
  queue.sendMessage({
     MessageBody: id,
     MessageAttributes: ..
  })
})
  1. Event Bridge
bus
  .when(scope, "rule", event => event.source === "lambda")
  .map(event => event.message)
  .pipe(queue)
  1. Lambda
new Function(scope, id, async () => {
  await queue.sendMessage(..);
});

AppSync: addResolver breaks if called before dependency is defined

If you call .addResolver on an AppsyncResolver before a dependency it references has been instantiated, an error is thrown.

new AppsyncResolver(() => {
  table.get()
}).addResolver(..);

// this is undefined at the time AppsyncResolver is synthesized
export const table = new f.Table(..);

Provide VTL evaluator for unit-testing

It should be possible to test the AppsyncFunctions locally in unit tests. This could be achieved by running the non-transformed function (see #12), but this relies on our transformation logic being perfectly sound.

So, we should also enable evaluation of the VTL templates.

Appsync: Unit Resolver

Support Unit Resolvers (no Resolver Functions)

new AppsyncFunction((_, name: string) => `hello ${name}`))

Support PartiQL queries

Some AWS services, notably DynamoDB, Redshift and Glue, support PartiQL as a query language. PartiQL support in the compiler plugin would be very useful.

It could look something like this:

val musicTable = new Table...;

musicTable
    .filter((x) => x.Artist === 'Artist' && x.SongTitle === 'SongTitle')
    .map(x => x.Album)

becomes

SELECT Album                                         
FROM Music  
WHERE Artist=? and SongTitle=?

AWS Event Bridge

Option 1: As Rule and Target

construct that accepts a bus and a predicate, returns a rule.

interface MyEventDetail {
    value: string 
}
type MyEvent = EventBusEvent<MyEventDetail, "my-event-detail-type">;

const rule = new EventBridgeRule<MyEvent>(bus, (event): boolean => {
   return event.source == 'lambda' && event.detail.value.startsWith('something');
})
const function = new Function(...);
const target = new EventBridgeTarget<MyEvent>(rule, (event) => {
    function(event.detail)
});

Option 2: As Single Integration

interface MyEventDetail {
    value: string 
}
type MyEvent = EventBusEvent<MyEventDetail, "my-event-detail-type">;

const function = new Function(...);
new EventBus<MyEvent>(bus, (event, $util) => {
    if($util.matchType<MyEvent>()) {
          function(event.detail);
    }
})

As AppSync target

import {aws_events} from 'aws-cdl-lib';
import { EventBus } from 'functionless';

cosnt eventBus = new EventBus(new aws_events.EventBus());

const postEvent = new AppsyncResolver<{ id: string }, Item | null>(($context) => {
    eventBus.putEvent($context.arguments);
})

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.