backend-manager 3.2.134 → 3.2.136

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "3.2.134",
3
+ "version": "3.2.136",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -22,7 +22,7 @@ Module.prototype.main = function () {
22
22
  if (!productId) {
23
23
  return reject(assistant.errorify(`No productId`, {code: 400}));
24
24
  }
25
- const processorPath = `${process.cwd()}/payment-processors/${productId}.js`
25
+ const processorPath = `${Manager.cwd}/payment-processors/${productId}.js`
26
26
  let processor;
27
27
  // console.log('---processorPath', processorPath);
28
28
 
@@ -1,3 +1,6 @@
1
+ // Load libraries
2
+ // const path = require('path');
3
+
1
4
  function Module() {
2
5
 
3
6
  }
@@ -10,9 +13,6 @@ Module.prototype.main = function () {
10
13
  const payload = self.payload;
11
14
 
12
15
  return new Promise(async function(resolve, reject) {
13
- // Log
14
- assistant.log('Manager.cwd', Manager.cwd)
15
-
16
16
  // If not dev, quit
17
17
  if (!assistant.isDevelopment()) {
18
18
  return reject(assistant.errorify(`This command is only available in development mode`, {code: 401}));
@@ -24,12 +24,7 @@ Module.prototype.main = function () {
24
24
  }
25
25
 
26
26
  // Load the hook
27
- let hook;
28
- try {
29
- hook = (new (require(pathify(`${Manager.cwd}/${payload.data.payload.path}.js`)))());
30
- } catch (e) {
31
- hook = (new (require(pathify(`${Manager.cwd}/hooks/${payload.data.payload.path}.js`)))());
32
- }
27
+ const hook = loadHook(assistant, payload);
33
28
 
34
29
  // Run the hook
35
30
  try {
@@ -39,6 +34,13 @@ Module.prototype.main = function () {
39
34
  hook.context = null;
40
35
  hook.libraries = Manager.libraries;
41
36
 
37
+ // Get hook name
38
+ const hookName = payload.data.payload.path.split('/').pop();
39
+
40
+ // Set log prefix
41
+ assistant.setLogPrefix(`cron/daily/${hookName}()`);
42
+
43
+ // Run the hook
42
44
  const result = await hook.main(assistant);
43
45
 
44
46
  return resolve({data: result, status: 200});
@@ -48,9 +50,33 @@ Module.prototype.main = function () {
48
50
  });
49
51
  };
50
52
 
53
+ function loadHook(assistant, payload) {
54
+ const Manager = assistant.Manager;
55
+ const paths = [
56
+ `${Manager.rootDirectory}/functions/core/${payload.data.payload.path}`,
57
+ `${Manager.cwd}/${payload.data.payload.path}`,
58
+ `${Manager.cwd}/hooks/${payload.data.payload.path}`,
59
+ ];
60
+
61
+ // Loop through paths and try to load the hook
62
+ for (let i = 0; i < paths.length; i++) {
63
+ const current = pathify(paths[i]);
64
+
65
+ // Log
66
+ assistant.log('Trying path:', current);
67
+
68
+ // Try to load the hook
69
+ try {
70
+ // If the hook is successfully loaded, break the loop
71
+ return (new (require(current))());
72
+ } catch (e) {
73
+ // if the hook fails to load, continue to the next path
74
+ }
75
+ }
76
+ }
77
+
51
78
  function pathify(path) {
52
- const fixed = path
53
- // Replace .js
79
+ const fixed = path
54
80
  .replace('.js', '')
55
81
 
56
82
  // Return
@@ -24,7 +24,7 @@ Module.prototype.main = function () {
24
24
 
25
25
  const settings = _.merge({}, payload.data.payload.existingSettings, payload.data.payload.newSettings);
26
26
 
27
- const resolvedPath = path.join(process.cwd(), `defaults.js`);
27
+ const resolvedPath = path.join(Manager.cwd, `defaults.js`);
28
28
 
29
29
  // Check if the file exists
30
30
  if (!jetpack.exists(resolvedPath)) {
@@ -326,7 +326,7 @@ function resolveBasePath(basePath, command) {
326
326
  };
327
327
 
328
328
  function resolveApiPath(command) {
329
- const projectBasePath = path.join(process.cwd(), 'routes/api');
329
+ const projectBasePath = path.join(Manager.cwd, 'routes/api');
330
330
  const localBasePath = './api/';
331
331
 
332
332
  const projectPath = resolveBasePath(projectBasePath, command);
@@ -0,0 +1,242 @@
1
+ // Libraries
2
+ const fetch = require('wonderful-fetch');
3
+ const powertools = require('node-powertools');
4
+ const moment = require('moment');
5
+
6
+ const PROMPT = `
7
+ Company: {name}: {description}
8
+ Current Date: {date}
9
+ Instructions: {prompt}
10
+ Topic Suggestion: {suggestion}
11
+ `
12
+
13
+ function Module() {
14
+
15
+ }
16
+
17
+ Module.prototype.main = function (assistant, context) {
18
+ const self = this;
19
+
20
+ // Shortcuts
21
+ const Manager = assistant.Manager;
22
+ const libraries = Manager.libraries;
23
+
24
+ return new Promise(async function(resolve, reject) {
25
+ // Log
26
+ assistant.log(`Starting...`);
27
+
28
+ // Get settings
29
+ const settings = Manager.config.ghostii;
30
+
31
+ // Log
32
+ assistant.log(`Settings`, settings);
33
+
34
+ // Quit if articles are disabled
35
+ if (!settings.articles || !settings.sources.length) {
36
+ assistant.log(`Quitting because articles are disabled`);
37
+
38
+ return resolve();
39
+ }
40
+
41
+ // Get app content
42
+ self.appObject = await self.getAppData().catch((e) => e);
43
+ if (self.appObject instanceof Error) {
44
+ return reject(self.appObject);
45
+ }
46
+
47
+ // Harvest articles
48
+ const result = await self.harvest(settings).catch((e) => e);
49
+ if (result instanceof Error) {
50
+ return reject(result);
51
+ }
52
+
53
+ // Log
54
+ assistant.log(`Finished!`);
55
+
56
+ // Resolve
57
+ return resolve();
58
+ });
59
+ }
60
+
61
+ Module.prototype.harvest = function (settings) {
62
+ const self = this;
63
+
64
+ // Shortcuts
65
+ const Manager = self.Manager;
66
+ const libraries = self.libraries;
67
+ const assistant = self.assistant;
68
+ const context = self.context;
69
+
70
+ return new Promise(async function(resolve, reject) {
71
+ const date = moment().format('MMMM YYYY');
72
+
73
+ // Log
74
+ assistant.log(`harvest(): Starting...`);
75
+
76
+ // Process the number of sources in the settings
77
+ for (let index = 0; index < settings.articles; index++) {
78
+ const source = powertools.random(settings.sources);
79
+ const isURL = self.isURL(source);
80
+ let suggestion = null;
81
+
82
+ // Log
83
+ assistant.log(`harvest(): Processing ${index + 1}/${settings.articles} sources isURL=${isURL}`, source);
84
+
85
+ // Get suggestion
86
+ if (source === '$app') {
87
+ suggestion = 'Write an article about any topic that would be relevant to our website and business.';
88
+ } else if (isURL) {
89
+ suggestion = await self.getURLContent(source).catch((e) => e);
90
+ } else {
91
+ suggestion = source;
92
+ }
93
+
94
+ // Check for errors
95
+ if (suggestion instanceof Error) {
96
+ assistant.error(`harvest(): Error fetching ${source} suggestion`, suggestion);
97
+
98
+ break;
99
+ }
100
+
101
+ // Set suggestion
102
+ const final = powertools.template(PROMPT, {
103
+ name: self.appObject.name,
104
+ description: self.appObject.description,
105
+ prompt: settings.prompt,
106
+ date: date,
107
+ suggestion: suggestion,
108
+ });
109
+
110
+ // Log
111
+ assistant.log('Get final content', final);
112
+
113
+ // Request to Ghostii
114
+ const result = await self.requestGhostii(final).catch((e) => e);
115
+ if (result instanceof Error) {
116
+ assistant.error(`harvest(): Error requesting Ghostii`, result);
117
+
118
+ break;
119
+ }
120
+
121
+ }
122
+
123
+ // Log
124
+ return resolve();
125
+ });
126
+ }
127
+
128
+ Module.prototype.getAppData = function (settings) {
129
+ const self = this;
130
+
131
+ // Shortcuts
132
+ const Manager = self.Manager;
133
+ const libraries = self.libraries;
134
+ const assistant = self.assistant;
135
+ const context = self.context;
136
+
137
+ return new Promise(async function(resolve, reject) {
138
+ // Fetch app details
139
+ fetch('https://us-central1-itw-creative-works.cloudfunctions.net/getApp', {
140
+ method: 'post',
141
+ timeout: 30000,
142
+ tries: 3,
143
+ response: 'json',
144
+ body: {
145
+ id: Manager.config.app.id,
146
+ },
147
+ })
148
+ .then((r) => {
149
+ return resolve({
150
+ name: r?.name,
151
+ description: r?.brand?.description || '',
152
+ acceptable: r?.sponsorships?.acceptable || [],
153
+ })
154
+ })
155
+ .catch((e) => reject(e));
156
+ });
157
+ }
158
+
159
+ Module.prototype.getURLContent = function (url) {
160
+ const self = this;
161
+
162
+ // Shortcuts
163
+ const Manager = self.Manager;
164
+ const libraries = self.libraries;
165
+ const assistant = self.assistant;
166
+ const context = self.context;
167
+
168
+ return new Promise(async function(resolve, reject) {
169
+ // Fetch URL
170
+ fetch(url, {
171
+ timeout: 30000,
172
+ tries: 3,
173
+ response: 'text',
174
+ })
175
+ .then((r) => {
176
+ return resolve(extractBodyContent(r));
177
+ })
178
+ .catch((e) => reject(e));
179
+ });
180
+ }
181
+
182
+ Module.prototype.isURL = function (url) {
183
+ const self = this;
184
+
185
+ // Shortcuts
186
+ const Manager = self.Manager;
187
+ const libraries = self.libraries;
188
+ const assistant = self.assistant;
189
+ const context = self.context;
190
+
191
+ try {
192
+ return !!new URL(url);
193
+ } catch (e) {
194
+ return false;
195
+ }
196
+ }
197
+
198
+ // Request to Ghostii
199
+ Module.prototype.requestGhostii = function (content) {
200
+ const self = this;
201
+
202
+ // Shortcuts
203
+ const Manager = self.Manager;
204
+ const libraries = self.libraries;
205
+ const assistant = self.assistant;
206
+ const context = self.context;
207
+
208
+ return new Promise(async function(resolve, reject) {
209
+ // Fetch URL
210
+ fetch('https://api.ghostii.ai/write/article', {
211
+ method: 'post',
212
+ timeout: 30000,
213
+ tries: 3,
214
+ response: 'json',
215
+ body: {
216
+ keywords: [''],
217
+ description: content,
218
+ },
219
+ })
220
+ .then((r) => {
221
+ return resolve(r);
222
+ })
223
+ .catch((e) => reject(e));
224
+ });
225
+ }
226
+
227
+ const extractBodyContent = (html) => {
228
+ // Extract the content within the body tag
229
+ const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
230
+ if (!bodyMatch) return '';
231
+
232
+ let bodyContent = bodyMatch[1];
233
+
234
+ // Remove script and meta tags
235
+ bodyContent = bodyContent.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '');
236
+ bodyContent = bodyContent.replace(/<meta[^>]*>/gi, '');
237
+
238
+ // Remove remaining HTML tags
239
+ return bodyContent.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
240
+ };
241
+
242
+ module.exports = Module;
@@ -15,7 +15,7 @@ Module.prototype.main = function (assistant, context) {
15
15
  return new Promise(async function(resolve, reject) {
16
16
  self.storage = Manager.storage({name: 'usage', temporary: true, clear: false, log: false});
17
17
 
18
- assistant.log(`cron/daily/reset-usage(): Starting main...`);
18
+ assistant.log(`Starting...`);
19
19
 
20
20
  // Clear local
21
21
  await self.clearLocal();
@@ -38,20 +38,20 @@ Module.prototype.clearLocal = function() {
38
38
 
39
39
  return new Promise(async function(resolve, reject) {
40
40
  // Log status
41
- assistant.log(`cron/daily/reset-usage() [local]: Starting...`);
41
+ assistant.log(`[local]: Starting...`);
42
42
 
43
43
  // Set variables
44
44
  const now = new Date();
45
45
 
46
46
  // Log storage
47
- assistant.log(`cron/daily/reset-usage() [local]: storage(apps)`, self.storage.get('apps', {}).value());
48
- assistant.log(`cron/daily/reset-usage() [local]: storage(users)`, self.storage.get('users', {}).value());
47
+ assistant.log(`[local]: storage(apps)`, self.storage.get('apps', {}).value());
48
+ assistant.log(`[local]: storage(users)`, self.storage.get('users', {}).value());
49
49
 
50
50
  // Clear storage
51
51
  self.storage.setState({}).write();
52
52
 
53
53
  // Log status
54
- assistant.log(`cron/daily/reset-usage() [local]: Completed!`);
54
+ assistant.log(`[local]: Completed!`);
55
55
 
56
56
  return resolve();
57
57
  });
@@ -68,7 +68,7 @@ Module.prototype.clearFirestore = function() {
68
68
 
69
69
  return new Promise(async function(resolve, reject) {
70
70
  // Log status
71
- assistant.log(`cron/daily/reset-usage() [firestore]: Starting...`);
71
+ assistant.log(`[firestore]: Starting...`);
72
72
 
73
73
  // Clear storage
74
74
  const metrics = await fetch(`https://us-central1-itw-creative-works.cloudfunctions.net/getApp`, {
@@ -95,7 +95,7 @@ Module.prototype.clearFirestore = function() {
95
95
  .catch(e => e);
96
96
 
97
97
  // Log status
98
- assistant.log(`cron/daily/reset-usage() [firestore]: Resetting metrics`, metrics);
98
+ assistant.log(`[firestore]: Resetting metrics`, metrics);
99
99
 
100
100
  if (metrics instanceof Error) {
101
101
  return reject(assistant.errorify(`Failed to check providers: ${metrics}`, {code: 500}));
@@ -104,7 +104,7 @@ Module.prototype.clearFirestore = function() {
104
104
  // Reset all metrics with for loop of metrics
105
105
  // TODO: OPTIMIZATION: Put all of the changes into a single batch
106
106
  for (const metric of Object.keys(metrics)) {
107
- assistant.log(`cron/daily/reset-usage() [firestore]: Resetting ${metric} for all users`);
107
+ assistant.log(`[firestore]: Resetting ${metric} for all users`);
108
108
 
109
109
  await Manager.Utilities().iterateCollection((batch, index) => {
110
110
  return new Promise(async (resolve, reject) => {
@@ -130,7 +130,7 @@ Module.prototype.clearFirestore = function() {
130
130
  // Update the doc
131
131
  await doc.ref.update({usage: data.usage})
132
132
  .then(r => {
133
- assistant.log(`cron/daily/reset-usage() [firestore]: Reset ${metric} for ${doc.id} (${original} -> 0)`);
133
+ assistant.log(`[firestore]: Reset ${metric} for ${doc.id} (${original} -> 0)`);
134
134
  })
135
135
  .catch(e => {
136
136
  assistant.errorify(`Error resetting ${metric} for ${doc.id}: ${e}`, {code: 500, log: true});
@@ -149,7 +149,7 @@ Module.prototype.clearFirestore = function() {
149
149
  log: true,
150
150
  })
151
151
  .then((r) => {
152
- assistant.log(`cron/daily/reset-usage() [firestore]: Reset ${metric} for all users complete!`);
152
+ assistant.log(`[firestore]: Reset ${metric} for all users complete!`);
153
153
  })
154
154
  .catch(e => {
155
155
  assistant.errorify(`Error resetting ${metric} for all users: ${e}`, {code: 500, log: true});
@@ -183,7 +183,7 @@ Module.prototype.clearFirestore = function() {
183
183
  })
184
184
  // await libraries.admin.firestore().doc(`temporary/usage`).delete()
185
185
  // .then(r => {
186
- // assistant.log(`cron/daily/reset-usage() [firestore]: Deleted temporary/usage`);
186
+ // assistant.log(`[firestore]: Deleted temporary/usage`);
187
187
  // })
188
188
  // .catch(e => {
189
189
  // assistant.errorify(`Error deleting temporary/usage: ${e}`, {code: 500, log: true});
@@ -41,7 +41,7 @@ Module.prototype.main = function() {
41
41
  await loadAndExecuteJobs(`${__dirname}/daily`, Manager, context).catch((e) => error = e);
42
42
 
43
43
  // Load custom jobs
44
- await loadAndExecuteJobs(`${process.cwd()}/hooks/cron/daily`, Manager, context).catch((e) => error = e);
44
+ await loadAndExecuteJobs(`${Manager.cwd}/hooks/cron/daily`, Manager, context).catch((e) => error = e);
45
45
 
46
46
  // If there was an error, reject
47
47
  if (error) {
@@ -40,8 +40,8 @@ Middleware.prototype.run = function (libPath, options) {
40
40
  options.schema = typeof options.schema === 'undefined' ? undefined : options.schema;
41
41
 
42
42
  // Set base path
43
- options.routesDir = typeof options.routesDir === 'undefined' ? `${process.cwd()}/routes` : options.routesDir;
44
- options.schemasDir = typeof options.schemasDir === 'undefined' ? `${process.cwd()}/schemas` : options.schemasDir;
43
+ options.routesDir = typeof options.routesDir === 'undefined' ? `${Manager.cwd}/routes` : options.routesDir;
44
+ options.schemasDir = typeof options.schemasDir === 'undefined' ? `${Manager.cwd}/schemas` : options.schemasDir;
45
45
 
46
46
  // Log
47
47
  assistant.log(`Middleware.process(): Request (${geolocation.ip} @ ${geolocation.country}, ${geolocation.region}, ${geolocation.city})`, JSON.stringify(data));
@@ -26,7 +26,7 @@ Settings.prototype.resolve = function (assistant, schema, settings, options) {
26
26
 
27
27
  // Set options
28
28
  options = options || {};
29
- options.dir = typeof options.dir === 'undefined' ? `${process.cwd()}/schemas` : options.dir;
29
+ options.dir = typeof options.dir === 'undefined' ? `${Manager.cwd}/schemas` : options.dir;
30
30
  options.schema = typeof options.schema === 'undefined' ? undefined : options.schema;
31
31
 
32
32
  // Load schema if not provided and schema is defined in options
@@ -14,6 +14,8 @@ const util = require('util');
14
14
  const core = './functions/core';
15
15
  const wrappers = './functions/wrappers';
16
16
 
17
+ const BEM_CONFIG_TEMPLATE_PATH = path.resolve(__dirname, '../../templates/backend-manager-config.json');
18
+
17
19
  function Manager(exporter, options) {
18
20
  const self = this;
19
21
 
@@ -64,6 +66,7 @@ Manager.prototype.init = function (exporter, options) {
64
66
  options.checkNodeVersion = typeof options.checkNodeVersion === 'undefined' ? true : options.checkNodeVersion;
65
67
  options.uniqueAppName = options.uniqueAppName || undefined;
66
68
  options.assistant = options.assistant || {};
69
+ options.cwd = typeof options.cwd === 'undefined' ? process.cwd() : options.cwd;
67
70
  // options.assistant.optionsLogString = options.assistant.optionsLogString || undefined;
68
71
 
69
72
  // Load libraries
@@ -84,7 +87,8 @@ Manager.prototype.init = function (exporter, options) {
84
87
  };
85
88
 
86
89
  // Set properties
87
- self.cwd = process.cwd();
90
+ self.cwd = options.cwd;
91
+ self.rootDirectory = __dirname;
88
92
 
89
93
  // Set options
90
94
  self.options = options;
@@ -94,12 +98,12 @@ Manager.prototype.init = function (exporter, options) {
94
98
  self.project.backendManagerConfigPath = path.resolve(self.cwd, options.backendManagerConfigPath);
95
99
 
96
100
  // Load package.json
97
- self.package = resolveProjectPackage();
101
+ self.package = resolveProjectPackage(self.cwd);
98
102
 
99
103
  // Load config
100
104
  self.config = merge(
101
105
  // Load basic config
102
- merge({}, requireJSON5('../../templates/backend-manager-config.json', true), requireJSON5(self.project.backendManagerConfigPath, true)),
106
+ merge({}, requireJSON5(BEM_CONFIG_TEMPLATE_PATH, true), requireJSON5(self.project.backendManagerConfigPath, true)),
103
107
  // Load ENV config as a fallback
104
108
  requireJSON5(path.resolve(self.cwd, '.runtimeconfig.json'), options.projectType === 'firebase'),
105
109
  // Finally, load the functions config
@@ -108,8 +112,8 @@ Manager.prototype.init = function (exporter, options) {
108
112
  : {},
109
113
  );
110
114
 
111
- // Saved config
112
- const appId = get(self.config, 'app.id');
115
+ // Get app ID
116
+ const appId = self.config?.app?.id;
113
117
 
114
118
  // Init assistant
115
119
  self.assistant = self.Assistant().init(undefined, options.assistant);
@@ -119,10 +123,17 @@ Manager.prototype.init = function (exporter, options) {
119
123
  ? `http://localhost:5001/${self.project.projectId}/${self.project.resourceZone}`
120
124
  : `https://${self.project.resourceZone}-${self.project.projectId}.cloudfunctions.net`;
121
125
 
122
- process.env.ENVIRONMENT = !process.env.ENVIRONMENT ? self.assistant.meta.environment : process.env.ENVIRONMENT;
126
+ // Set environment
127
+ process.env.ENVIRONMENT = !process.env.ENVIRONMENT
128
+ ? self.assistant.meta.environment
129
+ : process.env.ENVIRONMENT;
123
130
 
124
131
  // Use the working Firebase logger that they disabled for whatever reason
125
- if (process.env.GCLOUD_PROJECT && self.assistant.meta.environment !== 'development' && options.useFirebaseLogger) {
132
+ if (
133
+ process.env.GCLOUD_PROJECT
134
+ && self.assistant.meta.environment !== 'development'
135
+ && options.useFirebaseLogger
136
+ ) {
126
137
  // require('firebase-functions/lib/logger/compat'); // Old way
127
138
  require('firebase-functions/logger/compat'); // firebase-functions@4 and above?
128
139
  }
@@ -132,7 +143,7 @@ Manager.prototype.init = function (exporter, options) {
132
143
  const semverMajor = require('semver/functions/major')
133
144
  const semverCoerce = require('semver/functions/coerce')
134
145
  const semverUsing = semverMajor(semverCoerce(process.versions.node));
135
- const semverRequired = semverMajor(semverCoerce(get(self.package, 'engines.node', '0.0.0')));
146
+ const semverRequired = semverMajor(semverCoerce(self.package?.engines?.node || '0.0.0'));
136
147
 
137
148
  // Fix firebase-tools overwriting console.log
138
149
  // https://stackoverflow.com/questions/56026747/firebase-console-log-on-localhost
@@ -169,7 +180,7 @@ Manager.prototype.init = function (exporter, options) {
169
180
  // Setup sentry
170
181
  if (self.options.sentry) {
171
182
  const sentryRelease = `${appId || self.project.projectId}@${self.package.version}`;
172
- const sentryDSN = get(self.config, 'sentry.dsn', '');
183
+ const sentryDSN = self.config?.sentry?.dsn || '';
173
184
  // console.log('Sentry', sentryRelease, sentryDSN);
174
185
 
175
186
  self.libraries.sentry = require('@sentry/node');
@@ -973,13 +984,13 @@ Manager.prototype.setupCustomServer = function (_library, options) {
973
984
  });
974
985
  }
975
986
 
976
- function resolveProjectPackage() {
987
+ function resolveProjectPackage(dir) {
977
988
  try {
978
- return require(path.resolve(process.cwd(), 'functions', 'package.json'));
989
+ return require(path.resolve(dir, 'functions', 'package.json'));
979
990
  } catch (e) {}
980
991
 
981
992
  try {
982
- return require(path.resolve(process.cwd(), 'package.json'));
993
+ return require(path.resolve(dir, 'package.json'));
983
994
  } catch (e) {}
984
995
  }
985
996
 
@@ -37,6 +37,8 @@
37
37
  articles: 1,
38
38
  sources: [
39
39
  '$app',
40
- ]
40
+ // Add more sources here
41
+ ],
42
+ prompt: '',
41
43
  },
42
44
  }