GithubHelp home page GithubHelp logo

sindresorhus / alfy Goto Github PK

View Code? Open in Web Editor NEW
2.6K 24.0 121.0 628 KB

Create Alfred workflows with ease

License: MIT License

JavaScript 88.33% Shell 6.63% TypeScript 5.04%
macos alfy alfred alfred-workflow alfred3-workflow framework nodejs javascript workflow

alfy's Introduction

Alfy

Create Alfred workflows with ease

Highlights

  • Easy input↔output.
  • Config and cache handling built-in.
  • Fetching remote files with optional caching.
  • Publish your workflow to npm.
  • Automatic update notifications.
  • Easily testable workflows.
  • Finds the node binary.
  • Support for top-level await.
  • Presents uncaught exceptions and unhandled Promise rejections to the user.
    No need to manually .catch() top-level promises.

Prerequisites

You need Node.js 18+ and Alfred 4 or later with the paid Powerpack upgrade.

Install

npm install alfy

Usage

IMPORTANT: Your script will be run as ESM.

  1. Create a new blank Alfred workflow.

  2. Add a Script Filter (right-click the canvas → InputsScript Filter), set Language to /bin/bash, and add the following script:

./node_modules/.bin/run-node index.js "$1"

We can't call node directly as GUI apps on macOS doesn't inherit the $PATH.

Tip: You can use generator-alfred to scaffold out an alfy based workflow. If so, you can skip the rest of the steps, go straight to the index.js and do your thing.

  1. Set the Keyword by which you want to invoke your workflow.

  2. Go to your new workflow directory (right-click on the workflow in the sidebar → Open in Finder).

  3. Initialize a repo with npm init.

  4. Add "type": "module" to package.json.

  5. Install Alfy with npm install alfy.

  6. In the workflow directory, create a index.js file, import alfy, and do your thing.

Example

Here we fetch some JSON from a placeholder API and present matching items to the user:

import alfy from 'alfy';

const data = await alfy.fetch('https://jsonplaceholder.typicode.com/posts');

const items = alfy
	.inputMatches(data, 'title')
	.map(element => ({
		title: element.title,
		subtitle: element.body,
		arg: element.id
	}));

alfy.output(items);

More

Some example usage in the wild: alfred-npms, alfred-emoj, alfred-ng.

Update notifications

Alfy uses alfred-notifier in the background to show a notification when an update for your workflow is available.

Caching

Alfy offers the possibility of caching data, either with the fetch or directly through the cache object.

An important thing to note is that the cached data gets invalidated automatically when you update your workflow. This offers the flexibility for developers to change the structure of the cached data between workflows without having to worry about invalid older data.

Publish to npm

By adding alfy-init as postinstall and alfy-cleanup as preuninstall script, you can publish your package to npm instead of to Packal. This way, your packages are only one simple npm install command away.

{
	"name": "alfred-unicorn",
	"version": "1.0.0",
	"description": "My awesome unicorn workflow",
	"author": {
		"name": "Sindre Sorhus",
		"email": "[email protected]",
		"url": "https://sindresorhus.com"
	},
	"scripts": {
		"postinstall": "alfy-init",
		"preuninstall": "alfy-cleanup"
	},
	"dependencies": {
		"alfy": "*"
	}
}

Tip: Prefix your workflow with alfred- to make them easy searchable through npm.

You can remove these properties from your info.plist file as they are being added automatically at install time.

After publishing your workflow to npm, your users can easily install or update the workflow.

npm install --global alfred-unicorn

Tip: instead of manually updating every workflow yourself, use the alfred-updater workflow to do that for you.

Testing

Workflows can easily be tested with alfy-test. Here is a small example.

import test from 'ava';
import alfyTest from 'alfy-test';

test('main', async t => {
	const alfy = alfyTest();

	const result = await alfy('workflow input');

	t.deepEqual(result, [
		{
			title: 'foo',
			subtitle: 'bar'
		}
	]);
});

Debugging

When developing your workflow it can be useful to be able to debug it when something is not working. This is when the workflow debugger comes in handy. You can find it in your workflow view in Alfred. Press the insect icon to open it. It will show you the plain text output of alfy.output() and anything you log with alfy.log():

import alfy from 'alfy';

const unicorn = getUnicorn();
alfy.log(unicorn);

Environment variables

Alfred lets users set environment variables for a workflow which can then be used by that workflow. This can be useful if you, for example, need the user to specify an API token for a service. You can access the workflow environment variables from process.env. For example process.env.apiToken.

API

alfy

input

Type: string

Input from Alfred. What the user wrote in the input box.

output(list, options?)

Return output to Alfred.

list

Type: object[]

List of object with any of the supported properties.

Example:

import alfy from 'alfy';

alfy.output([
	{
		title: 'Unicorn'
	},
	{
		title: 'Rainbow'
	}
]);
options

Type: object

rerunInterval

Type: number (seconds)
Values: 0.1...5.0

A script can be set to re-run automatically after some interval. The script will only be re-run if the script filter is still active and the user hasn't changed the state of the filter by typing and triggering a re-run. More info.

For example, it could be used to update the progress of a particular task:

import alfy from 'alfy';

alfy.output(
	[
		{
			title: 'Downloading Unicorns…',
			subtitle: `${progress}%`,
		}
	],
	{
		// Re-run and update progress every 3 seconds.
		rerunInterval: 3
	}
);

log(value)

Log value to the Alfred workflow debugger.

matches(input, list, item?)

Returns an string[] of items in list that case-insensitively contains input.

import alfy from 'alfy';

alfy.matches('Corn', ['foo', 'unicorn']);
//=> ['unicorn']
input

Type: string

Text to match against the list items.

list

Type: string[]

List to be matched against.

item

Type: string | Function

By default, it will match against the list items.

Specify a string to match against an object property:

import alfy from 'alfy';

const list = [
	{
		title: 'foo'
	},
	{
		title: 'unicorn'
	}
];

alfy.matches('Unicorn', list, 'title');
//=> [{title: 'unicorn'}]

Or nested property:

import alfy from 'alfy';

const list = [
	{
		name: {
			first: 'John',
			last: 'Doe'
		}
	},
	{
		name: {
			first: 'Sindre',
			last: 'Sorhus'
		}
	}
];

alfy.matches('sindre', list, 'name.first');
//=> [{name: {first: 'Sindre', last: 'Sorhus'}}]

Specify a function to handle the matching yourself. The function receives the list item and input, both lowercased, as arguments, and is expected to return a boolean of whether it matches:

import alfy from 'alfy';

const list = ['foo', 'unicorn'];

// Here we do an exact match.
// `Foo` matches the item since it's lowercased for you.
alfy.matches('Foo', list, (item, input) => item === input);
//=> ['foo']

inputMatches(list, item?)

Same as matches(), but with alfy.input as input.

If you want to match against multiple items, you must define your own matching function (as shown here). Let’s extend the example from the beginning to search for a keyword that appears either within the title or body property or both.

import alfy from 'alfy';

const data = await alfy.fetch('https://jsonplaceholder.typicode.com/posts');

const items = alfy
	.inputMatches(
		data,
		(item, input) =>
			item.title?.toLowerCase().includes(input) ||
			item.body?.toLowerCase().includes(input)
	)
	.map((element) => ({
		title: element.title,
		subtitle: element.body,
		arg: element.id,
	}));

alfy.output(items);

error(error)

Display an error or error message in Alfred.

Note: You don't need to .catch() top-level promises. Alfy handles that for you.

error

Type: Error | string

Error or error message to be displayed.

fetch(url, options?)

Returns a Promise that returns the body of the response.

url

Type: string

URL to fetch.

options

Type: object

Any of the got options and the below options.

json

Type: boolean
Default: true

Parse response body with JSON.parse and set accept header to application/json.

maxAge

Type: number

Number of milliseconds this request should be cached.

resolveBodyOnly

Type: boolean
Default: true

Whether to resolve with only body or a full response.

import alfy from 'alfy';

await alfy.fetch('https://api.foo.com');
//=> {foo: 'bar'}

await alfy.fetch('https://api.foo.com', {
	resolveBodyOnly: false 
});
/*
{
	body: {
		foo: 'bar'
	},
	headers: {
		'content-type': 'application/json'
	}
}
*/
transform

Type: Function

Transform the response body before it gets cached.

import alfy from 'alfy';

await alfy.fetch('https://api.foo.com', {
	transform: body => {
		body.foo = 'bar';
		return body;
	}
})

Transform the response.

import alfy from 'alfy';

await alfy.fetch('https://api.foo.com', {
	resolveBodyOnly: false,
	transform: response => {
		response.body.foo = 'bar';
		return response;
	}
})

You can also return a Promise.

import alfy from 'alfy';
import xml2js from 'xml2js';
import pify from 'pify';

const parseString = pify(xml2js.parseString);

await alfy.fetch('https://api.foo.com', {
	transform: body => parseString(body)
})

config

Type: object

Persist config data.

Exports a conf instance with the correct config path set.

Example:

import alfy from 'alfy';

alfy.config.set('unicorn', '🦄');

alfy.config.get('unicorn');
//=> '🦄'

userConfig

Type: Map

Exports a Map with the user workflow configuration. A workflow configuration allows your users to provide configuration information for the workflow. For instance, if you are developing a GitHub workflow, you could let your users provide their own API tokens.

See alfred-config for more details.

Example:

import alfy from 'alfy';

alfy.userConfig.get('apiKey');
//=> '16811cad1b8547478b3e53eae2e0f083'

cache

Type: object

Persist cache data.

Exports a modified conf instance with the correct cache path set.

Example:

import alfy from 'alfy';

alfy.cache.set('unicorn', '🦄');

alfy.cache.get('unicorn');
//=> '🦄'
maxAge

The set method of this instance accepts an optional third argument where you can provide a maxAge option. maxAge is the number of milliseconds the value is valid in the cache.

Example:

import alfy from 'alfy';
import delay from 'delay';

alfy.cache.set('foo', 'bar', {maxAge: 5000});

alfy.cache.get('foo');
//=> 'bar'

// Wait 5 seconds
await delay(5000);

alfy.cache.get('foo');
//=> undefined

debug

Type: boolean

Whether the user currently has the workflow debugger open.

icon

Type: object
Keys: 'info' | 'warning' | 'error' | 'alert' | 'like' | 'delete'

Get various default system icons.

The most useful ones are included as keys. The rest you can get with icon.get(). Go to /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources in Finder to see them all.

Example:

import alfy from 'alfy';

console.log(alfy.icon.error);
//=> '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns'

console.log(alfy.icon.get('Clock'));
//=> '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/Clock.icns'

meta

Type: object

Example:

{
	name: 'Emoj',
	version: '0.2.5',
	uid: 'user.workflow.B0AC54EC-601C-479A-9428-01F9FD732959',
	bundleId: 'com.sindresorhus.emoj'
}

alfred

Type: object

Alfred metadata.

version

Example: '3.0.2'

Find out which version the user is currently running. This may be useful if your workflow depends on a particular Alfred version's features.

theme

Example: 'alfred.theme.yosemite'

Current theme used.

themeBackground

Example: 'rgba(255,255,255,0.98)'

If you're creating icons on the fly, this allows you to find out the color of the theme background.

themeSelectionBackground

Example: 'rgba(255,255,255,0.98)'

The color of the selected result.

themeSubtext

Example: 3

Find out what subtext mode the user has selected in the Appearance preferences.

Usability note: This is available so developers can tweak the result text based on the user's selected mode, but a workflow's result text should not be bloated unnecessarily based on this, as the main reason users generally hide the subtext is to make Alfred look cleaner.

data

Example: '/Users/sindresorhus/Library/Application Support/Alfred/Workflow Data/com.sindresorhus.npms'

Recommended location for non-volatile data. Just use alfy.data which uses this path.

cache

Example: '/Users/sindresorhus/Library/Caches/com.runningwithcrayons.Alfred/Workflow Data/com.sindresorhus.npms'

Recommended location for volatile data. Just use alfy.cache which uses this path.

preferences

Example: '/Users/sindresorhus/Dropbox/Alfred/Alfred.alfredpreferences'

This is the location of the Alfred.alfredpreferences. If a user has synced their settings, this will allow you to find out where their settings are regardless of sync state.

preferencesLocalHash

Example: 'adbd4f66bc3ae8493832af61a41ee609b20d8705'

Non-synced local preferences are stored within Alfred.alfredpreferences under …/preferences/local/${preferencesLocalHash}/.

Users

Alfred workflows using Alfy

Related

Maintainers

alfy's People

Contributors

abdul avatar adamkiss avatar adriantoine avatar adriantombu avatar afzalive avatar antonniklasson avatar briangonzalez avatar calpa avatar colinf avatar dameck avatar davidmondok avatar demartini avatar dev99problems avatar fregante avatar gil avatar importre avatar jeppestaerk avatar jhilker avatar jopemachine avatar nguyenvanduocit avatar nicklayb avatar radibit avatar rizowski avatar samverschueren avatar sindresorhus avatar sonicdoe avatar spinscale avatar tofrankie avatar vinkla avatar wolasss 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

alfy's Issues

slow to exit

While testing I've found that there's a noticable lag after the script outputs the final JSON. My solution was to add a .then(() => process.exit(0)) which speeded up results being outputted since Alfed seems to wait for the script to exit before returning anything.

non-bundlable with webpack

I've tried webpack, parcel, rollup and they all seem to fail at some stage.

I've gotten webpack to work by altering the way conf and cache-conf are given parentDir. By passing this explicitly in the constructor instead of relying on the module.parent object, webpack can resolve almost* all of the dependencies.

From conf/index.js:21

opts = Object.assign({
  projectName: pkgPath && JSON.parse(fs.readFileSync(pkgPath, 'utf8')).name
}, opts);

to

opts = Object.assign({
  projectName: opts.name
}, opts);

almost* refers to a dynamic require of electron in the got package. got/index.js:91

Add alfy-init script

Original comment: #24 (comment)

Basically it would just be a proxy to alfred-link. This would make the dev experience much better because it doesn't require installing alfred-link separately (assuming they know about alfred-link in the first place). Hopefully this encourages people to publish their workflows to npm instead of to Packal. Publishing to npm has much more benefits (which should be all described in my to-do blogpost :)).

Configuring workflows

Issuehunt badges

So, a couple of weeks back I thought we could make much more powerfull workflows if there was a way to configure them individually per user.

For instance, if someone creates a git workflow, the user installing that workflow should be able to put an API key somewhere. It's very dirty if he needs to change the workflow in that visual workflow editor and put the API key somewhere.

I have a couple of ideas for this, not sure if all that easy to do.

Configure script

We could allow a user to create a configure.js script which could use Inquirer.js or something to ask questions to the user. In the git workflow example for instance, either we create a new activation keyword, for instance configure-git which opens the terminal and executes configure.js. Or git configure for instance.

The name of that script could be set via package.json

{
  "name": "alfred-git",
  "alfy": {
    "configure": "configure.js"
  }
}

Configure questions

A second approach would be easier to add for the user. This would allow someone to define the questions in package.json and will store them in the config of the workflow. This way, in your workflow, you could just grab them from the config.

{
  "name": "alfred-git",
  "alfy": {
    "configure": {
      "api_key": {
        "type": "input",
        "message": "GitHub API key?"
      },
      "foo": {
        "type": "list",
        "message": "Some list here?",
        "choices": ["foo", "bar"]
      }
    }
  }
}

The first option offers the most flexibility in terms of the package you use to ask your questions (although 2 would offer more consistency).

The second one would definitely be easier for the developer, though less flexible. This would also mean every workflow is packaged with Inquirer.js, not sure how "heavy" it is. Although, it might be an idea to extract this in a separate package called alfy-configure and keep this the core. Something like alfy-test.


Note: This issue has a bounty, so it's expected that you are an experienced programmer and that you give it your best effort if you intend to tackle this. Don't forget, if applicable, to add tests, docs (double-check for typos), and update TypeScript definitions. And don't be sloppy. Review your own diff multiple times and try to find ways to improve and simplify your code. Instead of asking too many questions, present solutions. The point of an issue bounty is to reduce my workload, not give me more. Include a 🦄 in your PR description to indicate that you've read this. Thanks for helping out 🙌 - @sindresorhus


IssueHunt Summary

samverschueren samverschueren has been rewarded.

Backers (Total: $100.00)

Submitted pull Requests


Tips

[ERROR: input.scriptfilter] JSON error: JSON text did not start with array or object and option to allow fragments not set. in JSON:

Issuehunt badges

I'm getting the following error when just trying to log a string with alfy.log:

[ERROR: input.scriptfilter] JSON error: JSON text did not start with array or object and option to allow fragments not set. in JSON:

Project built with yo alfred and development linked with alfred-link

There is a $20.00 open bounty on this issue. Add more on Issuehunt.

Issue Updating Alfy Workflow

Yesterday we released a new version of the alfred-packagist. When we tried to update the package locally with npm i -g alfred-packagist we get the following error:

> [email protected] preuninstall /usr/local/lib/node_modules/alfred-packagist
> alfy-cleanup

{ Error: Command failed: alfred-unlink
module.js:529
    throw err;
    ^

Error: Cannot find module './'
    at Function.Module._resolveFilename (module.js:527:15)
    at Function.Module._load (module.js:476:23)
    at Module.require (module.js:568:17)
    at require (internal/module.js:11:18)
    at Object.<anonymous> (/usr/local/lib/node_modules/alfred-packagist/node_modules/.bin/alfred-unlink:3:20)
    at Module._compile (module.js:624:30)
    at Object.Module._extensions..js (module.js:635:10)
    at Module.load (module.js:545:32)
    at tryModuleLoad (module.js:508:12)
    at Function.Module._load (module.js:500:3)

    at Promise.all.then.arr (/usr/local/lib/node_modules/alfred-packagist/node_modules/execa/index.js:201:11)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
  code: 1,
  killed: false,
  stdout: '',
  stderr: 'module.js:529\n    throw err;\n    ^\n\nError: Cannot find module \'./\'\n    at Function.Module._resolveFilename (module.js:527:15)\n    at Function.Module._load (module.js:476:23)\n    at Module.require (module.js:568:17)\n    at require (internal/module.js:11:18)\n    at Object.<anonymous> (/usr/local/lib/node_modules/alfred-packagist/node_modules/.bin/alfred-unlink:3:20)\n    at Module._compile (module.js:624:30)\n    at Object.Module._extensions..js (module.js:635:10)\n    at Module.load (module.js:545:32)\n    at tryModuleLoad (module.js:508:12)\n    at Function.Module._load (module.js:500:3)\n',
  failed: true,
  signal: null,
  cmd: 'alfred-unlink',
  timedOut: false }
npm WARN lifecycle [email protected]~preuninstall: continuing anyway [email protected] preuninstall: `alfy-cleanup`
npm WARN lifecycle Exit status 1
npm ERR! path /usr/local/lib/node_modules/alfred-packagist/node_modules/.bin/rimraf
npm ERR! code EEXIST
npm ERR! Refusing to delete /usr/local/lib/node_modules/alfred-packagist/node_modules/.bin/rimraf: is outside /usr/local/lib/node_modules/alfred-packagist/node_modules/rimraf and not a link
npm ERR! File exists: /usr/local/lib/node_modules/alfred-packagist/node_modules/.bin/rimraf
npm ERR! Move it away, and try again.

We're using node v8.7.0 and npm v5.5.1 on macOS.

Is this something we have done wrong when we implemented Alfy into alfred-packagist? What is your thoughts on this issue?

npm can not install

npm install alfy -S -D
npm ERR! code E404
npm ERR! 404 Not Found: resolve-alfred-prefs@^1.0.0

[ERROR: input.scriptfilter] JSON error: JSON text did not start with array or object and option to allow fragments not set.

Alfred v3.5 [876]

Having this in my index.js,

"use strict";

const alfy = require("alfy");
const myConfig = require("./src/config.js");

alfy.output([
	{
		title: "Unicorn"
	},
	{
		title: "Rainbow"
	}
]);

I'm getting an error in Alfred debugger:

[ERROR: input.scriptfilter] JSON error: JSON text did not start with array or object and option to allow fragments not set. in JSON:
called
{
	"items": [
		{
			"title": "Unicorn"
		},
		{
			"title": "Rainbow"
		}
	]
}

If I require custom file const myConfig = require("./src/config.js");, error does show up.
If I require npm package (for example const notifier = require("node-notifier");), error does not show up.

Content of my config.js:

const alfy = require('alfy');

const config = alfy.config;

const getTimerObject = () => {
	return config.get('timerObject') || null;
};

const setTimerObject = newTimerObject => {
	config.set('timerObject', newTimerObject);
	return getTimerObject();
};

console.log('called');

module.exports = {
	setTimerObject,
	getTimerObject
};

Any way I can solve this?

require("alfy") takes ~120ms

I noticed that a very simple script of mine (just returning a query from a JSON file) was much slower than expected.
It turns out that just require('alfy') adds about 120ms to a script's execution time.

Since I only used alfy for outputs I replaced

const alfy = require('alfy');

with

const alfy = {
    output: (items) => {
        console.log(JSON.stringify({items}, null, '\t'));
    }
}

Now Alfred feels snappy again. :-)

Getting alfy-init: command not found

I was about to make an update to my Alfredinary, but now getting alfy-init: command not found. I have "postinstall": "alfy-init", in my package.json.

Using Node.js v14.8.0, Alfred 4.3.2 and these are my dependencies to the Alfred workflow

  "dependencies": {
    "alfy": "^0.10.0",
    "cloudinary": "^1.25.0",
    "dotenv": "^8.2.0"
  }

Error: Alfred preferences not found at location

Tried:
npm install --global alfred-npms

Got:

> [email protected] postinstall /usr/local/lib/node_modules/alfred-npms
> alfy-init

{ Error: Command failed: alfred-link
Error: Alfred preferences not found at location /Users/velocityzen/Library/Preferences/com.runningwithcrayons.Alfred-Preferences-3.plist
    at pathExists.then.exists (/usr/local/lib/node_modules/alfred-npms/node_modules/resolve-alfred-prefs/index.js:15:10)
    at <anonymous>

    at Promise.all.then.arr (/usr/local/lib/node_modules/alfred-npms/node_modules/execa/index.js:201:11)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:160:7)
  code: 1,
  killed: false,
  stdout: '',
  stderr: 'Error: Alfred preferences not found at location /Users/velocityzen/Library/Preferences/com.runningwithcrayons.Alfred-Preferences-3.plist\n    at pathExists.then.exists (/usr/local/lib/node_modules/alfred-npms/node_modules/resolve-alfred-prefs/index.js:15:10)\n    at <anonymous>\n',
  failed: true,
  signal: null,
  cmd: 'alfred-link',
  timedOut: false }
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] postinstall: `alfy-init`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] postinstall script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/velocityzen/.npm/_logs/2018-03-01T04_44_12_239Z-debug.log

My preferences file is in dropbox for synchronization.

ERR_INPUT_TYPE_NOT_ALLOWED with example script

Using:
Alfred version 4.5.1 with Powerpack
Node version 14.17.5

After following all steps as described under Usage, when running the example workflow I get the following error in Alfred debugger:

Alfy[Script Filter] Queuing argument 'h'
Alfy[Script Filter] Script with argv 'h' finished
ERROR: Alfy[Script Filter] Code 1: internal/process/esm_loader.js:74
    internalBinding('errors').triggerUncaughtException(
                              ^

Error [ERR_INPUT_TYPE_NOT_ALLOWED]: --input-type can only be used with string input via --eval, --print, or STDIN
    at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:813:13)
    at Loader.resolve (internal/modules/esm/loader.js:89:40)
    at Loader.getModuleJob (internal/modules/esm/loader.js:242:28)
    at Loader.import (internal/modules/esm/loader.js:177:28)
    at internal/modules/run_main.js:50:28
    at Object.loadESM (internal/process/esm_loader.js:68:11) {
  code: 'ERR_INPUT_TYPE_NOT_ALLOWED'
}

When I navigate to the workflow folder in terminal and run the javascript file with node index.js {query} I do get the expected result (JSON is returned).

Install it globally

Hi there,

I'm quite new to node.js / npm so please excuse my rookie question.

Are there any drawbacks to install it with --global?

How can i output a function?

Hello! How can i run a function (for example execa) when user select an item from list?

That's my code:

alfy.output([
	{
		title: 'Random',
		body: 'Get random photo'
	}, {
               title: 'ID',
               body: 'Pick photo by id'
        }
])  

Maybe not quite understand how it works

Add possibility to fetch from network with caching

We already discussed something like this here #4 (comment).

Here's a small wrap-up. We want to add got to alfy by default in order to make network requests. But not only that, it should be possible to cache the network requests. If later on, the same network request is performed and the cache is still valid, the cache will be returned. If the cache is not valid, a new network request is performed and the cache is updated.

Bonus feature: If the cache is not valid BUT the network request fails (user might be working offline), the cached data is still returned.

alfy.fetch('https://myapi.com/foo/bar.json', {maxAge: 5000}).then(res => {
    console.log(res.body);
    // {foo: 'bar'}
});

Some extra notes: fetch is just calling got. So you can just pass the method property with value POST if a post request has to be performed.

@sindresorhus Did I cover everything?

Alfy eats errors from Run Script action

Issuehunt badges

Alfred's workflow debugger only shows output from stderr. These lines redirect all output from stderr to alfy.error(), which then uses console.log() to print it to stdout:

loudRejection(alfy.error);
process.on('uncaughtException', alfy.error);
hookStd.stderr(alfy.error);

This means that any errors that occur inside a Run Script action are never displayed in the workflow debugger.


IssueHunt Summary

Backers (Total: $20.00)

Submitted pull Requests


Become a backer now!

Or submit a pull request to get the deposits!

Tips


IssueHunt has been backed by the following sponsors. Become a sponsor

Prevent cache failures on workflow update

I was implementing the transform function of the alfy.fetch method in alfred-ng2 and noticed a subtle area that could cause your workflow from failing a certain amount of time due to the cache being invalid.

Let me give you an example first to make it easier to understand. Let's assume I was calling an XML API that I transformed to JSON. At first (pre-transform), it would look like this.

alfy.fetch(`https://my-api.com/resources.xml`, {maxAge: ONE_WEEK})
    .then(result => xml2js(result))
    .then(jsonResult => {
         // transform the jsonResult to alfred items
    });

The XML data is cached for one day. Now let's assume I upgrade to the transform function. This could be beneficiel because it only has to run the xml2js call once before caching.

alfy.fetch(`https://my-api.com/resources.xml`, {
    maxAge: ONE_WEEK,
    transform: result => xml2js(result)
})
.then(jsonResult => {
    // transform the jsonResult to alfred items
});

An important thing to notice here is that in the second example, not the XML result is cached, but the result of the transform function and thus the JSON result is being cached. Pretty straightforward.

BUT, without knowing it, we could break everyones workflow now for one day. The thing is that if I had used this workflow quite recently, the XML data would still be cached and the fetch promise will not return JSON but XML. Boom, workflow explodes! Adding a function to (transform in this case), doesn't change anything to the cache key.

How did I solve this? By adding a version property to the options object.

alfy.fetch(`https://my-api.com/resources.xml`, {
    maxAge: ONE_WEEK,
    version: pkg.version,
    transform: result => xml2js(result)
})
.then(jsonResult => {
    // transform the jsonResult to alfred items
});

This will make sure that my cache key is different on every release and will invalidate the old cache.

Is this something we could add in alfy? Could we detect the callers package.json version number? In my case alfred-ng2 so that the developer doesn't have to worry about this issue? I'm quite sure that this issue will be overlooked by almost everyone if it was not tested properly.

A downside of this is that the cache could grow quite rapidly depending on the release cycles of the workflow.

Support Alfred 4

npm install --global alfred-lock

Errors:

> [email protected] postinstall /Users/vivaxy/.npm-packages/lib/node_modules/alfred-lock
> alfy-init

{ Error: Command failed: alfred-link
Error: Alfred preferences not found at location /Users/vivaxy/Library/Preferences/com.runningwithcrayons.Alfred-Preferences-3.plist
    at pathExists.then.exists (/Users/vivaxy/.npm-packages/lib/node_modules/alfred-lock/node_modules/resolve-alfred-prefs/index.js:15:10)


    at makeError (/Users/vivaxy/.npm-packages/lib/node_modules/alfred-lock/node_modules/execa/index.js:174:9)
    at Promise.all.then.arr (/Users/vivaxy/.npm-packages/lib/node_modules/alfred-lock/node_modules/execa/index.js:278:16)
    at processTicksAndRejections (internal/process/next_tick.js:81:5)
  code: 1,
  stdout: '',
  stderr:
   'Error: Alfred preferences not found at location /Users/vivaxy/Library/Preferences/com.runningwithcrayons.Alfred-Preferences-3.plist\n    at pathExists.then.exists (/Users/vivaxy/.npm-packages/lib/node_modules/alfred-lock/node_modules/resolve-alfred-prefs/index.js:15:10)\n',
  failed: true,
  signal: null,
  cmd: 'alfred-link',
  timedOut: false,
  killed: false }
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] postinstall: `alfy-init`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] postinstall script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/vivaxy/.npm/_logs/2019-05-31T14_25_10_280Z-debug.log

add `transform` function to `fetch` method

alfy.fetch allows someone to cache network requests for some time. An example is my alfred-ng2 workflow.

The json returned from the endpoint looks like this

{
  "@angular/common": [
    {
      "title": "APP_BASE_HREF",
      "path": "common/index/APP_BASE_HREF-let.html",
      "docType": "let",
      "stability": "stable",
      "secure": "false",
      "howToUse": "",
      "whatItDoes": "Not Done",
      "barrel" : "@angular/common"
    }
  ],
  "@angular/core": [
    {
      "title": "APPLICATION_COMMON_PROVIDERS",
      "path": "core/index/APPLICATION_COMMON_PROVIDERS-let.html",
      "docType": "let",
      "stability": "stable",
      "secure": "false",
      "howToUse": "",
      "whatItDoes": "Not Done",
      "barrel" : "@angular/core"
    }
  ]
}

I transform that data into one single list of items. Instead of doing the transformation every time a user searches through the packages, it could be done once before caching the data and directly work with the cached data instead. This could be very useful for large datasets or even for APIs returning xml and transform them to json before caching.

My suggestion is to add a transform option to the alfy.fetch options object.

alfy.fetch('https://my-xml-api.com/some-feed.xml', {
    maxAge: 86400000,    // 24 hours
    transform: result => xml2js.parseString(result)
}).then(jsonResult => {
     console.log(jsonResult);
     //=> some best-effort xml-2-json converted json
});

Feel free to provide (useful) feedback of any kind.

Logging prevents results from appearing

If I use alfy.log in my script, I can't get any valid results in the workflow. I get an error like this when trying to log some results during development:

[2017-12-07 15:27:10][ERROR: input.scriptfilter] JSON error: No string key for value in object around character 4. in JSON:

I know that alfy.log just goes to STDOUT (though console.error); is the issue that Alfred is reading that and trying to parse it for results? Or something in the way that my workflow is configured?

The search will be more quik without wifi

here is the code , the url is a visual host
I wonder why , when I shut down the wifi , the alfred will return the result more quickly?
If turn on the wifi,will be more slow, I have logged the url in alfy.fetch function,which shows
they both use the cached data .

alfy.fetch('http://linuxcmd.com/data.json',
           {maxAge: 86400000}).then(result => {
    var commands = [];
    var e = 0;
    for(var a in result){
      ++e;
      result[a]['id'] = e;
      commands.push(result[a]);
  }
  var i=0,
  page_size = commands.length,
  arrResult = [],
  query=alfy.input;
  if(commands&&commands.length&&toString.call(commands).indexOf('Array')>-1){
    var count = 0
    for (; i < page_size; i++) {
      if(isSreachIndexOF(commands[i].n,query)
       || isSreachIndexOF(commands[i].d,query) 
      ){
        if(count < page_size){
          arrResult.push(commands[i]);
          ++count;
        }
      }
    }
  }
  var items = [];
  for(var i = 0;i< arrResult.length;i++){
    items.push({
      title: arrResult[i].n,
      subtitle: arrResult[i].d,
      arg: arrResult[i].n
    })
  }

  if(items.length < 1){
    items.push({
      title: "没有搜素到内容",
      subtitle: "请尝试其它关键字",
    })
  }
  alfy.output(items);
});

Error: workflows directory does not exist

When installing a workflow for the first time with Alfy (when no other workflows have been installed yet), I get an error saying that the workflows directory in Alfred.alfredpreferences doesn't exist yet.

Error: Command failed: alfred-link
Error: Workflow directory `/Users/adriaan/stack/share/backups/Alfred 3/Alfred.alfredpreferences/workflows` does not exist
    at pathExists.then.exists (/Users/adriaan/.npm-packages/lib/node_modules/alfred-google-translate/node_modules/alfred-link/index.js:19:10)
    at <anonymous>

    at Promise.all.then.arr (/Users/adriaan/.npm-packages/lib/node_modules/alfred-google-translate/node_modules/execa/index.js:201:11)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:160:7)
  code: 1,
  killed: false,
  stdout: '',
  stderr: 'Error: Workflow directory `/Users/adriaan/stack/share/backups/Alfred 3/Alfred.alfredpreferences/workflows` does not exist\n    at pathExists.then.exists (/Users/adriaan/.npm-packages/lib/node_modules/alfred-google-translate/node_modules/alfred-link/index.js:19:10)\n    at <anonymous>\n',
  failed: true,
  signal: null,
  cmd: 'alfred-link',
  timedOut: false

The solution would be simple: just create the workflows directory when it does not exist yet.

Embed alfred-notifier

It would totally make sense to embed alfred-notifier. It's easy to miss the possibility of notifying your users when you have a new version, no one reads the docs (entirely). Notifying your users of a new version is best practice after all and I always want to know if a new version is available.

However, there should always be a way to turn this feature off. The question is, how? Some ways I can think off.

  1. alfy.disableUpdateNotification()
  2. alfy.configure({updateNotification: false});

Allow top-level await

Issuehunt badges

Would be a nice convenience as a lot of Alfy workflows use promises.

We could use esm for this.


IssueHunt Summary

[
<
i
m
g

s
r
c

'
h
t
t
p
s
:
/
/
a
v
a
t
a
r
s
3
.
g
i
t
h
u
b
u
s
e
r
c
o
n
t
e
n
t
.
c
o
m
/
u
/
1
4
0
2
2
4
1
?
v

4
'

a
l
t

'
b
f
r
e
d

i
t
'

w
i
d
t
h

2
4

h
e
i
g
h
t

2
4

b
f
r
e
d

i
t
]
(
h
t
t
p
s
:
/
/
i
s
s
u
e
h
u
n
t
.
i
o
/
u
/
b
f
r
e
d

i
t
)

h
a
s

b
e
e
n

r
e
w
a
r
d
e
d
.

Backers (Total: $40.00)

Submitted pull Requests


Tips


IssueHunt has been backed by the following sponsors. Become a sponsor

Built version caching into CacheConf

Currently, alfy.fetch will invalidate the cache if you update your workflow which is pretty nice. But maybe we should built this into cache-conf. The reason I'm thinking about this is because I'm creating a new workflow that does not fetch the data from a server, but from different files in a dependency. Reading all those files and transforming the data takes some time and I want to cache it. But if I want to do it correctly, I will have to write the versioned caching myself. So maybe we should add an optional version property to cache-conf which will invalidate older caches and just overwrite them.

Another suggestion of fixing this is if we would allow to read local files with alfy.fetch as well.

alfy.fetch('my-file.json', {
    maxAge: ONE_DAY,
    transform: data => {
       return {data};
    }
});

This way you will have the same benefits as URLs but for local files, as well as the transform function. Maybe this suggestion is a separate enhancement from the initial idea. But either one of them would solve my use case.

Plist Error

I've made a copy of the alfred-npms package and I'm trying to use the same workflow for another API. Though, the error below is thrown.

TypeError: Cannot read property '1' of null
    return /<key>version<\/key>[\s\S]*<string>([\d.]+)<\/string>/.exec(fs.readFileSync(infoPlist, 'utf8'))[1];
                                                                                                          ^

If I take the line below and remove the [1] in the end…

return /<key>version<\/key>[\s\S]*<string>([\d.]+)<\/string>/.exec(fs.readFileSync(infoPlist, 'utf8'))[1];

…it works. Then my package is executed as expected. What am I doing wrong? Any thoughts?

Support setting Alfred variables with alfy.output

Issuehunt badges

You can set variables in Alfred by writing to stdout using a special JSON format. It would be handy if alfy.output supported this, perhaps in a special variables key:

alfy.output([
  { title: 'Google', arg: 'https://www.google.com', variables: { browser: 'Chrome' },
  { title: 'Mozilla', arg: 'https://www.mozilla.org', variables:  { browser: 'Firefox' } }
])

Or as a second argument:

// backwards compatible, but less elegant
alfy.output([
  { title: 'Google', arg: 'https://www.google.com' },
  { title: 'Mozilla', arg: 'https://www.mozilla.org' }
], [
  { browser: 'Chrome' },
  { browser: 'Firefox' }
])

I'm happy to write a pull request if this seems worthwhile.

JSON Format

The format for emitting variables isn't complicated, but life would be easier if Alfy handled it.

Emit a string and set variables

// The root `alfredworkflow` key is required.
// Without it, Alfred will pass the object to the next node in the workflow like normal.
{
  "alfredworkflow": {
    "arg": "https://www.google.com",
    "variables": {"browser": "Chrome"}
  }
}

Emit multiple items and set variables

// `arg` is a stringified `alfredworkflow` object
{
  "items": [{
    "title": "Google",
    "arg": "{\"alfredworkflow\": {\"arg\": \"https://www.google.com\", \"variables\": {\"browser\": \"Chrome\"}}}"
  }]
}

Note, you can access Alfred variables through the environment:

const browser = process.env.browser

IssueHunt Summary

Backers (Total: $60.00)

Submitted pull Requests


Become a backer now!

Or submit a pull request to get the deposits!

Tips

Running basic script takes 5s and throws error

Hey,
I've created a new workflow with yeoman generator:

'use strict';
const alfy = require('alfy');

alfy.output([
	{
		title: 'Unicorn',
		subtitle: alfy.input
	}
]);

output

[00:16:30.984] alfy-pkc[Script Filter] Script with argv 'test' finished
[00:16:30.990] STDERR: alfy-pkc[Script Filter] /Users/pk/Library/Caches/com.runningwithcrayons.Alfred/Workflow Data/com.google.alfy-pkc/node_path: line 1: unexpected EOF while looking for matching `"'
/Users/pk/Library/Caches/com.runningwithcrayons.Alfred/Workflow Data/com.google.alfy-pkc/node_path: line 2: syntax error: unexpected end of file
[00:16:30.991] alfy-pkc[Script Filter] {
	"items": [
		{
			"title": "Unicorn",
			"subtitle": "test"
		}
	]

I'm using node 14 from nvm. Symlinked it to /usr/local/bin. Content of 'node_path' file:

PATH="/usr/local/opt/coreutils/libexec/gnubin

(missing " is not a typo)

GraphQL Support?

As more and more sites and services are using graphql it would be great if alfy would support graphql queries & mutations.

// edit: saw that alfy used got under the hood. So you can make POST requests to the graphq api 🙈

"Couldn't find binary node" error in two packages using alfy

Hey @sindresorhus,

On a 1h fresh Mac OS setup, I cannot use any alfred workflow relying on alfy.

Here's my setup:

  • Iterm with zsh and oh-my-zshrc
  • yarn installed through brew install yarn --without-node
  • nvm with version 9 and 10 installed.

I've tried command -v node from zsh, bash and sh and they all return the path to the nvm installed binary.

I think installing yarn without node, and using nvm as the only way to install node breaks something with how alfy calls the scripts.

How to copy output to clipboard?

This repo hasn't gotten a commit in a month so I'm assuming it won't get a response, but I might as well try.

alfy.output(
	[{
		title: `👏🏻 ${alfy.input.replace(/ /g, ' 👏🏻 ')} 👏🏻 `,
	}]);

This is my code. How do I copy the title to the clipboard?

screen shot 2017-11-22 at 8 38 53 pm

That's my workflow. What am I doing wrong?

Using `npm-registry-client` with alfy

I don't think this is an alfy issue but since this was the only way to reach you, I thought I'd post my question here.

To clarify, npm registry client is built to work with the browser only and I can't use their request methods?

Easiest to just highlight the work -- I've marked out most of it but this method is trying to fetch npm starred repos: https://github.com/artivilla/alfred-starred/blob/master/index.js#L28

Using postman, I get this response:
{"rows":[{"value":"basscss"},{"value":"bl"},{"value":"bluebird"},{"value":"fetch"},{"value":"jest-serializer-enzyme"},{"value":"json-web-token"},{"value":"kue"},{"value":"longjohn"},{"value":"mongoose-softdelete"},{"value":"nconf"},{"value":"nib"},{"value":"nodemon"},{"value":"v8-profiler"},{"value":"vinyl-source-stream"}]}

Access environment variables

#36 will likely resolve my issue so I can gather the users token to use to make API calls but for the short term is there a way to I can access the users system env variables?

Cannot read property 'alfy' of undefined

the index.js is

const alfy = require('alfy');
alfy.output([{
title: 'Unicorn'
}, {
title: 'Rainbow'
}]);

and the console outputs the error:

[ERROR: input.scriptfilter] Code 1: {
  "items": [
    {
      "title": "Unicorn"
    },
    {
      "title": "Rainbow"
    }
  ]
}
{
  "items": [
    {
      "title": "TypeError: Cannot read property 'alfy' of undefined\n    at readPkgUp.then.result (/Users/pro/Library/Application Support/Alfred 3/Alfred.alfredpreferences/workflows/user.workflow.0408C61C-45B8-474D-B77D-B3C6691E4024/node_modules/alfy/lib/update-notification.js:7:26)",
      "subtitle": "Press ⌘L to see the full error and ⌘C to copy it.",
      "valid": false,
      "text": {
        "copy": "```\nTypeError: Cannot read property 'alfy' of undefined\n    at readPkgUp.then.result (/Users/pro/Library/Application Support/Alfred 3/Alfred.alfredpreferences/workflows/user.workflow.0408C61C-45B8-474D-B77D-B3C6691E4024/node_modules/alfy/lib/update-notification.js:7:26)\n```\n\n-\nregurl undefined\nAlfred 3.3.2\ndarwin x64 15.6.0",
        "largetype": "TypeError: Cannot read property 'alfy' of undefined\n    at readPkgUp.then.result (/Users/pro/Library/Application Support/Alfred 3/Alfred.alfredpreferences/workflows/user.workflow.0408C61C-45B8-474D-B77D-B3C6691E4024/node_modules/alfy/lib/update-notification.js:7:26)"
      },
      "icon": {
        "path": "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns"
      }
    }
  ]
}
[2017-06-03 16:11:59][ERROR: input.scriptfilter] JSON error: Garbage at end. in JSON:
{
  "items": [
    {
      "title": "Unicorn"
    },
    {
      "title": "Rainbow"
    }
  ]
}
{
  "items": [
    {
      "title": "TypeError: Cannot read property 'alfy' of undefined\n    at readPkgUp.then.result (/Users/pro/Library/Application Support/Alfred 3/Alfred.alfredpreferences/workflows/user.workflow.0408C61C-45B8-474D-B77D-B3C6691E4024/node_modules/alfy/lib/update-notification.js:7:26)",
      "subtitle": "Press ⌘L to see the full error and ⌘C to copy it.",
      "valid": false,
      "text": {
        "copy": "```\nTypeError: Cannot read property 'alfy' of undefined\n    at readPkgUp.then.result (/Users/pro/Library/Application Support/Alfred 3/Alfred.alfredpreferences/workflows/user.workflow.0408C61C-45B8-474D-B77D-B3C6691E4024/node_modules/alfy/lib/update-notification.js:7:26)\n```\n\n-\nregurl undefined\nAlfred 3.3.2\ndarwin x64 15.6.0",
        "largetype": "TypeError: Cannot read property 'alfy' of undefined\n    at readPkgUp.then.result (/Users/pro/Library/Application Support/Alfred 3/Alfred.alfredpreferences/workflows/user.workflow.0408C61C-45B8-474D-B77D-B3C6691E4024/node_modules/alfy/lib/update-notification.js:7:26)"
      },
      "icon": {
        "path": "/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/AlertStopIcon.icns"
      }
    }
  ]
}

Extract and open source `run-node.sh`?

This would help with an issue we've had for a long time on ghooks.

I'd be happy to do it. I notice there's some specific Alfred stuff in there. Do you have any tips on how to make it in a way that works cross-platform and agnostic of Alfred?

Remove broken links in Users section

There are 5 broken links in Users section.

I think it is good to remove these lines in Users section if possible to avoid misclicks.

> blc https://github.com/sindresorhus/alfy | grep HTTP_404
├─BROKEN─ https://github.com/vinkla/alfred-kaomoji (HTTP_404)
├─BROKEN─ https://github.com/jacc/alfred-clap (HTTP_404)
├─BROKEN─ https://github.com/vinkla/alfred-reference (HTTP_404)
├─BROKEN─ https://github.com/vinkla/alfred-homebrew (HTTP_404)
├─BROKEN─ https://github.com/adriantombu/alfred-travis-ci (HTTP_404)

FYI: I used blc to check broken links

ERROR: alfred.workflow.input.scriptfilter

[ERROR: alfred.workflow.input.scriptfilter] XML Parse Error 'The operation couldn’t be completed. (NSXMLParserErrorDomain error 4.)'. Row (null), Col (null): 'Document is empty' in XML:

index.js code:

const alfy = require('alfy');
alfy.output([{
        title: 'Unicorn'
}, {
        title: 'Rainbow'
}]);

run failed if index.js is esm module

error

[ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/jiangwei/.nvm/versions/node/v14.17.3/lib/node_modules/@ifu/emoji/index.js
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1102:13) {
code: 'ERR_REQUIRE_ESM'
}

node: v14.17.3
alfy: 0.11.1

i try to remove --require esm in run-node.sh, will not throw this error anymore. so what --require esm doing

Flaky tests

The tests are flaky because of these two lines

They are both working on the same instance of CacheConf. Is there anyway we can work around this problem without merging everything in one test file or run the test serially?

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.