Update dependencies

This commit is contained in:
Zaid Abdulla committed 2022-08-22 09:44:25 -07:00
1 parent 2789961596
commit 25ea0c8b64
14 files changed
+3445 -3943

No files matched your search

+3 -2
View File
@@ -28,6 +28,7 @@ import commonjs from "@rollup/plugin-commonjs";
import postcss from "postcss";
import postcssImport from "postcss-import";
import postcssPresetEnv from "postcss-preset-env";
import postcssColorMod from "postcss-color-mod-function";
import cssnano from "cssnano";
import { optimize } from "svgo";
import * as xmlParser from "../lib/xml-parser";
@@ -185,10 +186,10 @@ async function generateCss(): Promise<void> {
stage: 3,
features: {
"nesting-rules": true,
"color-mod-function": true,
},
}),
cssnano,
postcssColorMod,
...(MODE === "production" ? [cssnano] : []),
]).process(cssIn, { from: cssInPath, to: cssOutPath });
fs.writeFileSync(cssOutPath, cssOut.css);
}
+9 -12
View File
@@ -20,11 +20,12 @@
import { Collection } from "mongodb";
import { onConnect } from "./db";
import * as config from "./config";
import { Cache } from "./mongodb-types";
const CLOCK_SKEW_TOLERANCE = 30000;
const MAX_CACHE_TTL = +config.get("MAX_CACHE_TTL");
let cacheCollection: Collection;
let cacheCollection: Collection<Cache>;
onConnect(async (db) => {
cacheCollection = db.collection("cache");
@@ -33,8 +34,7 @@ onConnect(async (db) => {
export async function get(key: string): Promise<string> {
const res = await cacheCollection.findOne({ _id: key });
if (res) return res["value"];
return null;
return res?.value;
}
export async function del(key: string): Promise<void> {
@@ -52,15 +52,14 @@ export async function set(
);
await cacheCollection.replaceOne(
{ _id: key },
{ _id: key, value, expire, timestamp },
{ value, expire, timestamp },
{ upsert: true }
);
}
export async function pop(key: string): Promise<any> {
export async function pop(key: string): Promise<string> {
const res = await cacheCollection.findOneAndDelete({ _id: key });
if (res?.["value"]) return res["value"]["value"];
return null;
return res.value?.value;
}
export async function acquireLock(
@@ -72,18 +71,16 @@ export async function acquireLock(
try {
const now = Date.now();
const r = await cacheCollection.findOneAndUpdate(
{ _id: lockName, token },
{ _id: lockName, value: token },
{
$set: {
value: token,
expire: new Date(now + ttl + CLOCK_SKEW_TOLERANCE),
},
$currentDate: { timestamp: true as unknown as Date },
$currentDate: { timestamp: true },
},
{ upsert: true, returnDocument: "after" }
);
const v = r.value;
if (Math.abs(v["timestamp"].getTime() - now) > CLOCK_SKEW_TOLERANCE)
if (Math.abs(r.value.timestamp.getTime() - now) > CLOCK_SKEW_TOLERANCE)
throw new Error("Database clock skew too great");
} catch (err) {
if (err.code !== 11000) throw err;
+1 -1
View File
@@ -536,7 +536,7 @@ async function applyPresets(sessionContext: SessionContext): Promise<void> {
session.configContextCallback(sessionContext, e)
)
),
]);
]) as [string, ...Expression[]][];
if (blackList[p.channel] === 2) {
appendProvisionsToFaults[p.channel] = (
appendProvisionsToFaults[p.channel] || []
+85 -39
View File
@@ -30,19 +30,20 @@ import {
Expression,
} from "./types";
import Path from "./common/path";
import * as MongoTypes from "./mongodb-types";
export let tasksCollection: Collection,
export let tasksCollection: Collection<MongoTypes.Task>,
devicesCollection: Collection,
presetsCollection: Collection,
objectsCollection: Collection,
provisionsCollection: Collection,
virtualParametersCollection: Collection,
faultsCollection: Collection,
faultsCollection: Collection<MongoTypes.Fault>,
filesCollection: Collection,
operationsCollection: Collection,
operationsCollection: Collection<MongoTypes.Operation>,
permissionsCollection: Collection,
usersCollection: Collection,
configCollection: Collection;
configCollection: Collection<MongoTypes.Config>;
let clientPromise: Promise<MongoClient>;
@@ -643,12 +644,16 @@ export async function getFaults(
const faults: { [channel: string]: SessionFault } = {};
for (const r of res) {
const channel = r._id.slice(deviceId.length + 1);
delete r._id;
delete r.channel;
delete r.device;
r.timestamp = +r.timestamp;
r.provisions = JSON.parse(r.provisions);
faults[channel] = r as SessionFault;
const fault: SessionFault = {
code: r.code,
message: r.message,
...(r.detail && { detail: r.detail }),
timestamp: +r.timestamp,
provisions: JSON.parse(r.provisions),
retries: r.retries,
...(r.expiry && { expiry: +r.expiry }),
};
faults[channel] = fault;
}
return faults;
@@ -660,12 +665,18 @@ export async function saveFault(
fault: SessionFault
): Promise<void> {
const id = `${deviceId}:${channel}`;
const f = Object.assign({}, fault) as Record<string, any>;
f["_id"] = id;
f["device"] = deviceId;
f["channel"] = channel;
f["timestamp"] = new Date(fault.timestamp);
f["provisions"] = JSON.stringify(fault.provisions);
const f: MongoTypes.Fault = {
_id: id,
device: deviceId,
channel: channel,
timestamp: new Date(fault.timestamp),
code: fault.code,
message: fault.message,
...(fault.detail && { detail: fault.detail }),
retries: fault.retries,
...(fault.expiry && { expiry: new Date(fault.expiry) }),
provisions: JSON.stringify(fault.provisions),
};
await faultsCollection.replaceOne({ _id: id }, f, { upsert: true });
}
@@ -683,20 +694,47 @@ export async function getDueTasks(
const cur = tasksCollection.find({ device: deviceId }).sort({ timestamp: 1 });
const tasks = [] as Task[];
for await (const task of cur) {
if (task.timestamp) task.timestamp = +task.timestamp;
if (task.expiry) task.expiry = +task.expiry;
if (task.timestamp >= timestamp) return [tasks, +task.timestamp];
task._id = String(task._id);
for await (const t of cur) {
if (+t.timestamp >= timestamp) return [tasks, +t.timestamp];
const task: Task = {
_id: t._id.toString(),
name: t.name,
...(t.timestamp && { timestamp: +t.timestamp }),
...(t.expiry && { expiry: +t.expiry }),
...(t.name === "getParameterValues" && {
parameterNames: t.parameterNames,
}),
...(t.name === "setParameterValues" && {
parameterValues: t.parameterValues,
}),
...(t.name === "refreshObject" && {
objectName: t.objectName,
}),
...(t.name === "download" && {
fileType: t.fileType,
fileName: t.fileName,
targetFileName: t.targetFileName,
}),
...(t.name === "addObject" && {
objectName: t.objectName,
parameterValues: t.parameterValues,
}),
...(t.name === "deleteObject" && {
objectName: t.objectName,
}),
...(t.name === "provisions" && {
provisions: t.provisions,
}),
};
tasks.push(task as Task);
tasks.push(task);
// For API compatibility
if (task.name === "download" && task.file) {
if (task.name === "download" && t["file"]) {
let q;
if (ObjectId.isValid(task.file))
q = { _id: { $in: [task.file, new ObjectId(task.file)] } };
else q = { _id: task.file };
if (ObjectId.isValid(t["file"]))
q = { _id: { $in: [t["file"], new ObjectId(t["file"])] } };
else q = { _id: t["file"] };
const res = await filesCollection.find(q).toArray();
@@ -730,17 +768,22 @@ export async function getOperations(
const operations: { [commandKey: string]: Operation } = {};
for (const r of res) {
const commandKey = r._id.slice(deviceId.length + 1);
delete r._id;
// Workaround for a bug in v1.2.1 where operation object is saved without deserialization
if (typeof r.provisions !== "string") {
operations[commandKey] = r as Operation;
delete r._id;
operations[commandKey] = r as unknown as Operation;
continue;
}
r.timestamp = +r.timestamp;
if (r.args) r.args = JSON.parse(r.args);
r.provisions = JSON.parse(r.provisions);
r.retries = JSON.parse(r.retries);
operations[commandKey] = r as Operation;
const operation: Operation = {
name: r.name,
timestamp: +r.timestamp,
channels:
typeof r.channels === "string" ? JSON.parse(r.channels) : r.channels,
retries: JSON.parse(r.retries),
provisions: JSON.parse(r.provisions),
...(r.args && { args: JSON.parse(r.args) }),
};
operations[commandKey] = operation;
}
return operations;
}
@@ -751,12 +794,15 @@ export async function saveOperation(
operation: Operation
): Promise<void> {
const id = `${deviceId}:${commandKey}`;
const o = Object.assign({}, operation) as Record<string, any>;
o["_id"] = id;
o["timestamp"] = new Date(operation.timestamp);
o["provisions"] = JSON.stringify(operation.provisions);
o["retries"] = JSON.stringify(operation.retries);
o["args"] = JSON.stringify(operation.args);
const o: MongoTypes.Operation = {
_id: id,
name: operation.name,
timestamp: new Date(operation.timestamp),
channels: JSON.stringify(operation.channels),
provisions: JSON.stringify(operation.provisions),
retries: JSON.stringify(operation.retries),
args: JSON.stringify(operation.args),
};
await operationsCollection.replaceOne({ _id: id }, o, {
upsert: true,
});
+107
View File
@@ -0,0 +1,107 @@
import { ObjectId } from "mongodb";
import { Expression, FaultStruct } from "./types";
export interface Fault {
_id: string;
device: string;
channel: string;
timestamp: Date;
provisions: string;
retries: number;
code: string;
message: string;
detail?:
| FaultStruct
| {
name: string;
message: string;
stack?: string;
};
expiry?: Date;
}
interface TaskBase {
_id: ObjectId;
timestamp?: Date;
expiry?: Date;
name: string;
}
interface TaskGetParameterValues extends TaskBase {
name: "getParameterValues";
parameterNames: string[];
}
interface TaskSetParameterValues extends TaskBase {
name: "setParameterValues";
parameterValues: [string, string | number | boolean, string?][];
}
interface TaskRefreshObject extends TaskBase {
name: "refreshObject";
objectName: string;
}
interface TaskReboot extends TaskBase {
name: "reboot";
}
interface TaskFactoryReset extends TaskBase {
name: "factoryReset";
}
interface TaskDownload extends TaskBase {
name: "download";
fileType: string;
fileName: string;
targetFileName?: string;
}
interface TaskAddObject extends TaskBase {
name: "addObject";
objectName: string;
parameterValues: [string, string | number | boolean, string?][];
}
interface TaskDeleteObject extends TaskBase {
name: "deleteObject";
objectName: string;
}
interface TaskProvisions extends TaskBase {
name: "provisions";
provisions?: [string, ...Expression[]][];
}
export type Task =
| TaskGetParameterValues
| TaskSetParameterValues
| TaskRefreshObject
| TaskReboot
| TaskFactoryReset
| TaskDownload
| TaskAddObject
| TaskDeleteObject
| TaskProvisions;
export interface Operation {
_id: string;
name: string;
timestamp: Date;
channels: string;
retries: string;
provisions: string;
args: string;
}
export interface Config {
_id: string;
value: string;
}
export interface Cache {
_id: string;
value: string;
timestamp: Date;
expire: Date;
}
+1 -1
View File
@@ -845,7 +845,7 @@ export function listener(
cur.sort(sort);
}
cur.count((err, total) => {
collection.countDocuments(q, (err, total) => {
if (err) return void throwError(err);
response.writeHead(200, {
+1 -1
View File
@@ -504,7 +504,7 @@ export async function timeoutOperations(
export function addProvisions(
sessionContext: SessionContext,
channel: string,
provisions: (string | number | boolean)[][]
provisions: [string, ...Expression[]][]
): void {
// Multiply by two because every iteration is two
// phases: read and update
+1 -1
View File
@@ -180,7 +180,7 @@ export interface Task {
fileName?: string;
targetFileName?: string;
expiry?: number;
provisions?: (string | number | boolean)[][];
provisions?: [string, ...Expression[]][];
}
export interface Operation {
+15 -19
View File
@@ -115,30 +115,31 @@ export function query(
cursor.toArray((err, docs) => {
if (err) return reject(err);
if (resource === "devices")
docs = docs.map((d) => mongodbFunctions.flattenDevice(d));
return resolve(docs.map((d) => mongodbFunctions.flattenDevice(d)));
else if (resource === "faults")
docs = docs.map((d) => mongodbFunctions.flattenFault(d));
return resolve(docs.map((d) => mongodbFunctions.flattenFault(d)));
else if (resource === "tasks")
docs = docs.map((d) => mongodbFunctions.flattenTask(d));
return resolve(docs.map((d) => mongodbFunctions.flattenTask(d)));
else if (resource === "presets")
docs = docs.map((d) => mongodbFunctions.flattenPreset(d));
return resolve(docs.map((d) => mongodbFunctions.flattenPreset(d)));
else if (resource === "files")
docs = docs.map((d) => mongodbFunctions.flattenFile(d));
return resolve(docs.map((d) => mongodbFunctions.flattenFile(d)));
return resolve(docs);
});
} else {
cursor.forEach(
(doc) => {
if (resource === "devices") doc = mongodbFunctions.flattenDevice(doc);
if (resource === "devices")
callback(mongodbFunctions.flattenDevice(doc));
else if (resource === "faults")
doc = mongodbFunctions.flattenFault(doc);
callback(mongodbFunctions.flattenFault(doc));
else if (resource === "tasks")
doc = mongodbFunctions.flattenTask(doc);
callback(mongodbFunctions.flattenTask(doc));
else if (resource === "presets")
doc = mongodbFunctions.flattenPreset(doc);
callback(mongodbFunctions.flattenPreset(doc));
else if (resource === "files")
doc = mongodbFunctions.flattenFile(doc);
callback(doc);
callback(mongodbFunctions.flattenFile(doc));
else callback(doc);
},
(err) => {
if (err) reject(err);
@@ -150,7 +151,8 @@ export function query(
}
export function count(resource: string, filter: Expression): Promise<number> {
let q;
const collection = db.collection(RESOURCE_COLLECTION[resource] || resource);
let q: Parameters<typeof collection.countDocuments>[0];
filter = expression.evaluate(filter, null, Date.now());
filter = minimize(filter, true);
@@ -166,13 +168,7 @@ export function count(resource: string, filter: Expression): Promise<number> {
return Promise.resolve(0);
}
return new Promise((resolve, reject) => {
const collection = db.collection(RESOURCE_COLLECTION[resource] || resource);
collection.find(q).count((err, c) => {
if (err) reject(err);
else resolve(c);
});
});
return collection.countDocuments(q);
}
export async function updateDeviceTags(
+3190 -3836
View File
File diff suppressed because it is too large. Load diff
+28 -27
View File
@@ -28,46 +28,47 @@
"koa-bodyparser": "^4.3.0",
"koa-compress": "^5.1.0",
"koa-jwt": "^4.0.3",
"koa-router": "^10.1.1",
"koa-router": "^12.0.0",
"koa-static": "^5.0.0",
"mongodb": "^4.1.3",
"parsimmon": "^1.18.0",
"mongodb": "^4.9.0",
"parsimmon": "^1.18.1",
"seedrandom": "^3.0.5"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-commonjs": "^22.0.2",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.0.6",
"@rollup/plugin-typescript": "^8.3.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-typescript": "^8.3.4",
"@types/codemirror": "^5.60.5",
"@types/jsonwebtoken": "^8.5.5",
"@types/koa": "^2.13.4",
"@types/jsonwebtoken": "^8.5.8",
"@types/koa": "^2.13.5",
"@types/koa-compress": "^4.0.3",
"@types/koa-router": "^7.4.4",
"@types/koa-static": "^4.0.2",
"@types/mithril": "^2.0.8",
"@types/node": "^16.11.6",
"@types/mithril": "^2.0.11",
"@types/node": "^18.7.9",
"@types/parsimmon": "^1.10.6",
"@types/seedrandom": "^3.0.1",
"@typescript-eslint/eslint-plugin": "^5.2.0",
"@typescript-eslint/parser": "^5.2.0",
"@types/seedrandom": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^5.33.1",
"@typescript-eslint/parser": "^5.33.1",
"ava": "^3.15.0",
"codemirror": "^5.63.3",
"cssnano": "^5.0.8",
"eslint": "^8.1.0",
"eslint-config-prettier": "^8.3.0",
"codemirror": "^5.65.8",
"cssnano": "^5.1.13",
"eslint": "^8.22.0",
"eslint-config-prettier": "^8.5.0",
"esm": "^3.2.25",
"mithril": "^2.0.4",
"postcss": "^8.3.11",
"postcss-import": "^14.0.2",
"postcss-preset-env": "^6.7.0",
"prettier": "^2.4.1",
"rollup": "^2.58.3",
"mithril": "^2.2.2",
"postcss": "^8.4.16",
"postcss-color-mod-function": "^3.0.3",
"postcss-import": "^14.1.0",
"postcss-preset-env": "^7.8.0",
"prettier": "^2.7.1",
"rollup": "^2.78.1",
"rollup-plugin-terser": "^7.0.2",
"sql.js": "^1.6.2",
"svgo": "^2.7.0",
"ts-node": "^10.4.0",
"typescript": "^4.4.4",
"sql.js": "^1.7.0",
"svgo": "^2.8.0",
"ts-node": "^10.9.1",
"typescript": "^4.7.4",
"yaml": "^1.10.2"
},
"engines": {
+1 -1
View File
@@ -1,7 +1,7 @@
import ava, { TestInterface } from "ava";
import { minimize, unionDiff } from "../lib/common/boolean-expression";
import { parse, stringify } from "../lib/common/expression-parser";
import initSqlJs from "sql.js";
import initSqlJs from "sql.js/dist/sql-asm.js";
const test = ava as TestInterface<{
query: (filter: string) => Promise<Set<number>>;
+2 -2
View File
@@ -99,9 +99,9 @@ export default class Autocomplete {
if (this.visible) this.hide();
return;
}
this.container.style.left = `${domRect.left + window.pageXOffset}px`;
this.container.style.left = `${domRect.left + window.scrollX}px`;
this.container.style.width = `${domRect.width}px`;
this.container.style.top = `${domRect.bottom + window.pageYOffset}px`;
this.container.style.top = `${domRect.bottom + window.scrollY}px`;
}
private hide(): void {
+1 -1
View File
@@ -648,7 +648,7 @@ span.na {
span.tag {
display: inline-block;
background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="190" height="16"><path d="M6.59 15.42l-6-6a2 2 0 0 1 0-2.83l6-6A2 2 0 0 1 8 0h180a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H8a2 2 0 0 1-1.41-.58zM4.35 6.59A2 2 0 1 0 7.17 9.4 2 2 0 0 0 4.35 6.6z" fill="#e8d5b2"></path></svg>');
background-image: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" width="190" height="16"><path d="M6.59 15.42l-6-6a2 2 0 0 1 0-2.83l6-6A2 2 0 0 1 8 0h180a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H8a2 2 0 0 1-1.41-.58zM4.35 6.59A2 2 0 1 0 7.17 9.4 2 2 0 0 0 4.35 6.6z" fill="%23e8d5b2"></path></svg>');
background-size: cover;
background-repeat: no-repeat;
border-radius: 0.2em;