Giter Site home page Giter Site logo

redux-persist-transform-encrypt's Introduction

redux-persist-transform-encrypt

npm CI

Encrypt your Redux store.

Maintenance notice

As of February 2, 2024, I will longer be maintaining redux-persist-transform-encrypt.

I have been supporting it as best I can these past few years, but the reality of it is I have not used redux-persist-transform-encrypt, redux-persist, or Redux since 2017.

Since I no longer use any of the technologies involved and don't have a good way of testing any potential changes, I am no longer in a position where I feel I can maintain this package to my desired standards.

Additionally, redux-persist as a project also seems dead, despite an attempted change in management.

Installation

redux-persist-transform-encrypt must be used in conjunction with redux-persist, so make sure you have that installed as well.

Yarn

yarn add redux-persist-transform-encrypt

npm

npm install redux-persist-transform-encrypt

Usage

Synchronous

import { persistReducer } from 'redux-persist';
import { encryptTransform } from 'redux-persist-transform-encrypt';

const reducer = persistReducer(
  {
    transforms: [
      encryptTransform({
        secretKey: 'my-super-secret-key',
        onError: function (error) {
          // Handle the error.
        },
      }),
    ],
  },
  baseReducer
);

Asynchronous

Asynchronous support was removed in v3.0.0, as it was never fully supported and is not able to be implemented correctly given the current constraints that redux-persist imposes on transforms. See #48 for more details.

Custom Error Handling

The onError property given to the encryptTransform options is an optional function that receives an Error object as its only parameter. This allows custom error handling from the parent application.

Secret Key Selection

The secretKey provided to encryptTransform is used as a passphrase to generate a 256-bit AES key which is then used to encrypt the Redux store.

You SHOULD NOT use a single secret key for all users of your application, as this negates any potential security benefits of encrypting the store in the first place.

You SHOULD NOT hard-code or generate your secret key anywhere on the client, as this risks exposing the key since the JavaScript source is ultimately accessible to the end-user.

If you are only interested in persisting the store over the course of a single session and then invalidating the store, consider using the user's access token or session key as the secret key.

For long-term persistence, you will want to use a unique, deterministic key that is provided by the server. For example, the server could derive a hash from the user's ID and a salt (also stored server-side) and then return that hash to the client to use to decrypt the store. Placing this key retrieval behind authentication would prevent someone from accessing the encrypted store data if they are not authenticated as the user.

redux-persist-transform-encrypt's People

Contributors

7rulnik avatar dependabot[bot] avatar ekynoxe avatar maxdeviant avatar stovmascript 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

redux-persist-transform-encrypt's Issues

Issue with REHYDRATE

I am using "redux-persist": "4.10.2", "redux-persist-transform-encrypt": "1.0.2" in my project.

I have recently added "redux-persist-transform-encrypt" to project. And getting issue when I try to refresh page in browser.

I have customized REHYDRATE action and was getting all of my reducers in action.payload in reducer. By using that values I used to perform some customizations. But after integrating "redux-persist-transform-encrypt" I found that reducer value is coming out to be null.

And if I use ayncEncryptor, for each reducer I am getting {"" : random_number}.

Otherwise encryption, decryption is working as expected.

Cannot save simple text as state.

I have a setup where I have multiple reducers and I only persist a part of the state coming from the reducers. For this I have the combineReducers reducers like:

combineReducers(
      reducer1: ...some reducer,
      reducer2: persistReducer({
            key: "foo",
            storage: createIdbStorage({ name: "foo", storeName: "foo" })
            transforms: [createEncryptor({
                secretKey: 'mykey',
            }]
        },
       ...some reducer
      )
)

Default state:

{
 reducer1: {/*some data*/},
 reducer2: {
      bar: {
                 key: "hello"
             },
       my:  "olleh"
     }
}

This persisting and ecrypt/decrypt will cause:

{
 reducer1: {/*some data*/}, //this is untouched as was never persisted
 reducer2: { 
      bar: {
                 key: "hello" //this works 
             },
       my:  null //returns null as JSON.parse cannot parse plain string
     }
}

We could simply add return the decrypted value at sync.js Line:16 instead if a throw error. But that will remove the way how the correctness of the key is checked.

Issues with installing, Unable to resolve module `events`

I tried to install this library, but sadly i have this error, after trying to install using yarn(1.19.1) or npm(6.9.0). I also tried removing node_modules and cleaning cache.
error: bundling failed: Error: Unable to resolve module eventsfromC:\graditude\minute-gratitude-journal\node_modules\readable-stream\lib_stream_readable.js: Module events does not exist in the Haste module map
Even after cloning it and installing it manually as local package with (events) I have another issues, which is:
Can't find variable Buffer, also I tried to add it to this library package json, but it still fails.
obraz

Error: (0,_reduxPersistTransformEcnrypt2.default) is not a function

With Version 1.0.0 I get the following error

(0,_reduxPersistTransformEncrypt2.default) is not a function.
(In '(0,_reduxPersistTransformEncrypt2.default)(
{secretKey:'my-super-secret-key'}
)', '(0,_reduxPersistTransformEncrypt2.default)' is undefined)

with this line of code:

import createEncryptor from 'redux-persist-transform-encrypt';
const encryptor = createEncryptor({
  secretKey: 'my-super-secret-key'
});

My package.json looks like this:

"dependencies": {
    "react": "15.4.1",
    "react-native": "^0.39.2",
    "react-redux": "^5.0.1",
    "redux": "^3.6.0",
    "redux-persist": "^4.0.1",
    "redux-persist-transform-encrypt": "^1.0.0"
  },

post-serialize transforms

Hey @maxdeviant, I noticed for this transform as well as the compress transform we need to serialize the state before transforming it. This leads to potentially multiple serializations, which while not terrible since stringifying a string is super cheap, it is not ideal.

Any thoughts on how to solve this? The simplest albeit ugly solution would be to split out transforms into preSerializeTransforms, postSerializeTransforms. Another option would be to require a serialize transform before the encrypt/compress like this: transforms: [serialize, encrypt] and then throw an error if encrypt receives non-serialized data.

Thoughts?

Module not found issue in react

I am facing an error on the latest version.
The error is Module not found: Error: Default condition should be last one.
Before i was using this import
import {encryptTransform} from "redux-persist-transform-encrypt";
How to fix this issue?
using latest node version
@maxdeviant

How to update the secretKey async

On app load, I want to do the following:

  1. Check expo-secure-store async for a previous stored encryption secret key
  2. If it doesn't exist, create a random uuid and save it in expo-secure-store async and use it as the secret key for encryption
  3. If it does exist, use it as the secret key for encryption

However, because this process is async, I don't see how to export the store and persistor.

const persistConfig = {
  key: 'root',
  version: 2,
  storage: FSStorage(),
  transforms: [
    encryptTransform({
      secretKey: 'my-super-secret-key',
      onError: function (error) {
        // Handle the error.
      },
    }),
  ],
};

export const store = configureStore({
  reducer: rootReducer,
  enhancers: [autoRehydrate()]
});

export const persistor = persistStore(store, persistConfig);

Does anyone have an example of how to update the secretKey after the app has loaded, without hard-coding it?

My persist didnt encrypted

image
image

I tried to use this library to encrypted my persist storage, but when i run the app and check in the browser storage, the redux state didnt get encrypted, can you help me? thanks

Example of how to securely generate the secret key

I imagine that many apps using this library would want to generate an encryption key rather than hardcoding it in the code and having an example of this would be great.

I have been combing the Internet to find out how to do this with no luck. I realize this isn't a function of the library but having an example or suggestion of how to generate the secret key would make it easier for people to use it without hardcoding a secret key in their code.

need some example about the transform

hi @maxdeviant ,

thanks for your work. but i think i cannot use this transform follow your example in docs.

i create a new project, and use redux-persist, it work well.
but when i use the transform,

Uncaught TypeError: Cannot read property 'settings' of undefined

could you give me a workable demo project ?

Vitest failing with message - Failed to resolve entry for package "redux-persist-transform-encrypt"

Hi.

Thanks for the wonderful package. I am using Vite it seems to be working fine, but when I run with Vitest, the tests are failing with this message. Not sure what to do, because I don't actually want to encrypt the store in the test, but I can't seem to put in a conditional that will ignore it. I'm using the same setup as the sample e.g.

transform: [
encryptTransform({
secretKey
})
]

Error: Failed to resolve entry for package "redux-persist-transform-encrypt". The package may have incorrect main/module/exports specified in its package.json.
❯ packageEntryFailure node_modules/vite/dist/node/chunks/dep-e8f070e8.js:23382:11
❯ resolvePackageEntry node_modules/vite/dist/node/chunks/dep-e8f070e8.js:23379:5
❯ tryNodeResolve node_modules/vite/dist/node/chunks/dep-e8f070e8.js:23113:20
❯ Context.resolveId node_modules/vite/dist/node/chunks/dep-e8f070e8.js:22874:28
❯ Object.resolveId node_modules/vite/dist/node/chunks/dep-e8f070e8.js:42847:46
❯ TransformContext.resolve node_modules/vite/dist/node/chunks/dep-e8f070e8.js:42575:23
❯ normalizeUrl node_modules/vite/dist/node/chunks/dep-e8f070e8.js:40500:34
❯ async file:/C:/projects/digital-patient-experience/node_modules/vite/dist/node/chunks/dep-e8f070e8.js:40651:47
❯ TransformContext.transform node_modules/vite/dist/node/chunks/dep-e8f070e8.js:40577:13
❯ Object.transform node_modules/vite/dist/node/chunks/dep-e8f070e8.js:42919:30
❯ loadAndTransform node_modules/vite/dist/node/chunks/dep-e8f070e8.js:53385:29

Is there some setup/config that I might be missing?

Even if I don't use the transform and just have encryptConfig somewhere in the file not doing anything it still fails.

Thanks very much,
J

Add configurable error handling/logging system

Currently we just call console.(log|info|warn|error) to report issues back to the user.

We should be able to pass in a custom error handler/logger that is configurable by the library consumer so usage is more flexible.

Question: Why the Readme says that you should not generate your secret key on the client ?

On the Readme I can read the following thing:

You SHOULD NOT hard-code or generate your secret key anywhere on the client, as this risks exposing the key since the JavaScript source is ultimately accessible to the end-user.

I totally understand that you should not hard-code it or store on it on the JS side but concerning the generation part I have trouble understanding why it's not a good practice.

What about generating a random uuid using this lib and store it on the keychain ? It seems secure enough for me but I may be missing something.

PS: thanks for the lib @maxdeviant, it was very helpful.

EPEERINVALID

npm ERR! peerinvalid The package [email protected] does not satisfy its siblings' peerDependencies requirements!
npm ERR! peerinvalid Peer [email protected] wants redux-persist@^3.0

Can you please set redux-persist-transform-encrypt to require the latest version of redux-persist? Thanks a lot in advance!

Get store current encrypted state

Hello,

for a ssr app I need to get current encrypted state.

const finalState = store.getState(); -> const finalState = store.getEncryptedState();

Is there a way to achieve that in an elegant way and without other libraries? Thanks.

expected outbound state to be a string on latest redux-persist

expected outbound state to be a string

on react-native

import createEncryptor from 'redux-persist-transform-encrypt';
import { persistStore, persistReducer, createTransform } from 'redux-persist';
import storage from 'redux-persist/lib/storage';

const configPersist = {
  key: 'app',
  storage,
  whitelist: [
    'app',
    'user',
  ],
  transforms: [
     encryptor,
  ],
};

const reducerWithPersist = persistReducer(configPersist, reducers);

persistor.purge() after error

When decryption fails (which triggers the onError event handler) the data in the resulting store may not be formed as expected. The store will NOT be automatically reverted to initialState. I do not suggest that it automatically be reverted, but rather I suggest an addition to documentation.

Since people commonly rely on initialState "guaranteeing" a certain shape for the store, when decryption fails this will result in errors such as "Cannot read property 'foo' of undefined" or "Cannot destructure property 'foo' of 'bar' as it is null". This can happen if you're using a cookie or other expiring token as secret key, and when that token expires, this will result in the the app crashing!

There is a simple solution to this, but I could not find it documented anywhere:

  onError: error => {
    persistor.purge()
  },

This will revert the store to initialState, and occurs before selectors, etc - so you will not get the error or crash.

I suggest that the above be added to the readme, to provide an obvious example of how to reset state and avoid runtime errors. I'd be happy to open a PR for this. I hope this helps someone!

Add encryption to project where users already have unencrypted data

Hello,

i have an app in production that uses the unencrypted asyncstore. Now I want to add encryption of this stroe as a new feature.

However, after installing according to your manual, I get

Expected outbound state to be a string.

This seems to be a logical Error since, upon app start, the store is an unencrypted object instead of a hashed string.

How can I activate the encryption transformer at the "next" apostate "rest" instead of the first "open"?

Thank you and best regards

"redux-persist": "6.0.0",
"redux-persist-transform-encrypt": "^4.0.0",

`

import logger from 'redux-logger'
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './reducers'
import thunk from 'redux-thunk'
import { persistStore, persistReducer } from 'redux-persist'
import AsyncStorage from '@react-native-async-storage/async-storage';
import { encryptTransform } from 'redux-persist-transform-encrypt';

const middleware = [thunk, logger];
//middleware.push(createLogger());
const persistConfig = {
key: 'root',
storage: AsyncStorage,
transforms: [
encryptTransform({
secretKey: 'my-super-secret-key',
onError: function (error) {
console.log("ecrypitError",error)
// Handle the error.
},
}),
],
}

const persistedReducer = persistReducer(persistConfig, rootReducer)

export const store = createStore(
persistedReducer,
applyMiddleware(...middleware)
)
export const persistor = persistStore(store)

export default () => {
return { store, persistor }
}
`

Add support for asynchronous encryption

Asynchronous support was previously added in #5.

However, it was never fully working properly, as redux-persist does not support asynchronous transforms. Support for asynchronous transforms is tracked by rt2zz/redux-persist#303.

Once redux-persist supports asynchronous transforms then so can redux-persist-transform-encrypt.

Failed to resolve the path

Error--
redux-persist-transform-encrypt from file /Users/netsmartz/Desktop/AgencyMobileApplication/src/config/ConfigureStore.js, the package /Users/netsmartz/Desktop/AgencyMobileApplication/node_modules/redux-persist-transform-encrypt/package.json was successfully found. However, this package itself specifies a main module field that could not be resolved (/Users/netsmartz/Desktop/AgencyMobileApplication/node_modules/redux-persist-transform-encrypt/index. Indeed, none of these files exist:

Current Version -- 4.0.0 and even with latest 5.0.1. i am having the same issue

Fix failing async test

Currently the tests for the async functionality (added in #5) are failing, as noted in this comment:

Also, as you might have noticed, the test for the async way was failing. I've wrapped the stream in a promise that will resolve with the completed string when it's ready. But still the encryption test passes and decryption fails. I think the encryption is passing only due to the test state object being really small and it manages to resolve before expect() starts working.

These should be fixed.

Securing the secret key

Question: Any tips on what to use as a secret key that would be secure?

Being this is normally going to be client code, using a hard-coded string as the key would be rather insecure, as anyone gaining access to the data would probably also extract strings from the app binary.

Perhaps using something like an MD5 of the device ID could be ok.

Any recommendations?

Issues using with redux-persist and AsyncStorage

I'm using redux-persist and AsyncStorage to persist my redux state, but I'm running into issues using this library with them. I've attempted using both the sync and async libs, but both give me errors:

img_0047

img_0048

Required key

Do I really need to pass scretKey and key ? If I don't pass key in persistReducer I receive an error (Error : key is required in persistor config) ...

const reducers = combineReducers({
  ...
});

const middlewares = applyMiddleware(error, thunk);

const encryptor = createEncryptor({
  secretKey: 'portfolio',
  onError: error => {
    // Handle the error
  }
})

const persistedReducer = persistReducer(
  {
    key: 'portfolio',
    storage,
    transforms: [encryptor]
  },
  reducers
);

const store = createStore(
  persistedReducer,
  initialState,
  middlewares,
  window.devToolsExtension && window.devToolsExtension()
);

const persistor = persistStore(store);

Error: Unable to resolve module 'events'

I'm getting an error if I try to import this module into a React Nativeapp.
I installed the redux-persist-transform-encryptwith

npm i --save redux-persist-transform-encrypt

Then i try to import the module as follows:

import createEncryptor from 'redux-persist-transform-encrypt'

This will give me the following error:

Unable to resolve module events from /redux-persist-transform-encrypt/node_modules/readable-stream/lib/_stream_readable.js

Any ideas what the problem could be?

ERROR: (0, _reduxPersistTransformEncrypt.default) is not a function

ERROR TypeError: (0, _reduxPersistTransformEncrypt.default) is not a function. (In '(0, _reduxPersistTransformEncrypt.default)({
secretKey: _LoginPasswordKey.reduxEncrypt,
onError: function onError(error) {}
})', '(0, _reduxPersistTransformEncrypt.default)' is undefined)

Package Version: "redux-persist-transform-encrypt": "^3.0.1",

System:
OS: macOS 10.15.7
CPU: (8) x64 Intel(R) Core(TM) i5-8257U CPU @ 1.40GHz
Memory: 432.12 MB / 16.00 GB
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 14.15.0 - ~/.nvm/versions/node/v14.15.0/bin/node
Yarn: 1.22.10 - ~/.nvm/versions/node/v14.15.0/bin/yarn
npm: 6.14.8 - ~/.nvm/versions/node/v14.15.0/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.10.0 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: iOS 14.1, DriverKit 19.0, macOS 10.15, tvOS 14.0, watchOS 7.0
Android SDK:
API Levels: 28, 29, 30
Build Tools: 28.0.3, 29.0.2, 29.0.3, 30.0.2
System Images: android-30 | Google APIs Intel x86 Atom
Android NDK: Not Found
IDEs:
Android Studio: 4.1 AI-201.8743.12.41.6858069
Xcode: 12.1/12A7403 - /usr/bin/xcodebuild
Languages:
Java: 1.8.0_272 - /usr/bin/javac
Python: 2.7.16 - /usr/bin/python
npmPackages:
@react-native-community/cli: Not Found
react: ^16.14.0 => 16.14.0
react-native: ^0.63.3 => 0.63.3
react-native-macos: Not Found
npmGlobalPackages:
react-native: Not Found

How to use .env.local variables with next js13 app directory

This is my code:
import { combineReducers } from 'redux'
import { persistReducer } from 'redux-persist'
import storage from 'redux-persist/lib/storage'
import moneyReducer from './Features/moneySlice'
import podReducer from './Features/PrintonDemand/podSlice'
import profileReducer from './Features/PrintonDemand/profileSlice'
import ButtonReducer from './Features/Buttons/ButtonsSlice'
import { encryptTransform } from 'redux-persist-transform-encrypt';
import SeasonsSlice from './Features/Seasons/SeasonsSlice'

const rootPersistConfig = {

key: 'persist-key',
storage: storage, 
transforms:[
    encryptTransform({
      secretKey: process.env.KEY,
      onError: function (error :Error) {
        // Handle the error.
      },
    }),
  ],

}

const rootReducer = combineReducers({

flag: podReducer,
profile: profileReducer,
money:moneyReducer,
Buttons:ButtonReducer,
Seasons: SeasonsSlice,

})
export default persistReducer(rootPersistConfig, rootReducer),
.env.local: KEY='key'

and it shows this error:Error: redux-persist-transform-encrypt: No secret key provided.:

when state has string type property, decryption failed

This is #21 explanation.
When state type has string type property, encryption is success, but decryption will be failed.
Here is an example.

// baseReducer output i.e. state is as follows
/*
const state1 = {
  key1: 'string value',
  key2: {
    innerKey: 100,
    innerKey2: 'also string value',
  },
}
*/

const encryptor = createEncryptor({
  secretKey: 'my-super-secret-key'
})

const reducer = persistReducer(
  {
    transforms: [encryptor]
  },
  baseReducer
)

state1 itself is object, but redux-persist persists value per state property.
It is probably because redux-persist config has blacklist whitelist config.
https://github.com/rt2zz/redux-persist/blob/master/docs/api.md#type-persistconfig

When key1: 'string value' is encrypted and decrypted, it is failed.

Could not run jest unit testcases on updating redux-persis-transform-encrypt package version from "3.0.1"to "5.0.0"

It throws below error on running unit testcases

Fatal error in , line 0

Fatal JavaScript invalid size error 174895934

#FailureMessage Object: 0000000CB33FB7B0
1: 00007FF79A1807BF node_api_throw_syntax_error+175823
2: 00007FF79A09630F v8::CTypeInfoBuilder::Build+12559
3: 00007FF79AF52AB2 V8_Fatal+162
4: 00007FF79AA4DD15 v8::internal::FactoryBasev8::internal::Factory::NewFixedArray+101
5: 00007FF79A8D3253 v8::internal::FeedbackNexus::ic_state+65795
6: 00007FF79A8F1DD0 v8::debug::Script::GetIsolate+15600
7: 00007FF79A73DEA0 v8::internal::CompilationCache::IsEnabledScriptAndEval+25952
8: 00007FF79AC4B1F1 v8::internal::SetupIsolateDelegate::SetupHeap+558193
9: 00007FF71B011B90

v4 Not working with react-native

When using version 4.0.0 of this library there are errors when starting the app:

Error: redux-persist-transform-encrypt: Expected outbound state to be a string.
and
Error: Native crypto module could not be used to get secure random number.

Downgrading to 3.x solves these issues

sync vs async

what are the differences of sync and async method?

both of them work on react-native?

Add ability to migrate from one secret key to another

I have tried to update the secret key. But no luck as the library returns error.

Error: Could not decrypt state. Please verify that you are using the correct secret key

Do anyone has any idea about implementing or updating the secret key used for the decryption. It's ok if have to throw old saved state.

Update to ES2015

Initially, we were only using regular JS for the library, but I think we should probably move to ES2015 and compile with Babel before publishing.

Is the package supported in react-web?

In readme there is no information, does is the package supported in react-web, or only in react-native?
There is only examples/ReactNativeExample.

Lack of answer to @pitops question #28 (comment)_ makes think that there is no way to keep secretkey in JavaScript in browser, that will not be visible to potential hackers.
Am I wrong?

Missing es2015, caused by .babelrc

Using this module in React Native causes the packager to throw a transform error (couldn't find preset es2015).
A solution would be to add a . npmignore file were this .babelrc file is ignored, either way a improvement to exclude unnecessary files when installing this module with NPN.

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.