From 421e4fcdb89957c69a892fc4cf6bc28d7e11ee99 Mon Sep 17 00:00:00 2001 From: Alikia2x Date: Wed, 19 Jun 2024 17:14:23 +0800 Subject: [PATCH] feature: intention detection by nlp.js, with demo of showing weather --- .../search/onesearch/handleNLUResult.ts | 42 ++ components/search/onesearch/onesearch.tsx | 28 +- lib/nlp/base.ts | 25 +- lib/nlp/data/en.json | 134 +++++ lib/nlp/data/zh.json | 124 +++++ lib/nlp/load.ts | 55 ++ lib/nlp/stopwords.ts | 4 +- lib/nlp/train.ts | 76 +++ lib/version.ts | 4 +- lib/weather/getCurrentWeather.ts | 39 ++ lib/weather/getLocation.ts | 17 + lib/weather/getWeather.ts | 23 + lib/weather/wmocode.ts | 294 ++++++++++ package.json | 13 +- pnpm-lock.yaml | 505 +++++++++++++++++- public/model | Bin 0 -> 27776 bytes test/NLP/removeStopwords.test.ts | 18 + 17 files changed, 1374 insertions(+), 27 deletions(-) create mode 100644 components/search/onesearch/handleNLUResult.ts create mode 100644 lib/nlp/data/en.json create mode 100644 lib/nlp/data/zh.json create mode 100644 lib/nlp/load.ts create mode 100644 lib/nlp/train.ts create mode 100644 lib/weather/getCurrentWeather.ts create mode 100644 lib/weather/getLocation.ts create mode 100644 lib/weather/getWeather.ts create mode 100644 lib/weather/wmocode.ts create mode 100644 public/model create mode 100644 test/NLP/removeStopwords.test.ts diff --git a/components/search/onesearch/handleNLUResult.ts b/components/search/onesearch/handleNLUResult.ts new file mode 100644 index 0000000..0feaeeb --- /dev/null +++ b/components/search/onesearch/handleNLUResult.ts @@ -0,0 +1,42 @@ +import { suggestionItem } from "@/global"; +import { findClosestDateIndex } from "@/lib/weather/getCurrentWeather"; +import { getLocationNative } from "@/lib/weather/getLocation"; +import { getWeather } from "@/lib/weather/getWeather"; +import { WMOCodeTable } from "@/lib/weather/wmocode"; + +type UpdateSuggestionFunction = (data: suggestionItem[]) => void; + +export function handleNLUResult(result: any, updateSuggestion: UpdateSuggestionFunction){ + if (result.intent == "weather.summary") { + getLocationNative((data: GeolocationCoordinates | GeolocationPositionError) => { + console.log(data); + if (data instanceof GeolocationCoordinates) { + getWeather(data.latitude, data.longitude).then((weather) => { + console.log(weather["hourly"]); + let hourIndex = findClosestDateIndex( + weather["hourly"]["time"], + weather["utc_offset_seconds"] + ); + let temp = weather["hourly"]["apparent_temperature"][hourIndex]; + let weatherCode = weather["hourly"]["weather_code"][hourIndex]; + console.log(temp, weatherCode, hourIndex); + updateSuggestion([ + { + type: "text", + suggestion: `Weather: ${temp}${weather["hourly_units"]["apparent_temperature"]}, ${WMOCodeTable[weatherCode]["day"].description}`, + relevance: 3000 * result.score + } + ]); + }); + } + }); + } else if (result.intent !== "None") { + updateSuggestion([ + { + type: "text", + suggestion: result.intent, + relevance: 2200 * result.score + } + ]); + } +} \ No newline at end of file diff --git a/components/search/onesearch/onesearch.tsx b/components/search/onesearch/onesearch.tsx index 2ebaa8c..1326e80 100644 --- a/components/search/onesearch/onesearch.tsx +++ b/components/search/onesearch/onesearch.tsx @@ -11,12 +11,19 @@ import validLink from "@/lib/url/validLink"; import Link from "./link"; import { selectedSuggestionState } from "@/components/state/suggestionSelection"; import { settingsState } from "@/components/state/settings"; -import { base64NLP } from "@/lib/onesearch/baseCheck"; import PlainText from "./plainText"; import { sendError } from "@/lib/telemetering/sendError"; +import { NLU } from "@/lib/nlp/load"; +import { getLocationNative } from "@/lib/weather/getLocation"; +import { getWeather } from "@/lib/weather/getWeather"; +import { findClosestDateIndex, getClosestHourTimestamp } from "@/lib/weather/getCurrentWeather"; +import { WMOCodeTable } from "@/lib/weather/wmocode"; +import { handleNLUResult } from "./handleNLUResult"; export default function () { const [suggestion, setFinalSuggetsion] = useRecoilState(suggestionsState); + const [location, setLocation] = useState(null); + const [manager, setManager] = useState(null); const lastRequestTimeRef = useRef(0); const selected = useRecoilValue(selectedSuggestionState); const settings = useRecoilValue(settingsState); @@ -78,6 +85,15 @@ export default function () { }); } + const NLUModel = new NLU(); + + useEffect(() => { + NLUModel.init().then((nlu) => { + setManager(nlu.manager); + console.log(nlu.manager); + }); + }, []); + useEffect(() => { cleanSuggestion("default-link", "default", "text"); if (validLink(query)) { @@ -94,9 +110,13 @@ export default function () { } ]); } - const b64 = base64NLP(query); - if (b64.suggestion !== null) { - updateSuggestion([b64 as suggestionItem]); + + if (manager != null) { + // @ts-ignore + manager.process(query).then((result) => { + console.log(result); + handleNLUResult(result, updateSuggestion); + }); } }, [query, engineName]); diff --git a/lib/nlp/base.ts b/lib/nlp/base.ts index c8abaf2..5e38b79 100644 --- a/lib/nlp/base.ts +++ b/lib/nlp/base.ts @@ -1,19 +1,32 @@ import { NLPResult } from "../onesearch/NLPResult"; import { stopwords } from "./stopwords"; -class NLP { +export class NLP { result: NLPResult; constructor( public query: String, - public task: String + public task: String, + public intentionKeywords?: String[], ) { this.result = new NLPResult(); } - public removeStopwords(str: string, extraStopwords: string[] = [], disableDefault: boolean = false){ + public removeStopwords(extraStopwords: string[] = [], disableDefault: boolean = false){ const list = disableDefault ? extraStopwords : stopwords.concat(extraStopwords); - for (let word of list){ - str = str.replace(new RegExp(`\\b${word}\\b`, 'gi'), ''); + if (list.includes(this.query.trim())) { + this.query = ""; } - return str; + for (let word of list){ + this.query = this.query.replace(new RegExp(`\\b${word}\\b`, 'gi'), ''); + } + } + public extractSlots(str: string, useNER = false): string[]{ + const slots: string[] = []; + + return slots; + } + public trim() { + this.query = this.query.trim(); + const wordList = this.query.split(" ").filter(word => word !== ""); + this.query = wordList.join(" "); } } \ No newline at end of file diff --git a/lib/nlp/data/en.json b/lib/nlp/data/en.json new file mode 100644 index 0000000..98e17dc --- /dev/null +++ b/lib/nlp/data/en.json @@ -0,0 +1,134 @@ +{ + "weather.summary": [ + "how's the weather", + "What's going on with the weather?", + "Can you give me an update on the weather?", + "How's the forecast looking today?", + "Give me a summary of the current weather.", + "Can you tell me the current weather?", + "What is the weather situation at the moment?", + "Could you provide a quick weather update?", + "Is it raining or sunny outside?", + "What's the weather like right now?", + "Tell me the current weather conditions.", + "How about the weather today?", + "Is it a good day to be outside?", + "What should I expect in terms of weather today?", + "Is there any severe weather to be aware of?", + "Can you summarize today's weather forecast?", + "What's the weather looking like for the next few hours?", + "Is it going to stay this way all day?", + "Could you give me a brief overview of the weather?", + "What's the general weather situation in our area?", + "Is it cloudy or clear outside?", + "Any weather alerts I should know about?", + "How's the weather looking for outdoor activities?", + "What's the forecast saying for today's weather?", + "Is it going to be a warm day?", + "Are we expecting any storms today?", + "What's the weather condition outside my window?", + "Is it a typical day for this season in terms of weather?", + "how's the weather now?" + ], + + "weather.temp": [ + "What's the temperature like right now?", + "Can you tell me the current temperature?", + "How hot is it outside?", + "What's the temperature supposed to be today?", + "What is the current temp outside?", + "Could you tell me the outdoor temperature?", + "Is it cold or warm outside?", + "What's the high temperature for today?", + "What's the low temperature expected tonight?", + "How does the temperature feel outside?", + "Is it going to get warmer or cooler today?", + "What's the temperature in the shade?", + "Can you provide the current temp in Celsius?", + "What's the temperature in Fahrenheit right now?", + "Is it too hot to be outside?", + "What's the temperature like in the morning?", + "How about the temperature in the evening?", + "Is it warm enough to go swimming?", + "What's the temperature in the city center?", + "Can you tell me the temp in the nearby area?", + "Is it below freezing outside?", + "What's the average temperature for today?", + "Is the temperature dropping or rising?", + "What should I wear considering the temperature?" + ], + + "base64.encode": [ + "Please encode this data with base64: %s", + "I need to encode the following data in base64: %s", + "Could you encode this string using base64? %s", + "Convert this data to b64 encoding: %s", + "I want to encode this information with base64: %s", + "Help me encode this in base64: %s", + "Can you encode this data to base64 format? %s", + "b64 encode", + "base64 encode", + "encode base64 %s" + ], + + "base64.decode": [ + "Please decode this base64 data: %s", + "I have a base64 encoded string that needs decoding: %s", + "Could you decode this base64 string for me? %s", + "Convert this base64 encoded data back to its original form: %s", + "I need to decode this base64 information: %s", + "Help me decode this base64 data: %s", + "Can you translate this base64 back to normal text? %s", + "b64 decode", + "base64 decode", + "decode base64 %s" + ], + + "url.encode": [ + "Please encode this URL: %s", + "I need to encode this URL component: %s", + "Could you encode this part of the URL? %s", + "Convert this URL to its encoded form: %s", + "I want to encode this URL for safe transmission: %s", + "Help me encode this URL segment: %s", + "Can you encode this URL data? %s" + ], + + "url.decode": [ + "Please decode this URL: %s", + "I have an encoded URL that needs decoding: %s", + "Could you decode this URL for me? %s", + "Convert this encoded URL back to its original form: %s", + "I need to decode this URL component: %s", + "Help me decode this URL segment: %s", + "Can you translate this encoded URL back to normal? %s" + ], + + "html.encode": [ + "Please encode this HTML entity: %s", + "I need to encode this text to HTML entity: %s", + "Could you encode this as an HTML entity? %s", + "Convert this text to HTML entity encoding: %s", + "I want to encode this to prevent HTML interpretation: %s", + "Help me encode this into HTML entity: %s", + "Can you encode this for HTML usage? %s" + ], + + "html.decode": [ + "Please decode this HTML entity: %s", + "I have an HTML entity that needs decoding: %s", + "Could you decode this HTML entity for me? %s", + "Convert this HTML entity back to its original text: %s", + "I need to decode this HTML entity to plain text: %s", + "Help me decode this HTML entity: %s", + "Can you translate this HTML entity back to normal text? %s" + ], + + "None": [ + "free weather api", + "js get timezone", + "how", + "how's", + "how's the" + ] +} diff --git a/lib/nlp/data/zh.json b/lib/nlp/data/zh.json new file mode 100644 index 0000000..09cdf11 --- /dev/null +++ b/lib/nlp/data/zh.json @@ -0,0 +1,124 @@ +{ + "weather.summary": [ + "天气如何", + "现在的天气", + "今天的天气预报", + "现在的天气状况", + "今天天气怎么样", + "目前是什么天气", + "今天的天气概述", + "当前天气状况如何", + "今天会下雨吗", + "今天会下雪吗", + "今天晴天吗", + "今天的天气状况如何", + "现在外面是什么天气", + "今天天气好么", + "今天适合外出吗", + "今天的天气适宜做什么", + "今天有没有雾霾", + "今天的空气质量如何", + "今天的紫外线指数是多少", + "今天有没有大风", + "今天会不会很冷", + "今天的天气会变化吗", + "今天晚上的天气如何", + "今天夜里会下雨吗", + "今天的天气对出行有影响吗", + "今天的天气对运动有影响吗", + "今天的天气对工作有影响吗", + "今天的天气对旅游有影响吗", + "今天的天气对健康有影响吗" + ], + "weather.temp": [ + "现在的温度", + "现在多少度", + "外面有多热", + "明天热不热?", + "现在的气温是多少", + "今天最高温度是多少", + "今天最低温度是多少", + "现在外面感觉冷吗", + "现在需要穿外套吗", + "现在适合穿短袖吗", + "现在的温度适合外出吗", + "现在的温度适合运动吗", + "现在的温度适合睡觉吗", + "明天会比今天热吗", + "明天会比今天冷吗", + "今天的温度变化大吗", + "现在的温度适合开空调吗", + "现在的温度适合开暖气吗", + "室外的温度是多少", + "室内的温度是多少", + "现在的温度适合种植吗", + "现在的温度适合养宠物吗", + "现在的温度对健康有影响吗", + "现在的温度是否舒适", + "现在的温度是否适合工作" + ], + "base64.encode": [ + "请将数据使用base64编码:%s", + "需要将以下数据base64编码:%s", + "请将此字符串转为base64:%s", + "将数据转为base64编码:%s", + "信息base64编码:%s", + "请帮忙编码base64:%s", + "将数据编码为base64:%s" + ], + + "base64.decode": [ + "请解码这个base64数据:%s", + "有base64编码字符串需要解码:%s", + "帮忙解码base64:%s", + "将base64编码转回原数据:%s", + "解码base64信息:%s", + "解码这个base64:%s", + "将base64转文本:%s" + ], + + "url.encode": [ + "请编码这个URL:%s", + "URL部分需要编码:%s", + "请将URL部分编码:%s", + "URL编码转换:%s", + "安全传输需编码URL:%s", + "编码URL段:%s", + "URL数据编码:%s" + ], + + "url.decode": [ + "请解码这个URL:%s", + "有URL编码需要解码:%s", + "解码这个URL:%s", + "URL编码转回原URL:%s", + "解码URL部分:%s", + "解码URL段:%s", + "URL编码转文本:%s" + ], + + "html.encode": [ + "请编码HTML实体:%s", + "文本转为HTML实体:%s", + "编码为HTML实体:%s", + "文本HTML实体编码:%s", + "预防HTML解析编码:%s", + "HTML实体编码:%s", + "文本HTML使用编码:%s" + ], + + "html.decode": [ + "请解码HTML实体:%s", + "HTML实体需要解码:%s", + "解码HTML实体:%s", + "HTML实体转回文本:%s", + "HTML实体解码:%s", + "解码HTML实体:%s", + "HTML实体转文本:%s" + ], + + "None": [ + "你好", + "为什么计算机使用二进制" + ] +} diff --git a/lib/nlp/load.ts b/lib/nlp/load.ts new file mode 100644 index 0000000..63d0517 --- /dev/null +++ b/lib/nlp/load.ts @@ -0,0 +1,55 @@ +// @ts-ignore +import { containerBootstrap } from "@nlpjs/core"; +// @ts-ignore +import { Nlp } from "@nlpjs/nlp"; +// @ts-ignore +import { NluManager, NluNeural } from "@nlpjs/nlu"; +// @ts-ignore +import { LangEn } from "@nlpjs/lang-en-min"; +// @ts-ignore +import { LangZh } from "@nlpjs/lang-zh"; +import * as fflate from 'fflate'; + +let zh: TrainData = {}; +let en: TrainData = {}; + +type TrainData = { + [key: string]: string[]; +}; + +export class NLU { + manager: any; + inited: boolean = false; + async loadIntentionModel() { + const container = await containerBootstrap(); + container.use(Nlp); + container.use(LangEn); + container.use(LangZh); + container.use(NluNeural); + const manager = new NluManager({ + container, + locales: ["en", "zh"], + nlu: { + useNoneFeature: true + } + }); + const response = await fetch("/model"); + + const responseBuf = await response.arrayBuffer(); + const compressed = new Uint8Array(responseBuf); + const decompressed = fflate.decompressSync(compressed); + const modelText = fflate.strFromU8(decompressed); + manager.fromJSON(JSON.parse(modelText)); + this.manager = manager; + // console.log(this.manager); + } + async init() { + await this.loadIntentionModel(); + this.inited = true; + return this; + } + async process(lang: string, text: string): Promise { + const actual = await this.manager.process(lang, text); + return actual; + } +} \ No newline at end of file diff --git a/lib/nlp/stopwords.ts b/lib/nlp/stopwords.ts index 0276f3a..692301a 100644 --- a/lib/nlp/stopwords.ts +++ b/lib/nlp/stopwords.ts @@ -1 +1,3 @@ -export const stopwords = ["a","about","above","after","again","against","all","am","an","and","any","are","aren't","as","at","be","because","been","before","being","below","between","both","but","by","can't","cannot","could","couldn't","did","didn't","do","does","doesn't","doing","don't","down","during","each","few","for","from","further","had","hadn't","has","hasn't","have","haven't","having","he","he'd","he'll","he's","her","here","here's","hers","herself","him","himself","his","how","how's","i","i'd","i'll","i'm","i've","if","in","into","is","isn't","it","it's","its","itself","let's","me","more","most","mustn't","my","myself","no","nor","not","of","off","on","once","only","or","other","ought","our","ours ourselves","out","over","own","same","shan't","she","she'd","she'll","she's","should","shouldn't","so","some","such","than","that","that's","the","their","theirs","them","themselves","then","there","there's","these","they","they'd","they'll","they're","they've","this","those","through","to","too","under","until","up","very","was","wasn't","we","we'd","we'll","we're","we've","were","weren't","what","what's","when","when's","where","where's","which","while","who","who's","whom","why","why's","with","won't","would","wouldn't","you","you'd","you'll","you're","you've","your","yours","yourself","yourselves"]; \ No newline at end of file +export const stopwords = ["a","about","above","after","again","against","all","am","an","and","any","are","aren't","as","at","be","because","been","before","being","below","between","both","but","by","can't","cannot","could","couldn't","did","didn't","do","does","doesn't","doing","don't","down","during","each","few","for","from","further","had","hadn't","has","hasn't","have","haven't","having","he","he'd","he'll","he's","her","here","here's","hers","herself","him","himself","his","how","how's","i","i'd","i'll","i'm","i've","if","in","into","is","isn't","it","it's","its","itself","let's","me","more","most","mustn't","my","myself","no","nor","not","of","off","on","once","only","or","other","ought","our","ours","ourselves","out","over","own","please","same","shan't","she","she'd","she'll","she's","should","shouldn't","so","some","such","than","that","that's","the","their","theirs","them","themselves","then","there","there's","these","they","they'd","they'll","they're","they've","this","those","through","to","too","under","until","up","very","was","wasn't","we","we'd","we'll","we're","we've","were","weren't","what","what's","when","when's","where","where's","which","while","who","who's","whom","why","why's","with","won't","would","wouldn't","you","you'd","you'll","you're","you've","your","yours","yourself","yourselves"]; + +export const convertStopwords = ["transform", "change", "translate", "convert"]; \ No newline at end of file diff --git a/lib/nlp/train.ts b/lib/nlp/train.ts new file mode 100644 index 0000000..21f6972 --- /dev/null +++ b/lib/nlp/train.ts @@ -0,0 +1,76 @@ +// @ts-ignore +import { containerBootstrap } from "@nlpjs/core"; +// @ts-ignore +import { Nlp } from "@nlpjs/nlp"; +// @ts-ignore +import { NluManager, NluNeural } from "@nlpjs/nlu"; +// @ts-ignore +import { LangEn } from "@nlpjs/lang-en-min"; +// @ts-ignore +import { LangZh } from "@nlpjs/lang-zh"; +import fs from "node:fs"; +import * as fflate from 'fflate'; + +let zh: TrainData = {}; +let en: TrainData = {}; + +type TrainData = { + [key: string]: string[]; +}; + +export async function trainIntentionModel() { + try { + const dataZH = fs.readFileSync("./lib/nlp/data/zh.json", "utf8"); + const dataEN = fs.readFileSync("./lib/nlp/data/en.json", "utf8"); + zh = JSON.parse(dataZH); + en = JSON.parse(dataEN); + } catch (err) { + console.error(err); + } + + const container = await containerBootstrap(); + container.use(Nlp); + container.use(LangEn); + container.use(LangZh); + container.use(NluNeural); + const manager = new NluManager({ + container, + locales: ["en", "zh"], + nlu: { + useNoneFeature: true + } + }); + // Adds the utterances and intents for the NLP + + for (const key in zh) { + for (const value of zh[key]) { + manager.add("zh", value, key); + } + } + + for (const key in en) { + for (const value of en[key]) { + manager.add("en", value, key); + } + } + + await manager.train(); + + // let actual = await manager.process("en", "base64 decode bilibili"); + // console.log(actual); + // let actualZH = await manager.process("zh", "去除百分号"); + // console.log(actualZH); + + const resultModel = manager.toJSON(); + + const buf = fflate.strToU8(JSON.stringify(resultModel)); + + const gzipped = fflate.gzipSync(buf, { + filename: 'model.json', + mtime: new Date().getTime() + }); + + fs.writeFileSync("./public/model", Buffer.from(gzipped)); +} + +trainIntentionModel(); diff --git a/lib/version.ts b/lib/version.ts index c478eac..e394c32 100644 --- a/lib/version.ts +++ b/lib/version.ts @@ -1,3 +1,3 @@ -export const SPARKHOME_VERSION="4.14.3"; -export const CLIENT_VERSION="4.14.2"; +export const SPARKHOME_VERSION="4.17.0"; +export const CLIENT_VERSION="4.17.0"; export const NEXT_API_VERSION="4.14.3"; \ No newline at end of file diff --git a/lib/weather/getCurrentWeather.ts b/lib/weather/getCurrentWeather.ts new file mode 100644 index 0000000..b762862 --- /dev/null +++ b/lib/weather/getCurrentWeather.ts @@ -0,0 +1,39 @@ +export function getClosestHourTimestamp(): string { + const now = new Date(); + now.setMinutes(0, 0, 0); // 设置分钟、秒和毫秒为0 + + // 获取本地时间的年份、月份、日期、小时 + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始 + const day = String(now.getDate()).padStart(2, '0'); + const hour = String(now.getHours()).padStart(2, '0'); + + // 拼接成所需的格式 + const localHourTimestamp = `${year}-${month}-${day}T${hour}:00`; + + return localHourTimestamp; +} + +export function findClosestDateIndex(dates: string[], utc_offset_seconds: number): number { + const now = new Date(); + const nowTimestamp = now.getTime(); + const offsetMilliseconds = utc_offset_seconds * 1000; + + let closestIndex = -1; + let closestDiff = Infinity; + + for (let i = 0; i < dates.length; i++) { + const date = new Date(dates[i]); + const adjustedTimestamp = date.getTime(); + + if (adjustedTimestamp <= nowTimestamp) { + const diff = nowTimestamp - adjustedTimestamp; + if (diff < closestDiff) { + closestDiff = diff; + closestIndex = i; + } + } + } + + return closestIndex; +} \ No newline at end of file diff --git a/lib/weather/getLocation.ts b/lib/weather/getLocation.ts new file mode 100644 index 0000000..93774cc --- /dev/null +++ b/lib/weather/getLocation.ts @@ -0,0 +1,17 @@ +const options = { + enableHighAccuracy: true, + timeout: 10000, + maximumAge: 3600 +}; + +export function getLocationNative(callback: Function) { + navigator.geolocation.getCurrentPosition( + (pos: GeolocationPosition) => { + callback(pos.coords); + }, + (err: GeolocationPositionError) => { + callback(err); + }, + options + ); +} diff --git a/lib/weather/getWeather.ts b/lib/weather/getWeather.ts new file mode 100644 index 0000000..b9cc28c --- /dev/null +++ b/lib/weather/getWeather.ts @@ -0,0 +1,23 @@ +export async function getWeather(lat: number, lon: number) { + const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const cacheKey = `weather-cache-${lat.toFixed(2)}-${lon.toFixed(2)}-${timezone}`; + const localData = localStorage.getItem(cacheKey); + if (localData != null) { + console.log('Using cache'); + const parsedLocalData = JSON.parse(localData); + if (parsedLocalData["hourly"]["time"][0] != undefined && + new Date().getTime() - new Date(parsedLocalData["hourly"]["time"][0]).getTime() < 86400 * 1000 + ) { + return parsedLocalData; + } + else { + console.log('Cache expired'); + localStorage.removeItem(cacheKey); + } + } + const url = `https://api.open-meteo.com/v1/cma?latitude=${lat.toString()}&longitude=${lon.toString()}&hourly=apparent_temperature,precipitation,weather_code&timezone=${encodeURIComponent(timezone)}&forecast_days=1`; + const response = await fetch(url); + const responseJson = await response.json(); + localStorage.setItem(cacheKey, JSON.stringify(responseJson)); + return responseJson; +} \ No newline at end of file diff --git a/lib/weather/wmocode.ts b/lib/weather/wmocode.ts new file mode 100644 index 0000000..d852747 --- /dev/null +++ b/lib/weather/wmocode.ts @@ -0,0 +1,294 @@ +type WeatherInfo = { + description: string; + image: string; +}; + +type WMOCodeTable = { + [key: string]: { + day: WeatherInfo; + night: WeatherInfo; + }; +}; + +export let WMOCodeTable: WMOCodeTable = { + "0": { + day: { + description: "Sunny", + image: "http://openweathermap.org/img/wn/01d@2x.png" + }, + night: { + description: "Clear", + image: "http://openweathermap.org/img/wn/01n@2x.png" + } + }, + "1": { + day: { + description: "Mainly Sunny", + image: "http://openweathermap.org/img/wn/01d@2x.png" + }, + night: { + description: "Mainly Clear", + image: "http://openweathermap.org/img/wn/01n@2x.png" + } + }, + "2": { + day: { + description: "Partly Cloudy", + image: "http://openweathermap.org/img/wn/02d@2x.png" + }, + night: { + description: "Partly Cloudy", + image: "http://openweathermap.org/img/wn/02n@2x.png" + } + }, + "3": { + day: { + description: "Cloudy", + image: "http://openweathermap.org/img/wn/03d@2x.png" + }, + night: { + description: "Cloudy", + image: "http://openweathermap.org/img/wn/03n@2x.png" + } + }, + "45": { + day: { + description: "Foggy", + image: "http://openweathermap.org/img/wn/50d@2x.png" + }, + night: { + description: "Foggy", + image: "http://openweathermap.org/img/wn/50n@2x.png" + } + }, + "48": { + day: { + description: "Rime Fog", + image: "http://openweathermap.org/img/wn/50d@2x.png" + }, + night: { + description: "Rime Fog", + image: "http://openweathermap.org/img/wn/50n@2x.png" + } + }, + "51": { + day: { + description: "Light Drizzle", + image: "http://openweathermap.org/img/wn/09d@2x.png" + }, + night: { + description: "Light Drizzle", + image: "http://openweathermap.org/img/wn/09n@2x.png" + } + }, + "53": { + day: { + description: "Drizzle", + image: "http://openweathermap.org/img/wn/09d@2x.png" + }, + night: { + description: "Drizzle", + image: "http://openweathermap.org/img/wn/09n@2x.png" + } + }, + "55": { + day: { + description: "Heavy Drizzle", + image: "http://openweathermap.org/img/wn/09d@2x.png" + }, + night: { + description: "Heavy Drizzle", + image: "http://openweathermap.org/img/wn/09n@2x.png" + } + }, + "56": { + day: { + description: "Light Freezing Drizzle", + image: "http://openweathermap.org/img/wn/09d@2x.png" + }, + night: { + description: "Light Freezing Drizzle", + image: "http://openweathermap.org/img/wn/09n@2x.png" + } + }, + "57": { + day: { + description: "Freezing Drizzle", + image: "http://openweathermap.org/img/wn/09d@2x.png" + }, + night: { + description: "Freezing Drizzle", + image: "http://openweathermap.org/img/wn/09n@2x.png" + } + }, + "61": { + day: { + description: "Light Rain", + image: "http://openweathermap.org/img/wn/10d@2x.png" + }, + night: { + description: "Light Rain", + image: "http://openweathermap.org/img/wn/10n@2x.png" + } + }, + "63": { + day: { + description: "Rain", + image: "http://openweathermap.org/img/wn/10d@2x.png" + }, + night: { + description: "Rain", + image: "http://openweathermap.org/img/wn/10n@2x.png" + } + }, + "65": { + day: { + description: "Heavy Rain", + image: "http://openweathermap.org/img/wn/10d@2x.png" + }, + night: { + description: "Heavy Rain", + image: "http://openweathermap.org/img/wn/10n@2x.png" + } + }, + "66": { + day: { + description: "Light Freezing Rain", + image: "http://openweathermap.org/img/wn/10d@2x.png" + }, + night: { + description: "Light Freezing Rain", + image: "http://openweathermap.org/img/wn/10n@2x.png" + } + }, + "67": { + day: { + description: "Freezing Rain", + image: "http://openweathermap.org/img/wn/10d@2x.png" + }, + night: { + description: "Freezing Rain", + image: "http://openweathermap.org/img/wn/10n@2x.png" + } + }, + "71": { + day: { + description: "Light Snow", + image: "http://openweathermap.org/img/wn/13d@2x.png" + }, + night: { + description: "Light Snow", + image: "http://openweathermap.org/img/wn/13n@2x.png" + } + }, + "73": { + day: { + description: "Snow", + image: "http://openweathermap.org/img/wn/13d@2x.png" + }, + night: { + description: "Snow", + image: "http://openweathermap.org/img/wn/13n@2x.png" + } + }, + "75": { + day: { + description: "Heavy Snow", + image: "http://openweathermap.org/img/wn/13d@2x.png" + }, + night: { + description: "Heavy Snow", + image: "http://openweathermap.org/img/wn/13n@2x.png" + } + }, + "77": { + day: { + description: "Snow Grains", + image: "http://openweathermap.org/img/wn/13d@2x.png" + }, + night: { + description: "Snow Grains", + image: "http://openweathermap.org/img/wn/13n@2x.png" + } + }, + "80": { + day: { + description: "Light Showers", + image: "http://openweathermap.org/img/wn/09d@2x.png" + }, + night: { + description: "Light Showers", + image: "http://openweathermap.org/img/wn/09n@2x.png" + } + }, + "81": { + day: { + description: "Showers", + image: "http://openweathermap.org/img/wn/09d@2x.png" + }, + night: { + description: "Showers", + image: "http://openweathermap.org/img/wn/09n@2x.png" + } + }, + "82": { + day: { + description: "Heavy Showers", + image: "http://openweathermap.org/img/wn/09d@2x.png" + }, + night: { + description: "Heavy Showers", + image: "http://openweathermap.org/img/wn/09n@2x.png" + } + }, + "85": { + day: { + description: "Light Snow Showers", + image: "http://openweathermap.org/img/wn/13d@2x.png" + }, + night: { + description: "Light Snow Showers", + image: "http://openweathermap.org/img/wn/13n@2x.png" + } + }, + "86": { + day: { + description: "Snow Showers", + image: "http://openweathermap.org/img/wn/13d@2x.png" + }, + night: { + description: "Snow Showers", + image: "http://openweathermap.org/img/wn/13n@2x.png" + } + }, + "95": { + day: { + description: "Thunderstorm", + image: "http://openweathermap.org/img/wn/11d@2x.png" + }, + night: { + description: "Thunderstorm", + image: "http://openweathermap.org/img/wn/11n@2x.png" + } + }, + "96": { + day: { + description: "Light Thunderstorms With Hail", + image: "http://openweathermap.org/img/wn/11d@2x.png" + }, + night: { + description: "Light Thunderstorms With Hail", + image: "http://openweathermap.org/img/wn/11n@2x.png" + } + }, + "99": { + day: { + description: "Thunderstorm With Hail", + image: "http://openweathermap.org/img/wn/11d@2x.png" + }, + night: { + description: "Thunderstorm With Hail", + image: "http://openweathermap.org/img/wn/11n@2x.png" + } + } +}; diff --git a/package.json b/package.json index 48ab966..dbe8f46 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "sparkhome", - "version": "4.10.2", + "version": "4.17.0", "private": false, "scripts": { "dev": "next dev", "build": "next build", + "build:NLP": "tsx ./lib/nlp/train.ts", "start": "next start", "lint": "next lint", "test": "jest", @@ -14,11 +15,19 @@ }, "dependencies": { "@nextui-org/react": "^2.3.6", + "@nlpjs/basic": "^4.27.0", + "@nlpjs/builtin-compromise": "^4.26.1", + "@nlpjs/core": "^4.26.1", + "@nlpjs/lang-en-min": "^4.26.1", + "@nlpjs/lang-zh": "^4.26.1", + "@nlpjs/nlp": "^4.27.0", "clsx": "^2.1.1", + "fflate": "^0.8.2", "framer-motion": "^11.1.7", "next": "14.1.4", "next-intl": "^3.12.0", "next-themes": "^0.3.0", + "openmeteo": "^1.1.4", "pino": "^9.0.0", "punycode": "^2.3.1", "react": "^18.3.0", @@ -26,7 +35,6 @@ "recoil": "^0.7.7", "search-engine-autocomplete": "^0.4.3", "tailwind-merge": "^2.3.0", - "ts-node": "^10.9.2", "unicode-encode": "^1.4.2", "valid-url": "^1.0.9", "validate-color": "^2.2.4" @@ -48,6 +56,7 @@ "postcss": "^8.4.38", "tailwindcss": "^3.4.3", "ts-jest": "^29.1.2", + "tsx": "^4.15.6", "typescript": "^5.4.5", "vitepress": "^1.2.3", "vue": "^3.4.29" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d807500..0ddf7e0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,9 +11,30 @@ importers: '@nextui-org/react': specifier: ^2.3.6 version: 2.3.6(@types/react@18.3.0)(framer-motion@11.1.7(react-dom@18.3.0(react@18.3.0))(react@18.3.0))(react-dom@18.3.0(react@18.3.0))(react@18.3.0)(tailwind-variants@0.2.1(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.4.5))))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.4.5))) + '@nlpjs/basic': + specifier: ^4.27.0 + version: 4.27.0 + '@nlpjs/builtin-compromise': + specifier: ^4.26.1 + version: 4.26.1 + '@nlpjs/core': + specifier: ^4.26.1 + version: 4.26.1 + '@nlpjs/lang-en-min': + specifier: ^4.26.1 + version: 4.26.1 + '@nlpjs/lang-zh': + specifier: ^4.26.1 + version: 4.26.1 + '@nlpjs/nlp': + specifier: ^4.27.0 + version: 4.27.0 clsx: specifier: ^2.1.1 version: 2.1.1 + fflate: + specifier: ^0.8.2 + version: 0.8.2 framer-motion: specifier: ^11.1.7 version: 11.1.7(react-dom@18.3.0(react@18.3.0))(react@18.3.0) @@ -26,6 +47,9 @@ importers: next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.0(react@18.3.0))(react@18.3.0) + openmeteo: + specifier: ^1.1.4 + version: 1.1.4 pino: specifier: ^9.0.0 version: 9.0.0 @@ -47,9 +71,6 @@ importers: tailwind-merge: specifier: ^2.3.0 version: 2.3.0 - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@20.12.7)(typescript@5.4.5) unicode-encode: specifier: ^1.4.2 version: 1.4.2 @@ -108,6 +129,9 @@ importers: ts-jest: specifier: ^29.1.2 version: 29.1.2(@babel/core@7.24.4)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.4))(jest@29.7.0(@types/node@20.12.7)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.4.5)))(typescript@5.4.5) + tsx: + specifier: ^4.15.6 + version: 4.15.6 typescript: specifier: ^5.4.5 version: 5.4.5 @@ -1224,6 +1248,69 @@ packages: react: '>=18' react-dom: '>=18' + '@nlpjs/basic@4.27.0': + resolution: {integrity: sha512-AL03Xdf2H1leAkWfjVwjm8SqeEj9M7ZBHoKC16sSd2SGgQN24YwZFpRyySW2vvDfHLHk8LkFu0+Efw9LdADz4A==} + + '@nlpjs/builtin-compromise@4.26.1': + resolution: {integrity: sha512-wc4tN0NIyfjMyRt1qpU+A4i6hIdtg92ZlfIgzx1q67sZJ+jMp0EQU2slokqFSRK1m4+WT9jAsbd/nrUan3oWlA==} + + '@nlpjs/connector@4.26.1': + resolution: {integrity: sha512-VCNoZpuKXoaQPT9djrEnpGDRFCYs8LBCeV/f4u0waaKM2VVp6OdLDCYV9RDQ7MK+VOsZhvDfPa2kuoixRE4srA==} + + '@nlpjs/console-connector@4.26.1': + resolution: {integrity: sha512-gktzTvDVAUzq5N/JTBZ5gMa8DhG4lV9rXOONIuuivo4Ze8jP/JdcenK1WQoisV/XnI7uMjnYhVnSB4wY7IizSA==} + + '@nlpjs/core-loader@4.26.1': + resolution: {integrity: sha512-IiRtn65bdiUSQHy2kusco2fmhk39u2Mc2c5Fsm9+9EVG6BtJCmVEFU/btAzGDAmxEA/E4qKecaAT4LvcW6TPbA==} + + '@nlpjs/core@4.26.1': + resolution: {integrity: sha512-M/PeFddsi3y7Z1piFJxsLGm5/xdMhcrpOsml7s6CTEgYo8iduaT30HDd61tZxDyvvJseU6uFqlXSn7XKkAcC1g==} + + '@nlpjs/evaluator@4.26.1': + resolution: {integrity: sha512-WeUrC8qq7+V8Jhkkjc2yiXdzy9V0wbETv8/qasQmL0QmEuwBDJF+fvfl4z2vWpBb0vW07A8aNrFElKELzbpkdg==} + + '@nlpjs/lang-en-min@4.26.1': + resolution: {integrity: sha512-1sJZ7dy7ysqzbsB8IklguvB88J8EPIv4XGVkZCcwecKtOw+fp5LAsZ3TJVmEf18iK1gD4cEGr7qZg5fpPxTpWQ==} + + '@nlpjs/lang-en@4.26.1': + resolution: {integrity: sha512-GVoJpOjyk5TtBAqo/fxsiuuH7jXycyakGT0gw5f01u9lOmUnpJegvXyGff/Nb0j14pXcGHXOhmpWrcTrG2B0LQ==} + + '@nlpjs/lang-zh@4.26.1': + resolution: {integrity: sha512-kwqeqeEgMAMvucVX9HNE1p6s/2APP23ZsS8Um/lNvtswb4gL5jjYF9kyCvRfqlPBQSWWdRv7wwcnNXOvXYkxcQ==} + + '@nlpjs/language-min@4.25.0': + resolution: {integrity: sha512-g8jtbDbqtRm+dlD/1Vnb4VWfKbKteApEGVTqIMxYkk6N/HMhvLZ5J2svrxzrB98a/HZ0fb//YBfFgymnz9Oukg==} + + '@nlpjs/logger@4.26.1': + resolution: {integrity: sha512-1WaXq+lt1vm34TPxoRZQ8c6PA7cqou/V18lGDt7uZbK0L5cXsB8OuYm5lEB/J1QlbJ819Nkmb32Oax9idQODRw==} + + '@nlpjs/ner@4.27.0': + resolution: {integrity: sha512-ptwkxriJdmgHSH9TfP10JQ1jviaSl2SupSFGUvTuWkuJhobQd3hbnlSq40V6XYvJNmqh9M9zEab/AKeghxYOTA==} + + '@nlpjs/neural@4.25.0': + resolution: {integrity: sha512-Oz20denGiBe0DlQsS7lN4TNrATN1nXlHKc/HB6jJPegjVmgJVCugDaHwIGoV7qOWyA6F2fRRwOgD+quNT2gVpg==} + + '@nlpjs/nlg@4.26.1': + resolution: {integrity: sha512-PCJWiZ7464ChXXUGvjBZIFtoqkC24Oy6X63HgQrSv+63svz22Y5Cmu1MYLk77Nb+4keWv+hKhFJKDkvJoOpBVg==} + + '@nlpjs/nlp@4.27.0': + resolution: {integrity: sha512-q6X7sY6TYVnQRZJKF/6mfLFlNA5oRYLhgQ5k3i1IBqH9lbWTAZJr31w/dCf97HXaYaj+vJp3h0ucfNumme9EIw==} + + '@nlpjs/nlu@4.27.0': + resolution: {integrity: sha512-j4DUdoXS/y/Xag6ysYXx7Ve8NBmUVViUSCJhj3r49+zGyYtyVAHuVcqSej5q0tJjn0JSMT+6+ip8klON1q8ixw==} + + '@nlpjs/request@4.25.0': + resolution: {integrity: sha512-MPVYWfFZY03WyFL7GWkUkv8tw968OXsdxFSJEvjXHzhiCe/vAlPCWbvoR+VnoQTgzLHxs/KIF6sIF2s9AzsLmQ==} + + '@nlpjs/sentiment@4.26.1': + resolution: {integrity: sha512-U2WmcW3w6yDDO45+Y7v5e6DPQj8e0x+RUUePPyRu2uIZmUtIKG+qCPMWnNLMmYQZoSQEFxmMMlLcGDC7tN7o3w==} + + '@nlpjs/similarity@4.26.1': + resolution: {integrity: sha512-QutSBFGo/huNuz60PgqCjub0oBd9S8MLrjme33U5GzxuSvToQzXtn9/ynIia8qDm009D09VXV+LPeNE4h7yuSg==} + + '@nlpjs/slot@4.26.1': + resolution: {integrity: sha512-mK8EEy5O+mRGne822PIKMxHSFh8j+iC7hGJ6T31XdFsNhFEYXLI/0dmeBstZgTSKBTe27HNFgCCwuGb77u0o9w==} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1236,6 +1323,10 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@openmeteo/sdk@1.11.7': + resolution: {integrity: sha512-qV790gksvJ+l/umb1iKt+ZRUKE5RzgmPkwTeUSmtUcnoRaAQZX9/BQLDpmEZrkcuv4g1trzcsNRwxBrBLWUnWA==} + engines: {node: '>=12.0'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -2051,6 +2142,10 @@ packages: argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + args@5.0.3: + resolution: {integrity: sha512-h6k/zfFgusnv3i5TU08KQkVKuCPBtL/PWQbWkHUxvJrZ2nAyeaUupneemcrgn1xmqxPQsPIzwkUhOpoqPDRZuA==} + engines: {node: '>= 6.0.0'} + aria-query@5.1.3: resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} @@ -2171,6 +2266,10 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} + camelcase@5.0.0: + resolution: {integrity: sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==} + engines: {node: '>=6'} + camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} @@ -2254,6 +2353,9 @@ packages: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -2262,6 +2364,21 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + compromise-dates@1.5.6: + resolution: {integrity: sha512-i51/snFZtaW+cLx16HrBMHdgqc9eB3lsT5igtm9pOw6sNsNMX64lKiJdp3/kxGtOnl0fjGbZ69DoHb6o4pGUJw==} + peerDependencies: + compromise: '>=12.0.0' + compromise-numbers: '>=1.0.0' + + compromise-numbers@1.4.0: + resolution: {integrity: sha512-3ceRpwZIWduVSMYn54ET1ELdI7bvXQk42uDwxffxiJBxgKCwcCfVbiLuTG62cI+qTHchwLDh4vp9i3WARXROFQ==} + peerDependencies: + compromise: '>=12.0.0' + + compromise@13.11.4: + resolution: {integrity: sha512-nBITcNdqIHSVDDluaG6guyFFCSNXN+Hu87fU8VlhkE5Z0PwTZN1nro2O7a8JcUH88nB5EOzrxd9zKfXLSNFqcg==} + engines: {node: '>=8.0.0'} + compute-scroll-into-view@3.1.0: resolution: {integrity: sha512-rj8l8pD4bJ1nx+dAkMhV1xB5RuZEyVysfxJqB1pRchh1KVvwOv9b7CGB8ZfjTImVv2oF+sYMUkMZq6Na5Ftmbg==} @@ -2312,6 +2429,9 @@ packages: resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} engines: {node: '>=12'} + dateformat@4.6.3: + resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} + debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -2388,9 +2508,15 @@ packages: engines: {node: '>=12'} deprecated: Use your platform's native DOMException instead + duplexify@4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + efrt-unpack@2.2.0: + resolution: {integrity: sha512-9xUSSj7qcUxz+0r4X3+bwUNttEfGfK5AH+LVa1aTpqdAfrN5VhROYCfcF+up4hp5OL7IUKcZJJrzAGipQRDoiQ==} + electron-to-chromium@1.4.749: resolution: {integrity: sha512-LRMMrM9ITOvue0PoBrvNIraVmuDbJV5QC9ierz/z5VilMdPOVMjOtpICNld3PuXuTZ3CHH/UPxX9gHhAPwi+0Q==} @@ -2404,6 +2530,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2491,12 +2620,18 @@ packages: resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} engines: {node: '>=6'} + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -2509,6 +2644,9 @@ packages: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true + flatbuffers@24.3.25: + resolution: {integrity: sha512-3HDgPbgiwWMI9zVB7VYBHaMrbOO7Gm0v+yD2FV/sCKj+9NDeVL7BOBYUuhWAQGKWOzBo8S9WdMvV0eixO233XQ==} + focus-trap@7.5.4: resolution: {integrity: sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==} @@ -2578,6 +2716,9 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + get-tsconfig@4.7.5: + resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2980,6 +3121,10 @@ packages: resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} hasBin: true + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3013,6 +3158,10 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + leven@2.1.0: + resolution: {integrity: sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==} + engines: {node: '>=0.10.0'} + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -3135,6 +3284,10 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + mri@1.1.4: + resolution: {integrity: sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w==} + engines: {node: '>=4'} + ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -3224,6 +3377,9 @@ packages: resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} + on-exit-leak-free@0.2.0: + resolution: {integrity: sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==} + on-exit-leak-free@2.1.2: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} @@ -3235,6 +3391,10 @@ packages: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} + openmeteo@1.1.4: + resolution: {integrity: sha512-TalTDl0M7JJoeRTf+rWiFZ9SLvoxm7KkFLOQqcSjCiYs+bVMhax1qtryJqeZ1RF4W4Xfsgcl9x+VC1z39ULCxA==} + engines: {node: '>=12.0'} + p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -3291,12 +3451,26 @@ packages: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} + pino-abstract-transport@0.5.0: + resolution: {integrity: sha512-+KAgmVeqXYbTtU2FScx1XS3kNyfZ5TrXY07V96QnUSFqo2gAqlvmaxH67Lj7SWazqsMabf+58ctdTcBgnOLUOQ==} + pino-abstract-transport@1.2.0: resolution: {integrity: sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==} + pino-pretty@7.6.1: + resolution: {integrity: sha512-H7N6ZYkiyrfwBGW9CSjx0uyO9Q2Lyt73881+OTYk8v3TiTdgN92QHrWlEq/LeWw5XtDP64jeSk3mnc6T+xX9/w==} + hasBin: true + + pino-std-serializers@4.0.0: + resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==} + pino-std-serializers@6.2.2: resolution: {integrity: sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==} + pino@7.11.0: + resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==} + hasBin: true + pino@9.0.0: resolution: {integrity: sha512-uI1ThkzTShNSwvsUM6b4ND8ANzWURk9zTELMztFkmnCQeR/4wkomJ+echHee5GMWGovoSfjwdeu80DsFIt7mbA==} hasBin: true @@ -3369,6 +3543,9 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + process-warning@1.0.0: + resolution: {integrity: sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==} + process-warning@3.0.0: resolution: {integrity: sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==} @@ -3383,6 +3560,9 @@ packages: psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3453,6 +3633,10 @@ packages: read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + readable-stream@4.5.2: resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3461,6 +3645,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + real-require@0.1.0: + resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==} + engines: {node: '>= 12.13.0'} + real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -3503,6 +3691,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve.exports@2.0.2: resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} engines: {node: '>=10'} @@ -3552,6 +3743,9 @@ packages: search-insights@2.14.0: resolution: {integrity: sha512-OLN6MsPMCghDOqlCtsIsYgtsC0pnwVTyT9Mu6A3ewOj1DxvzZF6COrn2g86E/c05xbktB0XN04m/t1Z+n+fTGw==} + secure-json-parse@2.7.0: + resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -3601,6 +3795,9 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + sonic-boom@2.8.0: + resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} + sonic-boom@3.8.1: resolution: {integrity: sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==} @@ -3615,6 +3812,14 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + spacetime-holiday@0.1.0: + resolution: {integrity: sha512-rYIpSDbHnznZRstUrmYYFAaruW8e96t+1JfS0b6qMiAAQ2DrkLKc8oMotAAkB9qMTUwXXf5bIkdTHfP434uitQ==} + peerDependencies: + spacetime: ^6.3.0 + + spacetime@6.14.0: + resolution: {integrity: sha512-pz/nMIRGNSJeFfDFvhPjMHXhFU1NcrYnpydMuSS2Zsk0NEoHJc2rRKXugkmlqUv/l/fPxWVJVnj8isVS0//vbQ==} + speakingurl@14.0.1: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} @@ -3634,6 +3839,9 @@ packages: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} + stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -3755,6 +3963,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + thread-stream@0.15.2: + resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} + thread-stream@2.7.0: resolution: {integrity: sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==} @@ -3818,6 +4029,11 @@ packages: tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + tsx@4.15.6: + resolution: {integrity: sha512-is0VQQlfNZRHEuSSTKA6m4xw74IU4AizmuB6lAYLRt9XtuyeQnyJYexhNZOPCB59SqC4JzmSzPnHGBXxf3k0hA==} + engines: {node: '>=18.0.0'} + hasBin: true + type-detect@4.0.8: resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} engines: {node: '>=4'} @@ -4403,6 +4619,7 @@ snapshots: '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 + optional: true '@docsearch/css@3.6.0': {} @@ -4778,6 +4995,7 @@ snapshots: dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 + optional: true '@next/env@14.1.4': {} @@ -5801,6 +6019,112 @@ snapshots: react: 18.3.0 react-dom: 18.3.0(react@18.3.0) + '@nlpjs/basic@4.27.0': + dependencies: + '@nlpjs/console-connector': 4.26.1 + '@nlpjs/core-loader': 4.26.1 + '@nlpjs/evaluator': 4.26.1 + '@nlpjs/lang-en': 4.26.1 + '@nlpjs/logger': 4.26.1 + '@nlpjs/nlp': 4.27.0 + transitivePeerDependencies: + - supports-color + + '@nlpjs/builtin-compromise@4.26.1': + dependencies: + '@nlpjs/core': 4.26.1 + compromise: 13.11.4 + compromise-dates: 1.5.6(compromise-numbers@1.4.0(compromise@13.11.4))(compromise@13.11.4) + compromise-numbers: 1.4.0(compromise@13.11.4) + + '@nlpjs/connector@4.26.1': + dependencies: + '@nlpjs/core': 4.26.1 + + '@nlpjs/console-connector@4.26.1': + dependencies: + '@nlpjs/connector': 4.26.1 + '@nlpjs/core': 4.26.1 + + '@nlpjs/core-loader@4.26.1': + dependencies: + '@nlpjs/core': 4.26.1 + '@nlpjs/request': 4.25.0 + transitivePeerDependencies: + - supports-color + + '@nlpjs/core@4.26.1': {} + + '@nlpjs/evaluator@4.26.1': + dependencies: + escodegen: 2.1.0 + esprima: 4.0.1 + + '@nlpjs/lang-en-min@4.26.1': + dependencies: + '@nlpjs/core': 4.26.1 + + '@nlpjs/lang-en@4.26.1': + dependencies: + '@nlpjs/core': 4.26.1 + '@nlpjs/lang-en-min': 4.26.1 + + '@nlpjs/lang-zh@4.26.1': + dependencies: + '@nlpjs/core': 4.26.1 + + '@nlpjs/language-min@4.25.0': {} + + '@nlpjs/logger@4.26.1': + dependencies: + pino: 7.11.0 + pino-pretty: 7.6.1 + + '@nlpjs/ner@4.27.0': + dependencies: + '@nlpjs/core': 4.26.1 + '@nlpjs/language-min': 4.25.0 + '@nlpjs/similarity': 4.26.1 + + '@nlpjs/neural@4.25.0': {} + + '@nlpjs/nlg@4.26.1': + dependencies: + '@nlpjs/core': 4.26.1 + + '@nlpjs/nlp@4.27.0': + dependencies: + '@nlpjs/core': 4.26.1 + '@nlpjs/ner': 4.27.0 + '@nlpjs/nlg': 4.26.1 + '@nlpjs/nlu': 4.27.0 + '@nlpjs/sentiment': 4.26.1 + '@nlpjs/slot': 4.26.1 + + '@nlpjs/nlu@4.27.0': + dependencies: + '@nlpjs/core': 4.26.1 + '@nlpjs/language-min': 4.25.0 + '@nlpjs/neural': 4.25.0 + '@nlpjs/similarity': 4.26.1 + + '@nlpjs/request@4.25.0': + dependencies: + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + transitivePeerDependencies: + - supports-color + + '@nlpjs/sentiment@4.26.1': + dependencies: + '@nlpjs/core': 4.26.1 + '@nlpjs/language-min': 4.25.0 + '@nlpjs/neural': 4.25.0 + + '@nlpjs/similarity@4.26.1': {} + + '@nlpjs/slot@4.26.1': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -5813,6 +6137,10 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 + '@openmeteo/sdk@1.11.7': + dependencies: + flatbuffers: 24.3.25 + '@pkgjs/parseargs@0.11.0': optional: true @@ -6639,13 +6967,17 @@ snapshots: '@tootallnate/once@2.0.0': {} - '@tsconfig/node10@1.0.11': {} + '@tsconfig/node10@1.0.11': + optional: true - '@tsconfig/node12@1.0.11': {} + '@tsconfig/node12@1.0.11': + optional: true - '@tsconfig/node14@1.0.3': {} + '@tsconfig/node14@1.0.3': + optional: true - '@tsconfig/node16@1.0.4': {} + '@tsconfig/node16@1.0.4': + optional: true '@types/aria-query@5.0.4': {} @@ -6919,7 +7251,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - arg@4.1.3: {} + arg@4.1.3: + optional: true arg@5.0.2: {} @@ -6927,6 +7260,13 @@ snapshots: dependencies: sprintf-js: 1.0.3 + args@5.0.3: + dependencies: + camelcase: 5.0.0 + chalk: 2.4.2 + leven: 2.1.0 + mri: 1.1.4 + aria-query@5.1.3: dependencies: deep-equal: 2.2.3 @@ -7073,6 +7413,8 @@ snapshots: camelcase-css@2.0.1: {} + camelcase@5.0.0: {} + camelcase@5.3.1: {} camelcase@6.3.0: {} @@ -7153,12 +7495,29 @@ snapshots: color-convert: 2.0.1 color-string: 1.9.1 + colorette@2.0.20: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 commander@4.1.1: {} + compromise-dates@1.5.6(compromise-numbers@1.4.0(compromise@13.11.4))(compromise@13.11.4): + dependencies: + compromise: 13.11.4 + compromise-numbers: 1.4.0(compromise@13.11.4) + spacetime: 6.14.0 + spacetime-holiday: 0.1.0(spacetime@6.14.0) + + compromise-numbers@1.4.0(compromise@13.11.4): + dependencies: + compromise: 13.11.4 + + compromise@13.11.4: + dependencies: + efrt-unpack: 2.2.0 + compute-scroll-into-view@3.1.0: {} concat-map@0.0.1: {} @@ -7184,7 +7543,8 @@ snapshots: - supports-color - ts-node - create-require@1.1.1: {} + create-require@1.1.1: + optional: true cross-spawn@7.0.3: dependencies: @@ -7212,6 +7572,8 @@ snapshots: whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 + dateformat@4.6.3: {} + debug@4.3.4: dependencies: ms: 2.1.2 @@ -7267,7 +7629,8 @@ snapshots: diff-sequences@29.6.3: {} - diff@4.0.2: {} + diff@4.0.2: + optional: true dlv@1.1.3: {} @@ -7279,8 +7642,17 @@ snapshots: dependencies: webidl-conversions: 7.0.0 + duplexify@4.1.3: + dependencies: + end-of-stream: 1.4.4 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + eastasianwidth@0.2.0: {} + efrt-unpack@2.2.0: {} + electron-to-chromium@1.4.749: {} emittery@0.13.1: {} @@ -7289,6 +7661,10 @@ snapshots: emoji-regex@9.2.2: {} + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + entities@4.5.0: {} error-ex@1.3.2: @@ -7399,6 +7775,8 @@ snapshots: fast-redact@3.5.0: {} + fast-safe-stringify@2.1.1: {} + fastq@1.17.1: dependencies: reusify: 1.0.4 @@ -7407,6 +7785,8 @@ snapshots: dependencies: bser: 2.1.1 + fflate@0.8.2: {} + fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 @@ -7418,6 +7798,8 @@ snapshots: flat@5.0.2: {} + flatbuffers@24.3.25: {} + focus-trap@7.5.4: dependencies: tabbable: 6.2.0 @@ -7473,6 +7855,10 @@ snapshots: get-stream@6.0.1: {} + get-tsconfig@4.7.5: + dependencies: + resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -8069,6 +8455,8 @@ snapshots: jiti@1.21.0: {} + joycon@3.1.1: {} + js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -8117,6 +8505,8 @@ snapshots: kleur@3.0.3: {} + leven@2.1.0: {} + leven@3.1.0: {} lilconfig@2.1.0: {} @@ -8210,6 +8600,8 @@ snapshots: mitt@3.0.1: {} + mri@1.1.4: {} + ms@2.1.2: {} mz@2.7.0: @@ -8296,6 +8688,8 @@ snapshots: has-symbols: 1.0.3 object-keys: 1.1.1 + on-exit-leak-free@0.2.0: {} + on-exit-leak-free@2.1.2: {} once@1.4.0: @@ -8306,6 +8700,11 @@ snapshots: dependencies: mimic-fn: 2.1.0 + openmeteo@1.1.4: + dependencies: + '@openmeteo/sdk': 1.11.7 + flatbuffers: 24.3.25 + p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -8352,13 +8751,50 @@ snapshots: pify@2.3.0: {} + pino-abstract-transport@0.5.0: + dependencies: + duplexify: 4.1.3 + split2: 4.2.0 + pino-abstract-transport@1.2.0: dependencies: readable-stream: 4.5.2 split2: 4.2.0 + pino-pretty@7.6.1: + dependencies: + args: 5.0.3 + colorette: 2.0.20 + dateformat: 4.6.3 + fast-safe-stringify: 2.1.1 + joycon: 3.1.1 + on-exit-leak-free: 0.2.0 + pino-abstract-transport: 0.5.0 + pump: 3.0.0 + readable-stream: 3.6.2 + rfdc: 1.4.1 + secure-json-parse: 2.7.0 + sonic-boom: 2.8.0 + strip-json-comments: 3.1.1 + + pino-std-serializers@4.0.0: {} + pino-std-serializers@6.2.2: {} + pino@7.11.0: + dependencies: + atomic-sleep: 1.0.0 + fast-redact: 3.5.0 + on-exit-leak-free: 0.2.0 + pino-abstract-transport: 0.5.0 + pino-std-serializers: 4.0.0 + process-warning: 1.0.0 + quick-format-unescaped: 4.0.4 + real-require: 0.1.0 + safe-stable-stringify: 2.4.3 + sonic-boom: 2.8.0 + thread-stream: 0.15.2 + pino@9.0.0: dependencies: atomic-sleep: 1.0.0 @@ -8439,6 +8875,8 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.0 + process-warning@1.0.0: {} + process-warning@3.0.0: {} process@0.11.10: {} @@ -8450,6 +8888,11 @@ snapshots: psl@1.9.0: {} + pump@3.0.0: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + punycode@2.3.1: {} pure-rand@6.1.0: {} @@ -8515,6 +8958,12 @@ snapshots: dependencies: pify: 2.3.0 + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + readable-stream@4.5.2: dependencies: abort-controller: 3.0.0 @@ -8527,6 +8976,8 @@ snapshots: dependencies: picomatch: 2.3.1 + real-require@0.1.0: {} + real-require@0.2.0: {} recoil@0.7.7(react-dom@18.3.0(react@18.3.0))(react@18.3.0): @@ -8560,6 +9011,8 @@ snapshots: resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve.exports@2.0.2: {} resolve@1.22.8: @@ -8620,6 +9073,8 @@ snapshots: search-insights@2.14.0: {} + secure-json-parse@2.7.0: {} + semver@6.3.1: {} semver@7.6.0: @@ -8671,6 +9126,10 @@ snapshots: slash@3.0.0: {} + sonic-boom@2.8.0: + dependencies: + atomic-sleep: 1.0.0 + sonic-boom@3.8.1: dependencies: atomic-sleep: 1.0.0 @@ -8684,6 +9143,12 @@ snapshots: source-map@0.6.1: {} + spacetime-holiday@0.1.0(spacetime@6.14.0): + dependencies: + spacetime: 6.14.0 + + spacetime@6.14.0: {} + speakingurl@14.0.1: {} split2@4.2.0: {} @@ -8698,6 +9163,8 @@ snapshots: dependencies: internal-slot: 1.0.7 + stream-shift@1.0.3: {} + streamsearch@1.1.0: {} string-length@4.0.2: @@ -8835,6 +9302,10 @@ snapshots: dependencies: any-promise: 1.3.0 + thread-stream@0.15.2: + dependencies: + real-require: 0.1.0 + thread-stream@2.7.0: dependencies: real-require: 0.2.0 @@ -8894,9 +9365,17 @@ snapshots: typescript: 5.4.5 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + optional: true tslib@2.6.2: {} + tsx@4.15.6: + dependencies: + esbuild: 0.21.5 + get-tsconfig: 4.7.5 + optionalDependencies: + fsevents: 2.3.3 + type-detect@4.0.8: {} type-fest@0.21.3: {} @@ -8963,7 +9442,8 @@ snapshots: util-deprecate@1.0.2: {} - v8-compile-cache-lib@3.0.1: {} + v8-compile-cache-lib@3.0.1: + optional: true v8-to-istanbul@9.2.0: dependencies: @@ -9138,6 +9618,7 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yn@3.1.1: {} + yn@3.1.1: + optional: true yocto-queue@0.1.0: {} diff --git a/public/model b/public/model new file mode 100644 index 0000000000000000000000000000000000000000..5c0b6b7721e0ac443bf35ddf9bfa3a6bbd3a71bf GIT binary patch literal 27776 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 literal 0 HcmV?d00001 diff --git a/test/NLP/removeStopwords.test.ts b/test/NLP/removeStopwords.test.ts new file mode 100644 index 0000000..63334d4 --- /dev/null +++ b/test/NLP/removeStopwords.test.ts @@ -0,0 +1,18 @@ + +import { NLP } from "@/lib/nlp/base"; +import { convertStopwords } from "@/lib/nlp/stopwords"; +import { describe, expect, test } from "@jest/globals"; + +describe("Test 1", () => { + test("basic", () => { + const nlp = new NLP("please", "remove-stopword"); + nlp.removeStopwords(); + expect(nlp.query).toBe(""); + }); + test("convert something", () => { + const nlp = new NLP("please convert 1cm to m", "remove-stopword"); + nlp.removeStopwords(convertStopwords); + nlp.trim(); + expect(nlp.query).toBe("1cm m"); + }); +});