Merge branch 'master' of https://github.com/rishighan/threetwo-import-service
This commit is contained in:
@@ -12,23 +12,23 @@ const ComicSchema = mongoose.Schema({
|
|||||||
userAddedMetadata: {
|
userAddedMetadata: {
|
||||||
tags: [],
|
tags: [],
|
||||||
},
|
},
|
||||||
comicInfo: {
|
|
||||||
blackAndWhite: String,
|
|
||||||
characters: [String],
|
|
||||||
count: String,
|
|
||||||
genre: String,
|
|
||||||
manga: String,
|
|
||||||
month: String,
|
|
||||||
number: String,
|
|
||||||
pageCount: String,
|
|
||||||
pages: [],
|
|
||||||
publisher: String,
|
|
||||||
summary: String,
|
|
||||||
title: String,
|
|
||||||
writer: String,
|
|
||||||
year: String,
|
|
||||||
},
|
|
||||||
sourcedMetadata: {
|
sourcedMetadata: {
|
||||||
|
comicInfo: {
|
||||||
|
blackAndWhite: String,
|
||||||
|
characters: [String],
|
||||||
|
count: String,
|
||||||
|
genre: String,
|
||||||
|
manga: String,
|
||||||
|
month: String,
|
||||||
|
number: String,
|
||||||
|
pageCount: String,
|
||||||
|
pages: [],
|
||||||
|
publisher: String,
|
||||||
|
summary: String,
|
||||||
|
title: String,
|
||||||
|
writer: String,
|
||||||
|
year: String,
|
||||||
|
},
|
||||||
comicvine: {},
|
comicvine: {},
|
||||||
shortboxed: {},
|
shortboxed: {},
|
||||||
gcd: {},
|
gcd: {},
|
||||||
@@ -37,7 +37,11 @@ const ComicSchema = mongoose.Schema({
|
|||||||
name: String,
|
name: String,
|
||||||
path: String,
|
path: String,
|
||||||
fileSize: Number,
|
fileSize: Number,
|
||||||
|
extension: String,
|
||||||
containedIn: String,
|
containedIn: String,
|
||||||
|
calibreMetadata :{
|
||||||
|
coverWriteResult: String,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
acquisition: {
|
acquisition: {
|
||||||
wanted: Boolean,
|
wanted: Boolean,
|
||||||
@@ -53,7 +57,7 @@ const ComicSchema = mongoose.Schema({
|
|||||||
sourceApplication: String,
|
sourceApplication: String,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
}, { timestamps: true});
|
||||||
|
|
||||||
ComicSchema.plugin(paginate);
|
ComicSchema.plugin(paginate);
|
||||||
const Comic = mongoose.model("Comic", ComicSchema);
|
const Comic = mongoose.model("Comic", ComicSchema);
|
||||||
|
|||||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -50,7 +50,7 @@
|
|||||||
"jest": "^25.1.0",
|
"jest": "^25.1.0",
|
||||||
"jest-cli": "^25.1.0",
|
"jest-cli": "^25.1.0",
|
||||||
"moleculer-repl": "^0.6.2",
|
"moleculer-repl": "^0.6.2",
|
||||||
"threetwo-ui-typings": "^1.0.1-0",
|
"threetwo-ui-typings": "^1.0.2-0",
|
||||||
"ts-jest": "^25.3.0",
|
"ts-jest": "^25.3.0",
|
||||||
"ts-node": "^8.8.1"
|
"ts-node": "^8.8.1"
|
||||||
},
|
},
|
||||||
@@ -11303,9 +11303,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/threetwo-ui-typings": {
|
"node_modules/threetwo-ui-typings": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/threetwo-ui-typings/-/threetwo-ui-typings-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/threetwo-ui-typings/-/threetwo-ui-typings-1.0.2.tgz",
|
||||||
"integrity": "sha512-g5FWa069AT1WfUHHEVFOQ1q6cfK+9UOzewfKblheuDBSsGN/e89MJMEVfuInBaHgxHM32/P+mNUFBqnBoeRSLQ==",
|
"integrity": "sha512-sK5cb/fApFKseQNgcnGmAnPNxDDXT+dQ/Blei1N4q0mduO3kZfJTnlMYaXjO4FDXLP+jkiwsv0bf7PXm0DgF7g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"typescript": "^4.3.2"
|
"typescript": "^4.3.2"
|
||||||
@@ -21081,9 +21081,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"threetwo-ui-typings": {
|
"threetwo-ui-typings": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/threetwo-ui-typings/-/threetwo-ui-typings-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/threetwo-ui-typings/-/threetwo-ui-typings-1.0.2.tgz",
|
||||||
"integrity": "sha512-g5FWa069AT1WfUHHEVFOQ1q6cfK+9UOzewfKblheuDBSsGN/e89MJMEVfuInBaHgxHM32/P+mNUFBqnBoeRSLQ==",
|
"integrity": "sha512-sK5cb/fApFKseQNgcnGmAnPNxDDXT+dQ/Blei1N4q0mduO3kZfJTnlMYaXjO4FDXLP+jkiwsv0bf7PXm0DgF7g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"typescript": "^4.3.2"
|
"typescript": "^4.3.2"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"jest": "^25.1.0",
|
"jest": "^25.1.0",
|
||||||
"jest-cli": "^25.1.0",
|
"jest-cli": "^25.1.0",
|
||||||
"moleculer-repl": "^0.6.2",
|
"moleculer-repl": "^0.6.2",
|
||||||
"threetwo-ui-typings": "^1.0.1-0",
|
"threetwo-ui-typings": "^1.0.2-0",
|
||||||
"ts-jest": "^25.3.0",
|
"ts-jest": "^25.3.0",
|
||||||
"ts-node": "^8.8.1"
|
"ts-node": "^8.8.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ export default class ApiService extends Service {
|
|||||||
// More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html
|
// More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html
|
||||||
settings: {
|
settings: {
|
||||||
port: process.env.PORT || 3000,
|
port: process.env.PORT || 3000,
|
||||||
|
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: "/api",
|
path: "/api",
|
||||||
@@ -39,7 +38,6 @@ export default class ApiService extends Service {
|
|||||||
use: [],
|
use: [],
|
||||||
mergeParams: true,
|
mergeParams: true,
|
||||||
autoAliases: true,
|
autoAliases: true,
|
||||||
|
|
||||||
aliases: {},
|
aliases: {},
|
||||||
|
|
||||||
// Calling options. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Calling-options
|
// Calling options. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Calling-options
|
||||||
@@ -55,11 +53,7 @@ export default class ApiService extends Service {
|
|||||||
limit: "1MB",
|
limit: "1MB",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// Mapping policy setting. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Mapping-policy
|
|
||||||
mappingPolicy: "all", // Available values: "all", "restrict"
|
mappingPolicy: "all", // Available values: "all", "restrict"
|
||||||
|
|
||||||
// Enable/disable logging
|
|
||||||
logging: true,
|
logging: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -71,15 +65,11 @@ export default class ApiService extends Service {
|
|||||||
use: [ApiGateway.serveStatic("comics")],
|
use: [ApiGateway.serveStatic("comics")],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
// Do not log client side errors (does not log an error response when the error.code is 400<=X<500)
|
|
||||||
log4XXResponses: false,
|
log4XXResponses: false,
|
||||||
// Logging the request parameters. Set to any log level to enable it. E.g. "info"
|
|
||||||
logRequestParams: null,
|
logRequestParams: null,
|
||||||
// Logging the response data. Set to any log level to enable it. E.g. "info"
|
|
||||||
logResponseData: null,
|
logResponseData: null,
|
||||||
assets: {
|
assets: {
|
||||||
folder: "public",
|
folder: "public",
|
||||||
// Options to `server-static` module
|
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -104,7 +94,7 @@ export default class ApiService extends Service {
|
|||||||
this.logger.info("Client connected via websocket!");
|
this.logger.info("Client connected via websocket!");
|
||||||
|
|
||||||
client.on(
|
client.on(
|
||||||
"importComicsInDB",
|
"importComicsToDB",
|
||||||
async ({ action, params, opts }, done) => {
|
async ({ action, params, opts }, done) => {
|
||||||
this.logger.info(
|
this.logger.info(
|
||||||
"Received request from client! Action:",
|
"Received request from client! Action:",
|
||||||
|
|||||||
@@ -8,12 +8,7 @@ import {
|
|||||||
} from "moleculer";
|
} from "moleculer";
|
||||||
import {
|
import {
|
||||||
resizeImage,
|
resizeImage,
|
||||||
calculateLevenshteinDistance,
|
|
||||||
} from "../utils/imagetransformation.utils";
|
} from "../utils/imagetransformation.utils";
|
||||||
import https from "https";
|
|
||||||
import fs from "fs";
|
|
||||||
import path from "path";
|
|
||||||
|
|
||||||
export default class ProductsService extends Service {
|
export default class ProductsService extends Service {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
public constructor(
|
public constructor(
|
||||||
@@ -58,44 +53,6 @@ export default class ProductsService extends Service {
|
|||||||
return { resizeOperationStatus: resizeResult };
|
return { resizeOperationStatus: resizeResult };
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
calculateLevenshteinDistance: {
|
|
||||||
rest: "POST /calculateLevenshteinDistance",
|
|
||||||
params: {},
|
|
||||||
async handler(
|
|
||||||
ctx: Context<{
|
|
||||||
imagePath: string;
|
|
||||||
imagePath2: string;
|
|
||||||
options: {
|
|
||||||
match_id: string,
|
|
||||||
};
|
|
||||||
}>
|
|
||||||
) {
|
|
||||||
const fileName = ctx.params.options.match_id + "_" + path.basename(
|
|
||||||
ctx.params.imagePath
|
|
||||||
);
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
https.get(ctx.params.imagePath2, (response) => {
|
|
||||||
const fileStream = response.pipe(
|
|
||||||
fs.createWriteStream(
|
|
||||||
`./userdata/temporary/${fileName}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
fileStream.on("finish", async () => {
|
|
||||||
const levenshteinDistance = await calculateLevenshteinDistance(
|
|
||||||
ctx.params.imagePath,
|
|
||||||
path.resolve(
|
|
||||||
`./userdata/temporary/${fileName}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
resolve(levenshteinDistance);
|
|
||||||
});
|
|
||||||
|
|
||||||
}).end();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {},
|
methods: {},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
import { isNil } from "lodash";
|
||||||
import {
|
import {
|
||||||
Context,
|
Context,
|
||||||
Service,
|
Service,
|
||||||
@@ -10,6 +11,9 @@ import { DbMixin } from "../mixins/db.mixin";
|
|||||||
import Comic from "../models/comic.model";
|
import Comic from "../models/comic.model";
|
||||||
import { walkFolder } from "../utils/file.utils";
|
import { walkFolder } from "../utils/file.utils";
|
||||||
import { convertXMLToJSON } from "../utils/xml.utils";
|
import { convertXMLToJSON } from "../utils/xml.utils";
|
||||||
|
import https from "https";
|
||||||
|
const ObjectId = require("mongoose").Types.ObjectId;
|
||||||
|
|
||||||
|
|
||||||
export default class ProductsService extends Service {
|
export default class ProductsService extends Service {
|
||||||
public constructor(
|
public constructor(
|
||||||
@@ -74,8 +78,55 @@ export default class ProductsService extends Service {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
getRecentlyImportedComicBooks: {
|
applyComicVineMetadata: {
|
||||||
rest: "POST /getRecentlyImportedComicBooks",
|
rest: "POST /applyComicVineMetadata",
|
||||||
|
params: {},
|
||||||
|
async handler(ctx: Context<{ match: { volume: { api_detail_url: string}, volumeInformation: object}, comicObjectId: string }>) {
|
||||||
|
// 1. find mongo object by id
|
||||||
|
// 2. import payload into sourcedMetadata.comicvine
|
||||||
|
const comicObjectId = new ObjectId(ctx.params.comicObjectId);
|
||||||
|
const matchedResult = ctx.params.match;
|
||||||
|
console.log(matchedResult.volume.api_detail_url);
|
||||||
|
let volumeDetailsPromise;
|
||||||
|
if (!isNil(matchedResult.volume)) {
|
||||||
|
volumeDetailsPromise = new Promise((resolve, reject) => {
|
||||||
|
return https.get(`${matchedResult.volume.api_detail_url}?api_key=a5fa0663683df8145a85d694b5da4b87e1c92c69&format=json&limit=1&offset=0&field_list=id,name,deck,image,first_issue,last_issue,publisher,count_of_issues`, (resp) => {
|
||||||
|
|
||||||
|
let data = '';
|
||||||
|
resp.on('data', (chunk) => {
|
||||||
|
data += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
resp.on('end', () => {
|
||||||
|
const volumeInformation = JSON.parse(data);
|
||||||
|
resolve(volumeInformation);
|
||||||
|
});
|
||||||
|
|
||||||
|
}).on("error", (err) => {
|
||||||
|
console.log("Error: " + err.message);
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const volumeDetails = await volumeDetailsPromise;
|
||||||
|
matchedResult.volumeInformation = volumeDetails.results;
|
||||||
|
Comic.findByIdAndUpdate(comicObjectId, { sourcedMetadata: { comicvine: matchedResult } }, { new: true }, (err, result) => {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
// 3. Fetch and append volume information
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
getComicBooks: {
|
||||||
|
rest: "POST /getComicBooks",
|
||||||
params: {},
|
params: {},
|
||||||
async handler(
|
async handler(
|
||||||
ctx: Context<{ paginationOptions: object }>
|
ctx: Context<{ paginationOptions: object }>
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
const sharp = require("sharp");
|
const sharp = require("sharp");
|
||||||
const imghash = require("imghash");
|
|
||||||
const leven = require("leven");
|
|
||||||
import path from "path";
|
|
||||||
import { isNull, reject } from "lodash";
|
|
||||||
import { logger } from "./logger.utils";
|
import { logger } from "./logger.utils";
|
||||||
|
import { ISharpResizedImageStats } from "threetwo-ui-typings";
|
||||||
|
|
||||||
export const extractMetadataFromImage = async (
|
export const extractMetadataFromImage = async (
|
||||||
imageFilePath: string
|
imageFilePath: string
|
||||||
@@ -21,35 +18,20 @@ export const resizeImage = async (
|
|||||||
outputPath: string,
|
outputPath: string,
|
||||||
newWidth: number,
|
newWidth: number,
|
||||||
newHeight?: number
|
newHeight?: number
|
||||||
): Promise<unknown> => {
|
): Promise<ISharpResizedImageStats> => {
|
||||||
return await sharp(imageFile)
|
return new Promise((resolve, reject) => {
|
||||||
.resize(newWidth)
|
sharp(imageFile)
|
||||||
.toFile(`${outputPath}`, (err, info) => {
|
.resize(newWidth)
|
||||||
if (err) {
|
.toFile(`${outputPath}`, (err, info) => {
|
||||||
logger.error("Failed to resize image:");
|
if (err) {
|
||||||
logger.error(err);
|
logger.error("Failed to resize image:");
|
||||||
return err;
|
logger.error(err);
|
||||||
}
|
reject(err);
|
||||||
|
}
|
||||||
|
|
||||||
logger.info("Image file resized with the following parameters:");
|
logger.info("Image file resized with the following parameters:");
|
||||||
logger.info(info);
|
logger.info(info);
|
||||||
return info;
|
resolve(info);
|
||||||
});
|
});
|
||||||
};
|
});
|
||||||
|
|
||||||
export const calculateLevenshteinDistance = async (
|
|
||||||
imagePath1: string,
|
|
||||||
imagePath2: string
|
|
||||||
): Promise<Record<string, unknown>> => {
|
|
||||||
console.log("AGANTUK", imagePath1)
|
|
||||||
const hash1 = await imghash.hash(imagePath1);
|
|
||||||
const hash2 = await imghash.hash(imagePath2);
|
|
||||||
console.log("HASHISH", hash1)
|
|
||||||
if (!isNull(hash1) && !isNull(hash2)) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
resolve({ levenshteinDistance: leven(hash1, hash2) });
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
reject("Can't calculate the Levenshtein distance")
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ SOFTWARE.
|
|||||||
* Initial: 2021/05/04 Rishi Ghan
|
* Initial: 2021/05/04 Rishi Ghan
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createReadStream, createWriteStream, readFileSync } from "fs";
|
import { createReadStream, createWriteStream, readFileSync, stat } from "fs";
|
||||||
const fse = require("fs-extra");
|
const fse = require("fs-extra");
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { each, isEmpty, map, flatten } from "lodash";
|
import { each, isEmpty, map, flatten } from "lodash";
|
||||||
@@ -42,6 +42,7 @@ import {
|
|||||||
IExtractedComicBookCoverFile,
|
IExtractedComicBookCoverFile,
|
||||||
IExtractionOptions,
|
IExtractionOptions,
|
||||||
IFolderData,
|
IFolderData,
|
||||||
|
ISharpResizedImageStats,
|
||||||
} from "threetwo-ui-typings";
|
} from "threetwo-ui-typings";
|
||||||
import { logger } from "./logger.utils";
|
import { logger } from "./logger.utils";
|
||||||
import { validateComicBookMetadata } from "../utils/validation.utils";
|
import { validateComicBookMetadata } from "../utils/validation.utils";
|
||||||
@@ -84,6 +85,7 @@ export const extractCoverFromFile = async (
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`${error}: Couldn't create directory.`);
|
logger.error(`${error}: Couldn't create directory.`);
|
||||||
}
|
}
|
||||||
|
// extract the cover
|
||||||
let result: string;
|
let result: string;
|
||||||
const targetCoverImageFilePath = path.resolve(constructedPaths.targetPath + "/" + walkedFolder.name + "_cover.jpg")
|
const targetCoverImageFilePath = path.resolve(constructedPaths.targetPath + "/" + walkedFolder.name + "_cover.jpg")
|
||||||
result = await calibre.run(
|
result = await calibre.run(
|
||||||
@@ -95,15 +97,17 @@ export const extractCoverFromFile = async (
|
|||||||
);
|
);
|
||||||
// create renditions
|
// create renditions
|
||||||
const renditionPath = constructedPaths.targetPath + "/" + walkedFolder.name + "_200px.jpg";
|
const renditionPath = constructedPaths.targetPath + "/" + walkedFolder.name + "_200px.jpg";
|
||||||
await resizeImage(targetCoverImageFilePath, path.resolve(renditionPath), 200);
|
const stats:ISharpResizedImageStats = await resizeImage(targetCoverImageFilePath, path.resolve(renditionPath), 200);
|
||||||
|
|
||||||
resolve({
|
resolve({
|
||||||
name: walkedFolder.name,
|
name: walkedFolder.name,
|
||||||
path: constructedPaths.targetPath + "/" + walkedFolder.name + "_200px.jpg", //renditionPath
|
path: renditionPath,
|
||||||
fileSize: 0,
|
fileSize: stats.size,
|
||||||
|
extension: path.extname(constructedPaths.inputFilePath),
|
||||||
containedIn: walkedFolder.containedIn,
|
containedIn: walkedFolder.containedIn,
|
||||||
//originalPath:
|
calibreMetadata: {
|
||||||
//extension:
|
coverWriteResult: result,
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
@@ -200,6 +204,7 @@ export const unrar = async (
|
|||||||
mode: 0o2775,
|
mode: 0o2775,
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
|
// read the file into a buffer
|
||||||
const fileBuffer = await readFile(
|
const fileBuffer = await readFile(
|
||||||
paths.inputFilePath
|
paths.inputFilePath
|
||||||
).catch((err) => console.error("Failed to read file", err));
|
).catch((err) => console.error("Failed to read file", err));
|
||||||
@@ -238,8 +243,12 @@ export const unrar = async (
|
|||||||
resolve({
|
resolve({
|
||||||
name: `${extractedFiles[0].fileHeader.name}`,
|
name: `${extractedFiles[0].fileHeader.name}`,
|
||||||
path: paths.targetPath,
|
path: paths.targetPath,
|
||||||
|
extension: path.extname(extractedFiles[0].fileHeader.name),
|
||||||
fileSize: extractedFiles[0].fileHeader.packSize,
|
fileSize: extractedFiles[0].fileHeader.packSize,
|
||||||
containedIn: walkedFolder.containedIn,
|
containedIn: walkedFolder.containedIn,
|
||||||
|
calibreMetadata: {
|
||||||
|
coverWriteResult: "",
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`${error}: Couldn't write file.`);
|
logger.error(`${error}: Couldn't write file.`);
|
||||||
|
|||||||
@@ -5906,10 +5906,10 @@
|
|||||||
"resolved" "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
|
"resolved" "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
|
||||||
"version" "0.2.0"
|
"version" "0.2.0"
|
||||||
|
|
||||||
"threetwo-ui-typings@^1.0.1-0":
|
"threetwo-ui-typings@^1.0.2-0":
|
||||||
"integrity" "sha512-g5FWa069AT1WfUHHEVFOQ1q6cfK+9UOzewfKblheuDBSsGN/e89MJMEVfuInBaHgxHM32/P+mNUFBqnBoeRSLQ=="
|
"integrity" "sha512-sK5cb/fApFKseQNgcnGmAnPNxDDXT+dQ/Blei1N4q0mduO3kZfJTnlMYaXjO4FDXLP+jkiwsv0bf7PXm0DgF7g=="
|
||||||
"resolved" "https://registry.npmjs.org/threetwo-ui-typings/-/threetwo-ui-typings-1.0.1.tgz"
|
"resolved" "https://registry.npmjs.org/threetwo-ui-typings/-/threetwo-ui-typings-1.0.2.tgz"
|
||||||
"version" "1.0.1"
|
"version" "1.0.2"
|
||||||
dependencies:
|
dependencies:
|
||||||
"typescript" "^4.3.2"
|
"typescript" "^4.3.2"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user