Git Product home page Git Product logo

typed-immutable's Introduction

Typed-immutable NPM version Build Status

=========

Library provides is built upon immutable.js to leverage it’s immutable persistent data and provide structural typing on top of that. Library is not aiming to provide type safety of typed language (some static type checker like flow would be tool for that) although it allows user to define structured types and guarantees that values produced and later transformed will conform to pre-defined structure. Handy use case for such tool would be an application state modelling (in MVC sense), specially if state is centralised but compartmentalized for us by independent components.

API

In the following sections we would use term "type" for a javascript class that can be instantiated via function call (although use of new still possible) and produces immutable persistent data structure that we’ll refer to as "value" as they will have more common with primitive values like strings or numbers than with objects.

Record

Records are a labeled data structure. They provide a lightweight representation for complex data. Record types can be defined by invoking Record function with a type structure descriptor, that is an object that provides field names and types associated with them:

var {Record} = require("typed-immutable")

var Point = Record({x: Number, y: Number})

Record types maybe invoked as functions or instantiated as a class to produce a value in form of immutable object with a pre-defined structure:

Point({x:0, y:0}) // => {x:0, y:0}
new Point({x: 0, y:0}) // => {x:0, y:0}

Record types enforce pre-defined structure and will fail if input provided does not match it:

Point() // => TypeError: Invalid value for "x" field:
        //               "undefined" is not a number


Point({x: "1", y: "2"}) // => TypeError: Invalid value for "x" field:
                        //     "1" is not a number

Record types definitions may also be provided a default values for feilds for a convenience of use:

var Point = Record({x: Number(0), y: Number(0)})
Point() // => { "x": 0, "y": 0 }
Point({x: 20}) // => { "x": 20, "y": 0 }

Point({x: null}) // => TypeError: Invalid value for "x" field:
                 //     "null" is not a number

Record fields can be accessed by name via property access syntax:

var p1 = Point({x: 17})
p1.x // => 17
p1.y // => 0

Attempts to update a field will fail with error:

p1.x = 30 // =>  TypeError: Cannot set on an immutable record.

Instead of changing a record values you can transform them or create new values from existing one similar to how you do that with strings or numbers:

p1 = Point() // => {x:0, y:0}
p1.set("x", 7) // => {x: 7, y:0}
p1 // => {x:0, y:0}

Removeing a field from a record simply resets it's value to the default if one was defined.

var p1 = Point({x: 1, y: 25}) // => {x:1, y:25}
p1.remove("y") // => {x:1, y:0}

Record types proudce values with only fields that they were defined with everything else they simply ignore:

Point({x:30, y:40, z:8}) // => {x:30, y:40}

Although the do explicitly forbid setting undeclared fields with error:

Point().set("z", 5) // => TypeError: Cannot set unknown field "z" on "Typed.Record({x: Number(0), y: Number(0)})"

Record values are actually instances of the record type / class but given immutablity they have much more common with values which is why we refer to them as such:

var p1 = Point()
p1 instanceof Point // true
p1.x // => 0
p1.y // => 0

var p2 = p1.merge({x: 23})
p2 instanceof Point // true
p2.x // => 23
p2.y // => 0

p1.equals(Point()) // => true

p1.equals(p2) // => false

p2.equals(Point({x: 23})) // => true

Record values serialize to strings that containing their value and a type signature

Point({x:23}).toString() // => ‘Typed.Record({x: Number(0), y: Number(0)})({ "x": 23, "y": 0 })’

But for records with large number of fields it maybe more handy to provide a name, that can be done during definition:

var Point = Record({x: Number(0), y: Number(0)}, "Point")

Point({x: 4, y: 7}).toString() // => ‘Point({ "x": 4, "y": 7 })’
Nested records

For any complex data defining records contaning records is crucial, which works exactly as expected:

var Line = Record({begin: Point, end: Point}, "Line")
var line = Line({end: {x: 70}})

line instanceof Line // => true

line.toString() // => Line({ "begin": Point({ "x": 0, "y": 0 }), "end": Point({ "x": 70, "y": 0 }) })

line.begin // => {x: 0, y:0}
line.begin instanceof Point // => true

line.end // => {x: 70, y:0}
line.end instanceof Point // => true

As with primitive fields you could provide defaults to a complex records as well:

var Line = Record({begin: Point({x:23}), end: Point({y:4})}, "Line")
Line().toString() //=> Line({ "begin": Point({ "x": 23, "y": 0 }), "end": Point({ "x": 0, "y": 4 }) })

Records can be serialized to JSON and then instantiated back to an equal record value:

Line(line.toJSON()).equals(line) // => true

List

You can define typed lists by providing a List function a type that it’s items are supposed to be of:

var {List} = require("typed-immutable")

var Numbers = List(Number)

Numbers().toString() // ‘Typed.List(Number)([])’

Numbers.of(1, 2, 3).toString() // => ‘Typed.List(Number)([ 1, 2, 3 ])’

Typed lists may contain only items of that type and fail with error if attempted to do otherwise:

Numbers([2, 3]).toString() // => Typed.List(Number)([ 2, 3 ])

Numbers([1, 2, 3, "4", "5"]) // => TypeError: Invalid value: "4" is not a number

Numbers([1, 2, 3]).push(null) // => TypeError: Invalid value: "null" is not a number

Typed lists can also be named for convenience:

var Strings = List(String, "Strings")

Strings.of("hello", "world").toString() // => Strings([ "hello", "world" ])

List can be of a complex a specific record type & records can also have fields of typed list:

var Points = List(Point, "Points")
Points().toString() // => Points([])

ps = Points.of({x:3}, {y: 5}).toString()
ps.toString() // => Points([ Point({ "x": 3, "y": 0 }), Point({ "x": 0, "y": 5 }) ])'

ps.get(0) instanceof Point // => true
ps.get(1) instanceof Point // => true

ps.get(0).x // => 3
ps.get(1).y // => 5

ps.push({z:4, x:-4}).toJSON() // => [ { x: 3, y: 0 }, { x: 0, y: 5 }, { x: -4, y: 0 } ]

Points(ps.toJSON()).equals(ps) // => true
mapping lists form one type to other

One somewhat tricky thing about lists is that while they enforce certain type they can also be as easily converted to list of other type by simply mapping it:

ps = Points.of({x:1}, {x:2})
xs = ps.map(p => p.x)

ps.toString() // => Points([ Point({ "x": 1, "y": 0 }), Point({ "x": 2, "y": 0 }) ])
xs.toString() // => Typed.List(Number)([ 1, 2 ])

As you can see from example above original ps list was of Point records while mapped xs list is of numbers and that is refleced in the type of the list. Although given that JS is untyped language theer is no guarantee that mapping function will return values of the same type which makes things little more complex, result of such mapping will be list of union type of all types that mapping funciton produced (see types section for union types).

Map

You can define a typed map by providing Map the type for the key and the type for the value:

var {Map, Record} = require("typed-immutable")
var Product = Record({name: String}, "Product")

var Products = Map(Number, Product)

Products().toString() // ‘Typed.Map(Number, Product)({})’

Products([[1, {name: "Mapper 1000"}]]).toString() 
//Typed.Map(Number, Product)({ 1: Product({ "name": "Mapper 1000" }) })

Typed maps may contain only entries with key and value that match the specified type:

Products([[1, "Mapper 1000"]]) 
// => TypeError: Invalid value: Invalid data structure "Mapper 1000" was passed to Product

Products().set("P1", {name: "Mapper 1000"}) 
// TypeError: Invalid key: "P1" is not a number

// All keys in an object are strings, so this fails too:
Products({1: {name: "Mapper 1000"}}) // TypeError: Invalid key: "1" is not a number 

Note the last example - all keys in an object are strings so if you instantiate a map from an object the type of your key must be a string (or something that handles strings).

As with other types Typed maps can also be named for convenience:

var Products = Map(Number, Product, "Products")
Products([[1, {name: "Mapper 1000"}]]).toString() 
// Products({ 1: Product({ "name": "Mapper 1000" }) })

Types

As it was illustrated in above sections we strucutre our types using other types there for this libary supports most JS types out of the box and provides few extra to cover more complex cases.

JS native types

You can use Boolean Number String RegExp JS built-in constructs structures of those types.

Maybe

You can define an optional type using Maybe that will produce a type whos value can be undefined null or a value of the provided type:

var {Maybe} = require("typed-immutable")
var Color = Record({
  red: Number(0),
  green: Number(0),
  blue: Number(0),
  opacity: Maybe(Number)
})

Color().toJSON() // => { red: 0, green: 0, blue: 0, opacity: null }
Color({red: 200, opacity: 80}).toJSON() // => { red: 200, green: 0, blue: 0, alpha: 80 }
Color({red: 200, opacity: "transparent"}) // => TypeError: Invalid value for "opacity" field:
                                          // "transparent" is not nully nor it is of Number type

Union

A union type is a way to put together many different types. This lets you create list or records fields that can take either one of the several types:

var {Union} = require("typed-immutable")
var Form = Record({
  user: Union(Username, Email),
  password: String('')
})

var form = Form()
form.set('user', Username('gozala'))
form.set('user', Email('[email protected]'))

Custom Type

Library lets you declare your own custom types that then you can use in defining more complex types with records and lists:

var {Typed} = require("typed-immutable")
var Range = (from, to=+Infinity) =>
  Typed(`Typed.Number.Range(${from}..${to})`, value => {
    if (typeof(value) !== 'number') {
      return TypeError(`"${value}" is not a number`)
    }

    if (!(value >= from && value <= to)) {
      return TypeError(`"${value}" isn't in the range of ${from}..${to}`)
    }

    return value
  })

var Color = Record({
  red: Range(0, 255),
  green: Range(0, 255),
  blue: Range(0, 255)
})

Color({red: 20, green: 20, blue: 20}).toJSON() // => { red: 20, green: 20, blue: 20 }
Color({red: 20, green: 20, blue: 300}) // => TypeError: Invalid value for "blue" field:
                                       // "300" isn't in the range of 0..255

Color() // => TypeError: Invalid value for "red" field:
        // "undefined" is not a number

var Color = Record({
  red: Range(0, 255)(0),
  green: Range(0, 255)(0),
  blue: Range(0, 255)(0)
})

Color().toJSON() // => { red: 0, green: 0, blue: 0 }

As a matter of fact Typed contains bunch of other types including Typed.Number.Range similar to one from the example above.

Any type

While this defeats the whole purpose there are still cases where use of Any type may be a good short term solution. In addition as described in the section about list mapping lists could be mapped to arbitrary types and there are cases where result of mapping is List(Any):

var {Any} = require("typed-immutable")
var Box = Record({value: Any})

var v1 = Box({value: 5})
var v2 = v1.set("value", "hello")
var v3 = v2.set("value", v2)

v1.toString() // => Typed.Record({value: Any})({ "value": 5 })
v2.toString() // => Typed.Record({value: Any})({ "value": "hello" })
v3.toString() // => Typed.Record({value: Any})({ "value": Typed.Record({value: Any})({ "value": "hello" }) })

Contribution

  • Run npm start before npm test as the tests are ran on built code

License

MIT License

typed-immutable's People

Contributors

davecoates avatar donabrams avatar gozala avatar jharris4 avatar lukesneeringer avatar stutrek avatar udnisap 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  avatar

typed-immutable's Issues

Switch on Union type

Hello,
thanks for this useful library !
Is it possible to switch/if/match on an Union type to determine the underlying concrete type ?

Minor release

@stutrek @davecoates @udnisap

I have gone through today and tried to do "low hanging fruit" things, and move along the pull requests. We now only have three that are active, and two should be merged soon. Once #18 and #29 are merged, which can hopefully be in a couple of days, I would like to do a release.

I will write up a changelog and such and make a pull request beforehand, so everyone will be able to see it. Does anyone have any objections to this?

Reconsider duck typing records

At the moment if you define types with a following signatures:

const X = Record({x: 0}, 'X')
const Y = Record({y: 0}, 'Y')

const C = Record({ data: Union(X, Y) })

Then type union interprets data value not always as one would expected:

C({data: {x: 2}}) // => Typed.Record({data: Union(X, Y)})({ "data": X({ "x": 2 }) })
C({data: X({x: 5})}) // => Typed.Record({data: Union(X, Y)})({ "data": X({ "x": 5 }) })
C({data: Y({y: 3})}) // => Typed.Record({data: Union(X, Y)})({ "data": Y({ "y": 3 }) })

C({data: {y: 2}}) // => Typed.Record({data: Union(X, Y)})({ "data": X({ "x": 0 }) })

Most likely in last statement data field was expected to be an instance of Y instead of X, although given that {y: 2} can be read both as X and Y it was interpreted as a first type in the union X.

It maybe better to disallow passing untyped values all together in order to avoid this issue.

Process discussion

@stutrek @davecoates @udnisap

I have merged #24 and we now have automated testing on all pull requests. I also set up master as a protected branch in GitHub, which means it is no longer possible to push to it directly; anything has to be a pull request and Travis tests must pass.

I have not set up Travis to auto-publish to npm. I can do that if we want, although I might want some coaching from @udnisap about the right way to do that.

I also think it would be useful to have a couple of process discussions. What should our rule on PR merging be? This is a pretty small project, so I think that, in general, the rule should be a +1 from any other person.

I would also like to take a pass through the code and add more commenting (and probably a linter), as well as do a thorough sweep through the documentation. Would that be something that you all would welcome, or find annoying?

Finally, would it be valuable to have a (presumably low volume) Slack (or similar) channel, or do we want to continue to use GitHub for discussion? I know I am not always the best at checking GitHub; not sure if that is a concern for others.

Consider adding type coercion

It'd be nice to be able to coerce convertible types to other types.

const PersonRecord = Record({
  id: String,
  phone: Coerce(Union(Number, String), String),
}, 'Person');

In this hypothetical example if we did the following:

const p1 = PersonRecord({ id: 0, phone: 12318001800 });

accessing p1.phone would give us a string.

We'd have to keep a map of how we get from one type to others. This could be hard-coded or supplied at run-time.

Support for Map

I noticed there seems to be support for Map but it's not documented anywhere and has to be imported from 'typed-immutable/lib/map'. Any reason for that (ie. experimental, has issues)?

Type checking task

Really nice project.

One of the main benefits to having types in code is that during compile time (or during your transpiling process) you can be warned of places where you have set the wrong type or called using the wrong type.

Typescript for example will warn you if you do something like:

Point({x: "1", y: 1});

The above will output an error explaining you have used a string instead of number type.

Would building a task to check the codes type safety be within the scope of this project?

Maybe using esprima and the parsing down the tree?

It's something I might try and help with if you feel it's within the scope, but thought I would ask as it would be good to get your opinions on this before starting extra work on this.

New maintainer is needed

I am afraid I do not have time to work on this project, so if someone want's to step up and move it forward review / land pull requests respond to issues etc.

[feature] Adding functions to types for derived data?

I'm somewhat new to react/redux and have been struggling with how to handle "derived data." A trivialized example is given raw data like

let person = {first: 'Bob', last: 'Fish'}

what is the appropriate way to define person.fullName? Or should I just have personUtils.fullName(person)? It seems if I were to use typed-immutable to define the structure of my data I could also define methods/ property-getters to define derived data.

Good idea/ bad idea?

Support for Enumerable fields

Im proposing somethings like custom type Called Enum
(I didnt test this)

const Enum = (values) =>{
  if (typeof(values) !== 'array') {
    return TypeError(`"${value}" is not an array`);
  }
  const map = {};
  values.each((val) => {
    map[val] = val;
  });
  var validValues = values.toString();

  return Typed(`Enum(${ validValues})`, value => {
    if (!map[value]){
      return TypeError(`"${value}" in ${validValues}`);
    }
    return value
  })
}

var enRecord = Record({
  enumField: Enum(['A','B'])
})

enRecord.set('enumField', 'B') //Succeed
enRecord.set('enumField', 'C') //Throw an Error

Is there any other workarounds to this?

Handle recursive types?

Is there any way to handle recursive types? I.e.

import {Record, List} from 'typed-immutable';

var Node = Record({
    data: String,
    subNodes: List(Node)
});

One can't do the above, as Node is not defined yet, when defining subNodes. Is there a way to accomplish this now, or do you have plans for this capability?

One possibility would be to add a constant, say "CurrentType", that would tell Record to use it's type for that member when creating new records. I.e.:

import {Record, List, CurrentType} from 'typed-immutable';
var Node = Record({
    data: String,
    subNodes: List(CurrentType)
});

Thoughts?

React PropType validations

Hi,

I'm using typed-immutables in a react project. I have a type called Card and it's passed as a prop to CardComponent. Similarly there's CardList and CardListComponent

const Card = Record({
  id: String,
  title: String
}, 'Card')

export const CardList = List(Card, 'CardList') 

export default Card

How would I validate them with react PropTypes? Are there any methods that I can use, that go as follows

const CardComponent = (card) => {
  return (
    <div>${card.title}</div>
  )
}

CardComponent.propTypes = {
  card: Card.isRequired // or Card.isOptional
}

How do I go about doing those validations now? Validations for CardList seem to be harder. What would be a good way to implement these validations?

flatMap doesn't work

Calling flatMap on a List doesn't work. Can get around it by just doing a map then a flatten.

var {Record, List} = require("typed-immutable")
var Item = Record({
    ids: List(Number)
});

var Items = List(Item);
var a = new Item({ids: [1,2,3,4]})
var b = new Item({ids: [5,6]})
var items = new Items([a, b])

// Array [1, 2, 3, 4, 5, 6]
items.map(item => item.ids).flatten().toJS(); 
// doesn't work
// TypeError: Invalid value: Invalid data structure "1" was passed to Typed.Record({ids: Typed.List(Number)})
items.flatMap(item => item.ids).toJS()

// Using immutable List directly works
var immutable = require('immutable');
items = immutable.List([a,b]);
// Array [1, 2, 3, 4, 5, 6]
items.flatMap(item => item.ids).toJS()

Add functions?

Any plans to supports function signatures as parameters to a record? and maybe enums as well?

Typed data must carry type information

At the moment if you define types with a following signatures:

const X = Record({x: 0}, 'X')
const Y = Record({y: 0}, 'Y')

const C = Record({ data: Union(X, Y) })

Then type union interprets data value not always as one would expected:

C({data: {x: 2}}) // => Typed.Record({data: Union(X, Y)})({ "data": X({ "x": 2 }) })
C({data: X({x: 5})}) // => Typed.Record({data: Union(X, Y)})({ "data": X({ "x": 5 }) })
C({data: Y({y: 3})}) // => Typed.Record({data: Union(X, Y)})({ "data": Y({ "y": 3 }) })

C({data: {y: 2}}) // => Typed.Record({data: Union(X, Y)})({ "data": X({ "x": 0 }) })

Most likely in last statement data field was expected to be an instance of Y instead of X, although given that {y: 2} can be read both as X and Y it was interpreted as a first type in the union X. While this is bad (see #2) it does not seem to be a common issue in my experience.

What happens far more often in my experience is that new type is defined which supposed to be added to a type union (but for whatever reasons it was not added to type union):

const Z = Record({ z: 0 })

C({data: Z({z: 5})}) // => Typed.Record({data: Union(X, Y)})({ "data": X({ "x": 0 }) })

Now surprise and a problem is far worse in this case, as passed data field had a type but it got casted to a completely different type. There is also no simple way to avoid or spot this issue.

setSize may only downsize

While using this library with immutable-js, we are encountering a typedError "setSize may only downsize".
But there could be cases in which list size might increase. Why there is this restriction of downsize?

Better error messages

After using typed-immutable on multiple projects now the biggest problem that comes up is tracking down why a type error occurs - particularly when restoring a large nested structure from a raw JS object.

As an example:

const Address = Record({
  line1: String,
  city: String,
  country: String,
});

const Person = Record({
  id: Number,
  name: String,
  email: String,
  age: Number,
  address: Address,
});

const People = new Map(Number, Person);

new People([[1, { 
  id: 1,
  name: 'Bob',
  email: '[email protected]',
  age: 100,
  address: {
    line1: 5,
  }
}]]);

currently gives an error

TypeError: Invalid value: Invalid value for "address" field:
 Invalid value for "line1" field:
 "undefined" is not a string

This is ok when you have the context and know what structures it's referring to be in many cases you don't or the fields are generic enough that it's hard to identify where it comes from. It's also very useful to know the data that failed.

I've been looking at improving them, eg. the above is now:

    TypeError: Entry in Map(Number, Person) failed to satisfy value type:

    Key:
    1

    Value:
    {
      "id": 1,
      "name": "Bob",
      "email": "[email protected]",
      "age": 100,
      "address": {
        "line1": 5
      }
    }

    Failed to create instance of Person

      Value for field 'address' must satisfy type 'Address'

    Failed to create instance of Address

      Value for field 'line1' must satisfy type 'String'

    Invalid value: "5" is not a string

My only concern with adding this is that it will be a bit slower to generate this level of detail (eg. doing a JSON.stringify on value). In my use cases so far it wouldn't matter as I never catch the TypeError's - if they occur it's a bug that we fix.

Does anyone have any thoughts about this? Should it be opt in? Am I overthinking it? I also haven't measured anything yet - I think it may only become a problem if you were generating a lot of these (eg. iterating a large list attempting to instantiating record's and handling any errors).

@lukesneeringer @stutrek @udnisap

Symbol polyfill overwrites native Symbol type in typed.js

The polyfill for Symbol (https://github.com/typed-immutable/typed-immutable/blob/master/src/typed.js#L3) always overwrites the native Symbol type since the var declaration is hoisted outside of the if statement, so that polyfill actually will act as the following:

var Symbol
if (typeof(Symbol) === 'undefined') {
  Symbol = hint => `@@${hint}`
  Symbol.for = Symbol
}

This means that you cannot use Symbol as a type, i.e. Map(Symbol, String) will fail.

List.map does not work with mapper that returns null or undefined

Example:

var {Record, List} = require("typed-immutable");

var Point = Record({x: Number(0), y: Number(0)})
var Points = List(Point, "Points")
ps = Points.of({x:3}, {y: 5})
ps.map(a => a.x || null);

this throws a "TypeError: Cannot read property 'constructor' of null"

I believe what is happening here is that the type inferer is not correctly returning a new List(Maybe(Number)).

Add pattern matching

Currently used patten in most code is something along these lines:

const Model = Record({value: Number})

const Increment = Record({label: '+'});
const Decrement = Record({label: '-'});
const Action = Union(Increment, Decrement);

const update = (model, action) =>
  action instanceof Increment ? model.update('value', x => x + 1) :
  action instanceof Decrement ? model.update('value', x => x - 1) :
  model;

With a pattern matching it should be possible to turn it to something like this:

const update = Match(Match._, Match.pattern)
  .case(Increment, (model, _) => model.update('value', x => x + 1))
  .case(Decrement, (model, _) => model.update('value', x => x + 1))
  .default((model, _) => model)

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.