From 00395a3e79bec6801ce0a17283a140873b3c40b4 Mon Sep 17 00:00:00 2001 From: ThaumRystra <9525416+ThaumRystra@users.noreply.github.com> Date: Wed, 13 Sep 2023 11:18:57 +0200 Subject: [PATCH 01/16] Added comment to start pull request --- app/imports/api/parenting/ChildSchema.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/imports/api/parenting/ChildSchema.js b/app/imports/api/parenting/ChildSchema.js index f4787bcc..c09a1fa7 100644 --- a/app/imports/api/parenting/ChildSchema.js +++ b/app/imports/api/parenting/ChildSchema.js @@ -26,6 +26,7 @@ let ChildSchema = new SimpleSchema({ type: RefSchema, optional: true, }, + // Change this from ancestor list to left and right indices ancestors: { type: Array, defaultValue: [], From d57e49f9696b947b247a4181fee1dfcbb50e36a9 Mon Sep 17 00:00:00 2001 From: ThaumRystra <9525416+ThaumRystra@users.noreply.github.com> Date: Wed, 13 Sep 2023 23:18:03 +0200 Subject: [PATCH 02/16] Began rewrite of all parenting functions to nested sets What have I gotten myself into :( --- app/.meteor/packages | 1 + app/.meteor/versions | 1 + app/imports/api/parenting/ChildSchema.js | 41 - app/imports/api/parenting/ChildSchema.ts | 78 + app/imports/api/parenting/fetchDocByRef.js | 12 - .../api/parenting/getCollectionByName.js | 11 - .../getDescendantsInDepthFirstOrder.js | 27 - app/imports/api/parenting/nodesToTree.js | 122 -- app/imports/api/parenting/order.js | 156 -- ...{organizeMethods.js => organizeMethods.ts} | 74 +- app/imports/api/parenting/parenting.js | 217 --- .../api/parenting/parentingFunctions.test.ts | 77 + .../api/parenting/parentingFunctions.ts | 494 +++++ app/imports/api/parenting/softRemove.js | 74 - app/imports/api/parenting/softRemove.ts | 90 + app/package-lock.json | 1687 ++++++++++++++++- app/package.json | 1 + app/{jsconfig.json => tsconfig.json} | 7 +- 18 files changed, 2427 insertions(+), 743 deletions(-) delete mode 100644 app/imports/api/parenting/ChildSchema.js create mode 100644 app/imports/api/parenting/ChildSchema.ts delete mode 100644 app/imports/api/parenting/fetchDocByRef.js delete mode 100644 app/imports/api/parenting/getCollectionByName.js delete mode 100644 app/imports/api/parenting/getDescendantsInDepthFirstOrder.js delete mode 100644 app/imports/api/parenting/nodesToTree.js delete mode 100644 app/imports/api/parenting/order.js rename app/imports/api/parenting/{organizeMethods.js => organizeMethods.ts} (52%) delete mode 100644 app/imports/api/parenting/parenting.js create mode 100644 app/imports/api/parenting/parentingFunctions.test.ts create mode 100644 app/imports/api/parenting/parentingFunctions.ts delete mode 100644 app/imports/api/parenting/softRemove.js create mode 100644 app/imports/api/parenting/softRemove.ts rename app/{jsconfig.json => tsconfig.json} (75%) diff --git a/app/.meteor/packages b/app/.meteor/packages index d2c043aa..102191c1 100644 --- a/app/.meteor/packages +++ b/app/.meteor/packages @@ -50,3 +50,4 @@ littledata:synced-cron typescript@4.9.4 seba:minifiers-autoprefixer mixmax:smart-disconnect +zodern:types diff --git a/app/.meteor/versions b/app/.meteor/versions index e5431969..4f1ea628 100644 --- a/app/.meteor/versions +++ b/app/.meteor/versions @@ -125,3 +125,4 @@ url@1.3.2 webapp@1.13.5 webapp-hashing@1.1.1 zer0th:meteor-vuetify-loader@0.1.41 +zodern:types@1.0.9 diff --git a/app/imports/api/parenting/ChildSchema.js b/app/imports/api/parenting/ChildSchema.js deleted file mode 100644 index c09a1fa7..00000000 --- a/app/imports/api/parenting/ChildSchema.js +++ /dev/null @@ -1,41 +0,0 @@ -import SimpleSchema from 'simpl-schema'; -import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; - -const RefSchema = new SimpleSchema({ - id: { - type: String, - regEx: SimpleSchema.RegEx.Id, - // TODO: Rather than indexing this field, index `ancestors.0.id` to only - // index the root of the ancestor heirarchy to significantly reduce - // index size and improve performance - // All queries on an ancestor document need to target `ancestors.0.id` first - // before targeting a younger ancestor - index: 1 - }, - collection: { - type: String, - max: STORAGE_LIMITS.collectionName, - }, -}); - -let ChildSchema = new SimpleSchema({ - order: { - type: Number, - }, - parent: { - type: RefSchema, - optional: true, - }, - // Change this from ancestor list to left and right indices - ancestors: { - type: Array, - defaultValue: [], - maxCount: STORAGE_LIMITS.ancestorCount, - }, - 'ancestors.$': { - type: RefSchema, - }, -}); - -export default ChildSchema; -export { RefSchema }; diff --git a/app/imports/api/parenting/ChildSchema.ts b/app/imports/api/parenting/ChildSchema.ts new file mode 100644 index 00000000..251128fa --- /dev/null +++ b/app/imports/api/parenting/ChildSchema.ts @@ -0,0 +1,78 @@ +import SimpleSchema from 'simpl-schema'; +import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; + +const RefSchema = new SimpleSchema({ + id: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + collection: { + type: String, + max: STORAGE_LIMITS.collectionName, + }, +}); + +const ChildSchema = new SimpleSchema({ + root: { + type: RefSchema, + }, + 'root.id': { + type: String, + regEx: SimpleSchema.RegEx.Id, + index: 1, + }, + // Parent id of a document in the same collection + // Undefined parent id implies the root is the parent + parentId: { + type: String, + regEx: SimpleSchema.RegEx.Id, + optional: true, + }, + /** + * The tree structure goes as follows where the numbering follows a counterclockwise depth first + * path around the tree. The canonical structure comes from the root and parentId references, + * while the left and right numbering is used to optimize ancestor queries. + * + * Left can be used as the canonical ordering of properties in an expanded tree folder view. + * + * 1 Books 12 + * ┃ + * 2 Programming 11 + * ┏━━━━━━━━┻━━━━━━━━━┓ + * 3 Languages 4 5 Databases 10 + * ┏━━━━━━━┻━━━━━━━┓ + * 6 MongoDB 7 8 dbm 9 + */ + left: { + type: Number, + index: 1, + }, + right: { + type: Number, + index: 1, + } +}); + +export interface Reference { + collection: string, + id: string, +} + +export interface TreeDoc { + _id: string, + root: Reference, + parentId?: string, + left: number, + right: number, +} + +export const treeDocFields = { + _id: 1, + root: 1, + parentId: 1, + left: 1, + right: 1, +} + +export default ChildSchema; +export { RefSchema }; diff --git a/app/imports/api/parenting/fetchDocByRef.js b/app/imports/api/parenting/fetchDocByRef.js deleted file mode 100644 index dfa485dc..00000000 --- a/app/imports/api/parenting/fetchDocByRef.js +++ /dev/null @@ -1,12 +0,0 @@ -import getCollectionByName from '/imports/api/parenting/getCollectionByName.js'; - -const docNotFoundError = function({id, collection}){ - throw new Meteor.Error('document-not-found', - `No document could be found with id: ${id} in ${collection}` - ); -}; - -export default function fetchDocByRef({id, collection}, options){ - return getCollectionByName(collection).findOne(id, options) || - docNotFoundError({id, collection}); -} diff --git a/app/imports/api/parenting/getCollectionByName.js b/app/imports/api/parenting/getCollectionByName.js deleted file mode 100644 index bc9fe4e0..00000000 --- a/app/imports/api/parenting/getCollectionByName.js +++ /dev/null @@ -1,11 +0,0 @@ -const collectionDoesntExistError = function(collectionName){ - throw new Meteor.Error('bad-collection-reference', - `Parent references collection ${collectionName}, which does not exist` - ); -}; - -const getCollectionByName = function(name){ - return Mongo.Collection.get(name) || collectionDoesntExistError(name); -}; - -export default getCollectionByName; diff --git a/app/imports/api/parenting/getDescendantsInDepthFirstOrder.js b/app/imports/api/parenting/getDescendantsInDepthFirstOrder.js deleted file mode 100644 index 9010d51b..00000000 --- a/app/imports/api/parenting/getDescendantsInDepthFirstOrder.js +++ /dev/null @@ -1,27 +0,0 @@ -import nodesToTree from '/imports/api/parenting/nodesToTree.js'; - -export default function getDescendantsInDepthFirstOrder({ - collection, - ancestorId, - filter, - options = {fields: {order: 1, ancestors: 1}}, -}){ - let forest = nodesToTree({collection, ancestorId, filter, options}); - let orderMemo = getDocsInDepthFirstOrder(forest); - return orderMemo; -} - -export function getDocsInDepthFirstOrder(forest){ - let docs = []; - forest.forEach(node => { - addNodeAndTraverse(node, docs) - }); - return docs; -} - -function addNodeAndTraverse(node, docs){ - docs.push(node.node); - node.children.forEach(child => { - addNodeAndTraverse(child, docs) - }); -} diff --git a/app/imports/api/parenting/nodesToTree.js b/app/imports/api/parenting/nodesToTree.js deleted file mode 100644 index 86fbe1c1..00000000 --- a/app/imports/api/parenting/nodesToTree.js +++ /dev/null @@ -1,122 +0,0 @@ -import { union, difference, sortBy, findLast, intersection } from 'lodash'; - -export function nodeArrayToTree(nodes) { - // Store a dict and list of all the nodes - let nodeIndex = {}; - let nodeList = []; - nodes.forEach(node => { - let treeNode = { - node: node, - children: [], - }; - nodeIndex[node._id] = treeNode; - nodeList.push(treeNode); - }); - // Create a forest of trees - let forest = []; - // Either the node is a child of its nearest found ancestor, or in the forest as a root - nodeList.forEach(treeNode => { - let ancestorInForest = findLast( - treeNode.node.ancestors, - ancestor => !!nodeIndex[ancestor.id] - ); - if (ancestorInForest) { - nodeIndex[ancestorInForest.id].children.push(treeNode); - } else { - forest.push(treeNode); - } - }); - forest.nodeIndex = nodeIndex; - return forest; -} - -// Fetch the documents from a collection, and return the tree of those documents -export default function nodesToTree({ - collection, ancestorId, filter, options = {}, - includeFilteredDocAncestors = false, includeFilteredDocDescendants = false -}) { - // Setup the filter - let collectionFilter = { - 'ancestors.id': ancestorId, - 'removed': { $ne: true }, - }; - if (filter) { - collectionFilter = { - ...collectionFilter, - ...filter, - } - } - // Set up the options - let collectionSort = { - order: 1 - }; - if (options && options.sort) { - collectionSort = { - ...collectionSort, - ...options.sort, - } - } - let collectionOptions = { - sort: collectionSort, - } - if (options) { - collectionOptions = { - ...collectionOptions, - ...options, - } - } - // Find all the nodes that match the filter - let docs = collection.find(collectionFilter, collectionOptions).map(doc => { - if (!filter) return doc; - // Mark the nodes that were found by the custom filter - doc._matchedDocumentFilter = true; - return doc; - }); - let ancestors = []; - let ancestorIds = []; - let docIds = []; - if (filter && (includeFilteredDocAncestors || includeFilteredDocDescendants)) { - docIds = docs.map(doc => doc._id) - } - if (filter && includeFilteredDocAncestors) { - // Add all ancestor ids to an array - docs.forEach(doc => { - ancestorIds = union(ancestorIds, doc.ancestors.map(ref => ref.id)); - }); - // Get all the docs that are also ancestors and mark them - docs.forEach(doc => { - if (ancestorIds.includes(doc._id)) { - doc._ancestorOfMatchedDocument = true; - } - }); - // Remove the ancestor IDs of docs we have already found - ancestorIds = difference(ancestorIds, docIds); - // Get the ancestor docs from the collection, don't worry about `removed` docs, - // if their descendant was not removed, neither are they - ancestors = collection.find({ _id: { $in: ancestorIds } }).map(doc => { - // Mark that the nodes are ancestors of the found nodes - doc._ancestorOfMatchedDocument = true; - return doc; - }); - } - let descendants = []; - if (filter && includeFilteredDocDescendants) { - let exludeIds = union(ancestorIds, docIds); - descendants = collection.find({ - '_id': { $nin: exludeIds }, - 'ancestors.id': { $in: docIds }, - 'removed': { $ne: true }, - }).map(doc => { - // Mark that the nodes are descendants of the found nodes - doc._descendantOfMatchedDocument = true; - return doc; - }); - } - let nodes = sortBy([ - ...ancestors, - ...docs, - ...descendants - ], 'order'); - // Find all the nodes - return nodeArrayToTree(nodes); -} diff --git a/app/imports/api/parenting/order.js b/app/imports/api/parenting/order.js deleted file mode 100644 index c94e814b..00000000 --- a/app/imports/api/parenting/order.js +++ /dev/null @@ -1,156 +0,0 @@ -import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; -import getCollectionByName from '/imports/api/parenting/getCollectionByName.js'; -import getDescendantsInDepthFirstOrder from '/imports/api/parenting/getDescendantsInDepthFirstOrder.js' - -// Docs keep track of their depth-first order amongst their entire ancestor tree -export function compareOrder(docA, docB){ - // < 0 if A comes before B - // = 0 if A and B are the same order - // > 0 if B comes before A - - // They must share a root ancestor to be meaningfully sorted - if (docA.ancestors[0].id !== docB.ancestors[0].id){ - return 0; - } else { - return docA.order - docB.order; - } -} - -export function getHighestOrder({collection, ancestorId}){ - const highestOrderedDoc = collection.findOne({ - 'ancestors.id': ancestorId, - }, { - fields: {order: 1}, - sort: {order: -1}, - }); - return highestOrderedDoc ? highestOrderedDoc.order : -1; -} - -export function setDocToLastOrder({collection, doc}){ - doc.order = getHighestOrder({ - collection, - ancestorId: doc.ancestors[0].id, - }) + 1; -} - -// update the order of a doc, and shift the related docs around to suit the new -// order -function cheapUpdateDocOrder({docRef, order}){ - let doc = fetchDocByRef(docRef, {fields: { - order: 1, - parent: 1, - }}); - let collection = getCollectionByName(docRef.collection); - const currentOrder = doc.order; - if (currentOrder === order){ - return; - } else { - // First move the documents that are in the way - let inBetweenSelector, increment; - if (order > currentOrder){ - // Move in-between docs backward - inBetweenSelector = { - $gt: currentOrder, - $lte: order - }; - increment = -1; - } else if (order < currentOrder){ - // Move in-between docs forward - inBetweenSelector = { - $lt: currentOrder, - $gte: order - }; - increment = 1; - } - collection.update({ - 'ancestors.id': doc.ancestors[0].id, - order: inBetweenSelector, - }, { - $inc: {order: increment}, - }, { - multi: true, - selector: {type: 'any'}, - }); - // Then move the document itself - collection.update(doc._id, {$set: {order}}, {selector: {type: 'any'}}); - } -} - -export function cheapRemovedDocAtOrder({collection, doc}){ - // Decrement the order of all docs after the removed doc - collection.update({ - 'ancestors.id': doc.ancestors[0].id, - order: {$gt: doc.order}, - }, { - $inc: {order: -1}, - }, { - multi: true, - selector: {type: 'any'}, - }); -} - -export function cheapInsertedDocAtOrder({collection, ancestorId, order}){ - // Increment the order of all docs after the inserted doc - collection.update({ - 'ancestors.id': ancestorId, - order: {$gte: order}, - }, { - $inc: {order: 1}, - }, { - multi: true, - selector: {type: 'any'}, - }); -} - -// Update the order a single doc and re-order the entire related doc list -// with the change -export function safeUpdateDocOrder({docRef, order}){ - let collection = getCollectionByName(docRef.collection); - // Put the new doc half a step in front of its new order - // to ensure it's in front of whichever doc was there before - collection.update(docRef.id, { - $set: {order} - }, { - selector: {type: 'any'} - }); - // reorder all related docs so that order is back to being a continous - // set of whole numbers - let movedDoc = fetchDocByRef(docRef, {fields: {ancestors: 1}}); - let ancestorId = movedDoc.ancestors[0].id; - reorderDocs({collection, ancestorId}); -} - -export function reorderDocs({collection, ancestorId}){ - let orderedDocs = getDescendantsInDepthFirstOrder({collection, ancestorId}); - let bulkWrite = []; - orderedDocs.forEach((doc, index) => { - if (doc.order !== index){ - bulkWrite.push({ - updateOne : { - filter: {_id: doc._id}, - update: {$set: {order: index}}, - }, - }); - } - }); - if (Meteor.isServer && bulkWrite.length){ - collection.rawCollection().bulkWrite( - bulkWrite, - {ordered : false}, - function(e){ - if (e) { - console.error('Bulk write failed: '); - console.error(e); - } - } - ); - } else { - bulkWrite.forEach(op => { - collection.update( - op.updateOne.filter, - op.updateOne.update, - {selector: {type: 'any'}} - ); - }); - } -} diff --git a/app/imports/api/parenting/organizeMethods.js b/app/imports/api/parenting/organizeMethods.ts similarity index 52% rename from app/imports/api/parenting/organizeMethods.js rename to app/imports/api/parenting/organizeMethods.ts index 54d5f1ba..e51bb108 100644 --- a/app/imports/api/parenting/organizeMethods.js +++ b/app/imports/api/parenting/organizeMethods.ts @@ -2,12 +2,9 @@ import SimpleSchema from 'simpl-schema'; import { union } from 'lodash'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import { updateParent } from '/imports/api/parenting/parenting.js'; -import { reorderDocs, safeUpdateDocOrder } from '/imports/api/parenting/order.js'; -import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; +import { changeParent, fetchDocByRef, getCollectionByName } from '/imports/api/parenting/parentingFunctions'; +import { RefSchema } from '/imports/api/parenting/ChildSchema'; import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; -import getCollectionByName from '/imports/api/parenting/getCollectionByName.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; const organizeDoc = new ValidatedMethod({ @@ -33,47 +30,45 @@ const organizeDoc = new ValidatedMethod({ numRequests: 5, timeInterval: 5000, }, - run({ docRef, parentRef, order, skipRecompute, skipClient }) { + async run({ docRef, parentId, order, skipRecompute, skipClient }) { if (skipClient && this.isSimulation) { return; } - let doc = fetchDocByRef(docRef); - let collection = getCollectionByName(docRef.collection); + const collection = getCollectionByName(docRef.collection); + const [doc, parent] = await Promise.all([ + collection.findOneAsync(docRef.id), + collection.findOneAsync(parentId), + ]); + + if (!doc) throw new Meteor.Error('Document not found', 'The property to move could not be found'); + if (!parent) throw new Meteor.Error('Document not found', 'The new parent could not be found'); // The user must be able to edit both the doc and its parent to move it // successfully - assertDocEditPermission(doc, this.userId); - let parent = fetchDocByRef(parentRef); - assertDocEditPermission(parent, this.userId); + await Promise.all([ + assertDocEditPermission(doc, this.userId), + // Only check parent if it has a different root + doc.root.id !== parent.root.id && assertDocEditPermission(parent, this.userId), + ]); // Change the doc's parent - updateParent({ docRef, parentRef }); - // Change the doc's order to be a half step ahead of its target location - collection.update(doc._id, { $set: { order } }, { selector: { type: 'any' } }); - - // Reorder both ancestors' documents - let oldAncestorId = doc.ancestors[0].id; - reorderDocs({ collection, ancestorId: oldAncestorId }); - - let newAncestorId = getRootId(parent); - if (newAncestorId !== oldAncestorId) { - reorderDocs({ collection, ancestorId: newAncestorId }); - } + await changeParent(doc, parent, collection, order); // Figure out which creatures need to be recalculated after this move - let docCreatures = getCreatureAncestors(doc); - let parentCreatures = getCreatureAncestors(parent); - if (!skipRecompute) { - let creaturesToRecompute = union(docCreatures, parentCreatures); + if (!skipRecompute && docRef.collection === 'creatures') { + const creaturesToRecompute = union[doc.root.id, parent.root.id]; // Mark the creatures for recompute - Creatures.update({ + await Creatures.updateAsync({ _id: { $in: creaturesToRecompute } }, { $set: { dirty: true }, + }, { + multi: true }); } }, }); +// TODO, rewrite const reorderDoc = new ValidatedMethod({ name: 'organize.reorderDoc', validate: new SimpleSchema({ @@ -104,27 +99,4 @@ const reorderDoc = new ValidatedMethod({ }, }); -function getRootId(doc) { - if (doc.ancestors && doc.ancestors.length && doc.ancestors[0]) { - return doc.ancestors[0].id; - } else { - return doc._id; - } -} - -function getCreatureAncestors(doc) { - let ids = []; - if (doc.type === 'pc' || doc.type === 'npc' || doc.type === 'monster') { - ids.push(doc._id); - } - if (doc.ancestors) { - doc.ancestors.forEach(ancestorRef => { - if (ancestorRef.collection === 'creatures') { - ids.push(ancestorRef.id); - } - }); - } - return ids; -} - export { organizeDoc, reorderDoc }; diff --git a/app/imports/api/parenting/parenting.js b/app/imports/api/parenting/parenting.js deleted file mode 100644 index e5a8cc62..00000000 --- a/app/imports/api/parenting/parenting.js +++ /dev/null @@ -1,217 +0,0 @@ -import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; -import getCollectionByName from '/imports/api/parenting/getCollectionByName.js'; -import { flatten } from 'lodash'; - -const generalParents = [ - 'attribute', - 'buff', - 'classLevel', - 'feature', - 'folder', - 'root', - 'item', - 'spell', -]; - -// Which types are allowed as parents for other types -const allowedParenting = { - folder: [...generalParents, 'container'], - rollResult: ['roll', 'rollResult'], - container: ['root', 'folder'], - item: ['root', 'container', 'folder'], -}; - -const allParentTypes = new Set(flatten(Object.values(allowedParenting))); - -export function canBeParent(type){ - return true; - //TODO until there is a good reason to disallow certain parenting options, - // this should just let the user do whatever - return type && allParentTypes.has(type); -} - -export function getAllowedParents({childType}){ - return allowedParenting[childType] || generalParents; -} - -export function isParentAllowed({parentType = 'root', childType}){ - return true; - //TODO until there is a good reason to disallow certain parenting options, - // this should just let the user do whatever - if (!childType) throw 'childType is required'; - let allowedParents = getAllowedParents({childType}); - return allowedParents.includes(parentType); -} - -export function fetchParent({id, collection}){ - return fetchDocByRef({id, collection}); -} - -export function fetchChildren({ collection, parentId, filter = {}, options = {sort: {order: 1}} }){ - filter['parent.id'] = parentId; - let children = []; - children.push( - ...collection.find({ - 'parent.id': parentId - }, options).fetch() - ); - return children; -} - -export function updateChildren({collection, parentId, filter = {}, modifier, options={}}){ - filter['parent.id'] = parentId; - options.multi = true; - collection.update(filter, modifier, options); -} - -export function fetchDescendants({ collection, ancestorId, filter = {}, options}){ - filter['ancestors.id'] = ancestorId; - let descendants = []; - descendants.push(...collection.find(filter, options).fetch()); - return descendants; -} - -export function updateDescendants({collection, ancestorId, filter = {}, modifier, options={}}){ - filter['ancestors.id'] = ancestorId; - options.multi = true; - options.selector = {type: 'any'}; - collection.update(filter, modifier, options); -} - -export function forEachDescendant({collection, ancestorId, filter = {}, options}, callback){ - filter['ancestors.id'] = ancestorId; - collection.find(filter, options).forEach(callback); -} - -// 1 database read -export function getAncestry({parentRef, inheritedFields = {}}){ - let parentDoc = fetchDocByRef(parentRef, {fields: inheritedFields}); - let parent = { ...parentRef}; - for (let field in inheritedFields){ - if (inheritedFields[field]){ - parent[field] = parentDoc[field]; - } - } - - // Ancestors is [...parent's ancestors, parent ref] - let ancestors = parentDoc.ancestors || []; - ancestors.push(parent); - - return {parentDoc, parent, ancestors}; -} - -export function setLineageOfDocs({docArray, oldParent, newAncestry}){ - const newParent = newAncestry[newAncestry.length - 1]; - docArray.forEach(doc => { - if(doc.parent.id === oldParent.id){ - doc.parent = newParent; - } - let oldAncestors = doc.ancestors; - let oldParentIndex = oldAncestors.findIndex(a => a.id === oldParent.id); - if (oldParentIndex === -1) return; - doc.ancestors = [...newAncestry, ...oldAncestors.slice(oldParentIndex + 1)]; - }); -} - -/** - * Give documents new random ids and transform their references. - * Transform collections of re-IDed docs according to the collection map - */ -export function renewDocIds({docArray, collectionMap, idMap = {}}){ - // idMap is a map of {oldId: newId} - // Get a random generator that's consistent on client and server - let randomSrc = DDP.randomStream('renewDocIds'); - - // Give new ids and map the changes as {oldId: newId} - docArray.forEach(doc => { - let oldId = doc._id; - let newId = idMap[oldId] || randomSrc.id(); - doc._id = newId; - idMap[oldId] = newId; - }); - - // Remap all references using the new IDs - const remapReference = ref => { - if (idMap[ref.id]){ - ref.id = idMap[ref.id]; - ref.collection = collectionMap && collectionMap[ref.collection] || ref.collection; - } - } - docArray.forEach(doc => { - remapReference(doc.parent); - doc.ancestors.forEach(remapReference); - }); -} - -export function updateParent({docRef, parentRef}){ - let collection = getCollectionByName(docRef.collection); - let oldDoc = fetchDocByRef(docRef, {fields: { - parent: 1, - ancestors: 1, - type: 1, - }}); - let updateOptions = { selector: {type: 'any'} }; - - // Skip if we aren't changing the parent id - if (oldDoc.parent.id === parentRef.id) return; - - // Get the parent and its ancestry - let {parentDoc, parent, ancestors} = getAncestry({parentRef}); - - // Check that the doc isn't its own ancestor - ancestors.forEach(ancestor => { - if (docRef.id === ancestor.id){ - throw new Meteor.Error('invalid parenting', - 'A doc can\'t be its own ancestor') - } - }); - - // If the doc and its parent are in the same collection, apply the allowed - // parent rules based on type - if (docRef.collection === parentRef.collection){ - let parentAllowed = isParentAllowed({ - parentType: parentDoc.type, - childType: oldDoc.type - }); - if (!parentAllowed){ - throw new Meteor.Error('invalid parenting', - `Can't make ${oldDoc.type} a child of ${parentDoc.type}`) - } - } - - // update the document's parenting - collection.update(docRef.id, { - $set: {parent, ancestors} - }, updateOptions); - - // Remove the old ancestors from the descendants - updateDescendants({ - collection, - ancestorId: docRef.id, - modifier: {$pullAll: { - ancestors: oldDoc.ancestors, - }}, - options: updateOptions, - }); - - // Add the new ancestors to the descendants - updateDescendants({ - collection, - ancestorId: docRef.id, - modifier: {$push: { - ancestors: { - $each: ancestors, - $position: 0, - }, - }}, - options: updateOptions, - }); -} - -export function getName(doc){ - if (doc.name) return name; - var i = doc.ancestors.length; - while(i--) { - if (doc.ancestors[i].name) return doc.ancestors[i].name; - } -} diff --git a/app/imports/api/parenting/parentingFunctions.test.ts b/app/imports/api/parenting/parentingFunctions.test.ts new file mode 100644 index 00000000..8c4f9104 --- /dev/null +++ b/app/imports/api/parenting/parentingFunctions.test.ts @@ -0,0 +1,77 @@ +import { docsToForest, calculateNestedSetOperations } from '/imports/api/parenting/parentingFunctions' +import { TreeDoc } from '/imports/api/parenting/ChildSchema'; +import { assert } from 'chai'; + +function doc(_id, left, right, parentId?): TreeDoc { + return { _id, root: { id: 'root', collection: 'col' }, left, right, parentId }; +} + +function op(_id, left, right) { + return { + updateOne: { + filter: { _id }, + update: { $set: { left, right } }, + }, + }; +} + +describe('Parenting with nested sets', function () { + /** + * Test the following structure + * + * 1 Books 12 + * ┃ + * 2 Programming 11 + * ┏━━━━━━━━┻━━━━━━━━━┓ + * 3 Languages 4 5 Databases 10 + * ┏━━━━━━━┻━━━━━━━┓ + * 6 MongoDB 7 8 dbm 9 + */ + it('Takes a set of documents and builds the forest', function () { + const docArray: Array = [ + doc('Books', 1, 12), + doc('Programming', 2, 11), + doc('Languages', 3, 4), + doc('Databases', 5, 10), + doc('MongoDB', 6, 7), + doc('dbm', 8, 9), + ]; + const forest = docsToForest(docArray); + assert.deepEqual(forest, [ + { + doc: doc('Books', 1, 12), children: [ + { + doc: doc('Programming', 2, 11), children: [ + { doc: doc('Languages', 3, 4), children: [] }, + { + doc: doc('Databases', 5, 10), children: [ + { doc: doc('MongoDB', 6, 7), children: [] }, + { doc: doc('dbm', 8, 9), children: [] }, + ] + } + ] + } + ] + } + ]); + }); + it('Can recalculate left and right for docs with set parents', function () { + const docArray = [ + doc('Books', 71, 33, undefined), + doc('Programming', 72, 33, 'Books'), + doc('Languages', 73, 33, 'Programming'), + doc('Databases', 74, 33, 'Programming'), + doc('MongoDB', 75, 33, 'Databases'), + doc('dbm', 76, 33, 'Databases'), + ]; + const ops = calculateNestedSetOperations(docArray); + assert.deepEqual(ops, [ + op('Books', 1, 12), + op('Programming', 2, 11), + op('Languages', 3, 4), + op('Databases', 5, 10), + op('MongoDB', 6, 7), + op('dbm', 8, 9), + ]); + }); +}); diff --git a/app/imports/api/parenting/parentingFunctions.ts b/app/imports/api/parenting/parentingFunctions.ts new file mode 100644 index 00000000..34d8eb91 --- /dev/null +++ b/app/imports/api/parenting/parentingFunctions.ts @@ -0,0 +1,494 @@ +import { chain, reverse } from 'lodash'; +import { TreeDoc, treeDocFields, Reference } from '/imports/api/parenting/ChildSchema'; + +export function getCollectionByName(name: string): Mongo.Collection { + const collection = Mongo.Collection.get(name) + if (!collection) { + throw new Meteor.Error('bad-collection-reference', + `Parent references collection ${name}, which does not exist` + ); + } + return collection; +} + +export async function fetchDocByRef(ref: Reference, options?: Mongo.Options) { + const doc = await getCollectionByName(ref.collection).findOneAsync(ref.id, options); + if (!doc) { + throw new Meteor.Error('document-not-found', + `No document could be found with id: ${ref.id} in ${ref.collection}` + ); + } + return doc; +} + +interface TreeNode { + doc: T, + children: TreeNode[] +} + +/** + * + * @param nodes An array of documents that share a common root. Must already be sorted by `.left` in ascending order + * @returns An array of tree nodes that each contain a document and its children. Children are + * assigned based on the nearest ancestor included in the input, which may or may not be their + * actual direct parents + */ +export function docsToForest(docs: Array): TreeNode[] { + if (!docs.length) return []; + const forest: TreeNode[] = []; + const ancestorStack: TreeNode[] = []; + let currentAncestor: TreeNode | undefined; + docs.forEach(doc => { + const node: TreeNode = { + doc, + children: [], + }; + // Remove ancestors from the stack until we find one that contains the current document + // Ancestor contains document if ancestor.left < doc.left and ancestor.right > doc.right + // ancestor.left < doc.left is ensured already, because we sorted by doc.left + while (currentAncestor && currentAncestor.doc.right < doc.left) { + currentAncestor = ancestorStack.pop(); + } + // Add this child to its place in the forest, either as a child of the ancestor or as the root + // of a new tree + if (currentAncestor) { + currentAncestor.children.push(node); + } else { + forest.push(node); + } + // Move the last ancestor onto the stack and make this node the new one + if (currentAncestor) ancestorStack.push(currentAncestor); + currentAncestor = node; + }); + return forest; +} + +/** + * Fetch the documents from a collection, and return the tree of those documents, potentially + * including their ancestors or descendants as required + * @param param options + * @returns An array of tree nodes that each contain a document and its children. Children are + * assigned based on the nearest ancestor included in the input, which may or may not be their + * actual direct parents + */ +type FilteredDoc = { + _descendantOfMatchedDocument?: boolean, + _matchedDocumentFilter?: boolean, + _ancestorOfMatchedDocument?: boolean, +} & TreeDoc; + +export default async function filterToForest( + collection: Mongo.Collection, + rootId: string, + filter: Mongo.Query, + options: Mongo.Options = {}, + includeFilteredDocAncestors = false, + includeFilteredDocDescendants = false +): Promise[]> { + // Setup the filter + let collectionFilter = { + 'root.id': rootId, + 'removed': { $ne: true }, + }; + if (filter) { + collectionFilter = { + ...collectionFilter, + ...filter, + } + } + // Set up the options + let collectionSort = { + left: 1 + }; + if (options && options.sort) { + collectionSort = { + ...collectionSort, + ...options.sort, + } + } + let collectionOptions: Mongo.Options = { + sort: collectionSort, + } + if (options) { + collectionOptions = { + ...collectionOptions, + ...options, + } + } + // Find all the docs that match the filter + const docs: TreeDoc[] = await collection.find(collectionFilter, collectionOptions) + .mapAsync(doc => { + if (!filter) return doc; + // Mark the docs that were found by the custom filter + doc._matchedDocumentFilter = true; + return doc; + }); + + // Get the doc ancestors + let ancestors: object[] = []; + if (filter && includeFilteredDocAncestors) { + ancestors = await collection.find(getFilter.ancestorsOfAll(docs), collectionOptions).mapAsync(doc => { + // Mark that the nodes are ancestors of the found nodes + doc._ancestorOfMatchedDocument = true; + return doc; + }); + } + + // Get the doc descendants + let descendants: FilteredDoc[] = []; + if (filter && includeFilteredDocDescendants) { + descendants = await collection.find({ + 'removed': { $ne: true }, + ...getFilter.descendantsOfAll(docs), + }).mapAsync((doc: FilteredDoc) => { + // Mark that the nodes are descendants of the found nodes + doc._descendantOfMatchedDocument = true; + return doc; + }); + } + const nodes = chain([ + ancestors, + docs, + descendants + ]).uniqBy('_id') + .sortBy('left') + .value(); + // Find all the nodes + return docsToForest(nodes); +} + +type ForestAndOrphans = { forest: TreeNode[], orphanIds: string[] } +/** + * Takes a complete set of documents and builds a forest using just their `.parentIds` + * Uses `.left` for sibling order within a parent only. + * Orphans whose direct parents can't be found are collected separately + * @param docs An array of all document that share a common root already sorted by `.left` in + * ascending order + * @returns forest: An array of tree nodes that each contain a document and its children. + * orphans: an array of the same, but their parents weren't in the input array + */ +export function docsToForestByParentId(docs: TreeDoc[]): ForestAndOrphans { + // Collect all the docs in a dict by id + const nodesById = <{ [_id: string]: TreeNode }>{}; + docs.forEach(doc => { + nodesById[doc._id] = { doc, children: [] }; + }); + // Assign the docs to their parent or the forest or orphanage + const forest: TreeNode[] = []; + const orphanIds: string[] = []; + docs.forEach(doc => { + const node = nodesById[doc._id]; + if (!doc.parentId) { + // Root is parent + forest.push(node); + } else if (nodesById[doc.parentId]) { + // Parent is found + nodesById[doc.parentId].children.push(node); + } else { + // Parent is missing, unset it, and store orphan + node.doc.parentId = undefined; + orphanIds.push(node.doc._id); + forest.push(node); + } + }); + return { forest, orphanIds }; +} + +export const getFilter = { + /** + * + * @param doc A document or array of documents that share a root + * @returns A query filter that finds all the ancestors of the doc(s) + */ + ancestors(doc: TreeDoc): Mongo.Query { + return { + 'root.id': doc.root.id, + left: { $lt: doc.left }, + right: { $gt: doc.right }, + }; + }, + ancestorsOfAll(docs: Array): Mongo.Query { + // The ancestors of no documents is a query that returns nothing + if (docs.length === 0) { + return { _id: '' }; + } + // Fallback to the simpler filter for a single document + if (docs.length === 1) { + return getFilter.ancestors(docs[0]); + } + // Build a filter that selects all ancestors + const filter = { + 'root.id': docs[0].root.id, + $or: [], + }; + docs.forEach(doc => { + filter.$or.push({ + left: { $lt: doc.left }, + right: { $gt: doc.right }, + }); + }); + return filter; + }, + descendants(doc: TreeDoc): Mongo.Query { + return { + 'root.id': doc.root.id, + left: { $gt: doc.left }, + right: { $lt: doc.right }, + }; + }, + descendantsOfAll(docs: Array): Mongo.Query { + // The descendants of no documents is a query that returns nothing + if (docs.length === 0) { + return { _id: '' }; + } + // Fallback to the simpler filter for a single document + if (docs.length === 1) { + return getFilter.descendants(docs[0]); + } + // Build a filter that selects all descendants + const filter = { + 'root.id': docs[0].root.id, + $or: [], + }; + docs.forEach(doc => { + filter.$or.push({ + left: { $gt: doc.left }, + right: { $lt: doc.right }, + }); + }); + return filter; + }, + children(doc: TreeDoc): Mongo.Query { + return { + 'root.id': doc.root.id, + parentId: doc._id, + }; + }, + parent(doc: TreeDoc): Mongo.Query { + return { + _id: doc.parentId, + }; + }, +} + +export async function fetchParent({ id, collection }) { + return await fetchDocByRef({ id, collection }); +} + +/** + * Give documents new random ids and transform their references. + * Transform collections of re-IDed docs according to the collection map + */ +export function renewDocIds({ docArray, collectionMap, idMap = {} }) { + // idMap is a map of {oldId: newId} + // Get a random generator that's consistent on client and server + const randomSrc = DDP.randomStream('renewDocIds'); + + // Give new ids and map the changes as {oldId: newId} + docArray.forEach(doc => { + const oldId = doc._id; + const newId = idMap[oldId] || randomSrc.id(); + doc._id = newId; + idMap[oldId] = newId; + }); + + // Remap all references using the new IDs + const remapReference = ref => { + if (idMap[ref.id]) { + ref.id = idMap[ref.id]; + ref.collection = collectionMap && collectionMap[ref.collection] || ref.collection; + } + } + docArray.forEach(doc => { + remapReference(doc.parent); + remapReference(doc.root); + }); +} + +/** + * Changes the doc to be a child of the parent, and then rebuilds the nested sets of the roots + * of both doc and parent + * @param doc The doc to move + * @param parent The new parent of the doc + * @param collection + * @returns + */ +export async function changeParent(doc: TreeDoc, parent: TreeDoc, collection: Mongo.Collection, order?: number) { + // Skip if we aren't changing the parent id + if (doc.parentId === parent._id) return; + + // Store the original roots + const rootChange = doc.root.id !== parent.root.id; + + // Check that the doc isn't becoming its own ancestor + if (parent.left > doc.left && parent.right < doc.right) { + throw new Meteor.Error('invalid parenting', 'A doc can\'t be its own ancestor'); + } + + // update the document's parenting and root if necessary + const update: Mongo.Modifier = { + $set: { parentId: parent._id } + }; + if (rootChange && update.$set) { + update.$set.root = parent.root; + } + if (order && update.$set) { + update.$set.left = order; + } + await collection.updateAsync(doc._id, update); + + // Rebuild the nested sets of everything on the root document(s) + rebuildNestedSets(collection, doc.root.id); + if (rootChange) { + rebuildNestedSets(collection, parent.root.id); + } +} + +export function compareOrder(docA, docB) { + // < 0 if A comes before B + // = 0 if A and B are the same order + // > 0 if B comes before A + + // They must share a root ancestor to be meaningfully sorted + if (docA.root.id !== docB.root.id) { + return 0; + } else { + return docA.left - docB.left; + } +} + +/** + * @deprecated Just set left to Number.MAX_SAFE_INTEGER instead + */ +export function setDocToLastOrder(collection: Mongo.Collection, doc: TreeDoc) { + doc.left = Number.MAX_SAFE_INTEGER; +} + +export async function rebuildNestedSets(collection: Mongo.Collection, rootId: string) { + const docs = collection.find({ + 'root.id': rootId, + removed: { $ne: true } + }, { + fields: { + root: 1, + parentId: 1, + left: 1, + right: 1, + }, + sort: { + //Reverse sorting so that arrays can be used as stacks with the first item on top + left: 1, + }, + }).fetch(); + + const operations = calculateNestedSetOperations(docs); + + await writeBulkOperations(collection, operations); +} + +export function calculateNestedSetOperations(docs: TreeDoc[]) { + // Walk around the tree numbering left on the way down and right on the way up like so: + /* + * 1 Books 12 + * ┃ + * 2 Programming 11 + * ┏━━━━━━━━┻━━━━━━━━━┓ + * 3 Languages 4 5 Databases 10 + * ┏━━━━━━━┻━━━━━━━┓ + * 6 MongoDB 7 8 dbm 9 + */ + // Get the forest, but in reverse order so that the stack always has the first documents on top + const { forest: stack, orphanIds } = docsToForestByParentId(reverse(docs)); + const removeMissingParentsOp = orphanIds.length ? { + updateMany: { + filter: { _id: { $in: orphanIds } }, + update: { $unset: { parentId: 1 } }, + } + } : undefined; + const visitedNodes = new Set(); + const visitedChildren = new Set(); + const opsById: { [_id: string]: any } = {} + let count = 1; + + while (stack.length) { + const top = stack[stack.length - 1]; + if (visitedNodes.has(top)) { + // We've arrived at this node again for some reason, this shouldn't happen + console.log('visited already, parent loop maybe?') + stack.pop(); + } else if (visitedChildren.has(top)) { + // We've arrived at this node after visiting the children, + // we must be on the way up, mark the right number + visitedNodes.add(top); + stack.pop(); + if (top.doc.right !== count) { + opsById[top.doc._id].updateOne.update.$set.right = count; + } + count += 1; + } else { + // We're arriving at this node for the first time + // We must be on the way down, mark the left number and go visit the children + visitedChildren.add(top); + stack.push(...top.children); + if (top.doc.left !== count) { + opsById[top.doc._id] = { + updateOne: { + filter: { _id: top.doc._id }, + update: { $set: { left: count } }, + } + }; + } + count += 1; + } + } + + const operations = [...Object.values(opsById)]; + if (removeMissingParentsOp) operations.push(removeMissingParentsOp); + return operations; +} + +/** + * Write some number of bulk operations to the collection, uses a bulk write on the server + * and iterates through regular updates on the client + * Resolves once all writes have completed + * @param collection The collection to write to + * @param operations An array of bulk operations to write + * @returns Promise + */ +async function writeBulkOperations(collection: Mongo.Collection, operations) { + if (Meteor.isServer && operations.length) { + return new Promise((resolve, reject) => { + collection.rawCollection().bulkWrite( + operations, + { ordered: false }, + function (e) { + if (e) { + reject(e); + } else { + resolve(undefined); + } + } + ); + }); + } else { + const promises = operations.map(op => { + if (op.updateOne) { + return collection.updateAsync( + op.updateOne.filter, + op.updateOne.update, + { selector: { type: 'any' } } + ); + } else if (op.updateMany) { + return collection.updateAsync( + op.updateMany.filter, + op.updateMany.update, + { + selector: { type: 'any' }, + multi: true, + }, + ) + } + }); + return Promise.all(promises); + } +} diff --git a/app/imports/api/parenting/softRemove.js b/app/imports/api/parenting/softRemove.js deleted file mode 100644 index 099abfc2..00000000 --- a/app/imports/api/parenting/softRemove.js +++ /dev/null @@ -1,74 +0,0 @@ -import getCollectionByName from '/imports/api/parenting/getCollectionByName.js'; -import { updateDescendants } from '/imports/api/parenting/parenting.js'; - -export function softRemove({_id, collection}){ - let removalDate = new Date(); - if (typeof collection === 'string') { - collection = getCollectionByName(collection); - } - // Remove this document - collection.update( - _id, { - $set: { - removed: true, - removedAt: removalDate, - }, - $unset: { - removedWith: 1, - } - }, { - selector: {type: 'any'}, - }, - ); - // Remove all the descendants that have not yet been removed, and set them to be - // removed with this document - updateDescendants({ - collection, - ancestorId: _id, - filter: {removed: {$ne: true}}, - modifier: {$set: { - removed: true, - removedAt: removalDate, - removedWith: _id, - }}, - }); -} - -const restoreError = function(){ - throw new Meteor.Error('restore-failed', - 'Could not restore this document, maybe it was removed by a parent?' - ); -}; - -export function restore({ _id, collection, extraUpdates}){ - if (typeof collection === 'string') { - collection = getCollectionByName(collection); - } - const update = { - $unset: { - removed: 1, - removedAt: 1, - }, - ...extraUpdates - } - - let numUpdated = collection.update({ - _id, - removedWith: {$exists: false} - }, update , { - selector: {type: 'any'}, - },); - if (numUpdated === 0) restoreError(); - updateDescendants({ - collection, - ancestorId: _id, - filter: { - removedWith: _id, - }, - modifier: { $unset: { - removed: 1, - removedAt: 1, - removedWith: 1, - }}, - }); -} diff --git a/app/imports/api/parenting/softRemove.ts b/app/imports/api/parenting/softRemove.ts new file mode 100644 index 00000000..e08cf7ba --- /dev/null +++ b/app/imports/api/parenting/softRemove.ts @@ -0,0 +1,90 @@ +import { getCollectionByName, getFilter } from '/imports/api/parenting/parentingFunctions'; +import { TreeDoc } from '/imports/api/parenting/ChildSchema'; + +export async function softRemove(collection: Mongo.Collection | string, doc?: TreeDoc | string) { + const removalDate = new Date(); + if (typeof collection === 'string') { + collection = getCollectionByName(collection); + } + if (typeof doc === 'string') { + doc = await collection.findOneAsync(doc); + } + if (!doc) { + throw new Meteor.Error('not found', 'The document to remove was not found'); + } + // Remove this document + const removeDocPromise = collection.updateAsync( + doc._id, + { + $set: { + removed: true, + removedAt: removalDate, + }, + $unset: { + removedWith: 1, + } + }, { + selector: { type: 'any' }, + }, + ); + // Remove all the descendants that have not yet been removed, and set them to be + // removed with this document + const removeDescendantsPromise = collection.updateAsync({ + ...getFilter.descendants(doc), + removed: { $ne: true }, + }, { + $set: { + removed: true, + removedAt: removalDate, + removedWith: doc._id, + } + }, { + selector: { type: 'any' }, + multi: true, + }); + return Promise.all([removeDocPromise, removeDescendantsPromise]); +} + +const restoreError = function () { + throw new Meteor.Error('restore-failed', + 'Could not restore this document, maybe it was removed by a parent?' + ); +}; + +export async function restore(collection: Mongo.Collection | string, doc: TreeDoc | string, extraUpdates) { + if (typeof collection === 'string') { + collection = getCollectionByName(collection); + } + if (typeof doc === 'string') { + const foundDoc = await collection.findOneAsync(doc) + if (!foundDoc) { + throw new Meteor.Error('not found', 'The document to remove was not found'); + } + doc = foundDoc; + } + const numUpdated: number = await collection.updateAsync({ + _id: doc._id, + removedWith: { $exists: false } + }, { + $unset: { + removed: 1, + removedAt: 1, + }, + ...extraUpdates + }, { + selector: { type: 'any' }, + }); + if (numUpdated === 0) restoreError(); + return collection.updateAsync({ + removedWith: doc._id, + }, { + $unset: { + removed: 1, + removedAt: 1, + removedWith: 1, + } + }, { + selector: { type: 'any' }, + multi: true, + }); +} diff --git a/app/package-lock.json b/app/package-lock.json index a772ebfa..23d1c5c1 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -4,6 +4,700 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "optional": true, + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "@aws-crypto/ie11-detection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-3.0.0.tgz", + "integrity": "sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==", + "optional": true, + "requires": { + "tslib": "^1.11.1" + } + }, + "@aws-crypto/sha256-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-3.0.0.tgz", + "integrity": "sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==", + "optional": true, + "requires": { + "@aws-crypto/ie11-detection": "^3.0.0", + "@aws-crypto/sha256-js": "^3.0.0", + "@aws-crypto/supports-web-crypto": "^3.0.0", + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "@aws-crypto/sha256-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-3.0.0.tgz", + "integrity": "sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==", + "optional": true, + "requires": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "@aws-crypto/supports-web-crypto": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-3.0.0.tgz", + "integrity": "sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==", + "optional": true, + "requires": { + "tslib": "^1.11.1" + } + }, + "@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "optional": true, + "requires": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "@aws-sdk/client-cognito-identity": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.410.0.tgz", + "integrity": "sha512-J4iPhXswm66Fsk1x0Kly+PWzBizmms4kkkoAU1sk9n08XfWqNBTyf01mx6/t/X+Yh43p2zaeB/XvUwa0jSsWaQ==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/client-sts": "3.410.0", + "@aws-sdk/credential-provider-node": "3.410.0", + "@aws-sdk/middleware-host-header": "3.410.0", + "@aws-sdk/middleware-logger": "3.410.0", + "@aws-sdk/middleware-recursion-detection": "3.410.0", + "@aws-sdk/middleware-signing": "3.410.0", + "@aws-sdk/middleware-user-agent": "3.410.0", + "@aws-sdk/types": "3.410.0", + "@aws-sdk/util-endpoints": "3.410.0", + "@aws-sdk/util-user-agent-browser": "3.410.0", + "@aws-sdk/util-user-agent-node": "3.410.0", + "@smithy/config-resolver": "^2.0.7", + "@smithy/fetch-http-handler": "^2.1.2", + "@smithy/hash-node": "^2.0.6", + "@smithy/invalid-dependency": "^2.0.6", + "@smithy/middleware-content-length": "^2.0.8", + "@smithy/middleware-endpoint": "^2.0.6", + "@smithy/middleware-retry": "^2.0.9", + "@smithy/middleware-serde": "^2.0.6", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.9", + "@smithy/node-http-handler": "^2.1.2", + "@smithy/protocol-http": "^3.0.2", + "@smithy/smithy-client": "^2.1.3", + "@smithy/types": "^2.3.0", + "@smithy/url-parser": "^2.0.6", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.7", + "@smithy/util-defaults-mode-node": "^2.0.9", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/client-sso": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.410.0.tgz", + "integrity": "sha512-MC9GrgwtlOuSL2WS3DRM3dQ/5y+49KSMMJRH6JiEcU5vE0dX/OtEcX+VfEwpi73x5pSfIjm7xnzjzOFx+sQBIg==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.410.0", + "@aws-sdk/middleware-logger": "3.410.0", + "@aws-sdk/middleware-recursion-detection": "3.410.0", + "@aws-sdk/middleware-user-agent": "3.410.0", + "@aws-sdk/types": "3.410.0", + "@aws-sdk/util-endpoints": "3.410.0", + "@aws-sdk/util-user-agent-browser": "3.410.0", + "@aws-sdk/util-user-agent-node": "3.410.0", + "@smithy/config-resolver": "^2.0.7", + "@smithy/fetch-http-handler": "^2.1.2", + "@smithy/hash-node": "^2.0.6", + "@smithy/invalid-dependency": "^2.0.6", + "@smithy/middleware-content-length": "^2.0.8", + "@smithy/middleware-endpoint": "^2.0.6", + "@smithy/middleware-retry": "^2.0.9", + "@smithy/middleware-serde": "^2.0.6", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.9", + "@smithy/node-http-handler": "^2.1.2", + "@smithy/protocol-http": "^3.0.2", + "@smithy/smithy-client": "^2.1.3", + "@smithy/types": "^2.3.0", + "@smithy/url-parser": "^2.0.6", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.7", + "@smithy/util-defaults-mode-node": "^2.0.9", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/client-sts": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.410.0.tgz", + "integrity": "sha512-e6VMrBJtnTxxUXwDmkADGIvyppmDMFf4+cGGA68tVCUm1cFNlCI6M/67bVSIPN/WVKAAfhEL5O2vVXCM7aatYg==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/credential-provider-node": "3.410.0", + "@aws-sdk/middleware-host-header": "3.410.0", + "@aws-sdk/middleware-logger": "3.410.0", + "@aws-sdk/middleware-recursion-detection": "3.410.0", + "@aws-sdk/middleware-sdk-sts": "3.410.0", + "@aws-sdk/middleware-signing": "3.410.0", + "@aws-sdk/middleware-user-agent": "3.410.0", + "@aws-sdk/types": "3.410.0", + "@aws-sdk/util-endpoints": "3.410.0", + "@aws-sdk/util-user-agent-browser": "3.410.0", + "@aws-sdk/util-user-agent-node": "3.410.0", + "@smithy/config-resolver": "^2.0.7", + "@smithy/fetch-http-handler": "^2.1.2", + "@smithy/hash-node": "^2.0.6", + "@smithy/invalid-dependency": "^2.0.6", + "@smithy/middleware-content-length": "^2.0.8", + "@smithy/middleware-endpoint": "^2.0.6", + "@smithy/middleware-retry": "^2.0.9", + "@smithy/middleware-serde": "^2.0.6", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.9", + "@smithy/node-http-handler": "^2.1.2", + "@smithy/protocol-http": "^3.0.2", + "@smithy/smithy-client": "^2.1.3", + "@smithy/types": "^2.3.0", + "@smithy/url-parser": "^2.0.6", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.7", + "@smithy/util-defaults-mode-node": "^2.0.9", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "fast-xml-parser": "4.2.5", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-cognito-identity": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.410.0.tgz", + "integrity": "sha512-2QMvdnwnYsKnwy8O+o9ozKL80VFWI0skXVvKB3DFW4cr9IX5cBCx7xuhI7TXbCqiBxuz5SSiA1s19fVtq0sUmw==", + "optional": true, + "requires": { + "@aws-sdk/client-cognito-identity": "3.410.0", + "@aws-sdk/types": "3.410.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-env": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.410.0.tgz", + "integrity": "sha512-c7TB9LbN0PkFOsXI0lcRJnqPNOmc4VBvrHf8jP/BkTDg4YUoKQKOFd4d0SqzODmlZiAyoMQVZTR4ISZo95Zj4Q==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-ini": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.410.0.tgz", + "integrity": "sha512-D8rcr5bRCFD0f42MPQ7K6TWZq5d3pfqrKINL1/bpfkK5BJbvq1BGYmR88UC6CLpTRtZ1LHY2HgYG0fp/2zjjww==", + "optional": true, + "requires": { + "@aws-sdk/credential-provider-env": "3.410.0", + "@aws-sdk/credential-provider-process": "3.410.0", + "@aws-sdk/credential-provider-sso": "3.410.0", + "@aws-sdk/credential-provider-web-identity": "3.410.0", + "@aws-sdk/types": "3.410.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-node": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.410.0.tgz", + "integrity": "sha512-0wmVm33T/j1FS7MZ/j+WsPlgSc0YnCXnpbWSov1Mn6R86SHI2b2JhdIPRRE4XbGfyW2QGNUl2CwoZVaqhXeF5g==", + "optional": true, + "requires": { + "@aws-sdk/credential-provider-env": "3.410.0", + "@aws-sdk/credential-provider-ini": "3.410.0", + "@aws-sdk/credential-provider-process": "3.410.0", + "@aws-sdk/credential-provider-sso": "3.410.0", + "@aws-sdk/credential-provider-web-identity": "3.410.0", + "@aws-sdk/types": "3.410.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-process": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.410.0.tgz", + "integrity": "sha512-BMju1hlDCDNkkSZpKF5SQ8G0WCLRj6/Jvw9QmudLHJuVwYJXEW1r2AsVMg98OZ3hB9G+MAvHruHZIbMiNmUMXQ==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-sso": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.410.0.tgz", + "integrity": "sha512-zEaoY/sY+KYTlQUkp9dvveAHf175b8RIt0DsQkDrRPtrg/RBHR00r5rFvz9+nrwsR8546RaBU7h/zzTaQGhmcA==", + "optional": true, + "requires": { + "@aws-sdk/client-sso": "3.410.0", + "@aws-sdk/token-providers": "3.410.0", + "@aws-sdk/types": "3.410.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/credential-provider-web-identity": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.410.0.tgz", + "integrity": "sha512-cE0l8LmEHdWbDkdPNgrfdYSgp4/cIVXrjUKI1QCATA729CrHZ/OQjB/maOBOrMHO9YTiggko887NkslVvwVB7w==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/credential-providers": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.410.0.tgz", + "integrity": "sha512-QcunzQRNi9dJdAGdduST7itRW+QhDrb9zZHn+HhLKUoVwLrqk1iuH2R9SoEdZg8eV5jR04yoOPdjj1jzdIkFXQ==", + "optional": true, + "requires": { + "@aws-sdk/client-cognito-identity": "3.410.0", + "@aws-sdk/client-sso": "3.410.0", + "@aws-sdk/client-sts": "3.410.0", + "@aws-sdk/credential-provider-cognito-identity": "3.410.0", + "@aws-sdk/credential-provider-env": "3.410.0", + "@aws-sdk/credential-provider-ini": "3.410.0", + "@aws-sdk/credential-provider-node": "3.410.0", + "@aws-sdk/credential-provider-process": "3.410.0", + "@aws-sdk/credential-provider-sso": "3.410.0", + "@aws-sdk/credential-provider-web-identity": "3.410.0", + "@aws-sdk/types": "3.410.0", + "@smithy/credential-provider-imds": "^2.0.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/middleware-host-header": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.410.0.tgz", + "integrity": "sha512-ED/OVcyITln5rrxnajZP+V0PN1nug+gSDHJDqdDo/oLy7eiDr/ZWn3nlWW7WcMplQ1/Jnb+hK0UetBp/25XooA==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "@smithy/protocol-http": "^3.0.2", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/middleware-logger": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.410.0.tgz", + "integrity": "sha512-YtmKYCVtBfScq3/UFJk+aSZOktKJBNZL9DaSc2aPcy/goCVsYDOkGwtHk0jIkC1JRSNCkVTqL7ya60sSr8zaQQ==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/middleware-recursion-detection": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.410.0.tgz", + "integrity": "sha512-KWaes5FLzRqj28vaIEE4Bimpga2E596WdPF2HaH6zsVMJddoRDsc3ZX9ZhLOGrXzIO1RqBd0QxbLrM0S/B2aOQ==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "@smithy/protocol-http": "^3.0.2", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/middleware-sdk-sts": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.410.0.tgz", + "integrity": "sha512-YfBpctDocRR4CcROoDueJA7D+aMLBV8nTFfmVNdLLLgyuLZ/AUR11VQSu1lf9gQZKl8IpKE/BLf2fRE/qV1ZuA==", + "optional": true, + "requires": { + "@aws-sdk/middleware-signing": "3.410.0", + "@aws-sdk/types": "3.410.0", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/middleware-signing": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.410.0.tgz", + "integrity": "sha512-KBAZ/eoAJUSJv5us2HsKwK2OszG2s9FEyKpEhgnHLcbbKzW873zHBH5GcOGEQu4AWArTy2ndzJu3FF+9/J9hJQ==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.2", + "@smithy/signature-v4": "^2.0.0", + "@smithy/types": "^2.3.0", + "@smithy/util-middleware": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/middleware-user-agent": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.410.0.tgz", + "integrity": "sha512-ZayDtLfvCZUohSxQc/49BfoU/y6bDHLfLdyyUJbJ54Sv8zQcrmdyKvCBFUZwE6tHQgAmv9/ZT18xECMl+xiONA==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "@aws-sdk/util-endpoints": "3.410.0", + "@smithy/protocol-http": "^3.0.2", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/token-providers": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.410.0.tgz", + "integrity": "sha512-d5Nc0xydkH/X0LA1HDyhGY5sEv4LuADFk+QpDtT8ogLilcre+b1jpdY8Sih/gd1KoGS1H+d1tz2hSGwUHAbUbw==", + "optional": true, + "requires": { + "@aws-crypto/sha256-browser": "3.0.0", + "@aws-crypto/sha256-js": "3.0.0", + "@aws-sdk/middleware-host-header": "3.410.0", + "@aws-sdk/middleware-logger": "3.410.0", + "@aws-sdk/middleware-recursion-detection": "3.410.0", + "@aws-sdk/middleware-user-agent": "3.410.0", + "@aws-sdk/types": "3.410.0", + "@aws-sdk/util-endpoints": "3.410.0", + "@aws-sdk/util-user-agent-browser": "3.410.0", + "@aws-sdk/util-user-agent-node": "3.410.0", + "@smithy/config-resolver": "^2.0.7", + "@smithy/fetch-http-handler": "^2.1.2", + "@smithy/hash-node": "^2.0.6", + "@smithy/invalid-dependency": "^2.0.6", + "@smithy/middleware-content-length": "^2.0.8", + "@smithy/middleware-endpoint": "^2.0.6", + "@smithy/middleware-retry": "^2.0.9", + "@smithy/middleware-serde": "^2.0.6", + "@smithy/middleware-stack": "^2.0.0", + "@smithy/node-config-provider": "^2.0.9", + "@smithy/node-http-handler": "^2.1.2", + "@smithy/property-provider": "^2.0.0", + "@smithy/protocol-http": "^3.0.2", + "@smithy/shared-ini-file-loader": "^2.0.6", + "@smithy/smithy-client": "^2.1.3", + "@smithy/types": "^2.3.0", + "@smithy/url-parser": "^2.0.6", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-body-length-browser": "^2.0.0", + "@smithy/util-body-length-node": "^2.1.0", + "@smithy/util-defaults-mode-browser": "^2.0.7", + "@smithy/util-defaults-mode-node": "^2.0.9", + "@smithy/util-retry": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/types": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.410.0.tgz", + "integrity": "sha512-D7iaUCszv/v04NDaZUmCmekamy6VD/lKozm/3gS9+dkfU6cC2CsNoUfPV8BlV6dPdw0oWgF91am3I1stdvfVrQ==", + "optional": true, + "requires": { + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/util-endpoints": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.410.0.tgz", + "integrity": "sha512-iNiqJyC7N3+8zFwnXUqcWSxrZecVZLToo1iTQQdeYL2af1IcOtRgb7n8jpAI/hmXhBSx2+3RI+Y7pxyFo1vu+w==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/util-locate-window": { + "version": "3.310.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz", + "integrity": "sha512-qo2t/vBTnoXpjKxlsC2e1gBrRm80M3bId27r0BRB2VniSSe7bL1mmzM+/HFtujm0iAxtPM+aLEflLJlJeDPg0w==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/util-user-agent-browser": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.410.0.tgz", + "integrity": "sha512-i1G/XGpXGMRT2zEiAhi1xucJsfCWk8nNYjk/LbC0sA+7B9Huri96YAzVib12wkHPsJQvZxZC6CpQDIHWm4lXMA==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "@smithy/types": "^2.3.0", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/util-user-agent-node": { + "version": "3.410.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.410.0.tgz", + "integrity": "sha512-bK70t1jHRl8HrJXd4hEIwc5PBZ7U0w+81AKFnanIVKZwZedd6nLibUXDTK14z/Jp2GFcBqd4zkt2YLGkRt/U4A==", + "optional": true, + "requires": { + "@aws-sdk/types": "3.410.0", + "@smithy/node-config-provider": "^2.0.9", + "@smithy/types": "^2.3.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "optional": true, + "requires": { + "tslib": "^2.3.1" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, "@babel/code-frame": { "version": "7.12.11", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", @@ -164,6 +858,15 @@ "tar": "^6.1.11" } }, + "@mongodb-js/saslprep": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.0.tgz", + "integrity": "sha512-Xfijy7HvfzzqiOAhAepF4SGN5e9leLkMvg/OPOF97XemjfVCYN/oWa75wnkc6mltMSTwY+XlbhWgUOJmkFspSw==", + "optional": true, + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -190,29 +893,834 @@ "fastq": "^1.6.0" } }, + "@smithy/abort-controller": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-2.0.7.tgz", + "integrity": "sha512-rITz65zk8QA3GQ1OeoJ3/Q4+8j/HqubWU8TBqk57BMYTOX+P+LNMoVHPqzLHhE6qKot5muhThNCYvOKNt7ojJA==", + "optional": true, + "requires": { + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/config-resolver": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-2.0.8.tgz", + "integrity": "sha512-e7mwQteHjo9S1GK+TfzP3o7ujE2ZK30d6wkv5brKtabrZF7MBflj9CwUP2XYuOYebdWirHOtv8ZfkMrpcbJfYw==", + "optional": true, + "requires": { + "@smithy/node-config-provider": "^2.0.10", + "@smithy/types": "^2.3.1", + "@smithy/util-config-provider": "^2.0.0", + "@smithy/util-middleware": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/credential-provider-imds": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-2.0.10.tgz", + "integrity": "sha512-may2/gYlDip2rjlU1Z5fcCEWY0Fu3tSu/HykgZrLfb2/171P6OYuz7dGNKBOCS1W57vP4W5wmUhm0WGehrixig==", + "optional": true, + "requires": { + "@smithy/node-config-provider": "^2.0.10", + "@smithy/property-provider": "^2.0.8", + "@smithy/types": "^2.3.1", + "@smithy/url-parser": "^2.0.7", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/eventstream-codec": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.7.tgz", + "integrity": "sha512-sW3AhXZhmmhh0f11EOotmNNa0rjrKwnMYNKfbp3B/qigdw6foKcmFGX+HF3XGN7w7fFeEFuXr97Ok24gRj92Xg==", + "optional": true, + "requires": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^2.3.1", + "@smithy/util-hex-encoding": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/fetch-http-handler": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-2.1.3.tgz", + "integrity": "sha512-kUg+Ey4mJeR/3+Ponuhb1rsmsfZRwjCLvC+WcPgeI+ittretEzuWAPN+9anD0HJEoApVjHpndzxPtlncbCUJDQ==", + "optional": true, + "requires": { + "@smithy/protocol-http": "^3.0.3", + "@smithy/querystring-builder": "^2.0.7", + "@smithy/types": "^2.3.1", + "@smithy/util-base64": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/hash-node": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-2.0.7.tgz", + "integrity": "sha512-aB5lvIDP1v+ZUUS8ek3XW5xnZ6jUQ86JXqG7a5jMP6AbjAc3439mIbs6+f1EQ5MtYmrQCEtRRyvv5QofvotH0w==", + "optional": true, + "requires": { + "@smithy/types": "^2.3.1", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/invalid-dependency": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-2.0.7.tgz", + "integrity": "sha512-qVOZnHFPzQo4BS47/PANHX32Y69c0tJxKBkqTL795D/DKInqBwmBO/m1gS7v0ZQqmtCuoy2l87RflQfRY2xEIw==", + "optional": true, + "requires": { + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/is-array-buffer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.0.0.tgz", + "integrity": "sha512-z3PjFjMyZNI98JFRJi/U0nGoLWMSJlDjAW4QUX2WNZLas5C0CmVV6LJ01JI0k90l7FvpmixjWxPFmENSClQ7ug==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/middleware-content-length": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-2.0.9.tgz", + "integrity": "sha512-2XVFsGqswxrIBi0w4Njwzb1zsbte26U513K+WPFm9z6SB/3WR5/VBVjTaTcamrXznTAqBjTwTL0Ysisv1dW0Rw==", + "optional": true, + "requires": { + "@smithy/protocol-http": "^3.0.3", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/middleware-endpoint": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-2.0.7.tgz", + "integrity": "sha512-4/L0wV7PzHEprJB0gazSTIwlW/2cCfwC9EHavUMhoCyl1tLer6CJwDbAMit1IMvwbHkwuKopueb8dFPHfpS2Pw==", + "optional": true, + "requires": { + "@smithy/middleware-serde": "^2.0.7", + "@smithy/types": "^2.3.1", + "@smithy/url-parser": "^2.0.7", + "@smithy/util-middleware": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/middleware-retry": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-2.0.10.tgz", + "integrity": "sha512-VwAQOR5Rh/y9BzUgb5DzUk7qYBiMZu3pEQa5EwwAf/F7lpMuNildGrAxtDmsXk90490FJwa6LyFknXP3kO5BnA==", + "optional": true, + "requires": { + "@smithy/node-config-provider": "^2.0.10", + "@smithy/protocol-http": "^3.0.3", + "@smithy/service-error-classification": "^2.0.0", + "@smithy/types": "^2.3.1", + "@smithy/util-middleware": "^2.0.0", + "@smithy/util-retry": "^2.0.0", + "tslib": "^2.5.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true + } + } + }, + "@smithy/middleware-serde": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-2.0.7.tgz", + "integrity": "sha512-tOldis4PUNafdGErLZ+33p9Pf3MmTlLa176X321Z6ZaCf1XNEow9m3T5vXrcHErVAvjPG0mp3l54J94HnPc+rQ==", + "optional": true, + "requires": { + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/middleware-stack": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-2.0.0.tgz", + "integrity": "sha512-31XC1xNF65nlbc16yuh3wwTudmqs6qy4EseQUGF8A/p2m/5wdd/cnXJqpniy/XvXVwkHPz/GwV36HqzHtIKATQ==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/node-config-provider": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-2.0.10.tgz", + "integrity": "sha512-e5MiLH5Eu+BbYsmhZIkvUKCzite6JCBPL75PNjlRK2TWvSpfp19hNf2SiJIQbPalcFj5zlyBvtcEkF1sfYIdhg==", + "optional": true, + "requires": { + "@smithy/property-provider": "^2.0.8", + "@smithy/shared-ini-file-loader": "^2.0.9", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/node-http-handler": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-2.1.3.tgz", + "integrity": "sha512-TGkgpx68SqvbspVHaG3iwqP2mKYOT4whiq7Kv2X9v+InngL4MkpH3LQ0Dk7kbloahZr+hAOyb6s8D7T8TXRrzA==", + "optional": true, + "requires": { + "@smithy/abort-controller": "^2.0.7", + "@smithy/protocol-http": "^3.0.3", + "@smithy/querystring-builder": "^2.0.7", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/property-provider": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-2.0.8.tgz", + "integrity": "sha512-oaaP/i7bGG8XbxG9Kx4PZh83iJ2jo/vt8RmJdi9hmc8APBaW1HGDperVXDCyPQdVYXmiqrtxc/rPImyBma1G3A==", + "optional": true, + "requires": { + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/protocol-http": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-3.0.3.tgz", + "integrity": "sha512-UGfmQNdijlFV+UzgdRyfe05S5vLDdcdkvNcxhGvQ+Er7TjUkZSxjukQB9VXtT8oTHztgOMX74DDlPBsVzZR5Pg==", + "optional": true, + "requires": { + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/querystring-builder": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-2.0.7.tgz", + "integrity": "sha512-RPHnqt4iH1Kwp1Zbf4gJI88hZiynEZjE5hEWJNBmKqCe1Q6v7HBLtaovTaiuYaMEmPyb2KxOi3lISAdT6uuPqw==", + "optional": true, + "requires": { + "@smithy/types": "^2.3.1", + "@smithy/util-uri-escape": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/querystring-parser": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-2.0.7.tgz", + "integrity": "sha512-Cwi/Hgs73nbLKfgH7dXAxzvDxyTrK+BLrlAd0KXU7xcBR94V132nvxoq39BMWckYAPmnMwxCwq8uusNH4Dnagw==", + "optional": true, + "requires": { + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/service-error-classification": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-2.0.0.tgz", + "integrity": "sha512-2z5Nafy1O0cTf69wKyNjGW/sNVMiqDnb4jgwfMG8ye8KnFJ5qmJpDccwIbJNhXIfbsxTg9SEec2oe1cexhMJvw==", + "optional": true + }, + "@smithy/shared-ini-file-loader": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-2.0.9.tgz", + "integrity": "sha512-vBLgJI+Qpz1TZ0W2kUBOmG2Q+geVEhiXE99UX02+UFag2WzOQ6frvV6rpadwJu0uwF02GG620NbiKGboqZ19YA==", + "optional": true, + "requires": { + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/signature-v4": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-2.0.7.tgz", + "integrity": "sha512-qNCJpyhRWxT5RWmeSo/Zv+miQ60Y/D2JmPdFw7v2WpPVxVK7JDpqUbvq0QYE+dBGPX/uagAkE3NvJUcn0fTE3A==", + "optional": true, + "requires": { + "@smithy/eventstream-codec": "^2.0.7", + "@smithy/is-array-buffer": "^2.0.0", + "@smithy/types": "^2.3.1", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-middleware": "^2.0.0", + "@smithy/util-uri-escape": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/smithy-client": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-2.1.4.tgz", + "integrity": "sha512-KRQvYYjEGqvmwnKSAZ8EL0hZvPxGQMYbAKS/AMGq2fuRmwAlinSVJ/fkIs65bZp2oYjcskd1ZgKcP+2UDjNPTQ==", + "optional": true, + "requires": { + "@smithy/middleware-stack": "^2.0.0", + "@smithy/types": "^2.3.1", + "@smithy/util-stream": "^2.0.10", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.3.1.tgz", + "integrity": "sha512-cS48e4Yawb6pGakj7DBJUIPFIkqnUWyXTe2ndPRNagD73b6kEJqTc8bhTyfUve0A+sijK256UKE0J1juAfCeDA==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/url-parser": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-2.0.7.tgz", + "integrity": "sha512-SwMl1Lq3yFR2hzhwWYKg04uJHpfcXWMBPycm4Z8GkLI6Dw7rJNDApEbMtujlYw6pVP2WKbrpaGHjQ9MdP92kMQ==", + "optional": true, + "requires": { + "@smithy/querystring-parser": "^2.0.7", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-base64": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-2.0.0.tgz", + "integrity": "sha512-Zb1E4xx+m5Lud8bbeYi5FkcMJMnn+1WUnJF3qD7rAdXpaL7UjkFQLdmW5fHadoKbdHpwH9vSR8EyTJFHJs++tA==", + "optional": true, + "requires": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-body-length-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-2.0.0.tgz", + "integrity": "sha512-JdDuS4ircJt+FDnaQj88TzZY3+njZ6O+D3uakS32f2VNnDo3vyEuNdBOh/oFd8Df1zSZOuH1HEChk2AOYDezZg==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-body-length-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-2.1.0.tgz", + "integrity": "sha512-/li0/kj/y3fQ3vyzn36NTLGmUwAICb7Jbe/CsWCktW363gh1MOcpEcSO3mJ344Gv2dqz8YJCLQpb6hju/0qOWw==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-buffer-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.0.0.tgz", + "integrity": "sha512-/YNnLoHsR+4W4Vf2wL5lGv0ksg8Bmk3GEGxn2vEQt52AQaPSCuaO5PM5VM7lP1K9qHRKHwrPGktqVoAHKWHxzw==", + "optional": true, + "requires": { + "@smithy/is-array-buffer": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-config-provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-2.0.0.tgz", + "integrity": "sha512-xCQ6UapcIWKxXHEU4Mcs2s7LcFQRiU3XEluM2WcCjjBtQkUN71Tb+ydGmJFPxMUrW/GWMgQEEGipLym4XG0jZg==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-defaults-mode-browser": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-2.0.8.tgz", + "integrity": "sha512-8znx01mkmfKxhiSB2bOF5eMutuCLMd8m2Kh0ulRp8vgzhwRLDJoU6aHSEUoNptbuTAtiFf4u0gpkYC2XfbWwuA==", + "optional": true, + "requires": { + "@smithy/property-provider": "^2.0.8", + "@smithy/types": "^2.3.1", + "bowser": "^2.11.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-defaults-mode-node": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-2.0.10.tgz", + "integrity": "sha512-QUcUckL4ZqDFVwLnh7zStRUnXtTC6hcJZ4FmMqnxlPcL33Rko0sMQwrMDnMdzF3rS3wvqugAaq3zzop1HCluvw==", + "optional": true, + "requires": { + "@smithy/config-resolver": "^2.0.8", + "@smithy/credential-provider-imds": "^2.0.10", + "@smithy/node-config-provider": "^2.0.10", + "@smithy/property-provider": "^2.0.8", + "@smithy/types": "^2.3.1", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-hex-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-2.0.0.tgz", + "integrity": "sha512-c5xY+NUnFqG6d7HFh1IFfrm3mGl29lC+vF+geHv4ToiuJCBmIfzx6IeHLg+OgRdPFKDXIw6pvi+p3CsscaMcMA==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-middleware": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-2.0.0.tgz", + "integrity": "sha512-eCWX4ECuDHn1wuyyDdGdUWnT4OGyIzV0LN1xRttBFMPI9Ff/4heSHVxneyiMtOB//zpXWCha1/SWHJOZstG7kA==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-retry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-2.0.0.tgz", + "integrity": "sha512-/dvJ8afrElasuiiIttRJeoS2sy8YXpksQwiM/TcepqdRVp7u4ejd9C4IQURHNjlfPUT7Y6lCDSa2zQJbdHhVTg==", + "optional": true, + "requires": { + "@smithy/service-error-classification": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-stream": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-2.0.10.tgz", + "integrity": "sha512-2EgK5cBiv9OaDmhSXmsZY8ZByBl1dg/Tbc51iBJ5GkLGVYhaA6/1l6vHHV41m4Im3D0XfZV1tmeLlQgmRnYsTQ==", + "optional": true, + "requires": { + "@smithy/fetch-http-handler": "^2.1.3", + "@smithy/node-http-handler": "^2.1.3", + "@smithy/types": "^2.3.1", + "@smithy/util-base64": "^2.0.0", + "@smithy/util-buffer-from": "^2.0.0", + "@smithy/util-hex-encoding": "^2.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-uri-escape": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-2.0.0.tgz", + "integrity": "sha512-ebkxsqinSdEooQduuk9CbKcI+wheijxEb3utGXkCoYQkJnwTnLbH1JXGimJtUkQwNQbsbuYwG2+aFVyZf5TLaw==", + "optional": true, + "requires": { + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, + "@smithy/util-utf8": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.0.0.tgz", + "integrity": "sha512-rctU1VkziY84n5OXe3bPNpKR001ZCME2JCaBBFgtiM2hfKbHFudc/BkMuPab8hRbLd0j3vbnBTTZ1igBf0wgiQ==", + "optional": true, + "requires": { + "@smithy/util-buffer-from": "^2.0.0", + "tslib": "^2.5.0" + }, + "dependencies": { + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "optional": true + } + } + }, "@tozd/vue-observer-utils": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@tozd/vue-observer-utils/-/vue-observer-utils-0.5.0.tgz", "integrity": "sha512-HeRxWFJB7FXcQigH2LvauiR0l7hA4qqBC6hK9rBeKf076Ew08C4lx3eo7/YmvADt3b8ZP1j+TN0pGCEhKYOhEA==" }, + "@types/connect": { + "version": "3.4.36", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", + "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", + "requires": { + "@types/node": "*" + } + }, + "@types/jquery": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.18.tgz", + "integrity": "sha512-sNm7O6LECFhHmF+3KYo6QIl2fIbjlPYa0PDgDQwfOaEJzwpK20Eub9Ke7VKkGsSJ2K0HUR50S266qYzRX4GlSw==", + "requires": { + "@types/sizzle": "*" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", "dev": true }, + "@types/meteor": { + "version": "2.9.4", + "resolved": "https://registry.npmjs.org/@types/meteor/-/meteor-2.9.4.tgz", + "integrity": "sha512-hJd3hHs9OxK4LdJM483zHIw1ORE7V8iLLs4tmvSERRR2L4yUJaLu+xeBq/rZDEzRdpdxvc1f3ywYzcLsP1KFkg==", + "requires": { + "@types/connect": "*", + "@types/jquery": "*", + "@types/nodemailer": "*", + "@types/react": "*", + "@types/underscore": "*", + "mongodb": "^4.3.1" + } + }, "@types/mocha": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", "dev": true }, + "@types/node": { + "version": "20.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.6.0.tgz", + "integrity": "sha512-najjVq5KN2vsH2U/xyh2opaSEz6cZMR2SetLIlxlj08nOcmPOemJmUK2o4kUzfLqfrWE0PIrNeE16XhYDd3nqg==" + }, + "@types/nodemailer": { + "version": "6.4.10", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.10.tgz", + "integrity": "sha512-oPW/IdhkU3FyZc1dzeqmS+MBjrjZNiiINnrEOrWALzccJlP5xTlbkNr2YnTnnyj9Eqm5ofjRoASEbrCYpA7BrA==", + "requires": { + "@types/node": "*" + } + }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + }, + "@types/react": { + "version": "18.2.21", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.21.tgz", + "integrity": "sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" + }, "@types/semver": { "version": "7.3.13", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, + "@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==" + }, + "@types/underscore": { + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.9.tgz", + "integrity": "sha512-M63wKUdsjDFUfyFt1TCUZHGFk9KDAa5JP0adNUErbm0U45Lr06HtANdYRP+GyleEopEoZ4UyBcdAC5TnW4Uz2w==" + }, + "@types/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==" + }, + "@types/whatwg-url": { + "version": "8.2.2", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz", + "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==", + "requires": { + "@types/node": "*", + "@types/webidl-conversions": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "5.59.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.2.tgz", @@ -241,9 +1749,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -338,9 +1846,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -365,9 +1873,9 @@ }, "dependencies": { "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -690,6 +2198,12 @@ } } }, + "bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "optional": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -708,6 +2222,25 @@ "fill-range": "^7.0.1" } }, + "bson": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bson/-/bson-4.7.2.tgz", + "integrity": "sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==", + "requires": { + "buffer": "^5.6.0" + }, + "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + } + } + }, "buffer": { "version": "4.9.2", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", @@ -934,8 +2467,7 @@ "csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "cytoscape": { "version": "3.25.0", @@ -1213,9 +2745,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -1394,6 +2926,15 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-xml-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz", + "integrity": "sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==", + "optional": true, + "requires": { + "strnum": "^1.0.5" + } + }, "fastq": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", @@ -1739,6 +3280,11 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, + "ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==" + }, "is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -1968,9 +3514,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" } } }, @@ -1979,6 +3525,12 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==" }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "optional": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2848,6 +4400,51 @@ "resolved": "https://registry.npmjs.org/mongo-object/-/mongo-object-0.1.4.tgz", "integrity": "sha512-QtYk0gupWEn2+iB+DDRt1L+WbcNYvJRaHdih/dcqthOa1DbnREUGSs2WGcW478GNYpElflo/yybZXu0sTiRXHg==" }, + "mongodb": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.17.1.tgz", + "integrity": "sha512-MBuyYiPUPRTqfH2dV0ya4dcr2E5N52ocBuZ8Sgg/M030nGF78v855B3Z27mZJnp8PxjnUquEnAtjOsphgMZOlQ==", + "requires": { + "@aws-sdk/credential-providers": "^3.186.0", + "@mongodb-js/saslprep": "^1.1.0", + "bson": "^4.7.2", + "mongodb-connection-string-url": "^2.6.0", + "socks": "^2.7.1" + } + }, + "mongodb-connection-string-url": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz", + "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==", + "requires": { + "@types/whatwg-url": "^8.2.1", + "whatwg-url": "^11.0.0" + }, + "dependencies": { + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "requires": { + "punycode": "^2.1.1" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, "moo": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", @@ -3368,9 +4965,9 @@ "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==" }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -3401,9 +4998,9 @@ }, "dependencies": { "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -3520,6 +5117,20 @@ } } }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" + }, + "socks": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "requires": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + } + }, "sortablejs": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz", @@ -3545,6 +5156,15 @@ "source-map": "^0.6.0" } }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "optional": true, + "requires": { + "memory-pager": "^1.0.2" + } + }, "speakingurl": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", @@ -3618,6 +5238,12 @@ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, + "strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "optional": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -3767,8 +5393,7 @@ "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tsutils": { "version": "3.21.0", @@ -3952,9 +5577,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -4045,9 +5670,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "wrappy": { diff --git a/app/package.json b/app/package.json index db8214ba..4a97e9e8 100644 --- a/app/package.json +++ b/app/package.json @@ -23,6 +23,7 @@ "@babel/runtime": "^7.21.5", "@chenfengyuan/vue-countdown": "^1.1.5", "@tozd/vue-observer-utils": "^0.5.0", + "@types/meteor": "^2.9.4", "aws-sdk": "^2.1373.0", "bcrypt": "^5.1.0", "chroma-js": "^2.4.2", diff --git a/app/jsconfig.json b/app/tsconfig.json similarity index 75% rename from app/jsconfig.json rename to app/tsconfig.json index 82b81ff2..f2595a71 100644 --- a/app/jsconfig.json +++ b/app/tsconfig.json @@ -7,12 +7,17 @@ "strictNullChecks": true, "strictFunctionTypes": true, "baseUrl": ".", + "preserveSymlinks": true, "paths": { "/*": [ "./*" ], + "meteor/*": [ + "node_modules/@types/meteor/*", + ".meteor/local/types/packages.d.ts" + ], "meteor/aldeed:collection2": [ - "packages\\collection2\\collection2.js" + "packages/collection2/collection2.js" ] }, "checkJs": false, From b9ed79d638fa17aa86183131846ea1b69b6a9b25 Mon Sep 17 00:00:00 2001 From: ThaumRystra <9525416+ThaumRystra@users.noreply.github.com> Date: Mon, 18 Sep 2023 20:39:34 +0200 Subject: [PATCH 03/16] iterated --- app/imports/api/parenting/parentingFunctions.ts | 7 +------ app/tsconfig.json | 4 +--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/app/imports/api/parenting/parentingFunctions.ts b/app/imports/api/parenting/parentingFunctions.ts index 34d8eb91..a5a97aa9 100644 --- a/app/imports/api/parenting/parentingFunctions.ts +++ b/app/imports/api/parenting/parentingFunctions.ts @@ -369,12 +369,7 @@ export async function rebuildNestedSets(collection: Mongo.Collection, r 'root.id': rootId, removed: { $ne: true } }, { - fields: { - root: 1, - parentId: 1, - left: 1, - right: 1, - }, + fields: treeDocFields, sort: { //Reverse sorting so that arrays can be used as stacks with the first item on top left: 1, diff --git a/app/tsconfig.json b/app/tsconfig.json index f2595a71..1ade8d6b 100644 --- a/app/tsconfig.json +++ b/app/tsconfig.json @@ -19,9 +19,7 @@ "meteor/aldeed:collection2": [ "packages/collection2/collection2.js" ] - }, - "checkJs": false, - "allowJs": true + } }, "vueCompilerOptions": { "target": 2 // For Vue version <= 2.6.14 From e6963ec8659622e48920278d42735bd49d4c6d44 Mon Sep 17 00:00:00 2001 From: Thaum Rystra <9525416+ThaumRystra@users.noreply.github.com> Date: Thu, 28 Sep 2023 14:16:34 +0200 Subject: [PATCH 04/16] Began the great TypeScript Migration It's helping a lot to move to the new parenting system --- .../creature/archive/ArchiveCreatureFiles.js | 2 +- .../archive/methods/archiveCreatureToFile.js | 18 ++-- .../methods/restoreCreatureFromFile.js | 2 +- ...ureProperties.js => CreatureProperties.ts} | 33 +++++-- .../methods/adjustQuantity.js | 2 +- .../methods/copyPropertyToLibrary.js | 19 ++-- .../methods/damageProperty.js | 8 +- .../methods/duplicateProperty.js | 11 +-- .../creatureProperties/methods/equipItem.js | 2 +- .../creatureProperties/methods/flipToggle.js | 2 +- .../methods/getParentRefByTag.js | 12 +-- .../methods/insertProperty.js | 13 +-- .../methods/insertPropertyFromLibraryNode.js | 15 ++- .../methods/pullFromProperty.js | 2 +- .../methods/pushToProperty.js | 2 +- .../methods/restoreProperty.js | 4 +- .../methods/selectAmmoItem.js | 2 +- .../methods/softRemoveProperty.js | 4 +- .../methods/updateCreatureProperty.js | 2 +- .../creatures/methods/insertCreature.js | 2 +- .../creatures/methods/removeCreature.js | 16 ++-- .../creatures/methods/restCreature.js | 6 +- app/imports/api/creature/log/CreatureLogs.js | 2 +- ...ogContentSchema.js => LogContentSchema.ts} | 13 ++- .../mixins/propagateInheritanceUpdateMixin.js | 18 ++-- app/imports/api/docs/Docs.js | 28 ++---- .../{ActionContext.js => ActionContext.ts} | 4 +- .../api/engine/actions/applyProperty.js | 35 ------- .../api/engine/actions/applyProperty.ts | 38 ++++++++ .../{applyAction.js => applyAction.ts} | 74 ++++++++------ .../applyPropertyByType/applyAdjustment.js | 2 +- .../applyPropertyByType/applyBranch.js | 4 +- .../actions/applyPropertyByType/applyBuff.js | 9 +- .../applyPropertyByType/applyBuffRemover.js | 8 +- .../applyPropertyByType/applyDamage.js | 2 +- .../applyPropertyByType/applyFolder.js | 2 +- .../applyPropertyByType/applyItemAsAmmo.js | 6 +- .../actions/applyPropertyByType/applyNote.js | 2 +- .../actions/applyPropertyByType/applyRoll.js | 2 +- .../applyPropertyByType/applySavingThrow.js | 4 +- .../applyPropertyByType/applyToggle.js | 2 +- .../shared/applyChildren.js | 4 +- .../engine/actions/applyTriggers.testFn.js | 2 +- .../{applyTriggers.js => applyTriggers.ts} | 8 +- app/imports/api/engine/actions/doAction.js | 17 ++-- .../api/engine/actions/doAction.test.js | 2 +- app/imports/api/engine/actions/doCastSpell.js | 4 +- app/imports/api/engine/actions/doCheck.js | 6 +- ...tiveStatus.js => computeInactiveStatus.ts} | 19 ++-- .../computation/buildCreatureComputation.js | 6 +- .../buildCreatureComputation.test.js | 8 +- .../computeCreatureComputation.test.js | 8 +- .../computation/utility/cleanProp.testFn.js | 4 +- .../writeAlteredProperties.js | 2 +- app/imports/api/engine/loadCreatures.js | 21 ++-- app/imports/api/library/LibraryNodes.js | 15 ++- .../api/library/methods/copyLibraryNodeTo.js | 15 ++- .../library/methods/duplicateLibraryNode.js | 4 +- .../library/methods/updateReferenceNode.js | 2 +- app/imports/api/parenting/ChildSchema.ts | 6 +- .../api/parenting/parentingFunctions.test.ts | 96 ++++++++++++++++++- .../api/parenting/parentingFunctions.ts | 10 +- .../api/properties/{Actions.js => Actions.ts} | 49 +++++++++- .../api/properties/{Items.js => Items.ts} | 8 ++ .../api/properties/{Spells.js => Spells.ts} | 26 ++++- .../computedOnlyPropertySchemasIndex.js | 6 +- .../computedPropertySchemasIndex.js | 6 +- .../api/properties/propertySchemasIndex.js | 6 +- .../{computedField.js => computedField.ts} | 28 ++++-- .../subSchemas/createPropertySchema.js | 22 ++--- ...tionField.js => inlineCalculationField.ts} | 17 +++- app/imports/api/sharing/sharing.js | 5 +- app/imports/api/sharing/sharingPermissions.js | 2 +- .../client/ui/components/tree/TreeNode.vue | 2 +- .../ui/components/tree/TreeNodeList.vue | 2 +- .../creature/character/CharacterSheetFab.vue | 4 +- .../creature/character/CreatureRootDialog.vue | 2 +- .../characterSheetTabs/ActionsTab.vue | 2 +- .../character/characterSheetTabs/BuildTab.vue | 4 +- .../characterSheetTabs/FeaturesTab.vue | 2 +- .../characterSheetTabs/InventoryTab.vue | 2 +- .../characterSheetTabs/JournalTab.vue | 2 +- .../characterSheetTabs/SpellsTab.vue | 2 +- .../character/characterSheetTabs/StatsTab.vue | 2 +- .../character/characterSheetTabs/TreeTab.vue | 2 +- .../character/errors/DependencyLoopError.vue | 2 +- .../CharacterSheetPrinted.vue | 2 +- .../PrintedInventory.vue | 2 +- .../printedCharacterSheet/PrintedSpells.vue | 2 +- .../printedCharacterSheet/PrintedStats.vue | 2 +- .../components/PrintedAction.vue | 4 +- .../creatureProperties/Breadcrumbs.vue | 2 +- .../CreaturePropertiesTree.vue | 2 +- .../CreaturePropertyDialog.vue | 2 +- .../ui/creature/slots/LevelUpDialog.vue | 2 +- .../ui/creature/slots/SlotCardsToFill.vue | 2 +- .../ui/creature/slots/SlotFillDialog.vue | 2 +- .../components/actions/ActionCard.vue | 4 +- .../actions/SelectItemToConsume.vue | 2 +- .../components/attributes/AttributeEffect.vue | 2 +- .../attributes/HealthBarCardContainer.vue | 2 +- .../components/folders/FolderGroupCard.vue | 2 +- .../FolderGroupChildren.vue | 2 +- .../folderGroupComponents/SlotBuildTree.vue | 4 +- .../components/folders/tabFoldersMixin.js | 2 +- .../components/inventory/ContainerCard.vue | 2 +- .../spells/CastSpellWithSlotDialog.vue | 2 +- .../components/spells/SpellListCard.vue | 2 +- .../shared/lists/createListOfProperties.js | 2 +- .../ui/properties/shared/PropertyViewer.vue | 2 +- .../ui/properties/viewers/SkillViewer.vue | 2 +- app/imports/client/ui/sharing/ShareDialog.vue | 2 +- .../ui/tabletop/TabletopActionCards.vue | 2 +- .../migrations/archive/cleanArchiveAt2.js | 2 +- .../migrations/archive/migrateArchive1To2.js | 2 +- .../migrations/archive/migrateArchiveTo1.js | 2 +- app/imports/migrations/server/dbv1/dbv1.js | 2 +- .../migrations/server/dbv1/dbv1.test.js | 2 +- app/imports/migrations/server/dbv2/dbv2.js | 2 +- .../server/cron/deleteSoftRemovedDocuments.js | 2 +- .../server/publications/singleCharacter.js | 2 +- .../server/publications/slotFillers.js | 2 +- .../server/rest/apiPublications/creature.js | 12 +-- app/package-lock.json | 6 +- app/tsconfig.json | 2 + 125 files changed, 607 insertions(+), 389 deletions(-) rename app/imports/api/creature/creatureProperties/{CreatureProperties.js => CreatureProperties.ts} (79%) rename app/imports/api/creature/log/{LogContentSchema.js => LogContentSchema.ts} (86%) rename app/imports/api/engine/actions/{ActionContext.js => ActionContext.ts} (96%) delete mode 100644 app/imports/api/engine/actions/applyProperty.js create mode 100644 app/imports/api/engine/actions/applyProperty.ts rename app/imports/api/engine/actions/applyPropertyByType/{applyAction.js => applyAction.ts} (83%) rename app/imports/api/engine/actions/{applyTriggers.js => applyTriggers.ts} (93%) rename app/imports/api/engine/computation/buildComputation/{computeInactiveStatus.js => computeInactiveStatus.ts} (63%) rename app/imports/api/properties/{Actions.js => Actions.ts} (82%) rename app/imports/api/properties/{Items.js => Items.ts} (88%) rename app/imports/api/properties/{Spells.js => Spells.ts} (75%) rename app/imports/api/properties/subSchemas/{computedField.js => computedField.ts} (84%) rename app/imports/api/properties/subSchemas/{inlineCalculationField.js => inlineCalculationField.ts} (88%) diff --git a/app/imports/api/creature/archive/ArchiveCreatureFiles.js b/app/imports/api/creature/archive/ArchiveCreatureFiles.js index 044e6158..206f187c 100644 --- a/app/imports/api/creature/archive/ArchiveCreatureFiles.js +++ b/app/imports/api/creature/archive/ArchiveCreatureFiles.js @@ -1,7 +1,7 @@ import SimpleSchema from 'simpl-schema'; import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileStorageUsed.js'; -import { CreaturePropertySchema } from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import { CreaturePropertySchema } from '/imports/api/creature/creatureProperties/CreatureProperties'; import { CreatureSchema } from '/imports/api/creature/creatures/Creatures.js'; let createS3FilesCollection; if (Meteor.isServer) { diff --git a/app/imports/api/creature/archive/methods/archiveCreatureToFile.js b/app/imports/api/creature/archive/methods/archiveCreatureToFile.js index 92b6f3bd..ca3f212e 100644 --- a/app/imports/api/creature/archive/methods/archiveCreatureToFile.js +++ b/app/imports/api/creature/archive/methods/archiveCreatureToFile.js @@ -4,18 +4,18 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js'; import Experiences from '/imports/api/creature/experience/Experiences.js'; import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature.js'; import ArchiveCreatureFiles from '/imports/api/creature/archive/ArchiveCreatureFiles.js'; -export function getArchiveObj(creatureId){ +export function getArchiveObj(creatureId) { // Build the archive document const creature = Creatures.findOne(creatureId); - const properties = CreatureProperties.find({'ancestors.id': creatureId}).fetch(); - const experiences = Experiences.find({creatureId}).fetch(); - const logs = CreatureLogs.find({creatureId}).fetch(); + const properties = CreatureProperties.find({ 'ancestors.id': creatureId }).fetch(); + const experiences = Experiences.find({ creatureId }).fetch(); + const logs = CreatureLogs.find({ creatureId }).fetch(); let archiveCreature = { meta: { type: 'DiceCloud V2 Creature Archive', @@ -31,7 +31,7 @@ export function getArchiveObj(creatureId){ return archiveCreature; } -export function archiveCreature(creatureId){ +export function archiveCreature(creatureId) { const archive = getArchiveObj(creatureId); const buffer = Buffer.from(JSON.stringify(archive, null, 2)); ArchiveCreatureFiles.write(buffer, { @@ -44,7 +44,7 @@ export function archiveCreature(creatureId){ creatureName: archive.creature.name, }, }, (error) => { - if (error){ + if (error) { throw error; } else { removeCreatureWork(creatureId); @@ -65,9 +65,9 @@ const archiveCreatureToFile = new ValidatedMethod({ numRequests: 10, timeInterval: 5000, }, - async run({creatureId}) { + async run({ creatureId }) { assertOwnership(creatureId, this.userId); - if (Meteor.isServer){ + if (Meteor.isServer) { archiveCreature(creatureId, this.userId); } else { removeCreatureWork(creatureId); diff --git a/app/imports/api/creature/archive/methods/restoreCreatureFromFile.js b/app/imports/api/creature/archive/methods/restoreCreatureFromFile.js index 112ea06d..516af6e9 100644 --- a/app/imports/api/creature/archive/methods/restoreCreatureFromFile.js +++ b/app/imports/api/creature/archive/methods/restoreCreatureFromFile.js @@ -3,7 +3,7 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js'; import Experiences from '/imports/api/creature/experience/Experiences.js'; import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature.js'; diff --git a/app/imports/api/creature/creatureProperties/CreatureProperties.js b/app/imports/api/creature/creatureProperties/CreatureProperties.ts similarity index 79% rename from app/imports/api/creature/creatureProperties/CreatureProperties.js rename to app/imports/api/creature/creatureProperties/CreatureProperties.ts index b451dd1d..e42d9a6c 100644 --- a/app/imports/api/creature/creatureProperties/CreatureProperties.js +++ b/app/imports/api/creature/creatureProperties/CreatureProperties.ts @@ -1,15 +1,35 @@ import { Mongo } from 'meteor/mongo'; import SimpleSchema from 'simpl-schema'; import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js'; -import ChildSchema from '/imports/api/parenting/ChildSchema.js'; +import ChildSchema, { TreeDoc } from '/imports/api/parenting/ChildSchema'; import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js'; import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex.js'; import { storedIconsSchema } from '/imports/api/icons/Icons.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; -let CreatureProperties = new Mongo.Collection('creatureProperties'); +const CreatureProperties: Mongo.Collection = new Mongo.Collection('creatureProperties'); -let CreaturePropertySchema = new SimpleSchema({ +export interface CreatureProperty extends TreeDoc { + _id: string + _migrationError?: string + type: string + tags: string[] + disabled?: boolean + icon?: { + name: string + shape: string + }, + libraryNodeId?: string + slotQuantityFilled?: number + inactive?: boolean + deactivatedByAncestor?: boolean + deactivatedBySelf?: boolean + deactivatedByToggle?: boolean + deactivatingToggleId?: boolean + dirty?: boolean +} + +const CreaturePropertySchema = new SimpleSchema({ _id: { type: String, regEx: SimpleSchema.RegEx.Id, @@ -56,7 +76,7 @@ let CreaturePropertySchema = new SimpleSchema({ const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({ // Denormalised flag if this property is inactive on the sheet for any reason - // Including being disabled, or a decendent of a disabled property + // Including being disabled, or a descendant of a disabled property inactive: { type: Boolean, optional: true, @@ -105,13 +125,14 @@ const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({ CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema); -for (let key in propertySchemasIndex) { - let schema = new SimpleSchema({}); +for (const key in propertySchemasIndex) { + const schema = new SimpleSchema({}); schema.extend(propertySchemasIndex[key]); schema.extend(CreaturePropertySchema); schema.extend(ColorSchema); schema.extend(ChildSchema); schema.extend(SoftRemovableSchema); + // @ts-expect-error don't have types for .attachSchema CreatureProperties.attachSchema(schema, { selector: { type: key } }); diff --git a/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js b/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js index d08c43bb..1f2874ef 100644 --- a/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js +++ b/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js @@ -1,7 +1,7 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import SimpleSchema from 'simpl-schema'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; diff --git a/app/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary.js b/app/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary.js index b46205b9..0cfcd86a 100644 --- a/app/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary.js +++ b/app/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary.js @@ -1,9 +1,9 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; -import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; +import { RefSchema } from '/imports/api/parenting/ChildSchema'; import { assertEditPermission, assertDocEditPermission, @@ -13,9 +13,8 @@ import { setLineageOfDocs, getAncestry, renewDocIds -} from '/imports/api/parenting/parenting.js'; -import { reorderDocs } from '/imports/api/parenting/order.js'; -import { setDocToLastOrder } from '/imports/api/parenting/order.js'; +} from '/imports/api/parenting/parentingFunctions'; +import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions'; import Libraries from '/imports/api/library/Libraries.js'; const DUPLICATE_CHILDREN_LIMIT = 500; @@ -57,10 +56,7 @@ const copyPropertyToLibrary = new ValidatedMethod({ const insertedRootNode = insertNodeFromProperty(propId, ancestors, order, this); // Tree structure changed by inserts, reorder the tree - reorderDocs({ - collection: LibraryNodes, - ancestorId: rootLibrary._id, - }); + rebuildNestedSets(LibraryNodes, rootLibrary._id); // Return the docId of the inserted root property return insertedRootNode?._id; @@ -124,10 +120,7 @@ function insertNodeFromProperty(propId, ancestors, order, method) { // Order the root node if (order === undefined) { - setDocToLastOrder({ - collection: LibraryNodes, - doc: prop, - }); + rebuildNestedSets(LibraryNodes, prop.root.id); } else { prop.order = order; } diff --git a/app/imports/api/creature/creatureProperties/methods/damageProperty.js b/app/imports/api/creature/creatureProperties/methods/damageProperty.js index a8cd0696..fee7a24e 100644 --- a/app/imports/api/creature/creatureProperties/methods/damageProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/damageProperty.js @@ -1,10 +1,10 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import SimpleSchema from 'simpl-schema'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js'; -import ActionContext from '/imports/api/engine/actions/ActionContext.js'; +import { applyTriggers } from '/imports/api/engine/actions/applyTriggers'; +import ActionContext from '/imports/api/engine/actions/ActionContext'; const damageProperty = new ValidatedMethod({ name: 'creatureProperties.damage', @@ -59,7 +59,7 @@ const damageProperty = new ValidatedMethod({ }, }); -export function damagePropertyWork({ prop, operation, value, actionContext, logFunction }) { +export function damagePropertyWork({ prop, operation, value, actionContext, logFunction = undefined }) { // Save the value to the scope before applying the before triggers if (operation === 'increment') { diff --git a/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js b/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js index 88e2f900..7dafa0f0 100644 --- a/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js @@ -1,14 +1,14 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { setLineageOfDocs, renewDocIds -} from '/imports/api/parenting/parenting.js'; -import { reorderDocs } from '/imports/api/parenting/order.js'; +} from '/imports/api/parenting/parentingFunctions'; +import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions'; var snackbar; if (Meteor.isClient) { snackbar = require( @@ -90,10 +90,7 @@ const duplicateProperty = new ValidatedMethod({ CreatureProperties.batchInsert(allNodes); // Tree structure changed by inserts, reorder the tree - reorderDocs({ - collection: CreatureProperties, - ancestorId: property.ancestors[0].id, - }); + rebuildNestedSets(CreatureProperties, property.root.id); return propertyId; }, diff --git a/app/imports/api/creature/creatureProperties/methods/equipItem.js b/app/imports/api/creature/creatureProperties/methods/equipItem.js index 671908b3..eb0e8057 100644 --- a/app/imports/api/creature/creatureProperties/methods/equipItem.js +++ b/app/imports/api/creature/creatureProperties/methods/equipItem.js @@ -1,5 +1,5 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { organizeDoc } from '/imports/api/parenting/organizeMethods.js'; diff --git a/app/imports/api/creature/creatureProperties/methods/flipToggle.js b/app/imports/api/creature/creatureProperties/methods/flipToggle.js index dbd814ff..1f60bb2c 100644 --- a/app/imports/api/creature/creatureProperties/methods/flipToggle.js +++ b/app/imports/api/creature/creatureProperties/methods/flipToggle.js @@ -1,6 +1,6 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; diff --git a/app/imports/api/creature/creatureProperties/methods/getParentRefByTag.js b/app/imports/api/creature/creatureProperties/methods/getParentRefByTag.js index f5236c9f..bdcb4c10 100644 --- a/app/imports/api/creature/creatureProperties/methods/getParentRefByTag.js +++ b/app/imports/api/creature/creatureProperties/methods/getParentRefByTag.js @@ -1,13 +1,13 @@ -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; -export default function getParentRefByTag(creatureId, tag){ +export default function getParentRefByTag(creatureId, tag) { let prop = CreatureProperties.findOne({ 'ancestors.id': creatureId, - removed: {$ne: true}, - inactive: {$ne: true}, + removed: { $ne: true }, + inactive: { $ne: true }, tags: tag, }, { - sort: {order: 1}, + sort: { order: 1 }, }); - return prop && {id: prop._id, collection: 'creatureProperties'}; + return prop && { id: prop._id, collection: 'creatureProperties' }; } diff --git a/app/imports/api/creature/creatureProperties/methods/insertProperty.js b/app/imports/api/creature/creatureProperties/methods/insertProperty.js index 2d872868..7647b64a 100644 --- a/app/imports/api/creature/creatureProperties/methods/insertProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/insertProperty.js @@ -1,13 +1,13 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import SimpleSchema from 'simpl-schema'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import { reorderDocs } from '/imports/api/parenting/order.js'; -import { getAncestry } from '/imports/api/parenting/parenting.js'; +import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions'; +import { getAncestry } from '/imports/api/parenting/parentingFunctions'; import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js'; -import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; +import { RefSchema } from '/imports/api/parenting/ChildSchema'; import { getHighestOrder } from '/imports/api/parenting/order.js'; const insertProperty = new ValidatedMethod({ @@ -134,10 +134,7 @@ export function insertPropertyWork({ property, creature }) { property.dirty = true; let _id = CreatureProperties.insert(property); // Tree structure changed by insert, reorder the tree - reorderDocs({ - collection: CreatureProperties, - ancestorId: creature._id, - }); + rebuildNestedSets(CreatureProperties, creature._id); return _id; } diff --git a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js index 8f0eefa6..f4b10416 100644 --- a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js +++ b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js @@ -1,19 +1,19 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; -import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; +import { RefSchema } from '/imports/api/parenting/ChildSchema'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { setLineageOfDocs, getAncestry, renewDocIds -} from '/imports/api/parenting/parenting.js'; -import { reorderDocs } from '/imports/api/parenting/order.js'; +} from '/imports/api/parenting/parentingFunctions'; +import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions'; import { setDocToLastOrder } from '/imports/api/parenting/order.js'; -import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; +import { fetchDocByRef } from '/imports/api/parenting/parentingFunctions'; import { union } from 'lodash'; const insertPropertyFromLibraryNode = new ValidatedMethod({ @@ -67,10 +67,7 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({ let rootId = node._id; // Tree structure changed by inserts, reorder the tree - reorderDocs({ - collection: CreatureProperties, - ancestorId: rootCreature._id, - }); + rebuildNestedSets(CreatureProperties, rootCreature._id); // Return the docId of the last property, the inserted root property return rootId; }, diff --git a/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js b/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js index 4dd8245e..d4fc9d33 100644 --- a/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js @@ -1,6 +1,6 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; diff --git a/app/imports/api/creature/creatureProperties/methods/pushToProperty.js b/app/imports/api/creature/creatureProperties/methods/pushToProperty.js index 95735d9b..9fadf1ca 100644 --- a/app/imports/api/creature/creatureProperties/methods/pushToProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/pushToProperty.js @@ -1,6 +1,6 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { get } from 'lodash'; diff --git a/app/imports/api/creature/creatureProperties/methods/restoreProperty.js b/app/imports/api/creature/creatureProperties/methods/restoreProperty.js index 9e66448e..810fc30f 100644 --- a/app/imports/api/creature/creatureProperties/methods/restoreProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/restoreProperty.js @@ -1,9 +1,9 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import SimpleSchema from 'simpl-schema'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import { restore } from '/imports/api/parenting/softRemove.js'; +import { restore } from '/imports/api/parenting/softRemove'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; const restoreProperty = new ValidatedMethod({ diff --git a/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js b/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js index 57bbe6bd..7379859f 100644 --- a/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js +++ b/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js @@ -1,7 +1,7 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import SimpleSchema from 'simpl-schema'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; diff --git a/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js b/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js index a4240ac7..de871b74 100644 --- a/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js @@ -1,9 +1,9 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import SimpleSchema from 'simpl-schema'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import { softRemove } from '/imports/api/parenting/softRemove.js'; +import { softRemove } from '/imports/api/parenting/softRemove'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; const softRemoveProperty = new ValidatedMethod({ diff --git a/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js b/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js index 5664d8bd..63739f50 100644 --- a/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js @@ -1,6 +1,6 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; diff --git a/app/imports/api/creature/creatures/methods/insertCreature.js b/app/imports/api/creature/creatures/methods/insertCreature.js index c978785b..79373ca5 100644 --- a/app/imports/api/creature/creatures/methods/insertCreature.js +++ b/app/imports/api/creature/creatures/methods/insertCreature.js @@ -2,7 +2,7 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js'; import Creatures, { CreatureSchema } from '/imports/api/creature/creatures/Creatures.js'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import defaultCharacterProperties from '/imports/api/creature/creatures/defaultCharacterProperties.js'; import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js'; import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots.js'; diff --git a/app/imports/api/creature/creatures/methods/removeCreature.js b/app/imports/api/creature/creatures/methods/removeCreature.js index d157f5d7..df0b8e5e 100644 --- a/app/imports/api/creature/creatures/methods/removeCreature.js +++ b/app/imports/api/creature/creatures/methods/removeCreature.js @@ -4,15 +4,15 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js'; import Experiences from '/imports/api/creature/experience/Experiences.js'; -function removeRelatedDocuments(creatureId){ - CreatureVariables.remove({_creatureId: creatureId}); - CreatureProperties.remove({'ancestors.id': creatureId}); - CreatureLogs.remove({creatureId}); - Experiences.remove({creatureId}); +function removeRelatedDocuments(creatureId) { + CreatureVariables.remove({ _creatureId: creatureId }); + CreatureProperties.remove({ 'ancestors.id': creatureId }); + CreatureLogs.remove({ creatureId }); + Experiences.remove({ creatureId }); } const removeCreature = new ValidatedMethod({ @@ -28,14 +28,14 @@ const removeCreature = new ValidatedMethod({ numRequests: 5, timeInterval: 5000, }, - run({charId}) { + run({ charId }) { assertOwnership(charId, this.userId) this.unblock(); removeCreatureWork(charId) }, }); -export function removeCreatureWork(creatureId){ +export function removeCreatureWork(creatureId) { Creatures.remove(creatureId); removeRelatedDocuments(creatureId); } diff --git a/app/imports/api/creature/creatures/methods/restCreature.js b/app/imports/api/creature/creatures/methods/restCreature.js index bb351c03..da06d093 100644 --- a/app/imports/api/creature/creatures/methods/restCreature.js +++ b/app/imports/api/creature/creatures/methods/restCreature.js @@ -1,11 +1,11 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { union } from 'lodash'; -import ActionContext from '/imports/api/engine/actions/ActionContext.js'; -import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import ActionContext from '/imports/api/engine/actions/ActionContext'; +import { applyTriggers } from '/imports/api/engine/actions/applyTriggers'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; const restCreature = new ValidatedMethod({ diff --git a/app/imports/api/creature/log/CreatureLogs.js b/app/imports/api/creature/log/CreatureLogs.js index 62fbf798..e3c2b04b 100644 --- a/app/imports/api/creature/log/CreatureLogs.js +++ b/app/imports/api/creature/log/CreatureLogs.js @@ -1,7 +1,7 @@ import SimpleSchema from 'simpl-schema'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js'; -import LogContentSchema from '/imports/api/creature/log/LogContentSchema.js'; +import LogContentSchema from '/imports/api/creature/log/LogContentSchema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; diff --git a/app/imports/api/creature/log/LogContentSchema.js b/app/imports/api/creature/log/LogContentSchema.ts similarity index 86% rename from app/imports/api/creature/log/LogContentSchema.js rename to app/imports/api/creature/log/LogContentSchema.ts index 05d9ce87..5d84d5fe 100644 --- a/app/imports/api/creature/log/LogContentSchema.js +++ b/app/imports/api/creature/log/LogContentSchema.ts @@ -3,6 +3,17 @@ import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js'; import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; +export interface LogContent { + name?: string + value?: string + inline?: boolean + context?: { + errors: any[] + rolls: any[] + doubleRolls?: boolean + } +} + let LogContentSchema = new SimpleSchema({ // The name of the field, included in discord webhook message name: { @@ -26,7 +37,7 @@ let LogContentSchema = new SimpleSchema({ type: Object, optional: true, }, - 'context.errors':{ + 'context.errors': { type: Array, defaultValue: [], maxCount: STORAGE_LIMITS.errorCount, diff --git a/app/imports/api/creature/mixins/propagateInheritanceUpdateMixin.js b/app/imports/api/creature/mixins/propagateInheritanceUpdateMixin.js index 0ed6ee9f..c238e113 100644 --- a/app/imports/api/creature/mixins/propagateInheritanceUpdateMixin.js +++ b/app/imports/api/creature/mixins/propagateInheritanceUpdateMixin.js @@ -1,21 +1,21 @@ import { updateChildren, updateDescendants, -} from '/imports/api/parenting/parenting.js'; -import { inheritedFields } from '/imports/api/parenting/ChildSchema.js'; +} from '/imports/api/parenting/parentingFunctions'; +import { inheritedFields } from '/imports/api/parenting/ChildSchema'; import MONGO_OPERATORS from '/imports/constants/MONGO_OPERATORS.js'; // This mixin can be safely applied to all update methods which are validated // with the updateSchemaMixin. It will propagate updates to fields which // are inherited and normalised on the parent or ancestor docs // It should have neglible performance impact for updates that aren't inherited -function propagateInheritanceUpdate({_id, update}){ +function propagateInheritanceUpdate({ _id, update }) { let childModifier = {}; let descendantModifier = {}; // For each operator - for (let operator of MONGO_OPERATORS){ + for (let operator of MONGO_OPERATORS) { // If the operator is in the update, for each field - if (update[operator]) for (let field in update[operator]){ + if (update[operator]) for (let field in update[operator]) { // Get the top level field that was actually modified const indexOfDot = field.indexOf('.'); let modifiedField; @@ -25,7 +25,7 @@ function propagateInheritanceUpdate({_id, update}){ modifiedField = field; } // If that field is updated and inherited - if (inheritedFields.has(modifiedField)){ + if (inheritedFields.has(modifiedField)) { // Perform the same update on the descendants if (!childModifier[operator]) childModifier[operator] = {}; if (!descendantModifier[operator]) descendantModifier[operator] = {}; @@ -48,11 +48,11 @@ function propagateInheritanceUpdate({_id, update}){ }); } -export default function propagateInheritanceUpdateMixin(methodOptions){ +export default function propagateInheritanceUpdateMixin(methodOptions) { let runFunc = methodOptions.run; - methodOptions.run = function({_id, update}){ + methodOptions.run = function ({ _id, update }) { const result = runFunc.apply(this, arguments); - propagateInheritanceUpdate({_id, update}); + propagateInheritanceUpdate({ _id, update }); return result; }; return methodOptions; diff --git a/app/imports/api/docs/Docs.js b/app/imports/api/docs/Docs.js index cb8aeb4e..6300c896 100644 --- a/app/imports/api/docs/Docs.js +++ b/app/imports/api/docs/Docs.js @@ -3,14 +3,14 @@ import { Mongo } from 'meteor/mongo'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import SimpleSchema from 'simpl-schema'; -import { softRemove } from '/imports/api/parenting/softRemove.js'; +import { softRemove } from '/imports/api/parenting/softRemove'; import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js'; import { storedIconsSchema } from '/imports/api/icons/Icons.js'; import '/imports/api/library/methods/index.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; -import { restore } from '/imports/api/parenting/softRemove.js'; -import { reorderDocs } from '/imports/api/parenting/order.js'; -import { getAncestry } from '/imports/api/parenting/parenting.js'; +import { restore } from '/imports/api/parenting/softRemove'; +import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions'; +import { getAncestry } from '/imports/api/parenting/parentingFunctions'; const Docs = new Mongo.Collection('docs'); @@ -184,10 +184,7 @@ const insertDoc = new ValidatedMethod({ } const docId = Docs.insert(doc); - reorderDocs({ - collection: Docs, - ancestorId: 'root', - }); + rebuildNestedSets(Docs, 'root'); return docId; }, }); @@ -231,10 +228,7 @@ const updateDoc = new ValidatedMethod({ if (pathString === 'name' || pathString === 'urlName') { rebuildDocAncestors(_id); } - reorderDocs({ - collection: Docs, - ancestorId: 'root', - }); + rebuildNestedSets(Docs, 'root'); return updates; }, }); @@ -284,10 +278,7 @@ const softRemoveDoc = new ValidatedMethod({ run({ _id }) { assertDocsEditPermission(this.userId); softRemove({ _id, collection: Docs }); - reorderDocs({ - collection: Docs, - ancestorId: 'root', - }); + rebuildNestedSets(Docs, 'root'); } }); @@ -304,10 +295,7 @@ const restoreDoc = new ValidatedMethod({ run({ _id }) { assertDocsEditPermission(this.userId); restore({ _id, collection: Docs }); - reorderDocs({ - collection: Docs, - ancestorId: 'root', - }); + rebuildNestedSets(Docs, 'root'); } }); diff --git a/app/imports/api/engine/actions/ActionContext.js b/app/imports/api/engine/actions/ActionContext.ts similarity index 96% rename from app/imports/api/engine/actions/ActionContext.js rename to app/imports/api/engine/actions/ActionContext.ts index 9c5e2e50..d7e1311a 100644 --- a/app/imports/api/engine/actions/ActionContext.js +++ b/app/imports/api/engine/actions/ActionContext.ts @@ -4,7 +4,7 @@ import { } from '/imports/api/engine/loadCreatures.js'; import { groupBy, remove } from 'lodash'; -export default class ActionContext{ +export default class ActionContext { constructor(creatureId, targetIds = [], method) { // Get the creature this.creature = getCreature(creatureId) @@ -64,7 +64,7 @@ export default class ActionContext{ } } addLog(content) { - if (content.name || content.value){ + if (content.name || content.value) { this.log.content.push(content); } } diff --git a/app/imports/api/engine/actions/applyProperty.js b/app/imports/api/engine/actions/applyProperty.js deleted file mode 100644 index b8e809d8..00000000 --- a/app/imports/api/engine/actions/applyProperty.js +++ /dev/null @@ -1,35 +0,0 @@ -import action from './applyPropertyByType/applyAction.js'; -import ammo from './applyPropertyByType/applyItemAsAmmo.js' -import adjustment from './applyPropertyByType/applyAdjustment.js'; -import branch from './applyPropertyByType/applyBranch.js'; -import buff from './applyPropertyByType/applyBuff.js'; -import buffRemover from './applyPropertyByType/applyBuffRemover.js'; -import damage from './applyPropertyByType/applyDamage.js'; -import folder from './applyPropertyByType/applyFolder.js'; -import note from './applyPropertyByType/applyNote.js'; -import roll from './applyPropertyByType/applyRoll.js'; -import savingThrow from './applyPropertyByType/applySavingThrow.js'; -import toggle from './applyPropertyByType/applyToggle.js'; - -const applyPropertyByType = { - action, - ammo, - adjustment, - branch, - buff, - buffRemover, - damage, - folder, - note, - propertySlot: folder, - roll, - savingThrow, - spell: action, - toggle, -}; - -export default function applyProperty(node, actionContext, ...rest) { - if (node.node.deactivatedByToggle) return; - actionContext.scope[`#${node.node.type}`] = node.node; - applyPropertyByType[node.node.type]?.(node, actionContext, ...rest); -} diff --git a/app/imports/api/engine/actions/applyProperty.ts b/app/imports/api/engine/actions/applyProperty.ts new file mode 100644 index 00000000..eeb010e6 --- /dev/null +++ b/app/imports/api/engine/actions/applyProperty.ts @@ -0,0 +1,38 @@ +import action from './applyPropertyByType/applyAction'; +import ammo from './applyPropertyByType/applyItemAsAmmo' +import adjustment from './applyPropertyByType/applyAdjustment'; +import branch from './applyPropertyByType/applyBranch'; +import buff from './applyPropertyByType/applyBuff'; +import buffRemover from './applyPropertyByType/applyBuffRemover'; +import damage from './applyPropertyByType/applyDamage'; +import folder from './applyPropertyByType/applyFolder'; +import note from './applyPropertyByType/applyNote'; +import roll from './applyPropertyByType/applyRoll'; +import savingThrow from './applyPropertyByType/applySavingThrow'; +import toggle from './applyPropertyByType/applyToggle'; +import ActionContext from '/imports/api/engine/actions/ActionContext'; +import { TreeNode } from '/imports/api/parenting/parentingFunctions'; +import { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties'; + +const applyPropertyByType = { + action, + ammo, + adjustment, + branch, + buff, + buffRemover, + damage, + folder, + note, + propertySlot: folder, + roll, + savingThrow, + spell: action, + toggle, +}; + +export default function applyProperty(node: TreeNode, actionContext: ActionContext, ...rest) { + if (node.doc.deactivatedByToggle) return; + actionContext.scope[`#${node.doc.type}`] = node.doc; + applyPropertyByType[node.doc.type]?.(node, actionContext, ...rest); +} diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyAction.js b/app/imports/api/engine/actions/applyPropertyByType/applyAction.ts similarity index 83% rename from app/imports/api/engine/actions/applyPropertyByType/applyAction.js rename to app/imports/api/engine/actions/applyPropertyByType/applyAction.ts index b44c1b44..91041ca7 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyAction.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyAction.ts @@ -1,22 +1,31 @@ import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js'; import recalculateCalculation from './shared/recalculateCalculation.js'; import rollDice from '/imports/parser/rollDice.js'; -import applyProperty from '../applyProperty.js'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import applyProperty from '../applyProperty'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; import numberToSignedString from '/imports/api/utility/numberToSignedString.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; import { resetProperties } from '/imports/api/creature/creatures/methods/restCreature.js'; +import { TreeNode } from '/imports/api/parenting/parentingFunctions'; +import { Action } from '/imports/api/properties/Actions'; +import { LogContent } from '/imports/api/creature/log/LogContentSchema.js'; +import { Item } from '/imports/api/properties/Items.js'; -export default function applyAction(node, actionContext) { +interface Ammo extends Item { + type: 'ammo' + adjustment: number +} + +export default function applyAction(node: TreeNode, actionContext) { applyNodeTriggers(node, 'before', actionContext); - const prop = node.node; + const prop = node.doc; if (prop.target === 'self') actionContext.targets = [actionContext.creature]; const targets = actionContext.targets; // Log the name and summary - let content = { name: prop.name }; + const content: LogContent = { name: prop.name, }; if (prop.summary?.text) { recalculateInlineCalculations(prop.summary, actionContext); content.value = prop.summary.value; @@ -27,7 +36,7 @@ export default function applyAction(node, actionContext) { const failed = spendResources(prop, actionContext); if (failed) return; - const attack = prop.attackRoll || prop.attackRollBonus; + const attack = prop.attackRoll; // Attack if there is an attack roll if (attack && attack.calculation) { @@ -59,7 +68,7 @@ function applyAttackWithoutTarget({ attack, actionContext }) { recalculateCalculation(attack, actionContext); const scope = actionContext.scope; - let { + const { resultPrefix, result, criticalHit, @@ -96,7 +105,7 @@ function applyAttackToTarget({ attack, target, actionContext }) { recalculateCalculation(attack, actionContext); - let { + const { resultPrefix, result, criticalHit, @@ -176,7 +185,7 @@ function applyCrits(value, scope) { scopeCrit = scopeCrit.value; } const criticalHitTarget = scopeCrit || 20; - let criticalHit = value >= criticalHitTarget; + const criticalHit = value >= criticalHitTarget; let criticalMiss; if (criticalHit) { scope['~criticalHit'] = { value: true }; @@ -189,9 +198,9 @@ function applyCrits(value, scope) { return { criticalHit, criticalMiss }; } -function spendResources(prop, actionContext) { +function spendResources(prop: Action, actionContext) { // Check Uses - if (prop.usesLeft <= 0) { + if (!prop.usesLeft || prop.usesLeft <= 0) { if (!prop.silent) actionContext.addLog({ name: 'Error', value: `${prop.name || 'action'} does not have enough uses left`, @@ -207,42 +216,45 @@ function spendResources(prop, actionContext) { return true; } // Items - let spendLog = []; - let gainLog = []; - const ammoToApply = []; + const spendLog: string[] = []; + const gainLog: string[] = []; + const ammoToApply: TreeNode[] = []; try { prop.resources.itemsConsumed.forEach(itemConsumed => { recalculateCalculation(itemConsumed.quantity, actionContext); if (!itemConsumed.itemId) { throw 'No ammo was selected for this prop'; } - let item = CreatureProperties.findOne(itemConsumed.itemId); - if (!item || item.ancestors[0].id !== prop.ancestors[0].id) { + const item = CreatureProperties.findOne(itemConsumed.itemId) as Item; + if (!item || item.root.id !== prop.root.id) { throw 'The prop\'s ammo was not found on the creature'; } + if ( !itemConsumed?.quantity?.value || - !isFinite(itemConsumed.quantity.value) + !isFinite(+itemConsumed.quantity.value) ) return; + const quantityConsumed = +itemConsumed.quantity.value; + let logName = item.name; - if (itemConsumed.quantity.value > 1 || itemConsumed.quantity.value < -1) { + if (quantityConsumed > 1 || quantityConsumed < -1) { logName = item.plural || logName; } - if (itemConsumed.quantity.value > 0) { - spendLog.push(logName + ': ' + itemConsumed.quantity.value); - } else if (itemConsumed.quantity.value < 0) { - gainLog.push(logName + ': ' + -itemConsumed.quantity.value); + if (quantityConsumed > 0) { + spendLog.push(logName + ': ' + quantityConsumed); + } else if (quantityConsumed < 0) { + gainLog.push(logName + ': ' + -quantityConsumed); } // So long as the item isn't an ancestor of the current prop apply it // If it was an ancestor this would be an infinite loop if (!hasAncestorRelationship(item, prop)) { ammoToApply.push({ - node: { + doc: { ...item, // Use ammo pseudo-type type: 'ammo', // Store the adjustment to be applied - adjustment: itemConsumed.quantity.value, + adjustment: quantityConsumed, }, children: [] }); @@ -263,6 +275,7 @@ function spendResources(prop, actionContext) { CreatureProperties.update(prop._id, { $inc: { usesUsed: 1 } }, { + //@ts-expect-error no typings for collection 2 selector selector: prop }); if (!prop.silent) actionContext.addLog({ @@ -277,8 +290,9 @@ function spendResources(prop, actionContext) { recalculateCalculation(attConsumed.quantity, actionContext); if (!attConsumed.quantity?.value) return; + const quantityConsumed = +attConsumed.quantity.value; if (!attConsumed.variableName) return; - let stat = actionContext.scope[attConsumed.variableName]; + const stat = actionContext.scope[attConsumed.variableName]; if (!stat) { spendLog.push(attConsumed.variableName + ': ' + ' not found'); return; @@ -289,10 +303,10 @@ function spendResources(prop, actionContext) { value: attConsumed.quantity.value, actionContext, }); - if (attConsumed.quantity.value > 0) { - spendLog.push(stat.name + ': ' + attConsumed.quantity.value); - } else if (attConsumed.quantity.value < 0) { - gainLog.push(stat.name + ': ' + -attConsumed.quantity.value); + if (quantityConsumed > 0) { + spendLog.push(stat.name + ': ' + quantityConsumed); + } else if (quantityConsumed < 0) { + gainLog.push(stat.name + ': ' + -quantityConsumed); } }); diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyAdjustment.js b/app/imports/api/engine/actions/applyPropertyByType/applyAdjustment.js index 5ca17972..756b244c 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyAdjustment.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyAdjustment.js @@ -1,7 +1,7 @@ import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js'; import recalculateCalculation from './shared/recalculateCalculation.js'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; export default function applyAdjustment(node, actionContext) { applyNodeTriggers(node, 'before', actionContext); diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyBranch.js b/app/imports/api/engine/actions/applyPropertyByType/applyBranch.js index 8888c461..b336c929 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyBranch.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyBranch.js @@ -1,8 +1,8 @@ -import applyProperty from '../applyProperty.js'; +import applyProperty from '../applyProperty'; import recalculateCalculation from './shared/recalculateCalculation.js'; import rollDice from '/imports/parser/rollDice.js'; import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; export default function applyBranch(node, actionContext) { applyNodeTriggers(node, 'before', actionContext); diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyBuff.js b/app/imports/api/engine/actions/applyPropertyByType/applyBuff.js index 49adf4eb..56ffb109 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyBuff.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyBuff.js @@ -1,9 +1,8 @@ import { setLineageOfDocs, renewDocIds -} from '/imports/api/parenting/parenting.js'; -import { setDocToLastOrder } from '/imports/api/parenting/order.js'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +} from '/imports/api/parenting/parentingFunctions'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js'; import applyFnToKey from '/imports/api/engine/computation/utility/applyFnToKey.js'; import { get } from 'lodash'; @@ -12,7 +11,7 @@ import symbol from '/imports/parser/parseTree/symbol.js'; import logErrors from './shared/logErrors.js'; import { insertCreatureLog } from '/imports/api/creature/log/CreatureLogs.js'; import cyrb53 from '/imports/api/engine/computation/utility/cyrb53.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; import INLINE_CALCULATION_REGEX from '/imports/constants/INLINE_CALCULTION_REGEX.js'; import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js'; @@ -92,10 +91,12 @@ function copyNodeListToTarget(propList, target, oldParent) { renewDocIds({ docArray: propList, }); + /* setDocToLastOrder({ collection: CreatureProperties, doc: propList[0], }); + */ CreatureProperties.batchInsert(propList); } diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyBuffRemover.js b/app/imports/api/engine/actions/applyPropertyByType/applyBuffRemover.js index a9915bdf..d73954cc 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyBuffRemover.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyBuffRemover.js @@ -1,9 +1,9 @@ import { findLast, difference, intersection, filter } from 'lodash'; -import applyProperty from '../applyProperty.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import applyProperty from '../applyProperty'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; import { getProperyAncestors, getPropertiesOfType } from '/imports/api/engine/loadCreatures.js'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; -import { softRemove } from '/imports/api/parenting/softRemove.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; +import { softRemove } from '/imports/api/parenting/softRemove'; import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js'; import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js'; diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyDamage.js b/app/imports/api/engine/actions/applyPropertyByType/applyDamage.js index 3cbe225c..d2e27a05 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyDamage.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyDamage.js @@ -8,7 +8,7 @@ import { damagePropertyWork } from '/imports/api/creature/creatureProperties/met import { getPropertiesOfType } from '/imports/api/engine/loadCreatures.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js'; import applySavingThrow from '/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js'; diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyFolder.js b/app/imports/api/engine/actions/applyPropertyByType/applyFolder.js index ea216685..c97c22dc 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyFolder.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyFolder.js @@ -1,5 +1,5 @@ import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; export default function applyFolder(node, actionContext) { // Apply triggers diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyItemAsAmmo.js b/app/imports/api/engine/actions/applyPropertyByType/applyItemAsAmmo.js index 3bc3b24e..d148184c 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyItemAsAmmo.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyItemAsAmmo.js @@ -1,7 +1,7 @@ import { getPropertyDecendants } from '/imports/api/engine/loadCreatures.js'; -import applyProperty from '../applyProperty.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; -import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js'; +import applyProperty from '../applyProperty'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; +import { docsToForest as nodeArrayToTree } from '/imports/api/parenting/parentingFunctions'; import { adjustQuantityWork } from '/imports/api/creature/creatureProperties/methods/adjustQuantity.js'; export default function applyItemAsAmmo(node, actionContext) { diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyNote.js b/app/imports/api/engine/actions/applyPropertyByType/applyNote.js index 332d93dc..27d320f0 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyNote.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyNote.js @@ -1,6 +1,6 @@ import recalculateInlineCalculations from './shared/recalculateInlineCalculations.js'; import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; export default function applyNote(node, actionContext) { applyNodeTriggers(node, 'before', actionContext); diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyRoll.js b/app/imports/api/engine/actions/applyPropertyByType/applyRoll.js index d3b7bf12..f24d5dcc 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyRoll.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyRoll.js @@ -2,7 +2,7 @@ import applyChildren from '/imports/api/engine/actions/applyPropertyByType/share import logErrors from './shared/logErrors.js'; import applyEffectsToCalculationParseNode from '/imports/api/engine/actions/applyPropertyByType/shared/applyEffectsToCalculationParseNode.js'; import resolve, { toString } from '/imports/parser/resolve.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; export default function applyRoll(node, actionContext) { applyNodeTriggers(node, 'before', actionContext); diff --git a/app/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js b/app/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js index ae2f878e..1c5d4cec 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js @@ -1,9 +1,9 @@ import rollDice from '/imports/parser/rollDice.js'; import recalculateCalculation from './shared/recalculateCalculation.js'; -import applyProperty from '../applyProperty.js'; +import applyProperty from '../applyProperty'; import numberToSignedString from '/imports/api/utility/numberToSignedString.js'; import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; import { applyUnresolvedEffects } from '/imports/api/engine/actions/doCheck.js'; export default function applySavingThrow(node, actionContext) { diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyToggle.js b/app/imports/api/engine/actions/applyPropertyByType/applyToggle.js index 47ec3e08..a73831c0 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyToggle.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyToggle.js @@ -1,5 +1,5 @@ import recalculateCalculation from './shared/recalculateCalculation.js'; -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; import applyChildren from '/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js'; export default function applyToggle(node, actionContext) { diff --git a/app/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js b/app/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js index 9c5c0cbd..0ec454db 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js +++ b/app/imports/api/engine/actions/applyPropertyByType/shared/applyChildren.js @@ -1,5 +1,5 @@ -import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers.js'; -import applyProperty from '/imports/api/engine/actions/applyProperty.js'; +import { applyNodeTriggers } from '/imports/api/engine/actions/applyTriggers'; +import applyProperty from '/imports/api/engine/actions/applyProperty'; export default function applyChildren(node, actionContext) { applyNodeTriggers(node, 'after', actionContext); diff --git a/app/imports/api/engine/actions/applyTriggers.testFn.js b/app/imports/api/engine/actions/applyTriggers.testFn.js index f2955afb..9bec8b5f 100644 --- a/app/imports/api/engine/actions/applyTriggers.testFn.js +++ b/app/imports/api/engine/actions/applyTriggers.testFn.js @@ -1,4 +1,4 @@ -import { triggerMatchTags } from '/imports/api/engine/actions/applyTriggers.js'; +import { triggerMatchTags } from '/imports/api/engine/actions/applyTriggers'; import clean from '/imports/api/engine/computation/utility/cleanProp.testFn.js'; import { assert } from 'chai'; diff --git a/app/imports/api/engine/actions/applyTriggers.js b/app/imports/api/engine/actions/applyTriggers.ts similarity index 93% rename from app/imports/api/engine/actions/applyTriggers.js rename to app/imports/api/engine/actions/applyTriggers.ts index cbaf2108..03c254de 100644 --- a/app/imports/api/engine/actions/applyTriggers.js +++ b/app/imports/api/engine/actions/applyTriggers.ts @@ -1,13 +1,13 @@ import recalculateCalculation from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateCalculation.js'; import recalculateInlineCalculations from '/imports/api/engine/actions/applyPropertyByType/shared/recalculateInlineCalculations.js'; import { getPropertyDecendants } from '/imports/api/engine/loadCreatures.js'; -import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js'; -import applyProperty from '/imports/api/engine/actions/applyProperty.js'; +import { TreeNode, docsToForest as nodeArrayToTree } from '/imports/api/parenting/parentingFunctions'; +import applyProperty from '/imports/api/engine/actions/applyProperty'; import { difference, intersection } from 'lodash'; import getEffectivePropTags from '/imports/api/engine/computation/utility/getEffectivePropTags.js'; -export function applyNodeTriggers(node, timing, actionContext) { - const prop = node.node; +export function applyNodeTriggers(node: TreeNode, timing, actionContext) { + const prop = node.doc; const type = prop.type; const triggers = actionContext.triggers?.doActionProperty?.[type]?.[timing]; if (triggers) { diff --git a/app/imports/api/engine/actions/doAction.js b/app/imports/api/engine/actions/doAction.js index 57c7beac..a25c5310 100644 --- a/app/imports/api/engine/actions/doAction.js +++ b/app/imports/api/engine/actions/doAction.js @@ -2,14 +2,14 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; -import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js'; +import { docsToForest } from '/imports/api/parenting/parentingFunctions'; import { getProperyAncestors, getPropertyDecendants } from '/imports/api/engine/loadCreatures.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; -import applyProperty from './applyProperty.js'; -import ActionContext from '/imports/api/engine/actions/ActionContext.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; +import applyProperty from './applyProperty'; +import ActionContext from '/imports/api/engine/actions/ActionContext'; const doAction = new ValidatedMethod({ name: 'creatureProperties.doAction', @@ -38,8 +38,9 @@ const doAction = new ValidatedMethod({ }, run({ actionId, targetIds = [], scope }) { // Get action context - let action = CreatureProperties.findOne(actionId); - const creatureId = action.ancestors[0].id; + const action = CreatureProperties.findOne(actionId); + if (!action) throw new Meteor.Error('not-found', 'The action was not found'); + const creatureId = action.root.id; const actionContext = new ActionContext(creatureId, targetIds, this); // Check permissions @@ -74,7 +75,7 @@ export function doActionWork({ }) { // get the docs const ancestorScope = getAncestorScope(ancestors); - const propertyForest = nodeArrayToTree(properties); + const propertyForest = docsToForest(properties); if (propertyForest.length !== 1) { throw new Meteor.Error(`The action has ${propertyForest.length} top level properties, expected 1`); } @@ -92,7 +93,7 @@ export function doActionWork({ // Assumes ancestors are in tree order already function getAncestorScope(ancestors) { - let scope = {}; + const scope = {}; ancestors.forEach(prop => { scope[`#${prop.type}`] = prop; }); diff --git a/app/imports/api/engine/actions/doAction.test.js b/app/imports/api/engine/actions/doAction.test.js index b9fdfa23..9e5b7c37 100644 --- a/app/imports/api/engine/actions/doAction.test.js +++ b/app/imports/api/engine/actions/doAction.test.js @@ -3,7 +3,7 @@ import '/imports/api/simpleSchemaConfig.js'; import applyTriggers from '/imports/api/engine/actions/applyTriggers.testFn.js'; import { doActionWork } from './doAction.js'; import { CreatureLogSchema } from '/imports/api/creature/log/CreatureLogs.js'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; function cleanProp(prop) { diff --git a/app/imports/api/engine/actions/doCastSpell.js b/app/imports/api/engine/actions/doCastSpell.js index 4ea03630..2d2da615 100644 --- a/app/imports/api/engine/actions/doCastSpell.js +++ b/app/imports/api/engine/actions/doCastSpell.js @@ -5,11 +5,11 @@ import Creatures from '/imports/api/creature/creatures/Creatures.js'; import { getProperyAncestors, getPropertyDecendants } from '/imports/api/engine/loadCreatures.js'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; import { doActionWork } from '/imports/api/engine/actions/doAction.js'; -import ActionContext from '/imports/api/engine/actions/ActionContext.js'; +import ActionContext from '/imports/api/engine/actions/ActionContext'; const doAction = new ValidatedMethod({ name: 'creatureProperties.doCastSpell', diff --git a/app/imports/api/engine/actions/doCheck.js b/app/imports/api/engine/actions/doCheck.js index 452c32eb..56c6e2ea 100644 --- a/app/imports/api/engine/actions/doCheck.js +++ b/app/imports/api/engine/actions/doCheck.js @@ -1,12 +1,12 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import rollDice from '/imports/parser/rollDice.js'; import numberToSignedString from '/imports/api/utility/numberToSignedString.js'; -import { applyTriggers } from '/imports/api/engine/actions/applyTriggers.js'; -import ActionContext from '/imports/api/engine/actions/ActionContext.js'; +import { applyTriggers } from '/imports/api/engine/actions/applyTriggers'; +import ActionContext from '/imports/api/engine/actions/ActionContext'; import evaluateCalculation from '/imports/api/engine/computation/utility/evaluateCalculation.js'; const doCheck = new ValidatedMethod({ diff --git a/app/imports/api/engine/computation/buildComputation/computeInactiveStatus.js b/app/imports/api/engine/computation/buildComputation/computeInactiveStatus.ts similarity index 63% rename from app/imports/api/engine/computation/buildComputation/computeInactiveStatus.js rename to app/imports/api/engine/computation/buildComputation/computeInactiveStatus.ts index 982f1c60..6dd96867 100644 --- a/app/imports/api/engine/computation/buildComputation/computeInactiveStatus.js +++ b/app/imports/api/engine/computation/buildComputation/computeInactiveStatus.ts @@ -1,7 +1,10 @@ +import { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties'; import walkDown from '/imports/api/engine/computation/utility/walkdown.js'; +import { TreeNode } from '/imports/api/parenting/parentingFunctions'; +import { isSpell } from '/imports/api/properties/Spells'; -export default function computeInactiveStatus(node) { - const prop = node.node; +export default function computeInactiveStatus(node: TreeNode): void { + const prop = node.doc; if (!isActive(prop)) { // Mark prop inactive due to self prop.inactive = true; @@ -16,16 +19,16 @@ export default function computeInactiveStatus(node) { } } -function isActive(prop) { +function isActive(prop: CreatureProperty): boolean { if (prop.disabled) return false; - switch (prop.type) { - // Unprepared spells are inactive - case 'spell': return !!prop.prepared || !!prop.alwaysPrepared; - default: return true; + if (isSpell(prop)) { + return !!prop.prepared || !!prop.alwaysPrepared; + } else { + return true; } } -function childrenActive(prop) { +function childrenActive(prop): boolean { // Children of disabled properties are always inactive if (prop.disabled) return false; switch (prop.type) { diff --git a/app/imports/api/engine/computation/buildCreatureComputation.js b/app/imports/api/engine/computation/buildCreatureComputation.js index e9dbb625..776d0db5 100644 --- a/app/imports/api/engine/computation/buildCreatureComputation.js +++ b/app/imports/api/engine/computation/buildCreatureComputation.js @@ -1,13 +1,13 @@ -import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js'; +import { docsToForest as nodeArrayToTree } from '/imports/api/parenting/parentingFunctions'; import { DenormalisedOnlyCreaturePropertySchema as denormSchema } - from '/imports/api/creature/creatureProperties/CreatureProperties.js'; + from '/imports/api/creature/creatureProperties/CreatureProperties'; import { getProperties, getCreature, getVariables } from '/imports/api/engine/loadCreatures.js'; import computedOnlySchemas from '/imports/api/properties/computedOnlyPropertySchemasIndex.js'; import computedSchemas from '/imports/api/properties/computedPropertySchemasIndex.js'; import linkInventory from './buildComputation/linkInventory.js'; import walkDown from './utility/walkdown.js'; import parseCalculationFields from './buildComputation/parseCalculationFields.js'; -import computeInactiveStatus from './buildComputation/computeInactiveStatus.js'; +import computeInactiveStatus from './buildComputation/computeInactiveStatus'; import computeToggleDependencies from './buildComputation/computeToggleDependencies.js'; import linkCalculationDependencies from './buildComputation/linkCalculationDependencies.js'; import linkTypeDependencies from './buildComputation/linkTypeDependencies.js'; diff --git a/app/imports/api/engine/computation/buildCreatureComputation.test.js b/app/imports/api/engine/computation/buildCreatureComputation.test.js index 33b3830b..c77f6712 100644 --- a/app/imports/api/engine/computation/buildCreatureComputation.test.js +++ b/app/imports/api/engine/computation/buildCreatureComputation.test.js @@ -1,7 +1,7 @@ import '/imports/api/simpleSchemaConfig.js'; import { buildComputationFromProps } from './buildCreatureComputation.js'; import { assert } from 'chai'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import computeInactiveStatus from './buildComputation/tests/computeInactiveStatus.testFn.js'; import computeSlotQuantityFilled from './buildComputation/tests/computeSlotQuantityFilled.testFn.js'; import computeToggleDependencies from './buildComputation/tests/computeToggleDependencies.testFn.js'; @@ -9,8 +9,8 @@ import linkCalculationDependencies from './buildComputation/tests/linkCalculatio import linkInventory from './buildComputation/tests/linkInventory.testFn.js'; import linkTypeDependencies from './buildComputation/tests/linkTypeDependencies.testFn.js'; -describe('buildComputation', function(){ - it('Builds something at all', function(){ +describe('buildComputation', function () { + it('Builds something at all', function () { let computation = buildComputationFromProps(testProperties); assert.exists(computation); }); @@ -37,7 +37,7 @@ var testProperties = [ }), ]; -function clean(prop){ +function clean(prop) { let schema = CreatureProperties.simpleSchema(prop); return schema.clean(prop); } diff --git a/app/imports/api/engine/computation/computeCreatureComputation.test.js b/app/imports/api/engine/computation/computeCreatureComputation.test.js index f64e6e7f..09876a75 100644 --- a/app/imports/api/engine/computation/computeCreatureComputation.test.js +++ b/app/imports/api/engine/computation/computeCreatureComputation.test.js @@ -1,11 +1,11 @@ import computeCreatureComputation from './computeCreatureComputation.js'; import { buildComputationFromProps } from './buildCreatureComputation.js'; import { assert } from 'chai'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import computeTests from './computeComputation/tests/index.js'; -describe('Compute compuation', function(){ - it('Computes something at all', function(){ +describe('Compute compuation', function () { + it('Computes something at all', function () { let computation = buildComputationFromProps(testProperties); computeCreatureComputation(computation); assert.exists(computation); @@ -28,7 +28,7 @@ var testProperties = [ }), ]; -function clean(prop){ +function clean(prop) { let schema = CreatureProperties.simpleSchema(prop); return schema.clean(prop); } diff --git a/app/imports/api/engine/computation/utility/cleanProp.testFn.js b/app/imports/api/engine/computation/utility/cleanProp.testFn.js index 1c845150..155fe000 100644 --- a/app/imports/api/engine/computation/utility/cleanProp.testFn.js +++ b/app/imports/api/engine/computation/utility/cleanProp.testFn.js @@ -1,6 +1,6 @@ -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; -export default function cleanProp(prop){ +export default function cleanProp(prop) { let schema = CreatureProperties.simpleSchema(prop); return schema.clean(prop); } diff --git a/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js b/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js index 0548fedd..4a4dadea 100644 --- a/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js +++ b/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor' import { EJSON } from 'meteor/ejson'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import propertySchemasIndex from '/imports/api/properties/computedOnlyPropertySchemasIndex.js'; export default function writeAlteredProperties(computation) { diff --git a/app/imports/api/engine/loadCreatures.js b/app/imports/api/engine/loadCreatures.js index aa036a37..d831e26b 100644 --- a/app/imports/api/engine/loadCreatures.js +++ b/app/imports/api/engine/loadCreatures.js @@ -1,11 +1,12 @@ import { debounce } from 'lodash'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables'; -import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import computeCreature from './computeCreature'; const COMPUTE_DEBOUNCE_TIME = 100; // ms export const loadedCreatures = new Map(); // creatureId => {creature, properties, etc.} +// TODO: migrate to nested sets export function loadCreature(creatureId, subscription) { if (!creatureId) throw 'creatureId is required'; @@ -43,7 +44,7 @@ export function getSingleProperty(creatureId, propertyId) { const prop = CreatureProperties.findOne({ _id: propertyId, 'ancestors.id': creatureId, - 'removed': {$ne: true}, + 'removed': { $ne: true }, }, { sort: { order: 1 }, }); @@ -61,7 +62,7 @@ export function getProperties(creatureId) { // console.time(`Cache miss on creature properties: ${creatureId}`) const props = CreatureProperties.find({ 'ancestors.id': creatureId, - 'removed': {$ne: true}, + 'removed': { $ne: true }, }, { sort: { order: 1 }, }).fetch(); @@ -73,7 +74,7 @@ export function getPropertiesOfType(creatureId, propType) { if (loadedCreatures.has(creatureId)) { const creature = loadedCreatures.get(creatureId); const props = [] - for (const prop of creature.properties.values()){ + for (const prop of creature.properties.values()) { if (prop.type === propType) { props.push(prop); } @@ -97,7 +98,7 @@ export function getCreature(creatureId) { if (loadedCreatures.has(creatureId)) { const loadedCreature = loadedCreatures.get(creatureId); const creature = loadedCreature.creature; - if (creature) { + if (creature) { const cloneCreature = EJSON.clone(creature); return cloneCreature; } @@ -118,7 +119,7 @@ export function getVariables(creatureId) { } } // console.time(`Cache miss on variables: ${creatureId}`); - const variables = CreatureVariables.findOne({_creatureId: creatureId}); + const variables = CreatureVariables.findOne({ _creatureId: creatureId }); // console.timeEnd(`Cache miss on variables: ${creatureId}`); return variables; } @@ -148,7 +149,7 @@ export function getProperyAncestors(creatureId, propertyId) { // Fetch from database return CreatureProperties.find({ _id: { $in: ancestorIds }, - removed: {$ne: true}, + removed: { $ne: true }, }, { sort: { order: 1 }, }).fetch(); @@ -164,7 +165,7 @@ export function getPropertyDecendants(creatureId, propertyId) { if (loadedCreatures.has(creatureId)) { const creature = loadedCreatures.get(creatureId); const props = []; - for(const prop of creature.properties.values()){ + for (const prop of creature.properties.values()) { if (prop.ancestors[expectedAncestorPostition]?.id === propertyId) { props.push(prop); } @@ -216,7 +217,7 @@ class LoadedCreature { compute(); }, }); - + // Observe the creature itself self.creatureObserver = Creatures.find({ _id: creatureId, @@ -239,7 +240,7 @@ class LoadedCreature { self.variablesObserver = CreatureVariables.find({ _creatureId: creatureId, }, { - fields: { _creatureId: 0}, + fields: { _creatureId: 0 }, }).observeChanges({ added(id, fields) { fields._id = id; diff --git a/app/imports/api/library/LibraryNodes.js b/app/imports/api/library/LibraryNodes.js index 39264fb6..9cdcfb56 100644 --- a/app/imports/api/library/LibraryNodes.js +++ b/app/imports/api/library/LibraryNodes.js @@ -4,19 +4,19 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import SimpleSchema from 'simpl-schema'; import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js'; -import ChildSchema, { RefSchema } from '/imports/api/parenting/ChildSchema.js'; +import ChildSchema, { RefSchema } from '/imports/api/parenting/ChildSchema'; import propertySchemasIndex from '/imports/api/properties/propertySchemasIndex.js'; import Libraries from '/imports/api/library/Libraries.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import { softRemove } from '/imports/api/parenting/softRemove.js'; +import { softRemove } from '/imports/api/parenting/softRemove'; import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js'; import { storedIconsSchema } from '/imports/api/icons/Icons.js'; import '/imports/api/library/methods/index.js'; import { updateReferenceNodeWork } from '/imports/api/library/methods/updateReferenceNode.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; -import { restore } from '/imports/api/parenting/softRemove.js'; -import { getAncestry } from '/imports/api/parenting/parenting.js'; -import { reorderDocs } from '/imports/api/parenting/order.js'; +import { restore } from '/imports/api/parenting/softRemove'; +import { getAncestry } from '/imports/api/parenting/parentingFunctions'; +import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions'; let LibraryNodes = new Mongo.Collection('libraryNodes'); @@ -178,10 +178,7 @@ const insertNode = new ValidatedMethod({ } // Tree structure changed by insert, reorder the tree - reorderDocs({ - collection: LibraryNodes, - ancestorId: root._id, - }); + rebuildNestedSets(LibraryNodes, root._id); // Return the id of the inserted node return nodeId; diff --git a/app/imports/api/library/methods/copyLibraryNodeTo.js b/app/imports/api/library/methods/copyLibraryNodeTo.js index ea9ce06f..cda54150 100644 --- a/app/imports/api/library/methods/copyLibraryNodeTo.js +++ b/app/imports/api/library/methods/copyLibraryNodeTo.js @@ -1,7 +1,7 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import SimpleSchema from 'simpl-schema'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; -import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; +import { RefSchema } from '/imports/api/parenting/ChildSchema'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import { assertDocCopyPermission, @@ -10,9 +10,9 @@ import { import { setLineageOfDocs, renewDocIds -} from '/imports/api/parenting/parenting.js'; -import { reorderDocs } from '/imports/api/parenting/order.js'; -import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; +} from '/imports/api/parenting/parentingFunctions'; +import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions'; +import { fetchDocByRef } from '/imports/api/parenting/parentingFunctions'; var snackbar; if (Meteor.isClient) { @@ -87,10 +87,9 @@ const copyLibraryNodeTo = new ValidatedMethod({ LibraryNodes.batchInsert(nodes); // Tree structure changed by inserts, reorder the tree - reorderDocs({ - collection: LibraryNodes, - ancestorId: parent.collection === 'libraries' ? parent.id : parentDoc.ancestors[0].id, - }); + // TODO: rebuild tree nested sets + + rebuildNestedSets(LibraryNodes, parentDoc.root.id); }, }); diff --git a/app/imports/api/library/methods/duplicateLibraryNode.js b/app/imports/api/library/methods/duplicateLibraryNode.js index eef9f10c..88a6e42b 100644 --- a/app/imports/api/library/methods/duplicateLibraryNode.js +++ b/app/imports/api/library/methods/duplicateLibraryNode.js @@ -6,8 +6,8 @@ import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions import { setLineageOfDocs, renewDocIds -} from '/imports/api/parenting/parenting.js'; -import { reorderDocs } from '/imports/api/parenting/order.js'; +} from '/imports/api/parenting/parentingFunctions'; +import { rebuildNestedSets } from '/imports/api/parenting/parentingFunctions'; var snackbar; if (Meteor.isClient) { diff --git a/app/imports/api/library/methods/updateReferenceNode.js b/app/imports/api/library/methods/updateReferenceNode.js index aa2a5407..440ff59e 100644 --- a/app/imports/api/library/methods/updateReferenceNode.js +++ b/app/imports/api/library/methods/updateReferenceNode.js @@ -6,7 +6,7 @@ import { assertDocEditPermission, assertViewPermission, } from '/imports/api/sharing/sharingPermissions.js'; -import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; +import { fetchDocByRef } from '/imports/api/parenting/parentingFunctions'; const updateReferenceNode = new ValidatedMethod({ name: 'libraryNodes.updateReferenceNode', diff --git a/app/imports/api/parenting/ChildSchema.ts b/app/imports/api/parenting/ChildSchema.ts index 251128fa..dd5ffb4d 100644 --- a/app/imports/api/parenting/ChildSchema.ts +++ b/app/imports/api/parenting/ChildSchema.ts @@ -14,13 +14,17 @@ const RefSchema = new SimpleSchema({ const ChildSchema = new SimpleSchema({ root: { - type: RefSchema, + type: Object, }, 'root.id': { type: String, regEx: SimpleSchema.RegEx.Id, index: 1, }, + 'root.collection': { + type: String, + max: STORAGE_LIMITS.collectionName, + }, // Parent id of a document in the same collection // Undefined parent id implies the root is the parent parentId: { diff --git a/app/imports/api/parenting/parentingFunctions.test.ts b/app/imports/api/parenting/parentingFunctions.test.ts index 8c4f9104..5e4375cb 100644 --- a/app/imports/api/parenting/parentingFunctions.test.ts +++ b/app/imports/api/parenting/parentingFunctions.test.ts @@ -1,4 +1,4 @@ -import { docsToForest, calculateNestedSetOperations } from '/imports/api/parenting/parentingFunctions' +import { docsToForest, calculateNestedSetOperations, getFilter } from '/imports/api/parenting/parentingFunctions' import { TreeDoc } from '/imports/api/parenting/ChildSchema'; import { assert } from 'chai'; @@ -75,3 +75,97 @@ describe('Parenting with nested sets', function () { ]); }); }); + +describe('Document tree filters can fetch other documents based on their position in the tree', function () { + // Add the docs to a new collection + /** + * Test the following structure + * + * 1 Books 12 + * ┃ + * 2 Programming 11 + * ┏━━━━━━━━┻━━━━━━━━━┓ + * 3 Languages 4 5 Databases 10 + * ┏━━━━━━━┻━━━━━━━┓ + * 6 MongoDB 7 8 dbm 9 + */ + const treeCollection: Mongo.Collection = new Mongo.Collection('treeDocs'); + treeCollection.remove({}); + [ + doc('Books', 1, 12, undefined), + doc('Programming', 2, 11, 'Books'), + doc('Languages', 3, 4, 'Programming'), + doc('Databases', 5, 10, 'Programming'), + doc('MongoDB', 6, 7, 'Databases'), + doc('dbm', 8, 9, 'Databases'), + ].map(doc => { + return treeCollection.insert(doc); + }); + const docs: TreeDoc[] = treeCollection.find({}).fetch(); + + it('Can filter ancestors', async function () { + const ancestorIds: { [id: string]: string[] } = {}; + docs.forEach(doc => { + ancestorIds[doc._id] = treeCollection.find( + getFilter.ancestors(doc) + ).map(doc => doc._id); + }); + assert.isEmpty(ancestorIds['Books'], 'Books has no ancestors'); + assert.sameMembers(ancestorIds['Programming'], ['Books']); + assert.sameMembers(ancestorIds['Languages'], ['Books', 'Programming']); + assert.sameMembers(ancestorIds['Databases'], ['Books', 'Programming']); + assert.sameMembers(ancestorIds['MongoDB'], ['Books', 'Programming', 'Databases']); + assert.sameMembers(ancestorIds['dbm'], ['Books', 'Programming', 'Databases']); + }); + + it('Can filter descendants', async function () { + const descendantIds: { [id: string]: string[] } = {}; + docs.forEach(doc => { + descendantIds[doc._id] = treeCollection.find( + getFilter.descendants(doc) + ).map(doc => doc._id); + }); + assert.isEmpty(descendantIds['MongoDB'], 'MongoDB has no descendants'); + assert.isEmpty(descendantIds['dbm'], 'dbm has no descendants'); + assert.isEmpty(descendantIds['Languages'], 'Languages has no descendants'); + assert.sameMembers(descendantIds['Databases'], ['dbm', 'MongoDB']); + assert.sameMembers(descendantIds['Programming'], ['dbm', 'MongoDB', 'Languages', 'Databases']); + assert.sameMembers(descendantIds['Books'], [ + 'dbm', 'MongoDB', 'Languages', 'Databases', 'Programming' + ]); + }); + + it('Can filter children', async function () { + const childrenIds: { [id: string]: string[] } = {}; + docs.forEach(doc => { + childrenIds[doc._id] = treeCollection.find( + getFilter.children(doc) + ).map(doc => doc._id); + }); + assert.sameMembers(childrenIds['Books'], ['Programming']); + assert.sameMembers(childrenIds['Programming'], ['Languages', 'Databases']); + assert.isEmpty(childrenIds['Languages'], 'Languages has no children'); + assert.sameMembers(childrenIds['Databases'], ['dbm', 'MongoDB']); + assert.isEmpty(childrenIds['MongoDB'], 'MongoDB has no children'); + assert.isEmpty(childrenIds['dbm'], 'dbm has no children'); + }); + + it('Can filter parents', async function () { + const parentIds: { [id: string]: string[] } = {}; + docs.forEach(doc => { + parentIds[doc._id] = treeCollection.find( + getFilter.parent(doc) + ).map(doc => doc._id); + }); + assert.isEmpty(parentIds['Books'], 'Books has no parent'); + assert.sameMembers(parentIds['Programming'], ['Books']); + assert.sameMembers(parentIds['Languages'], ['Programming']); + assert.sameMembers(parentIds['Databases'], ['Programming']); + assert.sameMembers(parentIds['MongoDB'], ['Databases']); + assert.sameMembers(parentIds['dbm'], ['Databases']); + }); +}); + + + + diff --git a/app/imports/api/parenting/parentingFunctions.ts b/app/imports/api/parenting/parentingFunctions.ts index a5a97aa9..c1cd800e 100644 --- a/app/imports/api/parenting/parentingFunctions.ts +++ b/app/imports/api/parenting/parentingFunctions.ts @@ -11,8 +11,8 @@ export function getCollectionByName(name: string): Mongo.Collection { return collection; } -export async function fetchDocByRef(ref: Reference, options?: Mongo.Options) { - const doc = await getCollectionByName(ref.collection).findOneAsync(ref.id, options); +export function fetchDocByRef(ref: Reference, options?: Mongo.Options): Promise { + const doc = getCollectionByName(ref.collection).findOneAsync(ref.id, options); if (!doc) { throw new Meteor.Error('document-not-found', `No document could be found with id: ${ref.id} in ${ref.collection}` @@ -21,7 +21,7 @@ export async function fetchDocByRef(ref: Reference, options?: Mongo.Options { +export interface TreeNode { doc: T, children: TreeNode[] } @@ -271,8 +271,8 @@ export const getFilter = { }, } -export async function fetchParent({ id, collection }) { - return await fetchDocByRef({ id, collection }); +export function fetchParent({ id, collection }) { + return fetchDocByRef({ id, collection }); } /** diff --git a/app/imports/api/properties/Actions.js b/app/imports/api/properties/Actions.ts similarity index 82% rename from app/imports/api/properties/Actions.js rename to app/imports/api/properties/Actions.ts index 163d938d..9b22f221 100644 --- a/app/imports/api/properties/Actions.js +++ b/app/imports/api/properties/Actions.ts @@ -3,11 +3,58 @@ import createPropertySchema from '/imports/api/properties/subSchemas/createPrope import { storedIconsSchema } from '/imports/api/icons/Icons.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js'; +import { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties'; +import { InlineCalculation } from '/imports/api/properties/subSchemas/inlineCalculationField'; +import { CalculatedField } from '/imports/api/properties/subSchemas/computedField'; + +export interface Action extends ActionBase { + type: 'action' +} + +export interface ActionBase extends CreatureProperty { + name?: string + summary?: InlineCalculation + description?: InlineCalculation + actionType: 'action' | 'bonus' | 'attack' | 'reaction' | 'free' | 'long' | 'event' + variableName?: string + target: 'self' | 'singleTarget' | 'multipleTargets' + attackRoll?: CalculatedField + uses?: CalculatedField + usesUsed?: number + reset?: string + silent?: boolean + insufficientResources?: boolean + usesLeft?: number + overridden?: boolean + // Resources + resources: { + itemsConsumed: { + _id: string + tag?: string + itemName?: string + quantity?: CalculatedField + itemId?: string + available?: number + }[] + attributesConsumed: { + _id: string + variableName?: string + quantity?: CalculatedField + available?: number + statName?: string + }[] + conditions?: { + _id: string, + condition?: CalculatedField + conditionNote?: string, + }[] + } +} /* * Actions are things a character can do */ -let ActionSchema = createPropertySchema({ +const ActionSchema = createPropertySchema({ name: { type: String, optional: true, diff --git a/app/imports/api/properties/Items.js b/app/imports/api/properties/Items.ts similarity index 88% rename from app/imports/api/properties/Items.js rename to app/imports/api/properties/Items.ts index b4bb7319..ba5cedc3 100644 --- a/app/imports/api/properties/Items.js +++ b/app/imports/api/properties/Items.ts @@ -1,6 +1,14 @@ import SimpleSchema from 'simpl-schema'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js'; +import { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties'; + +export interface Item extends CreatureProperty { + type: 'item' + name?: string + plural?: string + quantity: number +} const ItemSchema = createPropertySchema({ name: { diff --git a/app/imports/api/properties/Spells.js b/app/imports/api/properties/Spells.ts similarity index 75% rename from app/imports/api/properties/Spells.js rename to app/imports/api/properties/Spells.ts index 575c9b5e..bb62bd93 100644 --- a/app/imports/api/properties/Spells.js +++ b/app/imports/api/properties/Spells.ts @@ -1,6 +1,30 @@ -import { ActionSchema, ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js'; +import { ActionBase, ActionSchema, ComputedOnlyActionSchema } from '/imports/api/properties/Actions'; import SimpleSchema from 'simpl-schema'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; +import { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties'; + +export interface Spell extends ActionBase { + name?: string + type: 'spell' + alwaysPrepared?: boolean + prepared?: boolean + castWithoutSpellSlots?: boolean + hasAttackRoll?: boolean + castingTime?: string + range?: string + duration?: string + verbal?: boolean + somatic?: boolean + concentration?: boolean + material?: string + ritual?: boolean + level?: number + school?: string +} + +export function isSpell(prop: CreatureProperty): prop is Spell { + return prop.type === 'spell'; +} const magicSchools = [ 'abjuration', diff --git a/app/imports/api/properties/computedOnlyPropertySchemasIndex.js b/app/imports/api/properties/computedOnlyPropertySchemasIndex.js index 89cb21e4..872249e5 100644 --- a/app/imports/api/properties/computedOnlyPropertySchemasIndex.js +++ b/app/imports/api/properties/computedOnlyPropertySchemasIndex.js @@ -1,5 +1,5 @@ import SimpleSchema from 'simpl-schema'; -import { ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js'; +import { ComputedOnlyActionSchema } from '/imports/api/properties/Actions'; import { ComputedOnlyAdjustmentSchema } from '/imports/api/properties/Adjustments.js'; import { ComputedOnlyAttributeSchema } from '/imports/api/properties/Attributes.js'; import { ComputedOnlyBuffSchema } from '/imports/api/properties/Buffs.js'; @@ -14,7 +14,7 @@ import { ComputedOnlyDamageMultiplierSchema } from '/imports/api/properties/Dama import { ComputedOnlyEffectSchema } from '/imports/api/properties/Effects.js'; import { ComputedOnlyFeatureSchema } from '/imports/api/properties/Features.js'; import { ComputedOnlyFolderSchema } from '/imports/api/properties/Folders.js'; -import { ComputedOnlyItemSchema } from '/imports/api/properties/Items.js'; +import { ComputedOnlyItemSchema } from '/imports/api/properties/Items'; import { ComputedOnlyNoteSchema } from '/imports/api/properties/Notes.js'; import { ComputedOnlyPointBuySchema } from '/imports/api/properties/PointBuys.js'; import { ComputedOnlyProficiencySchema } from '/imports/api/properties/Proficiencies.js'; @@ -23,7 +23,7 @@ import { ComputedOnlyRollSchema } from '/imports/api/properties/Rolls.js'; import { ComputedOnlySavingThrowSchema } from '/imports/api/properties/SavingThrows.js'; import { ComputedOnlySkillSchema } from '/imports/api/properties/Skills.js'; import { ComputedOnlySlotSchema } from '/imports/api/properties/Slots.js'; -import { ComputedOnlySpellSchema } from '/imports/api/properties/Spells.js'; +import { ComputedOnlySpellSchema } from '/imports/api/properties/Spells'; import { ComputedOnlySpellListSchema } from '/imports/api/properties/SpellLists.js'; import { ComputedOnlyToggleSchema } from '/imports/api/properties/Toggles.js'; import { ComputedOnlyTriggerSchema } from '/imports/api/properties/Triggers.js'; diff --git a/app/imports/api/properties/computedPropertySchemasIndex.js b/app/imports/api/properties/computedPropertySchemasIndex.js index b6adaaa2..f3ce169b 100644 --- a/app/imports/api/properties/computedPropertySchemasIndex.js +++ b/app/imports/api/properties/computedPropertySchemasIndex.js @@ -1,5 +1,5 @@ import SimpleSchema from 'simpl-schema'; -import { ComputedActionSchema } from '/imports/api/properties/Actions.js'; +import { ComputedActionSchema } from '/imports/api/properties/Actions'; import { ComputedAdjustmentSchema } from '/imports/api/properties/Adjustments.js'; import { ComputedAttributeSchema } from '/imports/api/properties/Attributes.js'; import { ComputedBuffSchema } from '/imports/api/properties/Buffs.js'; @@ -14,7 +14,7 @@ import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultiplier import { ComputedEffectSchema } from '/imports/api/properties/Effects.js'; import { ComputedFeatureSchema } from '/imports/api/properties/Features.js'; import { ComputedFolderSchema } from '/imports/api/properties/Folders.js'; -import { ComputedItemSchema } from '/imports/api/properties/Items.js'; +import { ComputedItemSchema } from '/imports/api/properties/Items'; import { ComputedNoteSchema } from '/imports/api/properties/Notes.js'; import { ComputedPointBuySchema } from '/imports/api/properties/PointBuys.js'; import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js'; @@ -23,7 +23,7 @@ import { ComputedRollSchema } from '/imports/api/properties/Rolls.js'; import { ComputedSavingThrowSchema } from '/imports/api/properties/SavingThrows.js'; import { ComputedSkillSchema } from '/imports/api/properties/Skills.js'; import { ComputedSlotSchema } from '/imports/api/properties/Slots.js'; -import { ComputedSpellSchema } from '/imports/api/properties/Spells.js'; +import { ComputedSpellSchema } from '/imports/api/properties/Spells'; import { ComputedSpellListSchema } from '/imports/api/properties/SpellLists.js'; import { ComputedToggleSchema } from '/imports/api/properties/Toggles.js'; import { ComputedTriggerSchema } from '/imports/api/properties/Triggers.js'; diff --git a/app/imports/api/properties/propertySchemasIndex.js b/app/imports/api/properties/propertySchemasIndex.js index ed0a3a22..dd02e2da 100644 --- a/app/imports/api/properties/propertySchemasIndex.js +++ b/app/imports/api/properties/propertySchemasIndex.js @@ -1,5 +1,5 @@ import SimpleSchema from 'simpl-schema'; -import { ActionSchema } from '/imports/api/properties/Actions.js'; +import { ActionSchema } from '/imports/api/properties/Actions'; import { AdjustmentSchema } from '/imports/api/properties/Adjustments.js'; import { AttributeSchema } from '/imports/api/properties/Attributes.js'; import { BuffSchema } from '/imports/api/properties/Buffs.js'; @@ -22,11 +22,11 @@ import { SavingThrowSchema } from '/imports/api/properties/SavingThrows.js'; import { SkillSchema } from '/imports/api/properties/Skills.js'; import { SlotSchema } from '/imports/api/properties/Slots.js'; import { SpellListSchema } from '/imports/api/properties/SpellLists.js'; -import { SpellSchema } from '/imports/api/properties/Spells.js'; +import { SpellSchema } from '/imports/api/properties/Spells'; import { ToggleSchema } from '/imports/api/properties/Toggles.js'; import { TriggerSchema } from '/imports/api/properties/Triggers.js'; import { ContainerSchema } from '/imports/api/properties/Containers.js'; -import { ItemSchema } from '/imports/api/properties/Items.js'; +import { ItemSchema } from '/imports/api/properties/Items'; const propertySchemasIndex = { action: ActionSchema, diff --git a/app/imports/api/properties/subSchemas/computedField.js b/app/imports/api/properties/subSchemas/computedField.ts similarity index 84% rename from app/imports/api/properties/subSchemas/computedField.js rename to app/imports/api/properties/subSchemas/computedField.ts index 0d7ad114..5f5c13e0 100644 --- a/app/imports/api/properties/subSchemas/computedField.js +++ b/app/imports/api/properties/subSchemas/computedField.ts @@ -2,9 +2,19 @@ import SimpleSchema from 'simpl-schema'; import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; +export interface CalculatedField { + calculation?: string, + value?: string | number, + effectIds?: string[], + parseNode?: any, + parseError?: any, + hash?: number, + errors?: any[], +} + // Get schemas that apply fields directly so they can be gracefully extended // because {type: Schema} fields can't be extended -function fieldToCompute(field){ +function fieldToCompute(field) { const schemaObj = { [`${field}.calculation`]: { type: String, @@ -17,7 +27,7 @@ function fieldToCompute(field){ return new SimpleSchema(schemaObj); } -function computedOnlyField(field){ +function computedOnlyField(field) { const schemaObj = { [`${field}.value`]: { type: SimpleSchema.oneOf(String, Number), @@ -56,7 +66,7 @@ function computedOnlyField(field){ maxCount: STORAGE_LIMITS.errorCount, removeBeforeCompute: true, }, - [`${field}.errors.$`]:{ + [`${field}.errors.$`]: { type: ErrorSchema, }, } @@ -65,9 +75,9 @@ function computedOnlyField(field){ } // We must include parent array and object fields for the schema to be valid -function includeParentFields(field, schemaObj){ +function includeParentFields(field, schemaObj) { const splitField = field.split('.'); - if (splitField.length === 1){ + if (splitField.length === 1) { schemaObj[field] = { type: Object, optional: true, @@ -78,8 +88,8 @@ function includeParentFields(field, schemaObj){ let key = ''; splitField.push(''); splitField.forEach((value, index) => { - if (key){ - if (value === '$'){ + if (key) { + if (value === '$') { schemaObj[key] = { type: Array, optional: true @@ -90,7 +100,7 @@ function includeParentFields(field, schemaObj){ optional: true, }; // the last object is the computed field - if (index === splitField.length - 1){ + if (index === splitField.length - 1) { schemaObj[key].computedField = true; } } @@ -102,7 +112,7 @@ function includeParentFields(field, schemaObj){ // This should rarely be used, since the other two will merge correctly when // uncomputed and computedOnly schemas are merged -function computedField(field){ +function computedField(field) { return computedField(field).extend(computedOnlyField(field)); } diff --git a/app/imports/api/properties/subSchemas/createPropertySchema.js b/app/imports/api/properties/subSchemas/createPropertySchema.js index a866295c..2d2d6b11 100644 --- a/app/imports/api/properties/subSchemas/createPropertySchema.js +++ b/app/imports/api/properties/subSchemas/createPropertySchema.js @@ -1,17 +1,17 @@ import { inlineCalculationFieldToCompute, computedOnlyInlineCalculationField, -} from '/imports/api/properties/subSchemas/inlineCalculationField.js'; +} from '/imports/api/properties/subSchemas/inlineCalculationField'; import { fieldToCompute, computedOnlyField, -} from '/imports/api/properties/subSchemas/computedField.js'; +} from '/imports/api/properties/subSchemas/computedField'; import SimpleSchema from 'simpl-schema'; // Search through the schema for keys whose type is 'fieldToCompute' etc. // replace the type with Object and attach extend the schema with // the required fields to make the computation work -export default function createPropertySchema(definition){ +export default function createPropertySchema(definition) { const computationFields = { inlineCalculationFieldToCompute: [], computedOnlyInlineCalculationField: [], @@ -20,18 +20,18 @@ export default function createPropertySchema(definition){ }; const computedKeys = Object.keys(computationFields); - for (let key in definition){ + for (let key in definition) { const def = definition[key]; - if (computedKeys.includes(def.type)){ + if (computedKeys.includes(def.type)) { computationFields[def.type].push(key); applyDefaultCalculationValue(definition, key); def.type = Object; - if (!def.optional){ + if (!def.optional) { console.warn( `computed field: '${key}' of '${def.type}' is expected to be optional` ); } - if (def.removeBeforeCompute){ + if (def.removeBeforeCompute) { console.warn( `computed field: '${key}' of '${def.type}' should not be removed before computation` ) @@ -58,12 +58,12 @@ export default function createPropertySchema(definition){ return schema } -function applyDefaultCalculationValue(definition, key){ +function applyDefaultCalculationValue(definition, key) { const def = definition[key]; if ( def.type === 'computedOnlyField' || def.type === 'computedOnlyInlineCalculationField' - ){ + ) { // don't apply defaults to computed only fields // because it would add the calculation field which should only appear // on the fields to compute @@ -72,12 +72,12 @@ function applyDefaultCalculationValue(definition, key){ let defaultValue = def.defaultValue; if (!defaultValue) return; let calcField; - if (def.type === 'fieldToCompute'){ + if (def.type === 'fieldToCompute') { calcField = key + '.calculation' } else { calcField = key + '.text' } - if (definition[calcField]){ + if (definition[calcField]) { definition[calcField].defaultValue = defaultValue; } else { definition[calcField] = { diff --git a/app/imports/api/properties/subSchemas/inlineCalculationField.js b/app/imports/api/properties/subSchemas/inlineCalculationField.ts similarity index 88% rename from app/imports/api/properties/subSchemas/inlineCalculationField.js rename to app/imports/api/properties/subSchemas/inlineCalculationField.ts index 22e37654..a0e27827 100644 --- a/app/imports/api/properties/subSchemas/inlineCalculationField.js +++ b/app/imports/api/properties/subSchemas/inlineCalculationField.ts @@ -1,10 +1,18 @@ import SimpleSchema from 'simpl-schema'; import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; +import { CalculatedField } from './computedField'; + +export interface InlineCalculation { + text?: string, + hash?: number, + value?: string, + inlineCalculations: CalculatedField[], +} // Get schemas that apply fields directly so they can be gracefully extended // because {type: Schema} fields can't be extended -function inlineCalculationFieldToCompute(field){ +function inlineCalculationFieldToCompute(field) { return new SimpleSchema({ // The object should already be set, but set again just in case [field]: { @@ -20,7 +28,7 @@ function inlineCalculationFieldToCompute(field){ }); } -function computedOnlyInlineCalculationField(field){ +function computedOnlyInlineCalculationField(field) { return new SimpleSchema({ // The object should already be set, but set again just in case [field]: { @@ -30,9 +38,8 @@ function computedOnlyInlineCalculationField(field){ }, // a hash of the text to see if the current cached values need to be updated [`${field}.hash`]: { - type: String, + type: Number, optional: true, - max: STORAGE_LIMITS.inlineCalculationField, }, [`${field}.value`]: { type: String, @@ -90,7 +97,7 @@ function computedOnlyInlineCalculationField(field){ }); } -function computedInlineCalculationField(field){ +function computedInlineCalculationField(field) { return inlineCalculationFieldToCompute(field).extend( computedOnlyInlineCalculationField(field) ) diff --git a/app/imports/api/sharing/sharing.js b/app/imports/api/sharing/sharing.js index 7062ff40..84386618 100644 --- a/app/imports/api/sharing/sharing.js +++ b/app/imports/api/sharing/sharing.js @@ -1,8 +1,7 @@ import SimpleSchema from 'simpl-schema'; import { assertOwnership } from '/imports/api/sharing/sharingPermissions.js'; -import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; -import getCollectionByName from '/imports/api/parenting/getCollectionByName.js'; -import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; +import { getCollectionByName, fetchDocByRef } from '/imports/api/parenting/parentingFunctions'; +import { RefSchema } from '/imports/api/parenting/ChildSchema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { getUserTier } from '/imports/api/users/patreon/tiers.js'; diff --git a/app/imports/api/sharing/sharingPermissions.js b/app/imports/api/sharing/sharingPermissions.js index c01675f4..dcea4605 100644 --- a/app/imports/api/sharing/sharingPermissions.js +++ b/app/imports/api/sharing/sharingPermissions.js @@ -1,5 +1,5 @@ import { includes } from 'lodash'; -import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; +import { fetchDocByRef } from '/imports/api/parenting/parentingFunctions'; function assertIdValid(userId) { if (!userId || typeof userId !== 'string') { diff --git a/app/imports/client/ui/components/tree/TreeNode.vue b/app/imports/client/ui/components/tree/TreeNode.vue index 18df392e..1fc0af75 100644 --- a/app/imports/client/ui/components/tree/TreeNode.vue +++ b/app/imports/client/ui/components/tree/TreeNode.vue @@ -83,7 +83,7 @@ * the tree view shows off the full character structure, and where each part of * character comes from. **/ -import { canBeParent } from '/imports/api/parenting/parenting.js'; +import { canBeParent } from '/imports/api/parenting/parentingFunctions'; import { getPropertyIcon } from '/imports/constants/PROPERTIES.js'; import TreeNodeView from '/imports/client/ui/properties/treeNodeViews/TreeNodeView.vue'; import { some } from 'lodash'; diff --git a/app/imports/client/ui/components/tree/TreeNodeList.vue b/app/imports/client/ui/components/tree/TreeNodeList.vue index 5199180f..54c5d212 100644 --- a/app/imports/client/ui/components/tree/TreeNodeList.vue +++ b/app/imports/client/ui/components/tree/TreeNodeList.vue @@ -34,7 +34,7 @@