Update dependencies
This commit is contained in:
1 parent
2789961596
commit
25ea0c8b64
14 files changed
+3449
-3947
No files matched your search
+3
-2
@@ -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
@@ -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
@@ -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] || []
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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(
|
||||
|
||||
Generated
+3194
-3840
File diff suppressed because it is too large.
Load diff
+28
-27
@@ -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,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>>;
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user