alfy 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -36,6 +36,8 @@ export type CacheConfSetOptions = {
36
36
  readonly maxAge?: number;
37
37
  };
38
38
 
39
+ // TODO: Rename this in the next major version.
40
+
39
41
  export type CacheConf<T extends Record<string, any> = Record<string, unknown>> = {
40
42
  isExpired: (key: keyof T) => boolean;
41
43
 
@@ -363,9 +365,9 @@ export type Alfy = {
363
365
  /**
364
366
  Log value to the Alfred workflow debugger.
365
367
 
366
- @param text
368
+ @param value
367
369
  */
368
- log: (text: string) => void;
370
+ log: (value: unknown) => void;
369
371
 
370
372
  /**
371
373
  Display an error or error message in Alfred.
package/index.js CHANGED
@@ -3,18 +3,20 @@ import process from 'node:process';
3
3
  import {createRequire} from 'node:module';
4
4
  import Conf from 'conf';
5
5
  import got from 'got';
6
- import {hookStderr} from 'hook-std';
7
6
  import loudRejection from 'loud-rejection';
8
7
  import cleanStack from 'clean-stack';
9
8
  import {getProperty} from 'dot-prop';
10
9
  import AlfredConfig from 'alfred-config';
11
- import updateNotification from './lib/update-notification.js'; // eslint-disable-line import/order
10
+ import updateNotification from './lib/update-notification.js'; // eslint-disable-line import-x/order
12
11
 
13
12
  const require = createRequire(import.meta.url);
14
13
  const CacheConf = require('cache-conf');
15
14
 
16
15
  const alfy = {};
17
16
 
17
+ // Track if output has been generated to avoid empty JSON
18
+ let hasOutput = false;
19
+
18
20
  updateNotification();
19
21
 
20
22
  const getIcon = name => `/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/${name}.icns`;
@@ -42,6 +44,7 @@ alfy.alfred = {
42
44
  alfy.input = process.argv[2];
43
45
 
44
46
  alfy.output = (items, {rerunInterval} = {}) => {
47
+ hasOutput = true;
45
48
  console.log(JSON.stringify({items, rerun: rerunInterval}, null, '\t'));
46
49
  };
47
50
 
@@ -73,6 +76,7 @@ alfy.log = text => {
73
76
 
74
77
  alfy.error = error => {
75
78
  const stack = cleanStack(error.stack || error);
79
+ const title = error.stack ? `${error.name}: ${error.message}` : String(error);
76
80
 
77
81
  const copy = `
78
82
  \`\`\`
@@ -86,7 +90,7 @@ ${process.platform} ${os.release()}
86
90
  `.trim();
87
91
 
88
92
  alfy.output([{
89
- title: error.stack ? `${error.name}: ${error.message}` : error,
93
+ title,
90
94
  subtitle: 'Press ⌘L to see the full error and ⌘C to copy it.',
91
95
  valid: false,
92
96
  text: {
@@ -97,6 +101,9 @@ ${process.platform} ${os.release()}
97
101
  path: alfy.icon.error,
98
102
  },
99
103
  }]);
104
+
105
+ // Also output to stderr for the debugger (as requested in issue #86)
106
+ console.error(stack);
100
107
  };
101
108
 
102
109
  alfy.config = new Conf({
@@ -118,11 +125,6 @@ alfy.fetch = async (url, options) => {
118
125
  ...options,
119
126
  };
120
127
 
121
- // TODO: Remove this in 2024.
122
- if (options.query) {
123
- throw new Error('The `query` option was renamed to `searchParams`.');
124
- }
125
-
126
128
  if (typeof url !== 'string') {
127
129
  throw new TypeError(`Expected \`url\` to be a \`string\`, got \`${typeof url}\``);
128
130
  }
@@ -138,7 +140,7 @@ alfy.fetch = async (url, options) => {
138
140
  delete options.transform;
139
141
  delete options.maxAge;
140
142
 
141
- const key = rawKey.replaceAll('.', '\\.');
143
+ const key = rawKey.replaceAll('.', String.raw`\.`);
142
144
  const cachedResponse = alfy.cache.get(key, {ignoreMaxAge: true});
143
145
 
144
146
  if (cachedResponse && !alfy.cache.isExpired(key)) {
@@ -186,6 +188,12 @@ alfy.icon = {
186
188
 
187
189
  loudRejection(alfy.error);
188
190
  process.on('uncaughtException', alfy.error);
189
- hookStderr(alfy.error);
191
+
192
+ // Ensure valid JSON output even if user forgets to call alfy.output() (fixes #82)
193
+ process.on('beforeExit', () => {
194
+ if (!hasOutput) {
195
+ alfy.output([]);
196
+ }
197
+ });
190
198
 
191
199
  export default alfy;
@@ -2,8 +2,8 @@ import {readPackageUp} from 'read-package-up';
2
2
  import alfredNotifier from 'alfred-notifier';
3
3
 
4
4
  export default async function updateNotification() {
5
- const {package: pkg} = await readPackageUp();
6
- const alfy = (pkg || {}).alfy || {};
5
+ const {package: package_} = (await readPackageUp()) ?? {};
6
+ const alfy = package_?.alfy ?? {};
7
7
 
8
8
  if (alfy.updateNotification !== false) {
9
9
  alfredNotifier();
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "alfy",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Create Alfred workflows with ease",
5
5
  "license": "MIT",
6
6
  "repository": "sindresorhus/alfy",
7
+ "funding": "https://github.com/sponsors/sindresorhus",
7
8
  "type": "module",
8
9
  "bin": {
9
10
  "run-node": "./run-node.sh",
@@ -14,8 +15,9 @@
14
15
  "types": "./index.d.ts",
15
16
  "default": "./index.js"
16
17
  },
18
+ "sideEffects": false,
17
19
  "engines": {
18
- "node": ">=18"
20
+ "node": ">=20"
19
21
  },
20
22
  "scripts": {
21
23
  "test": "xo && ava && tsd"
@@ -45,21 +47,21 @@
45
47
  "alfred-notifier": "^0.2.3",
46
48
  "cache-conf": "^0.6.0",
47
49
  "clean-stack": "^5.2.0",
48
- "conf": "^12.0.0",
49
- "dot-prop": "^8.0.2",
50
- "execa": "^8.0.1",
51
- "got": "^13.0.0",
52
- "hook-std": "^3.0.0",
50
+ "conf": "^14.0.0",
51
+ "dot-prop": "^10.0.0",
52
+ "execa": "^9.6.0",
53
+ "got": "^14.4.8",
54
+ "hook-std": "^4.0.0",
53
55
  "loud-rejection": "^2.2.0",
54
56
  "read-package-up": "^11.0.0"
55
57
  },
56
58
  "devDependencies": {
57
- "ava": "^5.3.1",
59
+ "ava": "^6.4.1",
58
60
  "delay": "^6.0.0",
59
- "nock": "^13.3.8",
61
+ "nock": "^14.0.10",
60
62
  "tempfile": "^5.0.0",
61
- "tsd": "^0.29.0",
62
- "xo": "^0.56.0"
63
+ "tsd": "^0.33.0",
64
+ "xo": "^1.2.2"
63
65
  },
64
66
  "tsd": {
65
67
  "compilerOptions": {
package/readme.md CHANGED
@@ -103,7 +103,7 @@ By adding `alfy-init` as `postinstall` and `alfy-cleanup` as `preuninstall` scri
103
103
  "author": {
104
104
  "name": "Sindre Sorhus",
105
105
  "email": "sindresorhus@gmail.com",
106
- "url": "sindresorhus.com"
106
+ "url": "https://sindresorhus.com"
107
107
  },
108
108
  "scripts": {
109
109
  "postinstall": "alfy-init",
@@ -151,13 +151,16 @@ test('main', async t => {
151
151
 
152
152
  ## Debugging
153
153
 
154
- 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](https://www.alfredapp.com/help/workflows/advanced/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()`:
154
+ 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](https://www.alfredapp.com/help/workflows/advanced/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()` or `console.error()`:
155
155
 
156
156
  ```js
157
157
  import alfy from 'alfy';
158
158
 
159
159
  const unicorn = getUnicorn();
160
160
  alfy.log(unicorn);
161
+
162
+ // You can also use console.error() for debugging
163
+ console.error('Debug info:', {data: someData});
161
164
  ```
162
165
 
163
166
  ## Environment variables
@@ -237,7 +240,7 @@ Log `value` to the [Alfred workflow debugger](https://www.alfredapp.com/help/wor
237
240
 
238
241
  #### matches(input, list, item?)
239
242
 
240
- Returns an `string[]` of items in `list` that case-insensitively contains `input`.
243
+ Returns a `string[]` of items in `list` that case-insensitively contains `input`.
241
244
 
242
245
  ```js
243
246
  import alfy from 'alfy';
@@ -323,6 +326,29 @@ alfy.matches('Foo', list, (item, input) => item === input);
323
326
 
324
327
  Same as `matches()`, but with `alfy.input` as `input`.
325
328
 
329
+ If you want to match against multiple items, you must define your own matching function ([as shown here](#item)). Let’s extend the [example from the beginning](#example) to search for a keyword that appears either within the `title` or `body` property or both.
330
+
331
+ ```js
332
+ import alfy from 'alfy';
333
+
334
+ const data = await alfy.fetch('https://jsonplaceholder.typicode.com/posts');
335
+
336
+ const items = alfy
337
+ .inputMatches(
338
+ data,
339
+ (item, input) =>
340
+ item.title?.toLowerCase().includes(input) ||
341
+ item.body?.toLowerCase().includes(input)
342
+ )
343
+ .map((element) => ({
344
+ title: element.title,
345
+ subtitle: element.body,
346
+ arg: element.id,
347
+ }));
348
+
349
+ alfy.output(items);
350
+ ```
351
+
326
352
  #### error(error)
327
353
 
328
354
  Display an error or error message in Alfred.