mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-11 14:52:55 +01:00
Initial commit
Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com> Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com> Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com> Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com> Co-Authored-By: Johan <4934465+joolsus@users.noreply.github.com> Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com> Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
This commit is contained in:
73
packages/noodl-platform/README.md
Normal file
73
packages/noodl-platform/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Noodl Platform
|
||||
|
||||
Cross platform implementation of platform specific features.
|
||||
|
||||
## Getting Started
|
||||
|
||||
When the app is starting we have to set the desired providers.
|
||||
|
||||
### Electron
|
||||
|
||||
```ts
|
||||
// Setup the platform before anything else is loading
|
||||
// This is a problem since we are calling the platform when importing
|
||||
import "@noodl/platform-electron";
|
||||
|
||||
// Then import the platform etc via:
|
||||
import { filesystem, platform } from "@noodl/platform";
|
||||
```
|
||||
|
||||
### Node
|
||||
|
||||
```
|
||||
$ npm install @noodl/platform @noodl/platform-node
|
||||
```
|
||||
|
||||
```ts
|
||||
// Setup the platform before anything else is loading
|
||||
// This is a problem since we are calling the platform when importing
|
||||
import "@noodl/platform-node";
|
||||
|
||||
// Then import the platform etc via:
|
||||
import { filesystem, platform } from "@noodl/platform";
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
### Platform
|
||||
|
||||
```ts
|
||||
import { platform } from "@noodl/platform";
|
||||
|
||||
platform.getBuildNumber().then((version) => {});
|
||||
```
|
||||
|
||||
### File System
|
||||
|
||||
```ts
|
||||
import { filesystem } from "@noodl/platform";
|
||||
|
||||
filesystem.readJson("path/to/file.json").then((content) => {
|
||||
console.log(content.value);
|
||||
});
|
||||
```
|
||||
|
||||
### Storage (Config Storage)
|
||||
|
||||
> This API still needs some love to bring a better universal config system to Noodl.
|
||||
|
||||
```ts
|
||||
import { JSONStorage } from "@noodl/platform";
|
||||
|
||||
JSONStorage.get("my-key").then((content) => {
|
||||
// content = json file
|
||||
});
|
||||
|
||||
JSONStorage.set("my-key", { key: "value" }).then(() => {
|
||||
// done
|
||||
});
|
||||
|
||||
JSONStorage.remove("my-key").then(() => {
|
||||
// done
|
||||
});
|
||||
```
|
||||
8
packages/noodl-platform/package.json
Normal file
8
packages/noodl-platform/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "@noodl/platform",
|
||||
"version": "2.7.0",
|
||||
"main": "src/index.ts",
|
||||
"description": "Cross platform implementation of platform specific features.",
|
||||
"author": "Noodl <info@noodl.net>",
|
||||
"homepage": "https://noodl.net"
|
||||
}
|
||||
53
packages/noodl-platform/src/filesystem/common.ts
Normal file
53
packages/noodl-platform/src/filesystem/common.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
export type FileBlob = Buffer | string;
|
||||
|
||||
export interface FileInfo {
|
||||
fullPath: string;
|
||||
name: string;
|
||||
isDirectory: boolean;
|
||||
}
|
||||
|
||||
export interface FileStat {
|
||||
size: number;
|
||||
}
|
||||
|
||||
export type OpenDialogOptions = {
|
||||
allowCreateDirectory?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* File System that is designed to be cross platform.
|
||||
*/
|
||||
export interface IFileSystem {
|
||||
resolve(...paths: string[]): string;
|
||||
join(...paths: string[]): string;
|
||||
exists(path: string): boolean;
|
||||
dirname(path: string): string;
|
||||
basename(path: string): string;
|
||||
|
||||
file(path: string): FileStat;
|
||||
|
||||
writeFile(path: string, blob: FileBlob): Promise<void>;
|
||||
writeFileOverride(path: string, blob: FileBlob): Promise<void>;
|
||||
readFile(path: string): Promise<string>;
|
||||
readBinaryFile(path: string): Promise<Buffer>;
|
||||
removeFile(path: string): Promise<void>;
|
||||
renameFile(oldPath: string, newPath: string): Promise<void>;
|
||||
|
||||
copyFile(from: string, to: string): Promise<void>;
|
||||
copyFolder(from: string, to: string): Promise<void>;
|
||||
|
||||
readJson<T = any>(path: string): Promise<T>;
|
||||
writeJson(path: string, obj: any): Promise<void>;
|
||||
|
||||
isDirectoryEmpty(path: string): Promise<boolean>;
|
||||
listDirectory(path: string): Promise<FileInfo[]>;
|
||||
/** List all the files in this folder recursively */
|
||||
listDirectoryFiles(path: string): Promise<FileInfo[]>;
|
||||
makeDirectory(path: string): Promise<void>;
|
||||
removeDirRecursive(path: string): void;
|
||||
|
||||
openDialog(args: OpenDialogOptions): Promise<string>;
|
||||
|
||||
unzipUrl(url: string, to: string): Promise<void>;
|
||||
makeUniquePath(path: string): string;
|
||||
}
|
||||
1
packages/noodl-platform/src/filesystem/index.ts
Normal file
1
packages/noodl-platform/src/filesystem/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./common";
|
||||
27
packages/noodl-platform/src/index.ts
Normal file
27
packages/noodl-platform/src/index.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { IFileSystem } from './filesystem';
|
||||
import { IPlatform, PlatformWeb } from './platform';
|
||||
import { IStorage } from './storage';
|
||||
import { StorageWeb } from './storage/storage-web';
|
||||
|
||||
export * from './filesystem';
|
||||
export * from './platform';
|
||||
export * from './storage';
|
||||
export * from './utils';
|
||||
|
||||
let platform: IPlatform = new PlatformWeb('0.0.0', undefined, '0');
|
||||
let filesystem: IFileSystem;
|
||||
let JSONStorage: IStorage = new StorageWeb();
|
||||
|
||||
export { platform, filesystem, JSONStorage };
|
||||
|
||||
export function setPlatform(value: IPlatform): void {
|
||||
platform = value;
|
||||
}
|
||||
|
||||
export function setFileSystem(value: IFileSystem): void {
|
||||
filesystem = value;
|
||||
}
|
||||
|
||||
export function setStorage(value: IStorage): void {
|
||||
JSONStorage = value;
|
||||
}
|
||||
76
packages/noodl-platform/src/platform/common.ts
Normal file
76
packages/noodl-platform/src/platform/common.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
export enum PlatformOS {
|
||||
Web = "web",
|
||||
Windows = "windows",
|
||||
MacOS = "macOS",
|
||||
Linux = "linux",
|
||||
Unknown = "unknown"
|
||||
}
|
||||
|
||||
export interface IPlatform {
|
||||
get name(): string;
|
||||
|
||||
get os(): PlatformOS;
|
||||
|
||||
/**
|
||||
* @example '1'
|
||||
*/
|
||||
getBuildNumber(): string | undefined;
|
||||
|
||||
/**
|
||||
* @example '2.6.3-1'
|
||||
*/
|
||||
getFullVersion(): string;
|
||||
|
||||
/**
|
||||
* @example '2.6.3'
|
||||
*/
|
||||
getVersion(): string;
|
||||
|
||||
/**
|
||||
* @example '2.6.3' or '2.6.3-AI'
|
||||
*/
|
||||
getVersionWithTag(): string;
|
||||
|
||||
/**
|
||||
* @example Windows: 'C:/Users/Eric/AppData/Roaming/Noodl'
|
||||
* @example OSX: '/Users/eric/Library/Preferences/Noodl'
|
||||
*/
|
||||
getUserDataPath(): string;
|
||||
|
||||
/**
|
||||
* @example Windows: 'C:/Users/Eric/OneDrive/Dokument'
|
||||
*/
|
||||
getDocumentsPath(): string;
|
||||
|
||||
/**
|
||||
* @example Windows: 'C:/Users/Eric/AppData/Local/Temp/'
|
||||
* @example OSX: '/var/folders/8w/29mdvxz11f13l68p4xg_m_vc0000gn/T/'
|
||||
*/
|
||||
getTempPath(): string;
|
||||
|
||||
/**
|
||||
* @example Windows: 'C:/GitHub/noodl-editor/'
|
||||
* @example OSX: '/Users/eric/Documents/GitHub/noodl-editor/'
|
||||
*/
|
||||
getAppPath(): string;
|
||||
|
||||
/**
|
||||
* Open the given external protocol URL in the desktop's default manner.
|
||||
* (For example, mailto: URLs in the user's default mail agent).
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
openExternal(url: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Write the specified text string to the system clipboard.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
copyToClipboard(value: string): Promise<void>;
|
||||
}
|
||||
|
||||
// OSX and Windows add trailing slashes to the temp folder, Linux doesn't
|
||||
export function addTrailingSlash(path: string): string {
|
||||
return path[path.length - 1] !== "/" ? path + "/" : path;
|
||||
}
|
||||
3
packages/noodl-platform/src/platform/index.ts
Normal file
3
packages/noodl-platform/src/platform/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./common";
|
||||
export * from "./support";
|
||||
export * from "./platform-web";
|
||||
53
packages/noodl-platform/src/platform/platform-web.ts
Normal file
53
packages/noodl-platform/src/platform/platform-web.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { IPlatform } from '@noodl/platform';
|
||||
|
||||
import { PlatformOS } from './common';
|
||||
|
||||
export class PlatformWeb implements IPlatform {
|
||||
get name(): string {
|
||||
return 'Web';
|
||||
}
|
||||
|
||||
get os(): PlatformOS {
|
||||
return PlatformOS.Web;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _version: string,
|
||||
private readonly _versionTag: string,
|
||||
private readonly _buildNumber: string
|
||||
) {}
|
||||
|
||||
getBuildNumber(): string | undefined {
|
||||
return this._buildNumber;
|
||||
}
|
||||
getFullVersion(): string {
|
||||
return this._version + '-' + this._buildNumber;
|
||||
}
|
||||
getVersion(): string {
|
||||
return this._version;
|
||||
}
|
||||
getVersionWithTag(): string {
|
||||
return this._versionTag ? `${this._version}-${this._versionTag}` : this._version;
|
||||
}
|
||||
|
||||
getUserDataPath(): string {
|
||||
return '/user';
|
||||
}
|
||||
getDocumentsPath(): string {
|
||||
return '/documents';
|
||||
}
|
||||
getTempPath(): string {
|
||||
return '/tmp';
|
||||
}
|
||||
getAppPath(): string {
|
||||
return '/app';
|
||||
}
|
||||
|
||||
async openExternal(url: string): Promise<void> {
|
||||
window.open(url, '_blank').focus();
|
||||
}
|
||||
|
||||
async copyToClipboard(value: string): Promise<void> {
|
||||
await navigator.clipboard.writeText(value);
|
||||
}
|
||||
}
|
||||
43
packages/noodl-platform/src/platform/support.ts
Normal file
43
packages/noodl-platform/src/platform/support.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* https://stackoverflow.com/a/61725416/3211243
|
||||
*/
|
||||
export function isElectron(): boolean {
|
||||
// Renderer process
|
||||
// @ts-ignore window
|
||||
if (
|
||||
typeof window !== "undefined" &&
|
||||
typeof window.process === "object" &&
|
||||
// @ts-ignore
|
||||
window.process.type === "renderer"
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Main process
|
||||
if (
|
||||
typeof process !== "undefined" &&
|
||||
typeof process.versions === "object" &&
|
||||
!!process.versions.electron
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Detect the user agent when the `nodeIntegration` option is set to true
|
||||
// @ts-ignore navigator
|
||||
if (
|
||||
typeof navigator === "object" &&
|
||||
typeof navigator.userAgent === "string" &&
|
||||
navigator.userAgent.indexOf("Electron") >= 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isNode(): boolean {
|
||||
return (
|
||||
typeof process !== "undefined" &&
|
||||
process.release.name.search(/node|io.js/) !== -1
|
||||
);
|
||||
}
|
||||
5
packages/noodl-platform/src/storage/common.ts
Normal file
5
packages/noodl-platform/src/storage/common.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface IStorage {
|
||||
get(key: string): Promise<any>;
|
||||
set(key: string, data: { [key: string]: any }): Promise<void>;
|
||||
remove(key: string): Promise<void>;
|
||||
}
|
||||
2
packages/noodl-platform/src/storage/index.ts
Normal file
2
packages/noodl-platform/src/storage/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./common";
|
||||
export * from "./storage-web";
|
||||
13
packages/noodl-platform/src/storage/storage-web.ts
Normal file
13
packages/noodl-platform/src/storage/storage-web.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { IStorage } from "./common";
|
||||
|
||||
export class StorageWeb implements IStorage {
|
||||
get(key: string): Promise<any> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
set(key: string, data: { [key: string]: any }): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
remove(key: string): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
}
|
||||
28
packages/noodl-platform/src/utils/convert.ts
Normal file
28
packages/noodl-platform/src/utils/convert.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export namespace ConvertUtils {
|
||||
export function bytesToKilobytes(bytes: number): number {
|
||||
const kilobytes = bytes / 1024;
|
||||
return kilobytes;
|
||||
}
|
||||
|
||||
export function bytesToMegabytes(bytes: number): number {
|
||||
const megabytes = bytes / (1024 * 1024);
|
||||
return megabytes;
|
||||
}
|
||||
|
||||
export function bytesToGigabytes(bytes: number): number {
|
||||
const gigabytes = bytes / (1024 * 1024 * 1024);
|
||||
return gigabytes;
|
||||
}
|
||||
|
||||
export function bytesToMostSuitableSize(bytes: number): string {
|
||||
if (bytes < 1024) {
|
||||
return bytes + ' bytes';
|
||||
} else if (bytes < 1024 * 1024) {
|
||||
return bytesToKilobytes(bytes).toFixed(2) + ' KB';
|
||||
} else if (bytes < 1024 * 1024 * 1024) {
|
||||
return bytesToMegabytes(bytes).toFixed(2) + ' MB';
|
||||
} else {
|
||||
return bytesToGigabytes(bytes).toFixed(2) + ' GB';
|
||||
}
|
||||
}
|
||||
}
|
||||
3
packages/noodl-platform/src/utils/index.ts
Normal file
3
packages/noodl-platform/src/utils/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './convert';
|
||||
export * from './promise';
|
||||
export * from './random';
|
||||
30
packages/noodl-platform/src/utils/promise.ts
Normal file
30
packages/noodl-platform/src/utils/promise.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
// TODO(typescript): Remove when we upgrade to Typescript 4.5
|
||||
type Awaited<T> = T extends null | undefined
|
||||
? T // special case for `null | undefined` when not in `--strictNullChecks` mode
|
||||
: T extends object & { then(onfulfilled: infer F): any } // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
|
||||
? F extends (value: infer V, ...args: any) => any // if the argument to `then` is callable, extracts the first argument
|
||||
? Awaited<V> // recursively unwrap the value
|
||||
: never // the argument to `then` was not callable
|
||||
: T; // non-object or non-thenable
|
||||
|
||||
type PromiseHash = Record<string, Promise<unknown>>;
|
||||
|
||||
type AwaitedPromiseHash<T extends PromiseHash> = {
|
||||
[P in keyof T]: Awaited<T[P]>;
|
||||
};
|
||||
|
||||
export namespace PromiseUtils {
|
||||
export async function allObjects<T extends PromiseHash>(object: T): Promise<AwaitedPromiseHash<T>> {
|
||||
return Object.fromEntries(
|
||||
await Promise.all(
|
||||
Object.entries(object).map(async ([key, promise]) => {
|
||||
return [key, await promise];
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
}
|
||||
9
packages/noodl-platform/src/utils/random.ts
Normal file
9
packages/noodl-platform/src/utils/random.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export namespace RandomUtils {
|
||||
export function range(min: number, max: number): number {
|
||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
||||
}
|
||||
|
||||
export function within(max: number): number {
|
||||
return Math.floor(Math.random() * max);
|
||||
}
|
||||
}
|
||||
4
packages/noodl-platform/tsconfig.json
Normal file
4
packages/noodl-platform/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"exclude": []
|
||||
}
|
||||
Reference in New Issue
Block a user