mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 23:19:40 +01:00
core: convert everything else to typescript
This commit is contained in:
7
packages/core/package-lock.json
generated
7
packages/core/package-lock.json
generated
@@ -33,6 +33,7 @@
|
|||||||
"@types/event-source-polyfill": "^1.0.1",
|
"@types/event-source-polyfill": "^1.0.1",
|
||||||
"@types/html-to-text": "^9.0.0",
|
"@types/html-to-text": "^9.0.0",
|
||||||
"@types/katex": "^0.16.2",
|
"@types/katex": "^0.16.2",
|
||||||
|
"@types/mime-db": "^1.43.1",
|
||||||
"@types/prismjs": "^1.26.0",
|
"@types/prismjs": "^1.26.0",
|
||||||
"@types/spark-md5": "^3.0.2",
|
"@types/spark-md5": "^3.0.2",
|
||||||
"@types/streetwriters__showdown": "npm:@types/showdown@^2.0.6",
|
"@types/streetwriters__showdown": "npm:@types/showdown@^2.0.6",
|
||||||
@@ -2221,6 +2222,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/mime-db": {
|
||||||
|
"version": "1.43.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/mime-db/-/mime-db-1.43.5.tgz",
|
||||||
|
"integrity": "sha512-/bfTiIUTNPUBnwnYvUxXAre5MhD88jgagLEQiQtIASjU+bwxd8kS/ASDA4a8ufd8m0Lheu6eeMJHEUpLHoJ28A==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.11.9",
|
"version": "18.11.9",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"@types/event-source-polyfill": "^1.0.1",
|
"@types/event-source-polyfill": "^1.0.1",
|
||||||
"@types/html-to-text": "^9.0.0",
|
"@types/html-to-text": "^9.0.0",
|
||||||
"@types/katex": "^0.16.2",
|
"@types/katex": "^0.16.2",
|
||||||
|
"@types/mime-db": "^1.43.1",
|
||||||
"@types/prismjs": "^1.26.0",
|
"@types/prismjs": "^1.26.0",
|
||||||
"@types/spark-md5": "^3.0.2",
|
"@types/spark-md5": "^3.0.2",
|
||||||
"@types/streetwriters__showdown": "npm:@types/showdown@^2.0.6",
|
"@types/streetwriters__showdown": "npm:@types/showdown@^2.0.6",
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { IndexedCollection } from "./indexed-collection";
|
import { IndexedCollection } from "./indexed-collection";
|
||||||
import MapStub from "../utils/map";
|
|
||||||
import {
|
import {
|
||||||
CollectionType,
|
CollectionType,
|
||||||
Collections,
|
Collections,
|
||||||
@@ -51,9 +50,7 @@ export class CachedCollection<
|
|||||||
const data = await this.collection.indexer.readMulti(
|
const data = await this.collection.indexer.readMulti(
|
||||||
this.collection.indexer.indices
|
this.collection.indexer.indices
|
||||||
);
|
);
|
||||||
if ("dispose" in this.cache && typeof this.cache.dispose === "function")
|
this.cache = new Map(data);
|
||||||
this.cache.dispose();
|
|
||||||
this.cache = new MapStub.Map(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async add(item: MaybeDeletedItem<T>) {
|
async add(item: MaybeDeletedItem<T>) {
|
||||||
|
|||||||
@@ -18,13 +18,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ILogReporter,
|
||||||
LogLevel,
|
LogLevel,
|
||||||
|
LogMessage,
|
||||||
Logger,
|
Logger,
|
||||||
NoopLogger,
|
NoopLogger,
|
||||||
combineReporters,
|
combineReporters,
|
||||||
consoleReporter,
|
consoleReporter,
|
||||||
format
|
format,
|
||||||
|
ILogger
|
||||||
} from "@notesnook/logger";
|
} from "@notesnook/logger";
|
||||||
|
import { IStorage } from "./interfaces";
|
||||||
|
|
||||||
const WEEK = 86400000 * 7;
|
const WEEK = 86400000 * 7;
|
||||||
|
|
||||||
@@ -35,32 +39,21 @@ const WEEK = 86400000 * 7;
|
|||||||
// 4. Implement functions for log retrieval & filtering
|
// 4. Implement functions for log retrieval & filtering
|
||||||
|
|
||||||
class DatabaseLogReporter {
|
class DatabaseLogReporter {
|
||||||
/**
|
writer: DatabaseLogWriter;
|
||||||
*
|
constructor(storage: IStorage) {
|
||||||
* @param {import("./database/crypto").default} storage
|
|
||||||
*/
|
|
||||||
constructor(storage) {
|
|
||||||
this.writer = new DatabaseLogWriter(storage);
|
this.writer = new DatabaseLogWriter(storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
write(log: LogMessage) {
|
||||||
*
|
|
||||||
* @param {import("@notesnook/logger").LogMessage} log
|
|
||||||
*/
|
|
||||||
write(log) {
|
|
||||||
this.writer.push(log);
|
this.writer.push(log);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DatabaseLogWriter {
|
class DatabaseLogWriter {
|
||||||
/**
|
private queue: Map<string, LogMessage> = new Map();
|
||||||
*
|
private hasCleared = false;
|
||||||
* @param {import("./database/crypto").default} storage
|
|
||||||
*/
|
constructor(private readonly storage: IStorage) {
|
||||||
constructor(storage) {
|
|
||||||
this.storage = storage;
|
|
||||||
this.queue = new Map();
|
|
||||||
this.hasCleared = false;
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!this.hasCleared) {
|
if (!this.hasCleared) {
|
||||||
@@ -72,7 +65,7 @@ class DatabaseLogWriter {
|
|||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
push(message) {
|
push(message: LogMessage) {
|
||||||
const key = new Date(message.timestamp).toLocaleDateString();
|
const key = new Date(message.timestamp).toLocaleDateString();
|
||||||
this.queue.set(`${key}:${message.timestamp}`, message);
|
this.queue.set(`${key}:${message.timestamp}`, message);
|
||||||
}
|
}
|
||||||
@@ -100,33 +93,27 @@ class DatabaseLogWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DatabaseLogManager {
|
class DatabaseLogManager {
|
||||||
/**
|
constructor(private readonly storage: IStorage) {}
|
||||||
*
|
|
||||||
* @param {import("./database/crypto").default} storage
|
|
||||||
*/
|
|
||||||
constructor(storage) {
|
|
||||||
this.storage = storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
async get() {
|
async get() {
|
||||||
const logKeys = await this.storage.getAllKeys();
|
const logKeys = await this.storage.getAllKeys();
|
||||||
const logs = await this.storage.readMulti(logKeys);
|
const logEntries = await this.storage.readMulti<LogMessage>(logKeys);
|
||||||
const logGroups = {};
|
const logs: Record<string, LogMessage[]> = {};
|
||||||
|
|
||||||
for (const [key, log] of logs) {
|
for (const [logKey, log] of logEntries) {
|
||||||
const keyParts = key.split(":");
|
const keyParts = logKey.split(":");
|
||||||
if (keyParts.length === 1) continue;
|
if (keyParts.length === 1) continue;
|
||||||
|
|
||||||
const groupKey = keyParts[0];
|
const key = keyParts[0];
|
||||||
if (!logGroups[groupKey]) logGroups[groupKey] = [];
|
if (!logs[key]) logs[key] = [];
|
||||||
logGroups[groupKey].push(log);
|
logs[key].push(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.keys(logGroups)
|
return Object.keys(logs)
|
||||||
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }))
|
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true }))
|
||||||
.map((key) => ({
|
.map((key) => ({
|
||||||
key,
|
key,
|
||||||
logs: logGroups[key]?.sort((a, b) => a.timestamp - b.timestamp)
|
logs: logs[key]?.sort((a, b) => a.timestamp - b.timestamp)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +122,7 @@ class DatabaseLogManager {
|
|||||||
await this.storage.removeMulti(logKeys);
|
await this.storage.removeMulti(logKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(key) {
|
async delete(key: string) {
|
||||||
const logKeys = await this.storage.getAllKeys();
|
const logKeys = await this.storage.getAllKeys();
|
||||||
const keysToRemove = [];
|
const keysToRemove = [];
|
||||||
for (const logKey of logKeys) {
|
for (const logKey of logKeys) {
|
||||||
@@ -148,9 +135,9 @@ class DatabaseLogManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize(storage, disableConsoleLogs) {
|
function initialize(storage: IStorage, disableConsoleLogs?: boolean) {
|
||||||
if (storage) {
|
if (storage) {
|
||||||
let reporters = [new DatabaseLogReporter(storage)];
|
const reporters: ILogReporter[] = [new DatabaseLogReporter(storage)];
|
||||||
if (process.env.NODE_ENV !== "production" && !disableConsoleLogs)
|
if (process.env.NODE_ENV !== "production" && !disableConsoleLogs)
|
||||||
reporters.push(consoleReporter);
|
reporters.push(consoleReporter);
|
||||||
logger = new Logger({
|
logger = new Logger({
|
||||||
@@ -161,14 +148,7 @@ function initialize(storage, disableConsoleLogs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
let logger: ILogger = new NoopLogger();
|
||||||
* @type {import("@notesnook/logger").ILogger}
|
let logManager: DatabaseLogManager | undefined = undefined;
|
||||||
*/
|
|
||||||
var logger = new NoopLogger();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {DatabaseLogManager | undefined}
|
|
||||||
*/
|
|
||||||
var logManager;
|
|
||||||
|
|
||||||
export { LogLevel, format, initialize, logManager, logger };
|
export { LogLevel, format, initialize, logManager, logger };
|
||||||
@@ -17,15 +17,20 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var map = Map;
|
export class BufferPool {
|
||||||
class MapStub {
|
private freeBuffers: Buffer[] = [];
|
||||||
override(replacement) {
|
constructor(private readonly size: number) {}
|
||||||
map = replacement;
|
|
||||||
|
alloc() {
|
||||||
|
return this.freeBuffers.pop() || this.allocNew();
|
||||||
}
|
}
|
||||||
|
|
||||||
get Map() {
|
private allocNew() {
|
||||||
return map;
|
return Buffer.alloc(this.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf: Buffer) {
|
||||||
|
this.freeBuffers.push(buf);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new MapStub();
|
|
||||||
@@ -45,14 +45,15 @@ export const hosts = {
|
|||||||
|
|
||||||
export default hosts;
|
export default hosts;
|
||||||
|
|
||||||
export const getServerNameFromHost = (host) => {
|
const HOSTNAMES = {
|
||||||
const names = {
|
[extractHostname(hosts.API_HOST)]: "Notesnook Sync Server",
|
||||||
[extractHostname(hosts.API_HOST)]: "Notesnook Sync Server",
|
[extractHostname(hosts.AUTH_HOST)]: "Authentication Server",
|
||||||
[extractHostname(hosts.AUTH_HOST)]: "Authentication Server",
|
[extractHostname(hosts.SSE_HOST)]: "Eventing Server",
|
||||||
[extractHostname(hosts.SSE_HOST)]: "Eventing Server",
|
[extractHostname(hosts.SUBSCRIPTIONS_HOST)]:
|
||||||
[extractHostname(hosts.SUBSCRIPTIONS_HOST)]:
|
"Subscriptions Management Server",
|
||||||
"Subscriptions Management Server",
|
[extractHostname(hosts.ISSUES_HOST)]: "Bug Reporting Server"
|
||||||
[extractHostname(hosts.ISSUES_HOST)]: "Bug Reporting Server"
|
};
|
||||||
};
|
|
||||||
return names[host];
|
export const getServerNameFromHost = (host: string) => {
|
||||||
|
return HOSTNAMES[host];
|
||||||
};
|
};
|
||||||
@@ -16,22 +16,13 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import db from "mime-db";
|
import db from "mime-db";
|
||||||
|
|
||||||
// type MimeTypeInfo = {
|
export function getFileNameWithExtension(
|
||||||
// source: string;
|
filename: string,
|
||||||
// extensions?: string[];
|
mime: string | undefined
|
||||||
// charset?: string;
|
): string {
|
||||||
// compressible?: boolean;
|
|
||||||
// };
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {string} filename
|
|
||||||
* @param {string | undefined} mime
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
export function getFileNameWithExtension(filename, mime) {
|
|
||||||
if (!mime || mime === "application/octet-stream") return filename;
|
if (!mime || mime === "application/octet-stream") return filename;
|
||||||
const mimeData = db[mime];
|
const mimeData = db[mime];
|
||||||
if (!mimeData || !mimeData.extensions || mimeData.extensions.length === 0)
|
if (!mimeData || !mimeData.extensions || mimeData.extensions.length === 0)
|
||||||
@@ -59,24 +50,23 @@ export const DocumentMimeTypes = [
|
|||||||
"application/vnd.oasis.opendocument.presentation"
|
"application/vnd.oasis.opendocument.presentation"
|
||||||
];
|
];
|
||||||
|
|
||||||
export const WebClipMimeType = "application/vnd.notesnook.web-clip";
|
export function isDocument(mime: string) {
|
||||||
|
|
||||||
export function isDocument(mime) {
|
|
||||||
return DocumentMimeTypes.some((a) => a.startsWith(mime));
|
return DocumentMimeTypes.some((a) => a.startsWith(mime));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isWebClip(mime) {
|
export const WebClipMimeType = "application/vnd.notesnook.web-clip";
|
||||||
|
export function isWebClip(mime: string) {
|
||||||
return mime === WebClipMimeType;
|
return mime === WebClipMimeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isImage(mime) {
|
export function isImage(mime: string) {
|
||||||
return mime.startsWith("image/");
|
return mime.startsWith("image/");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isVideo(mime) {
|
export function isVideo(mime: string) {
|
||||||
return mime.startsWith("video/");
|
return mime.startsWith("video/");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isAudio(mime) {
|
export function isAudio(mime: string) {
|
||||||
return mime.startsWith("audio/");
|
return mime.startsWith("audio/");
|
||||||
}
|
}
|
||||||
@@ -17,20 +17,13 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function extractHostname(url) {
|
export function extractHostname(url: string) {
|
||||||
var hostname;
|
|
||||||
//find & remove protocol (http, ftp, etc.) and get hostname
|
//find & remove protocol (http, ftp, etc.) and get hostname
|
||||||
|
const hostname: string =
|
||||||
if (url.indexOf("//") > -1) {
|
url.indexOf("//") > -1 ? url.split("/")[2] : url.split("/")[0];
|
||||||
hostname = url.split("/")[2];
|
|
||||||
} else {
|
|
||||||
hostname = url.split("/")[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
//find & remove port number
|
//find & remove port number
|
||||||
// hostname = hostname.split(":")[0];
|
// hostname = hostname.split(":")[0];
|
||||||
//find & remove "?"
|
//find & remove "?"
|
||||||
hostname = hostname.split("?")[0];
|
return hostname.split("?")[0];
|
||||||
|
|
||||||
return hostname;
|
|
||||||
}
|
}
|
||||||
@@ -22,28 +22,43 @@ import { logger } from "../logger";
|
|||||||
import { getServerNameFromHost } from "./constants";
|
import { getServerNameFromHost } from "./constants";
|
||||||
import { extractHostname } from "./hostname";
|
import { extractHostname } from "./hostname";
|
||||||
|
|
||||||
function get(url, token) {
|
type ContentType = "application/json" | "application/x-www-form-urlencoded";
|
||||||
return request(url, token, "GET");
|
type RequestBody = Record<string, string | number | boolean | undefined> | null;
|
||||||
|
type JsonRequestBody = Record<string, unknown> | null;
|
||||||
|
function get(url: string, token?: string) {
|
||||||
|
return request(url, "GET", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteRequest(url, token) {
|
function deleteRequest(url: string, token?: string) {
|
||||||
return request(url, token, "DELETE");
|
return request(url, "DELETE", token);
|
||||||
}
|
}
|
||||||
|
|
||||||
function patch(url, data, token) {
|
function patch(url: string, data: RequestBody, token?: string) {
|
||||||
return bodyRequest(url, data, token, "PATCH");
|
return bodyRequest(url, transformFormData(data), token, "PATCH");
|
||||||
}
|
}
|
||||||
|
|
||||||
patch.json = function (url, data, token) {
|
patch.json = function (url: string, data: JsonRequestBody, token?: string) {
|
||||||
return bodyRequest(url, data, token, "PATCH", "application/json");
|
return bodyRequest(
|
||||||
|
url,
|
||||||
|
transformJson(data),
|
||||||
|
token,
|
||||||
|
"PATCH",
|
||||||
|
"application/json"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function post(url, data, token) {
|
function post(url: string, data: RequestBody, token?: string) {
|
||||||
return bodyRequest(url, data, token, "POST");
|
return bodyRequest(url, transformFormData(data), token, "POST");
|
||||||
}
|
}
|
||||||
|
|
||||||
post.json = function (url, data, token) {
|
post.json = function (url: string, data: JsonRequestBody, token?: string) {
|
||||||
return bodyRequest(url, data, token, "POST", "application/json");
|
return bodyRequest(
|
||||||
|
url,
|
||||||
|
transformJson(data),
|
||||||
|
token,
|
||||||
|
"POST",
|
||||||
|
"application/json"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -53,84 +68,40 @@ export default {
|
|||||||
patch
|
patch
|
||||||
};
|
};
|
||||||
|
|
||||||
function transformer(data, type) {
|
async function request(url: string, method: "GET" | "DELETE", token?: string) {
|
||||||
if (!data) return;
|
|
||||||
if (type === "application/json") return JSON.stringify(data);
|
|
||||||
else {
|
|
||||||
return Object.entries(data)
|
|
||||||
.map(([key, value]) =>
|
|
||||||
value ? `${encodeURIComponent(key)}=${encodeURIComponent(value)}` : ""
|
|
||||||
)
|
|
||||||
.join("&");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param {Response} response
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
async function handleResponse(response) {
|
|
||||||
try {
|
|
||||||
const contentType = response.headers.get("content-type");
|
|
||||||
if (contentType && contentType.includes("application/json")) {
|
|
||||||
const json = await response.json();
|
|
||||||
if (response.ok) {
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
throw new RequestError(errorTransformer(json));
|
|
||||||
} else {
|
|
||||||
if (response.status === 429)
|
|
||||||
throw new Error("You are being rate limited.");
|
|
||||||
|
|
||||||
if (response.ok) return await response.text();
|
|
||||||
else if (response.status === 401) {
|
|
||||||
EV.publish(EVENTS.userUnauthorized, response.url);
|
|
||||||
throw new Error("Unauthorized.");
|
|
||||||
} else
|
|
||||||
throw new Error(
|
|
||||||
`Request failed with status code: ${response.status} ${response.statusText}.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger.error(e, "Error while sending request:", { url: response.url });
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function request(url, token, method) {
|
|
||||||
return handleResponse(
|
return handleResponse(
|
||||||
await fetchWrapped(url, {
|
await fetchWrapped(url, {
|
||||||
method,
|
method,
|
||||||
headers: getAuthorizationHeader(token)
|
headers: getHeaders(token)
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bodyRequest(
|
async function bodyRequest(
|
||||||
url,
|
url: string,
|
||||||
data,
|
data: string | undefined,
|
||||||
token,
|
token: string | undefined,
|
||||||
method,
|
method: "POST" | "PATCH" | "PUT",
|
||||||
contentType = "application/x-www-form-urlencoded"
|
contentType: ContentType = "application/x-www-form-urlencoded"
|
||||||
) {
|
) {
|
||||||
return handleResponse(
|
return handleResponse(
|
||||||
await fetchWrapped(url, {
|
await fetchWrapped(url, {
|
||||||
method,
|
method,
|
||||||
body: transformer(data, contentType),
|
body: data,
|
||||||
headers: {
|
headers: {
|
||||||
...getAuthorizationHeader(token),
|
...getHeaders(token),
|
||||||
"Content-Type": contentType
|
"Content-Type": contentType
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAuthorizationHeader(token) {
|
export function errorTransformer(errorJson: {
|
||||||
return token ? { Authorization: "Bearer " + token } : {};
|
error?: string;
|
||||||
}
|
errors?: string[];
|
||||||
|
error_description?: string;
|
||||||
export function errorTransformer(errorJson) {
|
data?: string;
|
||||||
|
}) {
|
||||||
let errorMessage = "Unknown error.";
|
let errorMessage = "Unknown error.";
|
||||||
let errorCode = "unknown";
|
let errorCode = "unknown";
|
||||||
|
|
||||||
@@ -156,8 +127,8 @@ export function errorTransformer(errorJson) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
errorMessage = error_description || error || "An unknown error occurred.";
|
errorMessage = error_description || error || errorMessage;
|
||||||
errorCode = error;
|
errorCode = error || errorCode;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,12 +139,7 @@ export function errorTransformer(errorJson) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async function fetchWrapped(input: string, init: RequestInit) {
|
||||||
*
|
|
||||||
* @param {RequestInfo} input
|
|
||||||
* @param {RequestInit} init
|
|
||||||
*/
|
|
||||||
async function fetchWrapped(input, init) {
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(input, init);
|
const response = await fetch(input, init);
|
||||||
return response;
|
return response;
|
||||||
@@ -182,17 +148,73 @@ async function fetchWrapped(input, init) {
|
|||||||
const serverName = getServerNameFromHost(host);
|
const serverName = getServerNameFromHost(host);
|
||||||
if (serverName)
|
if (serverName)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`${serverName} is not responding. Please check your internet connection. If the problem persists, feel free email us at support@streetwriters.co. (Reference error: ${e.message})`
|
`${serverName} is not responding. Please check your internet connection. If the problem persists, feel free email us at support@streetwriters.co. (Reference error: ${
|
||||||
|
(e as Error).message
|
||||||
|
})`
|
||||||
);
|
);
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleResponse(response: Response) {
|
||||||
|
try {
|
||||||
|
const contentType = response.headers.get("content-type");
|
||||||
|
if (contentType && contentType.includes("application/json")) {
|
||||||
|
const json = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
throw new RequestError(errorTransformer(json));
|
||||||
|
} else {
|
||||||
|
if (response.status === 429)
|
||||||
|
throw new Error("You are being rate limited.");
|
||||||
|
|
||||||
|
if (response.ok) return await response.text();
|
||||||
|
else if (response.status === 401) {
|
||||||
|
EV.publish(EVENTS.userUnauthorized, response.url);
|
||||||
|
throw new Error("Unauthorized.");
|
||||||
|
} else
|
||||||
|
throw new Error(
|
||||||
|
`Request failed with status code: ${response.status} ${response.statusText}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e as Error, "Error while sending request:", {
|
||||||
|
url: response.url
|
||||||
|
});
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class RequestError extends Error {
|
export class RequestError extends Error {
|
||||||
constructor(error) {
|
code: string;
|
||||||
|
data: unknown;
|
||||||
|
constructor(error: { code: string; data: unknown; description: string }) {
|
||||||
super(error.description);
|
super(error.description);
|
||||||
this.code = error.code;
|
this.code = error.code;
|
||||||
this.data = error.data;
|
this.data = error.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getHeaders(token?: string | null) {
|
||||||
|
return token ? { Authorization: "Bearer " + token } : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformJson(data: JsonRequestBody) {
|
||||||
|
return JSON.stringify(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformFormData(data: RequestBody) {
|
||||||
|
if (data) {
|
||||||
|
return Object.entries(data)
|
||||||
|
.map(([key, value]) =>
|
||||||
|
value
|
||||||
|
? `${encodeURIComponent(key)}=${
|
||||||
|
value ? encodeURIComponent(value) : ""
|
||||||
|
}`
|
||||||
|
: ""
|
||||||
|
)
|
||||||
|
.join("&");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,12 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import SparkMD5 from "spark-md5";
|
import SparkMD5 from "spark-md5";
|
||||||
import ObjectID from "./object-id";
|
import { createObjectId } from "./object-id";
|
||||||
|
|
||||||
export function getId(time?: number) {
|
export function getId(time?: number) {
|
||||||
if (time)
|
return createObjectId(time);
|
||||||
return new ObjectID(new ObjectID().generate(time / 1000)).toHexString();
|
|
||||||
return new ObjectID().toHexString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeId(text: string) {
|
export function makeId(text: string) {
|
||||||
|
|||||||
@@ -1,336 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of the Notesnook project (https://notesnook.com/)
|
|
||||||
|
|
||||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Copied from https://github.com/williamkapke/bson-objectid
|
|
||||||
|
|
||||||
import { randomInt } from "./random";
|
|
||||||
|
|
||||||
var MACHINE_ID = randomInt();
|
|
||||||
var index = randomInt();
|
|
||||||
var pid =
|
|
||||||
(typeof process === "undefined" || typeof process.pid !== "number"
|
|
||||||
? randomInt()
|
|
||||||
: process.pid) % 0xffff;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if an object is Buffer
|
|
||||||
*
|
|
||||||
* Author: Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
|
|
||||||
* License: MIT
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
var isBuffer = function (obj) {
|
|
||||||
return !!(
|
|
||||||
obj != null &&
|
|
||||||
obj.constructor &&
|
|
||||||
typeof obj.constructor.isBuffer === "function" &&
|
|
||||||
obj.constructor.isBuffer(obj)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Precomputed hex table enables speedy hex string conversion
|
|
||||||
var hexTable = [];
|
|
||||||
for (var i = 0; i < 256; i++) {
|
|
||||||
hexTable[i] = (i <= 15 ? "0" : "") + i.toString(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regular expression that checks for hex value
|
|
||||||
var checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
|
|
||||||
|
|
||||||
// Lookup tables
|
|
||||||
var decodeLookup = [];
|
|
||||||
i = 0;
|
|
||||||
while (i < 10) decodeLookup[0x30 + i] = i++;
|
|
||||||
while (i < 16) decodeLookup[0x41 - 10 + i] = decodeLookup[0x61 - 10 + i] = i++;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new immutable ObjectID instance
|
|
||||||
*
|
|
||||||
* @class Represents the BSON ObjectID type
|
|
||||||
* @param {String|Number} id Can be a 24 byte hex string, 12 byte binary string or a Number.
|
|
||||||
* @return {Object} instance of ObjectID.
|
|
||||||
*/
|
|
||||||
class ObjectID {
|
|
||||||
constructor(id) {
|
|
||||||
if (!(this instanceof ObjectID)) return new ObjectID(id);
|
|
||||||
if (id && (id instanceof ObjectID || id._bsontype === "ObjectID"))
|
|
||||||
return id;
|
|
||||||
|
|
||||||
this._bsontype = "ObjectID";
|
|
||||||
|
|
||||||
// The most common usecase (blank id, new objectId instance)
|
|
||||||
if (id == null || typeof id === "number") {
|
|
||||||
// Generate a new id
|
|
||||||
this.id = this.generate(id);
|
|
||||||
// Return the object
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the passed in id is valid
|
|
||||||
var valid = ObjectID.isValid(id);
|
|
||||||
|
|
||||||
// Throw an error if it's not a valid setup
|
|
||||||
if (!valid && id != null) {
|
|
||||||
throw new Error(
|
|
||||||
"Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"
|
|
||||||
);
|
|
||||||
} else if (valid && typeof id === "string" && id.length === 24) {
|
|
||||||
return ObjectID.createFromHexString(id);
|
|
||||||
} else if (id != null && id.length === 12) {
|
|
||||||
// assume 12 byte string
|
|
||||||
this.id = id;
|
|
||||||
} else if (id != null && typeof id.toHexString === "function") {
|
|
||||||
// Duck-typing to support ObjectId from different npm packages
|
|
||||||
return id;
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
"Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates an ObjectID from a second based number, with the rest of the ObjectID zeroed out. Used for comparisons or sorting the ObjectID.
|
|
||||||
*
|
|
||||||
* @param {Number} time an integer number representing a number of seconds.
|
|
||||||
* @return {ObjectID} return the created ObjectID
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
static createFromTime(time) {
|
|
||||||
time = parseInt(time, 10) % 0xffffffff;
|
|
||||||
return new ObjectID(hex(8, time) + "0000000000000000");
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Creates an ObjectID from a hex string representation of an ObjectID.
|
|
||||||
*
|
|
||||||
* @param {String} hexString create a ObjectID from a passed in 24 byte hexstring.
|
|
||||||
* @return {ObjectID} return the created ObjectID
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
static createFromHexString(hexString) {
|
|
||||||
// Throw an error if it's not a valid setup
|
|
||||||
if (
|
|
||||||
typeof hexString === "undefined" ||
|
|
||||||
(hexString != null && hexString.length !== 24)
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
"Argument passed in must be a single String of 12 bytes or a string of 24 hex characters"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate lengths
|
|
||||||
var data = "";
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
while (i < 24) {
|
|
||||||
data += String.fromCharCode(
|
|
||||||
(decodeLookup[hexString.charCodeAt(i++)] << 4) |
|
|
||||||
decodeLookup[hexString.charCodeAt(i++)]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ObjectID(data);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Checks if a value is a valid bson ObjectId
|
|
||||||
*
|
|
||||||
* @param {String} objectid Can be a 24 byte hex string or an instance of ObjectID.
|
|
||||||
* @return {Boolean} return true if the value is a valid bson ObjectID, return false otherwise.
|
|
||||||
* @api public
|
|
||||||
*
|
|
||||||
* THE NATIVE DOCUMENTATION ISN'T CLEAR ON THIS GUY!
|
|
||||||
* http://mongodb.github.io/node-mongodb-native/api-bson-generated/objectid.html#objectid-isvalid
|
|
||||||
*/
|
|
||||||
static isValid(id) {
|
|
||||||
if (id == null) return false;
|
|
||||||
|
|
||||||
if (typeof id === "number") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof id === "string") {
|
|
||||||
return (
|
|
||||||
id.length === 12 || (id.length === 24 && checkForHexRegExp.test(id))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id instanceof ObjectID) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBuffer(id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Duck-Typing detection of ObjectId like objects
|
|
||||||
if (
|
|
||||||
typeof id.toHexString === "function" &&
|
|
||||||
(id.id instanceof Buffer || typeof id.id === "string")
|
|
||||||
) {
|
|
||||||
return (
|
|
||||||
id.id.length === 12 ||
|
|
||||||
(id.id.length === 24 && checkForHexRegExp.test(id.id))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Return the ObjectID id as a 24 byte hex string representation
|
|
||||||
*
|
|
||||||
* @return {String} return the 24 byte hex string representation.
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
toHexString() {
|
|
||||||
if (!this.id || !this.id.length) {
|
|
||||||
throw new Error(
|
|
||||||
"invalid ObjectId, ObjectId.id must be either a string or a Buffer, but is [" +
|
|
||||||
JSON.stringify(this.id) +
|
|
||||||
"]"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.id.length === 24) {
|
|
||||||
return this.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBuffer(this.id)) {
|
|
||||||
return this.id.toString("hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
var hexString = "";
|
|
||||||
for (var i = 0; i < this.id.length; i++) {
|
|
||||||
hexString += hexTable[this.id.charCodeAt(i)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return hexString;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Compares the equality of this ObjectID with `otherID`.
|
|
||||||
*
|
|
||||||
* @param {Object} otherId ObjectID instance to compare against.
|
|
||||||
* @return {Boolean} the result of comparing two ObjectID's
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
equals(otherId) {
|
|
||||||
if (otherId instanceof ObjectID) {
|
|
||||||
return this.toString() === otherId.toString();
|
|
||||||
} else if (
|
|
||||||
typeof otherId === "string" &&
|
|
||||||
ObjectID.isValid(otherId) &&
|
|
||||||
otherId.length === 12 &&
|
|
||||||
isBuffer(this.id)
|
|
||||||
) {
|
|
||||||
return otherId === this.id.toString("binary");
|
|
||||||
} else if (
|
|
||||||
typeof otherId === "string" &&
|
|
||||||
ObjectID.isValid(otherId) &&
|
|
||||||
otherId.length === 24
|
|
||||||
) {
|
|
||||||
return otherId.toLowerCase() === this.toHexString();
|
|
||||||
} else if (
|
|
||||||
typeof otherId === "string" &&
|
|
||||||
ObjectID.isValid(otherId) &&
|
|
||||||
otherId.length === 12
|
|
||||||
) {
|
|
||||||
return otherId === this.id;
|
|
||||||
} else if (
|
|
||||||
otherId != null &&
|
|
||||||
(otherId instanceof ObjectID || otherId.toHexString)
|
|
||||||
) {
|
|
||||||
return otherId.toHexString() === this.toHexString();
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Returns the generation date (accurate up to the second) that this ID was generated.
|
|
||||||
*
|
|
||||||
* @return {Date} the generation date
|
|
||||||
* @api public
|
|
||||||
*/
|
|
||||||
getTimestamp() {
|
|
||||||
var timestamp = new Date();
|
|
||||||
var time;
|
|
||||||
if (isBuffer(this.id)) {
|
|
||||||
time =
|
|
||||||
this.id[3] |
|
|
||||||
(this.id[2] << 8) |
|
|
||||||
(this.id[1] << 16) |
|
|
||||||
(this.id[0] << 24);
|
|
||||||
} else {
|
|
||||||
time =
|
|
||||||
this.id.charCodeAt(3) |
|
|
||||||
(this.id.charCodeAt(2) << 8) |
|
|
||||||
(this.id.charCodeAt(1) << 16) |
|
|
||||||
(this.id.charCodeAt(0) << 24);
|
|
||||||
}
|
|
||||||
timestamp.setTime(Math.floor(time) * 1000);
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Generate a 12 byte id buffer used in ObjectID's
|
|
||||||
*
|
|
||||||
* @method
|
|
||||||
* @param {number} [time] optional parameter allowing to pass in a second based timestamp.
|
|
||||||
* @return {string} return the 12 byte id buffer string.
|
|
||||||
*/
|
|
||||||
generate(time) {
|
|
||||||
if ("number" !== typeof time) {
|
|
||||||
time = ~~(Date.now() / 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
//keep it in the ring!
|
|
||||||
time = parseInt(time, 10) % 0xffffffff;
|
|
||||||
|
|
||||||
var inc = next();
|
|
||||||
|
|
||||||
return String.fromCharCode(
|
|
||||||
(time >> 24) & 0xff,
|
|
||||||
(time >> 16) & 0xff,
|
|
||||||
(time >> 8) & 0xff,
|
|
||||||
time & 0xff,
|
|
||||||
(MACHINE_ID >> 16) & 0xff,
|
|
||||||
(MACHINE_ID >> 8) & 0xff,
|
|
||||||
MACHINE_ID & 0xff,
|
|
||||||
(pid >> 8) & 0xff,
|
|
||||||
pid & 0xff,
|
|
||||||
(inc >> 16) & 0xff,
|
|
||||||
(inc >> 8) & 0xff,
|
|
||||||
inc & 0xff
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return this.toHexString();
|
|
||||||
}
|
|
||||||
toString() {
|
|
||||||
return this.toHexString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default ObjectID;
|
|
||||||
|
|
||||||
function next() {
|
|
||||||
return (index = (index + 1) % 0xffffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hex(length, n) {
|
|
||||||
n = n.toString(16);
|
|
||||||
return n.length === length ? n : "00000000".substring(n.length, length) + n;
|
|
||||||
}
|
|
||||||
51
packages/core/src/utils/object-id.ts
Normal file
51
packages/core/src/utils/object-id.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
This file is part of the Notesnook project (https://notesnook.com/)
|
||||||
|
|
||||||
|
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BufferPool } from "./buffer-pool";
|
||||||
|
import { randomBytes, randomInt } from "./random";
|
||||||
|
|
||||||
|
const PROCESS_UNIQUE = randomBytes(5);
|
||||||
|
let index = ~~(randomInt() * 0xffffff);
|
||||||
|
|
||||||
|
const objectIdPool = new BufferPool(12);
|
||||||
|
export function createObjectId(date = Date.now()): string {
|
||||||
|
const buffer = objectIdPool.alloc();
|
||||||
|
index = (index + 1) % 0xffffff;
|
||||||
|
|
||||||
|
const time = ~~(date / 1000);
|
||||||
|
|
||||||
|
// 4-byte timestamp
|
||||||
|
new DataView(buffer.buffer, 0, 4).setUint32(0, time);
|
||||||
|
|
||||||
|
// 5-byte process unique
|
||||||
|
buffer[4] = PROCESS_UNIQUE[0];
|
||||||
|
buffer[5] = PROCESS_UNIQUE[1];
|
||||||
|
buffer[6] = PROCESS_UNIQUE[2];
|
||||||
|
buffer[7] = PROCESS_UNIQUE[3];
|
||||||
|
buffer[8] = PROCESS_UNIQUE[4];
|
||||||
|
|
||||||
|
// 3-byte counter
|
||||||
|
buffer[11] = index & 0xff;
|
||||||
|
buffer[10] = (index >> 8) & 0xff;
|
||||||
|
buffer[9] = (index >> 16) & 0xff;
|
||||||
|
|
||||||
|
const objectId = buffer.toString("hex");
|
||||||
|
objectIdPool.free(buffer);
|
||||||
|
return objectId;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user