Giter Club home page Giter Club logo

ember-asset-loader's Issues

CSS asset loader, `onload` event called too soon. (can only repro on firefox)

I am running into an issue on firefox + lazy ember-engines.

Browsers fire the onload event for link elements after the file is downloaded and parsed but BEFORE the css is applied to the document. mdn docs

image

I believe the css loader should be updated with a RAF or something similar to ensure the browser has painted BEFORE allowing the engine to render.

On chrome, this is no big deal, on firefox, I can actually see the content from my async engine render (incorrectly) before css is applied and everything snaps into place.

My unacceptable workaround:

// beforeModel hook of the async engines application route
beforeModel() {
  if (browserIsFirefox) {
      return new RSVP.Promise((resolve) => {
          document.body.getBoundingClientRect(); // force a reflow
          getRequestAnimationFrame(resolve); // ensure there is at least 1 paint before moving forward
      });
  }
  return true;
}

I am about to launch a new feature on TheDyrt where you can repro this 100% of the time.

Improve CI to avoid dependency drift

Writing down tasks from our discussion at the contributors workshop:

  • Switch to yarn so we can use a lockfile.
  • Remove PhantomJS from .travis.yml (jQuery dropped support for Phantom 1.x and Ember dropped support for Phantom entirely.)
  • Use Travis stages to run tests with both yarn install and yarn install --no-lockfile.

cc @rwjblue @spencer516

API request: the ability to retrieve the list of assets loaded/in the cache

Hi, we have a use case where in order to prevent assets from being loaded too early and negatively impacting the page load time of a route, we would like to add an assert to ensure only the expected list of assets have been loaded at a particular lifecycle hook of a route. So if a new asset is added to be eagerly loaded, the assert would also need to be updated. Any thoughts? Thanks

fyi @sangm @chriskrycho

Add template helper for loading a bundle.

A template helper like this would allow folks to use lazy routeless engines with a nice(ish) syntax:

{{mount (load-engine 'engine-name-here')}}

Example implementation (thanks to @mike183):

import Ember from 'ember';

const { Helper, inject, getOwner } = Ember;

export default Helper.extend({

  assetLoader: inject.service("asset-loader"),

  compute: function([ engineName ]) {
    // Return engineName if engine is loaded
    if (this._engineName !== undefined && this._engineName === engineName) { 
      this._engineName = engineName; 
      return this._engineName;
    }

    // need to expose the ability to introspect a bundle's state (loaded, unloaded, etc)
    if (this.get('assetLoader').isLoaded(engineName)) {
      this._engineName = engineName;
      return this._engineName;
    }

    // Load unloaded engine
    this.get("assetLoader").loadBundle(engineName).then(() => {
      // Update this._engineName
      this._engineName = engineName;

      // Trigger recompution of helper
      this.recompute();
    });

    // Returning null ensures nothing is rendered
    return null;
  }
});

No manifest generated for engine when external addon extends from ember-asset-loader

I have an app already built that demonstrates this error. Please see this repo. In a nutshell, a fatal error is thrown when visiting an engine's route if that engine lists an external addon, which lists ember-asset-loader as a dependency, as a dependency.

Structure

Host app:

  • Depends on ember-engines
  • Depends on external addon ("some-addon")
  • Contains an engine that depends on "some-addon"

External addon ("some-addon"):

  • Depends on ember-asset-loader
  • index.js extends from manifest-generator.js

Error thrown:
The below error is thrown when visiting the engine's route

rsvp.js:26 Uncaught Error: Failed to load asset manifest. For browser environments, verify the meta tag with name "basic-engine/config/asset-manifest" is present. For non-browser environments, verify that you included the node-asset-manifest module. at Module.callback (asset-manifest.js:23) at Module.exports (loader.js:106) at Module._reify (loader.js:143) at Module.reify (loader.js:130) at Module.exports (loader.js:104) at requireModule (loader.js:27) at r (loader.js:176) at resolveInitializer (index.js:10) at registerInstanceInitializers (index.js:33) at loadInitializers (index.js:69)

Weird fingerprinting of images

I'm not 100% sure if this belongs here, but I think so.

I'm using lazyLoading engines.
In one of the engines, I have the following image:

<img src="assets/images/test.png">

When building, I use fingerprinting and a CDN with the following configuration:

// host-app's ember-cli-build.js
// ...

var cdnPrepend = false;
if (env === 'staging') {
  cdnPrepend = 'https://XXX.cloudfront.net/';
}
if (env === 'production') {
  cdnPrepend = 'https://YYY.cloudfront.net/';
}

var app = new EmberApp(defaults, {
 fingerprint: {
      enabled: isProductionLikeBuild,
      prepend: cdnPrepend,
      extensions: ['js', 'css', 'png', 'jpg', 'gif', 'map', 'svg']
    },
   assetLoader: {
      generateURI: function(filePath) {
        if (cdnPrepend) {
          // Prevent duplicate slashes
          return cdnPrepend.substr(0, cdnPrepend.length - 1) + filePath;
        }
        return filePath;
      }
    }
});

I get the image

<img src="https://XXX.cloudfront.net/assets/engines-dist/my-engine-name/assets/images/test-FINGERPRINT.png">

So basically, it is weirdly duplication the assets portion to before the engines-dist, which makes this fail. I'm not quite sure where/how this comes from.

Also, not sure if this is related, but the url is also wrongly rewritten if I use /assets/xxx. Leaving the leading slash doesn't work in development for me, but in production it lead to /https://XXX.cloudfront.net/assets/engines-dist/my-engine-name/assets/images/test-FINGERPRINT.png, which of course also doesn't work.

If I set lazyLoading: false, it works as expected.

I'm using:
[email protected]
[email protected]
[email protected]

How to handle running in Node

When running an Ember application in Node (such as through FastBoot), the asset-manifest module blows up since it can't read from the DOM (which doesn't exist).

There are patterns to handle this, but it raises the overarching issue of how should lazy assets be handled within a non-browser environment.

Open questions:

  • Should the Asset Manifest be present in Node? Or should it just be an empty config?
    • If the manifest is present, what should the loader functions do?
    • If the manifest is not present, what happens when a code path is hit that expects to load something?

My current thinking is:

In a Node process, all assets should be loaded upfront (need build targets in order to do this easily), but the manifest should still be present so that if a code path expects to load a bundle (or similar) it doesn't kill the app. This would mean that the loader functions should all return immediately resolving Promises (Promise.resolve()).

@nathanhammond @dgeb @rwjblue would greatly appreciate hearing your thoughts on this

Tasks:

  • Pre-load assets in non-browser environments (should be handle by whatever booted the application)
  • Enable asset-manifest to be present in non-browser environments [PR]
  • Return resolved Promises from loaders in non-browser environments [PR]

On Windows, engine asset paths use back slashes instead of forward slashes

I didn't notice this issue until after production.
It seems to be handled well by most browsers, but we have some users still on FF 48, which seems to sometimes crash when page loading inside a routable engine. However, this is ultimately irrelevant.

Our production app is https://www.typingtournament.com/
If you inspect the dom, you'll see the engine assets in <head> use back slashes instead of forward slashes.
I develop on Windows, so I assume that is the issue because I haven't seen anyone else report this before.

I added a console.log like this in my ember-cli-build.js file:

let app = new EmberApp(defaults, {
	...
	assetLoader: {
		generateURI: function(filePath){
			console.log(filePath);
			return filePath;
		}
	},
	...
});

and my build log showed this:

d:\src\edalive\typingtournament\client\src>ember build
\ Building\engines-dist\tt-app\assets\engine-vendor.js
\engines-dist\tt-app\assets\engine.js
\engines-dist\tt-teacher\assets\engine-vendor.css
\engines-dist\tt-teacher\assets\engine-vendor.js
\engines-dist\tt-teacher\assets\engine.css
\engines-dist\tt-teacher\assets\engine.js
\engines-dist\tt-web\assets\engine-vendor.css
\engines-dist\tt-web\assets\engine-vendor.js
\engines-dist\tt-web\assets\engine.css
\engines-dist\tt-web\assets\engine.js
cleaning up...
Built project successfully. Stored in "dist/".

I'll fix this with js string manipulation in the generateURI method for now, but this should probably also be handled better.

How use contentFor in engines

So, I have a engine with lazyLoading: false inside a fresh ember app. My engine have addons and one of them have the code below:

# index.js
/* eslint-env node */
'use strict';

module.exports = {
  name: 'ember-cli-neutral',

  contentFor(type, config) {
    if (type === 'head') {
      if (config.googleFonts) {
        let families = config.googleFonts.join('|');

        return '<link href="https://fonts.googleapis.com/css?family=' + families + '" rel="stylesheet">'; // eslint-disable-line
      }
    }
  }
};
# environment.js
/* eslint-env node */
'use strict';

module.exports = () => {
  const ENV = {
    googleFonts: [
      'Roboto:400,500'
    ]
  };

  return ENV;
};

If I run engine separately the link of google fonts works perfectly, but if I run inside the fresh ember app not.

Perhaps a feature for future or it's outside the scope of this project?

Multiple meta tags inserted into head-footer when multiple addons use ManifestGenerator

When an application has more than one ember addon installed that exports a ManifestGenerator-extended addon, a meta tag is inserted into the head-footer content-for block because each addon has a contentFor hook defined by ManifestGenerator:

contentFor: function(type, config) {

For example, with two addons that use ManifestGenerator, before post-processing, the index.html may look like:

<meta name="my-app/config/asset-manifest" content="%GENERATED_ASSET_MANIFEST%">
<meta name="my-app/config/asset-manifest" content="%GENERATED_ASSET_MANIFEST%">

After post-processing, the first meta tag is replaced by the contents of the asset manifests provided by both addons, so it has all of the correct content, but there is an extraneous meta tag that remains:

<meta name="my-app/config/asset-manifest" content="%7B%22bundles%22%3A%7B%22...">
<meta name="my-app/config/asset-manifest" content="%GENERATED_ASSET_MANIFEST%">

Ideally even if multiple addons consumed by the host app are using ManifestGenerator, only a single asset-manifest meta tag would be inserted, or the extra ones be removed during the post-processing when the manifest is being injected.

The loader when in node land does nothing, can we teach it to put link/script tags in the DOM?

Hey,
I am having an issue with lazy engines + fastboot resulting in a FOUC.

So far, I have nailed it down to 2 issues,

  1. This code from ember engines will always return true in fastboot... not sure how to fix that one. ember-engines/ember-engines#564
  2. Ember Asset Loader does nothing in node land/fastboot. So even if we were to fix that first bit, ember asset loader would not load the async engines css and js anyways.

I propose we teach each loader how to load itself in the browser and in node land.
In node land, it would just append a script/link tag to the dom.

Repeatedly performing npm-install fails with EISGIT: Appears to be a git repo or submodule

Get error EISGIT when performing npm-install

Error message:
npm ERR! path ...p/node_modules/ember-asset-loader
npm ERR! code EISGIT
npm ERR! git .../node_modules/ember-asset-loader: Appears to be a git repo or submodule.
npm ERR! git .../node_modules/ember-asset-loader
npm ERR! git Refusing to remove it. Update manually,
npm ERR! git or move it out of the way first.

Should not include empty files in generated asset manifest.

When working on a self contained engine it is quite possible to end up without any vendor'ed JS or CSS. In that scenario engine-vendor.css and engine-vendor.js are empty files.

I propose that empty files should be excluded from the generated asset-manifest.json by default.

Any objections?

option to make resetting asset state in tests less noisy

It would be great if there was a way to make this less noisy:

Ember.Logger.info('Resetting loaded asset state. This will attempt to restore the state of loaded assets to the last cached value. If an asset modified some global state, we cannot guarantee it will be reset. For more information see: https://github.com/trentmwillis/ember-asset-loader#resetting-test-state');

If it runs in afterEach after every test, that can really pollute a test run's output.

assetLoader Service is required in host route and component unit tests

Attempting to inject an unknown injection: 'service:assetLoader' is thrown when adding ember-engines to project with existing unit tests for routes and components. Can be worked around by adding service:assetLoader to the needs array, however this is not ideal and adds unnecessary cognitive load to the addon user.

CI/tests failing

After run yarn run test:sauce

Last login: Sun May 10 19:56:15 on ttys001
➜  ember-asset-loader git:(master) yarn run test:sauce
yarn run v1.22.4
$ ember test --config-file testem.sauce.js --test-port 7000
WARNING: Node v12.13.0 is not tested against Ember CLI on your platform. We recommend that you use the most-recent "Active LTS" version of Node.js. See https://git.io/v7S5n for details.
Could not start watchman
Visit https://ember-cli.com/user-guide/#watchman for more info.
DEPRECATION: Ember will stop including jQuery by default in an upcoming version. If you wish keep using jQuery in your application explicitly add `@ember/jquery` to your package.json
Environment: test
Warning: ignoring input sourcemap for vendor/ember/ember.debug.js because ENOENT: no such file or directory, open '/var/folders/0v/jpdg1flj3qscvbvt728pgt8c0000gn/T/broccoli-9719scuSj4FGFmkw/out-123-broccoli_debug_debug_7_vendor_js/vendor/ember/ember.debug.map'
Warning: ignoring input sourcemap for vendor/ember/ember-testing.js because ENOENT: no such file or directory, open '/var/folders/0v/jpdg1flj3qscvbvt728pgt8c0000gn/T/broccoli-9719scuSj4FGFmkw/out-147-broccoli_debug_debug_10_test_support_js/vendor/ember/ember-testing.map'
cleaning up...
Built project successfully. Stored in "/Users/villander/Projects/ember-asset-loader/tmp/class-tests_dist-zfQE2hCj.tmp".
not ok 1 SL_CHROME - [undefined ms] - error
    ---
        message: >
            Error: Browser exited unexpectedly
            Stderr: 
             Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.
                at launch (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/launch.js:21:28)
                at main (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/index.js:7:10)
                at Object.<anonymous> (/Users/villander/Projects/ember-asset-loader/node_modules/ember-cli-sauce/bin/cli.js:6:1)
                at Module._compile (internal/modules/cjs/loader.js:956:30)
                at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
                at Module.load (internal/modules/cjs/loader.js:812:32)
                at Function.Module._load (internal/modules/cjs/loader.js:724:14)
                at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
                at internal/main/run_main_module.js:17:11
            
            Stdout: 
             Bail out! Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.
            
            
        Log: |
            { type: 'error', text: 'Error: Browser exited unexpectedly' }
            {
              type: 'error',
              text: 'Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.\n' +
                '    at launch (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/launch.js:21:28)\n' +
                '    at main (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/index.js:7:10)\n' +
                '    at Object.<anonymous> (/Users/villander/Projects/ember-asset-loader/node_modules/ember-cli-sauce/bin/cli.js:6:1)\n' +
                '    at Module._compile (internal/modules/cjs/loader.js:956:30)\n' +
                '    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)\n' +
                '    at Module.load (internal/modules/cjs/loader.js:812:32)\n' +
                '    at Function.Module._load (internal/modules/cjs/loader.js:724:14)\n' +
                '    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)\n' +
                '    at internal/main/run_main_module.js:17:11\n'
            }
            {
              type: 'log',
              text: 'Bail out! Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.\n'
            }
    ...
not ok 2 SL_EDGE - [undefined ms] - error
    ---
        message: >
            Error: Browser exited unexpectedly
            Stderr: 
             Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.
                at launch (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/launch.js:21:28)
                at main (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/index.js:7:10)
                at Object.<anonymous> (/Users/villander/Projects/ember-asset-loader/node_modules/ember-cli-sauce/bin/cli.js:6:1)
                at Module._compile (internal/modules/cjs/loader.js:956:30)
                at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
                at Module.load (internal/modules/cjs/loader.js:812:32)
                at Function.Module._load (internal/modules/cjs/loader.js:724:14)
                at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
                at internal/main/run_main_module.js:17:11
            
            Stdout: 
             Bail out! Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.
            
            
        Log: |
            { type: 'error', text: 'Error: Browser exited unexpectedly' }
            {
              type: 'error',
              text: 'Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.\n' +
                '    at launch (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/launch.js:21:28)\n' +
                '    at main (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/index.js:7:10)\n' +
                '    at Object.<anonymous> (/Users/villander/Projects/ember-asset-loader/node_modules/ember-cli-sauce/bin/cli.js:6:1)\n' +
                '    at Module._compile (internal/modules/cjs/loader.js:956:30)\n' +
                '    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)\n' +
                '    at Module.load (internal/modules/cjs/loader.js:812:32)\n' +
                '    at Function.Module._load (internal/modules/cjs/loader.js:724:14)\n' +
                '    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)\n' +
                '    at internal/main/run_main_module.js:17:11\n'
            }
            {
              type: 'log',
              text: 'Bail out! Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.\n'
            }
    ...
not ok 3 SL_IE10 - [undefined ms] - error
    ---
        message: >
            Error: Browser exited unexpectedly
            Stderr: 
             Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.
                at launch (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/launch.js:21:28)
                at main (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/index.js:7:10)
                at Object.<anonymous> (/Users/villander/Projects/ember-asset-loader/node_modules/ember-cli-sauce/bin/cli.js:6:1)
                at Module._compile (internal/modules/cjs/loader.js:956:30)
                at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
                at Module.load (internal/modules/cjs/loader.js:812:32)
                at Function.Module._load (internal/modules/cjs/loader.js:724:14)
                at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
                at internal/main/run_main_module.js:17:11
            
            Stdout: 
             Bail out! Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.
            
            
        Log: |
            { type: 'error', text: 'Error: Browser exited unexpectedly' }
            {
              type: 'error',
              text: 'Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.\n' +
                '    at launch (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/launch.js:21:28)\n' +
                '    at main (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/index.js:7:10)\n' +
                '    at Object.<anonymous> (/Users/villander/Projects/ember-asset-loader/node_modules/ember-cli-sauce/bin/cli.js:6:1)\n' +
                '    at Module._compile (internal/modules/cjs/loader.js:956:30)\n' +
                '    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)\n' +
                '    at Module.load (internal/modules/cjs/loader.js:812:32)\n' +
                '    at Function.Module._load (internal/modules/cjs/loader.js:724:14)\n' +
                '    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)\n' +
                '    at internal/main/run_main_module.js:17:11\n'
            }
            {
              type: 'log',
              text: 'Bail out! Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.\n'
            }
    ...
not ok 4 SL_IE11 - [undefined ms] - error
    ---
        message: >
            Error: Browser exited unexpectedly
            Stderr: 
             Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.
                at launch (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/launch.js:21:28)
                at main (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/index.js:7:10)
                at Object.<anonymous> (/Users/villander/Projects/ember-asset-loader/node_modules/ember-cli-sauce/bin/cli.js:6:1)
                at Module._compile (internal/modules/cjs/loader.js:956:30)
                at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
                at Module.load (internal/modules/cjs/loader.js:812:32)
                at Function.Module._load (internal/modules/cjs/loader.js:724:14)
                at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)
                at internal/main/run_main_module.js:17:11
            
            Stdout: 
             Bail out! Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.
            
            
        Log: |
            { type: 'error', text: 'Error: Browser exited unexpectedly' }
            {
              type: 'error',
              text: 'Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.\n' +
                '    at launch (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/launch.js:21:28)\n' +
                '    at main (/Users/villander/Projects/ember-asset-loader/node_modules/saucie/lib/index.js:7:10)\n' +
                '    at Object.<anonymous> (/Users/villander/Projects/ember-asset-loader/node_modules/ember-cli-sauce/bin/cli.js:6:1)\n' +
                '    at Module._compile (internal/modules/cjs/loader.js:956:30)\n' +
                '    at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)\n' +
                '    at Module.load (internal/modules/cjs/loader.js:812:32)\n' +
                '    at Function.Module._load (internal/modules/cjs/loader.js:724:14)\n' +
                '    at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)\n' +
                '    at internal/main/run_main_module.js:17:11\n'
            }
            {
              type: 'log',
              text: 'Bail out! Error: Please, provide the username and the access key for SauceLabs. Use the option --help for more information.\n'
            }
    ...

1..4
# tests 4
# pass  0
# skip  0
# fail  4
Testem finished with non-zero exit code. Tests failed.

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
➜  ember-asset-loader git:(master) 

storeConfigInMeta

I wrongly assumed that Ember-CLI's storeConfigInMeta option would also apply to the config/asset-manifest meta tag.

Do you think that opting out of the meta tag is a realistic option?

Add additional acceptance-type tests

After #11 I would like to make sure there are more tests added. The current ones should cover most of the generation needs but the extensibility API could use work.

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.