Merge branch 'main' into fix315

This commit is contained in:
0xSage 2023-10-17 11:42:54 +08:00 committed by GitHub
commit 8d004ff0ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 10245 additions and 6406 deletions

View File

@ -32,6 +32,7 @@ jobs:
rm -rf ./* || true
rm -rf ./.??* || true
ls -la ./
rm -rf ~/Library/Application\ Support/jan
- name: Getting the repo
uses: actions/checkout@v3
@ -58,6 +59,7 @@ jobs:
- name: Clean workspace
run: |
Remove-Item -Path .\* -Force -Recurse
Remove-Item -Path "$Env:APPDATA\jan" -Force -Recurse
- name: Getting the repo
uses: actions/checkout@v3
@ -85,6 +87,7 @@ jobs:
rm -rf ./* || true
rm -rf ./.??* || true
ls -la ./
rm -rf ~/.config/jan
- name: Getting the repo
uses: actions/checkout@v3

91
.github/workflows/publish-plugins.yml vendored Normal file
View File

@ -0,0 +1,91 @@
name: Publish plugins/$dir Package to npmjs
on:
push:
branches:
- main
paths:
- "plugins/**"
- ".github/workflows/publish-plugins.yml"
- "!plugins/*/package.json"
jobs:
build:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
with:
fetch-depth: "0"
token: ${{ secrets.PAT_SERVICE_ACCOUNT }}
- name: Install jq
uses: dcarbone/install-jq-action@v2.0.1
- name: Check Path Change
run: |
git config --global user.email "service@jan.ai"
git config --global user.name "Service Account"
echo "Changes in these directories trigger the build:"
changed_dirs=$(git -c http.extraheader="AUTHORIZATION: bearer ${{ secrets.GITHUB_TOKEN }}" diff --name-only HEAD HEAD~1 | grep '^plugins/' | awk -F/ '{print $2}' | uniq)
echo $changed_dirs > /tmp/change_dir.txt
- name: "Auto Increase package Version"
run: |
cd plugins
for dir in $(cat /tmp/change_dir.txt)
do
echo "$dir"
# Extract current version
current_version=$(jq -r '.version' $dir/package.json)
# Break the version into its components
major_version=$(echo $current_version | cut -d "." -f 1)
minor_version=$(echo $current_version | cut -d "." -f 2)
patch_version=$(echo $current_version | cut -d "." -f 3)
# Increment the patch version by one
new_patch_version=$((patch_version+1))
# Construct the new version
new_version="$major_version.$minor_version.$new_patch_version"
# Replace the old version with the new version in package.json
jq --arg version "$new_version" '.version = $version' $dir/package.json > /tmp/package.json && mv /tmp/package.json $dir/package.json
# Print the new version
echo "Updated $dir package.json version to: $new_version"
done
# Setup .npmrc file to publish to npm
- uses: actions/setup-node@v3
with:
node-version: "20.x"
registry-url: "https://registry.npmjs.org"
- name: Publish npm packages
run: |
cd plugins
for dir in $(cat /tmp/change_dir.txt)
do
echo $dir
cd $dir
npm install && npm run build
npm publish --access public
cd ..
done
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: "Commit new version to main and create tag"
run: |
for dir in $(cat /tmp/change_dir.txt)
do
echo "$dir"
version=$(jq -r '.version' plugins/$dir/package.json)
git config --global user.email "service@jan.ai"
git config --global user.name "Service Account"
git add plugins/$dir/package.json
git commit -m "${GITHUB_REPOSITORY}: Update tag build $version for $dir"
git -c http.extraheader="AUTHORIZATION: bearer ${{ secrets.PAT_SERVICE_ACCOUNT }}" push origin HEAD:main
git tag -a $dir-$version -m "${GITHUB_REPOSITORY}: Update tag build $version for $dir"
git -c http.extraheader="AUTHORIZATION: bearer ${{ secrets.PAT_SERVICE_ACCOUNT }}" push origin $dir-$version
done

View File

@ -0,0 +1,54 @@
# ADR #001: Jan deployable cloud-native
## Changelog
- 23.10.03: Initial unfinished draft
- 23.10.16: Remove authentication
## Authors
- @nam-john-ho
- @louis
## Context
### Status Quo
* User doesn't have a local GPU machine but wants to run Jan on a rented server
* User wants a quick, fast way to experiment with Jan on a rented GPU
* https://github.com/janhq/jan/issues/255
## Decision
* This ADR aims to outline design decisions for deploying Jan in cloud native environments such as: Runpod, AWS, Azure, GCP in a fast and simple way.
* The current code-base should not change too much.
* The current plugins must be reusable across enviroments (Desktop, Cloud-native).
### Key Design Decisions
![Key Design](images/adr-001-02.png "Key Design")
#### Why middleware
* The /web codebase needs to operate in both browser and electron environments
* The /web codebase needs to route plugin routes accordingly, either to /server or /electron
* Middleware takes care of this
* We will have a /server codebase that takes care of routing to plugins
#### Unsuitable Alternatives
* Not possible to just run electron headless
* /web is on a different chromium window
* Does not have all the electron handlers
* Does not have the IPC handler
## Alternative Approaches
Separated server process runs along side with electron. https://github.com/janhq/jan/pull/184/commits/6005409a945bb0e80a61132b9eb77f47f19d0aa6
## Considerations
* Due to the limitation of accessing the file system in web browsers, the first version of the web app will load all the current plugins by default, and users will not be able to add, remove, or update plugins.
* Simple authentication will be implemented as a plugin.
## References
- https://www.runpod.io/console/templates
- https://repost.aws/articles/ARQ0Tz9eorSL6EAus7XPMG-Q/how-to-install-textgen-webui-on-aws
- https://www.youtube.com/watch?v=_59AsSyMERQ
- https://gpus.llm-utils.org/running-llama-2-on-runpod-with-oobaboogas-text-generation-webui/
- https://medium.com/@jarimh1984/installing-oobabooga-and-oobabooga-api-to-runpod-cloud-step-by-step-tutorial-47457974dfa5

View File

@ -3,6 +3,9 @@
## Changelog
- {date}: {changelog}
## Authors
- @usernames
## Status
What is the status, such as proposed, accepted, rejected, deprecated, superseded, etc.?
@ -21,4 +24,6 @@ What is the change that we're proposing and/or doing?
What becomes easier or more difficult to do because of this change?
## Alternatives
## Reference

BIN
adr/images/adr-001-01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

BIN
adr/images/adr-001-02.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

View File

@ -240,7 +240,6 @@ const config = {
additionalLanguages: ["python"],
},
colorMode: {
defaultMode: "dark",
disableSwitch: false,
respectPrefersColorScheme: false,
},

View File

@ -47,13 +47,7 @@ export default function HomepageHero() {
Run your own AI
</h1>
<p className="mt-6 text-lg leading-8 text-gray-600 dark:text-gray-300">
Jan lets you run AI on your own hardware. 1-click to install the
latest open-source models. Monitor and manage software-hardware
performance.
<br></br>
Jan is
<strong> free and open core</strong> with a Sustainable Use
License.
Run Large Language Models locally on Mac, Windows or Linux.
</p>
<div className="mt-10 flex items-center justify-center gap-x-6">
{/* TODO: handle mobile model download app instead */}
@ -78,8 +72,7 @@ export default function HomepageHero() {
<img
src={
colorMode === "dark"
? // TODO replace with darkmode image
require("@site/static/img/desktop-llm-chat-dark.png")
? require("@site/static/img/desktop-llm-chat-dark.png")
.default
: require("@site/static/img/desktop-llm-chat-light.png")
.default

View File

@ -19,7 +19,7 @@ export default function Home() {
title={`${siteConfig.tagline}`}
description="Description will go into a meta tag in <head />"
>
<HomepageBanner />
{/* <HomepageBanner /> */}
<main className={styles.main}>
<HomepageHero />
<HomepageSectionOne />

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +0,0 @@
const MODULE_PATH = "inference-plugin/dist/module.js";
const initModel = async (product) =>
new Promise(async (resolve) => {
if (window.electronAPI) {
window.electronAPI
.invokePluginFunc(MODULE_PATH, "initModel", product)
.then((res) => resolve(res));
}
});
const inferenceUrl = () => "http://localhost:3928/llama/chat_completion";
const stopModel = () => {
window.electronAPI.invokePluginFunc(MODULE_PATH, "killSubprocess");
};
// Register all the above functions and objects with the relevant extension points
export function init({ register }) {
register("initModel", "initModel", initModel);
register("inferenceUrl", "inferenceUrl", inferenceUrl);
register("stopModel", "stopModel", stopModel);
}

View File

@ -1,22 +0,0 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
/* Modules */
"module": "ES6" /* Specify what module code is generated. */,
// "rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "." /* Specify the base directory to resolve non-relative module names. */,
// "paths": {} /* Specify a set of entries that re-map imports to additional lookup locations. */,
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "resolveJsonModule": true, /* Enable importing .json files. */
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
/* Type Checking */
"strict": false /* Enable all strict type-checking options. */,
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}

View File

@ -1,7 +0,0 @@
export {};
declare global {
interface Window {
electronAPI?: any | undefined;
}
}

View File

@ -22,8 +22,8 @@
"build:core": "cd plugin-core && yarn install && yarn run build",
"build:web": "yarn workspace jan-web build && cpx \"web/out/**\" \"electron/renderer/\"",
"build:electron": "yarn workspace jan build",
"build:plugins": "rimraf ./electron/core/pre-install/*.tgz && concurrently \"cd ./electron/core/plugins/data-plugin && npm ci\" \"cd ./electron/core/plugins/inference-plugin && npm ci\" \"cd ./electron/core/plugins/model-management-plugin && npm ci\" \"cd ./electron/core/plugins/monitoring-plugin && npm ci\" && concurrently \"cd ./electron/core/plugins/data-plugin && npm run build:publish\" \"cd ./electron/core/plugins/inference-plugin && npm run build:publish\" \"cd ./electron/core/plugins/model-management-plugin && npm run build:publish\" \"cd ./electron/core/plugins/monitoring-plugin && npm run build:publish\"",
"build:plugins-darwin": "rimraf ./electron/core/pre-install/*.tgz && concurrently \"cd ./electron/core/plugins/data-plugin && npm ci\" \"cd ./electron/core/plugins/inference-plugin && npm ci\" \"cd ./electron/core/plugins/model-management-plugin && npm ci\" \"cd ./electron/core/plugins/monitoring-plugin && npm ci\" && chmod +x ./electron/auto-sign.sh && ./electron/auto-sign.sh && concurrently \"cd ./electron/core/plugins/data-plugin && npm run build:publish\" \"cd ./electron/core/plugins/inference-plugin && npm run build:publish\" \"cd ./electron/core/plugins/model-management-plugin && npm run build:publish\" \"cd ./electron/core/plugins/monitoring-plugin && npm run build:publish\"",
"build:plugins": "rimraf ./electron/core/pre-install/*.tgz && concurrently --kill-others-on-fail \"cd ./plugins/data-plugin && npm install && npm run postinstall\" \"cd ./plugins/inference-plugin && npm install && npm run postinstall\" \"cd ./plugins/model-management-plugin && npm install && npm run postinstall\" \"cd ./plugins/monitoring-plugin && npm install && npm run postinstall\" && concurrently --kill-others-on-fail \"cd ./plugins/data-plugin && npm run build:publish\" \"cd ./plugins/inference-plugin && npm run build:publish\" \"cd ./plugins/model-management-plugin && npm run build:publish\" \"cd ./plugins/monitoring-plugin && npm run build:publish\"",
"build:plugins-darwin": "rimraf ./electron/core/pre-install/*.tgz && concurrently \"cd ./plugins/data-plugin && npm install && npm run postinstall\" \"cd ./plugins/inference-plugin && npm install && npm run postinstall\" \"cd ./plugins/model-management-plugin && npm install && npm run postinstall\" \"cd ./plugins/monitoring-plugin && npm install && npm run postinstall\" && chmod +x ./electron/auto-sign.sh && ./electron/auto-sign.sh && concurrently \"cd ./plugins/data-plugin && npm run build:publish\" \"cd ./plugins/inference-plugin && npm run build:publish\" \"cd ./plugins/model-management-plugin && npm run build:publish\" \"cd ./plugins/monitoring-plugin && npm run build:publish\"",
"build": "yarn build:web && yarn build:electron",
"build:darwin": "yarn build:web && yarn workspace jan build:darwin",
"build:win32": "yarn build:web && yarn workspace jan build:win32",

View File

@ -34,20 +34,7 @@ export function init({ register }: { register: RegisterExtensionPoint }) {
}
```
### Access Core API
To access the Core API in your plugin, you can follow the code examples and explanations provided below.
##### Import Core API and Store Module
In your main entry code (e.g., `index.ts`), start by importing the necessary modules and functions from the `@janhq/plugin-core` library.
```js
// index.ts
import { store, core } from "@janhq/plugin-core";
```
#### Interact with Local Data Storage
### Interact with Local Data Storage
The Core API allows you to interact with local data storage. Here are a couple of examples of how you can use it:
@ -56,6 +43,8 @@ The Core API allows you to interact with local data storage. Here are a couple o
You can use the store.insertOne function to insert data into a specific collection in the local data store.
```js
import { store } from "@janhq/plugin-core";
function insertData() {
store.insertOne("conversations", { name: "meow" });
// Insert a new document with { name: "meow" } into the "conversations" collection.
@ -70,6 +59,8 @@ store.getOne(collectionName, key) retrieves a single document that matches the p
store.getMany(collectionName, selector, sort) retrieves multiple documents that match the provided selector in the specified collection.
```js
import { store } from "@janhq/plugin-core";
function getData() {
const selector = { name: "meow" };
const data = store.findMany("conversations", selector);
@ -108,6 +99,96 @@ function deleteData() {
}
```
### Events
You can subscribe to NewMessageRequest events by defining a function to handle the event and registering it with the events object:
```js
import { events } from "@janhq/plugin-core";
function handleMessageRequest(message: NewMessageRequest) {
// Your logic here. For example:
// const response = openai.createChatCompletion({...})
}
function registerListener() {
events.on(EventName.OnNewMessageRequest, handleMessageRequest);
}
// Register the listener function with the relevant extension points.
export function init({ register }) {
registerListener();
}
```
In this example, we're defining a function called handleMessageRequest that takes a NewMessageRequest object as its argument. We're also defining a function called registerListener that registers the handleMessageRequest function as a listener for NewMessageRequest events using the on method of the events object.
```js
import { events } from "@janhq/plugin-core";
function handleMessageRequest(data: NewMessageRequest) {
// Your logic here. For example:
const response = openai.createChatCompletion({...})
const message: NewMessageResponse = {
...data,
message: response.data.choices[0].message.content
}
// Now emit event so the app can display in the conversation
events.emit(EventName.OnNewMessageResponse, message)
}
```
### Preferences
To register plugin preferences, you can use the preferences object from the @janhq/plugin-core package. Here's an example of how to register and retrieve plugin preferences:
```js
import { PluginService, preferences } from "@janhq/plugin-core";
const pluginName = "your-first-plugin";
const preferenceKey = "";
const preferenceName = "Your First Preference";
const preferenceDescription = "This is for example only";
const defaultValue = "";
export function init({ register }: { register: RegisterExtensionPoint }) {
// Register preference update handlers. E.g. update plugin instance with new configuration
register(PluginService.OnPreferencesUpdate, pluginName, onPreferencesUpdate);
// Register plugin preferences. E.g. Plugin need apiKey to connect to your service
preferences.registerPreferences <
string >
(register, pluginName, preferenceKey, preferenceName, preferenceDescription, defaultValue);
}
```
In this example, we're registering preference update handlers and plugin preferences using the preferences object. We're also defining a PluginName constant to use as the name of the plugin.
To retrieve the values of the registered preferences, we're using the get method of the preferences object and passing in the name of the plugin and the name of the preference.
```js
import { preferences } from "@janhq/plugin-core";
const pluginName = "your-first-plugin";
const preferenceKey = "apiKey";
const setup = async () => {
// Retrieve apiKey
const apiKey: string = (await preferences.get(pluginName, preferenceKey)) ?? "";
};
```
### Access Core API
To access the Core API in your plugin, you can follow the code examples and explanations provided below.
##### Import Core API and Store Module
In your main entry code (e.g., `index.ts`), start by importing the necessary modules and functions from the `@janhq/plugin-core` library.
```js
// index.ts
import { core } from "@janhq/plugin-core";
```
#### Perform File Operations
The Core API also provides functions to perform file operations. Here are a couple of examples:
@ -132,7 +213,7 @@ function deleteModel(filePath: string) {
}
```
### Execute plugin module in main process
#### Execute plugin module in main process
To execute a plugin module in the main process of your application, you can follow the steps outlined below.
@ -180,8 +261,8 @@ function getConvMessages(id: number) {
}
module.exports = {
getConvMessages
}
getConvMessages,
};
```
## CoreService API
@ -224,7 +305,6 @@ The `DataService` enum represents methods related to managing conversations and
The `InferenceService` enum exports:
- `InferenceUrl`: The URL for the inference server.
- `InitModel`: Initializes a model for inference.
- `StopModel`: Stops a running inference model.
@ -258,4 +338,11 @@ The `SystemMonitoringService` enum includes methods for monitoring system resour
- `GetResourcesInfo`: Gets information about system resources.
- `GetCurrentLoad`: Gets the current system load.
## PluginService
The `PluginService` enum includes plugin cycle handlers:
- `OnStart`: Handler for starting. E.g. Create a collection.
- `OnPreferencesUpdate`: Handler for preferences update. E.g. Update instances with new configurations.
For more detailed information on each of these components, please refer to the source code.

View File

@ -7,11 +7,7 @@
* @returns Promise<any>
*
*/
const invokePluginFunc: (
plugin: string,
method: string,
...args: any[]
) => Promise<any> = (plugin, method, ...args) =>
const invokePluginFunc: (plugin: string, method: string, ...args: any[]) => Promise<any> = (plugin, method, ...args) =>
window.coreAPI?.invokePluginFunc(plugin, method, ...args) ??
window.electronAPI?.invokePluginFunc(plugin, method, ...args);

71
plugin-core/events.ts Normal file
View File

@ -0,0 +1,71 @@
/**
* The `EventName` enumeration contains the names of all the available events in the Jan platform.
*/
export enum EventName {
OnNewConversation = "onNewConversation",
OnNewMessageRequest = "onNewMessageRequest",
OnNewMessageResponse = "onNewMessageResponse",
OnMessageResponseUpdate = "onMessageResponseUpdate",
}
/**
* The `NewMessageRequest` type defines the shape of a new message request object.
*/
export type NewMessageRequest = {
_id?: string;
conversationId?: string;
user?: string;
avatar?: string;
message?: string;
createdAt?: string;
updatedAt?: string;
};
/**
* The `NewMessageRequest` type defines the shape of a new message request object.
*/
export type NewMessageResponse = {
_id?: string;
conversationId?: string;
user?: string;
avatar?: string;
message?: string;
createdAt?: string;
updatedAt?: string;
};
/**
* Adds an observer for an event.
*
* @param eventName The name of the event to observe.
* @param handler The handler function to call when the event is observed.
*/
const on: (eventName: string, handler: Function) => void = (eventName, handler) => {
window.corePlugin?.events?.on(eventName, handler);
};
/**
* Removes an observer for an event.
*
* @param eventName The name of the event to stop observing.
* @param handler The handler function to call when the event is observed.
*/
const off: (eventName: string, handler: Function) => void = (eventName, handler) => {
window.corePlugin?.events?.off(eventName, handler);
};
/**
* Emits an event.
*
* @param eventName The name of the event to emit.
* @param object The object to pass to the event callback.
*/
const emit: (eventName: string, object: any) => void = (eventName, object) => {
window.corePlugin?.events?.emit(eventName, object);
};
export const events = {
on,
off,
emit,
};

View File

@ -8,7 +8,8 @@ export type CoreService =
| InferenceService
| ModelManagementService
| SystemMonitoringService
| PreferenceService;
| PreferenceService
| PluginService;
/**
* Represents the available methods for the StoreService.
@ -107,11 +108,6 @@ export enum DataService {
* @enum {string}
*/
export enum InferenceService {
/**
* The URL for the inference server.
*/
InferenceUrl = "inferenceUrl",
/**
* Initializes a model for inference.
*/
@ -216,6 +212,32 @@ export enum SystemMonitoringService {
GetCurrentLoad = "getCurrentLoad",
}
/**
* PluginService exports.
* @enum {string}
*/
export enum PluginService {
/**
* The plugin is being started.
*/
OnStart = "pluginOnStart",
/**
* The plugin is being started.
*/
OnPreferencesUpdate = "pluginPreferencesUpdate",
/**
* The plugin is being stopped.
*/
OnStop = "pluginOnStop",
/**
* The plugin is being destroyed.
*/
OnDestroy = "pluginOnDestroy",
}
/**
* Store module exports.
* @module
@ -227,3 +249,15 @@ export { store } from "./store";
* @module
*/
export { core, RegisterExtensionPoint } from "./core";
/**
* Events module exports.
* @module
*/
export { events, EventName, NewMessageRequest, NewMessageResponse } from "./events";
/**
* Preferences module exports.
* @module
*/
export { preferences } from "./preferences";

View File

@ -1,15 +1,16 @@
{
"name": "@janhq/plugin-core",
"version": "0.1.0",
"version": "0.1.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@janhq/plugin-core",
"version": "0.1.0",
"version": "0.1.6",
"license": "MIT",
"devDependencies": {
"@types/node": "^12.0.2"
"@types/node": "^12.0.2",
"typescript": "^5.2.2"
}
},
"node_modules/@types/node": {
@ -17,6 +18,19 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==",
"dev": true
},
"node_modules/typescript": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@janhq/plugin-core",
"version": "0.1.6",
"version": "0.1.8",
"description": "Plugin core lib",
"keywords": [
"jan",

View File

@ -0,0 +1,84 @@
import { store } from "./store";
/**
* Returns the value of the specified preference for the specified plugin.
*
* @param pluginName The name of the plugin.
* @param preferenceKey The key of the preference.
* @returns A promise that resolves to the value of the preference.
*/
function get(pluginName: string, preferenceKey: string): Promise<any> {
return store
.createCollection("preferences", {})
.then(() => store.findOne("preferences", `${pluginName}.${preferenceKey}`))
.then((doc) => doc?.value ?? "");
}
/**
* Sets the value of the specified preference for the specified plugin.
*
* @param pluginName The name of the plugin.
* @param preferenceKey The key of the preference.
* @param value The value of the preference.
* @returns A promise that resolves when the preference has been set.
*/
function set(pluginName: string, preferenceKey: string, value: any): Promise<any> {
return store
.createCollection("preferences", {})
.then(() =>
store
.findOne("preferences", `${pluginName}.${preferenceKey}`)
.then((doc) =>
doc
? store.updateOne("preferences", `${pluginName}.${preferenceKey}`, { value })
: store.insertOne("preferences", { _id: `${pluginName}.${preferenceKey}`, value })
)
);
}
/**
* Clears all preferences for the specified plugin.
*
* @param pluginName The name of the plugin.
* @returns A promise that resolves when the preferences have been cleared.
*/
function clear(pluginName: string): Promise<void> {
return Promise.resolve();
}
/**
* Registers a preference with the specified default value.
*
* @param register The function to use for registering the preference.
* @param pluginName The name of the plugin.
* @param preferenceKey The key of the preference.
* @param preferenceName The name of the preference.
* @param preferenceDescription The description of the preference.
* @param defaultValue The default value of the preference.
*/
function registerPreferences<T>(
register: Function,
pluginName: string,
preferenceKey: string,
preferenceName: string,
preferenceDescription: string,
defaultValue: T
) {
register("PluginPreferences", `${pluginName}.${preferenceKey}`, () => ({
pluginName,
preferenceKey,
preferenceName,
preferenceDescription,
defaultValue,
}));
}
/**
* An object that provides methods for getting, setting, and clearing preferences.
*/
export const preferences = {
get,
set,
clear,
registerPreferences,
};

View File

@ -3,6 +3,7 @@ export {};
declare global {
interface CorePlugin {
store?: any | undefined;
events?: any | undefined;
}
interface Window {
corePlugin?: CorePlugin;

View File

@ -1,7 +1,7 @@
import { core, store, RegisterExtensionPoint, StoreService, DataService } from "@janhq/plugin-core";
import { core, store, RegisterExtensionPoint, StoreService, DataService, PluginService } from "@janhq/plugin-core";
// Provide an async method to manipulate the price provided by the extension point
const MODULE_PATH = "data-plugin/dist/cjs/module.js";
const PluginName = "@janhq/data-plugin";
const MODULE_PATH = "@janhq/data-plugin/dist/cjs/module.js";
/**
* Create a collection on data store
@ -12,7 +12,6 @@ const MODULE_PATH = "data-plugin/dist/cjs/module.js";
*
*/
function createCollection({ name, schema }: { name: string; schema?: { [key: string]: any } }): Promise<void> {
console.log("renderer: creating collection:", name, schema);
return core.invokePluginFunc(MODULE_PATH, "createCollection", name, schema);
}
@ -137,8 +136,7 @@ function onStart() {
// Register all the above functions and objects with the relevant extension points
export function init({ register }: { register: RegisterExtensionPoint }) {
onStart();
register(PluginService.OnStart, PluginName, onStart);
register(StoreService.CreateCollection, createCollection.name, createCollection);
register(StoreService.DeleteCollection, deleteCollection.name, deleteCollection);
register(StoreService.InsertOne, insertOne.name, insertOne);

View File

@ -1,20 +1,20 @@
{
"name": "data-plugin",
"name": "@janhq/data-plugin",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "data-plugin",
"name": "@janhq/data-plugin",
"version": "1.0.0",
"bundleDependencies": [
"rxdb",
"rxjs"
"pouchdb-node",
"pouchdb-find"
],
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@janhq/plugin-core": "file:../../../../plugin-core",
"@janhq/plugin-core": "^0.1.8",
"pouchdb-find": "^8.0.1",
"pouchdb-node": "^8.0.1"
},
@ -29,14 +29,6 @@
"webpack-cli": "^5.1.4"
}
},
"../../../../plugin-core": {
"name": "@janhq/plugin-core",
"version": "0.1.0",
"license": "MIT",
"devDependencies": {
"@types/node": "^12.0.2"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
@ -59,8 +51,9 @@
}
},
"node_modules/@janhq/plugin-core": {
"resolved": "../../../../plugin-core",
"link": true
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/@janhq/plugin-core/-/plugin-core-0.1.8.tgz",
"integrity": "sha512-wJe+8ndMQLGLSV36Jt1V3s5lmH+nQw87ol2NMhHuVWXfwf+DAO1GTdTeHfemHWdcB/oUY1HVRvsNh8DvBgbh/w=="
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3",
@ -177,9 +170,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "20.8.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz",
"integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==",
"version": "20.8.6",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.6.tgz",
"integrity": "sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ==",
"dev": true,
"dependencies": {
"undici-types": "~5.25.1"
@ -397,6 +390,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"inBundle": true,
"dependencies": {
"event-target-shim": "^5.0.0"
},
@ -408,6 +402,7 @@
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz",
"integrity": "sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==",
"inBundle": true,
"dependencies": {
"buffer": "^5.5.0",
"immediate": "^3.2.3",
@ -674,7 +669,8 @@
"type": "consulting",
"url": "https://feross.org/support"
}
]
],
"inBundle": true
},
"node_modules/binary-extensions": {
"version": "1.13.1",
@ -769,6 +765,7 @@
"url": "https://feross.org/support"
}
],
"inBundle": true,
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
@ -777,7 +774,8 @@
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
"inBundle": true
},
"node_modules/cache-base": {
"version": "1.0.1",
@ -809,9 +807,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001547",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001547.tgz",
"integrity": "sha512-W7CrtIModMAxobGhz8iXmDfuJiiKg1WADMO/9x7/CLNin5cpSbuBjooyoIUVB5eyCc36QuTVlkVa1iB2S5+/eA==",
"version": "1.0.30001549",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz",
"integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==",
"dev": true,
"funding": [
{
@ -966,6 +964,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
"integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==",
"inBundle": true,
"engines": {
"node": ">= 0.10"
}
@ -1083,7 +1082,8 @@
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"inBundle": true
},
"node_modules/cpx": {
"version": "1.5.0",
@ -1158,6 +1158,7 @@
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz",
"integrity": "sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==",
"inBundle": true,
"dependencies": {
"abstract-leveldown": "~6.2.1",
"inherits": "^2.0.3"
@ -1218,7 +1219,8 @@
"node_modules/double-ended-queue": {
"version": "2.1.0-0",
"resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
"integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ=="
"integrity": "sha512-+BNfZ+deCo8hMNpDqDnvT+c0XpJ5cUa6mqYq89bho2Ifze4URTqRkcwR399hWoTrTkbZ/XJYDgP6rc7pRgffEQ==",
"inBundle": true
},
"node_modules/duplexer": {
"version": "0.1.2",
@ -1227,15 +1229,16 @@
"dev": true
},
"node_modules/electron-to-chromium": {
"version": "1.4.551",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.551.tgz",
"integrity": "sha512-/Ng/W/kFv7wdEHYzxdK7Cv0BHEGSkSB3M0Ssl8Ndr1eMiYeas/+Mv4cNaDqamqWx6nd2uQZfPz6g25z25M/sdw==",
"version": "1.4.554",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.554.tgz",
"integrity": "sha512-Q0umzPJjfBrrj8unkONTgbKQXzXRrH7sVV7D9ea2yBV3Oaogz991yhbpfvo2LMNkJItmruXTEzVpP9cp7vaIiQ==",
"dev": true
},
"node_modules/encoding-down": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/encoding-down/-/encoding-down-6.3.0.tgz",
"integrity": "sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==",
"inBundle": true,
"dependencies": {
"abstract-leveldown": "^6.2.1",
"inherits": "^2.0.3",
@ -1250,6 +1253,7 @@
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/end-stream/-/end-stream-0.1.0.tgz",
"integrity": "sha512-Brl10T8kYnc75IepKizW6Y9liyW8ikz1B7n/xoHrJxoVSSjoqPn30sb7XVFfQERK4QfUMYRGs9dhWwtt2eu6uA==",
"inBundle": true,
"dependencies": {
"write-stream": "~0.4.3"
}
@ -1283,6 +1287,7 @@
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
"integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
"inBundle": true,
"dependencies": {
"prr": "~1.0.1"
},
@ -1352,6 +1357,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"inBundle": true,
"engines": {
"node": ">=6"
}
@ -1451,6 +1457,7 @@
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz",
"integrity": "sha512-BQm7iZLFhMWFy5CZ/162sAGjBfdNWb7a8LEqqnzsHFhxT/X/SVj/z2t2nu3aJvjlbQkrAlTUApplPRjWyH4mhA==",
"inBundle": true,
"dependencies": {
"tough-cookie": "^2.3.3 || ^3.0.1 || ^4.0.0"
},
@ -1792,7 +1799,8 @@
"type": "consulting",
"url": "https://feross.org/support"
}
]
],
"inBundle": true
},
"node_modules/ignore-walk": {
"version": "3.0.4",
@ -1806,7 +1814,8 @@
"node_modules/immediate": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz",
"integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q=="
"integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==",
"inBundle": true
},
"node_modules/import-local": {
"version": "3.1.0",
@ -1840,7 +1849,8 @@
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"inBundle": true
},
"node_modules/ini": {
"version": "1.3.8",
@ -2078,7 +2088,8 @@
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"inBundle": true
},
"node_modules/isexe": {
"version": "2.0.0",
@ -2155,6 +2166,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/level/-/level-6.0.1.tgz",
"integrity": "sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==",
"inBundle": true,
"dependencies": {
"level-js": "^5.0.0",
"level-packager": "^5.1.0",
@ -2172,6 +2184,7 @@
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/level-codec/-/level-codec-9.0.2.tgz",
"integrity": "sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==",
"inBundle": true,
"dependencies": {
"buffer": "^5.6.0"
},
@ -2183,6 +2196,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz",
"integrity": "sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==",
"inBundle": true,
"engines": {
"node": ">=6"
}
@ -2191,6 +2205,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/level-errors/-/level-errors-2.0.1.tgz",
"integrity": "sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==",
"inBundle": true,
"dependencies": {
"errno": "~0.1.1"
},
@ -2202,6 +2217,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz",
"integrity": "sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==",
"inBundle": true,
"dependencies": {
"inherits": "^2.0.4",
"readable-stream": "^3.4.0",
@ -2215,6 +2231,7 @@
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"inBundle": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@ -2228,6 +2245,7 @@
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/level-js/-/level-js-5.0.2.tgz",
"integrity": "sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==",
"inBundle": true,
"dependencies": {
"abstract-leveldown": "~6.2.3",
"buffer": "^5.5.0",
@ -2239,6 +2257,7 @@
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/level-packager/-/level-packager-5.1.1.tgz",
"integrity": "sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==",
"inBundle": true,
"dependencies": {
"encoding-down": "^6.3.0",
"levelup": "^4.3.2"
@ -2251,6 +2270,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/level-supports/-/level-supports-1.0.1.tgz",
"integrity": "sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==",
"inBundle": true,
"dependencies": {
"xtend": "^4.0.2"
},
@ -2262,6 +2282,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/level-write-stream/-/level-write-stream-1.0.0.tgz",
"integrity": "sha512-bBNKOEOMl8msO+uIM9YX/gUO6ckokZ/4pCwTm/lwvs46x6Xs8Zy0sn3Vh37eDqse4mhy4fOMIb/JsSM2nyQFtw==",
"inBundle": true,
"dependencies": {
"end-stream": "~0.1.0"
}
@ -2271,6 +2292,7 @@
"resolved": "https://registry.npmjs.org/leveldown/-/leveldown-5.6.0.tgz",
"integrity": "sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==",
"hasInstallScript": true,
"inBundle": true,
"dependencies": {
"abstract-leveldown": "~6.2.1",
"napi-macros": "~2.0.0",
@ -2284,6 +2306,7 @@
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/levelup/-/levelup-4.4.0.tgz",
"integrity": "sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==",
"inBundle": true,
"dependencies": {
"deferred-leveldown": "~5.3.0",
"level-errors": "~2.0.0",
@ -2337,7 +2360,8 @@
"node_modules/ltgt": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz",
"integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA=="
"integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==",
"inBundle": true
},
"node_modules/make-error": {
"version": "1.3.6",
@ -2565,7 +2589,8 @@
"node_modules/napi-macros": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz",
"integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg=="
"integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==",
"inBundle": true
},
"node_modules/needle": {
"version": "2.9.1",
@ -2594,6 +2619,7 @@
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"inBundle": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
@ -2613,6 +2639,7 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.1.1.tgz",
"integrity": "sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==",
"inBundle": true,
"bin": {
"node-gyp-build": "bin.js",
"node-gyp-build-optional": "optional.js",
@ -3041,6 +3068,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-abstract-mapreduce/-/pouchdb-abstract-mapreduce-8.0.1.tgz",
"integrity": "sha512-BxJRHdfiC8gID8h4DPS0Xy6wsa2VBHRHMv9hsm0BhGTWTqS4k8ivItVSeU2dMoXiTBYp+7SerYmovUQNGSX1GA==",
"inBundle": true,
"dependencies": {
"pouchdb-binary-utils": "8.0.1",
"pouchdb-collate": "8.0.1",
@ -3056,6 +3084,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-binary-utils/-/pouchdb-binary-utils-8.0.1.tgz",
"integrity": "sha512-WsuR/S0aoUlcA0Alt99czkXsfuXWcrYXAcvGiTW02zawVXOafCnb/qHjA09TUaV0oy5HeHmYaNnDckoOUqspeA==",
"inBundle": true,
"dependencies": {
"buffer-from": "1.1.2"
}
@ -3063,22 +3092,26 @@
"node_modules/pouchdb-collate": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-collate/-/pouchdb-collate-8.0.1.tgz",
"integrity": "sha512-DTuNz1UJjBTGZMUlWS1klSE1rPsmHy8IIDie3MFH1ZTz/C+SwGgGwkiAyUDv/n00D18EMLgXq5mu+r7L6K1BwQ=="
"integrity": "sha512-DTuNz1UJjBTGZMUlWS1klSE1rPsmHy8IIDie3MFH1ZTz/C+SwGgGwkiAyUDv/n00D18EMLgXq5mu+r7L6K1BwQ==",
"inBundle": true
},
"node_modules/pouchdb-collections": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-collections/-/pouchdb-collections-8.0.1.tgz",
"integrity": "sha512-TlkQ2GGHJApJgL0b7bJMQcwX6eMfVenLeoK9mqHfC2fJssui+HWJJ5LYKHOWan11SeB90BQVFbO6rHN6CJQeDg=="
"integrity": "sha512-TlkQ2GGHJApJgL0b7bJMQcwX6eMfVenLeoK9mqHfC2fJssui+HWJJ5LYKHOWan11SeB90BQVFbO6rHN6CJQeDg==",
"inBundle": true
},
"node_modules/pouchdb-errors": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-errors/-/pouchdb-errors-8.0.1.tgz",
"integrity": "sha512-H+ZsQxcG/JV3Tn29gnM6c9+lRPCN91ZYOkoIICsLjVRYgOTzN1AvNUD/G5JCB+81aI/u3fxZec0LEaZh6g6NHA=="
"integrity": "sha512-H+ZsQxcG/JV3Tn29gnM6c9+lRPCN91ZYOkoIICsLjVRYgOTzN1AvNUD/G5JCB+81aI/u3fxZec0LEaZh6g6NHA==",
"inBundle": true
},
"node_modules/pouchdb-fetch": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-fetch/-/pouchdb-fetch-8.0.1.tgz",
"integrity": "sha512-Px5HLT8MxqTujc8bpPRKoouznDTJa9XBGqCbhl95q6rhjWRfwZEvXjV92z0B5BALAM6D6avMyG0DjuNfUWnMuA==",
"inBundle": true,
"dependencies": {
"abort-controller": "3.0.0",
"fetch-cookie": "0.11.0",
@ -3089,6 +3122,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-find/-/pouchdb-find-8.0.1.tgz",
"integrity": "sha512-i5criYXMOXlbeRrCrXonqaOY+xiMiOyTLybqvtX/NkUsiD4BxJxkq5AxdSlHdJ9703nWJ0k6S+5C8VrpEj8tsQ==",
"inBundle": true,
"dependencies": {
"pouchdb-abstract-mapreduce": "8.0.1",
"pouchdb-collate": "8.0.1",
@ -3103,6 +3137,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-mapreduce-utils/-/pouchdb-mapreduce-utils-8.0.1.tgz",
"integrity": "sha512-asZcFLy1DA3oe5CeXIRCpfVrBHaHRvSb3Tc/LPD1dZDDtpEkeCuXGtJm+praN0jl41jTBEm0uMdD/YI0J5ZFXw==",
"inBundle": true,
"dependencies": {
"pouchdb-collections": "8.0.1",
"pouchdb-utils": "8.0.1"
@ -3112,6 +3147,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-md5/-/pouchdb-md5-8.0.1.tgz",
"integrity": "sha512-shVcs/K/iilrcAhDEERpLIrGm/cnDVsXiocOzs7kycJEuBqYnLD9nj58VwWDcum26wfa8T9cznvEGE1jlYVNPQ==",
"inBundle": true,
"dependencies": {
"pouchdb-binary-utils": "8.0.1",
"spark-md5": "3.0.2"
@ -3121,6 +3157,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-node/-/pouchdb-node-8.0.1.tgz",
"integrity": "sha512-xYvVmBB/nEZltBYkslE0RKciL7l359BFbG27gyGJlpPHeMUhtVlYHoEiI2gb2x2pEn7hG9B7Odo8keuq4/ot1w==",
"inBundle": true,
"dependencies": {
"abort-controller": "3.0.0",
"buffer-from": "1.1.2",
@ -3143,12 +3180,14 @@
"node_modules/pouchdb-node/node_modules/isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
"inBundle": true
},
"node_modules/pouchdb-node/node_modules/readable-stream": {
"version": "1.1.14",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==",
"inBundle": true,
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
@ -3159,12 +3198,14 @@
"node_modules/pouchdb-node/node_modules/string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
"inBundle": true
},
"node_modules/pouchdb-selector-core": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-selector-core/-/pouchdb-selector-core-8.0.1.tgz",
"integrity": "sha512-dHWsnR+mLGyfVld1vSHJI1xKTwS1xk1G2dggjfXfUrLehI+wysjTUOwiSNytyPzG6DpT+o86wyUpwzPwsDCLBw==",
"inBundle": true,
"dependencies": {
"pouchdb-collate": "8.0.1",
"pouchdb-utils": "8.0.1"
@ -3174,6 +3215,7 @@
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/pouchdb-utils/-/pouchdb-utils-8.0.1.tgz",
"integrity": "sha512-pWgxdk9EHVWJmjQoEvTe+ZlPXyjcuQ/vgLITN+RjGwcYhoQYUE1M0PksQd2dUP3V8lGS4+wrg9lEM/qSJPYcpw==",
"inBundle": true,
"dependencies": {
"clone-buffer": "1.0.0",
"immediate": "3.3.0",
@ -3195,22 +3237,26 @@
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
"inBundle": true
},
"node_modules/prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw=="
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
"inBundle": true
},
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"inBundle": true
},
"node_modules/punycode": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
"inBundle": true,
"engines": {
"node": ">=6"
}
@ -3218,7 +3264,8 @@
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
"inBundle": true
},
"node_modules/randomatic": {
"version": "3.1.1",
@ -3280,6 +3327,7 @@
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
"inBundle": true,
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
@ -3293,7 +3341,8 @@
"node_modules/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"inBundle": true
},
"node_modules/readdirp": {
"version": "2.2.1",
@ -3694,7 +3743,8 @@
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"inBundle": true
},
"node_modules/resolve": {
"version": "1.22.8",
@ -4145,7 +4195,8 @@
"node_modules/spark-md5": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz",
"integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw=="
"integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==",
"inBundle": true
},
"node_modules/split-string": {
"version": "3.1.0",
@ -4235,6 +4286,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"inBundle": true,
"dependencies": {
"safe-buffer": "~5.1.0"
}
@ -4242,7 +4294,8 @@
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"inBundle": true
},
"node_modules/string-width": {
"version": "1.0.2",
@ -4405,6 +4458,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.2.tgz",
"integrity": "sha512-enaDQ4MUyP2W6ZyT6EsMzqBPZaM/avg8iuo+l2d3QCs0J+6RaqkHV/2/lOwDTueBHeJ/2LG9lrLW3d5rWPucuQ==",
"inBundle": true,
"dependencies": {
"inherits": "^2.0.4",
"readable-stream": "2 || 3"
@ -4466,6 +4520,7 @@
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz",
"integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==",
"inBundle": true,
"dependencies": {
"psl": "^1.1.33",
"punycode": "^2.1.1",
@ -4479,7 +4534,8 @@
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"inBundle": true
},
"node_modules/ts-loader": {
"version": "9.5.0",
@ -4655,6 +4711,7 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
"inBundle": true,
"engines": {
"node": ">= 4.0.0"
}
@ -4766,6 +4823,7 @@
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"inBundle": true,
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
@ -4783,12 +4841,14 @@
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"inBundle": true
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"inBundle": true,
"bin": {
"uuid": "dist/bin/uuid"
}
@ -4802,7 +4862,8 @@
"node_modules/vuvuzela": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/vuvuzela/-/vuvuzela-1.0.3.tgz",
"integrity": "sha512-Tm7jR1xTzBbPW+6y1tknKiEhz04Wf/1iZkcTJjSFcpNko43+dFW6+OOeQe9taJIug3NdfUAjFKgUSyQrIKaDvQ=="
"integrity": "sha512-Tm7jR1xTzBbPW+6y1tknKiEhz04Wf/1iZkcTJjSFcpNko43+dFW6+OOeQe9taJIug3NdfUAjFKgUSyQrIKaDvQ==",
"inBundle": true
},
"node_modules/watchpack": {
"version": "2.4.0",
@ -4820,12 +4881,13 @@
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"inBundle": true
},
"node_modules/webpack": {
"version": "5.88.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz",
"integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==",
"version": "5.89.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
"dev": true,
"dependencies": {
"@types/eslint-scope": "^3.7.3",
@ -4949,6 +5011,7 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"inBundle": true,
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
@ -4994,6 +5057,7 @@
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/write-stream/-/write-stream-0.4.3.tgz",
"integrity": "sha512-IJrvkhbAnj89W/GAVdVgbnPiVw5Ntg/B4tc/MUCIEwj/g6JIww1DWJyB/yBMT3yw2/TkT6IUZ0+IYef3flEw8A==",
"inBundle": true,
"dependencies": {
"readable-stream": "~0.0.2"
}
@ -5001,12 +5065,14 @@
"node_modules/write-stream/node_modules/readable-stream": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-0.0.4.tgz",
"integrity": "sha512-azrivNydKRYt7zwLV5wWUK7YzKTWs3q87xSmY6DlHapPrCvaT6ZrukvM5erV+yCSSPmZT8zkSdttOHQpWWm9zw=="
"integrity": "sha512-azrivNydKRYt7zwLV5wWUK7YzKTWs3q87xSmY6DlHapPrCvaT6ZrukvM5erV+yCSSPmZT8zkSdttOHQpWWm9zw==",
"inBundle": true
},
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"inBundle": true,
"engines": {
"node": ">=0.4"
}

View File

@ -1,6 +1,6 @@
{
"name": "data-plugin",
"version": "1.0.4",
"name": "@janhq/data-plugin",
"version": "1.0.0",
"description": "The Data Connector provides easy access to a data API using the PouchDB engine. It offers accessible data management capabilities.",
"icon": "https://raw.githubusercontent.com/tailwindlabs/heroicons/88e98b0c2b458553fbadccddc2d2f878edc0387b/src/20/solid/circle-stack.svg",
"main": "dist/esm/index.js",
@ -12,7 +12,7 @@
"scripts": {
"build": "tsc --project ./config/tsconfig.esm.json && tsc --project ./config/tsconfig.cjs.json && webpack --config webpack.config.js",
"postinstall": "rimraf ./data-plugin*.tgz && npm run build",
"build:publish": "npm pack && cpx *.tgz ../../pre-install"
"build:publish": "npm pack && cpx *.tgz ../../electron/core/pre-install"
},
"exports": {
"import": "./dist/esm/index.js",
@ -39,7 +39,7 @@
"node_modules"
],
"dependencies": {
"@janhq/plugin-core": "file:../../../../plugin-core",
"@janhq/plugin-core": "^0.1.8",
"pouchdb-find": "^8.0.1",
"pouchdb-node": "^8.0.1"
}

View File

@ -0,0 +1,97 @@
import { EventName, InferenceService, NewMessageRequest, PluginService, core, events, store } from "@janhq/plugin-core";
const PluginName = "@janhq/inference-plugin";
const MODULE_PATH = `${PluginName}/dist/module.js`;
const inferenceUrl = "http://localhost:3928/llama/chat_completion";
const initModel = async (product) => core.invokePluginFunc(MODULE_PATH, "initModel", product);
const stopModel = () => {
core.invokePluginFunc(MODULE_PATH, "killSubprocess");
};
async function handleMessageRequest(data: NewMessageRequest) {
// TODO: Common collections should be able to access via core functions instead of store
const messageHistory =
(await store.findMany("messages", { conversationId: data.conversationId }, [{ createdAt: "asc" }])) ?? [];
const recentMessages = messageHistory
.filter((e) => e.message !== "" && (e.user === "user" || e.user === "assistant"))
.slice(-10)
.map((message) => {
return {
content: message.message.trim(),
role: message.user === "user" ? "user" : "assistant",
};
});
const message = {
...data,
message: "",
user: "assistant",
createdAt: new Date().toISOString(),
_id: undefined,
};
// TODO: Common collections should be able to access via core functions instead of store
const id = await store.insertOne("messages", message);
message._id = id;
events.emit(EventName.OnNewMessageResponse, message);
const response = await fetch(inferenceUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "text/event-stream",
"Access-Control-Allow-Origi": "*",
},
body: JSON.stringify({
messages: recentMessages,
stream: true,
model: "gpt-3.5-turbo",
max_tokens: 500,
}),
});
const stream = response.body;
const decoder = new TextDecoder("utf-8");
const reader = stream?.getReader();
let answer = "";
while (true && reader) {
const { done, value } = await reader.read();
if (done) {
console.log("SSE stream closed");
break;
}
const text = decoder.decode(value);
const lines = text.trim().split("\n");
for (const line of lines) {
if (line.startsWith("data: ") && !line.includes("data: [DONE]")) {
const data = JSON.parse(line.replace("data: ", ""));
answer += data.choices[0]?.delta?.content ?? "";
if (answer.startsWith("assistant: ")) {
answer = answer.replace("assistant: ", "");
}
message.message = answer;
events.emit(EventName.OnMessageResponseUpdate, message);
}
}
}
message.message = answer.trim();
// TODO: Common collections should be able to access via core functions instead of store
await store.updateOne("messages", message._id, message);
}
const registerListener = () => {
events.on(EventName.OnNewMessageRequest, handleMessageRequest);
};
const onStart = async () => {
registerListener();
};
// Register all the above functions and objects with the relevant extension points
export function init({ register }) {
register(PluginService.OnStart, PluginName, onStart);
register(InferenceService.InitModel, initModel.name, initModel);
register(InferenceService.StopModel, stopModel.name, stopModel);
}

View File

@ -1,11 +1,11 @@
{
"name": "inference-plugin",
"name": "@janhq/inference-plugin",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "inference-plugin",
"name": "@janhq/inference-plugin",
"version": "1.0.0",
"bundleDependencies": [
"tcp-port-used",
@ -14,6 +14,7 @@
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
"@janhq/plugin-core": "^0.1.8",
"kill-port-process": "^3.2.0",
"tcp-port-used": "^1.0.2",
"ts-loader": "^9.5.0"
@ -37,6 +38,11 @@
"node": ">=10.0.0"
}
},
"node_modules/@janhq/plugin-core": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/@janhq/plugin-core/-/plugin-core-0.1.8.tgz",
"integrity": "sha512-wJe+8ndMQLGLSV36Jt1V3s5lmH+nQw87ol2NMhHuVWXfwf+DAO1GTdTeHfemHWdcB/oUY1HVRvsNh8DvBgbh/w=="
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",

View File

@ -1,5 +1,5 @@
{
"name": "inference-plugin",
"name": "@janhq/inference-plugin",
"version": "1.0.0",
"description": "Inference Plugin, powered by @janhq/nitro, bring a high-performance Llama model inference in pure C++.",
"icon": "https://raw.githubusercontent.com/tailwindlabs/heroicons/88e98b0c2b458553fbadccddc2d2f878edc0387b/src/20/solid/command-line.svg",
@ -12,7 +12,7 @@
"scripts": {
"build": "tsc -b . && webpack --config webpack.config.js",
"postinstall": "rimraf ./*.tgz && npm run build && rimraf dist/nitro/* && cpx \"nitro/**\" \"dist/nitro\"",
"build:publish": "npm pack && cpx *.tgz ../../pre-install"
"build:publish": "npm pack && cpx *.tgz ../../electron/core/pre-install"
},
"exports": {
".": "./dist/index.js",
@ -25,6 +25,7 @@
"webpack-cli": "^5.1.4"
},
"dependencies": {
"@janhq/plugin-core": "^0.1.8",
"kill-port-process": "^3.2.0",
"tcp-port-used": "^1.0.2",
"ts-loader": "^9.5.0"

View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2016",
"module": "ES6",
"moduleResolution": "node",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": false,
"skipLibCheck": true
}
}

View File

@ -1,5 +1,7 @@
import { ModelManagementService, RegisterExtensionPoint, core, store } from "@janhq/plugin-core";
const MODULE_PATH = "model-management-plugin/dist/module.js";
import { ModelManagementService, PluginService, RegisterExtensionPoint, core, store } from "@janhq/plugin-core";
const PluginName = "@janhq/model-management-plugin";
const MODULE_PATH = "@janhq/model-management-plugin/dist/module.js";
const getDownloadedModels = () => core.invokePluginFunc(MODULE_PATH, "getDownloadedModels");
@ -81,7 +83,7 @@ function onStart() {
// Register all the above functions and objects with the relevant extension points
export function init({ register }: { register: RegisterExtensionPoint }) {
onStart();
register(PluginService.OnStart, PluginName, onStart);
register(ModelManagementService.GetDownloadedModels, getDownloadedModels.name, getDownloadedModels);
register(ModelManagementService.GetAvailableModels, getAvailableModels.name, getAvailableModels);

View File

@ -1,11 +1,11 @@
{
"name": "model-management-plugin",
"name": "@janhq/model-management-plugin",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "model-management-plugin",
"name": "@janhq/model-management-plugin",
"version": "1.0.0",
"bundleDependencies": [
"@huggingface/hub"
@ -14,7 +14,7 @@
"license": "MIT",
"dependencies": {
"@huggingface/hub": "^0.8.5",
"@janhq/plugin-core": "file:../../../../plugin-core",
"@janhq/plugin-core": "^0.1.8",
"ts-loader": "^9.5.0"
},
"devDependencies": {
@ -24,14 +24,6 @@
"webpack-cli": "^5.1.4"
}
},
"../../../../plugin-core": {
"name": "@janhq/plugin-core",
"version": "0.1.0",
"license": "MIT",
"devDependencies": {
"@types/node": "^12.0.2"
}
},
"node_modules/@discoveryjs/json-ext": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
@ -54,8 +46,9 @@
}
},
"node_modules/@janhq/plugin-core": {
"resolved": "../../../../plugin-core",
"link": true
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/@janhq/plugin-core/-/plugin-core-0.1.8.tgz",
"integrity": "sha512-wJe+8ndMQLGLSV36Jt1V3s5lmH+nQw87ol2NMhHuVWXfwf+DAO1GTdTeHfemHWdcB/oUY1HVRvsNh8DvBgbh/w=="
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3",

View File

@ -1,5 +1,5 @@
{
"name": "model-management-plugin",
"name": "@janhq/model-management-plugin",
"version": "1.0.0",
"description": "Model Management Plugin provides model exploration and seamless downloads",
"icon": "https://raw.githubusercontent.com/tailwindlabs/heroicons/88e98b0c2b458553fbadccddc2d2f878edc0387b/src/20/solid/queue-list.svg",
@ -12,7 +12,7 @@
"scripts": {
"build": "tsc -b . && webpack --config webpack.config.js",
"postinstall": "rimraf ./*.tgz && npm run build",
"build:publish": "npm pack && cpx *.tgz ../../pre-install"
"build:publish": "npm pack && cpx *.tgz ../../electron/core/pre-install"
},
"devDependencies": {
"cpx": "^1.5.0",
@ -27,7 +27,7 @@
],
"dependencies": {
"@huggingface/hub": "^0.8.5",
"@janhq/plugin-core": "file:../../../../plugin-core",
"@janhq/plugin-core": "^0.1.8",
"ts-loader": "^9.5.0"
},
"bundledDependencies": [

View File

@ -1,5 +1,5 @@
// Provide an async method to manipulate the price provided by the extension point
const PLUGIN_NAME = "monitoring-plugin/dist/module.js";
const PLUGIN_NAME = "@janhq/monitoring-plugin/dist/module.js";
const getResourcesInfo = () => {
return new Promise((resolve) => {

View File

@ -1,11 +1,11 @@
{
"name": "monitoring-plugin",
"name": "@janhq/monitoring-plugin",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "monitoring-plugin",
"name": "@janhq/monitoring-plugin",
"version": "1.0.0",
"bundleDependencies": [
"systeminformation"

View File

@ -1,5 +1,5 @@
{
"name": "monitoring-plugin",
"name": "@janhq/monitoring-plugin",
"version": "1.0.0",
"description": "Utilizing systeminformation, it provides essential System and OS information retrieval",
"icon": "https://raw.githubusercontent.com/tailwindlabs/heroicons/88e98b0c2b458553fbadccddc2d2f878edc0387b/src/20/solid/cpu-chip.svg",
@ -12,7 +12,7 @@
"scripts": {
"build": "webpack --config webpack.config.js",
"postinstall": "rimraf ./*.tgz && npm run build && cpx \"module.js\" \"dist\"",
"build:publish": "npm pack && cpx *.tgz ../../pre-install"
"build:publish": "npm pack && cpx *.tgz ../../electron/core/pre-install"
},
"devDependencies": {
"rimraf": "^3.0.2",

View File

@ -0,0 +1,115 @@
import {
PluginService,
EventName,
NewMessageRequest,
events,
store,
preferences,
RegisterExtensionPoint,
} from "@janhq/plugin-core";
import { Configuration, OpenAIApi } from "azure-openai";
const PluginName = "@janhq/openai-plugin";
const setRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.setRequestHeader = function newSetRequestHeader(key: string, val: string) {
if (key.toLocaleLowerCase() === "user-agent") {
return;
}
setRequestHeader.apply(this, [key, val]);
};
var openai: OpenAIApi | undefined = undefined;
const setup = async () => {
const apiKey: string = (await preferences.get(PluginName, "apiKey")) ?? "";
const endpoint: string = (await preferences.get(PluginName, "endpoint")) ?? "";
const deploymentName: string = (await preferences.get(PluginName, "deploymentName")) ?? "";
try {
openai = new OpenAIApi(
new Configuration({
azure: {
apiKey, //Your API key goes here
endpoint, //Your endpoint goes here. It is like: "https://endpointname.openai.azure.com/"
deploymentName, //Your deployment name goes here. It is like "chatgpt"
},
})
);
} catch (err) {
openai = undefined;
console.log(err);
}
};
async function onStart() {
setup();
registerListener();
}
async function handleMessageRequest(data: NewMessageRequest) {
if (!openai) {
const message = {
...data,
message: "Your API key is not set. Please set it in the plugin preferences.",
user: "GPT-3",
avatar: "https://static-assets.jan.ai/openai-icon.jpg",
createdAt: new Date().toISOString(),
_id: undefined,
};
const id = await store.insertOne("messages", message);
message._id = id;
events.emit(EventName.OnNewMessageResponse, message);
return;
}
const message = {
...data,
message: "",
user: "GPT-3",
avatar: "https://static-assets.jan.ai/openai-icon.jpg",
createdAt: new Date().toISOString(),
_id: undefined,
};
const id = await store.insertOne("messages", message);
message._id = id;
events.emit(EventName.OnNewMessageResponse, message);
const response = await openai.createChatCompletion({
messages: [{ role: "user", content: data.message }],
model: "gpt-3.5-turbo",
});
message.message = response.data.choices[0].message.content;
events.emit(EventName.OnMessageResponseUpdate, message);
await store.updateOne("messages", message._id, message);
}
const registerListener = () => {
events.on(EventName.OnNewMessageRequest, handleMessageRequest);
};
const onPreferencesUpdate = () => {
setup();
};
// Register all the above functions and objects with the relevant extension points
export function init({ register }: { register: RegisterExtensionPoint }) {
register(PluginService.OnStart, PluginName, onStart);
register(PluginService.OnPreferencesUpdate, PluginName, onPreferencesUpdate);
preferences.registerPreferences<string>(register, PluginName, "apiKey", "API Key", "Azure Project API Key", "");
preferences.registerPreferences<string>(
register,
PluginName,
"endpoint",
"API Endpoint",
"Azure Deployment Endpoint API",
""
);
preferences.registerPreferences<string>(
register,
PluginName,
"deploymentName",
"Deployment Name",
"The deployment name you chose when you deployed the model",
""
);
}

3708
plugins/openai-plugin/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
{
"name": "@janhq/azure-openai-plugin",
"version": "1.0.0",
"description": "Inference plugin for Azure OpenAI",
"icon": "https://static-assets.jan.ai/openai-icon.jpg",
"main": "dist/index.js",
"author": "Jan",
"license": "MIT",
"activationPoints": [
"init"
],
"scripts": {
"build": "tsc -b . && webpack --config webpack.config.js",
"postinstall": "rimraf ./*.tgz && npm run build && rimraf dist/nitro/* && cpx \"nitro/**\" \"dist/nitro\"",
"build:publish": "npm pack && cpx *.tgz ../../electron/core/pre-install"
},
"exports": {
".": "./dist/index.js",
"./main": "./dist/module.js"
},
"devDependencies": {
"cpx": "^1.5.0",
"rimraf": "^3.0.2",
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4"
},
"dependencies": {
"@janhq/plugin-core": "^0.1.8",
"azure-openai": "^0.9.4",
"kill-port-process": "^3.2.0",
"tcp-port-used": "^1.0.2",
"ts-loader": "^9.5.0"
},
"bundledDependencies": [
"tcp-port-used",
"kill-port-process"
],
"engines": {
"node": ">=18.0.0"
},
"files": [
"dist/*",
"package.json",
"README.md"
]
}

View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2016",
"module": "ES6",
"moduleResolution": "node",
"outDir": "./dist",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": false,
"skipLibCheck": true
}
}

View File

@ -0,0 +1,25 @@
const path = require("path");
module.exports = {
experiments: { outputModule: true },
entry: "./index.ts", // Adjust the entry point to match your project's main file
mode: "production",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
],
},
output: {
filename: "index.js", // Adjust the output file name as needed
path: path.resolve(__dirname, "dist"),
library: { type: "module" }, // Specify ESM output format
},
resolve: {
extensions: [".ts", ".js"],
},
// Add loaders and other configuration as needed for your project
};

View File

@ -6,21 +6,22 @@ import {
extensionPoints,
activationPoints,
} from "@/../../electron/core/plugin-manager/execution/index";
import {
ChartPieIcon,
CommandLineIcon,
PlayIcon,
} from "@heroicons/react/24/outline";
import { ChartPieIcon, CommandLineIcon, PlayIcon } from "@heroicons/react/24/outline";
import { MagnifyingGlassIcon } from "@heroicons/react/20/solid";
import classNames from "classnames";
import { PluginService, preferences } from "@janhq/plugin-core";
import { execute } from "../../../electron/core/plugin-manager/execution/extension-manager";
/* eslint-disable @next/next/no-sync-scripts */
export const Preferences = () => {
const [search, setSearch] = useState<string>("");
const [activePlugins, setActivePlugins] = useState<any[]>([]);
const [preferenceItems, setPreferenceItems] = useState<any[]>([]);
const [preferenceValues, setPreferenceValues] = useState<any[]>([]);
const [isTestAvailable, setIsTestAvailable] = useState(false);
const [fileName, setFileName] = useState("");
const experimentRef = useRef(null);
const preferenceRef = useRef(null);
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
@ -31,7 +32,6 @@ export const Preferences = () => {
}
};
const preferenceRef = useRef(null);
useEffect(() => {
async function setupPE() {
// Enable activation point management
@ -54,19 +54,22 @@ export const Preferences = () => {
setTimeout(async () => {
await activationPoints.trigger("init");
if (extensionPoints.get("experimentComponent")) {
const components = await Promise.all(
extensionPoints.execute("experimentComponent")
);
const components = await Promise.all(extensionPoints.execute("experimentComponent"));
if (components.length > 0) {
setIsTestAvailable(true);
}
components.forEach((e) => {
if (preferenceRef.current) {
if (experimentRef.current) {
// @ts-ignore
preferenceRef.current.appendChild(e);
experimentRef.current.appendChild(e);
}
});
}
if (extensionPoints.get("PluginPreferences")) {
const data = await Promise.all(extensionPoints.execute("PluginPreferences"));
setPreferenceItems(Array.isArray(data) ? data : []);
}
}, 500);
};
setupPE().then(() => activePlugins());
@ -86,17 +89,9 @@ export const Preferences = () => {
// Uninstall a plugin on clicking uninstall
const uninstall = async (name: string) => {
//@ts-ignore
// Send the filename of the to be uninstalled plugin
// to the main process for removal
//@ts-ignore
const res = await plugins.uninstall([name]);
console.log(
res
? "Plugin successfully uninstalled"
: "Plugin could not be uninstalled"
);
if (res) window.electronAPI.relaunch();
};
@ -110,6 +105,26 @@ export const Preferences = () => {
// plugins.update(active.map((plg) => plg.name));
};
let timeout: any | undefined = undefined;
function notifyPreferenceUpdate() {
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(() => execute(PluginService.OnPreferencesUpdate), 100);
}
useEffect(() => {
if (preferenceItems) {
Promise.all(
preferenceItems.map((e) =>
preferences.get(e.pluginName, e.preferenceKey).then((k) => ({ key: e.preferenceKey, value: k }))
)
).then((data) => {
setPreferenceValues(data);
});
}
}, [preferenceItems]);
return (
<div className="w-full h-screen overflow-scroll">
<div className="sticky top-0 z-40 flex h-16 shrink-0 items-center gap-x-4 border-b border-gray-200 bg-white shadow-sm sm:gap-x-6 sm:px-6 px-8">
@ -152,12 +167,9 @@ export const Preferences = () => {
{!fileName ? (
<div className="flex flex-col items-center justify-center pt-5 pb-6">
<p className="mb-2 text-sm text-gray-500 dark:text-gray-400">
<span className="font-semibold">Click to upload</span>{" "}
or drag and drop
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
TGZ (MAX 50MB)
<span className="font-semibold">Click to upload</span> or drag and drop
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">TGZ (MAX 50MB)</p>
</div>
) : (
<>{fileName}</>
@ -177,9 +189,7 @@ export const Preferences = () => {
type="submit"
className={classNames(
"rounded-md px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600",
fileName
? "bg-blue-500 hover:bg-blue-300"
: "bg-gray-500"
fileName ? "bg-blue-500 hover:bg-blue-300" : "bg-gray-500"
)}
>
Install Plugin
@ -205,11 +215,7 @@ export const Preferences = () => {
</div>
<div className="grid grid-cols-2 items-stretch gap-4">
{activePlugins
.filter(
(e) =>
search.trim() === "" ||
e.name.toLowerCase().includes(search.toLowerCase())
)
.filter((e) => search.trim() === "" || e.name.toLowerCase().includes(search.toLowerCase()))
.map((e) => (
<div
key={e.name}
@ -218,19 +224,13 @@ export const Preferences = () => {
>
<div className="flex flex-row space-x-2 items-center">
<span className="relative inline-block mt-1">
<img
className="h-14 w-14 rounded-md"
src={e.icon ?? "icons/app_icon.svg"}
alt=""
/>
<img className="h-14 w-14 rounded-md" src={e.icon ?? "icons/app_icon.svg"} alt="" />
</span>
<div className="flex flex-col">
<p className="text-xl font-bold tracking-tight text-gray-900 dark:text-white capitalize">
{e.name.replaceAll("-", " ")}
</p>
<p className="font-normal text-gray-700 dark:text-gray-400">
Version: {e.version}
</p>
<p className="font-normal text-gray-700 dark:text-gray-400">Version: {e.version}</p>
</div>
</div>
@ -266,8 +266,34 @@ export const Preferences = () => {
Test Plugins
</div>
)}
<div className="h-full w-full" ref={preferenceRef}></div>
{/* Content */}
<div className="h-full w-full" ref={experimentRef}></div>
<div className="flex flex-row items-center my-4">
<PlayIcon width={30} />
Preferences
</div>
<div className="h-full w-full flex flex-col" ref={preferenceRef}>
{preferenceItems?.map((e) => (
<div key={e.preferenceKey} className="flex flex-col mb-4">
<div>
<span className="text-[16px] text-gray-600">Setting:</span>{" "}
<span className="text-[16px] text-gray-900">{e.preferenceName}</span>
</div>
<span className="text-[14px] text-gray-400">{e.preferenceDescription}</span>
<div className="flex flex-row space-x-4 items-center mt-2">
<input
className="text-gray-500 w-1/3 rounded-sm border-gray-300 border-[1px] h-8"
defaultValue={preferenceValues.filter((v) => v.key === e.preferenceKey)[0]?.value}
onChange={(event) => {
preferences
.set(e.pluginName, e.preferenceKey, event.target.value)
.then(() => notifyPreferenceUpdate());
}}
></input>
</div>
</div>
))}
</div>
</div>
</main>
</div>

View File

@ -0,0 +1,34 @@
import { addNewMessageAtom, updateMessageAtom } from "@/_helpers/atoms/ChatMessage.atom";
import { toChatMessage } from "@/_models/ChatMessage";
import { events, EventName, NewMessageResponse } from "@janhq/plugin-core";
import { useSetAtom } from "jotai";
import { ReactNode, useEffect } from "react";
export default function EventHandler({ children }: { children: ReactNode }) {
const addNewMessage = useSetAtom(addNewMessageAtom);
const updateMessage = useSetAtom(updateMessageAtom);
function handleNewMessageResponse(message: NewMessageResponse) {
const newResponse = toChatMessage(message);
addNewMessage(newResponse);
}
async function handleMessageResponseUpdate(messageResponse: NewMessageResponse) {
if (messageResponse.conversationId && messageResponse._id && messageResponse.message)
updateMessage(messageResponse._id, messageResponse.conversationId, messageResponse.message);
}
useEffect(() => {
if (window.corePlugin.events) {
events.on(EventName.OnNewMessageResponse, handleNewMessageResponse);
events.on(EventName.OnMessageResponseUpdate, handleMessageResponseUpdate);
}
}, []);
useEffect(() => {
return () => {
events.off(EventName.OnNewMessageResponse, handleNewMessageResponse);
events.off(EventName.OnMessageResponseUpdate, handleMessageResponseUpdate);
};
}, []);
return <> {children}</>;
}

View File

@ -6,12 +6,10 @@ import { appDownloadProgress } from "./JotaiWrapper";
import { DownloadState } from "@/_models/DownloadState";
import { executeSerial } from "../../../electron/core/plugin-manager/execution/extension-manager";
import { ModelManagementService } from "@janhq/plugin-core";
import {
setDownloadStateAtom,
setDownloadStateSuccessAtom,
} from "./atoms/DownloadState.atom";
import { setDownloadStateAtom, setDownloadStateSuccessAtom } from "./atoms/DownloadState.atom";
import { getDownloadedModels } from "@/_hooks/useGetDownloadedModels";
import { downloadedModelAtom } from "./atoms/DownloadedModel.atom";
import EventHandler from "./EventHandler";
type Props = {
children: ReactNode;
@ -25,57 +23,46 @@ export default function EventListenerWrapper({ children }: Props) {
useEffect(() => {
if (window && window.electronAPI) {
window.electronAPI.onFileDownloadUpdate(
(_event: string, state: DownloadState | undefined) => {
window.electronAPI.onFileDownloadUpdate((_event: string, state: DownloadState | undefined) => {
if (!state) return;
setDownloadState(state);
}
);
});
window.electronAPI.onFileDownloadError(
(_event: string, callback: any) => {
window.electronAPI.onFileDownloadError((_event: string, callback: any) => {
console.log("Download error", callback);
}
);
});
window.electronAPI.onFileDownloadSuccess(
(_event: string, callback: any) => {
window.electronAPI.onFileDownloadSuccess((_event: string, callback: any) => {
if (callback && callback.fileName) {
setDownloadStateSuccess(callback.fileName);
executeSerial(
ModelManagementService.UpdateFinishedDownloadAt,
callback.fileName
).then(() => {
executeSerial(ModelManagementService.UpdateFinishedDownloadAt, callback.fileName).then(() => {
getDownloadedModels().then((models) => {
setDownloadedModels(models);
});
});
}
}
);
});
window.electronAPI.onAppUpdateDownloadUpdate(
(_event: string, progress: any) => {
window.electronAPI.onAppUpdateDownloadUpdate((_event: string, progress: any) => {
setProgress(progress.percent);
console.log("app update progress:", progress.percent);
}
);
});
window.electronAPI.onAppUpdateDownloadError(
(_event: string, callback: any) => {
window.electronAPI.onAppUpdateDownloadError((_event: string, callback: any) => {
console.log("Download error", callback);
setProgress(-1);
}
);
});
window.electronAPI.onAppUpdateDownloadSuccess(
(_event: string, callback: any) => {
window.electronAPI.onAppUpdateDownloadSuccess((_event: string, callback: any) => {
setProgress(-1);
}
);
});
}
}, []);
return <div id="eventlistener">{children}</div>;
return (
<div id="eventlistener">
<EventHandler>{children}</EventHandler>
</div>
);
}

View File

@ -31,28 +31,18 @@ const useChatMessages = (offset = 0) => {
if (!hasMore) return;
const getMessages = async () => {
executeSerial(
DataService.GetConversationMessages,
currentConvo._id
).then((data: any) => {
executeSerial(DataService.GetConversationMessages, currentConvo._id).then((data: any) => {
if (!data) {
return;
}
parseMessages(data ?? []).then((newMessages) => {
const newMessages = parseMessages(data ?? []);
addOldChatMessages(newMessages);
updateConvoHasMore(currentConvo._id ?? "", false);
setLoading(false);
});
});
};
getMessages();
}, [
offset,
currentConvo?._id,
convoStates,
addOldChatMessages,
updateConvoHasMore,
]);
}, [offset, currentConvo?._id, convoStates, addOldChatMessages, updateConvoHasMore]);
return {
loading: loading,
@ -61,10 +51,10 @@ const useChatMessages = (offset = 0) => {
};
};
async function parseMessages(messages: RawMessage[]): Promise<ChatMessage[]> {
function parseMessages(messages: RawMessage[]): ChatMessage[] {
const newMessages: ChatMessage[] = [];
for (const m of messages) {
const chatMessage = await toChatMessage(m);
const chatMessage = toChatMessage(m);
newMessages.push(chatMessage);
}
return newMessages;

View File

@ -1,51 +1,19 @@
import { currentPromptAtom } from "@/_helpers/JotaiWrapper";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { selectAtom } from "jotai/utils";
import { DataService, InferenceService } from "@janhq/plugin-core";
import {
ChatMessage,
MessageSenderType,
RawMessage,
toChatMessage,
} from "@/_models/ChatMessage";
import { DataService, EventName, events } from "@janhq/plugin-core";
import { RawMessage, toChatMessage } from "@/_models/ChatMessage";
import { executeSerial } from "@/_services/pluginService";
import { useCallback } from "react";
import {
addNewMessageAtom,
updateMessageAtom,
chatMessages,
currentChatMessagesAtom,
} from "@/_helpers/atoms/ChatMessage.atom";
import {
currentConversationAtom,
getActiveConvoIdAtom,
updateConversationAtom,
updateConversationWaitingForResponseAtom,
} from "@/_helpers/atoms/Conversation.atom";
import { Conversation } from "@/_models/Conversation";
import { addNewMessageAtom } from "@/_helpers/atoms/ChatMessage.atom";
import { currentConversationAtom } from "@/_helpers/atoms/Conversation.atom";
export default function useSendChatMessage() {
const currentConvo = useAtomValue(currentConversationAtom);
const updateConversation = useSetAtom(updateConversationAtom);
const addNewMessage = useSetAtom(addNewMessageAtom);
const updateMessage = useSetAtom(updateMessageAtom);
const activeConversationId = useAtomValue(getActiveConvoIdAtom) ?? "";
const updateConvWaiting = useSetAtom(
updateConversationWaitingForResponseAtom
);
const chatMessagesHistory = useAtomValue(
selectAtom(
chatMessages,
useCallback((v) => v[activeConversationId], [activeConversationId])
)
);
const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom);
const sendChatMessage = async () => {
setCurrentPrompt("");
const conversationId = activeConversationId;
updateConvWaiting(conversationId, true);
const prompt = currentPrompt.trim();
const newMessage: RawMessage = {
conversationId: currentConvo?._id,
@ -56,188 +24,9 @@ export default function useSendChatMessage() {
const id = await executeSerial(DataService.CreateMessage, newMessage);
newMessage._id = id;
const newChatMessage = await toChatMessage(newMessage);
addNewMessage(newChatMessage);
const messageHistory = chatMessagesHistory ?? [];
const recentMessages = [
...messageHistory.sort((a, b) => parseInt(a.id) - parseInt(b.id)),
newChatMessage,
]
.slice(-10)
.map((message) => {
return {
content: message.text,
role:
message.messageSenderType === MessageSenderType.User
? "user"
: "assistant",
};
});
const url = await executeSerial(InferenceService.InferenceUrl);
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "text/event-stream",
"Access-Control-Allow-Origi": "*",
},
body: JSON.stringify({
messages: recentMessages,
stream: true,
model: "gpt-3.5-turbo",
max_tokens: 500,
}),
});
const stream = response.body;
const decoder = new TextDecoder("utf-8");
const reader = stream?.getReader();
let answer = "";
// Cache received response
const newResponse: RawMessage = {
conversationId: currentConvo?._id,
message: answer,
user: "assistant",
createdAt: new Date().toISOString(),
};
const respId = await executeSerial(DataService.CreateMessage, newResponse);
newResponse._id = respId;
const responseChatMessage = toChatMessage(newResponse);
addNewMessage(responseChatMessage);
while (true && reader) {
const { done, value } = await reader.read();
if (done) {
console.log("SSE stream closed");
break;
}
const text = decoder.decode(value);
const lines = text.trim().split("\n");
for (const line of lines) {
if (line.startsWith("data: ") && !line.includes("data: [DONE]")) {
updateConvWaiting(conversationId, false);
const data = JSON.parse(line.replace("data: ", ""));
answer += data.choices[0]?.delta?.content ?? "";
if (answer.startsWith("assistant: ")) {
answer = answer.replace("assistant: ", "");
}
updateMessage(
responseChatMessage.id,
responseChatMessage.conversationId,
answer
);
}
}
}
updateMessage(
responseChatMessage.id,
responseChatMessage.conversationId,
answer.trimEnd()
);
await executeSerial(DataService.UpdateMessage, {
...newResponse,
message: answer.trimEnd(),
updatedAt: new Date()
.toISOString()
.replace("T", " ")
.replace(/\.\d+Z$/, ""),
});
const updatedConvo: Conversation = {
...currentConvo,
lastMessage: answer.trim(),
updatedAt: new Date().toISOString(),
};
await executeSerial(DataService.UpdateConversation, updatedConvo);
updateConversation(updatedConvo);
updateConvWaiting(conversationId, false);
newResponse.message = answer.trim();
const messages: RawMessage[] = [newMessage, newResponse];
inferConvoSummary(updatedConvo, messages);
};
const inferConvoSummary = async (
convo: Conversation,
lastMessages: RawMessage[]
) => {
if (convo.summary) return;
const newMessage: RawMessage = {
conversationId: currentConvo?._id,
message: "summary this conversation in 5 words",
user: "user",
createdAt: new Date().toISOString(),
};
const messageHistory = lastMessages.map((m) => toChatMessage(m));
const newChatMessage = toChatMessage(newMessage);
const recentMessages = [...messageHistory, newChatMessage]
.slice(-10)
.map((message) => ({
content: message.text,
role: message.messageSenderType,
}));
console.debug(`Sending ${JSON.stringify(recentMessages)}`);
const url = await executeSerial(InferenceService.InferenceUrl);
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "text/event-stream",
"Access-Control-Allow-Origi": "*",
},
body: JSON.stringify({
messages: recentMessages,
stream: true,
model: "gpt-3.5-turbo",
max_tokens: 500,
}),
});
const stream = response.body;
const decoder = new TextDecoder("utf-8");
const reader = stream?.getReader();
let answer = "";
// Cache received response
const newResponse: RawMessage = {
conversationId: currentConvo?._id,
message: answer,
user: "assistant",
createdAt: new Date().toISOString(),
};
while (reader) {
const { done, value } = await reader.read();
if (done) {
console.log("SSE stream closed");
break;
}
const text = decoder.decode(value);
const lines = text.trim().split("\n");
for (const line of lines) {
if (line.startsWith("data: ") && !line.includes("data: [DONE]")) {
const data = JSON.parse(line.replace("data: ", ""));
answer += data.choices[0]?.delta?.content ?? "";
if (answer.startsWith("assistant: ")) {
answer = answer.replace("assistant: ", "");
}
}
}
}
const updatedConvo: Conversation = {
...convo,
summary: answer.trim(),
};
console.debug(`Update convo: ${JSON.stringify(updatedConvo)}`);
await executeSerial(DataService.UpdateConversation, updatedConvo);
updateConversation(updatedConvo);
addNewMessage(newChatMessage);
events.emit(EventName.OnNewMessageRequest, newMessage);
};
return {

View File

@ -1,3 +1,4 @@
import { NewMessageResponse } from "@janhq/plugin-core";
export enum MessageType {
Text = "Text",
Image = "Image",
@ -33,12 +34,13 @@ export interface RawMessage {
_id?: string;
conversationId?: string;
user?: string;
avatar?: string;
message?: string;
createdAt?: string;
updatedAt?: string;
}
export const toChatMessage = (m: RawMessage): ChatMessage => {
export const toChatMessage = (m: RawMessage | NewMessageResponse): ChatMessage => {
const createdAt = new Date(m.createdAt ?? "").getTime();
const imageUrls: string[] = [];
const imageUrl = undefined;
@ -47,8 +49,7 @@ export const toChatMessage = (m: RawMessage): ChatMessage => {
}
const messageType = MessageType.Text;
const messageSenderType =
m.user === "user" ? MessageSenderType.User : MessageSenderType.Ai;
const messageSenderType = m.user === "user" ? MessageSenderType.User : MessageSenderType.Ai;
const content = m.message ?? "";
@ -58,9 +59,8 @@ export const toChatMessage = (m: RawMessage): ChatMessage => {
messageType: messageType,
messageSenderType: messageSenderType,
senderUid: m.user?.toString() || "0",
senderName: m.user === "user" ? "You" : "Assistant",
senderAvatarUrl:
m.user === "user" ? "icons/avatar.svg" : "icons/app_icon.svg",
senderName: m.user === "user" ? "You" : m.user && m.user !== "ai" && m.user !== "assistant" ? m.user : "Assistant",
senderAvatarUrl: m.avatar ? m.avatar : m.user === "user" ? "icons/avatar.svg" : "icons/app_icon.svg",
text: content,
imageUrls: imageUrls,
createdAt: createdAt,

View File

@ -1,8 +1,21 @@
import { store } from "./storeService";
import { EventEmitter } from "./eventsService";
export const setupCoreServices = () => {
if (typeof window === "undefined") {
console.log("undefine", window);
return;
} else {
console.log("Setting up core services");
}
if (!window.corePlugin) {
window.corePlugin = {
store,
events: new EventEmitter(),
};
}
if (!window.coreAPI) {
// fallback electron API
window.coreAPI = window.electronAPI;
}
};

View File

@ -0,0 +1,40 @@
export class EventEmitter {
private handlers: Map<string, Function[]>;
constructor() {
this.handlers = new Map<string, Function[]>();
}
public on(eventName: string, handler: Function): void {
if (!this.handlers.has(eventName)) {
this.handlers.set(eventName, []);
}
this.handlers.get(eventName)?.push(handler);
}
public off(eventName: string, handler: Function): void {
if (!this.handlers.has(eventName)) {
return;
}
const handlers = this.handlers.get(eventName);
const index = handlers?.indexOf(handler);
if (index !== undefined && index !== -1) {
handlers?.splice(index, 1);
}
}
public emit(eventName: string, args: any): void {
if (!this.handlers.has(eventName)) {
return;
}
const handlers = this.handlers.get(eventName);
handlers?.forEach((handler) => {
handler(args);
});
}
}

View File

@ -1,31 +1,21 @@
"use client";
import { PluginService } from "@janhq/plugin-core";
import { ThemeWrapper } from "./_helpers/ThemeWrapper";
import JotaiWrapper from "./_helpers/JotaiWrapper";
import { ModalWrapper } from "./_helpers/ModalWrapper";
import { useEffect, useState } from "react";
import Image from "next/image";
import {
setup,
plugins,
activationPoints,
} from "../../electron/core/plugin-manager/execution/index";
import {
isCorePluginInstalled,
setupBasePlugins,
} from "./_services/pluginService";
import { setup, plugins, activationPoints, extensionPoints } from "../../electron/core/plugin-manager/execution/index";
import { isCorePluginInstalled, setupBasePlugins } from "./_services/pluginService";
import EventListenerWrapper from "./_helpers/EventListenerWrapper";
import { setupCoreServices } from "./_services/coreService";
import MainContainer from "./_components/MainContainer";
import { executeSerial } from "../../electron/core/plugin-manager/execution/extension-manager";
const Page: React.FC = () => {
const [setupCore, setSetupCore] = useState(false);
const [activated, setActivated] = useState(false);
// Services Setup
useEffect(() => {
// Setup Core Service
setupCoreServices();
async function setupPE() {
// Enable activation point management
setup({
@ -44,9 +34,21 @@ const Page: React.FC = () => {
setupBasePlugins();
return;
}
if (extensionPoints.get(PluginService.OnStart)) {
await executeSerial(PluginService.OnStart);
}
setActivated(true);
}, 500);
}
// Services Setup
useEffect(() => {
setupCoreServices();
setSetupCore(true);
}, []);
useEffect(() => {
if (setupCore) {
// Electron
if (window && window.electronAPI) {
setupPE();
@ -54,10 +56,12 @@ const Page: React.FC = () => {
// Host
setActivated(true);
}
}, []);
}
}, [setupCore]);
return (
<JotaiWrapper>
{setupCore && (
<EventListenerWrapper>
<ThemeWrapper>
<ModalWrapper>
@ -71,6 +75,7 @@ const Page: React.FC = () => {
</ModalWrapper>
</ThemeWrapper>
</EventListenerWrapper>
)}
</JotaiWrapper>
);
};

View File

@ -13,7 +13,7 @@
"dependencies": {
"@headlessui/react": "^1.7.15",
"@heroicons/react": "^2.0.18",
"@janhq/plugin-core": "file:../plugin-core",
"@janhq/plugin-core": "^0.1.8",
"@tailwindcss/typography": "^0.5.9",
"@types/react": "18.2.15",
"@types/react-dom": "18.2.7",

View File

@ -4,5 +4,6 @@ declare global {
interface Window {
electronAPI?: any | undefined;
corePlugin?: any | undefined;
coreAPI?: any | undefined;
}
}

10591
yarn.lock

File diff suppressed because it is too large Load Diff