get-reading-time 1.0.5 → 1.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Md Nur Ahmad
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -12,6 +12,7 @@ A simple text analysis tool that provides insights into text such as reading tim
12
12
  - **Readability Score**: Calculates the Flesch Reading Ease score to evaluate the text's readability.
13
13
  - **Sentiment Analysis**: Analyzes the sentiment of the text (Positive, Negative, or Neutral).
14
14
  - **SEO-friendly Keywords**: Extracts the top 5 SEO-friendly keywords or key phrases (bi-grams and tri-grams) from the text.
15
+ - **aiTexGen**: Allows users to generate content based on a search prompt using the Cohere AI API, with a specified word count.
15
16
 
16
17
  ## Installation
17
18
 
@@ -35,7 +36,7 @@ Here’s how you can use the analyzeText function to analyze a piece of text:
35
36
  import { analyzeText } from "get-reading-time/dist/index.js";
36
37
  ```
37
38
 
38
- // Example input text
39
+ ### Example Code
39
40
 
40
41
  ```javascript
41
42
  const text = `This is an example of text. It contains words, sentences, and links like there are https://linkedin.com/in/06nurahmed https://github.com/nurahmed123`;
@@ -81,10 +82,55 @@ try {
81
82
  }
82
83
  ```
83
84
 
85
+ ### aiTexGen: Generate your content
86
+ This new feature allows users to generate content using Cohere AI. You provide your Cohere API key, a search prompt, and a word count, and it will return the generated content.
87
+
88
+ ### Usage
89
+ To use the **aiTexGen** feature, follow these steps:
90
+ ```bash
91
+ import { aiTexGen } from "get-reading-time/dist/index.js";
92
+ ```
93
+
94
+ ### Example Code
95
+ - Create your [Api key](https://dashboard.cohere.com/api-keys) From here.
96
+ ```javascript
97
+ // Replace this with your Cohere API key
98
+ const apiKey = '<api-key>';
99
+ const generator = new aiTexGen(apiKey);
100
+
101
+ async function testGenerateContent() {
102
+ try {
103
+ // Define a topic and word count for testing
104
+ const topic = 'arduino';
105
+ const wordCount = 100;
106
+
107
+ // Call the generateContent method
108
+ const result = await generator.generateContent(topic, wordCount);
109
+
110
+ // Output the result in JSON format
111
+ console.log('Generated Content (JSON Format):');
112
+ console.log(JSON.stringify(result, null, 2)); // Pretty print the JSON output
113
+
114
+ } catch (error) {
115
+ console.error('Error during content generation:', error);
116
+ }
117
+ }
118
+ // Run the test
119
+ testGenerateContent();
120
+ ```
121
+ ### Example Output
122
+ ```json
123
+ {
124
+ "topic": "arduino",
125
+ "content": "Arduino is an open-source electronics platform based on easy-to-use hardware and software. It's designed to make interactive projects accessible to everyone, from artists and designers to hobbyists and engineers. \n\nAt the heart of the Arduino platform is the Arduino board, a simple microcontroller board that can read inputs and turn them into outputs, such as turning on an LED light or activating a motor. \n\nWhat makes Arduino unique is its user-friendly approach, with a simplified programming language and easy-to-use hardware, making it a popular choice for beginners and professionals alike. With its versatility and robust community support, Arduino has become a go-to platform for creating interactive, sensor-based projects and prototypes."
126
+ }
127
+ ```
128
+
84
129
  ## Parameters
85
130
 
86
131
  - text: The text to be analyzed (required).
87
132
  - wordsPerMinute: The reading speed in words per minute (optional, default is 200 words per minute).
133
+ - **apiKey**: Your Cohere API key to use this feature (required).
88
134
 
89
135
  ## Functions
90
136
 
@@ -100,6 +146,8 @@ Analyzes the provided text and returns an object containing:
100
146
  - readabilityScore: Flesch Reading Ease score (higher is easier to read).
101
147
  - sentiment: Sentiment of the text (Positive, Negative, or Neutral).
102
148
  - keywords: Top 5 SEO-friendly keywords or key phrases.
149
+ ### Generate Content(topic: string, wordCount: number)
150
+ Generates content based on the given topic and word count using the Cohere AI API and returns the generated content.
103
151
 
104
152
  ### Helper Functions
105
153
 
@@ -113,6 +161,7 @@ Analyzes the provided text and returns an object containing:
113
161
  - **extractKeywords(text: string)**: Extracts the top 5 SEO-friendly keywords or key phrases.
114
162
  - **extractNGrams(words: string[])**: Extracts bi-grams and tri-grams from the list of words.
115
163
  - **isStopWord(word: string, stopWords: Set<string>)**: Checks if a word is a stop word to exclude from keyword extraction.
164
+ - **generateContent(words: string[])**: Genarate text based on the query and word limit.
116
165
 
117
166
  ## Contributing
118
167
 
@@ -120,7 +169,8 @@ Feel free to fork the repository, make your changes, and submit a pull request.
120
169
 
121
170
  ## License
122
171
 
123
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
172
+ This project is licensed under the MIT License - see the [LICENSE](https://github.com/nurahmed123/get-reading-time/blob/main/LICENSE) file for details.
173
+ [![npm version](https://badge.fury.io/js/get-reading-time.svg)](https://www.npmjs.com/get-reading-time)
124
174
 
125
175
  ## Contact
126
176
 
package/dist/index.d.mts CHANGED
@@ -14,4 +14,31 @@ interface TextAnalysisResult {
14
14
  }
15
15
  declare function analyzeText(text: string, wordsPerMinute?: number): TextAnalysisResult;
16
16
 
17
- export { analyzeText };
17
+ /**
18
+ * Formats an article by correcting punctuation and capitalization,
19
+ * intelligently inferring sentence boundaries and adding missing punctuation.
20
+ *
21
+ * @param {string} article - The raw input string to be formatted.
22
+ * @returns {{ original: string, formatted: string }} - A JSON object with the original and formatted strings.
23
+ * @throws {TypeError} If the input is not a valid non-empty string.
24
+ */
25
+ declare const getPunch: (article: string) => {
26
+ original: string;
27
+ formatted: string;
28
+ };
29
+
30
+ declare class AiTexGen {
31
+ private cohereClient;
32
+ constructor(apiKey: string);
33
+ private validateInputs;
34
+ generateContent(topic: string, wordCount?: number): Promise<{
35
+ topic: string;
36
+ content: string;
37
+ }>;
38
+ private processContent;
39
+ private extractTextFromObject;
40
+ private createErrorResponse;
41
+ private formatErrorDetails;
42
+ }
43
+
44
+ export { AiTexGen as aiTexGen, analyzeText, getPunch };
package/dist/index.d.ts CHANGED
@@ -14,4 +14,31 @@ interface TextAnalysisResult {
14
14
  }
15
15
  declare function analyzeText(text: string, wordsPerMinute?: number): TextAnalysisResult;
16
16
 
17
- export { analyzeText };
17
+ /**
18
+ * Formats an article by correcting punctuation and capitalization,
19
+ * intelligently inferring sentence boundaries and adding missing punctuation.
20
+ *
21
+ * @param {string} article - The raw input string to be formatted.
22
+ * @returns {{ original: string, formatted: string }} - A JSON object with the original and formatted strings.
23
+ * @throws {TypeError} If the input is not a valid non-empty string.
24
+ */
25
+ declare const getPunch: (article: string) => {
26
+ original: string;
27
+ formatted: string;
28
+ };
29
+
30
+ declare class AiTexGen {
31
+ private cohereClient;
32
+ constructor(apiKey: string);
33
+ private validateInputs;
34
+ generateContent(topic: string, wordCount?: number): Promise<{
35
+ topic: string;
36
+ content: string;
37
+ }>;
38
+ private processContent;
39
+ private extractTextFromObject;
40
+ private createErrorResponse;
41
+ private formatErrorDetails;
42
+ }
43
+
44
+ export { AiTexGen as aiTexGen, analyzeText, getPunch };
package/dist/index.js CHANGED
@@ -26,13 +26,37 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  mod
27
27
  ));
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var __async = (__this, __arguments, generator) => {
30
+ return new Promise((resolve, reject) => {
31
+ var fulfilled = (value) => {
32
+ try {
33
+ step(generator.next(value));
34
+ } catch (e) {
35
+ reject(e);
36
+ }
37
+ };
38
+ var rejected = (value) => {
39
+ try {
40
+ step(generator.throw(value));
41
+ } catch (e) {
42
+ reject(e);
43
+ }
44
+ };
45
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
46
+ step((generator = generator.apply(__this, __arguments)).next());
47
+ });
48
+ };
29
49
 
30
50
  // src/index.ts
31
51
  var index_exports = {};
32
52
  __export(index_exports, {
33
- analyzeText: () => analyzeText
53
+ aiTexGen: () => AiTexGen,
54
+ analyzeText: () => analyzeText,
55
+ getPunch: () => getPunch
34
56
  });
35
57
  module.exports = __toCommonJS(index_exports);
58
+
59
+ // src/utils/analyzeText.ts
36
60
  var import_sentiment = __toESM(require("sentiment"));
37
61
  var DEFAULT_READING_SPEED = 200;
38
62
  function analyzeText(text, wordsPerMinute = DEFAULT_READING_SPEED) {
@@ -160,7 +184,117 @@ function extractNGrams(words) {
160
184
  function isStopWord(word, stopWords) {
161
185
  return stopWords.has(word);
162
186
  }
187
+
188
+ // src/utils/getPunch.ts
189
+ var import_compromise = __toESM(require("compromise"));
190
+ var getPunch = (article) => {
191
+ if (typeof article !== "string" || article.trim().length === 0) {
192
+ throw new TypeError("Invalid input: article must be a non-empty string.");
193
+ }
194
+ try {
195
+ const doc = (0, import_compromise.default)(article);
196
+ const formatted = doc.sentences().normalize().out("text");
197
+ return {
198
+ original: article.trim(),
199
+ formatted
200
+ };
201
+ } catch (error) {
202
+ throw new Error(`Error processing the article: ${error instanceof Error ? error.message : "Unknown error"}`);
203
+ }
204
+ };
205
+
206
+ // src/utils/aiTexGen.ts
207
+ var import_cohere_ai = require("cohere-ai");
208
+ var AiTexGen = class {
209
+ constructor(apiKey) {
210
+ if (!apiKey || typeof apiKey !== "string") {
211
+ throw this.createErrorResponse("API_KEY_MISSING", "A valid API key must be provided.", 400);
212
+ }
213
+ this.cohereClient = new import_cohere_ai.CohereClientV2({ token: apiKey });
214
+ }
215
+ // Validate user input (topic and word count)
216
+ validateInputs(topic, wordCount) {
217
+ if (typeof topic !== "string" || topic.trim().length === 0) {
218
+ throw this.createErrorResponse("INVALID_TOPIC", "Topic must be a non-empty string.", 400);
219
+ }
220
+ if (typeof wordCount !== "number" || wordCount <= 0) {
221
+ console.warn("Invalid word count provided. Defaulting to 100 words.");
222
+ wordCount = 100;
223
+ }
224
+ }
225
+ // Generate content using Cohere's API
226
+ generateContent(topic, wordCount = 100) {
227
+ return __async(this, null, function* () {
228
+ var _a;
229
+ try {
230
+ this.validateInputs(topic, wordCount);
231
+ const prompt = `Write a detailed article about ${topic}, aiming for maximum ${wordCount} words.`;
232
+ const response = yield this.cohereClient.chat({
233
+ model: "command-r-plus",
234
+ // Specify the model you want to use
235
+ messages: [
236
+ {
237
+ role: "user",
238
+ content: prompt
239
+ // Use the prompt with the topic and word count
240
+ }
241
+ ]
242
+ });
243
+ const content = this.processContent((_a = response.message) == null ? void 0 : _a.content);
244
+ return {
245
+ topic,
246
+ content
247
+ };
248
+ } catch (error) {
249
+ console.error("Error generating content from Cohere API:", error);
250
+ throw this.createErrorResponse("API_ERROR", "Failed to generate content from Cohere API.", 500, this.formatErrorDetails(error));
251
+ }
252
+ });
253
+ }
254
+ // Helper method to process the content and format it properly
255
+ processContent(content) {
256
+ if (!content || content.length === 0) {
257
+ return "No content generated or content is empty.";
258
+ }
259
+ return content.map((item) => {
260
+ if (typeof item === "string") {
261
+ return item.trim();
262
+ } else if (typeof item === "object") {
263
+ return this.extractTextFromObject(item);
264
+ }
265
+ return "";
266
+ }).join(" ").trim();
267
+ }
268
+ // Helper method to safely extract text from an object, if needed
269
+ extractTextFromObject(obj) {
270
+ if (obj && obj.text) {
271
+ return obj.text.trim();
272
+ }
273
+ return "";
274
+ }
275
+ // Helper method to create an error response with a status code
276
+ createErrorResponse(code, message, statusCode, details) {
277
+ return {
278
+ error: {
279
+ code,
280
+ message,
281
+ statusCode,
282
+ details: details || ""
283
+ }
284
+ };
285
+ }
286
+ // Helper method to format error details (narrowing down the 'unknown' type)
287
+ formatErrorDetails(error) {
288
+ if (error instanceof Error) {
289
+ return error.message;
290
+ } else {
291
+ return "An unknown error occurred.";
292
+ }
293
+ }
294
+ };
163
295
  // Annotate the CommonJS export names for ESM import in node:
164
296
  0 && (module.exports = {
165
- analyzeText
297
+ aiTexGen,
298
+ analyzeText,
299
+ getPunch
166
300
  });
package/dist/index.mjs CHANGED
@@ -1,4 +1,25 @@
1
- // src/index.ts
1
+ var __async = (__this, __arguments, generator) => {
2
+ return new Promise((resolve, reject) => {
3
+ var fulfilled = (value) => {
4
+ try {
5
+ step(generator.next(value));
6
+ } catch (e) {
7
+ reject(e);
8
+ }
9
+ };
10
+ var rejected = (value) => {
11
+ try {
12
+ step(generator.throw(value));
13
+ } catch (e) {
14
+ reject(e);
15
+ }
16
+ };
17
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
18
+ step((generator = generator.apply(__this, __arguments)).next());
19
+ });
20
+ };
21
+
22
+ // src/utils/analyzeText.ts
2
23
  import Sentiment from "sentiment";
3
24
  var DEFAULT_READING_SPEED = 200;
4
25
  function analyzeText(text, wordsPerMinute = DEFAULT_READING_SPEED) {
@@ -126,6 +147,116 @@ function extractNGrams(words) {
126
147
  function isStopWord(word, stopWords) {
127
148
  return stopWords.has(word);
128
149
  }
150
+
151
+ // src/utils/getPunch.ts
152
+ import nlp from "compromise";
153
+ var getPunch = (article) => {
154
+ if (typeof article !== "string" || article.trim().length === 0) {
155
+ throw new TypeError("Invalid input: article must be a non-empty string.");
156
+ }
157
+ try {
158
+ const doc = nlp(article);
159
+ const formatted = doc.sentences().normalize().out("text");
160
+ return {
161
+ original: article.trim(),
162
+ formatted
163
+ };
164
+ } catch (error) {
165
+ throw new Error(`Error processing the article: ${error instanceof Error ? error.message : "Unknown error"}`);
166
+ }
167
+ };
168
+
169
+ // src/utils/aiTexGen.ts
170
+ import { CohereClientV2 } from "cohere-ai";
171
+ var AiTexGen = class {
172
+ constructor(apiKey) {
173
+ if (!apiKey || typeof apiKey !== "string") {
174
+ throw this.createErrorResponse("API_KEY_MISSING", "A valid API key must be provided.", 400);
175
+ }
176
+ this.cohereClient = new CohereClientV2({ token: apiKey });
177
+ }
178
+ // Validate user input (topic and word count)
179
+ validateInputs(topic, wordCount) {
180
+ if (typeof topic !== "string" || topic.trim().length === 0) {
181
+ throw this.createErrorResponse("INVALID_TOPIC", "Topic must be a non-empty string.", 400);
182
+ }
183
+ if (typeof wordCount !== "number" || wordCount <= 0) {
184
+ console.warn("Invalid word count provided. Defaulting to 100 words.");
185
+ wordCount = 100;
186
+ }
187
+ }
188
+ // Generate content using Cohere's API
189
+ generateContent(topic, wordCount = 100) {
190
+ return __async(this, null, function* () {
191
+ var _a;
192
+ try {
193
+ this.validateInputs(topic, wordCount);
194
+ const prompt = `Write a detailed article about ${topic}, aiming for maximum ${wordCount} words.`;
195
+ const response = yield this.cohereClient.chat({
196
+ model: "command-r-plus",
197
+ // Specify the model you want to use
198
+ messages: [
199
+ {
200
+ role: "user",
201
+ content: prompt
202
+ // Use the prompt with the topic and word count
203
+ }
204
+ ]
205
+ });
206
+ const content = this.processContent((_a = response.message) == null ? void 0 : _a.content);
207
+ return {
208
+ topic,
209
+ content
210
+ };
211
+ } catch (error) {
212
+ console.error("Error generating content from Cohere API:", error);
213
+ throw this.createErrorResponse("API_ERROR", "Failed to generate content from Cohere API.", 500, this.formatErrorDetails(error));
214
+ }
215
+ });
216
+ }
217
+ // Helper method to process the content and format it properly
218
+ processContent(content) {
219
+ if (!content || content.length === 0) {
220
+ return "No content generated or content is empty.";
221
+ }
222
+ return content.map((item) => {
223
+ if (typeof item === "string") {
224
+ return item.trim();
225
+ } else if (typeof item === "object") {
226
+ return this.extractTextFromObject(item);
227
+ }
228
+ return "";
229
+ }).join(" ").trim();
230
+ }
231
+ // Helper method to safely extract text from an object, if needed
232
+ extractTextFromObject(obj) {
233
+ if (obj && obj.text) {
234
+ return obj.text.trim();
235
+ }
236
+ return "";
237
+ }
238
+ // Helper method to create an error response with a status code
239
+ createErrorResponse(code, message, statusCode, details) {
240
+ return {
241
+ error: {
242
+ code,
243
+ message,
244
+ statusCode,
245
+ details: details || ""
246
+ }
247
+ };
248
+ }
249
+ // Helper method to format error details (narrowing down the 'unknown' type)
250
+ formatErrorDetails(error) {
251
+ if (error instanceof Error) {
252
+ return error.message;
253
+ } else {
254
+ return "An unknown error occurred.";
255
+ }
256
+ }
257
+ };
129
258
  export {
130
- analyzeText
259
+ AiTexGen as aiTexGen,
260
+ analyzeText,
261
+ getPunch
131
262
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "get-reading-time",
3
- "version": "1.0.5",
3
+ "version": "1.2.0",
4
4
  "main": "/dist/index.js",
5
5
  "module": "/dist/index.mjs",
6
6
  "types": "/dist/index.d.ts",
@@ -12,7 +12,11 @@
12
12
  },
13
13
  "repository": {
14
14
  "type": "git",
15
- "url": "git+https://github.com/nurahmed123/get-reading-time.git"
15
+ "url": "git+https://github.com/nurahmed123/get-reading-time.git",
16
+ "license": "https://github.com/nurahmed123/get-reading-time/blob/main/LICENSE"
17
+ },
18
+ "publishConfig": {
19
+ "registry": "https://www.npmjs.com/package/get-reading-time"
16
20
  },
17
21
  "keywords": [
18
22
  "Reading",
@@ -136,11 +140,15 @@
136
140
  "homepage": "https://github.com/nurahmed123/get-reading-time#readme",
137
141
  "description": "",
138
142
  "devDependencies": {
143
+ "@types/axios": "^0.9.36",
139
144
  "@types/node": "^22.10.2",
140
145
  "@types/sentiment": "^5.0.4",
141
146
  "typescript": "^5.7.2"
142
147
  },
143
148
  "dependencies": {
149
+ "axios": "^1.7.9",
150
+ "cohere-ai": "^7.15.0",
151
+ "compromise": "^14.14.3",
144
152
  "sentiment": "^5.0.2",
145
153
  "tsup": "^8.3.5"
146
154
  }