diff --git a/.meteorignore b/.meteorignore
new file mode 100644
index 000000000..49ec70b2d
--- /dev/null
+++ b/.meteorignore
@@ -0,0 +1 @@
+npm-packages/
diff --git a/METEOR3_MIGRATION.md b/METEOR3_MIGRATION.md
new file mode 100644
index 000000000..f933abe4e
--- /dev/null
+++ b/METEOR3_MIGRATION.md
@@ -0,0 +1,187 @@
+# Meteor 3.0 Migration Guide
+
+Reference document capturing patterns, constraints, and lessons learned during the async migration of WeKan from Meteor 2.16 toward Meteor 3.0 readiness.
+
+---
+
+## 1. Dual-Compatibility Strategy
+
+WeKan runs on **Meteor 2.16 with Blaze 2.x**. The goal is dual compatibility: changes must work on 2.16 now and remain compatible with a future Meteor 3.0 upgrade.
+
+**Key constraint:** Blaze 2.x does NOT support async template helpers. Client-side code must receive synchronous data.
+
+---
+
+## 2. ReactiveCache Facade Pattern
+
+`ReactiveCache` dispatches to `ReactiveCacheServer` (async MongoDB) or `ReactiveCacheClient` (sync Minimongo).
+
+**Rule:** Facade methods must NOT be `async`. They return a Promise on the server and data on the client. Server callers `await`; client code uses the return value directly.
+
+```javascript
+// CORRECT:
+getBoard(boardId) {
+ if (Meteor.isServer) {
+ return ReactiveCacheServer.getBoard(boardId); // Returns Promise
+ } else {
+ return ReactiveCacheClient.getBoard(boardId); // Returns data
+ }
+}
+
+// WRONG:
+async getBoard(boardId) { ... } // Wraps client return in Promise too!
+```
+
+---
+
+## 3. Model Helpers (Collection.helpers)
+
+Model helpers defined via `Collection.helpers({})` are used by Blaze templates. They must NOT be `async`.
+
+```javascript
+// CORRECT:
+Cards.helpers({
+ board() {
+ return ReactiveCache.getBoard(this.boardId); // Promise on server, data on client
+ },
+});
+
+// WRONG:
+Cards.helpers({
+ async board() { // Blaze gets Promise instead of data
+ return await ReactiveCache.getBoard(this.boardId);
+ },
+});
+```
+
+**Server-side callers** of these helpers must `await` the result:
+```javascript
+// In a Meteor method or hook (server-only):
+const board = await card.board();
+```
+
+---
+
+## 4. Allow/Deny Callbacks Must Be Synchronous
+
+Meteor 2.x evaluates allow/deny callbacks synchronously. An `async` callback returns a Promise:
+- **allow** callback returning Promise (truthy) → always passes
+- **deny** callback returning Promise (truthy) → always denies
+
+**Rule:** Never use `async` in allow/deny. Replace `ReactiveCache` calls with direct sync Mongo calls.
+
+```javascript
+// CORRECT:
+Cards.allow({
+ insert(userId, doc) {
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
+ },
+ fetch: ['boardId'],
+});
+
+// WRONG:
+Cards.allow({
+ async insert(userId, doc) {
+ return allowIsBoardMemberWithWriteAccess(userId, await ReactiveCache.getBoard(doc.boardId));
+ },
+});
+```
+
+### Sync alternatives for common patterns:
+
+| Async (broken in allow/deny) | Sync replacement |
+|------------------------------|------------------|
+| `await ReactiveCache.getBoard(id)` | `Boards.findOne(id)` |
+| `await ReactiveCache.getCard(id)` | `Cards.findOne(id)` |
+| `await ReactiveCache.getCurrentUser()` | `Meteor.users.findOne(userId)` |
+| `await ReactiveCache.getBoards({...})` | `Boards.find({...}).fetch()` |
+| `await card.board()` | `Boards.findOne(card.boardId)` |
+
+**Note:** These sync Mongo calls (`findOne`, `find().fetch()`) are available in Meteor 2.x. In Meteor 3.0, they will be replaced by `findOneAsync` / `find().fetchAsync()`, which will require allow/deny callbacks to be reworked again (or replaced by Meteor 3.0's new permission model).
+
+---
+
+## 5. Server-Only Code CAN Be Async
+
+Code that runs exclusively on the server can safely use `async`/`await`:
+
+- `Meteor.methods({})` — method bodies
+- `Meteor.publish()` — publication functions
+- `JsonRoutes.add()` — REST API handlers
+- `Collection.before.*` / `Collection.after.*` — collection hooks (via `matb33:collection-hooks`)
+- Standalone server functions
+
+```javascript
+Meteor.methods({
+ async createCard(data) {
+ const board = await ReactiveCache.getBoard(data.boardId); // OK
+ // ...
+ },
+});
+```
+
+---
+
+## 6. forEach with await Anti-Pattern
+
+`Array.forEach()` does not handle async callbacks — iterations run concurrently without awaiting.
+
+```javascript
+// WRONG:
+items.forEach(async (item) => {
+ await processItem(item); // Runs all in parallel, not sequentially
+});
+
+// CORRECT:
+for (const item of items) {
+ await processItem(item); // Runs sequentially
+}
+```
+
+---
+
+## 7. Client-Side Collection Updates
+
+Meteor requires client-side collection updates to use `_id` as the selector:
+
+```javascript
+// CORRECT:
+Lists.updateAsync(listId, { $set: { title: newTitle } });
+
+// WRONG - fails with "Untrusted code may only update documents by ID":
+Lists.updateAsync({ _id: listId, boardId: boardId }, { $set: { title: newTitle } });
+```
+
+---
+
+## 8. Sync Meteor 2.x APIs to Convert for 3.0
+
+These Meteor 2.x sync APIs will need conversion when upgrading to Meteor 3.0:
+
+| Meteor 2.x (sync) | Meteor 3.0 (async) |
+|--------------------|--------------------|
+| `Collection.findOne()` | `Collection.findOneAsync()` |
+| `Collection.find().fetch()` | `Collection.find().fetchAsync()` |
+| `Collection.insert()` | `Collection.insertAsync()` |
+| `Collection.update()` | `Collection.updateAsync()` |
+| `Collection.remove()` | `Collection.removeAsync()` |
+| `Collection.upsert()` | `Collection.upsertAsync()` |
+| `Meteor.user()` | `Meteor.userAsync()` |
+| `Meteor.userId()` | Remains sync |
+
+**Current status:** Server-side code already uses async patterns via `ReactiveCache`. The sync `findOne()` calls in allow/deny callbacks will need to be addressed when Meteor 3.0's allow/deny system supports async (or is replaced).
+
+---
+
+## 9. Files Reference
+
+Key files involved in the async migration:
+
+| File | Role |
+|------|------|
+| `imports/reactiveCache.js` | ReactiveCache facade + Server/Client/Index implementations |
+| `server/lib/utils.js` | Permission helper functions (`allowIsBoardMember*`) |
+| `models/*.js` | Collection schemas, helpers, allow/deny, hooks, methods |
+| `server/publications/*.js` | Meteor publications |
+| `server/rulesHelper.js` | Rule trigger/action evaluation |
+| `server/cronMigrationManager.js` | Cron-based migration jobs |
diff --git a/imports/reactiveCache.js b/imports/reactiveCache.js
index a46b6d296..83ee614fc 100644
--- a/imports/reactiveCache.js
+++ b/imports/reactiveCache.js
@@ -2,277 +2,278 @@ import { DataCache } from '@wekanteam/meteor-reactive-cache';
import Settings from '../models/settings';
// Server isn't reactive, so search for the data always.
+// All methods are async for Meteor 3.0 compatibility.
ReactiveCacheServer = {
- getBoard(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Boards.findOne(idOrFirstObjectSelector, options);
+ async getBoard(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Boards.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getBoards(selector = {}, options = {}, getQuery = false) {
+ async getBoards(selector = {}, options = {}, getQuery = false) {
let ret = Boards.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getList(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Lists.findOne(idOrFirstObjectSelector, options);
+ async getList(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Lists.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getLists(selector = {}, options = {}, getQuery = false) {
+ async getLists(selector = {}, options = {}, getQuery = false) {
let ret = Lists.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getSwimlane(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Swimlanes.findOne(idOrFirstObjectSelector, options);
+ async getSwimlane(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Swimlanes.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getSwimlanes(selector = {}, options = {}, getQuery = false) {
+ async getSwimlanes(selector = {}, options = {}, getQuery = false) {
let ret = Swimlanes.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getChecklist(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Checklists.findOne(idOrFirstObjectSelector, options);
+ async getChecklist(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Checklists.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getChecklists(selector = {}, options = {}, getQuery = false) {
+ async getChecklists(selector = {}, options = {}, getQuery = false) {
let ret = Checklists.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getChecklistItem(idOrFirstObjectSelector = {}, options = {}) {
- const ret = ChecklistItems.findOne(idOrFirstObjectSelector, options);
+ async getChecklistItem(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await ChecklistItems.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getChecklistItems(selector = {}, options = {}, getQuery = false) {
+ async getChecklistItems(selector = {}, options = {}, getQuery = false) {
let ret = ChecklistItems.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getCard(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Cards.findOne(idOrFirstObjectSelector, options);
+ async getCard(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Cards.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getCards(selector = {}, options = {}, getQuery = false) {
- let ret = Cards.find(selector, options, options);
+ async getCards(selector = {}, options = {}, getQuery = false) {
+ let ret = Cards.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getCardComment(idOrFirstObjectSelector = {}, options = {}) {
- const ret = CardComments.findOne(idOrFirstObjectSelector, options);
+ async getCardComment(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await CardComments.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getCardComments(selector = {}, options = {}, getQuery = false) {
+ async getCardComments(selector = {}, options = {}, getQuery = false) {
let ret = CardComments.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getCardCommentReaction(idOrFirstObjectSelector = {}, options = {}) {
- const ret = CardCommentReactions.findOne(idOrFirstObjectSelector, options);
+ async getCardCommentReaction(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await CardCommentReactions.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getCardCommentReactions(selector = {}, options = {}, getQuery = false) {
+ async getCardCommentReactions(selector = {}, options = {}, getQuery = false) {
let ret = CardCommentReactions.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getCustomField(idOrFirstObjectSelector = {}, options = {}) {
- const ret = CustomFields.findOne(idOrFirstObjectSelector, options);
+ async getCustomField(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await CustomFields.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getCustomFields(selector = {}, options = {}, getQuery = false) {
+ async getCustomFields(selector = {}, options = {}, getQuery = false) {
let ret = CustomFields.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getAttachment(idOrFirstObjectSelector = {}, options = {}) {
+ async getAttachment(idOrFirstObjectSelector = {}, options = {}) {
// Try new structure first
- let ret = Attachments.findOne(idOrFirstObjectSelector, options);
+ let ret = await Attachments.findOneAsync(idOrFirstObjectSelector, options);
if (!ret && typeof idOrFirstObjectSelector === 'string') {
// Fall back to old structure for single attachment lookup
- ret = Attachments.getAttachmentWithBackwardCompatibility(
+ ret = await Attachments.getAttachmentWithBackwardCompatibility(
idOrFirstObjectSelector,
);
}
return ret;
},
- getAttachments(selector = {}, options = {}, getQuery = false) {
+ async getAttachments(selector = {}, options = {}, getQuery = false) {
// Try new structure first
let ret = Attachments.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
// If no results and we have a cardId selector, try old structure
if (ret.length === 0 && selector['meta.cardId']) {
- ret = Attachments.getAttachmentsWithBackwardCompatibility(selector);
+ ret = await Attachments.getAttachmentsWithBackwardCompatibility(selector);
}
}
return ret;
},
- getAvatar(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Avatars.findOne(idOrFirstObjectSelector, options);
+ async getAvatar(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Avatars.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getAvatars(selector = {}, options = {}, getQuery = false) {
+ async getAvatars(selector = {}, options = {}, getQuery = false) {
let ret = Avatars.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getUser(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Users.findOne(idOrFirstObjectSelector, options);
+ async getUser(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Users.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getUsers(selector = {}, options = {}, getQuery = false) {
+ async getUsers(selector = {}, options = {}, getQuery = false) {
let ret = Users.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getOrg(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Org.findOne(idOrFirstObjectSelector, options);
+ async getOrg(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Org.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getOrgs(selector = {}, options = {}, getQuery = false) {
+ async getOrgs(selector = {}, options = {}, getQuery = false) {
let ret = Org.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getTeam(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Team.findOne(idOrFirstObjectSelector, options);
+ async getTeam(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Team.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getTeams(selector = {}, options = {}, getQuery = false) {
+ async getTeams(selector = {}, options = {}, getQuery = false) {
let ret = Team.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getActivity(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Activities.findOne(idOrFirstObjectSelector, options);
+ async getActivity(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Activities.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getActivities(selector = {}, options = {}, getQuery = false) {
+ async getActivities(selector = {}, options = {}, getQuery = false) {
let ret = Activities.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getRule(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Rules.findOne(idOrFirstObjectSelector, options);
+ async getRule(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Rules.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getRules(selector = {}, options = {}, getQuery = false) {
+ async getRules(selector = {}, options = {}, getQuery = false) {
let ret = Rules.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getAction(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Actions.findOne(idOrFirstObjectSelector, options);
+ async getAction(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Actions.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getActions(selector = {}, options = {}, getQuery = false) {
+ async getActions(selector = {}, options = {}, getQuery = false) {
let ret = Actions.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getTrigger(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Triggers.findOne(idOrFirstObjectSelector, options);
+ async getTrigger(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Triggers.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getTriggers(selector = {}, options = {}, getQuery = false) {
+ async getTriggers(selector = {}, options = {}, getQuery = false) {
let ret = Triggers.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getImpersonatedUser(idOrFirstObjectSelector = {}, options = {}) {
- const ret = ImpersonatedUsers.findOne(idOrFirstObjectSelector, options);
+ async getImpersonatedUser(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await ImpersonatedUsers.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getImpersonatedUsers(selector = {}, options = {}, getQuery = false) {
+ async getImpersonatedUsers(selector = {}, options = {}, getQuery = false) {
let ret = ImpersonatedUsers.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getIntegration(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Integrations.findOne(idOrFirstObjectSelector, options);
+ async getIntegration(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Integrations.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getIntegrations(selector = {}, options = {}, getQuery = false) {
+ async getIntegrations(selector = {}, options = {}, getQuery = false) {
let ret = Integrations.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getSessionData(idOrFirstObjectSelector = {}, options = {}) {
- const ret = SessionData.findOne(idOrFirstObjectSelector, options);
+ async getSessionData(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await SessionData.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getSessionDatas(selector = {}, options = {}, getQuery = false) {
+ async getSessionDatas(selector = {}, options = {}, getQuery = false) {
let ret = SessionData.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getInvitationCode(idOrFirstObjectSelector = {}, options = {}) {
- const ret = InvitationCodes.findOne(idOrFirstObjectSelector, options);
+ async getInvitationCode(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await InvitationCodes.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getInvitationCodes(selector = {}, options = {}, getQuery = false) {
+ async getInvitationCodes(selector = {}, options = {}, getQuery = false) {
let ret = InvitationCodes.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
- getCurrentSetting() {
- const ret = Settings.findOne();
+ async getCurrentSetting() {
+ const ret = await Settings.findOneAsync();
return ret;
},
- getCurrentUser() {
- const ret = Meteor.user();
+ async getCurrentUser() {
+ const ret = await Meteor.userAsync();
return ret;
},
- getTranslation(idOrFirstObjectSelector = {}, options = {}) {
- const ret = Translation.findOne(idOrFirstObjectSelector, options);
+ async getTranslation(idOrFirstObjectSelector = {}, options = {}) {
+ const ret = await Translation.findOneAsync(idOrFirstObjectSelector, options);
return ret;
},
- getTranslations(selector = {}, options = {}, getQuery = false) {
+ async getTranslations(selector = {}, options = {}, getQuery = false) {
let ret = Translation.find(selector, options);
if (getQuery !== true) {
- ret = ret.fetch();
+ ret = await ret.fetchAsync();
}
return ret;
},
@@ -995,537 +996,447 @@ ReactiveCacheClient = {
// having this class here has several advantages:
// - The Programmer hasn't to care about in which context he call's this class
// - having all queries together in 1 class to make it possible to see which queries in Wekan happens, e.g. with console.log
+//
+// Methods are NOT async - they return a Promise on server (from async ReactiveCacheServer)
+// and synchronous data on client (from ReactiveCacheClient).
+// Server callers must await; client code uses the return value directly.
ReactiveCache = {
getBoard(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getBoard(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getBoard(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getBoard(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getBoard(idOrFirstObjectSelector, options);
}
- return ret;
},
getBoards(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getBoards(selector, options, getQuery);
+ return ReactiveCacheServer.getBoards(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getBoards(selector, options, getQuery);
+ return ReactiveCacheClient.getBoards(selector, options, getQuery);
}
- return ret;
},
getList(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getList(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getList(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getList(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getList(idOrFirstObjectSelector, options);
}
- return ret;
},
getLists(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getLists(selector, options, getQuery);
+ return ReactiveCacheServer.getLists(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getLists(selector, options, getQuery);
+ return ReactiveCacheClient.getLists(selector, options, getQuery);
}
- return ret;
},
getSwimlane(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getSwimlane(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getSwimlane(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getSwimlane(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getSwimlane(idOrFirstObjectSelector, options);
}
- return ret;
},
getSwimlanes(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getSwimlanes(selector, options, getQuery);
+ return ReactiveCacheServer.getSwimlanes(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getSwimlanes(selector, options, getQuery);
+ return ReactiveCacheClient.getSwimlanes(selector, options, getQuery);
}
- return ret;
},
getChecklist(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getChecklist(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getChecklist(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getChecklist(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getChecklist(idOrFirstObjectSelector, options);
}
- return ret;
},
getChecklists(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getChecklists(selector, options, getQuery);
+ return ReactiveCacheServer.getChecklists(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getChecklists(selector, options, getQuery);
+ return ReactiveCacheClient.getChecklists(selector, options, getQuery);
}
- return ret;
},
getChecklistItem(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getChecklistItem(
+ return ReactiveCacheServer.getChecklistItem(
idOrFirstObjectSelector,
options,
);
} else {
- ret = ReactiveCacheClient.getChecklistItem(
+ return ReactiveCacheClient.getChecklistItem(
idOrFirstObjectSelector,
options,
);
}
- return ret;
},
getChecklistItems(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getChecklistItems(selector, options, getQuery);
+ return ReactiveCacheServer.getChecklistItems(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getChecklistItems(selector, options, getQuery);
+ return ReactiveCacheClient.getChecklistItems(selector, options, getQuery);
}
- return ret;
},
getCard(idOrFirstObjectSelector = {}, options = {}, noCache = false) {
- let ret;
if (Meteor.isServer || noCache === true) {
- ret = ReactiveCacheServer.getCard(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getCard(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getCard(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getCard(idOrFirstObjectSelector, options);
}
- return ret;
},
getCards(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getCards(selector, options, getQuery);
+ return ReactiveCacheServer.getCards(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getCards(selector, options, getQuery);
+ return ReactiveCacheClient.getCards(selector, options, getQuery);
}
- return ret;
},
getCardComment(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getCardComment(
+ return ReactiveCacheServer.getCardComment(
idOrFirstObjectSelector,
options,
);
} else {
- ret = ReactiveCacheClient.getCardComment(
+ return ReactiveCacheClient.getCardComment(
idOrFirstObjectSelector,
options,
);
}
- return ret;
},
getCardComments(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getCardComments(selector, options, getQuery);
+ return ReactiveCacheServer.getCardComments(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getCardComments(selector, options, getQuery);
+ return ReactiveCacheClient.getCardComments(selector, options, getQuery);
}
- return ret;
},
getCardCommentReaction(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getCardCommentReaction(
+ return ReactiveCacheServer.getCardCommentReaction(
idOrFirstObjectSelector,
options,
);
} else {
- ret = ReactiveCacheClient.getCardCommentReaction(
+ return ReactiveCacheClient.getCardCommentReaction(
idOrFirstObjectSelector,
options,
);
}
- return ret;
},
getCardCommentReactions(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getCardCommentReactions(
+ return ReactiveCacheServer.getCardCommentReactions(
selector,
options,
getQuery,
);
} else {
- ret = ReactiveCacheClient.getCardCommentReactions(
+ return ReactiveCacheClient.getCardCommentReactions(
selector,
options,
getQuery,
);
}
- return ret;
},
getCustomField(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getCustomField(
+ return ReactiveCacheServer.getCustomField(
idOrFirstObjectSelector,
options,
);
} else {
- ret = ReactiveCacheClient.getCustomField(
+ return ReactiveCacheClient.getCustomField(
idOrFirstObjectSelector,
options,
);
}
- return ret;
},
getCustomFields(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getCustomFields(selector, options, getQuery);
+ return ReactiveCacheServer.getCustomFields(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getCustomFields(selector, options, getQuery);
+ return ReactiveCacheClient.getCustomFields(selector, options, getQuery);
}
- return ret;
},
getAttachment(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getAttachment(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getAttachment(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getAttachment(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getAttachment(idOrFirstObjectSelector, options);
}
- return ret;
},
getAttachments(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getAttachments(selector, options, getQuery);
+ return ReactiveCacheServer.getAttachments(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getAttachments(selector, options, getQuery);
+ return ReactiveCacheClient.getAttachments(selector, options, getQuery);
}
- return ret;
},
getAvatar(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getAvatar(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getAvatar(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getAvatar(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getAvatar(idOrFirstObjectSelector, options);
}
- return ret;
},
getAvatars(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getAvatars(selector, options, getQuery);
+ return ReactiveCacheServer.getAvatars(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getAvatars(selector, options, getQuery);
+ return ReactiveCacheClient.getAvatars(selector, options, getQuery);
}
- return ret;
},
getUser(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getUser(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getUser(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getUser(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getUser(idOrFirstObjectSelector, options);
}
- return ret;
},
getUsers(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getUsers(selector, options, getQuery);
+ return ReactiveCacheServer.getUsers(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getUsers(selector, options, getQuery);
+ return ReactiveCacheClient.getUsers(selector, options, getQuery);
}
- return ret;
},
getOrg(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getOrg(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getOrg(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getOrg(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getOrg(idOrFirstObjectSelector, options);
}
- return ret;
},
getOrgs(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getOrgs(selector, options, getQuery);
+ return ReactiveCacheServer.getOrgs(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getOrgs(selector, options, getQuery);
+ return ReactiveCacheClient.getOrgs(selector, options, getQuery);
}
- return ret;
},
getTeam(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getTeam(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getTeam(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getTeam(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getTeam(idOrFirstObjectSelector, options);
}
- return ret;
},
getTeams(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getTeams(selector, options, getQuery);
+ return ReactiveCacheServer.getTeams(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getTeams(selector, options, getQuery);
+ return ReactiveCacheClient.getTeams(selector, options, getQuery);
}
- return ret;
},
getActivity(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getActivity(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getActivity(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getActivity(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getActivity(idOrFirstObjectSelector, options);
}
- return ret;
},
getActivities(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getActivities(selector, options, getQuery);
+ return ReactiveCacheServer.getActivities(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getActivities(selector, options, getQuery);
+ return ReactiveCacheClient.getActivities(selector, options, getQuery);
}
- return ret;
},
getRule(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getRule(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getRule(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getRule(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getRule(idOrFirstObjectSelector, options);
}
- return ret;
},
getRules(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getRules(selector, options, getQuery);
+ return ReactiveCacheServer.getRules(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getRules(selector, options, getQuery);
+ return ReactiveCacheClient.getRules(selector, options, getQuery);
}
- return ret;
},
getAction(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getAction(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getAction(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getAction(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getAction(idOrFirstObjectSelector, options);
}
- return ret;
},
getActions(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getActions(selector, options, getQuery);
+ return ReactiveCacheServer.getActions(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getActions(selector, options, getQuery);
+ return ReactiveCacheClient.getActions(selector, options, getQuery);
}
- return ret;
},
getTrigger(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getTrigger(idOrFirstObjectSelector, options);
+ return ReactiveCacheServer.getTrigger(idOrFirstObjectSelector, options);
} else {
- ret = ReactiveCacheClient.getTrigger(idOrFirstObjectSelector, options);
+ return ReactiveCacheClient.getTrigger(idOrFirstObjectSelector, options);
}
- return ret;
},
getTriggers(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getTriggers(selector, options, getQuery);
+ return ReactiveCacheServer.getTriggers(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getTriggers(selector, options, getQuery);
+ return ReactiveCacheClient.getTriggers(selector, options, getQuery);
}
- return ret;
},
getImpersonatedUser(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getImpersonatedUser(
+ return ReactiveCacheServer.getImpersonatedUser(
idOrFirstObjectSelector,
options,
);
} else {
- ret = ReactiveCacheClient.getImpersonatedUser(
+ return ReactiveCacheClient.getImpersonatedUser(
idOrFirstObjectSelector,
options,
);
}
- return ret;
},
getImpersonatedUsers(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getImpersonatedUsers(
+ return ReactiveCacheServer.getImpersonatedUsers(
selector,
options,
getQuery,
);
} else {
- ret = ReactiveCacheClient.getImpersonatedUsers(
+ return ReactiveCacheClient.getImpersonatedUsers(
selector,
options,
getQuery,
);
}
- return ret;
},
getIntegration(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getIntegration(
+ return ReactiveCacheServer.getIntegration(
idOrFirstObjectSelector,
options,
);
} else {
- ret = ReactiveCacheClient.getIntegration(
+ return ReactiveCacheClient.getIntegration(
idOrFirstObjectSelector,
options,
);
}
- return ret;
},
getIntegrations(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getIntegrations(selector, options, getQuery);
+ return ReactiveCacheServer.getIntegrations(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getIntegrations(selector, options, getQuery);
+ return ReactiveCacheClient.getIntegrations(selector, options, getQuery);
}
- return ret;
},
getSessionData(idOrFirstObjectSelector = {}, options = {}) {
// no reactive cache, otherwise global search will not work anymore
- let ret = ReactiveCacheServer.getSessionData(
+ return ReactiveCacheServer.getSessionData(
idOrFirstObjectSelector,
options,
);
- return ret;
},
getSessionDatas(selector = {}, options = {}, getQuery = false) {
// no reactive cache, otherwise global search will not work anymore
- let ret = ReactiveCacheServer.getSessionDatas(selector, options, getQuery);
- return ret;
+ return ReactiveCacheServer.getSessionDatas(selector, options, getQuery);
},
getInvitationCode(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getInvitationCode(
+ return ReactiveCacheServer.getInvitationCode(
idOrFirstObjectSelector,
options,
);
} else {
- ret = ReactiveCacheClient.getInvitationCode(
+ return ReactiveCacheClient.getInvitationCode(
idOrFirstObjectSelector,
options,
);
}
- return ret;
},
getInvitationCodes(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getInvitationCodes(selector, options, getQuery);
+ return ReactiveCacheServer.getInvitationCodes(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getInvitationCodes(selector, options, getQuery);
+ return ReactiveCacheClient.getInvitationCodes(selector, options, getQuery);
}
- return ret;
},
getCurrentSetting() {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getCurrentSetting();
+ return ReactiveCacheServer.getCurrentSetting();
} else {
- ret = ReactiveCacheClient.getCurrentSetting();
+ return ReactiveCacheClient.getCurrentSetting();
}
- return ret;
},
getCurrentUser() {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getCurrentUser();
+ return ReactiveCacheServer.getCurrentUser();
} else {
- ret = ReactiveCacheClient.getCurrentUser();
+ return ReactiveCacheClient.getCurrentUser();
}
- return ret;
},
getTranslation(idOrFirstObjectSelector = {}, options = {}) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getTranslation(
+ return ReactiveCacheServer.getTranslation(
idOrFirstObjectSelector,
options,
);
} else {
- ret = ReactiveCacheClient.getTranslation(
+ return ReactiveCacheClient.getTranslation(
idOrFirstObjectSelector,
options,
);
}
- return ret;
},
getTranslations(selector = {}, options = {}, getQuery = false) {
- let ret;
if (Meteor.isServer) {
- ret = ReactiveCacheServer.getTranslations(selector, options, getQuery);
+ return ReactiveCacheServer.getTranslations(selector, options, getQuery);
} else {
- ret = ReactiveCacheClient.getTranslations(selector, options, getQuery);
+ return ReactiveCacheClient.getTranslations(selector, options, getQuery);
}
- return ret;
},
};
// Server isn't reactive, so search for the data always.
ReactiveMiniMongoIndexServer = {
- getSubTasksWithParentId(parentId, addSelect = {}, options = {}) {
+ async getSubTasksWithParentId(parentId, addSelect = {}, options = {}) {
let ret = [];
if (parentId) {
- ret = ReactiveCache.getCards({ parentId, ...addSelect }, options);
+ ret = await ReactiveCache.getCards({ parentId, ...addSelect }, options);
}
return ret;
},
- getChecklistsWithCardId(cardId, addSelect = {}, options = {}) {
+ async getChecklistsWithCardId(cardId, addSelect = {}, options = {}) {
let ret = [];
if (cardId) {
- ret = ReactiveCache.getChecklists({ cardId, ...addSelect }, options);
+ ret = await ReactiveCache.getChecklists({ cardId, ...addSelect }, options);
}
return ret;
},
- getChecklistItemsWithChecklistId(checklistId, addSelect = {}, options = {}) {
+ async getChecklistItemsWithChecklistId(checklistId, addSelect = {}, options = {}) {
let ret = [];
if (checklistId) {
- ret = ReactiveCache.getChecklistItems(
+ ret = await ReactiveCache.getChecklistItems(
{ checklistId, ...addSelect },
options,
);
}
return ret;
},
- getCardCommentsWithCardId(cardId, addSelect = {}, options = {}) {
+ async getCardCommentsWithCardId(cardId, addSelect = {}, options = {}) {
let ret = [];
if (cardId) {
- ret = ReactiveCache.getCardComments({ cardId, ...addSelect }, options);
+ ret = await ReactiveCache.getCardComments({ cardId, ...addSelect }, options);
}
return ret;
},
- getActivityWithId(activityId, addSelect = {}, options = {}) {
+ async getActivityWithId(activityId, addSelect = {}, options = {}) {
let ret = [];
if (activityId) {
- ret = ReactiveCache.getActivities(
+ ret = await ReactiveCache.getActivities(
{ _id: activityId, ...addSelect },
options,
);
@@ -1597,12 +1508,6 @@ ReactiveMiniMongoIndexClient = {
}
ret = this.__checklistItemsWithId.get(EJSON.stringify(select));
if (ret) {
- if (Meteor.isServer) {
- ret[checklistId] = ReactiveCache.getChecklistItems(
- { checklistId: checklistId, ...addSelect },
- options,
- );
- }
ret = ret[checklistId] || [];
}
}
diff --git a/models/accessibilitySettings.js b/models/accessibilitySettings.js
index b9785bb5a..0cc9260dc 100644
--- a/models/accessibilitySettings.js
+++ b/models/accessibilitySettings.js
@@ -47,7 +47,7 @@ AccessibilitySettings.attachSchema(
AccessibilitySettings.allow({
update(userId) {
- const user = ReactiveCache.getUser(userId);
+ const user = Meteor.users.findOne(userId);
return user && user.isAdmin;
},
});
diff --git a/models/accountSettings.js b/models/accountSettings.js
index 6b5a6b246..9030e56a9 100644
--- a/models/accountSettings.js
+++ b/models/accountSettings.js
@@ -46,7 +46,7 @@ AccountSettings.attachSchema(
AccountSettings.allow({
update(userId) {
- const user = ReactiveCache.getUser(userId);
+ const user = Meteor.users.findOne(userId);
return user && user.isAdmin;
},
});
diff --git a/models/actions.js b/models/actions.js
index b7c946318..09dd501d5 100644
--- a/models/actions.js
+++ b/models/actions.js
@@ -5,13 +5,13 @@ Actions = new Mongo.Collection('actions');
Actions.allow({
insert(userId, doc) {
- return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
update(userId, doc) {
- return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
remove(userId, doc) {
- return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
});
diff --git a/models/activities.js b/models/activities.js
index a53164bb0..6594ef620 100644
--- a/models/activities.js
+++ b/models/activities.js
@@ -105,12 +105,12 @@ if (Meteor.isServer) {
//Activities._collection.dropIndex({ labelId: 1 }, { partialFilterExpression: { labelId: { $exists: true } } });
});
- Activities.after.insert((userId, doc) => {
+ Activities.after.insert(async (userId, doc) => {
const activity = Activities._transform(doc);
let participants = [];
let watchers = [];
let title = 'act-activity-notify';
- const board = ReactiveCache.getBoard(activity.boardId);
+ const board = await ReactiveCache.getBoard(activity.boardId);
const description = `act-${activity.activityType}`;
const params = {
activityId: activity._id,
@@ -118,7 +118,7 @@ if (Meteor.isServer) {
if (activity.userId) {
// No need send notification to user of activity
// participants = _.union(participants, [activity.userId]);
- const user = activity.user();
+ const user = await activity.user();
if (user) {
if (user.getName()) {
params.user = user.getName();
@@ -146,7 +146,7 @@ if (Meteor.isServer) {
params.boardId = activity.boardId;
}
if (activity.oldBoardId) {
- const oldBoard = activity.oldBoard();
+ const oldBoard = await activity.oldBoard();
if (oldBoard) {
watchers = _.union(watchers, oldBoard.watchers || []);
params.oldBoard = oldBoard.title;
@@ -155,10 +155,10 @@ if (Meteor.isServer) {
}
if (activity.memberId) {
participants = _.union(participants, [activity.memberId]);
- params.member = activity.member().getName();
+ params.member = (await activity.member()).getName();
}
if (activity.listId) {
- const list = activity.list();
+ const list = await activity.list();
if (list) {
if (list.watchers !== undefined) {
watchers = _.union(watchers, list.watchers || []);
@@ -168,7 +168,7 @@ if (Meteor.isServer) {
}
}
if (activity.oldListId) {
- const oldList = activity.oldList();
+ const oldList = await activity.oldList();
if (oldList) {
watchers = _.union(watchers, oldList.watchers || []);
params.oldList = oldList.title;
@@ -176,7 +176,7 @@ if (Meteor.isServer) {
}
}
if (activity.oldSwimlaneId) {
- const oldSwimlane = activity.oldSwimlane();
+ const oldSwimlane = await activity.oldSwimlane();
if (oldSwimlane) {
watchers = _.union(watchers, oldSwimlane.watchers || []);
params.oldSwimlane = oldSwimlane.title;
@@ -184,7 +184,7 @@ if (Meteor.isServer) {
}
}
if (activity.cardId) {
- const card = activity.card();
+ const card = await activity.card();
participants = _.union(participants, [card.userId], card.members || []);
watchers = _.union(watchers, card.watchers || []);
params.card = card.title;
@@ -193,26 +193,26 @@ if (Meteor.isServer) {
params.cardId = activity.cardId;
}
if (activity.swimlaneId) {
- const swimlane = activity.swimlane();
+ const swimlane = await activity.swimlane();
params.swimlane = swimlane.title;
params.swimlaneId = activity.swimlaneId;
}
if (activity.commentId) {
- const comment = activity.comment();
+ const comment = await activity.comment();
params.comment = comment.text;
let hasMentions = false; // Track if comment has @mentions
if (board) {
const comment = params.comment;
- const knownUsers = board.members
- .filter((member) => member.isActive)
- .map((member) => {
- const u = ReactiveCache.getUser(member.userId);
- if (u) {
- member.username = u.username;
- member.emails = u.emails;
- }
- return member;
- });
+ // Build knownUsers with async user lookups
+ const knownUsers = [];
+ for (const member of board.members) {
+ const u = await ReactiveCache.getUser(member.userId);
+ if (u) {
+ member.username = u.username;
+ member.emails = u.emails;
+ }
+ knownUsers.push(member);
+ }
// Match @mentions including usernames with @ symbols (like email addresses)
// Pattern matches: @username, @user@example.com, @"quoted username"
const mentionRegex = /\B@(?:(?:"([\w.\s-]*)")|([\w.@-]+))/gi;
@@ -227,55 +227,62 @@ if (Meteor.isServer) {
if (activity.boardId && username === 'board_members') {
// mentions all board members
- const validUserIds = knownUsers
- .map((u) => u.userId)
- .filter((userId) => {
- const user = ReactiveCache.getUser(userId);
- return user && user._id;
- });
+ const validUserIds = [];
+ for (const u of knownUsers) {
+ const user = await ReactiveCache.getUser(u.userId);
+ if (user && user._id) {
+ validUserIds.push(u.userId);
+ }
+ }
watchers = _.union(watchers, validUserIds);
title = 'act-atUserComment';
hasMentions = true;
} else if (activity.boardId && username === 'board_assignees') {
// mentions all assignees of all cards on the board
- const allCards = ReactiveCache.getCards({ boardId: activity.boardId });
+ const allCards = await ReactiveCache.getCards({ boardId: activity.boardId });
const assigneeIds = [];
- allCards.forEach((card) => {
+ for (const card of allCards) {
if (card.assignees && card.assignees.length > 0) {
- card.assignees.forEach((assigneeId) => {
+ for (const assigneeId of card.assignees) {
// Only add if the user exists and is a board member
- const user = ReactiveCache.getUser(assigneeId);
+ const user = await ReactiveCache.getUser(assigneeId);
if (user && _.findWhere(knownUsers, { userId: assigneeId })) {
assigneeIds.push(assigneeId);
}
- });
+ }
}
- });
+ }
watchers = _.union(watchers, assigneeIds);
title = 'act-atUserComment';
hasMentions = true;
} else if (activity.cardId && username === 'card_members') {
// mentions all card members if assigned
- const card = activity.card();
+ const card = await activity.card();
if (card && card.members && card.members.length > 0) {
// Filter to only valid users who are board members
- const validMembers = card.members.filter((memberId) => {
- const user = ReactiveCache.getUser(memberId);
- return user && user._id && _.findWhere(knownUsers, { userId: memberId });
- });
+ const validMembers = [];
+ for (const memberId of card.members) {
+ const user = await ReactiveCache.getUser(memberId);
+ if (user && user._id && _.findWhere(knownUsers, { userId: memberId })) {
+ validMembers.push(memberId);
+ }
+ }
watchers = _.union(watchers, validMembers);
}
title = 'act-atUserComment';
hasMentions = true;
} else if (activity.cardId && username === 'card_assignees') {
// mentions all assignees of the current card
- const card = activity.card();
+ const card = await activity.card();
if (card && card.assignees && card.assignees.length > 0) {
// Filter to only valid users who are board members
- const validAssignees = card.assignees.filter((assigneeId) => {
- const user = ReactiveCache.getUser(assigneeId);
- return user && user._id && _.findWhere(knownUsers, { userId: assigneeId });
- });
+ const validAssignees = [];
+ for (const assigneeId of card.assignees) {
+ const user = await ReactiveCache.getUser(assigneeId);
+ if (user && user._id && _.findWhere(knownUsers, { userId: assigneeId })) {
+ validAssignees.push(assigneeId);
+ }
+ }
watchers = _.union(watchers, validAssignees);
}
title = 'act-atUserComment';
@@ -303,7 +310,7 @@ if (Meteor.isServer) {
params.attachmentId = activity.attachmentId;
}
if (activity.checklistId) {
- const checklist = activity.checklist();
+ const checklist = await activity.checklist();
if (checklist) {
if (checklist.title) {
params.checklist = checklist.title;
@@ -311,7 +318,7 @@ if (Meteor.isServer) {
}
}
if (activity.checklistItemId) {
- const checklistItem = activity.checklistItem();
+ const checklistItem = await activity.checklistItem();
if (checklistItem) {
if (checklistItem.title) {
params.checklistItem = checklistItem.title;
@@ -319,7 +326,7 @@ if (Meteor.isServer) {
}
}
if (activity.customFieldId) {
- const customField = activity.customField();
+ const customField = await activity.customField();
if (customField) {
if (customField.name) {
params.customField = customField.name;
@@ -331,7 +338,7 @@ if (Meteor.isServer) {
}
// Label activity did not work yet, unable to edit labels when tried this.
if (activity.labelId) {
- const label = activity.label();
+ const label = await activity.label();
if (label) {
if (label.name) {
params.label = label.name;
@@ -361,10 +368,8 @@ if (Meteor.isServer) {
try {
const atype = activity.activityType;
if (new RegExp(BIGEVENTS).exec(atype)) {
- watchers = _.union(
- watchers,
- board.activeMembers().map((member) => member.userId),
- ); // notify all active members for important events
+ const activeMemberIds = _.filter(board.members, m => m.isActive === true).map(m => m.userId);
+ watchers = _.union(watchers, activeMemberIds); // notify all active members for important events
}
} catch (e) {
// passed env var BIGEVENTS_PATTERN is not a valid regex
@@ -389,7 +394,7 @@ if (Meteor.isServer) {
);
}
}
- Notifications.getUsers(watchers).forEach((user) => {
+ (await Notifications.getUsers(watchers)).forEach((user) => {
// Skip if user is undefined or doesn't have an _id (e.g., deleted user or invalid ID)
if (!user || !user._id) return;
@@ -400,7 +405,7 @@ if (Meteor.isServer) {
}
});
- const integrations = ReactiveCache.getIntegrations({
+ const integrations = await ReactiveCache.getIntegrations({
boardId: { $in: [board._id, Integrations.Const.GLOBAL_WEBHOOK_ID] },
// type: 'outgoing-webhooks', // all types
enabled: true,
diff --git a/models/announcements.js b/models/announcements.js
index b44f7a229..a4f3455b4 100644
--- a/models/announcements.js
+++ b/models/announcements.js
@@ -51,7 +51,7 @@ Announcements.attachSchema(
Announcements.allow({
update(userId) {
- const user = ReactiveCache.getUser(userId);
+ const user = Meteor.users.findOne(userId);
return user && user.isAdmin;
},
});
diff --git a/models/attachmentStorageSettings.js b/models/attachmentStorageSettings.js
index ce0db9fb8..550bafcd3 100644
--- a/models/attachmentStorageSettings.js
+++ b/models/attachmentStorageSettings.js
@@ -257,12 +257,12 @@ AttachmentStorageSettings.helpers({
if (Meteor.isServer) {
// Get or create default settings
Meteor.methods({
- 'getAttachmentStorageSettings'() {
+ async 'getAttachmentStorageSettings'() {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -306,13 +306,13 @@ if (Meteor.isServer) {
return settings;
},
-
- 'updateAttachmentStorageSettings'(settings) {
+
+ async 'updateAttachmentStorageSettings'(settings) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -344,13 +344,13 @@ if (Meteor.isServer) {
const settings = AttachmentStorageSettings.findOne({});
return settings ? settings.getDefaultStorage() : STORAGE_NAME_FILESYSTEM;
},
-
- 'setDefaultAttachmentStorage'(storageName) {
+
+ async 'setDefaultAttachmentStorage'(storageName) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -375,12 +375,12 @@ if (Meteor.isServer) {
});
// Publication for settings
- Meteor.publish('attachmentStorageSettings', function() {
+ Meteor.publish('attachmentStorageSettings', async function() {
if (!this.userId) {
return this.ready();
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
return this.ready();
}
diff --git a/models/attachments.js b/models/attachments.js
index 3ac87da3b..5e20cf993 100644
--- a/models/attachments.js
+++ b/models/attachments.js
@@ -179,13 +179,13 @@ Attachments = new FilesCollection({
// We authorize the attachment download either:
// - if the board is public, everyone (even unconnected) can download it
// - if the board is private, only board members can download it
- protected(fileObj) {
+ async protected(fileObj) {
// file may have been deleted already again after upload validation failed
if (!fileObj) {
return false;
}
- const board = ReactiveCache.getBoard(fileObj.meta.boardId);
+ const board = await ReactiveCache.getBoard(fileObj.meta.boardId);
if (board.isPublic()) {
return true;
}
@@ -198,7 +198,7 @@ if (Meteor.isServer) {
Attachments.allow({
insert(userId, fileObj) {
// ReadOnly users cannot upload attachments
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(fileObj.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(fileObj.boardId));
},
update(userId, fileObj, fields) {
// SECURITY: The 'name' field is sanitized in onBeforeUpload and server-side methods,
@@ -230,7 +230,7 @@ if (Meteor.isServer) {
}
// ReadOnly users cannot update attachments
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(fileObj.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(fileObj.boardId));
},
remove(userId, fileObj) {
// Additional security check: ensure the file belongs to the board the user has access to
@@ -241,7 +241,7 @@ if (Meteor.isServer) {
return false;
}
- const board = ReactiveCache.getBoard(fileObj.boardId);
+ const board = Boards.findOne(fileObj.boardId);
if (!board) {
if (process.env.DEBUG === 'true') {
console.warn('Blocked attachment removal: board not found');
@@ -293,7 +293,7 @@ if (Meteor.isServer) {
return { valid: true };
},
- moveAttachmentToStorage(fileObjId, storageDestination) {
+ async moveAttachmentToStorage(fileObjId, storageDestination) {
check(fileObjId, String);
check(storageDestination, String);
@@ -301,12 +301,12 @@ if (Meteor.isServer) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const fileObj = ReactiveCache.getAttachment(fileObjId);
+ const fileObj = await ReactiveCache.getAttachment(fileObjId);
if (!fileObj) {
throw new Meteor.Error('attachment-not-found', 'Attachment not found');
}
- const board = ReactiveCache.getBoard(fileObj.boardId);
+ const board = await ReactiveCache.getBoard(fileObj.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -319,7 +319,7 @@ if (Meteor.isServer) {
moveToStorage(fileObj, storageDestination, fileStoreStrategyFactory);
},
- renameAttachment(fileObjId, newName) {
+ async renameAttachment(fileObjId, newName) {
check(fileObjId, String);
check(newName, String);
@@ -328,13 +328,13 @@ if (Meteor.isServer) {
throw new Meteor.Error('not-authorized', 'User must be logged in');
}
- const fileObj = ReactiveCache.getAttachment(fileObjId);
+ const fileObj = await ReactiveCache.getAttachment(fileObjId);
if (!fileObj) {
throw new Meteor.Error('file-not-found', 'Attachment not found');
}
// Verify the user has permission to modify this attachment
- const board = ReactiveCache.getBoard(fileObj.boardId);
+ const board = await ReactiveCache.getBoard(fileObj.boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
@@ -348,30 +348,30 @@ if (Meteor.isServer) {
rename(fileObj, newName, fileStoreStrategyFactory);
},
- validateAttachment(fileObjId) {
+ async validateAttachment(fileObjId) {
check(fileObjId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const fileObj = ReactiveCache.getAttachment(fileObjId);
+ const fileObj = await ReactiveCache.getAttachment(fileObjId);
if (!fileObj) {
throw new Meteor.Error('attachment-not-found', 'Attachment not found');
}
- const board = ReactiveCache.getBoard(fileObj.boardId);
+ const board = await ReactiveCache.getBoard(fileObj.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
- const isValid = Promise.await(isFileValid(fileObj, attachmentUploadMimeTypes, attachmentUploadSize, attachmentUploadExternalProgram));
+ const isValid = await isFileValid(fileObj, attachmentUploadMimeTypes, attachmentUploadSize, attachmentUploadExternalProgram);
if (!isValid) {
Attachments.remove(fileObjId);
}
},
- validateAttachmentAndMoveToStorage(fileObjId, storageDestination) {
+ async validateAttachmentAndMoveToStorage(fileObjId, storageDestination) {
check(fileObjId, String);
check(storageDestination, String);
@@ -379,12 +379,12 @@ if (Meteor.isServer) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const fileObj = ReactiveCache.getAttachment(fileObjId);
+ const fileObj = await ReactiveCache.getAttachment(fileObjId);
if (!fileObj) {
throw new Meteor.Error('attachment-not-found', 'Attachment not found');
}
- const board = ReactiveCache.getBoard(fileObj.boardId);
+ const board = await ReactiveCache.getBoard(fileObj.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -395,9 +395,9 @@ if (Meteor.isServer) {
throw new Meteor.Error('invalid-storage-destination', 'Invalid storage destination');
}
- Meteor.call('validateAttachment', fileObjId);
+ await Meteor.callAsync('validateAttachment', fileObjId);
- const fileObjAfter = ReactiveCache.getAttachment(fileObjId);
+ const fileObjAfter = await ReactiveCache.getAttachment(fileObjId);
if (fileObjAfter) {
Meteor.defer(() => Meteor.call('moveAttachmentToStorage', fileObjId, storageDestination));
diff --git a/models/avatars.js b/models/avatars.js
index da3033bc8..369eb473b 100644
--- a/models/avatars.js
+++ b/models/avatars.js
@@ -106,7 +106,7 @@ Avatars = new FilesCollection({
}
return TAPi18n.__('avatar-too-big', {size: filesize(avatarsUploadSize)});
},
- onAfterUpload(fileObj) {
+ async onAfterUpload(fileObj) {
// current storage is the filesystem, update object and database
Object.keys(fileObj.versions).forEach(versionName => {
fileObj.versions[versionName].storage = STORAGE_NAME_FILESYSTEM;
@@ -114,12 +114,13 @@ Avatars = new FilesCollection({
Avatars.update({ _id: fileObj._id }, { $set: { "versions": fileObj.versions } });
- const isValid = Promise.await(isFileValid(fileObj, avatarsUploadMimeTypes, avatarsUploadSize, avatarsUploadExternalProgram));
+ const isValid = await isFileValid(fileObj, avatarsUploadMimeTypes, avatarsUploadSize, avatarsUploadExternalProgram);
if (isValid) {
// Set avatar URL using universal URL generator (URL-agnostic)
const universalUrl = generateUniversalAvatarUrl(fileObj._id);
- ReactiveCache.getUser(fileObj.userId).setAvatarUrl(universalUrl);
+ const user = await ReactiveCache.getUser(fileObj.userId);
+ user.setAvatarUrl(universalUrl);
} else {
Avatars.remove(fileObj._id);
}
@@ -128,12 +129,13 @@ Avatars = new FilesCollection({
const ret = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName).interceptDownload(http, this.cacheControl);
return ret;
},
- onBeforeRemove(files) {
- files.forEach(fileObj => {
+ async onBeforeRemove(files) {
+ for (const fileObj of files) {
if (fileObj.userId) {
- ReactiveCache.getUser(fileObj.userId).setAvatarUrl('');
+ const user = await ReactiveCache.getUser(fileObj.userId);
+ user.setAvatarUrl('');
}
- });
+ }
return true;
},
diff --git a/models/boards.js b/models/boards.js
index a8ec9e1ed..223699e84 100644
--- a/models/boards.js
+++ b/models/boards.js
@@ -703,12 +703,12 @@ Boards.attachSchema(
);
Boards.helpers({
- copy() {
+ async copy() {
const oldId = this._id;
const oldWatchers = this.watchers ? this.watchers.slice() : [];
delete this._id;
delete this.slug;
- this.title = this.copyTitle();
+ this.title = await this.copyTitle();
const _id = Boards.insert(this);
// Temporary remove watchers to disable notifications
@@ -719,23 +719,26 @@ Boards.helpers({
});
// Copy all swimlanes in board
- ReactiveCache.getSwimlanes({
+ const swimlanes = await ReactiveCache.getSwimlanes({
boardId: oldId,
archived: false,
- }).forEach(swimlane => {
+ });
+ for (const swimlane of swimlanes) {
swimlane.type = 'swimlane';
swimlane.copy(_id);
- });
+ }
// copy custom field definitions
const cfMap = {};
- ReactiveCache.getCustomFields({ boardIds: oldId }).forEach(cf => {
+ const customFields = await ReactiveCache.getCustomFields({ boardIds: oldId });
+ for (const cf of customFields) {
const id = cf._id;
delete cf._id;
cf.boardIds = [_id];
cfMap[id] = CustomFields.insert(cf);
- });
- ReactiveCache.getCards({ boardId: _id }).forEach(card => {
+ }
+ const cards = await ReactiveCache.getCards({ boardId: _id });
+ for (const card of cards) {
Cards.update(card._id, {
$set: {
customFields: card.customFields.map(cf => {
@@ -744,30 +747,33 @@ Boards.helpers({
}),
},
});
- });
+ }
// copy rules, actions, and triggers
const actionsMap = {};
- ReactiveCache.getActions({ boardId: oldId }).forEach(action => {
+ const actions = await ReactiveCache.getActions({ boardId: oldId });
+ for (const action of actions) {
const id = action._id;
delete action._id;
action.boardId = _id;
actionsMap[id] = Actions.insert(action);
- });
+ }
const triggersMap = {};
- ReactiveCache.getTriggers({ boardId: oldId }).forEach(trigger => {
+ const triggers = await ReactiveCache.getTriggers({ boardId: oldId });
+ for (const trigger of triggers) {
const id = trigger._id;
delete trigger._id;
trigger.boardId = _id;
triggersMap[id] = Triggers.insert(trigger);
- });
- ReactiveCache.getRules({ boardId: oldId }).forEach(rule => {
+ }
+ const rules = await ReactiveCache.getRules({ boardId: oldId });
+ for (const rule of rules) {
delete rule._id;
rule.boardId = _id;
rule.actionId = actionsMap[rule.actionId];
rule.triggerId = triggersMap[rule.triggerId];
Rules.insert(rule);
- });
+ }
// Re-set Watchers to reenable notification
Boards.update(_id, {
@@ -781,8 +787,8 @@ Boards.helpers({
*
* @returns {string|null}
*/
- copyTitle() {
- return Boards.uniqueTitle(this.title);
+ async copyTitle() {
+ return await Boards.uniqueTitle(this.title);
},
/**
@@ -837,7 +843,8 @@ Boards.helpers({
newestLists() {
// sorted lists from newest to the oldest, by its creation date or its cards' last modification date
- const value = ReactiveCache.getCurrentUser()._getListSortBy();
+ const user = ReactiveCache.getCurrentUser();
+ const value = user._getListSortBy();
const sortKey = { starred: -1, [value[0]]: value[1] }; // [["starred",-1],value];
return ReactiveCache.getLists(
{
@@ -924,9 +931,9 @@ Boards.helpers({
let linkedBoardId = [this._id];
ReactiveCache.getCards({
"type": "cardType-linkedBoard",
- "boardId": this._id}
- ).forEach(card => {
- linkedBoardId.push(card.linkedId);
+ "boardId": this._id
+ }).forEach(card => {
+ linkedBoardId.push(card.linkedId);
});
const ret = ReactiveCache.getActivities({ boardId: { $in: linkedBoardId } }, { sort: { createdAt: -1 } });
return ret;
@@ -1455,7 +1462,7 @@ Boards.helpers({
},
async setBackgroundImageURL(backgroundImageURL) {
- const currentUser = ReactiveCache.getCurrentUser();
+ const currentUser = await ReactiveCache.getCurrentUser();
if (currentUser.isBoardAdmin() || currentUser.isAdmin()) {
return await Boards.updateAsync(this._id, { $set: { backgroundImageURL } });
}
@@ -1718,31 +1725,32 @@ function boardRemover(userId, doc) {
);
}
-Boards.uniqueTitle = title => {
+Boards.uniqueTitle = async title => {
const m = title.match(
new RegExp('^(?
.*?)\\s*(\\[(?\\d+)]\\s*$|\\s*$)'),
);
const base = escapeForRegex(m.groups.title);
const baseTitle = m.groups.title;
- boards = ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*(\\[(?\\d+)]\\s*$|\\s*$)`) });
+ const boards = await ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*(\\[(?\\d+)]\\s*$|\\s*$)`) });
if (boards.length > 0) {
let num = 0;
- ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*\\[\\d+]\\s*$`) }).forEach(
- board => {
- const m = board.title.match(
- new RegExp('^(?.*?)\\s*\\[(?\\d+)]\\s*$'),
- );
- if (m) {
- const n = parseInt(m.groups.num, 10);
- num = num < n ? n : num;
- }
- },
- );
+ const numberedBoards = await ReactiveCache.getBoards({ title: new RegExp(`^${base}\\s*\\[\\d+]\\s*$`) });
+ for (const board of numberedBoards) {
+ const m = board.title.match(
+ new RegExp('^(?.*?)\\s*\\[(?\\d+)]\\s*$'),
+ );
+ if (m) {
+ const n = parseInt(m.groups.num, 10);
+ num = num < n ? n : num;
+ }
+ }
return `${baseTitle} [${num + 1}]`;
}
return title;
};
+// Non-async: returns data on client, Promise on server.
+// Server callers must await.
Boards.userSearch = (
userId,
selector = {},
@@ -1757,42 +1765,51 @@ Boards.userSearch = (
if (userId) {
selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } });
}
- const ret = ReactiveCache.getBoards(selector, projection);
- return ret;
+ return ReactiveCache.getBoards(selector, projection);
};
+// Non-async: returns data on client (for Blaze templates), Promise on server.
+// Server callers must await.
Boards.userBoards = (
userId,
archived = false,
selector = {},
projection = {},
) => {
+ const _buildSelector = (user) => {
+ if (!user) return null;
+ if (typeof archived === 'boolean') {
+ selector.archived = archived;
+ }
+ if (!selector.type) {
+ selector.type = 'board';
+ }
+ selector.$or = [
+ { permission: 'public' },
+ { members: { $elemMatch: { userId, isActive: true } } },
+ { orgs: { $elemMatch: { orgId: { $in: user.orgIds() }, isActive: true } } },
+ { teams: { $elemMatch: { teamId: { $in: user.teamIds() }, isActive: true } } },
+ ];
+ return selector;
+ };
+
+ if (Meteor.isServer) {
+ return (async () => {
+ const user = await ReactiveCache.getUser(userId);
+ if (!_buildSelector(user)) return [];
+ return await ReactiveCache.getBoards(selector, projection);
+ })();
+ }
const user = ReactiveCache.getUser(userId);
- if (!user) {
- return [];
- }
-
- if (typeof archived === 'boolean') {
- selector.archived = archived;
- }
- if (!selector.type) {
- selector.type = 'board';
- }
-
- selector.$or = [
- { permission: 'public' },
- { members: { $elemMatch: { userId, isActive: true } } },
- { orgs: { $elemMatch: { orgId: { $in: user.orgIds() }, isActive: true } } },
- { teams: { $elemMatch: { teamId: { $in: user.teamIds() }, isActive: true } } },
- ];
-
+ if (!_buildSelector(user)) return [];
return ReactiveCache.getBoards(selector, projection);
};
-Boards.userBoardIds = (userId, archived = false, selector = {}) => {
- return Boards.userBoards(userId, archived, selector, {
+Boards.userBoardIds = async (userId, archived = false, selector = {}) => {
+ const boards = await Boards.userBoards(userId, archived, selector, {
fields: { _id: 1 },
- }).map(board => {
+ });
+ return boards.map(board => {
return board._id;
});
};
@@ -1888,13 +1905,13 @@ if (Meteor.isServer) {
});
Meteor.methods({
- getBackgroundImageURL(boardId) {
+ async getBackgroundImageURL(boardId) {
check(boardId, String);
- return ReactiveCache.getBoard(boardId, {}, { backgroundImageUrl: 1 });
+ return await ReactiveCache.getBoard(boardId, {}, { backgroundImageUrl: 1 });
},
async quitBoard(boardId) {
check(boardId, String);
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (board) {
const userId = Meteor.userId();
const index = board.memberIndex(userId);
@@ -1904,9 +1921,9 @@ if (Meteor.isServer) {
} else throw new Meteor.Error('error-board-notAMember');
} else throw new Meteor.Error('error-board-doesNotExist');
},
- acceptInvite(boardId) {
+ async acceptInvite(boardId) {
check(boardId, String);
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('error-board-doesNotExist');
}
@@ -1928,9 +1945,10 @@ if (Meteor.isServer) {
}
});
},
- myLabelNames() {
+ async myLabelNames() {
let names = [];
- Boards.userBoards(Meteor.userId()).forEach(board => {
+ const boards = await Boards.userBoards(Meteor.userId());
+ for (const board of boards) {
// Only return labels when they exist.
if (board.labels !== undefined) {
names = names.concat(
@@ -1940,21 +1958,21 @@ if (Meteor.isServer) {
return label.name;
}),
);
- } else {
- return [];
}
- });
+ }
return _.uniq(names).sort();
},
- myBoardNames() {
+ async myBoardNames() {
+ const boards = await Boards.userBoards(Meteor.userId());
return _.uniq(
- Boards.userBoards(Meteor.userId()).map(board => {
+ boards.map(board => {
return board.title;
}),
).sort();
},
- setAllBoardsHideActivities() {
- if ((ReactiveCache.getCurrentUser() || {}).isAdmin) {
+ async setAllBoardsHideActivities() {
+ const currentUser = await ReactiveCache.getCurrentUser();
+ if ((currentUser || {}).isAdmin) {
Boards.update(
{
showActivities: true
@@ -1978,7 +1996,7 @@ if (Meteor.isServer) {
Meteor.methods({
async archiveBoard(boardId) {
check(boardId, String);
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (board) {
const userId = Meteor.userId();
const index = board.memberIndex(userId);
@@ -1988,14 +2006,14 @@ if (Meteor.isServer) {
} else throw new Meteor.Error('error-board-notAMember');
} else throw new Meteor.Error('error-board-doesNotExist');
},
- setBoardOrgs(boardOrgsArray, currBoardId){
+ async setBoardOrgs(boardOrgsArray, currBoardId){
check(boardOrgsArray, Array);
check(currBoardId, String);
const userId = Meteor.userId();
if (!userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in to perform this action.');
}
- const board = ReactiveCache.getBoard(currBoardId);
+ const board = await ReactiveCache.getBoard(currBoardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found.');
}
@@ -2014,7 +2032,7 @@ if (Meteor.isServer) {
},
});
},
- setBoardTeams(boardTeamsArray, membersArray, currBoardId){
+ async setBoardTeams(boardTeamsArray, membersArray, currBoardId){
check(boardTeamsArray, Array);
check(membersArray, Array);
check(currBoardId, String);
@@ -2022,7 +2040,7 @@ if (Meteor.isServer) {
if (!userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in to perform this action.');
}
- const board = ReactiveCache.getBoard(currBoardId);
+ const board = await ReactiveCache.getBoard(currBoardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found.');
}
@@ -2059,8 +2077,8 @@ if (Meteor.isServer) {
}
// Insert new board at last position in sort order.
-Boards.before.insert((userId, doc) => {
- const lastBoard = ReactiveCache.getBoard(
+Boards.before.insert(async (userId, doc) => {
+ const lastBoard = await ReactiveCache.getBoard(
{ sort: { $exists: true } },
{ sort: { sort: -1 } },
);
@@ -2244,7 +2262,7 @@ if (Meteor.isServer) {
* @return_type [{_id: string,
* title: string}]
*/
- JsonRoutes.add('GET', '/api/users/:userId/boards', function(req, res) {
+ JsonRoutes.add('GET', '/api/users/:userId/boards', async function(req, res) {
try {
Authentication.checkLoggedIn(req.userId);
const paramUserId = req.params.userId;
@@ -2255,7 +2273,7 @@ if (Meteor.isServer) {
req.userId === paramUserId,
);
- const data = ReactiveCache.getBoards(
+ const boards = await ReactiveCache.getBoards(
{
archived: false,
'members.userId': paramUserId,
@@ -2263,7 +2281,8 @@ if (Meteor.isServer) {
{
sort: { sort: 1 /* boards default sorting */ },
},
- ).map(function(board) {
+ );
+ const data = boards.map(function(board) {
return {
_id: board._id,
title: board.title,
@@ -2286,17 +2305,18 @@ if (Meteor.isServer) {
* @return_type [{_id: string,
title: string}]
*/
- JsonRoutes.add('GET', '/api/boards', function(req, res) {
+ JsonRoutes.add('GET', '/api/boards', async function(req, res) {
try {
Authentication.checkUserId(req.userId);
+ const boards = await ReactiveCache.getBoards(
+ { permission: 'public' },
+ {
+ sort: { sort: 1 /* boards default sorting */ },
+ },
+ );
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getBoards(
- { permission: 'public' },
- {
- sort: { sort: 1 /* boards default sorting */ },
- },
- ).map(function(doc) {
+ data: boards.map(function(doc) {
return {
_id: doc._id,
title: doc.title,
@@ -2317,14 +2337,16 @@ if (Meteor.isServer) {
*
* @return_type {private: integer, public: integer}
*/
- JsonRoutes.add('GET', '/api/boards_count', function(req, res) {
+ JsonRoutes.add('GET', '/api/boards_count', async function(req, res) {
try {
Authentication.checkUserId(req.userId);
+ const privateBoards = await ReactiveCache.getBoards({ permission: 'private' });
+ const publicBoards = await ReactiveCache.getBoards({ permission: 'public' });
JsonRoutes.sendResult(res, {
code: 200,
data: {
- private: ReactiveCache.getBoards({ permission: 'private' }).length,
- public: ReactiveCache.getBoards({ permission: 'public' }).length,
+ private: privateBoards.length,
+ public: publicBoards.length,
},
});
} catch (error) {
@@ -2342,14 +2364,15 @@ if (Meteor.isServer) {
* @param {string} boardId the ID of the board to retrieve the data
* @return_type Boards
*/
- JsonRoutes.add('GET', '/api/boards/:boardId', function(req, res) {
+ JsonRoutes.add('GET', '/api/boards/:boardId', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
+ const board = await ReactiveCache.getBoard(paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getBoard(paramBoardId),
+ data: board,
});
} catch (error) {
JsonRoutes.sendResult(res, {
@@ -2494,12 +2517,12 @@ if (Meteor.isServer) {
*
* @return_type string
*/
- JsonRoutes.add('PUT', '/api/boards/:boardId/labels', function(req, res) {
+ JsonRoutes.add('PUT', '/api/boards/:boardId/labels', async function(req, res) {
const id = req.params.boardId;
Authentication.checkBoardWriteAccess(req.userId, id);
try {
if (req.body.hasOwnProperty('label')) {
- const board = ReactiveCache.getBoard(id);
+ const board = await ReactiveCache.getBoard(id);
const color = req.body.label.color;
const name = req.body.label.name;
const labelId = Random.id(6);
@@ -2537,14 +2560,14 @@ if (Meteor.isServer) {
*
* @return_type string
*/
-JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
+JsonRoutes.add('POST', '/api/boards/:boardId/copy', async function(req, res) {
const id = req.params.boardId;
- const board = ReactiveCache.getBoard(id);
+ const board = await ReactiveCache.getBoard(id);
const adminAccess = board.members.some(e => e.userId === req.userId && e.isAdmin);
Authentication.checkAdminOrCondition(req.userId, adminAccess);
try {
- board['title'] = req.body.title || Boards.uniqueTitle(board.title);
- ret = board.copy();
+ board['title'] = req.body.title || await Boards.uniqueTitle(board.title);
+ ret = await board.copy();
JsonRoutes.sendResult(res, {
code: 200,
data: ret,
@@ -2581,7 +2604,7 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
const boardId = req.params.boardId;
const memberId = req.params.memberId;
const { isAdmin, isNoComments, isCommentOnly, isWorker, isNormalAssignedOnly, isCommentAssignedOnly, isReadOnly, isReadAssignedOnly } = req.body;
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
function isTrue(data) {
try {
return data.toLowerCase() === 'true';
@@ -2631,13 +2654,14 @@ JsonRoutes.add('POST', '/api/boards/:boardId/copy', function(req, res) {
* cardId: string
* }]
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/attachments', function(req, res) {
+ JsonRoutes.add('GET', '/api/boards/:boardId/attachments', async function(req, res) {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
+ const attachments = await ReactiveCache
+ .getAttachments({'meta.boardId': paramBoardId }, {}, true);
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache
- .getAttachments({'meta.boardId': paramBoardId }, {}, true)
+ data: attachments
.each()
.map(function(attachment) {
return {
diff --git a/models/cardCommentReactions.js b/models/cardCommentReactions.js
index 6ddd930fa..8271a563f 100644
--- a/models/cardCommentReactions.js
+++ b/models/cardCommentReactions.js
@@ -51,13 +51,13 @@ CardCommentReactions.attachSchema(
CardCommentReactions.allow({
insert(userId, doc) {
- return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
},
update(userId, doc) {
- return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
},
remove(userId, doc) {
- return allowIsBoardMember(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMember(userId, Boards.findOne(doc.boardId));
},
fetch: ['boardId'],
});
diff --git a/models/cardComments.js b/models/cardComments.js
index fd2e8502d..a377083ed 100644
--- a/models/cardComments.js
+++ b/models/cardComments.js
@@ -83,13 +83,13 @@ CardComments.attachSchema(
CardComments.allow({
insert(userId, doc) {
// ReadOnly users cannot add comments. Only members who can comment are allowed.
- return allowIsBoardMemberCommentOnly(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMemberCommentOnly(userId, Boards.findOne(doc.boardId));
},
update(userId, doc) {
- return userId === doc.userId || allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return userId === doc.userId || allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
remove(userId, doc) {
- return userId === doc.userId || allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return userId === doc.userId || allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
fetch: ['userId', 'boardId'],
});
@@ -154,8 +154,8 @@ CardComments.helpers({
CardComments.hookOptions.after.update = { fetchPrevious: false };
-function commentCreation(userId, doc) {
- const card = ReactiveCache.getCard(doc.cardId);
+async function commentCreation(userId, doc) {
+ const card = await ReactiveCache.getCard(doc.cardId);
Activities.insert({
userId,
activityType: 'addComment',
@@ -167,9 +167,9 @@ function commentCreation(userId, doc) {
});
}
-CardComments.textSearch = (userId, textArray) => {
+CardComments.textSearch = async (userId, textArray) => {
const selector = {
- boardId: { $in: Boards.userBoardIds(userId) },
+ boardId: { $in: await Boards.userBoardIds(userId) },
$and: [],
};
@@ -180,7 +180,7 @@ CardComments.textSearch = (userId, textArray) => {
// eslint-disable-next-line no-console
// console.log('cardComments selector:', selector);
- const comments = ReactiveCache.getCardComments(selector);
+ const comments = await ReactiveCache.getCardComments(selector);
// eslint-disable-next-line no-console
// console.log('count:', comments.count());
// eslint-disable-next-line no-console
@@ -197,12 +197,12 @@ if (Meteor.isServer) {
await CardComments._collection.createIndexAsync({ cardId: 1, createdAt: -1 });
});
- CardComments.after.insert((userId, doc) => {
- commentCreation(userId, doc);
+ CardComments.after.insert(async (userId, doc) => {
+ await commentCreation(userId, doc);
});
- CardComments.after.update((userId, doc) => {
- const card = ReactiveCache.getCard(doc.cardId);
+ CardComments.after.update(async (userId, doc) => {
+ const card = await ReactiveCache.getCard(doc.cardId);
Activities.insert({
userId,
activityType: 'editComment',
@@ -214,8 +214,8 @@ if (Meteor.isServer) {
});
});
- CardComments.before.remove((userId, doc) => {
- const card = ReactiveCache.getCard(doc.cardId);
+ CardComments.before.remove(async (userId, doc) => {
+ const card = await ReactiveCache.getCard(doc.cardId);
Activities.insert({
userId,
activityType: 'deleteComment',
@@ -225,7 +225,7 @@ if (Meteor.isServer) {
listId: card.listId,
swimlaneId: card.swimlaneId,
});
- const activity = ReactiveCache.getActivity({ commentId: doc._id });
+ const activity = await ReactiveCache.getActivity({ commentId: doc._id });
if (activity) {
Activities.remove(activity._id);
}
@@ -244,7 +244,7 @@ if (Meteor.isServer) {
* comment: string,
* authorId: string}]
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/comments', function (
+ JsonRoutes.add('GET', '/api/boards/:boardId/cards/:cardId/comments', async function (
req,
res,
) {
@@ -254,10 +254,10 @@ if (Meteor.isServer) {
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getCardComments({
+ data: (await ReactiveCache.getCardComments({
boardId: paramBoardId,
cardId: paramCardId,
- }).map(function (doc) {
+ })).map(function (doc) {
return {
_id: doc._id,
comment: doc.text,
@@ -285,7 +285,7 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/cards/:cardId/comments/:commentId',
- function (req, res) {
+ async function (req, res) {
try {
const paramBoardId = req.params.boardId;
const paramCommentId = req.params.commentId;
@@ -293,7 +293,7 @@ if (Meteor.isServer) {
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getCardComment({
+ data: await ReactiveCache.getCardComment({
_id: paramCommentId,
cardId: paramCardId,
boardId: paramBoardId,
@@ -320,7 +320,7 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/cards/:cardId/comments',
- function (req, res) {
+ async function (req, res) {
try {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
@@ -339,12 +339,12 @@ if (Meteor.isServer) {
},
});
- const cardComment = ReactiveCache.getCardComment({
+ const cardComment = await ReactiveCache.getCardComment({
_id: id,
cardId: paramCardId,
boardId: paramBoardId,
});
- commentCreation(req.userId, cardComment);
+ await commentCreation(req.userId, cardComment);
} catch (error) {
JsonRoutes.sendResult(res, {
code: 200,
diff --git a/models/cards.js b/models/cards.js
index 43ebe484b..290447ada 100644
--- a/models/cards.js
+++ b/models/cards.js
@@ -531,21 +531,20 @@ canUpdateCard = function(userId, doc, fields) {
return false;
}
// ReadOnly users cannot edit cards
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
};
Cards.allow({
insert(userId, doc) {
// ReadOnly users cannot create cards
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
},
-
update(userId, doc, fields) {
return canUpdateCard(userId, doc, fields);
},
remove(userId, doc) {
// ReadOnly users cannot delete cards
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
},
fetch: ['boardId'],
});
@@ -572,7 +571,7 @@ Cards.helpers({
});
},
- async mapCustomFieldsToBoard(boardId) {
+ mapCustomFieldsToBoard(boardId) {
// Guard against undefined/null customFields
if (!this.customFields || !Array.isArray(this.customFields)) {
return [];
@@ -598,7 +597,7 @@ Cards.helpers({
if (newCf) {
cf._id = newCf._id;
} else if (!_.contains(oldCf.boardIds, boardId)) {
- await oldCf.addBoard(boardId);
+ oldCf.addBoard(boardId);
}
result.push(cf);
@@ -607,7 +606,7 @@ Cards.helpers({
},
- async copy(boardId, swimlaneId, listId) {
+ copy(boardId, swimlaneId, listId) {
const oldId = this._id;
const oldCard = ReactiveCache.getCard(oldId);
@@ -644,14 +643,16 @@ Cards.helpers({
);
cardData.labelIds = newCardLabels;
- cardData.customFields = await this.mapCustomFieldsToBoard(newBoard._id);
+ this.customFields = this.mapCustomFieldsToBoard(newBoard._id);
}
- cardData.boardId = boardId;
- cardData.cardNumber = ReactiveCache.getBoard(boardId).getNextCardNumber();
- cardData.swimlaneId = swimlaneId;
- cardData.listId = listId;
- const _id = Cards.insert(cardData);
+ delete this._id;
+ this.boardId = boardId;
+ const board = ReactiveCache.getBoard(boardId);
+ this.cardNumber = board.getNextCardNumber();
+ this.swimlaneId = swimlaneId;
+ this.listId = listId;
+ const _id = Cards.insertAsync(this);
// Copy attachments
oldCard.attachments()
@@ -660,21 +661,26 @@ Cards.helpers({
});
// copy checklists
- ReactiveCache.getChecklists({ cardId: oldId }).forEach(ch => {
+ const checklists = ReactiveCache.getChecklists({ cardId: oldId });
+ for (const ch of checklists) {
ch.copy(_id);
- });
+ }
// copy subtasks
- ReactiveCache.getCards({ parentId: oldId }).forEach(subtask => {
+ const subtasks = ReactiveCache.getCards({ parentId: oldId });
+ for (const subtask of subtasks) {
subtask.parentId = _id;
subtask._id = null;
- Cards.insert(subtask);
- });
+ Cards.insertAsync(subtask);
+ }
// copy card comments
- ReactiveCache.getCardComments({ cardId: oldId }).forEach(cmt => {
+ const comments = ReactiveCache.getCardComments({ cardId: oldId });
+ for (const cmt of comments) {
cmt.copy(_id);
- });
+ }
+ // restore the id, otherwise new copies will fail
+ this._id = oldId;
return _id;
},
@@ -1119,7 +1125,7 @@ Cards.helpers({
},
parentString(sep) {
- return this.parentList()
+ return (this.parentList())
.map(function(elem) {
return elem.title;
})
@@ -1214,13 +1220,13 @@ Cards.helpers({
}
},
- async assignMember(memberId) {
+ assignMember(memberId) {
let ret;
if (this.isLinkedBoard()) {
const board = ReactiveCache.getBoard(this.linkedId);
- ret = await board.addMember(memberId);
+ ret = board.addMember(memberId);
} else {
- ret = Cards.update(
+ ret = Cards.updateAsync(
{ _id: this.getRealId() },
{ $addToSet: { members: memberId } },
);
@@ -1230,7 +1236,7 @@ Cards.helpers({
assignAssignee(assigneeId) {
if (this.isLinkedCard()) {
- return Cards.update(
+ return Cards.updateAsync(
{ _id: this.linkedId },
{ $addToSet: { assignees: assigneeId } },
);
@@ -1238,30 +1244,30 @@ Cards.helpers({
const board = ReactiveCache.getBoard(this.linkedId);
return board.addAssignee(assigneeId);
} else {
- return Cards.update(
+ return Cards.updateAsync(
{ _id: this._id },
{ $addToSet: { assignees: assigneeId } },
);
}
},
- async unassignMember(memberId) {
+ unassignMember(memberId) {
if (this.isLinkedCard()) {
- return Cards.update(
+ return Cards.updateAsync(
{ _id: this.linkedId },
{ $pull: { members: memberId } },
);
} else if (this.isLinkedBoard()) {
const board = ReactiveCache.getBoard(this.linkedId);
- return await board.removeMember(memberId);
+ return board.removeMember(memberId);
} else {
- return Cards.update({ _id: this._id }, { $pull: { members: memberId } });
+ return Cards.updateAsync({ _id: this._id }, { $pull: { members: memberId } });
}
},
unassignAssignee(assigneeId) {
if (this.isLinkedCard()) {
- return Cards.update(
+ return Cards.updateAsync(
{ _id: this.linkedId },
{ $pull: { assignees: assigneeId } },
);
@@ -1269,7 +1275,7 @@ Cards.helpers({
const board = ReactiveCache.getBoard(this.linkedId);
return board.removeAssignee(assigneeId);
} else {
- return Cards.update(
+ return Cards.updateAsync(
{ _id: this._id },
{ $pull: { assignees: assigneeId } },
);
@@ -1277,7 +1283,8 @@ Cards.helpers({
},
toggleMember(memberId) {
- if (this.getMembers() && this.getMembers().indexOf(memberId) > -1) {
+ const members = this.getMembers();
+ if (members && members.indexOf(memberId) > -1) {
return this.unassignMember(memberId);
} else {
return this.assignMember(memberId);
@@ -1285,7 +1292,8 @@ Cards.helpers({
},
toggleAssignee(assigneeId) {
- if (this.getAssignees() && this.getAssignees().indexOf(assigneeId) > -1) {
+ const assignees = this.getAssignees();
+ if (assignees && assignees.indexOf(assigneeId) > -1) {
return this.unassignAssignee(assigneeId);
} else {
return this.assignAssignee(assigneeId);
@@ -2003,32 +2011,32 @@ Cards.helpers({
return pokerWinnersListMap[0].pokerCard;
},
- async applyToChildren(funct) {
+ applyToChildren(funct) {
const cards = ReactiveCache.getCards({ parentId: this._id });
for (const card of cards) {
- await funct(card);
+ funct(card);
}
},
- async archive() {
- await this.applyToChildren(async card => {
- await card.archive();
+ archive() {
+ this.applyToChildren(card => {
+ card.archive();
});
- return await Cards.updateAsync(this._id, {
+ return Cards.updateAsync(this._id, {
$set: { archived: true, archivedAt: new Date() },
});
},
- async restore() {
- await this.applyToChildren(async card => {
- await card.restore();
+ restore() {
+ this.applyToChildren(card => {
+ card.restore();
});
- return await Cards.updateAsync(this._id, {
+ return Cards.updateAsync(this._id, {
$set: { archived: false },
});
},
- async moveToEndOfList({ listId, swimlaneId } = {}) {
+ moveToEndOfList({ listId, swimlaneId } = {}) {
swimlaneId = swimlaneId || this.swimlaneId;
const boardId = this.boardId;
let sortIndex = 0;
@@ -2045,7 +2053,7 @@ Cards.helpers({
.get(0);
if (lastCardDom) sortIndex = Utils.calculateIndex(lastCardDom, null).base;
- return await this.moveOptionalArgs({
+ return this.moveOptionalArgs({
boardId,
swimlaneId,
listId,
@@ -2053,7 +2061,7 @@ Cards.helpers({
});
},
- async moveOptionalArgs({ boardId, swimlaneId, listId, sort } = {}) {
+ moveOptionalArgs({ boardId, swimlaneId, listId, sort } = {}) {
boardId = boardId || this.boardId;
swimlaneId = swimlaneId || this.swimlaneId;
if (!swimlaneId) {
@@ -2062,10 +2070,10 @@ Cards.helpers({
}
listId = listId || this.listId;
if (sort === undefined || sort === null) sort = this.sort;
- return await this.move(boardId, swimlaneId, listId, sort);
+ return this.move(boardId, swimlaneId, listId, sort);
},
- async move(boardId, swimlaneId, listId, sort = null) {
+ move(boardId, swimlaneId, listId, sort = null) {
const previousState = {
boardId: this.boardId,
swimlaneId: this.swimlaneId,
@@ -2105,7 +2113,7 @@ Cards.helpers({
cardNumber: newCardNumber
});
- mutatedFields.customFields = await this.mapCustomFieldsToBoard(newBoard._id);
+ mutatedFields.customFields = this.mapCustomFieldsToBoard(newBoard._id);
// Ensure customFields is always an array (guards against legacy {} data)
if (!Array.isArray(mutatedFields.customFields)) {
@@ -2113,7 +2121,7 @@ Cards.helpers({
}
}
- await Cards.updateAsync(this._id, { $set: mutatedFields });
+ Cards.updateAsync(this._id, { $set: mutatedFields });
if (Meteor.isServer && Meteor.userId() && typeof UserPositionHistory !== 'undefined') {
try {
@@ -2144,7 +2152,7 @@ Cards.helpers({
if (Object.keys(updateMeta).length > 0) {
try {
- await Attachments.collection.updateAsync(
+ Attachments.collection.updateAsync(
{ 'meta.cardId': this._id },
{ $set: updateMeta },
{ multi: true },
@@ -2156,147 +2164,147 @@ Cards.helpers({
}
},
- async addLabel(labelId) {
+ addLabel(labelId) {
this.labelIds.push(labelId);
- return await Cards.updateAsync(this._id, { $addToSet: { labelIds: labelId } });
+ return Cards.updateAsync(this._id, { $addToSet: { labelIds: labelId } });
},
- async removeLabel(labelId) {
+ removeLabel(labelId) {
this.labelIds = _.without(this.labelIds, labelId);
- return await Cards.updateAsync(this._id, { $pull: { labelIds: labelId } });
+ return Cards.updateAsync(this._id, { $pull: { labelIds: labelId } });
},
- async toggleLabel(labelId) {
+ toggleLabel(labelId) {
if (this.labelIds && this.labelIds.indexOf(labelId) > -1) {
- return await this.removeLabel(labelId);
+ return this.removeLabel(labelId);
} else {
- return await this.addLabel(labelId);
+ return this.addLabel(labelId);
}
},
- async setColor(newColor) {
+ setColor(newColor) {
if (newColor === 'white') {
newColor = null;
}
- return await Cards.updateAsync(this._id, { $set: { color: newColor } });
+ return Cards.updateAsync(this._id, { $set: { color: newColor } });
},
- async assignMember(memberId) {
- return await Cards.updateAsync(this._id, { $addToSet: { members: memberId } });
+ assignMember(memberId) {
+ return Cards.updateAsync(this._id, { $addToSet: { members: memberId } });
},
- async assignAssignee(assigneeId) {
- return await Cards.updateAsync(this._id, { $addToSet: { assignees: assigneeId } });
+ assignAssignee(assigneeId) {
+ return Cards.updateAsync(this._id, { $addToSet: { assignees: assigneeId } });
},
- async unassignMember(memberId) {
- return await Cards.updateAsync(this._id, { $pull: { members: memberId } });
+ unassignMember(memberId) {
+ return Cards.updateAsync(this._id, { $pull: { members: memberId } });
},
- async unassignAssignee(assigneeId) {
- return await Cards.updateAsync(this._id, { $pull: { assignees: assigneeId } });
+ unassignAssignee(assigneeId) {
+ return Cards.updateAsync(this._id, { $pull: { assignees: assigneeId } });
},
- async toggleMember(memberId) {
+ toggleMember(memberId) {
if (this.members && this.members.indexOf(memberId) > -1) {
- return await this.unassignMember(memberId);
+ return this.unassignMember(memberId);
} else {
- return await this.assignMember(memberId);
+ return this.assignMember(memberId);
}
},
- async toggleAssignee(assigneeId) {
+ toggleAssignee(assigneeId) {
if (this.assignees && this.assignees.indexOf(assigneeId) > -1) {
- return await this.unassignAssignee(assigneeId);
+ return this.unassignAssignee(assigneeId);
} else {
- return await this.assignAssignee(assigneeId);
+ return this.assignAssignee(assigneeId);
}
},
- async assignCustomField(customFieldId) {
- return await Cards.updateAsync(this._id, {
+ assignCustomField(customFieldId) {
+ return Cards.updateAsync(this._id, {
$addToSet: { customFields: { _id: customFieldId, value: null } },
});
},
- async unassignCustomField(customFieldId) {
- return await Cards.updateAsync(this._id, {
+ unassignCustomField(customFieldId) {
+ return Cards.updateAsync(this._id, {
$pull: { customFields: { _id: customFieldId } },
});
},
- async toggleCustomField(customFieldId) {
+ toggleCustomField(customFieldId) {
if (this.customFields && this.customFieldIndex(customFieldId) > -1) {
- return await this.unassignCustomField(customFieldId);
+ return this.unassignCustomField(customFieldId);
} else {
- return await this.assignCustomField(customFieldId);
+ return this.assignCustomField(customFieldId);
}
},
- async toggleShowActivities() {
- return await Cards.updateAsync(this._id, {
+ toggleShowActivities() {
+ return Cards.updateAsync(this._id, {
$set: { showActivities: !this.showActivities },
});
},
- async toggleShowChecklistAtMinicard() {
- return await Cards.updateAsync(this._id, {
+ toggleShowChecklistAtMinicard() {
+ return Cards.updateAsync(this._id, {
$set: { showChecklistAtMinicard: !this.showChecklistAtMinicard },
});
},
- async setCustomField(customFieldId, value) {
+ setCustomField(customFieldId, value) {
const index = this.customFieldIndex(customFieldId);
if (index > -1) {
const update = { $set: {} };
update.$set[`customFields.${index}.value`] = value;
- return await Cards.updateAsync(this._id, update);
+ return Cards.updateAsync(this._id, update);
}
return null;
},
- async setCover(coverId) {
- return await Cards.updateAsync(this._id, { $set: { coverId } });
+ setCover(coverId) {
+ return Cards.updateAsync(this._id, { $set: { coverId } });
},
- async unsetCover() {
- return await Cards.updateAsync(this._id, { $unset: { coverId: '' } });
+ unsetCover() {
+ return Cards.updateAsync(this._id, { $unset: { coverId: '' } });
},
- async unsetReceived() {
- return await Cards.updateAsync(this._id, { $unset: { receivedAt: '' } });
+ unsetReceived() {
+ return Cards.updateAsync(this._id, { $unset: { receivedAt: '' } });
},
- async unsetStart() {
- return await Cards.updateAsync(this._id, { $unset: { startAt: '' } });
+ unsetStart() {
+ return Cards.updateAsync(this._id, { $unset: { startAt: '' } });
},
- async unsetDue() {
- return await Cards.updateAsync(this._id, { $unset: { dueAt: '' } });
+ unsetDue() {
+ return Cards.updateAsync(this._id, { $unset: { dueAt: '' } });
},
- async unsetEnd() {
- return await Cards.updateAsync(this._id, { $unset: { endAt: '' } });
+ unsetEnd() {
+ return Cards.updateAsync(this._id, { $unset: { endAt: '' } });
},
- async setOvertime(isOvertime) {
- return await Cards.updateAsync(this._id, { $set: { isOvertime } });
+ setOvertime(isOvertime) {
+ return Cards.updateAsync(this._id, { $set: { isOvertime } });
},
- async setSpentTime(spentTime) {
- return await Cards.updateAsync(this._id, { $set: { spentTime } });
+ setSpentTime(spentTime) {
+ return Cards.updateAsync(this._id, { $set: { spentTime } });
},
- async unsetSpentTime() {
- return await Cards.updateAsync(this._id, { $unset: { spentTime: '', isOvertime: false } });
+ unsetSpentTime() {
+ return Cards.updateAsync(this._id, { $unset: { spentTime: '', isOvertime: false } });
},
- async setParentId(parentId) {
- return await Cards.updateAsync(this._id, { $set: { parentId } });
+ setParentId(parentId) {
+ return Cards.updateAsync(this._id, { $set: { parentId } });
},
- async setVoteQuestion(question, publicVote, allowNonBoardMembers) {
- return await Cards.updateAsync(this._id, {
+ setVoteQuestion(question, publicVote, allowNonBoardMembers) {
+ return Cards.updateAsync(this._id, {
$set: {
vote: {
question,
@@ -2309,39 +2317,39 @@ Cards.helpers({
});
},
- async unsetVote() {
- return await Cards.updateAsync(this._id, { $unset: { vote: '' } });
+ unsetVote() {
+ return Cards.updateAsync(this._id, { $unset: { vote: '' } });
},
- async setVoteEnd(end) {
- return await Cards.updateAsync(this._id, { $set: { 'vote.end': end } });
+ setVoteEnd(end) {
+ return Cards.updateAsync(this._id, { $set: { 'vote.end': end } });
},
- async unsetVoteEnd() {
- return await Cards.updateAsync(this._id, { $unset: { 'vote.end': '' } });
+ unsetVoteEnd() {
+ return Cards.updateAsync(this._id, { $unset: { 'vote.end': '' } });
},
- async setVote(userId, forIt) {
+ setVote(userId, forIt) {
switch (forIt) {
case true:
- return await Cards.updateAsync(this._id, {
+ return Cards.updateAsync(this._id, {
$pull: { 'vote.negative': userId },
$addToSet: { 'vote.positive': userId },
});
case false:
- return await Cards.updateAsync(this._id, {
+ return Cards.updateAsync(this._id, {
$pull: { 'vote.positive': userId },
$addToSet: { 'vote.negative': userId },
});
default:
- return await Cards.updateAsync(this._id, {
+ return Cards.updateAsync(this._id, {
$pull: { 'vote.positive': userId, 'vote.negative': userId },
});
}
},
- async setPokerQuestion(question, allowNonBoardMembers) {
- return await Cards.updateAsync(this._id, {
+ setPokerQuestion(question, allowNonBoardMembers) {
+ return Cards.updateAsync(this._id, {
$set: {
poker: {
question,
@@ -2361,44 +2369,44 @@ Cards.helpers({
});
},
- async setPokerEstimation(estimation) {
- return await Cards.updateAsync(this._id, { $set: { 'poker.estimation': estimation } });
+ setPokerEstimation(estimation) {
+ return Cards.updateAsync(this._id, { $set: { 'poker.estimation': estimation } });
},
- async unsetPokerEstimation() {
- return await Cards.updateAsync(this._id, { $unset: { 'poker.estimation': '' } });
+ unsetPokerEstimation() {
+ return Cards.updateAsync(this._id, { $unset: { 'poker.estimation': '' } });
},
- async unsetPoker() {
- return await Cards.updateAsync(this._id, { $unset: { poker: '' } });
+ unsetPoker() {
+ return Cards.updateAsync(this._id, { $unset: { poker: '' } });
},
- async setPokerEnd(end) {
- return await Cards.updateAsync(this._id, { $set: { 'poker.end': end } });
+ setPokerEnd(end) {
+ return Cards.updateAsync(this._id, { $set: { 'poker.end': end } });
},
- async unsetPokerEnd() {
- return await Cards.updateAsync(this._id, { $unset: { 'poker.end': '' } });
+ unsetPokerEnd() {
+ return Cards.updateAsync(this._id, { $unset: { 'poker.end': '' } });
},
- async setPoker(userId, state) {
+ setPoker(userId, state) {
const pokerFields = ['one', 'two', 'three', 'five', 'eight', 'thirteen', 'twenty', 'forty', 'oneHundred', 'unsure'];
const pullFields = {};
pokerFields.forEach(f => { pullFields[`poker.${f}`] = userId; });
if (pokerFields.includes(state)) {
delete pullFields[`poker.${state}`];
- return await Cards.updateAsync(this._id, {
+ return Cards.updateAsync(this._id, {
$pull: pullFields,
$addToSet: { [`poker.${state}`]: userId },
});
} else {
- return await Cards.updateAsync(this._id, { $pull: pullFields });
+ return Cards.updateAsync(this._id, { $pull: pullFields });
}
},
- async replayPoker() {
- return await Cards.updateAsync(this._id, {
+ replayPoker() {
+ return Cards.updateAsync(this._id, {
$set: {
'poker.one': [],
'poker.two': [],
@@ -2417,33 +2425,34 @@ Cards.helpers({
//FUNCTIONS FOR creation of Activities
-function updateActivities(doc, fieldNames, modifier) {
+async function updateActivities(doc, fieldNames, modifier) {
if (_.contains(fieldNames, 'labelIds') && _.contains(fieldNames, 'boardId')) {
- ReactiveCache.getActivities({
+ const activities = await ReactiveCache.getActivities({
activityType: 'addedLabel',
cardId: doc._id,
- }).forEach(a => {
+ });
+ for (const a of activities) {
const lidx = doc.labelIds.indexOf(a.labelId);
if (lidx !== -1 && modifier.$set.labelIds.length > lidx) {
- Activities.update(a._id, {
+ await Activities.updateAsync(a._id, {
$set: {
labelId: modifier.$set.labelIds[doc.labelIds.indexOf(a.labelId)],
boardId: modifier.$set.boardId,
},
});
} else {
- Activities.remove(a._id);
+ await Activities.removeAsync(a._id);
}
- });
+ }
} else if (_.contains(fieldNames, 'boardId')) {
- Activities.remove({
+ await Activities.removeAsync({
activityType: 'addedLabel',
cardId: doc._id,
});
}
}
-function cardMove(
+async function cardMove(
userId,
doc,
fieldNames,
@@ -2452,15 +2461,18 @@ function cardMove(
oldBoardId,
) {
if (_.contains(fieldNames, 'boardId') && doc.boardId !== oldBoardId) {
- Activities.insert({
+ const newBoard = await ReactiveCache.getBoard(doc.boardId);
+ const oldBoard = await ReactiveCache.getBoard(oldBoardId);
+ const swimlane = await ReactiveCache.getSwimlane(doc.swimlaneId);
+ await Activities.insertAsync({
userId,
activityType: 'moveCardBoard',
- boardName: ReactiveCache.getBoard(doc.boardId).title,
+ boardName: newBoard.title,
boardId: doc.boardId,
oldBoardId,
- oldBoardName: ReactiveCache.getBoard(oldBoardId).title,
+ oldBoardName: oldBoard.title,
cardId: doc._id,
- swimlaneName: ReactiveCache.getSwimlane(doc.swimlaneId).title,
+ swimlaneName: swimlane.title,
swimlaneId: doc.swimlaneId,
oldSwimlaneId,
});
@@ -2468,40 +2480,43 @@ function cardMove(
(_.contains(fieldNames, 'listId') && doc.listId !== oldListId) ||
(_.contains(fieldNames, 'swimlaneId') && doc.swimlaneId !== oldSwimlaneId)
) {
- Activities.insert({
+ const list = await ReactiveCache.getList(doc.listId);
+ const swimlane = await ReactiveCache.getSwimlane(doc.swimlaneId);
+ await Activities.insertAsync({
userId,
oldListId,
activityType: 'moveCard',
- listName: ReactiveCache.getList(doc.listId).title,
+ listName: list.title,
listId: doc.listId,
boardId: doc.boardId,
cardId: doc._id,
cardTitle: doc.title,
- swimlaneName: ReactiveCache.getSwimlane(doc.swimlaneId).title,
+ swimlaneName: swimlane.title,
swimlaneId: doc.swimlaneId,
oldSwimlaneId,
});
}
}
-function cardState(userId, doc, fieldNames) {
+async function cardState(userId, doc, fieldNames) {
if (_.contains(fieldNames, 'archived')) {
+ const list = await ReactiveCache.getList(doc.listId);
if (doc.archived) {
- Activities.insert({
+ await Activities.insertAsync({
userId,
activityType: 'archivedCard',
- listName: ReactiveCache.getList(doc.listId).title,
+ listName: list.title,
boardId: doc.boardId,
listId: doc.listId,
cardId: doc._id,
swimlaneId: doc.swimlaneId,
});
} else {
- Activities.insert({
+ await Activities.insertAsync({
userId,
activityType: 'restoredCard',
boardId: doc.boardId,
- listName: ReactiveCache.getList(doc.listId).title,
+ listName: list.title,
listId: doc.listId,
cardId: doc._id,
swimlaneId: doc.swimlaneId,
@@ -2510,15 +2525,16 @@ function cardState(userId, doc, fieldNames) {
}
}
-function cardMembers(userId, doc, fieldNames, modifier) {
+async function cardMembers(userId, doc, fieldNames, modifier) {
if (!_.contains(fieldNames, 'members')) return;
let memberId;
// Say hello to the new member
if (modifier.$addToSet && modifier.$addToSet.members) {
memberId = modifier.$addToSet.members;
- const username = ReactiveCache.getUser(memberId).username;
+ const user = await ReactiveCache.getUser(memberId);
+ const username = user.username;
if (!_.contains(doc.members, memberId)) {
- Activities.insert({
+ await Activities.insertAsync({
userId,
username,
activityType: 'joinMember',
@@ -2534,10 +2550,11 @@ function cardMembers(userId, doc, fieldNames, modifier) {
// Say goodbye to the former member
if (modifier.$pull && modifier.$pull.members) {
memberId = modifier.$pull.members;
- const username = ReactiveCache.getUser(memberId).username;
+ const user = await ReactiveCache.getUser(memberId);
+ const username = user.username;
// Check that the former member is member of the card
if (_.contains(doc.members, memberId)) {
- Activities.insert({
+ await Activities.insertAsync({
userId,
username,
activityType: 'unjoinMember',
@@ -2551,15 +2568,16 @@ function cardMembers(userId, doc, fieldNames, modifier) {
}
}
-function cardAssignees(userId, doc, fieldNames, modifier) {
+async function cardAssignees(userId, doc, fieldNames, modifier) {
if (!_.contains(fieldNames, 'assignees')) return;
let assigneeId;
// Say hello to the new assignee
if (modifier.$addToSet && modifier.$addToSet.assignees) {
assigneeId = modifier.$addToSet.assignees;
- const username = ReactiveCache.getUser(assigneeId).username;
+ const user = await ReactiveCache.getUser(assigneeId);
+ const username = user.username;
if (!_.contains(doc.assignees, assigneeId)) {
- Activities.insert({
+ await Activities.insertAsync({
userId,
username,
activityType: 'joinAssignee',
@@ -2574,10 +2592,11 @@ function cardAssignees(userId, doc, fieldNames, modifier) {
// Say goodbye to the former assignee
if (modifier.$pull && modifier.$pull.assignees) {
assigneeId = modifier.$pull.assignees;
- const username = ReactiveCache.getUser(assigneeId).username;
+ const user = await ReactiveCache.getUser(assigneeId);
+ const username = user.username;
// Check that the former assignee is assignee of the card
if (_.contains(doc.assignees, assigneeId)) {
- Activities.insert({
+ await Activities.insertAsync({
userId,
username,
activityType: 'unjoinAssignee',
@@ -2681,16 +2700,18 @@ function cardCustomFields(userId, doc, fieldNames, modifier) {
}
}
-function cardCreation(userId, doc) {
- Activities.insert({
+async function cardCreation(userId, doc) {
+ const list = await ReactiveCache.getList(doc.listId);
+ const swimlane = await ReactiveCache.getSwimlane(doc.swimlaneId);
+ await Activities.insertAsync({
userId,
activityType: 'createCard',
boardId: doc.boardId,
- listName: ReactiveCache.getList(doc.listId).title,
+ listName: list.title,
listId: doc.listId,
cardId: doc._id,
cardTitle: doc.title,
- swimlaneName: ReactiveCache.getSwimlane(doc.swimlaneId).title,
+ swimlaneName: swimlane.title,
swimlaneId: doc.swimlaneId,
});
}
@@ -2735,13 +2756,15 @@ function cardRemover(userId, doc) {
});
}
-const findDueCards = days => {
- const seekDue = ($from, $to, activityType) => {
- ReactiveCache.getCards({
+const findDueCards = async days => {
+ const seekDue = async ($from, $to, activityType) => {
+ const cards = await ReactiveCache.getCards({
archived: false,
dueAt: { $gte: $from, $lt: $to },
- }).forEach(card => {
- const username = ReactiveCache.getUser(card.userId).username;
+ });
+ for (const card of cards) {
+ const user = await ReactiveCache.getUser(card.userId);
+ const username = user.username;
const activity = {
userId: card.userId,
username,
@@ -2753,15 +2776,15 @@ const findDueCards = days => {
timeValue: card.dueAt,
swimlaneId: card.swimlaneId,
};
- Activities.insert(activity);
- });
+ await Activities.insertAsync(activity);
+ }
};
const now = new Date(),
aday = 3600 * 24 * 1e3,
then = day => new Date(now.setHours(0, 0, 0, 0) + day * aday);
if (!days) return;
if (!days.map) days = [days];
- days.map(day => {
+ for (const day of days) {
let args = [];
if (day === 0) {
args = [then(0), then(1), 'duenow'];
@@ -2770,8 +2793,8 @@ const findDueCards = days => {
} else {
args = [then(day), now, 'pastdue'];
}
- seekDue(...args);
- });
+ await seekDue(...args);
+ }
};
const addCronJob = _.debounce(
Meteor.bindEnvironment(function findDueCardsDebounced() {
@@ -2814,14 +2837,14 @@ const addCronJob = _.debounce(
if (Meteor.isServer) {
Meteor.methods({
// Secure poker voting: only the caller's userId is modified
- 'cards.pokerVote'(cardId, state) {
+ async 'cards.pokerVote'(cardId, state) {
check(cardId, String);
if (state !== undefined && state !== null) check(state, String);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!board) throw new Meteor.Error('not-found');
const isMember = allowIsBoardMember(this.userId, board);
@@ -2833,19 +2856,19 @@ if (Meteor.isServer) {
let mod = card.setPoker(this.userId, state);
if (!mod || typeof mod !== 'object') mod = {};
mod.$set = Object.assign({}, mod.$set, { modifiedAt: new Date(), dateLastActivity: new Date() });
- return Cards.update({ _id: cardId }, mod);
+ return await Cards.updateAsync({ _id: cardId }, mod);
},
// Configure planning poker on a card (members only)
- 'cards.setPokerQuestion'(cardId, question, allowNonBoardMembers) {
+ async 'cards.setPokerQuestion'(cardId, question, allowNonBoardMembers) {
check(cardId, String);
check(question, Boolean);
check(allowNonBoardMembers, Boolean);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
const modifier = {
@@ -2859,96 +2882,96 @@ if (Meteor.isServer) {
dateLastActivity: new Date(),
},
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
- 'cards.setPokerEnd'(cardId, end) {
+ async 'cards.setPokerEnd'(cardId, end) {
check(cardId, String);
check(end, Date);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
const modifier = {
$set: { 'poker.end': end, modifiedAt: new Date(), dateLastActivity: new Date() },
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
- 'cards.unsetPokerEnd'(cardId) {
+ async 'cards.unsetPokerEnd'(cardId) {
check(cardId, String);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
const modifier = {
$unset: { 'poker.end': '' },
$set: { modifiedAt: new Date(), dateLastActivity: new Date() },
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
- 'cards.unsetPoker'(cardId) {
+ async 'cards.unsetPoker'(cardId) {
check(cardId, String);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
const modifier = {
$unset: { poker: '' },
$set: { modifiedAt: new Date(), dateLastActivity: new Date() },
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
- 'cards.setPokerEstimation'(cardId, estimation) {
+ async 'cards.setPokerEstimation'(cardId, estimation) {
check(cardId, String);
check(estimation, Number);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
const modifier = {
$set: { 'poker.estimation': estimation, modifiedAt: new Date(), dateLastActivity: new Date() },
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
- 'cards.unsetPokerEstimation'(cardId) {
+ async 'cards.unsetPokerEstimation'(cardId) {
check(cardId, String);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
const modifier = {
$unset: { 'poker.estimation': '' },
$set: { modifiedAt: new Date(), dateLastActivity: new Date() },
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
- 'cards.replayPoker'(cardId) {
+ async 'cards.replayPoker'(cardId) {
check(cardId, String);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
// Reset all poker votes arrays
@@ -2960,19 +2983,19 @@ if (Meteor.isServer) {
},
$unset: { 'poker.end': '' },
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
// Configure voting on a card (members only)
- 'cards.setVoteQuestion'(cardId, question, publicVote, allowNonBoardMembers) {
+ async 'cards.setVoteQuestion'(cardId, question, publicVote, allowNonBoardMembers) {
check(cardId, String);
check(question, String);
check(publicVote, Boolean);
check(allowNonBoardMembers, Boolean);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
const modifier = {
@@ -2988,66 +3011,66 @@ if (Meteor.isServer) {
dateLastActivity: new Date(),
},
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
- 'cards.setVoteEnd'(cardId, end) {
+ async 'cards.setVoteEnd'(cardId, end) {
check(cardId, String);
check(end, Date);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
const modifier = {
$set: { 'vote.end': end, modifiedAt: new Date(), dateLastActivity: new Date() },
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
- 'cards.unsetVoteEnd'(cardId) {
+ async 'cards.unsetVoteEnd'(cardId) {
check(cardId, String);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
const modifier = {
$unset: { 'vote.end': '' },
$set: { modifiedAt: new Date(), dateLastActivity: new Date() },
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
- 'cards.unsetVote'(cardId) {
+ async 'cards.unsetVote'(cardId) {
check(cardId, String);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!allowIsBoardMember(this.userId, board)) throw new Meteor.Error('not-authorized');
const modifier = {
$unset: { vote: '' },
$set: { modifiedAt: new Date(), dateLastActivity: new Date() },
};
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
// Secure voting: only the caller can set/unset their vote; non-members can vote only when allowed
- 'cards.vote'(cardId, forIt) {
+ async 'cards.vote'(cardId, forIt) {
check(cardId, String);
// forIt may be true (upvote), false (downvote), or null/undefined (clear)
if (forIt !== undefined && forIt !== null) check(forIt, Boolean);
if (!this.userId) throw new Meteor.Error('not-authorized');
- const card = ReactiveCache.getCard(cardId) || Cards.findOne(cardId);
+ const card = await ReactiveCache.getCard(cardId) || await Cards.findOneAsync(cardId);
if (!card) throw new Meteor.Error('not-found');
- const board = ReactiveCache.getBoard(card.boardId) || Boards.findOne(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId) || await Boards.findOneAsync(card.boardId);
if (!board) throw new Meteor.Error('not-found');
const isMember = allowIsBoardMember(this.userId, board);
@@ -3078,7 +3101,7 @@ if (Meteor.isServer) {
};
}
- return Cards.update({ _id: cardId }, modifier);
+ return await Cards.updateAsync({ _id: cardId }, modifier);
},
/** copies a card
* this method is needed on the server because attachments can only be copied on the server (access to file system)
@@ -3098,10 +3121,10 @@ if (Meteor.isServer) {
check(insertAtTop, Boolean);
check(mergeCardValues, Object);
- const card = ReactiveCache.getCard(cardId);
+ const card = await ReactiveCache.getCard(cardId);
Object.assign(card, mergeCardValues);
- const sort = card.getSort(listId, swimlaneId, insertAtTop);
+ const sort = await card.getSort(listId, swimlaneId, insertAtTop);
if (insertAtTop) {
card.sort = sort - 1;
} else
@@ -3132,40 +3155,40 @@ if (Meteor.isServer) {
});
});
- Cards.after.insert((userId, doc) => {
- cardCreation(userId, doc);
+ Cards.after.insert(async (userId, doc) => {
+ await cardCreation(userId, doc);
// Track original position for new cards
- Meteor.setTimeout(() => {
- const card = Cards.findOne(doc._id);
+ Meteor.setTimeout(async () => {
+ const card = await Cards.findOneAsync(doc._id);
if (card) {
card.trackOriginalPosition();
}
}, 100);
});
// New activity for card (un)archivage
- Cards.after.update((userId, doc, fieldNames) => {
- cardState(userId, doc, fieldNames);
+ Cards.after.update(async (userId, doc, fieldNames) => {
+ await cardState(userId, doc, fieldNames);
});
//New activity for card moves
- Cards.after.update(function(userId, doc, fieldNames) {
+ Cards.after.update(async function(userId, doc, fieldNames) {
const oldListId = this.previous.listId;
const oldSwimlaneId = this.previous.swimlaneId;
const oldBoardId = this.previous.boardId;
- cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId, oldBoardId);
+ await cardMove(userId, doc, fieldNames, oldListId, oldSwimlaneId, oldBoardId);
});
// Add a new activity if we add or remove a member to the card
- Cards.before.update((userId, doc, fieldNames, modifier) => {
- cardMembers(userId, doc, fieldNames, modifier);
- updateActivities(doc, fieldNames, modifier);
+ Cards.before.update(async (userId, doc, fieldNames, modifier) => {
+ await cardMembers(userId, doc, fieldNames, modifier);
+ await updateActivities(doc, fieldNames, modifier);
});
// Add a new activity if we add or remove a assignee to the card
- Cards.before.update((userId, doc, fieldNames, modifier) => {
- cardAssignees(userId, doc, fieldNames, modifier);
- updateActivities(doc, fieldNames, modifier);
+ Cards.before.update(async (userId, doc, fieldNames, modifier) => {
+ await cardAssignees(userId, doc, fieldNames, modifier);
+ await updateActivities(doc, fieldNames, modifier);
});
// Add a new activity if we add or remove a label to the card
@@ -3179,7 +3202,7 @@ if (Meteor.isServer) {
});
// Add a new activity if modify time related field like dueAt startAt etc
- Cards.before.update((userId, doc, fieldNames, modifier) => {
+ Cards.before.update(async (userId, doc, fieldNames, modifier) => {
const dla = 'dateLastActivity';
const fields = fieldNames.filter(name => name !== dla);
const timingaction = ['receivedAt', 'dueAt', 'startAt', 'endAt'];
@@ -3189,15 +3212,15 @@ if (Meteor.isServer) {
const value = modifier.$set[action];
const oldvalue = doc[action] || '';
const activityType = `a-${action}`;
- const card = ReactiveCache.getCard(doc._id);
- const list = card.list();
+ const card = await ReactiveCache.getCard(doc._id);
+ const list = await card.list();
if (list) {
// change list modifiedAt, when user modified the key values in
// timingaction array, if it's endAt, put the modifiedAt of list
// back to one year ago for sorting purpose
const modifiedAt = add(now(), -1, 'year').toISOString();
const boardId = list.boardId;
- Lists.direct.update(
+ await Lists.direct.updateAsync(
{
_id: list._id,
},
@@ -3209,7 +3232,8 @@ if (Meteor.isServer) {
},
);
}
- const username = ReactiveCache.getUser(userId).username;
+ const user = await ReactiveCache.getUser(userId);
+ const username = user.username;
const activity = {
userId,
username,
@@ -3223,7 +3247,7 @@ if (Meteor.isServer) {
listId: card.listId,
swimlaneId: card.swimlaneId,
};
- Activities.insert(activity);
+ await Activities.insertAsync(activity);
}
});
// Remove all activities associated with a card if we remove the card
@@ -3248,18 +3272,19 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/swimlanes/:swimlaneId/cards',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramSwimlaneId = req.params.swimlaneId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
+ const cards = await ReactiveCache.getCards({
+ boardId: paramBoardId,
+ swimlaneId: paramSwimlaneId,
+ archived: false,
+ },
+ { sort: ['sort'] });
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getCards({
- boardId: paramBoardId,
- swimlaneId: paramSwimlaneId,
- archived: false,
- },
- { sort: ['sort'] }).map(function(doc) {
+ data: cards.map(function(doc) {
return {
_id: doc._id,
title: doc.title,
@@ -3289,21 +3314,22 @@ if (Meteor.isServer) {
* title: string,
* description: string}]
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards', function(
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards', async function(
req,
res,
) {
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
+ const cards = await ReactiveCache.getCards({
+ boardId: paramBoardId,
+ listId: paramListId,
+ archived: false,
+ },
+ { sort: ['sort'] });
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getCards({
- boardId: paramBoardId,
- listId: paramListId,
- archived: false,
- },
- { sort: ['sort'] }).map(function(doc) {
+ data: cards.map(function(doc) {
return {
_id: doc._id,
title: doc.title,
@@ -3330,9 +3356,9 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/cards/:cardId',
- function(req, res) {
+ async function(req, res) {
const paramCardId = req.params.cardId;
- card = ReactiveCache.getCard(paramCardId)
+ const card = await ReactiveCache.getCard(paramCardId);
Authentication.checkBoardAccess(req.userId, card.boardId);
JsonRoutes.sendResult(res, {
code: 200,
@@ -3353,14 +3379,14 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/lists/:listId/cards/:cardId',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
const paramCardId = req.params.cardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getCard({
+ data: await ReactiveCache.getCard({
_id: paramCardId,
listId: paramListId,
boardId: paramBoardId,
@@ -3385,7 +3411,7 @@ if (Meteor.isServer) {
* @param {string} [assignees] the assignee IDs list of the new card
* @return_type {_id: string}
*/
- JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', function(
+ JsonRoutes.add('POST', '/api/boards/:boardId/lists/:listId/cards', async function(
req,
res,
) {
@@ -3393,7 +3419,7 @@ if (Meteor.isServer) {
Authentication.checkLoggedIn(req.userId);
const paramBoardId = req.params.boardId;
// Check user has permission to add card to the board
- const board = ReactiveCache.getBoard(paramBoardId);
+ const board = await ReactiveCache.getBoard(paramBoardId);
const addPermission = allowIsBoardMemberCommentOnly(req.userId, board);
Authentication.checkAdminOrCondition(req.userId, addPermission);
const paramListId = req.params.listId;
@@ -3401,26 +3427,27 @@ if (Meteor.isServer) {
const nextCardNumber = board.getNextCardNumber();
let customFieldsArr = [];
+ const customFields = await ReactiveCache.getCustomFields({'boardIds': paramBoardId});
_.forEach(
- ReactiveCache.getCustomFields({'boardIds': paramBoardId}),
+ customFields,
function (field) {
if (field.automaticallyOnCard || field.alwaysOnCard)
customFieldsArr.push({ _id: field._id, value: null });
},
);
- const currentCards = ReactiveCache.getCards(
+ const currentCards = await ReactiveCache.getCards(
{
listId: paramListId,
archived: false,
},
{ sort: ['sort'] },
);
- const check = ReactiveCache.getUser(req.body.authorId);
+ const checkUser = await ReactiveCache.getUser(req.body.authorId);
const members = req.body.members;
const assignees = req.body.assignees;
- if (typeof check !== 'undefined') {
- const id = Cards.direct.insert({
+ if (typeof checkUser !== 'undefined') {
+ const id = await Cards.direct.insertAsync({
title: req.body.title,
boardId: paramBoardId,
listId: paramListId,
@@ -3441,8 +3468,8 @@ if (Meteor.isServer) {
},
});
- const card = ReactiveCache.getCard(id);
- cardCreation(req.body.authorId, card);
+ const card = await ReactiveCache.getCard(id);
+ await cardCreation(req.body.authorId, card);
} else {
JsonRoutes.sendResult(res, {
code: 401,
@@ -3457,20 +3484,21 @@ if (Meteor.isServer) {
* @param {string} boardId the board ID
* @return_type {board_cards_count: integer}
*/
-JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
+JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', async function(
req,
res,
) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
+ const cards = await ReactiveCache.getCards({
+ boardId: paramBoardId,
+ archived: false,
+ });
JsonRoutes.sendResult(res, {
code: 200,
data: {
- board_cards_count: ReactiveCache.getCards({
- boardId: paramBoardId,
- archived: false,
- }).length,
+ board_cards_count: cards.length,
}
});
} catch (error) {
@@ -3489,7 +3517,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
* @param {string} listId the List ID
* @return_type {list_cards_count: integer}
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards_count', function(
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId/cards_count', async function(
req,
res,
) {
@@ -3497,14 +3525,15 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
+ const cards = await ReactiveCache.getCards({
+ boardId: paramBoardId,
+ listId: paramListId,
+ archived: false,
+ });
JsonRoutes.sendResult(res, {
code: 200,
data: {
- list_cards_count: ReactiveCache.getCards({
- boardId: paramBoardId,
- listId: paramListId,
- archived: false,
- }).length,
+ list_cards_count: cards.length,
}
});
} catch (error) {
@@ -3573,7 +3602,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
JsonRoutes.add(
'PUT',
'/api/boards/:boardId/lists/:listId/cards/:cardId',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramListId = req.params.listId;
@@ -3926,8 +3955,8 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
);
updated = true;
- const card = ReactiveCache.getCard(paramCardId);
- cardMove(
+ const card = await ReactiveCache.getCard(paramCardId);
+ await cardMove(
req.body.authorId,
card,
{
@@ -3941,7 +3970,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
Authentication.checkBoardWriteAccess(req.userId, newBoardId);
// Validate that the destination list exists and belongs to the destination board
- const destList = ReactiveCache.getList({
+ const destList = await ReactiveCache.getList({
_id: newListId,
boardId: newBoardId,
archived: false,
@@ -3955,7 +3984,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
}
// Validate that the destination swimlane exists and belongs to the destination board
- const destSwimlane = ReactiveCache.getSwimlane({
+ const destSwimlane = await ReactiveCache.getSwimlane({
_id: newSwimlaneId,
boardId: newBoardId,
archived: false,
@@ -3969,7 +3998,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
}
// Move the card to the new board, swimlane, and list
- Cards.direct.update(
+ await Cards.direct.updateAsync(
{
_id: paramCardId,
listId: paramListId,
@@ -3986,8 +4015,8 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
);
updated = true;
- const card = ReactiveCache.getCard(paramCardId);
- cardMove(
+ const card = await ReactiveCache.getCard(paramCardId);
+ await cardMove(
req.userId,
card,
['boardId', 'swimlaneId', 'listId'],
@@ -4050,13 +4079,13 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
JsonRoutes.add(
'DELETE',
'/api/boards/:boardId/lists/:listId/cards/:cardId',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramListId = req.params.listId;
const paramCardId = req.params.cardId;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
- const card = ReactiveCache.getCard(paramCardId);
+ const card = await ReactiveCache.getCard(paramCardId);
Cards.direct.remove({
_id: paramCardId,
listId: paramListId,
@@ -4088,14 +4117,14 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
JsonRoutes.add(
'GET',
'/api/boards/:boardId/cardsByCustomField/:customFieldId/:customFieldValue',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCustomFieldId = req.params.customFieldId;
const paramCustomFieldValue = req.params.customFieldValue;
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getCards({
+ data: await ReactiveCache.getCards({
boardId: paramBoardId,
customFields: {
$elemMatch: {
@@ -4124,14 +4153,14 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
JsonRoutes.add(
'POST',
'/api/boards/:boardId/lists/:listId/cards/:cardId/customFields/:customFieldId',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramListId = req.params.listId;
const paramCustomFieldId = req.params.customFieldId;
const paramCustomFieldValue = req.body.value;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
- const card = ReactiveCache.getCard({
+ const card = await ReactiveCache.getCard({
_id: paramCardId,
listId: paramListId,
boardId: paramBoardId,
@@ -4187,7 +4216,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
const paramCardId = req.params.cardId;
const paramListId = req.params.listId;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
- const card = ReactiveCache.getCard({
+ const card = await ReactiveCache.getCard({
_id: paramCardId,
listId: paramListId,
boardId: paramBoardId,
@@ -4226,7 +4255,7 @@ JsonRoutes.add('GET', '/api/boards/:boardId/cards_count', function(
const paramCardId = req.params.cardId;
const paramListId = req.params.listId;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
- const card = ReactiveCache.getCard({
+ const card = await ReactiveCache.getCard({
_id: paramCardId,
listId: paramListId,
boardId: paramBoardId,
diff --git a/models/checklistItems.js b/models/checklistItems.js
index 7b55ac9a7..3b7573ee4 100644
--- a/models/checklistItems.js
+++ b/models/checklistItems.js
@@ -71,15 +71,15 @@ ChecklistItems.attachSchema(
ChecklistItems.allow({
insert(userId, doc) {
// ReadOnly users cannot create checklist items
- return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
+ return allowIsBoardMemberWithWriteAccessByCard(userId, Cards.findOne(doc.cardId));
},
update(userId, doc) {
// ReadOnly users cannot edit checklist items
- return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
+ return allowIsBoardMemberWithWriteAccessByCard(userId, Cards.findOne(doc.cardId));
},
remove(userId, doc) {
// ReadOnly users cannot delete checklist items
- return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
+ return allowIsBoardMemberWithWriteAccessByCard(userId, Cards.findOne(doc.cardId));
},
fetch: ['userId', 'cardId'],
});
@@ -104,7 +104,8 @@ ChecklistItems.helpers({
return await ChecklistItems.updateAsync(this._id, { $set: { isFinished: !this.isFinished } });
},
async move(checklistId, sortIndex) {
- const cardId = ReactiveCache.getChecklist(checklistId).cardId;
+ const checklist = await ReactiveCache.getChecklist(checklistId);
+ const cardId = checklist.cardId;
return await ChecklistItems.updateAsync(this._id, {
$set: { cardId, checklistId, sort: sortIndex },
});
@@ -112,8 +113,8 @@ ChecklistItems.helpers({
});
// Activities helper
-function itemCreation(userId, doc) {
- const card = ReactiveCache.getCard(doc.cardId);
+async function itemCreation(userId, doc) {
+ const card = await ReactiveCache.getCard(doc.cardId);
const boardId = card.boardId;
Activities.insert({
userId,
@@ -134,8 +135,8 @@ function itemRemover(userId, doc) {
});
}
-function publishCheckActivity(userId, doc) {
- const card = ReactiveCache.getCard(doc.cardId);
+async function publishCheckActivity(userId, doc) {
+ const card = await ReactiveCache.getCard(doc.cardId);
const boardId = card.boardId;
let activityType;
if (doc.isFinished) {
@@ -157,12 +158,15 @@ function publishCheckActivity(userId, doc) {
Activities.insert(act);
}
-function publishChekListCompleted(userId, doc) {
- const card = ReactiveCache.getCard(doc.cardId);
+async function publishChekListCompleted(userId, doc) {
+ const card = await ReactiveCache.getCard(doc.cardId);
const boardId = card.boardId;
const checklistId = doc.checklistId;
- const checkList = ReactiveCache.getChecklist(checklistId);
- if (checkList.isFinished()) {
+ const checkList = await ReactiveCache.getChecklist(checklistId);
+ const checklistItems = await ReactiveCache.getChecklistItems({ checklistId });
+ const isChecklistFinished = checkList.hideAllChecklistItems ||
+ (checklistItems.length > 0 && checklistItems.length === checklistItems.filter(i => i.isFinished).length);
+ if (isChecklistFinished) {
const act = {
userId,
activityType: 'completeChecklist',
@@ -177,16 +181,16 @@ function publishChekListCompleted(userId, doc) {
}
}
-function publishChekListUncompleted(userId, doc) {
- const card = ReactiveCache.getCard(doc.cardId);
+async function publishChekListUncompleted(userId, doc) {
+ const card = await ReactiveCache.getCard(doc.cardId);
const boardId = card.boardId;
const checklistId = doc.checklistId;
- const checkList = ReactiveCache.getChecklist(checklistId);
+ const checkList = await ReactiveCache.getChecklist(checklistId);
// BUGS in IFTTT Rules: https://github.com/wekan/wekan/issues/1972
// Currently in checklist all are set as uncompleted/not checked,
// IFTTT Rule does not move card to other list.
// If following line is negated/changed to:
- // if(!checkList.isFinished()){
+ // if(!isChecklistFinished){
// then unchecking of any checkbox will move card to other list,
// even when all checkboxes are not yet unchecked.
// What is correct code for only moving when all in list is unchecked?
@@ -195,7 +199,10 @@ function publishChekListUncompleted(userId, doc) {
// find . | xargs grep 'count' -sl | grep -v .meteor | grep -v node_modules | grep -v .build
// Maybe something related here?
// wekan/client/components/rules/triggers/checklistTriggers.js
- if (checkList.isFinished()) {
+ const uncheckItems = await ReactiveCache.getChecklistItems({ checklistId });
+ const isChecklistFinished = checkList.hideAllChecklistItems ||
+ (uncheckItems.length > 0 && uncheckItems.length === uncheckItems.filter(i => i.isFinished).length);
+ if (isChecklistFinished) {
const act = {
userId,
activityType: 'uncompleteChecklist',
@@ -218,22 +225,22 @@ if (Meteor.isServer) {
await ChecklistItems._collection.createIndexAsync({ cardId: 1 });
});
- ChecklistItems.after.update((userId, doc, fieldNames) => {
- publishCheckActivity(userId, doc);
- publishChekListCompleted(userId, doc, fieldNames);
+ ChecklistItems.after.update(async (userId, doc, fieldNames) => {
+ await publishCheckActivity(userId, doc);
+ await publishChekListCompleted(userId, doc, fieldNames);
});
- ChecklistItems.before.update((userId, doc, fieldNames) => {
- publishChekListUncompleted(userId, doc, fieldNames);
+ ChecklistItems.before.update(async (userId, doc, fieldNames) => {
+ await publishChekListUncompleted(userId, doc, fieldNames);
});
- ChecklistItems.after.insert((userId, doc) => {
- itemCreation(userId, doc);
+ ChecklistItems.after.insert(async (userId, doc) => {
+ await itemCreation(userId, doc);
});
- ChecklistItems.before.remove((userId, doc) => {
+ ChecklistItems.before.remove(async (userId, doc) => {
itemRemover(userId, doc);
- const card = ReactiveCache.getCard(doc.cardId);
+ const card = await ReactiveCache.getCard(doc.cardId);
const boardId = card.boardId;
Activities.insert({
userId,
@@ -264,15 +271,15 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramChecklistId = req.params.checklistId;
const paramItemId = req.params.itemId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
- const checklistItem = ReactiveCache.getChecklistItem(paramItemId);
+ const checklistItem = await ReactiveCache.getChecklistItem(paramItemId);
if (checklistItem && checklistItem.cardId === paramCardId && checklistItem.checklistId === paramChecklistId) {
- const card = ReactiveCache.getCard(checklistItem.cardId);
+ const card = await ReactiveCache.getCard(checklistItem.cardId);
if (card && card.boardId === paramBoardId) {
JsonRoutes.sendResult(res, {
code: 200,
@@ -305,17 +312,17 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramChecklistId = req.params.checklistId;
const paramCardId = req.params.cardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
- const checklist = ReactiveCache.getChecklist({
+ const checklist = await ReactiveCache.getChecklist({
_id: paramChecklistId,
cardId: paramCardId,
});
if (checklist) {
- const card = ReactiveCache.getCard(paramCardId);
+ const card = await ReactiveCache.getCard(paramCardId);
if (card && card.boardId === paramBoardId) {
const id = ChecklistItems.insert({
cardId: paramCardId,
@@ -359,21 +366,21 @@ if (Meteor.isServer) {
JsonRoutes.add(
'PUT',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramChecklistId = req.params.checklistId;
const paramItemId = req.params.itemId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
- const checklistItem = ReactiveCache.getChecklistItem(paramItemId);
+ const checklistItem = await ReactiveCache.getChecklistItem(paramItemId);
if (!checklistItem || checklistItem.cardId !== paramCardId || checklistItem.checklistId !== paramChecklistId) {
JsonRoutes.sendResult(res, {
code: 404,
});
return;
}
- const card = ReactiveCache.getCard(checklistItem.cardId);
+ const card = await ReactiveCache.getCard(checklistItem.cardId);
if (!card || card.boardId !== paramBoardId) {
JsonRoutes.sendResult(res, {
code: 404,
@@ -427,21 +434,21 @@ if (Meteor.isServer) {
JsonRoutes.add(
'DELETE',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId/items/:itemId',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramChecklistId = req.params.checklistId;
const paramItemId = req.params.itemId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
- const checklistItem = ReactiveCache.getChecklistItem(paramItemId);
+ const checklistItem = await ReactiveCache.getChecklistItem(paramItemId);
if (!checklistItem || checklistItem.cardId !== paramCardId || checklistItem.checklistId !== paramChecklistId) {
JsonRoutes.sendResult(res, {
code: 404,
});
return;
}
- const card = ReactiveCache.getCard(checklistItem.cardId);
+ const card = await ReactiveCache.getCard(checklistItem.cardId);
if (!card || card.boardId !== paramBoardId) {
JsonRoutes.sendResult(res, {
code: 404,
diff --git a/models/checklists.js b/models/checklists.js
index 216524f2e..77196984c 100644
--- a/models/checklists.js
+++ b/models/checklists.js
@@ -88,19 +88,18 @@ Checklists.attachSchema(
);
Checklists.helpers({
- copy(newCardId) {
+ async copy(newCardId) {
let copyObj = Object.assign({}, this);
delete copyObj._id;
copyObj.cardId = newCardId;
const newChecklistId = Checklists.insert(copyObj);
- ReactiveCache.getChecklistItems({ checklistId: this._id }).forEach(function(
- item,
- ) {
+ const items = await ReactiveCache.getChecklistItems({ checklistId: this._id });
+ for (const item of items) {
item._id = null;
item.checklistId = newChecklistId;
item.cardId = newCardId;
ChecklistItems.insert(item);
- });
+ }
},
itemCount() {
@@ -151,13 +150,13 @@ Checklists.helpers({
return ret;
},
async checkAllItems() {
- const checkItems = ReactiveCache.getChecklistItems({ checklistId: this._id });
+ const checkItems = await ReactiveCache.getChecklistItems({ checklistId: this._id });
for (const item of checkItems) {
await item.check();
}
},
async uncheckAllItems() {
- const checkItems = ReactiveCache.getChecklistItems({ checklistId: this._id });
+ const checkItems = await ReactiveCache.getChecklistItems({ checklistId: this._id });
for (const item of checkItems) {
await item.uncheck();
}
@@ -198,15 +197,15 @@ Checklists.helpers({
Checklists.allow({
insert(userId, doc) {
// ReadOnly users cannot create checklists
- return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
+ return allowIsBoardMemberWithWriteAccessByCard(userId, Cards.findOne(doc.cardId));
},
update(userId, doc) {
// ReadOnly users cannot edit checklists
- return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
+ return allowIsBoardMemberWithWriteAccessByCard(userId, Cards.findOne(doc.cardId));
},
remove(userId, doc) {
// ReadOnly users cannot delete checklists
- return allowIsBoardMemberWithWriteAccessByCard(userId, ReactiveCache.getCard(doc.cardId));
+ return allowIsBoardMemberWithWriteAccessByCard(userId, Cards.findOne(doc.cardId));
},
fetch: ['userId', 'cardId'],
});
@@ -221,22 +220,22 @@ Checklists.before.insert((userId, doc) => {
if (Meteor.isServer) {
Meteor.methods({
- moveChecklist(checklistId, newCardId) {
+ async moveChecklist(checklistId, newCardId) {
check(checklistId, String);
check(newCardId, String);
- const checklist = ReactiveCache.getChecklist(checklistId);
+ const checklist = await ReactiveCache.getChecklist(checklistId);
if (!checklist) {
throw new Meteor.Error('checklist-not-found', 'Checklist not found');
}
- const newCard = ReactiveCache.getCard(newCardId);
+ const newCard = await ReactiveCache.getCard(newCardId);
if (!newCard) {
throw new Meteor.Error('card-not-found', 'Target card not found');
}
// Check permissions on both source and target cards
- const sourceCard = ReactiveCache.getCard(checklist.cardId);
+ const sourceCard = await ReactiveCache.getCard(checklist.cardId);
if (!allowIsBoardMemberByCard(this.userId, sourceCard)) {
throw new Meteor.Error('not-authorized', 'Not authorized to move checklist from source card');
}
@@ -245,22 +244,24 @@ if (Meteor.isServer) {
}
// Update activities
- ReactiveCache.getActivities({ checklistId }).forEach(activity => {
+ const activities = await ReactiveCache.getActivities({ checklistId });
+ for (const activity of activities) {
Activities.update(activity._id, {
$set: {
cardId: newCardId,
},
});
- });
+ }
// Update checklist items
- ReactiveCache.getChecklistItems({ checklistId }).forEach(checklistItem => {
+ const checklistItems = await ReactiveCache.getChecklistItems({ checklistId });
+ for (const checklistItem of checklistItems) {
ChecklistItems.update(checklistItem._id, {
$set: {
cardId: newCardId,
},
});
- });
+ }
// Update the checklist itself
Checklists.update(checklistId, {
@@ -278,8 +279,8 @@ if (Meteor.isServer) {
await Checklists._collection.createIndexAsync({ cardId: 1, createdAt: 1 });
});
- Checklists.after.insert((userId, doc) => {
- const card = ReactiveCache.getCard(doc.cardId);
+ Checklists.after.insert(async (userId, doc) => {
+ const card = await ReactiveCache.getCard(doc.cardId);
Activities.insert({
userId,
activityType: 'addChecklist',
@@ -292,19 +293,19 @@ if (Meteor.isServer) {
});
});
- Checklists.before.remove((userId, doc) => {
- const activities = ReactiveCache.getActivities({ checklistId: doc._id });
- const card = ReactiveCache.getCard(doc.cardId);
+ Checklists.before.remove(async (userId, doc) => {
+ const activities = await ReactiveCache.getActivities({ checklistId: doc._id });
+ const card = await ReactiveCache.getCard(doc.cardId);
if (activities) {
- activities.forEach(activity => {
+ for (const activity of activities) {
Activities.remove(activity._id);
- });
+ }
}
Activities.insert({
userId,
activityType: 'removeChecklist',
cardId: doc.cardId,
- boardId: ReactiveCache.getCard(doc.cardId).boardId,
+ boardId: (await ReactiveCache.getCard(doc.cardId)).boardId,
checklistId: doc._id,
checklistName: doc.title,
listId: card.listId,
@@ -326,13 +327,13 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/cards/:cardId/checklists',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
// Verify the card belongs to the board
- const card = ReactiveCache.getCard({
+ const card = await ReactiveCache.getCard({
_id: paramCardId,
boardId: paramBoardId,
});
@@ -344,7 +345,7 @@ if (Meteor.isServer) {
return;
}
- const checklists = ReactiveCache.getChecklists({ cardId: paramCardId }).map(function(
+ const checklists = (await ReactiveCache.getChecklists({ cardId: paramCardId })).map(function(
doc,
) {
return {
@@ -384,14 +385,14 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramChecklistId = req.params.checklistId;
const paramCardId = req.params.cardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
// Verify the card belongs to the board
- const card = ReactiveCache.getCard({
+ const card = await ReactiveCache.getCard({
_id: paramCardId,
boardId: paramBoardId,
});
@@ -403,14 +404,14 @@ if (Meteor.isServer) {
return;
}
- const checklist = ReactiveCache.getChecklist({
+ const checklist = await ReactiveCache.getChecklist({
_id: paramChecklistId,
cardId: paramCardId,
});
if (checklist) {
- checklist.items = ReactiveCache.getChecklistItems({
+ checklist.items = (await ReactiveCache.getChecklistItems({
checklistId: checklist._id,
- }).map(function(doc) {
+ })).map(function(doc) {
return {
_id: doc._id,
title: doc.title,
@@ -442,19 +443,19 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/cards/:cardId/checklists',
- function(req, res) {
+ async function(req, res) {
// Check user is logged in
//Authentication.checkLoggedIn(req.userId);
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
// Check user has permission to add checklist to the card
- const board = ReactiveCache.getBoard(paramBoardId);
+ const board = await ReactiveCache.getBoard(paramBoardId);
const addPermission = allowIsBoardMemberCommentOnly(req.userId, board);
Authentication.checkAdminOrCondition(req.userId, addPermission);
const paramCardId = req.params.cardId;
// Verify the card belongs to the board
- const card = ReactiveCache.getCard({
+ const card = await ReactiveCache.getCard({
_id: paramCardId,
boardId: paramBoardId,
});
@@ -516,14 +517,14 @@ if (Meteor.isServer) {
JsonRoutes.add(
'DELETE',
'/api/boards/:boardId/cards/:cardId/checklists/:checklistId',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCardId = req.params.cardId;
const paramChecklistId = req.params.checklistId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
// Verify the card belongs to the board
- const card = ReactiveCache.getCard({
+ const card = await ReactiveCache.getCard({
_id: paramCardId,
boardId: paramBoardId,
});
@@ -536,7 +537,7 @@ if (Meteor.isServer) {
}
// Verify the checklist exists and belongs to the card
- const checklist = ReactiveCache.getChecklist({
+ const checklist = await ReactiveCache.getChecklist({
_id: paramChecklistId,
cardId: paramCardId,
});
diff --git a/models/csvCreator.js b/models/csvCreator.js
index fd3ee399a..85428ab9e 100644
--- a/models/csvCreator.js
+++ b/models/csvCreator.js
@@ -247,7 +247,7 @@ export class CsvCreator {
this.swimlane = swimlaneId;
}
- createLists(csvData, boardId) {
+ async createLists(csvData, boardId) {
let numOfCreatedLists = 0;
for (let i = 1; i < csvData.length; i++) {
const listToCreate = {
@@ -256,7 +256,7 @@ export class CsvCreator {
createdAt: this._now(),
};
if (csvData[i][this.fieldIndex.stage]) {
- const existingList = ReactiveCache.getLists({
+ const existingList = await ReactiveCache.getLists({
title: csvData[i][this.fieldIndex.stage],
boardId,
});
@@ -279,7 +279,7 @@ export class CsvCreator {
}
}
- createCards(csvData, boardId) {
+ async createCards(csvData, boardId) {
for (let i = 1; i < csvData.length; i++) {
const cardToCreate = {
archived: false,
@@ -321,7 +321,7 @@ export class CsvCreator {
}
// add the labels
if (csvData[i][this.fieldIndex.labels]) {
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
for (const importedLabel of csvData[i][this.fieldIndex.labels].split(
' ',
)) {
@@ -388,15 +388,15 @@ export class CsvCreator {
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
- const currentBoard = ReactiveCache.getBoard(currentBoardId);
+ const currentBoard = await ReactiveCache.getBoard(currentBoardId);
await currentBoard.archive();
}
this.mapHeadertoCardFieldIndex(board[0]);
const boardId = this.createBoard(board);
- this.createLists(board, boardId);
+ await this.createLists(board, boardId);
this.createSwimlanes(boardId);
this.createCustomFields(boardId);
- this.createCards(board, boardId);
+ await this.createCards(board, boardId);
return boardId;
}
}
diff --git a/models/customFields.js b/models/customFields.js
index 5d1499e5f..f9535d76a 100644
--- a/models/customFields.js
+++ b/models/customFields.js
@@ -167,25 +167,25 @@ CustomFields.allow({
insert(userId, doc) {
return allowIsAnyBoardMember(
userId,
- ReactiveCache.getBoards({
+ Boards.find({
_id: { $in: doc.boardIds },
- }),
+ }).fetch(),
);
},
update(userId, doc) {
return allowIsAnyBoardMember(
userId,
- ReactiveCache.getBoards({
+ Boards.find({
_id: { $in: doc.boardIds },
- }),
+ }).fetch(),
);
},
remove(userId, doc) {
return allowIsAnyBoardMember(
userId,
- ReactiveCache.getBoards({
+ Boards.find({
_id: { $in: doc.boardIds },
- }),
+ }).fetch(),
);
},
fetch: ['userId', 'boardIds'],
@@ -214,9 +214,9 @@ function customFieldDeletion(userId, doc) {
// This has some bug, it does not show edited customField value at Outgoing Webhook,
// instead it shows undefined, and no listId and swimlaneId.
-function customFieldEdit(userId, doc) {
- const card = ReactiveCache.getCard(doc.cardId);
- const customFieldValue = ReactiveCache.getActivity({ customFieldId: doc._id }).value;
+async function customFieldEdit(userId, doc) {
+ const card = await ReactiveCache.getCard(doc.cardId);
+ const customFieldValue = (await ReactiveCache.getActivity({ customFieldId: doc._id })).value;
Activities.insert({
userId,
activityType: 'setCustomField',
@@ -242,14 +242,14 @@ if (Meteor.isServer) {
}
});
- CustomFields.before.update((userId, doc, fieldNames, modifier) => {
+ CustomFields.before.update(async (userId, doc, fieldNames, modifier) => {
if (_.contains(fieldNames, 'boardIds') && modifier.$pull) {
Cards.update(
{ boardId: modifier.$pull.boardIds, 'customFields._id': doc._id },
{ $pull: { customFields: { _id: doc._id } } },
{ multi: true },
);
- customFieldEdit(userId, doc);
+ await customFieldEdit(userId, doc);
Activities.remove({
customFieldId: doc._id,
boardId: modifier.$pull.boardIds,
@@ -296,7 +296,7 @@ if (Meteor.isServer) {
* name: string,
* type: string}]
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', function(
+ JsonRoutes.add('GET', '/api/boards/:boardId/custom-fields', async function(
req,
res,
) {
@@ -304,7 +304,7 @@ if (Meteor.isServer) {
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getCustomFields({ boardIds: { $in: [paramBoardId] } }).map(
+ data: (await ReactiveCache.getCustomFields({ boardIds: { $in: [paramBoardId] } })).map(
function(cf) {
return {
_id: cf._id,
@@ -328,13 +328,13 @@ if (Meteor.isServer) {
JsonRoutes.add(
'GET',
'/api/boards/:boardId/custom-fields/:customFieldId',
- function(req, res) {
+ async function(req, res) {
const paramBoardId = req.params.boardId;
const paramCustomFieldId = req.params.customFieldId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getCustomField({
+ data: await ReactiveCache.getCustomField({
_id: paramCustomFieldId,
boardIds: { $in: [paramBoardId] },
}),
@@ -356,13 +356,13 @@ if (Meteor.isServer) {
* @param {boolean} showSumAtTopOfList should the sum of the custom fields be shown at top of list?
* @return_type {_id: string}
*/
- JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', function(
+ JsonRoutes.add('POST', '/api/boards/:boardId/custom-fields', async function(
req,
res,
) {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
- const board = ReactiveCache.getBoard(paramBoardId);
+ const board = await ReactiveCache.getBoard(paramBoardId);
const id = CustomFields.direct.insert({
name: req.body.name,
type: req.body.type,
@@ -374,7 +374,7 @@ if (Meteor.isServer) {
boardIds: [board._id],
});
- const customField = ReactiveCache.getCustomField({
+ const customField = await ReactiveCache.getCustomField({
_id: id,
boardIds: { $in: [paramBoardId] },
});
diff --git a/models/export.js b/models/export.js
index a45f8dc77..c72d2ff42 100644
--- a/models/export.js
+++ b/models/export.js
@@ -27,14 +27,14 @@ if (Meteor.isServer) {
* @param {string} boardId the ID of the board we are exporting
* @param {string} authToken the loginToken
*/
- JsonRoutes.add('get', '/api/boards/:boardId/export', function (req, res) {
+ JsonRoutes.add('get', '/api/boards/:boardId/export', async function (req, res) {
const boardId = req.params.boardId;
let user = null;
let impersonateDone = false;
let adminId = null;
// First check if board exists and is public to avoid unnecessary authentication
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
JsonRoutes.sendResult(res, 404);
return;
@@ -46,7 +46,7 @@ if (Meteor.isServer) {
const exporter = new Exporter(boardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: exporter.build(),
+ data: await exporter.build(),
});
return;
}
@@ -64,18 +64,18 @@ if (Meteor.isServer) {
}
const hashToken = Accounts._hashLoginToken(loginToken);
- user = ReactiveCache.getUser({
+ user = await ReactiveCache.getUser({
'services.resume.loginTokens.hashedToken': hashToken,
});
adminId = user._id.toString();
- impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId });
+ impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId });
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
- user = ReactiveCache.getUser({ _id: req.userId, isAdmin: true });
+ user = await ReactiveCache.getUser({ _id: req.userId, isAdmin: true });
}
const exporter = new Exporter(boardId);
- if (exporter.canExport(user) || impersonateDone) {
+ if (await exporter.canExport(user) || impersonateDone) {
if (impersonateDone) {
ImpersonatedUsers.insert({
adminId: adminId,
@@ -86,7 +86,7 @@ if (Meteor.isServer) {
JsonRoutes.sendResult(res, {
code: 200,
- data: exporter.build(),
+ data: await exporter.build(),
});
} else {
// we could send an explicit error message, but on the other hand the only
@@ -118,7 +118,7 @@ if (Meteor.isServer) {
JsonRoutes.add(
'get',
'/api/boards/:boardId/attachments/:attachmentId/export',
- function (req, res) {
+ async function (req, res) {
const boardId = req.params.boardId;
const attachmentId = req.params.attachmentId;
let user = null;
@@ -126,7 +126,7 @@ if (Meteor.isServer) {
let adminId = null;
// First check if board exists and is public to avoid unnecessary authentication
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
JsonRoutes.sendResult(res, 404);
return;
@@ -138,7 +138,7 @@ if (Meteor.isServer) {
const exporter = new Exporter(boardId, attachmentId);
JsonRoutes.sendResult(res, {
code: 200,
- data: exporter.build(),
+ data: await exporter.build(),
});
return;
}
@@ -156,18 +156,18 @@ if (Meteor.isServer) {
}
const hashToken = Accounts._hashLoginToken(loginToken);
- user = ReactiveCache.getUser({
+ user = await ReactiveCache.getUser({
'services.resume.loginTokens.hashedToken': hashToken,
});
adminId = user._id.toString();
- impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId });
+ impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId });
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
- user = ReactiveCache.getUser({ _id: req.userId, isAdmin: true });
+ user = await ReactiveCache.getUser({ _id: req.userId, isAdmin: true });
}
const exporter = new Exporter(boardId, attachmentId);
- if (exporter.canExport(user) || impersonateDone) {
+ if (await exporter.canExport(user) || impersonateDone) {
if (impersonateDone) {
ImpersonatedUsers.insert({
adminId: adminId,
@@ -178,7 +178,7 @@ if (Meteor.isServer) {
}
JsonRoutes.sendResult(res, {
code: 200,
- data: exporter.build(),
+ data: await exporter.build(),
});
} else {
// we could send an explicit error message, but on the other hand the only
@@ -203,14 +203,14 @@ if (Meteor.isServer) {
* @param {string} authToken the loginToken
* @param {string} delimiter delimiter to use while building export. Default is comma ','
*/
- Picker.route('/api/boards/:boardId/export/csv', function (params, req, res) {
+ Picker.route('/api/boards/:boardId/export/csv', async function (params, req, res) {
const boardId = params.boardId;
let user = null;
let impersonateDone = false;
let adminId = null;
// First check if board exists and is public to avoid unnecessary authentication
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
res.writeHead(404);
res.end('Board not found');
@@ -237,7 +237,7 @@ if (Meteor.isServer) {
// use Uint8Array to prevent from converting bytes to string
res.write(new Uint8Array([0xEF, 0xBB, 0xBF]));
}
- res.write(exporter.buildCsv(params.query.delimiter, 'en'));
+ res.write(await exporter.buildCsv(params.query.delimiter, 'en'));
res.end();
return;
}
@@ -256,21 +256,21 @@ if (Meteor.isServer) {
}
const hashToken = Accounts._hashLoginToken(loginToken);
- user = ReactiveCache.getUser({
+ user = await ReactiveCache.getUser({
'services.resume.loginTokens.hashedToken': hashToken,
});
adminId = user._id.toString();
- impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId });
+ impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId });
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
- user = ReactiveCache.getUser({
+ user = await ReactiveCache.getUser({
_id: req.userId,
isAdmin: true,
});
}
const exporter = new Exporter(boardId);
- if (exporter.canExport(user) || impersonateDone) {
+ if (await exporter.canExport(user) || impersonateDone) {
if (impersonateDone) {
let exportType = 'exportCSV';
if( params.query.delimiter == "\t" ) {
@@ -303,7 +303,7 @@ if (Meteor.isServer) {
// use Uint8Array to prevent from converting bytes to string
res.write(new Uint8Array([0xEF, 0xBB, 0xBF]));
}
- res.write(exporter.buildCsv(params.query.delimiter, userLanguage));
+ res.write(await exporter.buildCsv(params.query.delimiter, userLanguage));
res.end();
} else {
res.writeHead(403);
diff --git a/models/exportExcel.js b/models/exportExcel.js
index 598fb92d6..aac56cd40 100644
--- a/models/exportExcel.js
+++ b/models/exportExcel.js
@@ -30,14 +30,14 @@ runOnServer(function() {
* @param {string} boardId the ID of the board we are exporting
* @param {string} authToken the loginToken
*/
- Picker.route('/api/boards/:boardId/exportExcel', function (params, req, res) {
+ Picker.route('/api/boards/:boardId/exportExcel', async function (params, req, res) {
const boardId = params.boardId;
let user = null;
let impersonateDone = false;
let adminId = null;
// First check if board exists and is public to avoid unnecessary authentication
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
res.end('Board not found');
return;
@@ -64,14 +64,14 @@ runOnServer(function() {
}
const hashToken = Accounts._hashLoginToken(loginToken);
- user = ReactiveCache.getUser({
+ user = await ReactiveCache.getUser({
'services.resume.loginTokens.hashedToken': hashToken,
});
adminId = user._id.toString();
- impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId });
+ impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId });
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
- user = ReactiveCache.getUser({
+ user = await ReactiveCache.getUser({
_id: req.userId,
isAdmin: true,
});
diff --git a/models/exportPDF.js b/models/exportPDF.js
index 272651f98..14173c711 100644
--- a/models/exportPDF.js
+++ b/models/exportPDF.js
@@ -30,7 +30,7 @@ runOnServer(function() {
* @param {string} boardId the ID of the board we are exporting
* @param {string} authToken the loginToken
*/
- Picker.route('/api/boards/:boardId/lists/:listId/cards/:cardId/exportPDF', function (params, req, res) {
+ Picker.route('/api/boards/:boardId/lists/:listId/cards/:cardId/exportPDF', async function (params, req, res) {
const boardId = params.boardId;
const paramListId = req.params.listId;
const paramCardId = req.params.cardId;
@@ -39,7 +39,7 @@ runOnServer(function() {
let adminId = null;
// First check if board exists and is public to avoid unnecessary authentication
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
res.end('Board not found');
return;
@@ -66,14 +66,14 @@ runOnServer(function() {
}
const hashToken = Accounts._hashLoginToken(loginToken);
- user = ReactiveCache.getUser({
+ user = await ReactiveCache.getUser({
'services.resume.loginTokens.hashedToken': hashToken,
});
adminId = user._id.toString();
- impersonateDone = ReactiveCache.getImpersonatedUser({ adminId: adminId });
+ impersonateDone = await ReactiveCache.getImpersonatedUser({ adminId: adminId });
} else if (!Meteor.settings.public.sandstorm) {
Authentication.checkUserId(req.userId);
- user = ReactiveCache.getUser({
+ user = await ReactiveCache.getUser({
_id: req.userId,
isAdmin: true,
});
diff --git a/models/exporter.js b/models/exporter.js
index f1f601540..4cef38d1a 100644
--- a/models/exporter.js
+++ b/models/exporter.js
@@ -34,7 +34,7 @@ export class Exporter {
this._attachmentId = attachmentId;
}
- build() {
+ async build() {
const fs = Npm.require('fs');
const os = Npm.require('os');
const path = Npm.require('path');
@@ -55,7 +55,7 @@ export class Exporter {
};
_.extend(
result,
- ReactiveCache.getBoard(this._boardId, {
+ await ReactiveCache.getBoard(this._boardId, {
fields: {
stars: 0,
},
@@ -97,7 +97,7 @@ export class Exporter {
const byBoardAndAttachment = this._attachmentId
? { boardId: this._boardId, _id: this._attachmentId }
: byBoard;
- result.attachments = ReactiveCache.getAttachments(byBoardAndAttachment)
+ result.attachments = (await ReactiveCache.getAttachments(byBoardAndAttachment))
.map((attachment) => {
let filebase64 = null;
filebase64 = getBase64DataSync(attachment);
@@ -116,41 +116,41 @@ export class Exporter {
return result.attachments.length > 0 ? result.attachments[0] : {};
}
- result.lists = ReactiveCache.getLists(byBoard, noBoardId);
- result.cards = ReactiveCache.getCards(byBoardNoLinked, noBoardId);
- result.swimlanes = ReactiveCache.getSwimlanes(byBoard, noBoardId);
- result.customFields = ReactiveCache.getCustomFields(
+ result.lists = await ReactiveCache.getLists(byBoard, noBoardId);
+ result.cards = await ReactiveCache.getCards(byBoardNoLinked, noBoardId);
+ result.swimlanes = await ReactiveCache.getSwimlanes(byBoard, noBoardId);
+ result.customFields = await ReactiveCache.getCustomFields(
{ boardIds: this._boardId },
{ fields: { boardIds: 0 } },
);
- result.comments = ReactiveCache.getCardComments(byBoard, noBoardId);
- result.activities = ReactiveCache.getActivities(byBoard, noBoardId);
- result.rules = ReactiveCache.getRules(byBoard, noBoardId);
+ result.comments = await ReactiveCache.getCardComments(byBoard, noBoardId);
+ result.activities = await ReactiveCache.getActivities(byBoard, noBoardId);
+ result.rules = await ReactiveCache.getRules(byBoard, noBoardId);
result.checklists = [];
result.checklistItems = [];
result.subtaskItems = [];
result.triggers = [];
result.actions = [];
- result.cards.forEach((card) => {
+ for (const card of result.cards) {
result.checklists.push(
- ...ReactiveCache.getChecklists({
+ ...await ReactiveCache.getChecklists({
cardId: card._id,
}),
);
result.checklistItems.push(
- ...ReactiveCache.getChecklistItems({
+ ...await ReactiveCache.getChecklistItems({
cardId: card._id,
}),
);
result.subtaskItems.push(
- ...ReactiveCache.getCards({
+ ...await ReactiveCache.getCards({
parentId: card._id,
}),
);
- });
- result.rules.forEach((rule) => {
+ }
+ for (const rule of result.rules) {
result.triggers.push(
- ...ReactiveCache.getTriggers(
+ ...await ReactiveCache.getTriggers(
{
_id: rule.triggerId,
},
@@ -158,14 +158,14 @@ export class Exporter {
),
);
result.actions.push(
- ...ReactiveCache.getActions(
+ ...await ReactiveCache.getActions(
{
_id: rule.actionId,
},
noBoardId,
),
);
- });
+ }
// we also have to export some user data - as the other elements only
// include id but we have to be careful:
@@ -211,7 +211,7 @@ export class Exporter {
'profile.avatarUrl': 1,
},
};
- result.users = ReactiveCache.getUsers(byUserIds, userFields)
+ result.users = (await ReactiveCache.getUsers(byUserIds, userFields))
.map((user) => {
// user avatar is stored as a relative url, we export absolute
if ((user.profile || {}).avatarUrl) {
@@ -222,8 +222,8 @@ export class Exporter {
return result;
}
- buildCsv(userDelimiter = ',', userLanguage='en') {
- const result = this.build();
+ async buildCsv(userDelimiter = ',', userLanguage='en') {
+ const result = await this.build();
const columnHeaders = [];
const cardRows = [];
@@ -398,8 +398,8 @@ export class Exporter {
return Papa.unparse(cardRows, papaconfig);
}
- canExport(user) {
- const board = ReactiveCache.getBoard(this._boardId);
+ async canExport(user) {
+ const board = await ReactiveCache.getBoard(this._boardId);
return board && board.isVisibleBy(user);
}
}
diff --git a/models/integrations.js b/models/integrations.js
index 3daa3cb8d..d3c7aee5b 100644
--- a/models/integrations.js
+++ b/models/integrations.js
@@ -102,9 +102,9 @@ Integrations.Const = {
};
const permissionHelper = {
allow(userId, doc) {
- const user = ReactiveCache.getUser(userId);
- const isAdmin = user && ReactiveCache.getCurrentUser().isAdmin;
- return isAdmin || allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ const user = Meteor.users.findOne(userId);
+ const isAdmin = user && user.isAdmin;
+ return isAdmin || allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
};
Integrations.allow({
@@ -134,7 +134,7 @@ if (Meteor.isServer) {
* @param {string} boardId the board ID
* @return_type [Integrations]
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/integrations', function(
+ JsonRoutes.add('GET', '/api/boards/:boardId/integrations', async function(
req,
res,
) {
@@ -142,10 +142,10 @@ if (Meteor.isServer) {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
- const data = ReactiveCache.getIntegrations(
+ const data = (await ReactiveCache.getIntegrations(
{ boardId: paramBoardId },
{ fields: { token: 0 } },
- ).map(function(doc) {
+ )).map(function(doc) {
return doc;
});
@@ -166,7 +166,7 @@ if (Meteor.isServer) {
* @param {string} intId the integration ID
* @return_type Integrations
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', function(
+ JsonRoutes.add('GET', '/api/boards/:boardId/integrations/:intId', async function(
req,
res,
) {
@@ -177,7 +177,7 @@ if (Meteor.isServer) {
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getIntegration(
+ data: await ReactiveCache.getIntegration(
{ _id: paramIntId, boardId: paramBoardId },
{ fields: { token: 0 } },
),
@@ -310,7 +310,7 @@ if (Meteor.isServer) {
JsonRoutes.add(
'DELETE',
'/api/boards/:boardId/integrations/:intId/activities',
- function(req, res) {
+ async function(req, res) {
try {
const paramBoardId = req.params.boardId;
const paramIntId = req.params.intId;
@@ -324,7 +324,7 @@ if (Meteor.isServer) {
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getIntegration(
+ data: await ReactiveCache.getIntegration(
{ _id: paramIntId, boardId: paramBoardId },
{ fields: { _id: 1, activities: 1 } },
),
@@ -350,7 +350,7 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/integrations/:intId/activities',
- function(req, res) {
+ async function(req, res) {
try {
const paramBoardId = req.params.boardId;
const paramIntId = req.params.intId;
@@ -364,7 +364,7 @@ if (Meteor.isServer) {
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getIntegration(
+ data: await ReactiveCache.getIntegration(
{ _id: paramIntId, boardId: paramBoardId },
{ fields: { _id: 1, activities: 1 } },
),
diff --git a/models/lib/fileStoreStrategy.js b/models/lib/fileStoreStrategy.js
index 911011526..426e956b5 100644
--- a/models/lib/fileStoreStrategy.js
+++ b/models/lib/fileStoreStrategy.js
@@ -580,8 +580,8 @@ export const moveToStorage = function(fileObj, storageDestination, fileStoreStra
});
};
-export const copyFile = function(fileObj, newCardId, fileStoreStrategyFactory) {
- const newCard = ReactiveCache.getCard(newCardId);
+export const copyFile = async function(fileObj, newCardId, fileStoreStrategyFactory) {
+ const newCard = await ReactiveCache.getCard(newCardId);
Object.keys(fileObj.versions).forEach(versionName => {
const strategyRead = fileStoreStrategyFactory.getFileStrategy(fileObj, versionName);
const readStream = strategyRead.getReadStream();
diff --git a/models/lists.js b/models/lists.js
index 77d917ed7..a974bcbd4 100644
--- a/models/lists.js
+++ b/models/lists.js
@@ -182,15 +182,15 @@ Lists.attachSchema(
Lists.allow({
insert(userId, doc) {
// ReadOnly and CommentOnly users cannot create lists
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
},
update(userId, doc) {
// ReadOnly and CommentOnly users cannot edit lists
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
},
remove(userId, doc) {
// ReadOnly and CommentOnly users cannot delete lists
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
},
fetch: ['boardId'],
});
@@ -203,7 +203,7 @@ Lists.helpers({
this.swimlaneId = swimlaneId;
let _id = null;
- const existingListWithSameName = ReactiveCache.getList({
+ const existingListWithSameName = await ReactiveCache.getList({
boardId,
title: this.title,
archived: false,
@@ -213,11 +213,11 @@ Lists.helpers({
} else {
delete this._id;
this.swimlaneId = swimlaneId; // Set the target swimlane for the copied list
- _id = Lists.insert(this);
+ _id = await Lists.insertAsync(this);
}
// Copy all cards in list
- const cards = ReactiveCache.getCards({
+ const cards = await ReactiveCache.getCards({
swimlaneId: oldSwimlaneId,
listId: oldId,
archived: false,
@@ -230,7 +230,7 @@ Lists.helpers({
},
async move(boardId, swimlaneId) {
- const boardList = ReactiveCache.getList({
+ const boardList = await ReactiveCache.getList({
boardId,
title: this.title,
archived: false,
@@ -238,13 +238,13 @@ Lists.helpers({
let listId;
if (boardList) {
listId = boardList._id;
- for (const card of this.cards()) {
+ for (const card of await this.cards()) {
await card.move(boardId, this._id, boardList._id);
}
} else {
console.log('list.title:', this.title);
console.log('boardList:', boardList);
- listId = Lists.insert({
+ listId = await Lists.insertAsync({
title: this.title,
boardId,
type: this.type,
@@ -254,7 +254,7 @@ Lists.helpers({
});
}
- for (const card of this.cards(swimlaneId)) {
+ for (const card of await this.cards(swimlaneId)) {
await card.move(boardId, swimlaneId, listId);
}
},
@@ -364,7 +364,7 @@ Lists.helpers({
async archive() {
if (this.isTemplateList()) {
- for (const card of this.cards()) {
+ for (const card of await this.cards()) {
await card.archive();
}
}
@@ -373,7 +373,7 @@ Lists.helpers({
async restore() {
if (this.isTemplateList()) {
- for (const card of this.allCards()) {
+ for (const card of await this.allCards()) {
await card.restore();
}
}
@@ -397,23 +397,25 @@ Lists.helpers({
},
});
-Lists.userArchivedLists = userId => {
- return ReactiveCache.getLists({
- boardId: { $in: Boards.userBoardIds(userId, null) },
+Lists.userArchivedLists = async userId => {
+ return await ReactiveCache.getLists({
+ boardId: { $in: await Boards.userBoardIds(userId, null) },
archived: true,
})
};
-Lists.userArchivedListIds = () => {
- return Lists.userArchivedLists().map(list => { return list._id; });
+Lists.userArchivedListIds = async () => {
+ const lists = await Lists.userArchivedLists();
+ return lists.map(list => { return list._id; });
};
-Lists.archivedLists = () => {
- return ReactiveCache.getLists({ archived: true });
+Lists.archivedLists = async () => {
+ return await ReactiveCache.getLists({ archived: true });
};
-Lists.archivedListIds = () => {
- return Lists.archivedLists().map(list => {
+Lists.archivedListIds = async () => {
+ const lists = await Lists.archivedLists();
+ return lists.map(list => {
return list._id;
});
};
@@ -427,12 +429,12 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const list = ReactiveCache.getList(listId);
+ const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
- const board = ReactiveCache.getBoard(list.boardId);
+ const board = await ReactiveCache.getBoard(list.boardId);
if (!board || !board.hasAdmin(this.userId)) {
throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.');
}
@@ -450,63 +452,62 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const list = ReactiveCache.getList(listId);
+ const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
- const board = ReactiveCache.getBoard(list.boardId);
+ const board = await ReactiveCache.getBoard(list.boardId);
if (!board || !board.hasAdmin(this.userId)) {
throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.');
}
- if (list.getWipLimit('value') === 0) {
+ if ((await list.getWipLimit('value')) === 0) {
await list.setWipLimit(1);
}
- list.toggleWipLimit(!list.getWipLimit('enabled'));
+ await list.toggleWipLimit(!(await list.getWipLimit('enabled')));
},
- enableSoftLimit(listId) {
+ async enableSoftLimit(listId) {
check(listId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const list = ReactiveCache.getList(listId);
+ const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
- const board = ReactiveCache.getBoard(list.boardId);
+ const board = await ReactiveCache.getBoard(list.boardId);
if (!board || !board.hasAdmin(this.userId)) {
throw new Meteor.Error('not-authorized', 'You must be a board admin to modify WIP limits.');
}
- list.toggleSoftLimit(!list.getWipLimit('soft'));
+ await list.toggleSoftLimit(!(await list.getWipLimit('soft')));
},
- myLists() {
+ async myLists() {
// my lists
- return _.uniq(
- ReactiveCache.getLists(
- {
- boardId: { $in: Boards.userBoardIds(this.userId) },
- archived: false,
- },
- {
- fields: { title: 1 },
- },
- ).map(list => list.title),
- ).sort();
+ const lists = await ReactiveCache.getLists(
+ {
+ boardId: { $in: await Boards.userBoardIds(this.userId) },
+ archived: false,
+ },
+ {
+ fields: { title: 1 },
+ },
+ );
+ return _.uniq(lists.map(list => list.title)).sort();
},
- updateListSort(listId, boardId, updateData) {
+ async updateListSort(listId, boardId, updateData) {
check(listId, String);
check(boardId, String);
check(updateData, Object);
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
@@ -519,7 +520,7 @@ Meteor.methods({
}
}
- const list = ReactiveCache.getList(listId);
+ const list = await ReactiveCache.getList(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
@@ -532,14 +533,14 @@ Meteor.methods({
});
if (updateData.swimlaneId) {
- const swimlane = ReactiveCache.getSwimlane(updateData.swimlaneId);
+ const swimlane = await ReactiveCache.getSwimlane(updateData.swimlaneId);
if (!swimlane || swimlane.boardId !== boardId) {
throw new Meteor.Error('invalid-swimlane', 'Invalid swimlane for this board');
}
}
- Lists.update(
- { _id: listId, boardId },
+ await Lists.updateAsync(
+ listId,
{
$set: {
...updateData,
@@ -586,14 +587,14 @@ Lists.after.insert((userId, doc) => {
}, 100);
});
-Lists.before.remove((userId, doc) => {
- const cards = ReactiveCache.getCards({ listId: doc._id });
+Lists.before.remove(async (userId, doc) => {
+ const cards = await ReactiveCache.getCards({ listId: doc._id });
if (cards) {
- cards.forEach(card => {
- Cards.remove(card._id);
- });
+ for (const card of cards) {
+ await Cards.removeAsync(card._id);
+ }
}
- Activities.insert({
+ await Activities.insertAsync({
userId,
type: 'list',
activityType: 'removeList',
@@ -661,14 +662,14 @@ if (Meteor.isServer) {
* @return_type [{_id: string,
* title: string}]
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/lists', function(req, res) {
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getLists({ boardId: paramBoardId, archived: false }).map(
+ data: (await ReactiveCache.getLists({ boardId: paramBoardId, archived: false })).map(
function(doc) {
return {
_id: doc._id,
@@ -693,7 +694,7 @@ if (Meteor.isServer) {
* @param {string} listId the List ID
* @return_type Lists
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId', function(
+ JsonRoutes.add('GET', '/api/boards/:boardId/lists/:listId', async function(
req,
res,
) {
@@ -703,7 +704,7 @@ if (Meteor.isServer) {
Authentication.checkBoardAccess(req.userId, paramBoardId);
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getList({
+ data: await ReactiveCache.getList({
_id: paramListId,
boardId: paramBoardId,
archived: false,
@@ -725,12 +726,12 @@ if (Meteor.isServer) {
* @param {string} title the title of the List
* @return_type {_id: string}
*/
- JsonRoutes.add('POST', '/api/boards/:boardId/lists', function(req, res) {
+ JsonRoutes.add('POST', '/api/boards/:boardId/lists', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
- const board = ReactiveCache.getBoard(paramBoardId);
- const id = Lists.insert({
+ const board = await ReactiveCache.getBoard(paramBoardId);
+ const id = await Lists.insertAsync({
title: req.body.title,
boardId: paramBoardId,
sort: board.lists().length,
@@ -766,7 +767,7 @@ if (Meteor.isServer) {
* @param {boolean} [collapsed] whether the list is collapsed
* @return_type {_id: string}
*/
- JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId', function(
+ JsonRoutes.add('PUT', '/api/boards/:boardId/lists/:listId', async function(
req,
res,
) {
@@ -776,7 +777,7 @@ if (Meteor.isServer) {
let updated = false;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
- const list = ReactiveCache.getList({
+ const list = await ReactiveCache.getList({
_id: paramListId,
boardId: paramBoardId,
archived: false,
diff --git a/models/lockoutSettings.js b/models/lockoutSettings.js
index 7585087ce..454b64481 100644
--- a/models/lockoutSettings.js
+++ b/models/lockoutSettings.js
@@ -49,7 +49,7 @@ LockoutSettings.attachSchema(
LockoutSettings.allow({
update(userId) {
- const user = ReactiveCache.getUser(userId);
+ const user = Meteor.users.findOne(userId);
return user && user.isAdmin;
},
});
diff --git a/models/org.js b/models/org.js
index 41bdb47b4..eeb5cb6c3 100644
--- a/models/org.js
+++ b/models/org.js
@@ -88,7 +88,7 @@ Org.attachSchema(
if (Meteor.isServer) {
Org.allow({
insert(userId, doc) {
- const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
+ const user = Meteor.users.findOne(userId);
if (user?.isAdmin)
return true;
if (!user) {
@@ -97,7 +97,7 @@ if (Meteor.isServer) {
return doc._id === userId;
},
update(userId, doc) {
- const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
+ const user = Meteor.users.findOne(userId);
if (user?.isAdmin)
return true;
if (!user) {
@@ -106,7 +106,7 @@ if (Meteor.isServer) {
return doc._id === userId;
},
remove(userId, doc) {
- const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
+ const user = Meteor.users.findOne(userId);
if (user?.isAdmin)
return true;
if (!user) {
@@ -119,7 +119,7 @@ if (Meteor.isServer) {
Meteor.methods({
- setCreateOrg(
+ async setCreateOrg(
orgDisplayName,
orgDesc,
orgShortName,
@@ -127,7 +127,7 @@ if (Meteor.isServer) {
orgWebsite,
orgIsActive,
) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(orgDisplayName, String);
check(orgDesc, String);
check(orgShortName, String);
@@ -135,7 +135,7 @@ if (Meteor.isServer) {
check(orgWebsite, String);
check(orgIsActive, Boolean);
- const nOrgNames = ReactiveCache.getOrgs({ orgShortName }).length;
+ const nOrgNames = (await ReactiveCache.getOrgs({ orgShortName })).length;
if (nOrgNames > 0) {
throw new Meteor.Error('orgname-already-taken');
} else {
@@ -150,7 +150,7 @@ if (Meteor.isServer) {
}
}
},
- setCreateOrgFromOidc(
+ async setCreateOrgFromOidc(
orgDisplayName,
orgDesc,
orgShortName,
@@ -165,7 +165,7 @@ if (Meteor.isServer) {
check(orgWebsite, String);
check(orgIsActive, Boolean);
- const nOrgNames = ReactiveCache.getOrgs({ orgShortName }).length;
+ const nOrgNames = (await ReactiveCache.getOrgs({ orgShortName })).length;
if (nOrgNames > 0) {
throw new Meteor.Error('orgname-already-taken');
} else {
@@ -179,19 +179,19 @@ if (Meteor.isServer) {
});
}
},
- setOrgDisplayName(org, orgDisplayName) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ async setOrgDisplayName(org, orgDisplayName) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgDisplayName, String);
Org.update(org, {
$set: { orgDisplayName: orgDisplayName },
});
- Meteor.call('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
+ await Meteor.callAsync('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
}
},
- setOrgDesc(org, orgDesc) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ async setOrgDesc(org, orgDesc) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgDesc, String);
Org.update(org, {
@@ -200,8 +200,8 @@ if (Meteor.isServer) {
}
},
- setOrgShortName(org, orgShortName) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ async setOrgShortName(org, orgShortName) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgShortName, String);
Org.update(org, {
@@ -210,8 +210,8 @@ if (Meteor.isServer) {
}
},
- setAutoAddUsersWithDomainName(org, orgAutoAddUsersWithDomainName) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ async setAutoAddUsersWithDomainName(org, orgAutoAddUsersWithDomainName) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgAutoAddUsersWithDomainName, String);
Org.update(org, {
@@ -220,8 +220,8 @@ if (Meteor.isServer) {
}
},
- setOrgIsActive(org, orgIsActive) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ async setOrgIsActive(org, orgIsActive) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgIsActive, Boolean);
Org.update(org, {
@@ -229,7 +229,7 @@ if (Meteor.isServer) {
});
}
},
- setOrgAllFieldsFromOidc(
+ async setOrgAllFieldsFromOidc(
org,
orgDisplayName,
orgDesc,
@@ -255,9 +255,9 @@ if (Meteor.isServer) {
orgIsActive: orgIsActive,
},
});
- Meteor.call('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
+ await Meteor.callAsync('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
},
- setOrgAllFields(
+ async setOrgAllFields(
org,
orgDisplayName,
orgDesc,
@@ -266,7 +266,7 @@ if (Meteor.isServer) {
orgWebsite,
orgIsActive,
) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(org, Object);
check(orgDisplayName, String);
check(orgDesc, String);
@@ -284,7 +284,7 @@ if (Meteor.isServer) {
orgIsActive: orgIsActive,
},
});
- Meteor.call('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
+ await Meteor.callAsync('setUsersOrgsOrgDisplayName', org._id, orgDisplayName);
}
},
});
diff --git a/models/rules.js b/models/rules.js
index ea1ba571f..5d533372b 100644
--- a/models/rules.js
+++ b/models/rules.js
@@ -73,13 +73,13 @@ Rules.helpers({
Rules.allow({
insert(userId, doc) {
- return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
update(userId, doc) {
- return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
remove(userId, doc) {
- return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
});
diff --git a/models/server/ExporterCardPDF.js b/models/server/ExporterCardPDF.js
index 37551e6cb..454c17f4c 100644
--- a/models/server/ExporterCardPDF.js
+++ b/models/server/ExporterCardPDF.js
@@ -640,8 +640,8 @@ class ExporterCardPDF {
}
- canExport(user) {
- const board = ReactiveCache.getBoard(this._boardId);
+ async canExport(user) {
+ const board = await ReactiveCache.getBoard(this._boardId);
return board && board.isVisibleBy(user);
}
}
diff --git a/models/server/ExporterExcel.js b/models/server/ExporterExcel.js
index 66684917e..55d27011e 100644
--- a/models/server/ExporterExcel.js
+++ b/models/server/ExporterExcel.js
@@ -31,7 +31,7 @@ class ExporterExcel {
this.userLanguage = userLanguage;
}
- build(res) {
+ async build(res) {
const fs = Npm.require('fs');
const os = Npm.require('os');
const path = Npm.require('path');
@@ -56,16 +56,16 @@ class ExporterExcel {
};
_.extend(
result,
- ReactiveCache.getBoard(this._boardId, {
+ await ReactiveCache.getBoard(this._boardId, {
fields: {
stars: 0,
},
}),
);
- result.lists = ReactiveCache.getLists(byBoard, noBoardId);
- result.cards = ReactiveCache.getCards(byBoardNoLinked, noBoardId);
- result.swimlanes = ReactiveCache.getSwimlanes(byBoard, noBoardId);
- result.customFields = ReactiveCache.getCustomFields(
+ result.lists = await ReactiveCache.getLists(byBoard, noBoardId);
+ result.cards = await ReactiveCache.getCards(byBoardNoLinked, noBoardId);
+ result.swimlanes = await ReactiveCache.getSwimlanes(byBoard, noBoardId);
+ result.customFields = await ReactiveCache.getCustomFields(
{
boardIds: {
$in: [this.boardId],
@@ -77,34 +77,34 @@ class ExporterExcel {
},
},
);
- result.comments = ReactiveCache.getCardComments(byBoard, noBoardId);
- result.activities = ReactiveCache.getActivities(byBoard, noBoardId);
- result.rules = ReactiveCache.getRules(byBoard, noBoardId);
+ result.comments = await ReactiveCache.getCardComments(byBoard, noBoardId);
+ result.activities = await ReactiveCache.getActivities(byBoard, noBoardId);
+ result.rules = await ReactiveCache.getRules(byBoard, noBoardId);
result.checklists = [];
result.checklistItems = [];
result.subtaskItems = [];
result.triggers = [];
result.actions = [];
- result.cards.forEach((card) => {
+ for (const card of result.cards) {
result.checklists.push(
- ...ReactiveCache.getChecklists({
+ ...await ReactiveCache.getChecklists({
cardId: card._id,
}),
);
result.checklistItems.push(
- ...ReactiveCache.getChecklistItems({
+ ...await ReactiveCache.getChecklistItems({
cardId: card._id,
}),
);
result.subtaskItems.push(
- ...ReactiveCache.getCards({
+ ...await ReactiveCache.getCards({
parentId: card._id,
}),
);
- });
- result.rules.forEach((rule) => {
+ }
+ for (const rule of result.rules) {
result.triggers.push(
- ...ReactiveCache.getTriggers(
+ ...await ReactiveCache.getTriggers(
{
_id: rule.triggerId,
},
@@ -112,14 +112,14 @@ class ExporterExcel {
),
);
result.actions.push(
- ...ReactiveCache.getActions(
+ ...await ReactiveCache.getActions(
{
_id: rule.actionId,
},
noBoardId,
),
);
- });
+ }
// we also have to export some user data - as the other elements only
// include id but we have to be careful:
@@ -169,7 +169,7 @@ class ExporterExcel {
'profile.avatarUrl': 1,
},
};
- result.users = ReactiveCache.getUsers(byUserIds, userFields)
+ result.users = (await ReactiveCache.getUsers(byUserIds, userFields))
.map((user) => {
// user avatar is stored as a relative url, we export absolute
if ((user.profile || {}).avatarUrl) {
@@ -905,8 +905,8 @@ class ExporterExcel {
workbook.xlsx.write(res).then(function () {});
}
- canExport(user) {
- const board = ReactiveCache.getBoard(this._boardId);
+ async canExport(user) {
+ const board = await ReactiveCache.getBoard(this._boardId);
return board && board.isVisibleBy(user);
}
}
diff --git a/models/server/metrics.js b/models/server/metrics.js
index 669bbbf92..af4c9eca0 100644
--- a/models/server/metrics.js
+++ b/models/server/metrics.js
@@ -57,12 +57,12 @@ const getBoardTitleWithMostActivities = (dateWithXdaysAgo, nbLimit) => {
);
};
-const getBoards = (boardIds) => {
- const ret = ReactiveCache.getBoards({ _id: { $in: boardIds } });
+const getBoards = async (boardIds) => {
+ const ret = await ReactiveCache.getBoards({ _id: { $in: boardIds } });
return ret;
};
Meteor.startup(() => {
- WebApp.connectHandlers.use('/metrics', (req, res, next) => {
+ WebApp.connectHandlers.use('/metrics', async (req, res, next) => {
try {
const ipAddress =
req.headers['x-forwarded-for'] || req.socket.remoteAddress;
@@ -95,7 +95,7 @@ Meteor.startup(() => {
metricsRes += '# Number of registered users\n';
// Get number of registered user
- resCount = ReactiveCache.getUsers({}).length; // KPI 2
+ resCount = (await ReactiveCache.getUsers({})).length; // KPI 2
metricsRes += 'wekan_registeredUsers ' + resCount + '\n';
resCount = 0;
@@ -103,7 +103,7 @@ Meteor.startup(() => {
metricsRes += '# Number of registered boards\n';
// Get number of registered boards
- resCount = ReactiveCache.getBoards({ archived: false, type: 'board' }).length; // KPI 3
+ resCount = (await ReactiveCache.getBoards({ archived: false, type: 'board' })).length; // KPI 3
metricsRes += 'wekan_registeredboards ' + resCount + '\n';
resCount = 0;
@@ -112,8 +112,8 @@ Meteor.startup(() => {
// Get number of registered boards by registered users
resCount =
- ReactiveCache.getBoards({ archived: false, type: 'board' }).length /
- ReactiveCache.getUsers({}).length; // KPI 4
+ (await ReactiveCache.getBoards({ archived: false, type: 'board' })).length /
+ (await ReactiveCache.getUsers({})).length; // KPI 4
metricsRes +=
'wekan_registeredboardsBysRegisteredUsers ' + resCount + '\n';
resCount = 0;
@@ -122,11 +122,11 @@ Meteor.startup(() => {
metricsRes += '# Number of registered boards\n';
// Get board numbers with only one member
- resCount = ReactiveCache.getBoards({
+ resCount = (await ReactiveCache.getBoards({
archived: false,
type: 'board',
members: { $size: 1 },
- }).length; // KPI 5
+ })).length; // KPI 5
metricsRes +=
'wekan_registeredboardsWithOnlyOneMember ' + resCount + '\n';
resCount = 0;
@@ -144,9 +144,9 @@ Meteor.startup(() => {
let dateWithXdaysAgo = new Date(
new Date() - xdays * 24 * 60 * 60 * 1000,
);
- resCount = ReactiveCache.getUsers({
+ resCount = (await ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
- }).length; // KPI 5
+ })).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated5DaysAgo ' + resCount + '\n';
resCount = 0;
@@ -157,9 +157,9 @@ Meteor.startup(() => {
// Get number of users with last connection dated 10 days ago
xdays = 10;
dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
- resCount = ReactiveCache.getUsers({
+ resCount = (await ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
- }).length; // KPI 5
+ })).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated10DaysAgo ' + resCount + '\n';
resCount = 0;
@@ -170,9 +170,9 @@ Meteor.startup(() => {
// Get number of users with last connection dated 20 days ago
xdays = 20;
dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
- resCount = ReactiveCache.getUsers({
+ resCount = (await ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
- }).length; // KPI 5
+ })).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated20DaysAgo ' + resCount + '\n';
resCount = 0;
@@ -183,9 +183,9 @@ Meteor.startup(() => {
// Get number of users with last connection dated 20 days ago
xdays = 30;
dateWithXdaysAgo = new Date(new Date() - xdays * 24 * 60 * 60 * 1000);
- resCount = ReactiveCache.getUsers({
+ resCount = (await ReactiveCache.getUsers({
lastConnectionDate: { $gte: dateWithXdaysAgo },
- }).length; // KPI 5
+ })).length; // KPI 5
metricsRes +=
'wekan_usersWithLastConnectionDated30DaysAgo ' + resCount + '\n';
resCount = 0;
diff --git a/models/settings.js b/models/settings.js
index 4f45b0c8f..00dd1b653 100644
--- a/models/settings.js
+++ b/models/settings.js
@@ -224,7 +224,7 @@ Settings.helpers({
});
Settings.allow({
update(userId) {
- const user = ReactiveCache.getUser(userId);
+ const user = Meteor.users.findOne(userId);
return user && user.isAdmin;
},
});
@@ -232,7 +232,7 @@ Settings.allow({
if (Meteor.isServer) {
Meteor.startup(async () => {
await Settings._collection.createIndexAsync({ modifiedAt: -1 });
- const setting = ReactiveCache.getCurrentSetting();
+ const setting = await ReactiveCache.getCurrentSetting();
if (!setting) {
const now = new Date();
const domain = process.env.ROOT_URL.match(
@@ -258,7 +258,7 @@ if (Meteor.isServer) {
}
if (isSandstorm) {
// At Sandstorm, Admin Panel has SMTP settings
- const newSetting = ReactiveCache.getCurrentSetting();
+ const newSetting = await ReactiveCache.getCurrentSetting();
if (!process.env.MAIL_URL && newSetting.mailUrl())
process.env.MAIL_URL = newSetting.mailUrl();
Accounts.emailTemplates.from = process.env.MAIL_FROM
@@ -312,15 +312,16 @@ if (Meteor.isServer) {
return config;
}
- function sendInvitationEmail(_id) {
- const icode = ReactiveCache.getInvitationCode(_id);
- const author = ReactiveCache.getCurrentUser();
+ async function sendInvitationEmail(_id) {
+ const icode = await ReactiveCache.getInvitationCode(_id);
+ const author = await ReactiveCache.getCurrentUser();
try {
- const fullName = ReactiveCache.getUser(icode.authorId)?.profile?.fullname || "";
+ const authorUser = await ReactiveCache.getUser(icode.authorId);
+ const fullName = authorUser?.profile?.fullname || "";
const params = {
email: icode.email,
- inviter: fullName != "" ? fullName + " (" + ReactiveCache.getUser(icode.authorId).username + " )" : ReactiveCache.getUser(icode.authorId).username,
+ inviter: fullName != "" ? fullName + " (" + authorUser.username + " )" : authorUser.username,
user: icode.email.split('@')[0],
icode: icode.code,
url: FlowRouter.url('sign-up'),
@@ -351,8 +352,8 @@ if (Meteor.isServer) {
}
}
- function isNonAdminAllowedToSendMail(currentUser){
- const currSett = ReactiveCache.getCurrentSetting();
+ async function isNonAdminAllowedToSendMail(currentUser){
+ const currSett = await ReactiveCache.getCurrentSetting();
let isAllowed = false;
if(currSett && currSett != undefined && currSett.disableRegistration && currSett.mailDomainName !== undefined && currSett.mailDomainName != ""){
for(let i = 0; i < currentUser.emails.length; i++) {
@@ -389,20 +390,20 @@ if (Meteor.isServer) {
}
Meteor.methods({
- sendInvitation(emails, boards) {
+ async sendInvitation(emails, boards) {
let rc = 0;
check(emails, [String]);
check(boards, [String]);
- const user = ReactiveCache.getCurrentUser();
- if (!user.isAdmin && !isNonAdminAllowedToSendMail(user)) {
+ const user = await ReactiveCache.getCurrentUser();
+ if (!user.isAdmin && !(await isNonAdminAllowedToSendMail(user))) {
rc = -1;
throw new Meteor.Error('not-allowed');
}
- emails.forEach(email => {
+ for (const email of emails) {
if (email && SimpleSchema.RegEx.Email.test(email)) {
// Checks if the email is already link to an account.
- const userExist = ReactiveCache.getUser({ email });
+ const userExist = await ReactiveCache.getUser({ email });
if (userExist) {
rc = -1;
throw new Meteor.Error(
@@ -411,12 +412,12 @@ if (Meteor.isServer) {
);
}
// Checks if the email is already link to an invitation.
- const invitation = ReactiveCache.getInvitationCode({ email });
+ const invitation = await ReactiveCache.getInvitationCode({ email });
if (invitation) {
InvitationCodes.update(invitation, {
$set: { boardsToBeInvited: boards },
});
- sendInvitationEmail(invitation._id);
+ await sendInvitationEmail(invitation._id);
} else {
const code = getRandomNum(100000, 999999);
InvitationCodes.insert(
@@ -427,9 +428,9 @@ if (Meteor.isServer) {
createdAt: new Date(),
authorId: Meteor.userId(),
},
- function(err, _id) {
+ async function(err, _id) {
if (!err && _id) {
- sendInvitationEmail(_id);
+ await sendInvitationEmail(_id);
} else {
rc = -1;
throw new Meteor.Error(
@@ -441,15 +442,15 @@ if (Meteor.isServer) {
);
}
}
- });
+ }
return rc;
},
- sendSMTPTestEmail() {
+ async sendSMTPTestEmail() {
if (!Meteor.userId()) {
throw new Meteor.Error('invalid-user');
}
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
if (!user.emails || !user.emails[0] || !user.emails[0].address) {
throw new Meteor.Error('email-invalid');
}
@@ -499,8 +500,8 @@ if (Meteor.isServer) {
};
},
- getCustomUI() {
- const setting = ReactiveCache.getCurrentSetting();
+ async getCustomUI() {
+ const setting = await ReactiveCache.getCurrentSetting();
if (!setting.productName) {
return {
productName: '',
@@ -512,8 +513,8 @@ if (Meteor.isServer) {
}
},
- isDisableRegistration() {
- const setting = ReactiveCache.getCurrentSetting();
+ async isDisableRegistration() {
+ const setting = await ReactiveCache.getCurrentSetting();
if (setting.disableRegistration === true) {
return true;
} else {
@@ -521,8 +522,8 @@ if (Meteor.isServer) {
}
},
- isDisableForgotPassword() {
- const setting = ReactiveCache.getCurrentSetting();
+ async isDisableForgotPassword() {
+ const setting = await ReactiveCache.getCurrentSetting();
if (setting.disableForgotPassword === true) {
return true;
} else {
diff --git a/models/swimlanes.js b/models/swimlanes.js
index 26c55c69f..8b41a0292 100644
--- a/models/swimlanes.js
+++ b/models/swimlanes.js
@@ -133,15 +133,15 @@ Swimlanes.attachSchema(
Swimlanes.allow({
insert(userId, doc) {
// ReadOnly and CommentOnly users cannot create swimlanes
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
},
update(userId, doc) {
// ReadOnly and CommentOnly users cannot edit swimlanes
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
},
remove(userId, doc) {
// ReadOnly and CommentOnly users cannot delete swimlanes
- return allowIsBoardMemberWithWriteAccess(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardMemberWithWriteAccess(userId, Boards.findOne(doc.boardId));
},
fetch: ['boardId'],
});
@@ -152,7 +152,7 @@ Swimlanes.helpers({
const oldBoardId = this.boardId;
this.boardId = boardId;
delete this._id;
- const _id = Swimlanes.insert(this);
+ const _id = await Swimlanes.insertAsync(this);
const query = {
swimlaneId: { $in: [oldId, ''] },
@@ -163,7 +163,7 @@ Swimlanes.helpers({
}
// Copy all lists in swimlane
- const lists = ReactiveCache.getLists(query);
+ const lists = await ReactiveCache.getLists(query);
for (const list of lists) {
list.type = 'list';
list.swimlaneId = oldId;
@@ -175,8 +175,8 @@ Swimlanes.helpers({
},
async move(toBoardId) {
- for (const list of this.lists()) {
- const toList = ReactiveCache.getList({
+ for (const list of await this.lists()) {
+ const toList = await ReactiveCache.getList({
boardId: toBoardId,
title: list.title,
archived: false,
@@ -196,7 +196,7 @@ Swimlanes.helpers({
});
}
- const cards = ReactiveCache.getCards({
+ const cards = await ReactiveCache.getCards({
listId: list._id,
swimlaneId: this._id,
});
@@ -212,7 +212,7 @@ Swimlanes.helpers({
});
// make sure there is a default swimlane
- this.board().getDefaultSwimline();
+ (await this.board()).getDefaultSwimline();
},
cards() {
@@ -331,7 +331,7 @@ Swimlanes.helpers({
async archive() {
if (this.isTemplateSwimlane()) {
- for (const list of this.myLists()) {
+ for (const list of await this.myLists()) {
await list.archive();
}
}
@@ -340,7 +340,7 @@ Swimlanes.helpers({
async restore() {
if (this.isTemplateSwimlane()) {
- for (const list of this.myLists()) {
+ for (const list of await this.myLists()) {
await list.restore();
}
}
@@ -352,23 +352,25 @@ Swimlanes.helpers({
},
});
-Swimlanes.userArchivedSwimlanes = userId => {
- return ReactiveCache.getSwimlanes({
- boardId: { $in: Boards.userBoardIds(userId, null) },
+Swimlanes.userArchivedSwimlanes = async userId => {
+ return await ReactiveCache.getSwimlanes({
+ boardId: { $in: await Boards.userBoardIds(userId, null) },
archived: true,
})
};
-Swimlanes.userArchivedSwimlaneIds = () => {
- return Swimlanes.userArchivedSwimlanes().map(swim => { return swim._id; });
+Swimlanes.userArchivedSwimlaneIds = async () => {
+ const swimlanes = await Swimlanes.userArchivedSwimlanes();
+ return swimlanes.map(swim => { return swim._id; });
};
-Swimlanes.archivedSwimlanes = () => {
- return ReactiveCache.getSwimlanes({ archived: true });
+Swimlanes.archivedSwimlanes = async () => {
+ return await ReactiveCache.getSwimlanes({ archived: true });
};
-Swimlanes.archivedSwimlaneIds = () => {
- return Swimlanes.archivedSwimlanes().map(swim => {
+Swimlanes.archivedSwimlaneIds = async () => {
+ const swimlanes = await Swimlanes.archivedSwimlanes();
+ return swimlanes.map(swim => {
return swim._id;
});
};
@@ -399,8 +401,8 @@ if (Meteor.isServer) {
}, 100);
});
- Swimlanes.before.remove(function(userId, doc) {
- const lists = ReactiveCache.getLists(
+ Swimlanes.before.remove(async function(userId, doc) {
+ const lists = await ReactiveCache.getLists(
{
boardId: doc.boardId,
swimlaneId: { $in: [doc._id, ''] },
@@ -410,14 +412,14 @@ if (Meteor.isServer) {
);
if (lists.length < 2) {
- lists.forEach(list => {
- list.remove();
- });
+ for (const list of lists) {
+ await list.remove();
+ }
} else {
- Cards.remove({ swimlaneId: doc._id });
+ await Cards.removeAsync({ swimlaneId: doc._id });
}
- Activities.insert({
+ await Activities.insertAsync({
userId,
type: 'swimlane',
activityType: 'removeSwimlane',
@@ -476,14 +478,15 @@ if (Meteor.isServer) {
* @return_type [{_id: string,
* title: string}]
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes', function(req, res) {
+ JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardAccess(req.userId, paramBoardId);
+ const swimlanes = await ReactiveCache.getSwimlanes({ boardId: paramBoardId, archived: false });
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getSwimlanes({ boardId: paramBoardId, archived: false }).map(
+ data: swimlanes.map(
function(doc) {
return {
_id: doc._id,
@@ -509,7 +512,7 @@ if (Meteor.isServer) {
* @param {string} swimlaneId the ID of the swimlane
* @return_type Swimlanes
*/
- JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', function(
+ JsonRoutes.add('GET', '/api/boards/:boardId/swimlanes/:swimlaneId', async function(
req,
res,
) {
@@ -520,7 +523,7 @@ if (Meteor.isServer) {
JsonRoutes.sendResult(res, {
code: 200,
- data: ReactiveCache.getSwimlane({
+ data: await ReactiveCache.getSwimlane({
_id: paramSwimlaneId,
boardId: paramBoardId,
archived: false,
@@ -543,13 +546,13 @@ if (Meteor.isServer) {
* @param {string} title the new title of the swimlane
* @return_type {_id: string}
*/
- JsonRoutes.add('POST', '/api/boards/:boardId/swimlanes', function(req, res) {
+ JsonRoutes.add('POST', '/api/boards/:boardId/swimlanes', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
- const board = ReactiveCache.getBoard(paramBoardId);
- const id = Swimlanes.insert({
+ const board = await ReactiveCache.getBoard(paramBoardId);
+ const id = await Swimlanes.insertAsync({
title: req.body.title,
boardId: paramBoardId,
sort: board.swimlanes().length,
@@ -578,13 +581,13 @@ if (Meteor.isServer) {
* @param {string} title the new title of the swimlane
* @return_type {_id: string}
*/
- JsonRoutes.add('PUT', '/api/boards/:boardId/swimlanes/:swimlaneId', function(req, res) {
+ JsonRoutes.add('PUT', '/api/boards/:boardId/swimlanes/:swimlaneId', async function(req, res) {
try {
const paramBoardId = req.params.boardId;
const paramSwimlaneId = req.params.swimlaneId;
Authentication.checkBoardWriteAccess(req.userId, paramBoardId);
- const board = ReactiveCache.getBoard(paramBoardId);
- const swimlane = ReactiveCache.getSwimlane({
+ const board = await ReactiveCache.getBoard(paramBoardId);
+ const swimlane = await ReactiveCache.getSwimlane({
_id: paramSwimlaneId,
boardId: paramBoardId,
});
diff --git a/models/tableVisibilityModeSettings.js b/models/tableVisibilityModeSettings.js
index b68cd971b..157152783 100644
--- a/models/tableVisibilityModeSettings.js
+++ b/models/tableVisibilityModeSettings.js
@@ -46,7 +46,7 @@ TableVisibilityModeSettings.attachSchema(
TableVisibilityModeSettings.allow({
update(userId) {
- const user = ReactiveCache.getUser(userId);
+ const user = Meteor.users.findOne(userId);
return user && user.isAdmin;
},
});
diff --git a/models/team.js b/models/team.js
index 57fbf315a..38f819aba 100644
--- a/models/team.js
+++ b/models/team.js
@@ -79,7 +79,7 @@ Team.attachSchema(
if (Meteor.isServer) {
Team.allow({
insert(userId, doc) {
- const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
+ const user = Meteor.users.findOne(userId);
if (user?.isAdmin)
return true;
if (!user) {
@@ -88,7 +88,7 @@ if (Meteor.isServer) {
return doc._id === userId;
},
update(userId, doc) {
- const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
+ const user = Meteor.users.findOne(userId);
if (user?.isAdmin)
return true;
if (!user) {
@@ -97,7 +97,7 @@ if (Meteor.isServer) {
return doc._id === userId;
},
remove(userId, doc) {
- const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
+ const user = Meteor.users.findOne(userId);
if (user?.isAdmin)
return true;
if (!user) {
@@ -109,21 +109,21 @@ if (Meteor.isServer) {
});
Meteor.methods({
- setCreateTeam(
+ async setCreateTeam(
teamDisplayName,
teamDesc,
teamShortName,
teamWebsite,
teamIsActive,
) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(teamDisplayName, String);
check(teamDesc, String);
check(teamShortName, String);
check(teamWebsite, String);
check(teamIsActive, Boolean);
- const nTeamNames = ReactiveCache.getTeams({ teamShortName }).length;
+ const nTeamNames = (await ReactiveCache.getTeams({ teamShortName })).length;
if (nTeamNames > 0) {
throw new Meteor.Error('teamname-already-taken');
} else {
@@ -137,7 +137,7 @@ if (Meteor.isServer) {
}
}
},
- setCreateTeamFromOidc(
+ async setCreateTeamFromOidc(
teamDisplayName,
teamDesc,
teamShortName,
@@ -149,7 +149,7 @@ if (Meteor.isServer) {
check(teamShortName, String);
check(teamWebsite, String);
check(teamIsActive, Boolean);
- const nTeamNames = ReactiveCache.getTeams({ teamShortName }).length;
+ const nTeamNames = (await ReactiveCache.getTeams({ teamShortName })).length;
if (nTeamNames > 0) {
throw new Meteor.Error('teamname-already-taken');
} else {
@@ -162,19 +162,19 @@ if (Meteor.isServer) {
});
}
},
- setTeamDisplayName(team, teamDisplayName) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ async setTeamDisplayName(team, teamDisplayName) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(team, Object);
check(teamDisplayName, String);
Team.update(team, {
$set: { teamDisplayName: teamDisplayName },
});
- Meteor.call('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
+ await Meteor.callAsync('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
}
},
- setTeamDesc(team, teamDesc) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ async setTeamDesc(team, teamDesc) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(team, Object);
check(teamDesc, String);
Team.update(team, {
@@ -183,8 +183,8 @@ if (Meteor.isServer) {
}
},
- setTeamShortName(team, teamShortName) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ async setTeamShortName(team, teamShortName) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(team, Object);
check(teamShortName, String);
Team.update(team, {
@@ -193,8 +193,8 @@ if (Meteor.isServer) {
}
},
- setTeamIsActive(team, teamIsActive) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ async setTeamIsActive(team, teamIsActive) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(team, Object);
check(teamIsActive, Boolean);
Team.update(team, {
@@ -202,7 +202,7 @@ if (Meteor.isServer) {
});
}
},
- setTeamAllFieldsFromOidc(
+ async setTeamAllFieldsFromOidc(
team,
teamDisplayName,
teamDesc,
@@ -225,9 +225,9 @@ if (Meteor.isServer) {
teamIsActive: teamIsActive,
},
});
- Meteor.call('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
+ await Meteor.callAsync('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
},
- setTeamAllFields(
+ async setTeamAllFields(
team,
teamDisplayName,
teamDesc,
@@ -235,7 +235,7 @@ if (Meteor.isServer) {
teamWebsite,
teamIsActive,
) {
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
check(team, Object);
check(teamDisplayName, String);
check(teamDesc, String);
@@ -251,7 +251,7 @@ if (Meteor.isServer) {
teamIsActive: teamIsActive,
},
});
- Meteor.call('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
+ await Meteor.callAsync('setUsersTeamsTeamDisplayName', team._id, teamDisplayName);
}
},
});
diff --git a/models/translation.js b/models/translation.js
index a901c3eeb..4a12c5389 100644
--- a/models/translation.js
+++ b/models/translation.js
@@ -59,7 +59,7 @@ Translation.attachSchema(
if (Meteor.isServer) {
Translation.allow({
insert(userId, doc) {
- const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
+ const user = Meteor.users.findOne(userId);
if (user?.isAdmin)
return true;
if (!user) {
@@ -68,7 +68,7 @@ if (Meteor.isServer) {
return doc._id === userId;
},
update(userId, doc) {
- const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
+ const user = Meteor.users.findOne(userId);
if (user?.isAdmin)
return true;
if (!user) {
@@ -77,7 +77,7 @@ if (Meteor.isServer) {
return doc._id === userId;
},
remove(userId, doc) {
- const user = ReactiveCache.getUser(userId) || ReactiveCache.getCurrentUser();
+ const user = Meteor.users.findOne(userId);
if (user?.isAdmin)
return true;
if (!user) {
@@ -89,7 +89,7 @@ if (Meteor.isServer) {
});
Meteor.methods({
- setCreateTranslation(
+ async setCreateTranslation(
language,
text,
translationText,
@@ -98,11 +98,11 @@ if (Meteor.isServer) {
check(text, String);
check(translationText, String);
- if (!ReactiveCache.getCurrentUser()?.isAdmin) {
+ if (!(await ReactiveCache.getCurrentUser())?.isAdmin) {
throw new Meteor.Error('not-authorized');
}
- const nTexts = ReactiveCache.getTranslations({ language, text }).length;
+ const nTexts = (await ReactiveCache.getTranslations({ language, text })).length;
if (nTexts > 0) {
throw new Meteor.Error('text-already-taken');
} else {
@@ -113,11 +113,11 @@ if (Meteor.isServer) {
});
}
},
- setTranslationText(translation, translationText) {
+ async setTranslationText(translation, translationText) {
check(translation, Object);
check(translationText, String);
- if (!ReactiveCache.getCurrentUser()?.isAdmin) {
+ if (!(await ReactiveCache.getCurrentUser())?.isAdmin) {
throw new Meteor.Error('not-authorized');
}
@@ -125,10 +125,10 @@ if (Meteor.isServer) {
$set: { translationText: translationText },
});
},
- deleteTranslation(translationId) {
+ async deleteTranslation(translationId) {
check(translationId, String);
- if (!ReactiveCache.getCurrentUser()?.isAdmin) {
+ if (!(await ReactiveCache.getCurrentUser())?.isAdmin) {
throw new Meteor.Error('not-authorized');
}
diff --git a/models/trelloCreator.js b/models/trelloCreator.js
index a16a7d491..71ad277ba 100644
--- a/models/trelloCreator.js
+++ b/models/trelloCreator.js
@@ -181,7 +181,7 @@ export class TrelloCreator {
}
// You must call parseActions before calling this one.
- createBoardAndLabels(trelloBoard) {
+ async createBoardAndLabels(trelloBoard) {
let color = 'blue';
if (this.getColor(trelloBoard.prefs.background) !== undefined) {
color = this.getColor(trelloBoard.prefs.background);
@@ -207,7 +207,7 @@ export class TrelloCreator {
permission: this.getPermission(trelloBoard.prefs.permissionLevel),
slug: getSlug(trelloBoard.name) || 'board',
stars: 0,
- title: Boards.uniqueTitle(trelloBoard.name),
+ title: await Boards.uniqueTitle(trelloBoard.name),
};
// now add other members
if (trelloBoard.memberships) {
@@ -775,11 +775,11 @@ export class TrelloCreator {
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
- const currentBoard = ReactiveCache.getBoard(currentBoardId);
+ const currentBoard = await ReactiveCache.getBoard(currentBoardId);
await currentBoard.archive();
}
this.parseActions(board.actions);
- const boardId = this.createBoardAndLabels(board);
+ const boardId = await this.createBoardAndLabels(board);
this.createLists(board.lists, boardId);
this.createSwimlanes(boardId);
this.createCards(board.cards, boardId);
diff --git a/models/triggers.js b/models/triggers.js
index 58da64ee9..fd8dc045b 100644
--- a/models/triggers.js
+++ b/models/triggers.js
@@ -15,13 +15,13 @@ Triggers.before.update((userId, doc, fieldNames, modifier) => {
Triggers.allow({
insert(userId, doc) {
- return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
update(userId, doc) {
- return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
remove(userId, doc) {
- return allowIsBoardAdmin(userId, ReactiveCache.getBoard(doc.boardId));
+ return allowIsBoardAdmin(userId, Boards.findOne(doc.boardId));
},
});
diff --git a/models/userPositionHistory.js b/models/userPositionHistory.js
index 0e292f0fc..dd3ac29c5 100644
--- a/models/userPositionHistory.js
+++ b/models/userPositionHistory.js
@@ -174,17 +174,17 @@ UserPositionHistory.helpers({
/**
* Can this change be undone?
*/
- canUndo() {
+ async canUndo() {
// Can undo if the entity still exists
switch (this.entityType) {
case 'card':
- return !!ReactiveCache.getCard(this.entityId);
+ return !!(await ReactiveCache.getCard(this.entityId));
case 'list':
- return !!ReactiveCache.getList(this.entityId);
+ return !!(await ReactiveCache.getList(this.entityId));
case 'swimlane':
- return !!ReactiveCache.getSwimlane(this.entityId);
+ return !!(await ReactiveCache.getSwimlane(this.entityId));
case 'checklist':
- return !!ReactiveCache.getChecklist(this.entityId);
+ return !!(await ReactiveCache.getChecklist(this.entityId));
case 'checklistItem':
return !!ChecklistItems.findOne(this.entityId);
default:
@@ -195,8 +195,8 @@ UserPositionHistory.helpers({
/**
* Undo this change
*/
- undo() {
- if (!this.canUndo()) {
+ async undo() {
+ if (!(await this.canUndo())) {
throw new Meteor.Error('cannot-undo', 'Entity no longer exists');
}
@@ -204,7 +204,7 @@ UserPositionHistory.helpers({
switch (this.entityType) {
case 'card': {
- const card = ReactiveCache.getCard(this.entityId);
+ const card = await ReactiveCache.getCard(this.entityId);
if (card) {
// Restore previous position
const boardId = this.previousBoardId || card.boardId;
@@ -224,7 +224,7 @@ UserPositionHistory.helpers({
break;
}
case 'list': {
- const list = ReactiveCache.getList(this.entityId);
+ const list = await ReactiveCache.getList(this.entityId);
if (list) {
const sort = this.previousSort !== undefined ? this.previousSort : list.sort;
const swimlaneId = this.previousSwimlaneId || list.swimlaneId;
@@ -239,7 +239,7 @@ UserPositionHistory.helpers({
break;
}
case 'swimlane': {
- const swimlane = ReactiveCache.getSwimlane(this.entityId);
+ const swimlane = await ReactiveCache.getSwimlane(this.entityId);
if (swimlane) {
const sort = this.previousSort !== undefined ? this.previousSort : swimlane.sort;
@@ -252,7 +252,7 @@ UserPositionHistory.helpers({
break;
}
case 'checklist': {
- const checklist = ReactiveCache.getChecklist(this.entityId);
+ const checklist = await ReactiveCache.getChecklist(this.entityId);
if (checklist) {
const sort = this.previousSort !== undefined ? this.previousSort : checklist.sort;
@@ -411,7 +411,7 @@ Meteor.methods({
});
},
- 'userPositionHistory.undo'(historyId) {
+ async 'userPositionHistory.undo'(historyId) {
check(historyId, String);
if (!this.userId) {
@@ -423,7 +423,7 @@ Meteor.methods({
throw new Meteor.Error('not-found', 'History entry not found');
}
- return history.undo();
+ return await history.undo();
},
'userPositionHistory.getRecent'(boardId, limit = 50) {
@@ -453,7 +453,7 @@ Meteor.methods({
).fetch();
},
- 'userPositionHistory.restoreToCheckpoint'(checkpointId) {
+ async 'userPositionHistory.restoreToCheckpoint'(checkpointId) {
check(checkpointId, String);
if (!this.userId) {
@@ -482,16 +482,16 @@ Meteor.methods({
).fetch();
let undoneCount = 0;
- changesToUndo.forEach(change => {
+ for (const change of changesToUndo) {
try {
- if (change.canUndo()) {
- change.undo();
+ if (await change.canUndo()) {
+ await change.undo();
undoneCount++;
}
} catch (e) {
console.warn('Failed to undo change:', change._id, e);
}
- });
+ };
return { undoneCount, totalChanges: changesToUndo.length };
},
diff --git a/models/users.js b/models/users.js
index 6beb0d5eb..253f1b699 100644
--- a/models/users.js
+++ b/models/users.js
@@ -1798,7 +1798,7 @@ Users.helpers({
Meteor.methods({
// Secure user removal method with proper authorization checks
- removeUser(targetUserId) {
+ async removeUser(targetUserId) {
check(targetUserId, String);
const currentUserId = Meteor.userId();
@@ -1806,12 +1806,12 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'User must be logged in');
}
- const currentUser = ReactiveCache.getUser(currentUserId);
+ const currentUser = await ReactiveCache.getUser(currentUserId);
if (!currentUser) {
throw new Meteor.Error('not-authorized', 'Current user not found');
}
- const targetUser = ReactiveCache.getUser(targetUserId);
+ const targetUser = await ReactiveCache.getUser(targetUserId);
if (!targetUser) {
throw new Meteor.Error('user-not-found', 'Target user not found');
}
@@ -1829,9 +1829,9 @@ Meteor.methods({
}
// Check if target user is the last admin
- const adminsNumber = ReactiveCache.getUsers({
+ const adminsNumber = (await ReactiveCache.getUsers({
isAdmin: true,
- }).length;
+ })).length;
if (adminsNumber === 1 && targetUser.isAdmin) {
throw new Meteor.Error('not-authorized', 'Cannot delete the last administrator');
@@ -1841,7 +1841,7 @@ Meteor.methods({
Users.remove(targetUserId);
return { success: true, message: 'User deleted successfully' };
},
- editUser(targetUserId, updateData) {
+ async editUser(targetUserId, updateData) {
check(targetUserId, String);
check(updateData, Object);
@@ -1850,7 +1850,7 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'User must be logged in');
}
- const currentUser = ReactiveCache.getUser(currentUserId);
+ const currentUser = await ReactiveCache.getUser(currentUserId);
if (!currentUser) {
throw new Meteor.Error('not-authorized', 'Current user not found');
}
@@ -1860,7 +1860,7 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'Only administrators can edit other users');
}
- const targetUser = ReactiveCache.getUser(targetUserId);
+ const targetUser = await ReactiveCache.getUser(targetUserId);
if (!targetUser) {
throw new Meteor.Error('user-not-found', 'Target user not found');
}
@@ -1894,9 +1894,9 @@ Meteor.methods({
Users.update(targetUserId, { $set: updateObject });
},
- setListSortBy(value) {
+ async setListSortBy(value) {
check(value, String);
- ReactiveCache.getCurrentUser().setListSortBy(value);
+ (await ReactiveCache.getCurrentUser()).setListSortBy(value);
},
setAvatarUrl(avatarUrl) {
check(avatarUrl, String);
@@ -1943,8 +1943,8 @@ Meteor.methods({
Users.update(this.userId, { $set: { 'profile.GreyIcons': newValue } });
return newValue;
},
- toggleDesktopDragHandles() {
- const user = ReactiveCache.getCurrentUser();
+ async toggleDesktopDragHandles() {
+ const user = await ReactiveCache.getCurrentUser();
user.toggleDesktopHandles(user.hasShowDesktopDragHandles());
},
// Spaces: create a new space under parentId (or root when null)
@@ -2015,16 +2015,16 @@ Meteor.methods({
});
return true;
},
- toggleHideCheckedItems() {
- const user = ReactiveCache.getCurrentUser();
+ async toggleHideCheckedItems() {
+ const user = await ReactiveCache.getCurrentUser();
user.toggleHideCheckedItems();
},
- toggleCustomFieldsGrid() {
- const user = ReactiveCache.getCurrentUser();
+ async toggleCustomFieldsGrid() {
+ const user = await ReactiveCache.getCurrentUser();
user.toggleFieldsGrid(user.hasCustomFieldsGrid());
},
- toggleCardMaximized() {
- const user = ReactiveCache.getCurrentUser();
+ async toggleCardMaximized() {
+ const user = await ReactiveCache.getCurrentUser();
user.toggleCardMaximized(user.hasCardMaximized());
},
setCardCollapsed(value) {
@@ -2032,32 +2032,32 @@ Meteor.methods({
if (!this.userId) throw new Meteor.Error('not-logged-in');
Users.update(this.userId, { $set: { 'profile.cardCollapsed': value } });
},
- toggleMinicardLabelText() {
- const user = ReactiveCache.getCurrentUser();
+ async toggleMinicardLabelText() {
+ const user = await ReactiveCache.getCurrentUser();
user.toggleLabelText(user.hasHiddenMinicardLabelText());
},
- toggleRescueCardDescription() {
- const user = ReactiveCache.getCurrentUser();
+ async toggleRescueCardDescription() {
+ const user = await ReactiveCache.getCurrentUser();
user.toggleRescueCardDescription(user.hasRescuedCardDescription());
},
- changeLimitToShowCardsCount(limit) {
+ async changeLimitToShowCardsCount(limit) {
check(limit, Number);
- ReactiveCache.getCurrentUser().setShowCardsCountAt(limit);
+ (await ReactiveCache.getCurrentUser()).setShowCardsCountAt(limit);
},
- changeStartDayOfWeek(startDay) {
+ async changeStartDayOfWeek(startDay) {
check(startDay, Number);
- ReactiveCache.getCurrentUser().setStartDayOfWeek(startDay);
+ (await ReactiveCache.getCurrentUser()).setStartDayOfWeek(startDay);
},
- changeDateFormat(dateFormat) {
+ async changeDateFormat(dateFormat) {
check(dateFormat, String);
- ReactiveCache.getCurrentUser().setDateFormat(dateFormat);
+ (await ReactiveCache.getCurrentUser()).setDateFormat(dateFormat);
},
- applyListWidth(boardId, listId, width, constraint) {
+ async applyListWidth(boardId, listId, width, constraint) {
check(boardId, String);
check(listId, String);
check(width, Number);
check(constraint, Number);
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
user.setListWidth(boardId, listId, width);
user.setListConstraint(boardId, listId, constraint);
},
@@ -2081,11 +2081,11 @@ Meteor.methods({
},
});
},
- applySwimlaneHeight(boardId, swimlaneId, height) {
+ async applySwimlaneHeight(boardId, swimlaneId, height) {
check(boardId, String);
check(swimlaneId, String);
check(height, Number);
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
user.setSwimlaneHeight(boardId, swimlaneId, height);
},
@@ -2110,42 +2110,42 @@ Meteor.methods({
});
},
- applySwimlaneHeightToStorage(boardId, swimlaneId, height) {
+ async applySwimlaneHeightToStorage(boardId, swimlaneId, height) {
check(boardId, String);
check(swimlaneId, String);
check(height, Number);
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
if (user) {
user.setSwimlaneHeightToStorage(boardId, swimlaneId, height);
}
// For non-logged-in users, the client-side code will handle localStorage
},
- applyListWidthToStorage(boardId, listId, width, constraint) {
+ async applyListWidthToStorage(boardId, listId, width, constraint) {
check(boardId, String);
check(listId, String);
check(width, Number);
check(constraint, Number);
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
if (user) {
user.setListWidthToStorage(boardId, listId, width);
user.setListConstraintToStorage(boardId, listId, constraint);
}
// For non-logged-in users, the client-side code will handle localStorage
},
- setZoomLevel(level) {
+ async setZoomLevel(level) {
check(level, Number);
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
user.setZoomLevel(level);
},
- setMobileMode(enabled) {
+ async setMobileMode(enabled) {
check(enabled, Boolean);
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
user.setMobileMode(enabled);
},
- setBoardView(view) {
+ async setBoardView(view) {
check(view, String);
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
if (!user) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
@@ -2155,7 +2155,7 @@ Meteor.methods({
if (Meteor.isServer) {
Meteor.methods({
- setCreateUser(
+ async setCreateUser(
fullname,
username,
initials,
@@ -2185,13 +2185,13 @@ if (Meteor.isServer) {
initials.includes('/')) {
return false;
}
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
- const nUsersWithUsername = ReactiveCache.getUsers({
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
+ const nUsersWithUsername = (await ReactiveCache.getUsers({
username,
- }).length;
- const nUsersWithEmail = ReactiveCache.getUsers({
+ })).length;
+ const nUsersWithEmail = (await ReactiveCache.getUsers({
email,
- }).length;
+ })).length;
if (nUsersWithUsername > 0) {
throw new Meteor.Error('username-already-taken');
} else if (nUsersWithEmail > 0) {
@@ -2206,8 +2206,8 @@ if (Meteor.isServer) {
from: 'admin',
});
const user =
- ReactiveCache.getUser(username) ||
- ReactiveCache.getUser({ username });
+ await ReactiveCache.getUser(username) ||
+ await ReactiveCache.getUser({ username });
if (user) {
Users.update(user._id, {
$set: {
@@ -2222,7 +2222,7 @@ if (Meteor.isServer) {
}
}
},
- setUsername(username, userId) {
+ async setUsername(username, userId) {
check(username, String);
check(userId, String);
// Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176
@@ -2231,10 +2231,10 @@ if (Meteor.isServer) {
userId.includes('/')) {
return false;
}
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
- const nUsersWithUsername = ReactiveCache.getUsers({
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
+ const nUsersWithUsername = (await ReactiveCache.getUsers({
username,
- }).length;
+ })).length;
if (nUsersWithUsername > 0) {
throw new Meteor.Error('username-already-taken');
} else {
@@ -2246,7 +2246,7 @@ if (Meteor.isServer) {
}
}
},
- setEmail(email, userId) {
+ async setEmail(email, userId) {
check(email, String);
check(username, String);
// Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176
@@ -2255,11 +2255,11 @@ if (Meteor.isServer) {
email.includes('/')) {
return false;
}
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
if (Array.isArray(email)) {
email = email.shift();
}
- const existingUser = ReactiveCache.getUser(
+ const existingUser = await ReactiveCache.getUser(
{
'emails.address': email,
},
@@ -2285,7 +2285,7 @@ if (Meteor.isServer) {
}
}
},
- setUsernameAndEmail(username, email, userId) {
+ async setUsernameAndEmail(username, email, userId) {
check(username, String);
check(email, String);
check(userId, String);
@@ -2296,22 +2296,22 @@ if (Meteor.isServer) {
userId.includes('/')) {
return false;
}
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
if (Array.isArray(email)) {
email = email.shift();
}
- Meteor.call('setUsername', username, userId);
- Meteor.call('setEmail', email, userId);
+ await Meteor.callAsync('setUsername', username, userId);
+ await Meteor.callAsync('setEmail', email, userId);
}
},
- setPassword(newPassword, userId) {
+ async setPassword(newPassword, userId) {
check(userId, String);
check(newPassword, String);
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
Accounts.setPassword(userId, newPassword);
}
},
- setEmailVerified(email, verified, userId) {
+ async setEmailVerified(email, verified, userId) {
check(email, String);
check(verified, Boolean);
check(userId, String);
@@ -2321,7 +2321,7 @@ if (Meteor.isServer) {
userId.includes('/')) {
return false;
}
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
Users.update(userId, {
$set: {
emails: [
@@ -2334,7 +2334,7 @@ if (Meteor.isServer) {
});
}
},
- setInitials(initials, userId) {
+ async setInitials(initials, userId) {
check(initials, String);
check(userId, String);
// Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176
@@ -2343,7 +2343,7 @@ if (Meteor.isServer) {
userId.includes('/')) {
return false;
}
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
Users.update(userId, {
$set: {
'profile.initials': initials,
@@ -2352,7 +2352,7 @@ if (Meteor.isServer) {
}
},
// we accept userId, username, email
- inviteUserToBoard(username, boardId) {
+ async inviteUserToBoard(username, boardId) {
check(username, String);
check(boardId, String);
// Prevent Hyperlink Injection https://github.com/wekan/wekan/issues/5176
@@ -2361,8 +2361,8 @@ if (Meteor.isServer) {
boardId.includes('/')) {
return false;
}
- const inviter = ReactiveCache.getCurrentUser();
- const board = ReactiveCache.getBoard(boardId);
+ const inviter = await ReactiveCache.getCurrentUser();
+ const board = await ReactiveCache.getBoard(boardId);
const member = _.find(board.members, function(member) { return member.userId === inviter._id; });
if (!member) throw new Meteor.Error('error-board-notAMember');
const allowInvite = member.isActive;
@@ -2375,7 +2375,7 @@ if (Meteor.isServer) {
const posAt = username.indexOf('@');
let user = null;
if (posAt >= 0) {
- user = ReactiveCache.getUser({
+ user = await ReactiveCache.getUser({
emails: {
$elemMatch: {
address: username,
@@ -2384,15 +2384,15 @@ if (Meteor.isServer) {
});
} else {
user =
- ReactiveCache.getUser(username) ||
- ReactiveCache.getUser({ username });
+ await ReactiveCache.getUser(username) ||
+ await ReactiveCache.getUser({ username });
}
if (user) {
if (user._id === inviter._id)
throw new Meteor.Error('error-user-notAllowSelf');
} else {
if (posAt <= 0) throw new Meteor.Error('error-user-doesNotExist');
- if (ReactiveCache.getCurrentSetting().disableRegistration) {
+ if ((await ReactiveCache.getCurrentSetting()).disableRegistration) {
throw new Meteor.Error('error-user-notCreated');
}
// Set in lowercase email before creating account
@@ -2418,7 +2418,7 @@ if (Meteor.isServer) {
});
}
Accounts.sendEnrollmentEmail(newUserId);
- user = ReactiveCache.getUser(newUserId);
+ user = await ReactiveCache.getUser(newUserId);
}
const memberIndex = board.members.findIndex(m => m.userId === user._id);
@@ -2431,7 +2431,7 @@ if (Meteor.isServer) {
//Check if there is a subtasks board
if (board.subtasksDefaultBoardId) {
- const subBoard = ReactiveCache.getBoard(board.subtasksDefaultBoardId);
+ const subBoard = await ReactiveCache.getBoard(board.subtasksDefaultBoardId);
//If there is, also add user to that board
if (subBoard) {
const subMemberIndex = subBoard.members.findIndex(m => m.userId === user._id);
@@ -2495,35 +2495,35 @@ if (Meteor.isServer) {
email: user.emails[0].address,
};
},
- impersonate(userId) {
+ async impersonate(userId) {
check(userId, String);
- if (!ReactiveCache.getUser(userId))
+ if (!(await ReactiveCache.getUser(userId)))
throw new Meteor.Error(404, 'User not found');
- if (!ReactiveCache.getCurrentUser().isAdmin)
+ if (!(await ReactiveCache.getCurrentUser()).isAdmin)
throw new Meteor.Error(403, 'Permission denied');
ImpersonatedUsers.insert({
- adminId: ReactiveCache.getCurrentUser()._id,
+ adminId: (await ReactiveCache.getCurrentUser())._id,
userId: userId,
reason: 'clickedImpersonate',
});
this.setUserId(userId);
},
- isImpersonated(userId) {
+ async isImpersonated(userId) {
check(userId, String);
- const isImpersonated = ReactiveCache.getImpersonatedUser({ userId: userId });
+ const isImpersonated = await ReactiveCache.getImpersonatedUser({ userId: userId });
return isImpersonated;
},
- setUsersTeamsTeamDisplayName(teamId, teamDisplayName) {
+ async setUsersTeamsTeamDisplayName(teamId, teamDisplayName) {
check(teamId, String);
check(teamDisplayName, String);
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
- ReactiveCache.getUsers({
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
+ for (const user of await ReactiveCache.getUsers({
teams: {
$elemMatch: { teamId: teamId },
},
- }).forEach((user) => {
+ })) {
Users.update(
{
_id: user._id,
@@ -2537,18 +2537,18 @@ if (Meteor.isServer) {
},
},
);
- });
+ }
}
},
- setUsersOrgsOrgDisplayName(orgId, orgDisplayName) {
+ async setUsersOrgsOrgDisplayName(orgId, orgDisplayName) {
check(orgId, String);
check(orgDisplayName, String);
- if (ReactiveCache.getCurrentUser()?.isAdmin) {
- ReactiveCache.getUsers({
+ if ((await ReactiveCache.getCurrentUser())?.isAdmin) {
+ for (const user of await ReactiveCache.getUsers({
orgs: {
$elemMatch: { orgId: orgId },
},
- }).forEach((user) => {
+ })) {
Users.update(
{
_id: user._id,
@@ -2562,12 +2562,12 @@ if (Meteor.isServer) {
},
},
);
- });
+ }
}
},
});
- Accounts.onCreateUser((options, user) => {
- const userCount = ReactiveCache.getUsers({}, {}, true).count();
+ Accounts.onCreateUser(async (options, user) => {
+ const userCount = (await ReactiveCache.getUsers({}, {}, true)).count();
user.isAdmin = userCount === 0;
if (user.services.oidc) {
@@ -2607,7 +2607,7 @@ if (Meteor.isServer) {
user.authenticationMethod = 'oauth2';
// see if any existing user has this email address or username, otherwise create new
- const existingUser = ReactiveCache.getUser({
+ const existingUser = await ReactiveCache.getUser({
$or: [
{
'emails.address': email,
@@ -2641,7 +2641,7 @@ if (Meteor.isServer) {
return user;
}
- const disableRegistration = ReactiveCache.getCurrentSetting().disableRegistration;
+ const disableRegistration = (await ReactiveCache.getCurrentSetting()).disableRegistration;
// If this is the first Authentication by the ldap and self registration disabled
if (disableRegistration && options && options.ldap) {
user.authenticationMethod = 'ldap';
@@ -2659,7 +2659,7 @@ if (Meteor.isServer) {
'The invitation code is required',
);
}
- const invitationCode = ReactiveCache.getInvitationCode({
+ const invitationCode = await ReactiveCache.getInvitationCode({
code: options.profile.invitationcode,
email: options.email,
valid: true,
@@ -2702,8 +2702,8 @@ const addCronJob = _.debounce(
SyncedCron.add({
name: 'notification_cleanup',
schedule: (parser) => parser.text('every 1 days'),
- job: () => {
- for (const user of ReactiveCache.getUsers()) {
+ job: async () => {
+ for (const user of await ReactiveCache.getUsers()) {
if (!user.profile || !user.profile.notifications) continue;
for (const notification of user.profile.notifications) {
if (notification.read) {
@@ -2938,9 +2938,9 @@ if (Meteor.isServer) {
});
}
- Users.after.insert((userId, doc) => {
+ Users.after.insert(async (userId, doc) => {
// HACK
- doc = ReactiveCache.getUser(doc._id);
+ doc = await ReactiveCache.getUser(doc._id);
if (doc.createdThroughApi) {
// The admin user should be able to create a user despite disabling registration because
// it is two different things (registration and creation).
@@ -2957,19 +2957,19 @@ if (Meteor.isServer) {
}
//invite user to corresponding boards
- const disableRegistration = ReactiveCache.getCurrentSetting().disableRegistration;
+ const disableRegistration = (await ReactiveCache.getCurrentSetting()).disableRegistration;
// If ldap, bypass the inviation code if the self registration isn't allowed.
// TODO : pay attention if ldap field in the user model change to another content ex : ldap field to connection_type
if (doc.authenticationMethod !== 'ldap' && disableRegistration) {
let invitationCode = null;
if (doc.authenticationMethod.toLowerCase() == 'oauth2') {
// OIDC authentication mode
- invitationCode = ReactiveCache.getInvitationCode({
+ invitationCode = await ReactiveCache.getInvitationCode({
email: doc.emails[0].address.toLowerCase(),
valid: true,
});
} else {
- invitationCode = ReactiveCache.getInvitationCode({
+ invitationCode = await ReactiveCache.getInvitationCode({
code: doc.profile.icode,
valid: true,
});
@@ -2977,15 +2977,15 @@ if (Meteor.isServer) {
if (!invitationCode) {
throw new Meteor.Error('error-invitation-code-not-exist');
} else {
- invitationCode.boardsToBeInvited.forEach((boardId) => {
- const board = ReactiveCache.getBoard(boardId);
+ for (const boardId of invitationCode.boardsToBeInvited) {
+ const board = await ReactiveCache.getBoard(boardId);
const memberIndex = board.members.findIndex(m => m.userId === doc._id);
if (memberIndex >= 0) {
Boards.update(boardId, { $set: { [`members.${memberIndex}.isActive`]: true } });
} else {
Boards.update(boardId, { $push: { members: { userId: doc._id, isAdmin: false, isActive: true, isNoComments: false, isCommentOnly: false, isWorker: false, isNormalAssignedOnly: false, isCommentAssignedOnly: false, isReadOnly: false, isReadAssignedOnly: false } } });
}
- });
+ }
if (!doc.profile) {
doc.profile = {};
}
@@ -3026,16 +3026,16 @@ if (Meteor.isServer) {
* @summary returns the current user
* @return_type Users
*/
- JsonRoutes.add('GET', '/api/user', function (req, res) {
+ JsonRoutes.add('GET', '/api/user', async function (req, res) {
try {
Authentication.checkLoggedIn(req.userId);
- const data = ReactiveCache.getUser({
+ const data = await ReactiveCache.getUser({
_id: req.userId,
});
delete data.services;
// get all boards where the user is member of
- let boards = ReactiveCache.getBoards(
+ let boards = await ReactiveCache.getBoards(
{
type: 'board',
'members.userId': req.userId,
@@ -3108,22 +3108,22 @@ if (Meteor.isServer) {
* @param {string} userId the user ID or username
* @return_type Users
*/
- JsonRoutes.add('GET', '/api/users/:userId', function (req, res) {
+ JsonRoutes.add('GET', '/api/users/:userId', async function (req, res) {
try {
Authentication.checkUserId(req.userId);
let id = req.params.userId;
- let user = ReactiveCache.getUser({
+ let user = await ReactiveCache.getUser({
_id: id,
});
if (!user) {
- user = ReactiveCache.getUser({
+ user = await ReactiveCache.getUser({
username: id,
});
id = user._id;
}
// get all boards where the user is member of
- let boards = ReactiveCache.getBoards(
+ let boards = await ReactiveCache.getBoards(
{
type: 'board',
'members.userId': id,
@@ -3177,12 +3177,12 @@ if (Meteor.isServer) {
Authentication.checkUserId(req.userId);
const id = req.params.userId;
const action = req.body.action;
- let data = ReactiveCache.getUser({
+ let data = await ReactiveCache.getUser({
_id: id,
});
if (data !== undefined) {
if (action === 'takeOwnership') {
- const boards = ReactiveCache.getBoards(
+ const boards = await ReactiveCache.getBoards(
{
'members.userId': id,
'members.isAdmin': true,
@@ -3229,7 +3229,7 @@ if (Meteor.isServer) {
},
);
}
- data = ReactiveCache.getUser(id);
+ data = await ReactiveCache.getUser(id);
}
}
JsonRoutes.sendResult(res, {
@@ -3272,19 +3272,19 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/members/:userId/add',
- function (req, res) {
+ async function (req, res) {
try {
Authentication.checkUserId(req.userId);
const userId = req.params.userId;
const boardId = req.params.boardId;
const action = req.body.action;
const { isAdmin, isNoComments, isCommentOnly, isWorker, isNormalAssignedOnly, isCommentAssignedOnly, isReadOnly, isReadAssignedOnly } = req.body;
- let data = ReactiveCache.getUser(userId);
+ let data = await ReactiveCache.getUser(userId);
if (data !== undefined) {
if (action === 'add') {
- data = ReactiveCache.getBoards({
+ data = (await ReactiveCache.getBoards({
_id: boardId,
- }).map(function (board) {
+ })).map(function (board) {
const hasMember = board.members.some(m => m.userId === userId && m.isActive);
if (!hasMember) {
const memberIndex = board.members.findIndex(m => m.userId === userId);
@@ -3347,18 +3347,18 @@ if (Meteor.isServer) {
JsonRoutes.add(
'POST',
'/api/boards/:boardId/members/:userId/remove',
- function (req, res) {
+ async function (req, res) {
try {
Authentication.checkUserId(req.userId);
const userId = req.params.userId;
const boardId = req.params.boardId;
const action = req.body.action;
- let data = ReactiveCache.getUser(userId);
+ let data = await ReactiveCache.getUser(userId);
if (data !== undefined) {
if (action === 'remove') {
- data = ReactiveCache.getBoards({
+ data = (await ReactiveCache.getBoards({
_id: boardId,
- }).map(function (board) {
+ })).map(function (board) {
const hasMember = board.members.some(m => m.userId === userId && m.isActive);
if (hasMember) {
const memberIndex = board.members.findIndex(m => m.userId === userId);
@@ -3593,7 +3593,7 @@ if (Meteor.isServer) {
check(userData, Object);
return sanitizeUserForSearch(userData);
},
- searchUsers(query, boardId) {
+ async searchUsers(query, boardId) {
check(query, String);
check(boardId, String);
@@ -3601,8 +3601,8 @@ if (Meteor.isServer) {
throw new Meteor.Error('not-logged-in', 'User must be logged in');
}
- const currentUser = ReactiveCache.getCurrentUser();
- const board = ReactiveCache.getBoard(boardId);
+ const currentUser = await ReactiveCache.getCurrentUser();
+ const board = await ReactiveCache.getBoard(boardId);
// Check if current user is a member of the board
const member = _.find(board.members, function(member) { return member.userId === currentUser._id; });
@@ -3615,7 +3615,7 @@ if (Meteor.isServer) {
}
const searchRegex = new RegExp(query, 'i');
- const users = ReactiveCache.getUsers({
+ const users = await ReactiveCache.getUsers({
$or: [
{ username: searchRegex },
{ 'profile.fullname': searchRegex },
diff --git a/models/wekanCreator.js b/models/wekanCreator.js
index 0580ca139..96e1f30fe 100644
--- a/models/wekanCreator.js
+++ b/models/wekanCreator.js
@@ -244,14 +244,14 @@ export class WekanCreator {
]);
}
- getMembersToMap(data) {
+ async getMembersToMap(data) {
// we will work on the list itself (an ordered array of objects) when a
// mapping is done, we add a 'wekan' field to the object representing the
// imported member
const membersToMap = data.members;
const users = data.users;
// auto-map based on username
- membersToMap.forEach(importedMember => {
+ for (const importedMember of membersToMap) {
importedMember.id = importedMember.userId;
delete importedMember.userId;
const user = users.filter(user => {
@@ -261,11 +261,11 @@ export class WekanCreator {
importedMember.fullName = user.profile.fullname;
}
importedMember.username = user.username;
- const wekanUser = ReactiveCache.getUser({ username: importedMember.username });
+ const wekanUser = await ReactiveCache.getUser({ username: importedMember.username });
if (wekanUser) {
importedMember.wekanId = wekanUser._id;
}
- });
+ }
return membersToMap;
}
@@ -280,7 +280,7 @@ export class WekanCreator {
}
// You must call parseActions before calling this one.
- createBoardAndLabels(boardToImport) {
+ async createBoardAndLabels(boardToImport) {
const boardToCreate = {
archived: boardToImport.archived,
color: boardToImport.color,
@@ -304,7 +304,7 @@ export class WekanCreator {
permission: boardToImport.permission,
slug: getSlug(boardToImport.title) || 'board',
stars: 0,
- title: Boards.uniqueTitle(boardToImport.title),
+ title: await Boards.uniqueTitle(boardToImport.title),
};
// now add other members
if (boardToImport.members) {
@@ -665,8 +665,8 @@ export class WekanCreator {
});
}
- createSubtasks(wekanCards) {
- wekanCards.forEach(card => {
+ async createSubtasks(wekanCards) {
+ for (const card of wekanCards) {
// get new id of card (in created / new board)
const cardIdInNewBoard = this.cards[card._id];
@@ -683,7 +683,7 @@ export class WekanCreator {
: card.parentId;
//if the parent card exists, proceed
- if (ReactiveCache.getCard(parentIdInNewBoard)) {
+ if (await ReactiveCache.getCard(parentIdInNewBoard)) {
//set parent id of the card in the new board to the new id of the parent
Cards.direct.update(cardIdInNewBoard, {
$set: {
@@ -691,7 +691,7 @@ export class WekanCreator {
},
});
}
- });
+ }
}
createChecklists(wekanChecklists) {
@@ -978,16 +978,16 @@ export class WekanCreator {
Meteor.settings.public &&
Meteor.settings.public.sandstorm;
if (isSandstorm && currentBoardId) {
- const currentBoard = ReactiveCache.getBoard(currentBoardId);
+ const currentBoard = await ReactiveCache.getBoard(currentBoardId);
await currentBoard.archive();
}
this.parseActivities(board);
- const boardId = this.createBoardAndLabels(board);
+ const boardId = await this.createBoardAndLabels(board);
this.createLists(board.lists, boardId);
this.createSwimlanes(board.swimlanes, boardId);
this.createCustomFields(board.customFields, boardId);
this.createCards(board.cards, boardId);
- this.createSubtasks(board.cards);
+ await this.createSubtasks(board.cards);
this.createChecklists(board.checklists);
this.createChecklistItems(board.checklistItems);
this.importActivities(board.activities, boardId);
diff --git a/models/wekanmapper.js b/models/wekanmapper.js
index 3dc449c7a..be849af7c 100644
--- a/models/wekanmapper.js
+++ b/models/wekanmapper.js
@@ -1,13 +1,13 @@
import { ReactiveCache } from '/imports/reactiveCache';
-export function getMembersToMap(data) {
+export async function getMembersToMap(data) {
// we will work on the list itself (an ordered array of objects) when a
// mapping is done, we add a 'wekan' field to the object representing the
// imported member
const membersToMap = data.members;
const users = data.users;
// auto-map based on username
- membersToMap.forEach(importedMember => {
+ for (const importedMember of membersToMap) {
importedMember.id = importedMember.userId;
delete importedMember.userId;
const user = users.filter(user => {
@@ -17,10 +17,10 @@ export function getMembersToMap(data) {
importedMember.fullName = user.profile.fullname;
}
importedMember.username = user.username;
- const wekanUser = ReactiveCache.getUser({ username: importedMember.username });
+ const wekanUser = await ReactiveCache.getUser({ username: importedMember.username });
if (wekanUser) {
importedMember.wekanId = wekanUser._id;
}
- });
+ }
return membersToMap;
}
diff --git a/npm-packages/meteor-reactive-cache/.gitignore b/npm-packages/meteor-reactive-cache/.gitignore
new file mode 100644
index 000000000..114623a68
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/.gitignore
@@ -0,0 +1,8 @@
+*.swp
+*~
+*.iml
+.*.haste_cache.*
+.DS_Store
+.idea
+npm-debug.log
+node_modules
diff --git a/npm-packages/meteor-reactive-cache/.travis.yml b/npm-packages/meteor-reactive-cache/.travis.yml
new file mode 100644
index 000000000..a9f8f263b
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/.travis.yml
@@ -0,0 +1,22 @@
+sudo: false
+language: node_js
+node_js:
+ - "4.8.4"
+
+addons:
+ apt:
+ packages:
+ - xvfb
+install:
+ - export DISPLAY=':99.0'
+ - Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
+ - npm install
+
+before_install:
+ - curl https://install.meteor.com/?release=1.6.1.1 | /bin/sh
+ - export PATH="$HOME/.meteor:$PATH"
+
+script:
+ - (cd tests && meteor npm install)
+ - npm run lint
+ - npm test
diff --git a/npm-packages/meteor-reactive-cache/CHANGELOG.md b/npm-packages/meteor-reactive-cache/CHANGELOG.md
new file mode 100644
index 000000000..12c262adc
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/CHANGELOG.md
@@ -0,0 +1,50 @@
+# ChangeLog
+
+# v1.0.7 2026-01-05 meteor-reactive-cache release
+
+This release adds the following updates:
+
+- Updated dependencies.
+ [Part 1](https://github.com/wekan/meteor-reactive-cache/commit/eed764fb54428224a970e96e5ea12a64470ea1d2),
+ [Part 2](https://github.com/wekan/meteor-reactive-cache/commit/32a496271ed45c2b3a8a26d6bde878e14b113637).
+ Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v1.0.6 2023-07-19 meteor-reactive-cache release
+
+This release adds the following updates:
+
+- [Updated dependencies](https://github.com/wekan/meteor-reactive-cache/commit/63c2ecc549e5c985be70af70a11ae4ac614e3455).
+ Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v1.0.5 2023-07-19 meteor-reactive-cache release
+
+This release fixes the following bugs:
+
+- [Fixed using newer fixed @wekanteam/meteor-globals package](https://github.com/wekan/meteor-reactive-cache/commit/1fe7a07c8607419c86bceabce5ca024432435fc2).
+ Thanks to xet7.
+- [Renamed publish to release. Added release script](https://github.com/wekan/meteor-reactive-cache/commit/e43c232453c0d7267576c82c6f6463ede34a2c55).
+ Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v1.0.4 2023-07-19 meteor-reactive-cache release
+
+This release fixes the following bugs:
+
+- [Added PUBLISH.md about missing steps to publish npm package. Added missing published files also to git repo](https://github.com/wekan/meteor-reactive-cache/commit/b355ca815ebf9389d3a0dd57cddee7938aa4bf0c).
+ Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
+
+# v1.0.3 2023-07-19 meteor-reactive-cache release
+
+This release adds the following updates:
+
+- [Updated dependencies in this package and @wekanteam/meteor-globals](https://github.com/wekan/meteor-reactive-cache/commit/659a4e51c11bd95ec2fc5dccfb0bf1003ca7737d).
+ Thanks to xet7.
+
+Thanks to above GitHub users for their contributions and translators for their translations.
diff --git a/npm-packages/meteor-reactive-cache/LICENSE b/npm-packages/meteor-reactive-cache/LICENSE
new file mode 100644
index 000000000..ef913fe7c
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Max Nowack
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/npm-packages/meteor-reactive-cache/README.md b/npm-packages/meteor-reactive-cache/README.md
new file mode 100644
index 000000000..cae89e134
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/README.md
@@ -0,0 +1,72 @@
+# meteor-reactive-cache [](https://travis-ci.org/maxnowack/meteor-reactive-cache)
+Utilities for caching reactive data
+
+### Installation
+````bash
+ $ npm install --save meteor-reactive-cache
+````
+
+### Usage
+
+#### `ReactiveCache(compare: function)`
+A simple reactive cache. It haves the same API like a `ReactiveDict`, but the values are getting deleted if all wrapped computations are stopped.
+
+````es6
+import { Tracker } from 'meteor/tracker'
+import { ReactiveCache } from 'meteor-reactive-cache'
+
+const reactiveCache = new ReactiveCache(/* compareFn */);
+reactiveCache.set('foo', 'bar');
+const computation = Tracker.autorun(() => {
+ reactiveCache.get('foo'); // reactive!
+})
+reactiveCache.set('foo', 'new bar');
+computation.stop(); // keys will be invalidated if they don't have reactive dependants
+reactiveCache.get('foo'); // undefined
+````
+
+#### `DataCache(resolve: function, { timeout: number, compare: function })`
+Provides a simple reactive data cache, by passing in a function, that resolves a key to data in a reactive context.
+
+````es6
+import { Tracker } from 'meteor/tracker'
+import { DataCache } from 'meteor-reactive-cache'
+
+const dataCache = new DataCache((key) => {
+ // do some expensive reactive work here, which returns the same data for the same key.
+ // this function will only be executed if a reactive dependency changes or the requested key isn't cached.
+
+})
+const computation = Tracker.autorun(() => {
+ reactiveCache.get('foo'); // reactive!
+})
+computation.stop(); // keys will be invalidated if they don't have reactive dependants
+reactiveCache.get('foo'); // undefined
+````
+
+#### `reactiveField(resolve: function, { timeout: number, compare: function })`
+Like DataCache, but with a much simpler API and support for multiple function parameters.
+
+````es6
+import { Tracker } from 'meteor/tracker'
+import { reactiveField } from 'meteor-reactive-cache'
+
+const field = reactiveField((val1, val2, val3) => {
+ // …
+})
+const computation = Tracker.autorun(() => {
+ field('foo', 'bar', 1234); // reactive!
+})
+````
+
+
+
+
+## License
+Licensed under MIT license. Copyright (c) 2017 Max Nowack
+
+## Contributions
+Contributions are welcome. Please open issues and/or file Pull Requests.
+
+## Maintainers
+- Max Nowack ([maxnowack](https://github.com/maxnowack))
diff --git a/npm-packages/meteor-reactive-cache/RELEASE.md b/npm-packages/meteor-reactive-cache/RELEASE.md
new file mode 100644
index 000000000..9c4db5658
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/RELEASE.md
@@ -0,0 +1,14 @@
+# Steps to release
+
+## 1. Update these for new version info
+
+- package.json: updated dependencies and new version info
+- package-lock.json: updated dependencies and 2x new version info
+- CHANGELOG.md: Newest changes and new version info
+- Optionally: source code at src/ directory
+
+## 2. Release new version number
+
+```
+./release.sh 1.0.5
+```
diff --git a/npm-packages/meteor-reactive-cache/dist/DataCache.js b/npm-packages/meteor-reactive-cache/dist/DataCache.js
new file mode 100644
index 000000000..e61e3cb60
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/dist/DataCache.js
@@ -0,0 +1,92 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports["default"] = void 0;
+var _bindEnvironment = _interopRequireDefault(require("./meteor/bindEnvironment"));
+var _tracker = _interopRequireDefault(require("./meteor/tracker"));
+var _ReactiveCache = _interopRequireDefault(require("./ReactiveCache"));
+function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
+function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
+function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
+function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
+function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
+function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
+function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
+function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
+function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
+function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
+var DataCache = exports["default"] = /*#__PURE__*/function () {
+ function DataCache(getData, options) {
+ _classCallCheck(this, DataCache);
+ this.options = _objectSpread({
+ timeout: 60 * 1000
+ }, typeof options === 'function' ? {
+ compare: options
+ } : options);
+ this.getData = getData;
+ this.cache = new _ReactiveCache["default"](this.options.compare, function () {
+ return false;
+ });
+ this.timeouts = {};
+ this.computations = {};
+ }
+ return _createClass(DataCache, [{
+ key: "ensureComputation",
+ value: function ensureComputation(key) {
+ var _this = this;
+ if (this.timeouts[key]) {
+ clearTimeout(this.timeouts[key]);
+ delete this.timeouts[key];
+ }
+ if (this.computations[key] && !this.computations[key].stopped) return;
+ this.computations[key] = _tracker["default"].nonreactive(function () {
+ return _tracker["default"].autorun(function () {
+ _this.cache.set(key, _this.getData(key));
+ });
+ });
+
+ // stop the computation if the key doesn't have any dependants
+ this.computations[key].onInvalidate(function () {
+ return _this.checkStop(key);
+ });
+ }
+ }, {
+ key: "checkStop",
+ value: function checkStop(key) {
+ var _this2 = this;
+ if (this.cache.ensureDependency(key).hasDependents()) return;
+ if (this.timeouts[key]) {
+ clearTimeout(this.timeouts[key]);
+ delete this.timeouts[key];
+ }
+ this.timeouts[key] = setTimeout((0, _bindEnvironment["default"])(function () {
+ if (!_this2.computations[key]) return;
+ _this2.computations[key].stop();
+ delete _this2.computations[key];
+ _this2.cache.del(key);
+ }), this.options.timeout);
+ }
+ }, {
+ key: "get",
+ value: function get(key) {
+ var _this3 = this;
+ if (!_tracker["default"].currentComputation) {
+ var _data = this.cache.get(key);
+ if (_data == null) {
+ _data = this.getData(key);
+ this.cache.set(key, _data);
+ this.checkStop(key);
+ }
+ return _data;
+ }
+ this.ensureComputation(key);
+ var data = this.cache.get(key);
+ _tracker["default"].currentComputation.onStop(function () {
+ return _this3.checkStop(key);
+ });
+ return data;
+ }
+ }]);
+}();
\ No newline at end of file
diff --git a/npm-packages/meteor-reactive-cache/dist/ReactiveCache.js b/npm-packages/meteor-reactive-cache/dist/ReactiveCache.js
new file mode 100644
index 000000000..62c66b0d6
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/dist/ReactiveCache.js
@@ -0,0 +1,84 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports["default"] = void 0;
+var _tracker = _interopRequireDefault(require("./meteor/tracker"));
+function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
+function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
+function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
+function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
+function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
+function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
+function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
+var ReactiveCache = exports["default"] = /*#__PURE__*/function () {
+ function ReactiveCache(compare, shouldStop) {
+ _classCallCheck(this, ReactiveCache);
+ this.shouldStop = shouldStop || function () {
+ return true;
+ };
+ this.compare = compare || function (a, b) {
+ return a === b;
+ };
+ this.values = {};
+ this.deps = {};
+ }
+ return _createClass(ReactiveCache, [{
+ key: "ensureDependency",
+ value: function ensureDependency(key) {
+ if (!this.deps[key]) this.deps[key] = new _tracker["default"].Dependency();
+ return this.deps[key];
+ }
+ }, {
+ key: "checkDeletion",
+ value: function checkDeletion(key) {
+ var dep = this.ensureDependency(key);
+ if (dep.hasDependents()) return false;
+ delete this.values[key];
+ delete this.deps[key];
+ return true;
+ }
+ }, {
+ key: "clear",
+ value: function clear() {
+ var _this = this;
+ Object.keys(this.values).forEach(function (key) {
+ return _this.del(key);
+ });
+ }
+ }, {
+ key: "del",
+ value: function del(key) {
+ var dep = this.ensureDependency(key);
+ delete this.values[key];
+ if (this.checkDeletion(key)) return;
+ dep.changed();
+ }
+ }, {
+ key: "set",
+ value: function set(key, data, bypassCompare) {
+ var dep = this.ensureDependency(key);
+ var current = this.values[key];
+ this.values[key] = data;
+ if (!this.compare(current, data) || bypassCompare) {
+ dep.changed();
+ }
+ }
+ }, {
+ key: "get",
+ value: function get(key) {
+ var _this2 = this;
+ var data = this.values[key];
+ if (_tracker["default"].currentComputation) {
+ var dep = this.ensureDependency(key);
+ dep.depend();
+ _tracker["default"].currentComputation.onStop(function () {
+ if (!_this2.shouldStop(key)) return;
+ _this2.checkDeletion(key);
+ });
+ }
+ return data;
+ }
+ }]);
+}();
\ No newline at end of file
diff --git a/npm-packages/meteor-reactive-cache/dist/index.js b/npm-packages/meteor-reactive-cache/dist/index.js
new file mode 100644
index 000000000..fc7e304cd
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/dist/index.js
@@ -0,0 +1,27 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+Object.defineProperty(exports, "DataCache", {
+ enumerable: true,
+ get: function get() {
+ return _DataCache["default"];
+ }
+});
+Object.defineProperty(exports, "ReactiveCache", {
+ enumerable: true,
+ get: function get() {
+ return _ReactiveCache["default"];
+ }
+});
+Object.defineProperty(exports, "reactiveField", {
+ enumerable: true,
+ get: function get() {
+ return _reactiveField["default"];
+ }
+});
+var _DataCache = _interopRequireDefault(require("./DataCache"));
+var _ReactiveCache = _interopRequireDefault(require("./ReactiveCache"));
+var _reactiveField = _interopRequireDefault(require("./reactiveField"));
+function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
\ No newline at end of file
diff --git a/npm-packages/meteor-reactive-cache/dist/meteor/bindEnvironment.js b/npm-packages/meteor-reactive-cache/dist/meteor/bindEnvironment.js
new file mode 100644
index 000000000..b5cab58aa
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/dist/meteor/bindEnvironment.js
@@ -0,0 +1,9 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports["default"] = void 0;
+var _meteorGlobals = require("@wekanteam/meteor-globals");
+var Meteor = (0, _meteorGlobals.getGlobal)('meteor', 'Meteor');
+var _default = exports["default"] = Meteor.bindEnvironment.bind(Meteor);
\ No newline at end of file
diff --git a/npm-packages/meteor-reactive-cache/dist/meteor/ejson.js b/npm-packages/meteor-reactive-cache/dist/meteor/ejson.js
new file mode 100644
index 000000000..de74b31af
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/dist/meteor/ejson.js
@@ -0,0 +1,8 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports["default"] = void 0;
+var _meteorGlobals = require("@wekanteam/meteor-globals");
+var _default = exports["default"] = (0, _meteorGlobals.getGlobal)('ejson', 'EJSON');
\ No newline at end of file
diff --git a/npm-packages/meteor-reactive-cache/dist/meteor/tracker.js b/npm-packages/meteor-reactive-cache/dist/meteor/tracker.js
new file mode 100644
index 000000000..6a176c868
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/dist/meteor/tracker.js
@@ -0,0 +1,8 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports["default"] = void 0;
+var _meteorGlobals = require("@wekanteam/meteor-globals");
+var _default = exports["default"] = (0, _meteorGlobals.getGlobal)('tracker', 'Tracker');
\ No newline at end of file
diff --git a/npm-packages/meteor-reactive-cache/dist/reactiveField.js b/npm-packages/meteor-reactive-cache/dist/reactiveField.js
new file mode 100644
index 000000000..36162b82e
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/dist/reactiveField.js
@@ -0,0 +1,28 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports["default"] = void 0;
+var _DataCache = _interopRequireDefault(require("./DataCache"));
+var _ejson = _interopRequireDefault(require("./meteor/ejson"));
+function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
+function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
+function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
+function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
+function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
+function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
+function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
+var _default = exports["default"] = function _default(fn, compare) {
+ var cache = new _DataCache["default"](function (key) {
+ return fn.apply(void 0, _toConsumableArray(_ejson["default"].parse(key)));
+ }, compare);
+ var resolver = function resolver() {
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+ return cache.get(_ejson["default"].stringify(args));
+ };
+ resolver.cache = cache;
+ return resolver;
+};
\ No newline at end of file
diff --git a/npm-packages/meteor-reactive-cache/package-lock.json b/npm-packages/meteor-reactive-cache/package-lock.json
new file mode 100644
index 000000000..0f6e9221c
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/package-lock.json
@@ -0,0 +1,5321 @@
+{
+ "name": "@wekanteam/meteor-reactive-cache",
+ "version": "1.0.7",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@wekanteam/meteor-reactive-cache",
+ "version": "1.0.7",
+ "license": "MIT",
+ "dependencies": {
+ "@wekanteam/meteor-globals": "^1.1.6"
+ },
+ "devDependencies": {
+ "@babel/cli": "^7.23.9",
+ "@babel/core": "^7.23.9",
+ "@babel/eslint-parser": "^7.23.9",
+ "@babel/preset-env": "^7.23.9",
+ "eslint": "^8.56.0",
+ "eslint-config-airbnb-base": "^15.0.0",
+ "eslint-plugin-import": "^2.29.1"
+ }
+ },
+ "node_modules/@babel/cli": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.28.3.tgz",
+ "integrity": "sha512-n1RU5vuCX0CsaqaXm9I0KUCNKNQMy5epmzl/xdSSm70bSqhg9GWhgeosypyQLc0bK24+Xpk1WGzZlI9pJtkZdg==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "commander": "^6.2.0",
+ "convert-source-map": "^2.0.0",
+ "fs-readdir-recursive": "^1.1.0",
+ "glob": "^7.2.0",
+ "make-dir": "^2.1.0",
+ "slash": "^2.0.0"
+ },
+ "bin": {
+ "babel": "bin/babel.js",
+ "babel-external-helpers": "bin/babel-external-helpers.js"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "optionalDependencies": {
+ "@nicolo-ribaudo/chokidar-2": "2.1.8-no-fsevents.3",
+ "chokidar": "^3.6.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/commander": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "node_modules/@babel/cli/node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@babel/cli/node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/code-frame/node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz",
+ "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
+ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.4",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true
+ },
+ "node_modules/@babel/core/node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/eslint-parser": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.28.5.tgz",
+ "integrity": "sha512-fcdRcWahONYo+JRnJg1/AekOacGvKx12Gu0qXJXFi2WBqQA1i7+O5PaxRB7kxE/Op94dExnCiiar6T09pvdHpA==",
+ "dev": true,
+ "dependencies": {
+ "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1",
+ "eslint-visitor-keys": "^2.1.0",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": "^10.13.0 || ^12.13.0 || >=14.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.11.0",
+ "eslint": "^7.5.0 || ^8.0.0 || ^9.0.0"
+ }
+ },
+ "node_modules/@babel/eslint-parser/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz",
+ "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "@babel/types": "^7.28.5",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator/node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.27.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
+ "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.27.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz",
+ "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
+ "@babel/helper-optimise-call-expression": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/traverse": "^7.28.5",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz",
+ "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "regexpu-core": "^6.3.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/regexpu-core": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz",
+ "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==",
+ "dev": true,
+ "dependencies": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.2.2",
+ "regjsgen": "^0.8.0",
+ "regjsparser": "^0.13.0",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/regjsgen": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz",
+ "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
+ "dev": true
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/regjsparser": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.0.tgz",
+ "integrity": "sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==",
+ "dev": true,
+ "dependencies": {
+ "jsesc": "~3.1.0"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.5.tgz",
+ "integrity": "sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "debug": "^4.4.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.22.10"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz",
+ "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
+ "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz",
+ "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-remap-async-to-generator": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz",
+ "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "@babel/helper-wrap-function": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz",
+ "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-member-expression-to-functions": "^7.27.1",
+ "@babel/helper-optimise-call-expression": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
+ "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-wrap-function": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.3.tgz",
+ "integrity": "sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.3",
+ "@babel/types": "^7.28.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz",
+ "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz",
+ "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/types": "^7.28.5"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz",
+ "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz",
+ "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz",
+ "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
+ "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/plugin-transform-optional-chaining": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.13.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.3.tgz",
+ "integrity": "sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-assertions": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz",
+ "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz",
+ "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
+ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-arrow-functions": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz",
+ "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-generator-functions": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz",
+ "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-remap-async-to-generator": "^7.27.1",
+ "@babel/traverse": "^7.28.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-to-generator": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz",
+ "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-remap-async-to-generator": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz",
+ "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoping": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz",
+ "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-properties": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz",
+ "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-static-block": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.3.tgz",
+ "integrity": "sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.28.3",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.12.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-classes": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz",
+ "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.27.1",
+ "@babel/traverse": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-computed-properties": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz",
+ "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/template": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-destructuring": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz",
+ "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dotall-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz",
+ "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-keys": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz",
+ "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz",
+ "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dynamic-import": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz",
+ "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-explicit-resource-management": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.0.tgz",
+ "integrity": "sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-transform-destructuring": "^7.28.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.5.tgz",
+ "integrity": "sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-export-namespace-from": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz",
+ "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-for-of": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz",
+ "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-function-name": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz",
+ "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/traverse": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-json-strings": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz",
+ "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz",
+ "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-logical-assignment-operators": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.5.tgz",
+ "integrity": "sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-member-expression-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz",
+ "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-amd": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz",
+ "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-commonjs": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz",
+ "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-systemjs": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz",
+ "integrity": "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-umd": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz",
+ "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz",
+ "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-new-target": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz",
+ "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz",
+ "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-numeric-separator": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz",
+ "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-rest-spread": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.4.tgz",
+ "integrity": "sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-transform-destructuring": "^7.28.0",
+ "@babel/plugin-transform-parameters": "^7.27.7",
+ "@babel/traverse": "^7.28.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-super": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz",
+ "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-catch-binding": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz",
+ "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-chaining": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.5.tgz",
+ "integrity": "sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-parameters": {
+ "version": "7.27.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz",
+ "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-private-methods": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz",
+ "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-private-property-in-object": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz",
+ "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.1",
+ "@babel/helper-create-class-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-property-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz",
+ "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regenerator": {
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.28.4.tgz",
+ "integrity": "sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regexp-modifiers": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz",
+ "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-reserved-words": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz",
+ "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-shorthand-properties": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz",
+ "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-spread": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz",
+ "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-sticky-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz",
+ "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-template-literals": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz",
+ "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typeof-symbol": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz",
+ "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz",
+ "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-property-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz",
+ "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz",
+ "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-sets-regex": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz",
+ "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/preset-env": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.5.tgz",
+ "integrity": "sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.28.5",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/helper-validator-option": "^7.27.1",
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5",
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1",
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3",
+ "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
+ "@babel/plugin-syntax-import-assertions": "^7.27.1",
+ "@babel/plugin-syntax-import-attributes": "^7.27.1",
+ "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
+ "@babel/plugin-transform-arrow-functions": "^7.27.1",
+ "@babel/plugin-transform-async-generator-functions": "^7.28.0",
+ "@babel/plugin-transform-async-to-generator": "^7.27.1",
+ "@babel/plugin-transform-block-scoped-functions": "^7.27.1",
+ "@babel/plugin-transform-block-scoping": "^7.28.5",
+ "@babel/plugin-transform-class-properties": "^7.27.1",
+ "@babel/plugin-transform-class-static-block": "^7.28.3",
+ "@babel/plugin-transform-classes": "^7.28.4",
+ "@babel/plugin-transform-computed-properties": "^7.27.1",
+ "@babel/plugin-transform-destructuring": "^7.28.5",
+ "@babel/plugin-transform-dotall-regex": "^7.27.1",
+ "@babel/plugin-transform-duplicate-keys": "^7.27.1",
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1",
+ "@babel/plugin-transform-dynamic-import": "^7.27.1",
+ "@babel/plugin-transform-explicit-resource-management": "^7.28.0",
+ "@babel/plugin-transform-exponentiation-operator": "^7.28.5",
+ "@babel/plugin-transform-export-namespace-from": "^7.27.1",
+ "@babel/plugin-transform-for-of": "^7.27.1",
+ "@babel/plugin-transform-function-name": "^7.27.1",
+ "@babel/plugin-transform-json-strings": "^7.27.1",
+ "@babel/plugin-transform-literals": "^7.27.1",
+ "@babel/plugin-transform-logical-assignment-operators": "^7.28.5",
+ "@babel/plugin-transform-member-expression-literals": "^7.27.1",
+ "@babel/plugin-transform-modules-amd": "^7.27.1",
+ "@babel/plugin-transform-modules-commonjs": "^7.27.1",
+ "@babel/plugin-transform-modules-systemjs": "^7.28.5",
+ "@babel/plugin-transform-modules-umd": "^7.27.1",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1",
+ "@babel/plugin-transform-new-target": "^7.27.1",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1",
+ "@babel/plugin-transform-numeric-separator": "^7.27.1",
+ "@babel/plugin-transform-object-rest-spread": "^7.28.4",
+ "@babel/plugin-transform-object-super": "^7.27.1",
+ "@babel/plugin-transform-optional-catch-binding": "^7.27.1",
+ "@babel/plugin-transform-optional-chaining": "^7.28.5",
+ "@babel/plugin-transform-parameters": "^7.27.7",
+ "@babel/plugin-transform-private-methods": "^7.27.1",
+ "@babel/plugin-transform-private-property-in-object": "^7.27.1",
+ "@babel/plugin-transform-property-literals": "^7.27.1",
+ "@babel/plugin-transform-regenerator": "^7.28.4",
+ "@babel/plugin-transform-regexp-modifiers": "^7.27.1",
+ "@babel/plugin-transform-reserved-words": "^7.27.1",
+ "@babel/plugin-transform-shorthand-properties": "^7.27.1",
+ "@babel/plugin-transform-spread": "^7.27.1",
+ "@babel/plugin-transform-sticky-regex": "^7.27.1",
+ "@babel/plugin-transform-template-literals": "^7.27.1",
+ "@babel/plugin-transform-typeof-symbol": "^7.27.1",
+ "@babel/plugin-transform-unicode-escapes": "^7.27.1",
+ "@babel/plugin-transform-unicode-property-regex": "^7.27.1",
+ "@babel/plugin-transform-unicode-regex": "^7.27.1",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.27.1",
+ "@babel/preset-modules": "0.1.6-no-external-plugins",
+ "babel-plugin-polyfill-corejs2": "^0.4.14",
+ "babel-plugin-polyfill-corejs3": "^0.13.0",
+ "babel-plugin-polyfill-regenerator": "^0.6.5",
+ "core-js-compat": "^3.43.0",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-env/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/preset-modules": {
+ "version": "0.1.6-no-external-plugins",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
+ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz",
+ "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.5",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.5",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.5",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz",
+ "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
+ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
+ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
+ "deprecated": "Use @eslint/config-array instead",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^2.0.3",
+ "debug": "^4.3.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "deprecated": "Use @eslint/object-schema instead",
+ "dev": true
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@nicolo-ribaudo/chokidar-2": {
+ "version": "2.1.8-no-fsevents.3",
+ "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz",
+ "integrity": "sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": {
+ "version": "5.1.1-v1",
+ "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz",
+ "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==",
+ "dev": true,
+ "dependencies": {
+ "eslint-scope": "5.1.1"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+ "dev": true
+ },
+ "node_modules/@wekanteam/meteor-globals": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@wekanteam/meteor-globals/-/meteor-globals-1.1.6.tgz",
+ "integrity": "sha512-JJJZLXbvdpfRYtBBQ9JEk0iLU6yCs076r+qJ4e8gMD/AQpva7xOeLzMPdmzfemQ0M/Asa+7mVS1tU+2/5VQO9w==",
+ "dependencies": {
+ "semver": "^7.5.4"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.14.tgz",
+ "integrity": "sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/compat-data": "^7.27.7",
+ "@babel/helper-define-polyfill-provider": "^0.6.5",
+ "semver": "^6.3.1"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz",
+ "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.5",
+ "core-js-compat": "^3.43.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-regenerator": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.5.tgz",
+ "integrity": "sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.5"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.9.11",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz",
+ "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==",
+ "dev": true,
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.js"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001762",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz",
+ "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ]
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/confusing-browser-globals": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz",
+ "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==",
+ "dev": true
+ },
+ "node_modules/core-js-compat": {
+ "version": "3.47.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.47.0.tgz",
+ "integrity": "sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==",
+ "dev": true,
+ "dependencies": {
+ "browserslist": "^4.28.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.267",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
+ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==",
+ "dev": true
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
+ "integrity": "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==",
+ "dev": true,
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
+ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.1",
+ "@humanwhocodes/config-array": "^0.13.0",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-config-airbnb-base": {
+ "version": "15.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz",
+ "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==",
+ "dev": true,
+ "dependencies": {
+ "confusing-browser-globals": "^1.0.10",
+ "object.assign": "^4.1.2",
+ "object.entries": "^1.1.5",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.32.0 || ^8.2.0",
+ "eslint-plugin-import": "^2.25.2"
+ }
+ },
+ "node_modules/eslint-config-airbnb-base/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
+ "dev": true,
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.1",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.16.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.1",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.9",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
+ "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz",
+ "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint/node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esquery/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/fs-readdir-recursive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
+ "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
+ "dev": true
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
+ "dev": true,
+ "dependencies": {
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dev": true,
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "dev": true
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
+ "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.entries": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz",
+ "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regenerate": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
+ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+ "dev": true
+ },
+ "node_modules/regenerate-unicode-properties": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
+ "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
+ "dev": true,
+ "dependencies": {
+ "regenerate": "^1.4.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
+ "dev": true,
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/unicode-canonical-property-names-ecmascript": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
+ "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-ecmascript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+ "dev": true,
+ "dependencies": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-value-ecmascript": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
+ "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-property-aliases-ecmascript": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
+ "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dev": true,
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/npm-packages/meteor-reactive-cache/package.json b/npm-packages/meteor-reactive-cache/package.json
new file mode 100644
index 000000000..94a86d2cf
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/package.json
@@ -0,0 +1,63 @@
+{
+ "name": "@wekanteam/meteor-reactive-cache",
+ "version": "1.0.7",
+ "description": "Utilities for caching reactive data",
+ "main": "dist/index.js",
+ "scripts": {
+ "prepublish": "./node_modules/.bin/babel src -d dist",
+ "lint": "./node_modules/.bin/eslint src",
+ "copy": "npm link && cd tests && npm link meteor-reactive-cache",
+ "test": "npm run copy && cd tests && TEST_BROWSER_DRIVER=nightmare meteor test -p 3100 --once --driver-package meteortesting:mocha"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/maxnowack/meteor-reactive-cache.git"
+ },
+ "author": "Max Nowack ",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/maxnowack/meteor-reactive-cache/issues"
+ },
+ "homepage": "https://github.com/maxnowack/meteor-reactive-cache#readme",
+ "files": [
+ "dist"
+ ],
+ "keywords": [
+ "caching",
+ "meteor",
+ "react",
+ "reactivity",
+ "tracker"
+ ],
+ "babel": {
+ "presets": [
+ "@babel/preset-env"
+ ]
+ },
+ "eslintConfig": {
+ "extends": [
+ "airbnb-base"
+ ],
+ "parser": "@babel/eslint-parser",
+ "rules": {
+ "no-param-reassign": [
+ 2,
+ {
+ "props": false
+ }
+ ]
+ }
+ },
+ "devDependencies": {
+ "@babel/cli": "^7.23.9",
+ "@babel/core": "^7.23.9",
+ "@babel/eslint-parser": "^7.23.9",
+ "@babel/preset-env": "^7.23.9",
+ "eslint": "^8.56.0",
+ "eslint-config-airbnb-base": "^15.0.0",
+ "eslint-plugin-import": "^2.29.1"
+ },
+ "dependencies": {
+ "@wekanteam/meteor-globals": "^1.1.6"
+ }
+}
diff --git a/npm-packages/meteor-reactive-cache/release.sh b/npm-packages/meteor-reactive-cache/release.sh
new file mode 100755
index 000000000..46fb0b183
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/release.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+## 1. Copy files
+
+npm run prepublish
+
+npm run copy
+
+## 2. Commit
+
+git add --all
+
+git commit -m "$1"
+
+## 3. Add tags
+
+git tag -a $1 -m "$1"
+
+git push origin $1
+
+git push
+
+## 4. Publish npm package
+
+npm publish --access public
diff --git a/npm-packages/meteor-reactive-cache/src/DataCache.js b/npm-packages/meteor-reactive-cache/src/DataCache.js
new file mode 100644
index 000000000..c9098bbca
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/src/DataCache.js
@@ -0,0 +1,61 @@
+import bindEnvironment from './meteor/bindEnvironment';
+import Tracker from './meteor/tracker';
+import ReactiveCache from './ReactiveCache';
+
+export default class DataCache {
+ constructor(getData, options) {
+ this.options = {
+ timeout: 60 * 1000, // 60 seconds
+ ...(typeof options === 'function' ? { compare: options } : options),
+ };
+
+ this.getData = getData;
+ this.cache = new ReactiveCache(this.options.compare, () => false);
+ this.timeouts = {};
+ this.computations = {};
+ }
+
+ ensureComputation(key) {
+ if (this.timeouts[key]) {
+ clearTimeout(this.timeouts[key]);
+ delete this.timeouts[key];
+ }
+ if (this.computations[key] && !this.computations[key].stopped) return;
+ this.computations[key] = Tracker.nonreactive(() => Tracker.autorun(() => {
+ this.cache.set(key, this.getData(key));
+ }));
+
+ // stop the computation if the key doesn't have any dependants
+ this.computations[key].onInvalidate(() => this.checkStop(key));
+ }
+
+ checkStop(key) {
+ if (this.cache.ensureDependency(key).hasDependents()) return;
+ if (this.timeouts[key]) {
+ clearTimeout(this.timeouts[key]);
+ delete this.timeouts[key];
+ }
+ this.timeouts[key] = setTimeout(bindEnvironment(() => {
+ if (!this.computations[key]) return;
+ this.computations[key].stop();
+ delete this.computations[key];
+ this.cache.del(key);
+ }), this.options.timeout);
+ }
+
+ get(key) {
+ if (!Tracker.currentComputation) {
+ let data = this.cache.get(key);
+ if (data == null) {
+ data = this.getData(key);
+ this.cache.set(key, data);
+ this.checkStop(key);
+ }
+ return data;
+ }
+ this.ensureComputation(key);
+ const data = this.cache.get(key);
+ Tracker.currentComputation.onStop(() => this.checkStop(key));
+ return data;
+ }
+}
diff --git a/npm-packages/meteor-reactive-cache/src/ReactiveCache.js b/npm-packages/meteor-reactive-cache/src/ReactiveCache.js
new file mode 100644
index 000000000..2b8cad078
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/src/ReactiveCache.js
@@ -0,0 +1,56 @@
+import Tracker from './meteor/tracker';
+
+export default class ReactiveCache {
+ constructor(compare, shouldStop) {
+ this.shouldStop = shouldStop || (() => true);
+ this.compare = compare || ((a, b) => a === b);
+ this.values = {};
+ this.deps = {};
+ }
+
+ ensureDependency(key) {
+ if (!this.deps[key]) this.deps[key] = new Tracker.Dependency();
+ return this.deps[key];
+ }
+
+ checkDeletion(key) {
+ const dep = this.ensureDependency(key);
+ if (dep.hasDependents()) return false;
+ delete this.values[key];
+ delete this.deps[key];
+ return true;
+ }
+
+ clear() {
+ Object.keys(this.values).forEach((key) => this.del(key));
+ }
+
+ del(key) {
+ const dep = this.ensureDependency(key);
+ delete this.values[key];
+ if (this.checkDeletion(key)) return;
+ dep.changed();
+ }
+
+ set(key, data, bypassCompare) {
+ const dep = this.ensureDependency(key);
+ const current = this.values[key];
+ this.values[key] = data;
+ if (!this.compare(current, data) || bypassCompare) {
+ dep.changed();
+ }
+ }
+
+ get(key) {
+ const data = this.values[key];
+ if (Tracker.currentComputation) {
+ const dep = this.ensureDependency(key);
+ dep.depend();
+ Tracker.currentComputation.onStop(() => {
+ if (!this.shouldStop(key)) return;
+ this.checkDeletion(key);
+ });
+ }
+ return data;
+ }
+}
diff --git a/npm-packages/meteor-reactive-cache/src/index.js b/npm-packages/meteor-reactive-cache/src/index.js
new file mode 100644
index 000000000..69625f5b2
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/src/index.js
@@ -0,0 +1,3 @@
+export { default as DataCache } from './DataCache';
+export { default as ReactiveCache } from './ReactiveCache';
+export { default as reactiveField } from './reactiveField';
diff --git a/npm-packages/meteor-reactive-cache/src/meteor/bindEnvironment.js b/npm-packages/meteor-reactive-cache/src/meteor/bindEnvironment.js
new file mode 100644
index 000000000..0807b7b67
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/src/meteor/bindEnvironment.js
@@ -0,0 +1,4 @@
+import { getGlobal } from '@wekanteam/meteor-globals';
+
+const Meteor = getGlobal('meteor', 'Meteor');
+export default Meteor.bindEnvironment.bind(Meteor);
diff --git a/npm-packages/meteor-reactive-cache/src/meteor/ejson.js b/npm-packages/meteor-reactive-cache/src/meteor/ejson.js
new file mode 100644
index 000000000..0b1904633
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/src/meteor/ejson.js
@@ -0,0 +1,3 @@
+import { getGlobal } from '@wekanteam/meteor-globals';
+
+export default getGlobal('ejson', 'EJSON');
diff --git a/npm-packages/meteor-reactive-cache/src/meteor/tracker.js b/npm-packages/meteor-reactive-cache/src/meteor/tracker.js
new file mode 100644
index 000000000..346924b91
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/src/meteor/tracker.js
@@ -0,0 +1,3 @@
+import { getGlobal } from '@wekanteam/meteor-globals';
+
+export default getGlobal('tracker', 'Tracker');
diff --git a/npm-packages/meteor-reactive-cache/src/reactiveField.js b/npm-packages/meteor-reactive-cache/src/reactiveField.js
new file mode 100644
index 000000000..12bd173b2
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/src/reactiveField.js
@@ -0,0 +1,9 @@
+import DataCache from './DataCache';
+import ejson from './meteor/ejson';
+
+export default (fn, compare) => {
+ const cache = new DataCache((key) => fn(...ejson.parse(key)), compare);
+ const resolver = (...args) => cache.get(ejson.stringify(args));
+ resolver.cache = cache;
+ return resolver;
+};
diff --git a/npm-packages/meteor-reactive-cache/tests/.gitignore b/npm-packages/meteor-reactive-cache/tests/.gitignore
new file mode 100644
index 000000000..e69de29bb
diff --git a/npm-packages/meteor-reactive-cache/tests/.meteor/.finished-upgraders b/npm-packages/meteor-reactive-cache/tests/.meteor/.finished-upgraders
new file mode 100644
index 000000000..4538749ab
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/.meteor/.finished-upgraders
@@ -0,0 +1,18 @@
+# This file contains information which helps Meteor properly upgrade your
+# app when you run 'meteor update'. You should check it into version control
+# with your project.
+
+notices-for-0.9.0
+notices-for-0.9.1
+0.9.4-platform-file
+notices-for-facebook-graph-api-2
+1.2.0-standard-minifiers-package
+1.2.0-meteor-platform-split
+1.2.0-cordova-changes
+1.2.0-breaking-changes
+1.3.0-split-minifiers-package
+1.4.0-remove-old-dev-bundle-link
+1.4.1-add-shell-server-package
+1.4.3-split-account-service-packages
+1.5-add-dynamic-import-package
+1.7-split-underscore-from-meteor-base
diff --git a/npm-packages/meteor-reactive-cache/tests/.meteor/.gitignore b/npm-packages/meteor-reactive-cache/tests/.meteor/.gitignore
new file mode 100644
index 000000000..408303742
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/.meteor/.gitignore
@@ -0,0 +1 @@
+local
diff --git a/npm-packages/meteor-reactive-cache/tests/.meteor/.id b/npm-packages/meteor-reactive-cache/tests/.meteor/.id
new file mode 100644
index 000000000..2f9766ead
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/.meteor/.id
@@ -0,0 +1,7 @@
+# This file contains a token that is unique to your project.
+# Check it into your repository along with the rest of this directory.
+# It can be used for purposes such as:
+# - ensuring you don't accidentally deploy one app on top of another
+# - providing package authors with aggregated statistics
+
+1efzo9epsn1ob1vv1hyv
diff --git a/npm-packages/meteor-reactive-cache/tests/.meteor/packages b/npm-packages/meteor-reactive-cache/tests/.meteor/packages
new file mode 100644
index 000000000..733c08ac3
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/.meteor/packages
@@ -0,0 +1,18 @@
+# Meteor packages used by this project, one per line.
+# Check this file (and the other files in this directory) into your repository.
+#
+# 'meteor add' and 'meteor remove' will edit this file for you,
+# but you can also edit it by hand.
+
+meteor-base@1.4.0 # Packages every Meteor app needs to have
+reactive-var@1.0.11 # Reactive variable for tracker
+tracker@1.2.0 # Meteor's client-side reactive programming library
+
+standard-minifier-css@1.5.2 # CSS minifier run for production mode
+standard-minifier-js@2.4.0 # JS minifier run for production mode
+es5-shim@4.8.0 # ECMAScript 5 compatibility for older browsers.
+ecmascript@0.12.3 # Enable ECMAScript2015+ syntax in app code
+shell-server@0.4.0 # Server-side component of the `meteor shell` command
+reactive-dict@1.2.1
+dynamic-import@0.5.0
+meteortesting:mocha
diff --git a/npm-packages/meteor-reactive-cache/tests/.meteor/platforms b/npm-packages/meteor-reactive-cache/tests/.meteor/platforms
new file mode 100644
index 000000000..efeba1b50
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/.meteor/platforms
@@ -0,0 +1,2 @@
+server
+browser
diff --git a/npm-packages/meteor-reactive-cache/tests/.meteor/release b/npm-packages/meteor-reactive-cache/tests/.meteor/release
new file mode 100644
index 000000000..2299ae70d
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/.meteor/release
@@ -0,0 +1 @@
+METEOR@1.8.0.1
diff --git a/npm-packages/meteor-reactive-cache/tests/.meteor/versions b/npm-packages/meteor-reactive-cache/tests/.meteor/versions
new file mode 100644
index 000000000..02fad789a
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/.meteor/versions
@@ -0,0 +1,63 @@
+allow-deny@1.1.0
+autoupdate@1.5.0
+babel-compiler@7.2.3
+babel-runtime@1.3.0
+base64@1.0.11
+binary-heap@1.0.11
+boilerplate-generator@1.6.0
+callback-hook@1.1.0
+check@1.3.1
+ddp@1.4.0
+ddp-client@2.3.3
+ddp-common@1.4.0
+ddp-server@2.2.0
+diff-sequence@1.1.1
+dynamic-import@0.5.1
+ecmascript@0.12.3
+ecmascript-runtime@0.7.0
+ecmascript-runtime-client@0.8.0
+ecmascript-runtime-server@0.7.1
+ejson@1.1.0
+es5-shim@4.8.0
+fetch@0.1.0
+geojson-utils@1.0.10
+hot-code-push@1.0.4
+http@1.4.2
+id-map@1.1.0
+inter-process-messaging@0.1.0
+livedata@1.0.18
+lmieulet:meteor-coverage@2.0.2
+logging@1.1.20
+meteor@1.9.2
+meteor-base@1.4.0
+meteortesting:browser-tests@1.2.0
+meteortesting:mocha@1.1.0
+meteortesting:mocha-core@5.2.0_3
+minifier-css@1.4.1
+minifier-js@2.4.0
+minimongo@1.4.5
+modern-browsers@0.1.3
+modules@0.13.0
+modules-runtime@0.10.3
+mongo@1.6.0
+mongo-decimal@0.1.0
+mongo-dev-server@1.1.0
+mongo-id@1.0.7
+npm-mongo@3.1.1
+ordered-dict@1.1.0
+promise@0.11.1
+random@1.1.0
+reactive-dict@1.2.1
+reactive-var@1.0.11
+reload@1.2.0
+retry@1.1.0
+routepolicy@1.1.0
+shell-server@0.4.0
+socket-stream-client@0.2.2
+standard-minifier-css@1.5.2
+standard-minifier-js@2.4.0
+tracker@1.2.0
+underscore@1.0.10
+url@1.2.0
+webapp@1.7.2
+webapp-hashing@1.0.9
diff --git a/npm-packages/meteor-reactive-cache/tests/imports/DataCache.test.js b/npm-packages/meteor-reactive-cache/tests/imports/DataCache.test.js
new file mode 100644
index 000000000..8350a4ddb
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/imports/DataCache.test.js
@@ -0,0 +1,54 @@
+/* global describe it */
+import chai from 'chai';
+import { DataCache } from 'meteor-reactive-cache';
+import { Meteor } from 'meteor/meteor';
+import { Tracker } from 'meteor/tracker';
+import { ReactiveDict } from 'meteor/reactive-dict';
+
+describe('DataCache', () => {
+ it('should be reactive', (done) => {
+ const reactiveDict = new ReactiveDict();
+ reactiveDict.set('foo', '000');
+ let numGets = 0;
+ const reactiveCache = new DataCache((key) => {
+ numGets += 1;
+ return reactiveDict.get(key);
+ }, {
+ timeout: 10,
+ });
+
+ let runs = 0;
+ const computation = Tracker.autorun(() => {
+ runs += 1;
+ const value = reactiveCache.get('foo'); // eslint-disable-line
+ });
+
+ chai.assert.equal(reactiveCache.get('foo'), '000');
+ chai.assert.equal(numGets, 1);
+ chai.assert.equal(runs, 1);
+
+ reactiveDict.set('foo', 'bar');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(reactiveCache.get('foo'), 'bar');
+ chai.assert.equal(numGets, 2);
+ chai.assert.equal(runs, 2);
+
+ reactiveDict.set('foo', 'bar');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(reactiveCache.get('foo'), 'bar');
+ chai.assert.equal(numGets, 2);
+ chai.assert.equal(runs, 2);
+
+
+ computation.stop();
+
+ chai.assert.equal(reactiveCache.get('foo'), 'bar');
+ chai.assert.equal(numGets, 2);
+
+ Meteor.setTimeout(() => {
+ chai.assert.equal(reactiveCache.get('foo'), 'bar');
+ chai.assert.equal(numGets, 3);
+ done();
+ }, 50);
+ });
+});
diff --git a/npm-packages/meteor-reactive-cache/tests/imports/ReactiveCache.test.js b/npm-packages/meteor-reactive-cache/tests/imports/ReactiveCache.test.js
new file mode 100644
index 000000000..160352862
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/imports/ReactiveCache.test.js
@@ -0,0 +1,89 @@
+/* global describe it */
+import chai from 'chai';
+import { ReactiveCache } from 'meteor-reactive-cache';
+import { Tracker } from 'meteor/tracker';
+import isEqual from 'lodash.isequal';
+
+describe('ReactiveCache', () => {
+ it('should be reactive', () => {
+ const reactiveCache = new ReactiveCache();
+
+ let runs = 0;
+ Tracker.autorun(() => {
+ runs += 1;
+ const value = reactiveCache.get('foo'); // eslint-disable-line
+ });
+
+ chai.assert.equal(typeof reactiveCache.get('foo'), 'undefined');
+ chai.assert.equal(runs, 1);
+
+ reactiveCache.set('foo', 'bar');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(reactiveCache.get('foo'), 'bar');
+ chai.assert.equal(runs, 2);
+
+ reactiveCache.set('foo', 'bar');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(reactiveCache.get('foo'), 'bar');
+ chai.assert.equal(runs, 2);
+
+ reactiveCache.del('foo');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(typeof reactiveCache.get('foo'), 'undefined');
+ chai.assert.equal(runs, 3);
+ });
+
+ it('should be reactive with custom compare', () => {
+ const reactiveCache = new ReactiveCache();
+ const reactiveObjCache = new ReactiveCache((a, b) => isEqual(a, b));
+
+ reactiveCache.set('foo', { test: true });
+ reactiveObjCache.set('foo', { test: true });
+
+ let runs = 0;
+ Tracker.autorun(() => {
+ runs += 1;
+ const value = reactiveCache.get('foo'); // eslint-disable-line
+ });
+
+ let runsObj = 0;
+ Tracker.autorun(() => {
+ runsObj += 1;
+ const value = reactiveObjCache.get('foo'); // eslint-disable-line
+ });
+
+ chai.assert.equal(runs, 1);
+ chai.assert.equal(runsObj, 1);
+
+ reactiveCache.set('foo', { test: true });
+ reactiveObjCache.set('foo', { test: true });
+ Tracker.flush({ _throwFirstError: true });
+
+ chai.assert.equal(runs, 2);
+ chai.assert.equal(runsObj, 1);
+ });
+
+ it('gets invalidated if computation stops', (done) => {
+ const reactiveCache = new ReactiveCache();
+
+ let runs = 0;
+ const computation = Tracker.autorun(() => {
+ runs += 1;
+ const value = reactiveCache.get('foo'); // eslint-disable-line
+ });
+
+ chai.assert.equal(typeof reactiveCache.get('foo'), 'undefined');
+ chai.assert.equal(runs, 1);
+
+ reactiveCache.set('foo', 'bar');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(reactiveCache.get('foo'), 'bar');
+ chai.assert.equal(runs, 2);
+
+ computation.stop();
+ chai.assert.equal(typeof reactiveCache.get('foo'), 'undefined');
+ setTimeout(() => {
+ done();
+ }, 200);
+ });
+});
diff --git a/npm-packages/meteor-reactive-cache/tests/imports/reactiveField.test.js b/npm-packages/meteor-reactive-cache/tests/imports/reactiveField.test.js
new file mode 100644
index 000000000..d3b9b8e16
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/imports/reactiveField.test.js
@@ -0,0 +1,154 @@
+/* global describe it */
+import chai from 'chai';
+import { reactiveField } from 'meteor-reactive-cache';
+import { Meteor } from 'meteor/meteor';
+import { Tracker } from 'meteor/tracker';
+import { ReactiveVar } from 'meteor/reactive-var';
+import { ReactiveDict } from 'meteor/reactive-dict';
+
+describe('reactiveField', () => {
+ it('should be reactive', (done) => {
+ const reactiveDict = new ReactiveDict();
+ reactiveDict.set('foo', '000');
+ let numGets = 0;
+ const field = reactiveField((key) => {
+ numGets += 1;
+ return reactiveDict.get(key);
+ }, {
+ timeout: 10,
+ });
+
+ let runs = 0;
+ const computation = Tracker.autorun(() => {
+ runs += 1;
+ const value = field('foo'); // eslint-disable-line
+ });
+
+ chai.assert.equal(field('foo'), '000');
+ chai.assert.equal(numGets, 1);
+ chai.assert.equal(runs, 1);
+
+ reactiveDict.set('foo', 'bar');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(field('foo'), 'bar');
+ chai.assert.equal(numGets, 2);
+ chai.assert.equal(runs, 2);
+
+ reactiveDict.set('foo', 'bar');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(field('foo'), 'bar');
+ chai.assert.equal(numGets, 2);
+ chai.assert.equal(runs, 2);
+
+
+ computation.stop();
+
+ chai.assert.equal(field('foo'), 'bar');
+ chai.assert.equal(numGets, 2);
+
+ Meteor.setTimeout(() => {
+ chai.assert.equal(field('foo'), 'bar');
+ chai.assert.equal(numGets, 3);
+ done();
+ }, 50);
+ });
+
+ it('should be reactive with complex values', () => {
+ const reactiveDict = new ReactiveDict();
+ reactiveDict.set('foo', { date: new Date('2018-01-01') });
+ let numGets = 0;
+ const field = reactiveField((key) => {
+ numGets += 1;
+ return reactiveDict.get(key);
+ }, {
+ timeout: 10,
+ });
+
+ let runs = 0;
+ const computation = Tracker.autorun(() => {
+ runs += 1;
+ const value = field('foo'); // eslint-disable-line
+ });
+
+ chai.assert.instanceOf(field('foo').date, Date);
+ chai.assert.equal(numGets, 1);
+ chai.assert.equal(runs, 1);
+
+ reactiveDict.set('foo', { date: new Date('2018-01-02') });
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.instanceOf(field('foo').date, Date);
+ chai.assert.equal(numGets, 2);
+ chai.assert.equal(runs, 2);
+
+ computation.stop();
+ });
+
+ it('should work with multiple parameters', () => {
+ const reactiveDict = new ReactiveDict();
+ reactiveDict.set('foofoo', '000');
+ reactiveDict.set('foobar', '000');
+ reactiveDict.set('barbar', '000');
+ let numGets = 0;
+ const field = reactiveField((val1, val2) => {
+ numGets += 1;
+ return reactiveDict.get(`${val1}${val2}`);
+ }, {
+ timeout: 100,
+ });
+
+ const runs = {
+ foofoo: 0,
+ foobar: 0,
+ barbar: 0,
+ };
+
+ Tracker.autorun(() => {
+ runs.foofoo += 1;
+ const value = field('foo', 'foo'); // eslint-disable-line
+ });
+ Tracker.autorun(() => {
+ runs.foobar += 1;
+ const value = field('foo', 'bar'); // eslint-disable-line
+ });
+ Tracker.autorun(() => {
+ runs.barbar += 1;
+ const value = field('bar', 'bar'); // eslint-disable-line
+ });
+
+ chai.assert.equal(field('foo', 'foo'), '000');
+ chai.assert.equal(numGets, 3);
+ chai.assert.equal(runs.foofoo, 1);
+
+ chai.assert.equal(field('foo', 'bar'), '000');
+ chai.assert.equal(numGets, 3);
+ chai.assert.equal(runs.foobar, 1);
+
+ chai.assert.equal(field('bar', 'bar'), '000');
+ chai.assert.equal(numGets, 3);
+ chai.assert.equal(runs.barbar, 1);
+
+ reactiveDict.set('foofoo', 'bar');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(field('foo', 'foo'), 'bar');
+ chai.assert.equal(numGets, 4);
+ chai.assert.equal(runs.foofoo, 2);
+ chai.assert.equal(runs.foobar, 1);
+ chai.assert.equal(runs.barbar, 1);
+
+ reactiveDict.set('foobar', 'bar');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(field('foo', 'bar'), 'bar');
+ chai.assert.equal(numGets, 5);
+ chai.assert.equal(runs.foofoo, 2);
+ chai.assert.equal(runs.foobar, 2);
+ chai.assert.equal(runs.barbar, 1);
+
+ reactiveDict.set('barbar', 'bar');
+ Tracker.flush({ _throwFirstError: true });
+ chai.assert.equal(field('bar', 'bar'), 'bar');
+ chai.assert.equal(numGets, 6);
+ chai.assert.equal(runs.foofoo, 2);
+ chai.assert.equal(runs.foobar, 2);
+ chai.assert.equal(runs.barbar, 2);
+ });
+});
diff --git a/npm-packages/meteor-reactive-cache/tests/package.json b/npm-packages/meteor-reactive-cache/tests/package.json
new file mode 100644
index 000000000..0c2063900
--- /dev/null
+++ b/npm-packages/meteor-reactive-cache/tests/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "tests",
+ "private": true,
+ "scripts": {
+ "start": "meteor run"
+ },
+ "eslintConfig": {
+ "extends": [
+ "airbnb-base",
+ "plugin:meteor/recommended"
+ ],
+ "plugins": [
+ "meteor"
+ ],
+ "parser": "@babel/eslint-parser",
+ "rules": {
+ "meteor/audit-argument-checks": 0,
+ "import/no-extraneous-dependencies": 0,
+ "import/extensions": 0,
+ "import/no-unresolved": 0,
+ "no-underscore-dangle": 0
+ }
+ },
+ "dependencies": {
+ "@babel/runtime": "^7.26.0",
+ "meteor-node-stubs": "^1.2.13",
+ "meteor-reactive-cache": "^1.0.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.26.0",
+ "@babel/eslint-parser": "^7.25.9",
+ "chai": "^4.5.0",
+ "eslint": "^8.57.1",
+ "eslint-config-airbnb-base": "^15.0.0",
+ "eslint-plugin-import": "^2.31.0",
+ "eslint-plugin-meteor": "^7.3.0",
+ "lodash.isequal": "^4.5.0",
+ "mocha": "^10.8.2"
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index bbb208d1c..6bd66c443 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -205,9 +205,7 @@
}
},
"@wekanteam/meteor-reactive-cache": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/@wekanteam/meteor-reactive-cache/-/meteor-reactive-cache-1.0.7.tgz",
- "integrity": "sha512-PSxoCX46sGcLygaKN/i/DrtPbKbm8AOnuNzK8lBE1BQTFkdnr7KBG2neGjFDbwLRHGmvvSfYStUmPtAk6xfx8w==",
+ "version": "file:npm-packages/meteor-reactive-cache",
"requires": {
"@wekanteam/meteor-globals": "^1.1.6"
}
diff --git a/package.json b/package.json
index 73a25f4c6..6a0ed05ba 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
"@wekanteam/exceljs": "^4.6.0",
"@wekanteam/html-to-markdown": "^1.0.2",
"@wekanteam/meteor-globals": "^1.1.6",
- "@wekanteam/meteor-reactive-cache": "^1.0.7",
+ "@wekanteam/meteor-reactive-cache": "file:./npm-packages/meteor-reactive-cache",
"ajv": "^6.12.6",
"bcryptjs": "^2.4.3",
"bson": "^4.7.2",
diff --git a/server/attachmentApi.js b/server/attachmentApi.js
index 220b43727..e1d46003d 100644
--- a/server/attachmentApi.js
+++ b/server/attachmentApi.js
@@ -12,7 +12,7 @@ import { ObjectID } from 'bson';
if (Meteor.isServer) {
Meteor.methods({
// Upload attachment via API
- 'api.attachment.upload'(boardId, swimlaneId, listId, cardId, fileData, fileName, fileType, storageBackend) {
+ async 'api.attachment.upload'(boardId, swimlaneId, listId, cardId, fileData, fileName, fileType, storageBackend) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
@@ -23,12 +23,12 @@ if (Meteor.isServer) {
}
// Check if user has permission to modify the card
- const card = ReactiveCache.getCard(cardId);
+ const card = await ReactiveCache.getCard(cardId);
if (!card) {
throw new Meteor.Error('card-not-found', 'Card not found');
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
@@ -114,19 +114,19 @@ if (Meteor.isServer) {
},
// Download attachment via API
- 'api.attachment.download'(attachmentId) {
+ async 'api.attachment.download'(attachmentId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
// Get attachment
- const attachment = ReactiveCache.getAttachment(attachmentId);
+ const attachment = await ReactiveCache.getAttachment(attachmentId);
if (!attachment) {
throw new Meteor.Error('attachment-not-found', 'Attachment not found');
}
// Check permissions
- const board = ReactiveCache.getBoard(attachment.meta.boardId);
+ const board = await ReactiveCache.getBoard(attachment.meta.boardId);
if (!board || !board.isBoardMember(this.userId)) {
throw new Meteor.Error('not-authorized', 'You do not have permission to access this attachment');
}
@@ -173,13 +173,13 @@ if (Meteor.isServer) {
},
// List attachments for board, swimlane, list, or card
- 'api.attachment.list'(boardId, swimlaneId, listId, cardId) {
+ async 'api.attachment.list'(boardId, swimlaneId, listId, cardId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
// Check permissions
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.isBoardMember(this.userId)) {
throw new Meteor.Error('not-authorized', 'You do not have permission to access this board');
}
@@ -199,8 +199,8 @@ if (Meteor.isServer) {
query['meta.cardId'] = cardId;
}
- const attachments = ReactiveCache.getAttachments(query);
-
+ const attachments = await ReactiveCache.getAttachments(query);
+
const attachmentList = attachments.map(attachment => {
const strategy = fileStoreStrategyFactory.getFileStrategy(attachment, 'original');
return {
@@ -230,25 +230,25 @@ if (Meteor.isServer) {
},
// Copy attachment to another card
- 'api.attachment.copy'(attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId) {
+ async 'api.attachment.copy'(attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
// Get source attachment
- const sourceAttachment = ReactiveCache.getAttachment(attachmentId);
+ const sourceAttachment = await ReactiveCache.getAttachment(attachmentId);
if (!sourceAttachment) {
throw new Meteor.Error('attachment-not-found', 'Source attachment not found');
}
// Check source permissions
- const sourceBoard = ReactiveCache.getBoard(sourceAttachment.meta.boardId);
+ const sourceBoard = await ReactiveCache.getBoard(sourceAttachment.meta.boardId);
if (!sourceBoard || !sourceBoard.isBoardMember(this.userId)) {
throw new Meteor.Error('not-authorized', 'You do not have permission to access the source attachment');
}
// Check target permissions
- const targetBoard = ReactiveCache.getBoard(targetBoardId);
+ const targetBoard = await ReactiveCache.getBoard(targetBoardId);
if (!targetBoard || !targetBoard.isBoardMember(this.userId)) {
throw new Meteor.Error('not-authorized', 'You do not have permission to modify the target card');
}
@@ -328,25 +328,25 @@ if (Meteor.isServer) {
},
// Move attachment to another card
- 'api.attachment.move'(attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId) {
+ async 'api.attachment.move'(attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
// Get source attachment
- const sourceAttachment = ReactiveCache.getAttachment(attachmentId);
+ const sourceAttachment = await ReactiveCache.getAttachment(attachmentId);
if (!sourceAttachment) {
throw new Meteor.Error('attachment-not-found', 'Source attachment not found');
}
// Check source permissions
- const sourceBoard = ReactiveCache.getBoard(sourceAttachment.meta.boardId);
+ const sourceBoard = await ReactiveCache.getBoard(sourceAttachment.meta.boardId);
if (!sourceBoard || !sourceBoard.isBoardMember(this.userId)) {
throw new Meteor.Error('not-authorized', 'You do not have permission to access the source attachment');
}
// Check target permissions
- const targetBoard = ReactiveCache.getBoard(targetBoardId);
+ const targetBoard = await ReactiveCache.getBoard(targetBoardId);
if (!targetBoard || !targetBoard.isBoardMember(this.userId)) {
throw new Meteor.Error('not-authorized', 'You do not have permission to modify the target card');
}
@@ -385,19 +385,19 @@ if (Meteor.isServer) {
},
// Delete attachment via API
- 'api.attachment.delete'(attachmentId) {
+ async 'api.attachment.delete'(attachmentId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
// Get attachment
- const attachment = ReactiveCache.getAttachment(attachmentId);
+ const attachment = await ReactiveCache.getAttachment(attachmentId);
if (!attachment) {
throw new Meteor.Error('attachment-not-found', 'Attachment not found');
}
// Check permissions
- const board = ReactiveCache.getBoard(attachment.meta.boardId);
+ const board = await ReactiveCache.getBoard(attachment.meta.boardId);
if (!board || !board.isBoardMember(this.userId)) {
throw new Meteor.Error('not-authorized', 'You do not have permission to delete this attachment');
}
@@ -419,19 +419,19 @@ if (Meteor.isServer) {
},
// Get attachment info via API
- 'api.attachment.info'(attachmentId) {
+ async 'api.attachment.info'(attachmentId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
// Get attachment
- const attachment = ReactiveCache.getAttachment(attachmentId);
+ const attachment = await ReactiveCache.getAttachment(attachmentId);
if (!attachment) {
throw new Meteor.Error('attachment-not-found', 'Attachment not found');
}
// Check permissions
- const board = ReactiveCache.getBoard(attachment.meta.boardId);
+ const board = await ReactiveCache.getBoard(attachment.meta.boardId);
if (!board || !board.isBoardMember(this.userId)) {
throw new Meteor.Error('not-authorized', 'You do not have permission to access this attachment');
}
diff --git a/server/attachmentMigration.js b/server/attachmentMigration.js
index e6c287999..c436ddcfd 100644
--- a/server/attachmentMigration.js
+++ b/server/attachmentMigration.js
@@ -151,13 +151,13 @@ class AttachmentMigrationService {
async migrateAttachment(attachment) {
try {
// Get the card to find board and list information
- const card = ReactiveCache.getCard(attachment.cardId);
+ const card = await ReactiveCache.getCard(attachment.cardId);
if (!card) {
console.warn(`Card not found for attachment ${attachment._id}`);
return;
}
- const list = ReactiveCache.getList(card.listId);
+ const list = await ReactiveCache.getList(card.listId);
if (!list) {
console.warn(`List not found for attachment ${attachment._id}`);
return;
@@ -259,12 +259,12 @@ Meteor.methods({
throw new Meteor.Error('not-authorized');
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
const isBoardAdmin = board.hasAdmin(this.userId);
const isInstanceAdmin = user && user.isAdmin;
@@ -275,14 +275,14 @@ Meteor.methods({
return await attachmentMigrationService.migrateBoardAttachments(boardId);
},
- 'attachmentMigration.getProgress'(boardId) {
+ async 'attachmentMigration.getProgress'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -290,14 +290,14 @@ Meteor.methods({
return attachmentMigrationService.getMigrationProgress(boardId);
},
- 'attachmentMigration.getUnconvertedAttachments'(boardId) {
+ async 'attachmentMigration.getUnconvertedAttachments'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -305,14 +305,14 @@ Meteor.methods({
return attachmentMigrationService.getUnconvertedAttachments(boardId);
},
- 'attachmentMigration.isBoardMigrated'(boardId) {
+ async 'attachmentMigration.isBoardMigrated'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
diff --git a/server/authentication.js b/server/authentication.js
index 5b7ad2ee6..77aac7f9f 100644
--- a/server/authentication.js
+++ b/server/authentication.js
@@ -15,13 +15,13 @@ Meteor.startup(() => {
Authentication = {};
- Authentication.checkUserId = function(userId) {
+ Authentication.checkUserId = async function(userId) {
if (userId === undefined) {
const error = new Meteor.Error('Unauthorized', 'Unauthorized');
error.statusCode = 401;
throw error;
}
- const admin = ReactiveCache.getUser({ _id: userId, isAdmin: true });
+ const admin = await ReactiveCache.getUser({ _id: userId, isAdmin: true });
if (admin === undefined) {
const error = new Meteor.Error('Forbidden', 'Forbidden');
@@ -42,9 +42,9 @@ Meteor.startup(() => {
// An admin should be authorized to access everything, so we use a separate check for admins
// This throws an error if otherReq is false and the user is not an admin
- Authentication.checkAdminOrCondition = function(userId, otherReq) {
+ Authentication.checkAdminOrCondition = async function(userId, otherReq) {
if (otherReq) return;
- const admin = ReactiveCache.getUser({ _id: userId, isAdmin: true });
+ const admin = await ReactiveCache.getUser({ _id: userId, isAdmin: true });
if (admin === undefined) {
const error = new Meteor.Error('Forbidden', 'Forbidden');
error.statusCode = 403;
@@ -53,19 +53,19 @@ Meteor.startup(() => {
};
// Helper function. Will throw an error if the user is not active BoardAdmin or active Normal user of the board.
- Authentication.checkBoardAccess = function(userId, boardId) {
+ Authentication.checkBoardAccess = async function(userId, boardId) {
Authentication.checkLoggedIn(userId);
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
const normalAccess = board.members.some(e => e.userId === userId && e.isActive && !e.isNoComments && !e.isCommentOnly && !e.isWorker);
- Authentication.checkAdminOrCondition(userId, normalAccess);
+ await Authentication.checkAdminOrCondition(userId, normalAccess);
};
// Helper function. Will throw an error if the user does not have write access to the board (excludes read-only users).
- Authentication.checkBoardWriteAccess = function(userId, boardId) {
+ Authentication.checkBoardWriteAccess = async function(userId, boardId) {
Authentication.checkLoggedIn(userId);
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
const writeAccess = board.members.some(e => e.userId === userId && e.isActive && !e.isNoComments && !e.isCommentOnly && !e.isWorker && !e.isReadOnly && !e.isReadAssignedOnly);
- Authentication.checkAdminOrCondition(userId, writeAccess);
+ await Authentication.checkAdminOrCondition(userId, writeAccess);
};
if (Meteor.isServer) {
diff --git a/server/cronMigrationManager.js b/server/cronMigrationManager.js
index a1e9fb2c4..7ae295ffb 100644
--- a/server/cronMigrationManager.js
+++ b/server/cronMigrationManager.js
@@ -1646,8 +1646,8 @@ class CronMigrationManager {
SyncedCron.add({
name: step.cronName,
schedule: (parser) => parser.text(step.schedule),
- job: () => {
- this.runMigrationStep(step);
+ job: async () => {
+ await this.runMigrationStep(step);
},
});
}
@@ -2773,130 +2773,130 @@ Meteor.startup(() => {
// Meteor methods for client-server communication
Meteor.methods({
- 'cron.startAllMigrations'() {
+ async 'cron.startAllMigrations'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
return cronMigrationManager.startAllMigrations();
},
-
- 'cron.startSpecificMigration'(migrationIndex) {
+
+ async 'cron.startSpecificMigration'(migrationIndex) {
check(migrationIndex, Number);
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
return cronMigrationManager.startSpecificMigration(migrationIndex);
},
-
- 'cron.startJob'(cronName) {
+
+ async 'cron.startJob'(cronName) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
return cronMigrationManager.startCronJob(cronName);
},
-
- 'cron.stopJob'(cronName) {
+
+ async 'cron.stopJob'(cronName) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
return cronMigrationManager.stopCronJob(cronName);
},
-
- 'cron.pauseJob'(cronName) {
+
+ async 'cron.pauseJob'(cronName) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
return cronMigrationManager.pauseCronJob(cronName);
},
-
- 'cron.resumeJob'(cronName) {
+
+ async 'cron.resumeJob'(cronName) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
return cronMigrationManager.resumeCronJob(cronName);
},
-
- 'cron.removeJob'(cronName) {
+
+ async 'cron.removeJob'(cronName) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
return cronMigrationManager.removeCronJob(cronName);
},
-
- 'cron.addJob'(jobData) {
+
+ async 'cron.addJob'(jobData) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
return cronMigrationManager.addCronJob(jobData);
},
-
- 'cron.getJobs'() {
+
+ async 'cron.getJobs'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
return cronMigrationManager.getAllCronJobs();
},
-
- 'cron.getMigrationProgress'() {
+
+ async 'cron.getMigrationProgress'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -2970,12 +2970,12 @@ Meteor.methods({
};
},
- 'cron.pauseAllMigrations'() {
+ async 'cron.pauseAllMigrations'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -2983,12 +2983,12 @@ Meteor.methods({
return cronMigrationManager.pauseAllMigrations();
},
- 'cron.stopAllMigrations'() {
+ async 'cron.stopAllMigrations'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -2996,12 +2996,25 @@ Meteor.methods({
return cronMigrationManager.stopAllMigrations();
},
- 'cron.resumeAllMigrations'() {
+ async 'cron.stopAllMigrations'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
+ if (!user || !user.isAdmin) {
+ throw new Meteor.Error('not-authorized', 'Admin access required');
+ }
+
+ return cronMigrationManager.stopAllMigrations();
+ },
+
+ async 'cron.resumeAllMigrations'() {
+ const userId = this.userId;
+ if (!userId) {
+ throw new Meteor.Error('not-authorized', 'Must be logged in');
+ }
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3009,12 +3022,12 @@ Meteor.methods({
return cronMigrationManager.resumeAllMigrations();
},
- 'cron.retryFailedMigrations'() {
+ async 'cron.retryFailedMigrations'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3022,14 +3035,14 @@ Meteor.methods({
return cronMigrationManager.retryFailedMigrations();
},
- 'cron.getAllMigrationErrors'(limit = 50) {
+ async 'cron.getAllMigrationErrors'(limit = 50) {
check(limit, Match.Optional(Number));
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3037,7 +3050,7 @@ Meteor.methods({
return cronMigrationManager.getAllMigrationErrors(limit);
},
- 'cron.getJobErrors'(jobId, options = {}) {
+ async 'cron.getJobErrors'(jobId, options = {}) {
check(jobId, String);
check(options, Match.Optional(Object));
@@ -3045,7 +3058,7 @@ Meteor.methods({
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3053,12 +3066,12 @@ Meteor.methods({
return cronMigrationManager.getJobErrors(jobId, options);
},
- 'cron.getMigrationStats'() {
+ async 'cron.getMigrationStats'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3066,15 +3079,15 @@ Meteor.methods({
return cronMigrationManager.getMigrationStats();
},
- 'cron.startBoardOperation'(boardId, operationType, operationData) {
+ async 'cron.startBoardOperation'(boardId, operationType, operationData) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
// Check if user is global admin OR board admin
- const user = ReactiveCache.getUser(userId);
- const board = ReactiveCache.getBoard(boardId);
+ const user = await ReactiveCache.getUser(userId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!user) {
throw new Meteor.Error('not-authorized', 'User not found');
@@ -3097,15 +3110,15 @@ Meteor.methods({
return cronMigrationManager.startBoardOperation(boardId, operationType, operationData);
},
- 'cron.getBoardOperations'(boardId) {
+ async 'cron.getBoardOperations'(boardId) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
// Check if user is global admin OR board admin
- const user = ReactiveCache.getUser(userId);
- const board = ReactiveCache.getBoard(boardId);
+ const user = await ReactiveCache.getUser(userId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!user) {
throw new Meteor.Error('not-authorized', 'User not found');
@@ -3128,12 +3141,12 @@ Meteor.methods({
return cronMigrationManager.getBoardOperations(boardId);
},
- 'cron.getAllBoardOperations'(page, limit, searchTerm) {
+ async 'cron.getAllBoardOperations'(page, limit, searchTerm) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3141,12 +3154,12 @@ Meteor.methods({
return cronMigrationManager.getAllBoardOperations(page, limit, searchTerm);
},
- 'cron.getBoardOperationStats'() {
+ async 'cron.getBoardOperationStats'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3154,12 +3167,12 @@ Meteor.methods({
return cronMigrationManager.getBoardOperationStats();
},
- 'cron.getJobDetails'(jobId) {
+ async 'cron.getJobDetails'(jobId) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3167,12 +3180,12 @@ Meteor.methods({
return cronJobStorage.getJobDetails(jobId);
},
- 'cron.getQueueStats'() {
+ async 'cron.getQueueStats'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3180,12 +3193,12 @@ Meteor.methods({
return cronJobStorage.getQueueStats();
},
- 'cron.getSystemResources'() {
+ async 'cron.getSystemResources'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3193,12 +3206,12 @@ Meteor.methods({
return cronJobStorage.getSystemResources();
},
- 'cron.clearAllJobs'() {
+ async 'cron.clearAllJobs'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3206,12 +3219,12 @@ Meteor.methods({
return cronMigrationManager.clearAllCronJobs();
},
- 'cron.pauseJob'(jobId) {
+ async 'cron.pauseJob'(jobId) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3221,12 +3234,12 @@ Meteor.methods({
return { success: true };
},
- 'cron.resumeJob'(jobId) {
+ async 'cron.resumeJob'(jobId) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3236,12 +3249,12 @@ Meteor.methods({
return { success: true };
},
- 'cron.stopJob'(jobId) {
+ async 'cron.stopJob'(jobId) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3254,12 +3267,12 @@ Meteor.methods({
return { success: true };
},
- 'cron.cleanupOldJobs'(daysOld) {
+ async 'cron.cleanupOldJobs'(daysOld) {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3267,12 +3280,76 @@ Meteor.methods({
return cronJobStorage.cleanupOldJobs(daysOld);
},
- 'cron.getBoardMigrationStats'() {
+ async 'cron.pauseAllMigrations'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
+ if (!user || !user.isAdmin) {
+ throw new Meteor.Error('not-authorized', 'Admin access required');
+ }
+
+ // Pause all running jobs in the queue
+ const runningJobs = cronJobStorage.getIncompleteJobs().filter(job => job.status === 'running');
+ runningJobs.forEach(job => {
+ cronJobStorage.updateQueueStatus(job.jobId, 'paused');
+ cronJobStorage.saveJobStatus(job.jobId, { status: 'paused' });
+ });
+
+ cronMigrationStatus.set('All migrations paused');
+ return { success: true, message: 'All migrations paused' };
+ },
+
+ async 'cron.stopAllMigrations'() {
+ const userId = this.userId;
+ if (!userId) {
+ throw new Meteor.Error('not-authorized', 'Must be logged in');
+ }
+ const user = await ReactiveCache.getUser(userId);
+ if (!user || !user.isAdmin) {
+ throw new Meteor.Error('not-authorized', 'Admin access required');
+ }
+
+ // Clear monitor interval first to prevent status override
+ if (cronMigrationManager.monitorInterval) {
+ Meteor.clearInterval(cronMigrationManager.monitorInterval);
+ cronMigrationManager.monitorInterval = null;
+ }
+
+ // Stop all running and pending jobs
+ const incompleteJobs = cronJobStorage.getIncompleteJobs();
+ incompleteJobs.forEach(job => {
+ cronJobStorage.updateQueueStatus(job.jobId, 'stopped', { stoppedAt: new Date() });
+ cronJobStorage.saveJobStatus(job.jobId, {
+ status: 'stopped',
+ stoppedAt: new Date()
+ });
+ });
+
+ // Reset migration state immediately
+ cronMigrationManager.isRunning = false;
+ cronIsMigrating.set(false);
+ cronMigrationProgress.set(0);
+ cronMigrationCurrentStep.set('');
+ cronMigrationCurrentStepNum.set(0);
+ cronMigrationTotalSteps.set(0);
+ cronMigrationStatus.set('All migrations stopped');
+
+ // Clear status message after delay
+ setTimeout(() => {
+ cronMigrationStatus.set('');
+ }, 3000);
+
+ return { success: true, message: 'All migrations stopped' };
+ },
+
+ async 'cron.getBoardMigrationStats'() {
+ const userId = this.userId;
+ if (!userId) {
+ throw new Meteor.Error('not-authorized', 'Must be logged in');
+ }
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
@@ -3282,12 +3359,12 @@ Meteor.methods({
return boardMigrationDetector.getMigrationStats();
},
- 'cron.forceBoardMigrationScan'() {
+ async 'cron.forceBoardMigrationScan'() {
const userId = this.userId;
if (!userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin access required');
}
diff --git a/server/lib/emailLocalization.js b/server/lib/emailLocalization.js
index 4c8bd0b17..19f0e8992 100644
--- a/server/lib/emailLocalization.js
+++ b/server/lib/emailLocalization.js
@@ -17,13 +17,13 @@ EmailLocalization = {
* @param {String} options.language - Language code to use (if not provided, will try to detect)
* @param {String} options.userId - User ID to determine language (if not provided with language)
*/
- sendEmail(options) {
+ async sendEmail(options) {
// Determine the language to use
let lang = options.language;
// If no language is specified but we have a userId, try to get the user's language
if (!lang && options.userId) {
- const user = ReactiveCache.getUser(options.userId);
+ const user = await ReactiveCache.getUser(options.userId);
if (user) {
lang = user.getLanguage();
}
diff --git a/server/lib/utils.js b/server/lib/utils.js
index a19456b1a..d4f5667bb 100644
--- a/server/lib/utils.js
+++ b/server/lib/utils.js
@@ -27,12 +27,12 @@ allowIsBoardMemberWithWriteAccess = function(userId, board) {
// Check if user has write access via a card's board
allowIsBoardMemberWithWriteAccessByCard = function(userId, card) {
- const board = card && card.board && card.board();
+ const board = card && Boards.findOne(card.boardId);
return allowIsBoardMemberWithWriteAccess(userId, board);
};
allowIsBoardMemberByCard = function(userId, card) {
- const board = card.board();
+ const board = card && Boards.findOne(card.boardId);
return board && board.hasMember(userId);
};
diff --git a/server/methods/fixDuplicateLists.js b/server/methods/fixDuplicateLists.js
index 7647f3b89..12aebc069 100644
--- a/server/methods/fixDuplicateLists.js
+++ b/server/methods/fixDuplicateLists.js
@@ -11,12 +11,12 @@ import { ReactiveCache } from '/imports/reactiveCache';
* This method identifies and removes duplicate lists while preserving cards
*/
Meteor.methods({
- 'fixDuplicateLists.fixAllBoards'() {
+ async 'fixDuplicateLists.fixAllBoards'() {
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- if (!ReactiveCache.getUser(this.userId).isAdmin) {
+ if (!(await ReactiveCache.getUser(this.userId)).isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin required');
}
@@ -53,14 +53,14 @@ Meteor.methods({
};
},
- 'fixDuplicateLists.fixBoard'(boardId) {
+ async 'fixDuplicateLists.fixBoard'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.hasAdmin(this.userId)) {
throw new Meteor.Error('not-authorized');
}
@@ -208,12 +208,12 @@ function fixDuplicateLists(boardId) {
}
Meteor.methods({
- 'fixDuplicateLists.getReport'() {
+ async 'fixDuplicateLists.getReport'() {
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- if (!ReactiveCache.getUser(this.userId).isAdmin) {
+ if (!(await ReactiveCache.getUser(this.userId)).isAdmin) {
throw new Meteor.Error('not-authorized', 'Admin required');
}
diff --git a/server/methods/lockedUsers.js b/server/methods/lockedUsers.js
index a5de5075b..7458b832e 100644
--- a/server/methods/lockedUsers.js
+++ b/server/methods/lockedUsers.js
@@ -8,7 +8,7 @@ Meteor.methods({
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('error-not-allowed', 'Not allowed');
}
@@ -56,7 +56,7 @@ Meteor.methods({
if (!adminId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user');
}
- const admin = ReactiveCache.getUser(adminId);
+ const admin = await ReactiveCache.getUser(adminId);
if (!admin || !admin.isAdmin) {
throw new Meteor.Error('error-not-allowed', 'Not allowed');
}
@@ -86,7 +86,7 @@ Meteor.methods({
if (!adminId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user');
}
- const admin = ReactiveCache.getUser(adminId);
+ const admin = await ReactiveCache.getUser(adminId);
if (!admin || !admin.isAdmin) {
throw new Meteor.Error('error-not-allowed', 'Not allowed');
}
diff --git a/server/methods/lockoutSettings.js b/server/methods/lockoutSettings.js
index cf12b083a..70b8efead 100644
--- a/server/methods/lockoutSettings.js
+++ b/server/methods/lockoutSettings.js
@@ -9,7 +9,7 @@ Meteor.methods({
if (!userId) {
throw new Meteor.Error('error-invalid-user', 'Invalid user');
}
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('error-not-allowed', 'Not allowed');
}
diff --git a/server/methods/positionHistory.js b/server/methods/positionHistory.js
index ed98ad640..95a585a88 100644
--- a/server/methods/positionHistory.js
+++ b/server/methods/positionHistory.js
@@ -13,19 +13,19 @@ Meteor.methods({
/**
* Track original position for a swimlane
*/
- 'positionHistory.trackSwimlane'(swimlaneId) {
+ async 'positionHistory.trackSwimlane'(swimlaneId) {
check(swimlaneId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const swimlane = Swimlanes.findOne(swimlaneId);
+ const swimlane = await Swimlanes.findOneAsync(swimlaneId);
if (!swimlane) {
throw new Meteor.Error('swimlane-not-found', 'Swimlane not found');
}
- const board = ReactiveCache.getBoard(swimlane.boardId);
+ const board = await ReactiveCache.getBoard(swimlane.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -36,19 +36,19 @@ Meteor.methods({
/**
* Track original position for a list
*/
- 'positionHistory.trackList'(listId) {
+ async 'positionHistory.trackList'(listId) {
check(listId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const list = Lists.findOne(listId);
+ const list = await Lists.findOneAsync(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
- const board = ReactiveCache.getBoard(list.boardId);
+ const board = await ReactiveCache.getBoard(list.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -59,19 +59,19 @@ Meteor.methods({
/**
* Track original position for a card
*/
- 'positionHistory.trackCard'(cardId) {
+ async 'positionHistory.trackCard'(cardId) {
check(cardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const card = Cards.findOne(cardId);
+ const card = await Cards.findOneAsync(cardId);
if (!card) {
throw new Meteor.Error('card-not-found', 'Card not found');
}
- const board = ReactiveCache.getBoard(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -82,19 +82,19 @@ Meteor.methods({
/**
* Get original position for a swimlane
*/
- 'positionHistory.getSwimlaneOriginalPosition'(swimlaneId) {
+ async 'positionHistory.getSwimlaneOriginalPosition'(swimlaneId) {
check(swimlaneId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const swimlane = Swimlanes.findOne(swimlaneId);
+ const swimlane = await Swimlanes.findOneAsync(swimlaneId);
if (!swimlane) {
throw new Meteor.Error('swimlane-not-found', 'Swimlane not found');
}
- const board = ReactiveCache.getBoard(swimlane.boardId);
+ const board = await ReactiveCache.getBoard(swimlane.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -105,19 +105,19 @@ Meteor.methods({
/**
* Get original position for a list
*/
- 'positionHistory.getListOriginalPosition'(listId) {
+ async 'positionHistory.getListOriginalPosition'(listId) {
check(listId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const list = Lists.findOne(listId);
+ const list = await Lists.findOneAsync(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
- const board = ReactiveCache.getBoard(list.boardId);
+ const board = await ReactiveCache.getBoard(list.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -128,19 +128,19 @@ Meteor.methods({
/**
* Get original position for a card
*/
- 'positionHistory.getCardOriginalPosition'(cardId) {
+ async 'positionHistory.getCardOriginalPosition'(cardId) {
check(cardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const card = Cards.findOne(cardId);
+ const card = await Cards.findOneAsync(cardId);
if (!card) {
throw new Meteor.Error('card-not-found', 'Card not found');
}
- const board = ReactiveCache.getBoard(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -151,19 +151,19 @@ Meteor.methods({
/**
* Check if a swimlane has moved from its original position
*/
- 'positionHistory.hasSwimlaneMoved'(swimlaneId) {
+ async 'positionHistory.hasSwimlaneMoved'(swimlaneId) {
check(swimlaneId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const swimlane = Swimlanes.findOne(swimlaneId);
+ const swimlane = await Swimlanes.findOneAsync(swimlaneId);
if (!swimlane) {
throw new Meteor.Error('swimlane-not-found', 'Swimlane not found');
}
- const board = ReactiveCache.getBoard(swimlane.boardId);
+ const board = await ReactiveCache.getBoard(swimlane.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -174,19 +174,19 @@ Meteor.methods({
/**
* Check if a list has moved from its original position
*/
- 'positionHistory.hasListMoved'(listId) {
+ async 'positionHistory.hasListMoved'(listId) {
check(listId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const list = Lists.findOne(listId);
+ const list = await Lists.findOneAsync(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
- const board = ReactiveCache.getBoard(list.boardId);
+ const board = await ReactiveCache.getBoard(list.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -197,19 +197,19 @@ Meteor.methods({
/**
* Check if a card has moved from its original position
*/
- 'positionHistory.hasCardMoved'(cardId) {
+ async 'positionHistory.hasCardMoved'(cardId) {
check(cardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const card = Cards.findOne(cardId);
+ const card = await Cards.findOneAsync(cardId);
if (!card) {
throw new Meteor.Error('card-not-found', 'Card not found');
}
- const board = ReactiveCache.getBoard(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -220,19 +220,19 @@ Meteor.methods({
/**
* Get original position description for a swimlane
*/
- 'positionHistory.getSwimlaneDescription'(swimlaneId) {
+ async 'positionHistory.getSwimlaneDescription'(swimlaneId) {
check(swimlaneId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const swimlane = Swimlanes.findOne(swimlaneId);
+ const swimlane = await Swimlanes.findOneAsync(swimlaneId);
if (!swimlane) {
throw new Meteor.Error('swimlane-not-found', 'Swimlane not found');
}
- const board = ReactiveCache.getBoard(swimlane.boardId);
+ const board = await ReactiveCache.getBoard(swimlane.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -243,19 +243,19 @@ Meteor.methods({
/**
* Get original position description for a list
*/
- 'positionHistory.getListDescription'(listId) {
+ async 'positionHistory.getListDescription'(listId) {
check(listId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const list = Lists.findOne(listId);
+ const list = await Lists.findOneAsync(listId);
if (!list) {
throw new Meteor.Error('list-not-found', 'List not found');
}
- const board = ReactiveCache.getBoard(list.boardId);
+ const board = await ReactiveCache.getBoard(list.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -266,19 +266,19 @@ Meteor.methods({
/**
* Get original position description for a card
*/
- 'positionHistory.getCardDescription'(cardId) {
+ async 'positionHistory.getCardDescription'(cardId) {
check(cardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const card = Cards.findOne(cardId);
+ const card = await Cards.findOneAsync(cardId);
if (!card) {
throw new Meteor.Error('card-not-found', 'Card not found');
}
- const board = ReactiveCache.getBoard(card.boardId);
+ const board = await ReactiveCache.getBoard(card.boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -289,14 +289,14 @@ Meteor.methods({
/**
* Get all position history for a board
*/
- 'positionHistory.getBoardHistory'(boardId) {
+ async 'positionHistory.getBoardHistory'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -305,13 +305,13 @@ Meteor.methods({
boardId: boardId,
}, {
sort: { createdAt: -1 }
- }).fetch();
+ }).fetchAsync();
},
/**
* Get position history by entity type for a board
*/
- 'positionHistory.getBoardHistoryByType'(boardId, entityType) {
+ async 'positionHistory.getBoardHistoryByType'(boardId, entityType) {
check(boardId, String);
check(entityType, String);
@@ -319,7 +319,7 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'You must be logged in.');
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.isVisibleBy({ _id: this.userId })) {
throw new Meteor.Error('not-authorized', 'You do not have access to this board.');
}
@@ -333,6 +333,6 @@ Meteor.methods({
entityType: entityType,
}, {
sort: { createdAt: -1 }
- }).fetch();
+ }).fetchAsync();
},
});
diff --git a/server/migrations/comprehensiveBoardMigration.js b/server/migrations/comprehensiveBoardMigration.js
index ee380a447..59e90afc2 100644
--- a/server/migrations/comprehensiveBoardMigration.js
+++ b/server/migrations/comprehensiveBoardMigration.js
@@ -41,9 +41,9 @@ class ComprehensiveBoardMigration {
/**
* Check if migration is needed for a board
*/
- needsMigration(boardId) {
+ async needsMigration(boardId) {
try {
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) return false;
// Check if board has already been processed
@@ -52,7 +52,7 @@ class ComprehensiveBoardMigration {
}
// Check for various issues that need migration
- const issues = this.detectMigrationIssues(boardId);
+ const issues = await this.detectMigrationIssues(boardId);
return issues.length > 0;
} catch (error) {
@@ -64,13 +64,13 @@ class ComprehensiveBoardMigration {
/**
* Detect all migration issues in a board
*/
- detectMigrationIssues(boardId) {
+ async detectMigrationIssues(boardId) {
const issues = [];
try {
- const cards = ReactiveCache.getCards({ boardId });
- const lists = ReactiveCache.getLists({ boardId });
- const swimlanes = ReactiveCache.getSwimlanes({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
+ const lists = await ReactiveCache.getLists({ boardId });
+ const swimlanes = await ReactiveCache.getSwimlanes({ boardId });
// Issue 1: Cards with missing swimlaneId
const cardsWithoutSwimlane = cards.filter(card => !card.swimlaneId);
@@ -157,7 +157,7 @@ class ComprehensiveBoardMigration {
console.log(`Starting comprehensive board migration for board ${boardId}`);
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Error(`Board ${boardId} not found`);
}
@@ -288,7 +288,7 @@ class ComprehensiveBoardMigration {
* Step 1: Analyze board structure
*/
async analyzeBoardStructure(boardId) {
- const issues = this.detectMigrationIssues(boardId);
+ const issues = await this.detectMigrationIssues(boardId);
return {
issues,
issueCount: issues.length,
@@ -300,9 +300,9 @@ class ComprehensiveBoardMigration {
* Step 2: Fix orphaned cards (cards with missing swimlaneId or listId)
*/
async fixOrphanedCards(boardId, progressCallback = null) {
- const cards = ReactiveCache.getCards({ boardId });
- const swimlanes = ReactiveCache.getSwimlanes({ boardId });
- const lists = ReactiveCache.getLists({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
+ const swimlanes = await ReactiveCache.getSwimlanes({ boardId });
+ const lists = await ReactiveCache.getLists({ boardId });
let cardsFixed = 0;
const defaultSwimlane = swimlanes.find(s => s.title === 'Default') || swimlanes[0];
@@ -370,9 +370,9 @@ class ComprehensiveBoardMigration {
* Step 3: Convert shared lists to per-swimlane lists
*/
async convertSharedListsToPerSwimlane(boardId, progressCallback = null) {
- const cards = ReactiveCache.getCards({ boardId });
- const lists = ReactiveCache.getLists({ boardId });
- const swimlanes = ReactiveCache.getSwimlanes({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
+ const lists = await ReactiveCache.getLists({ boardId });
+ const swimlanes = await ReactiveCache.getSwimlanes({ boardId });
let listsProcessed = 0;
let listsCreated = 0;
@@ -475,8 +475,8 @@ class ComprehensiveBoardMigration {
* Step 4: Ensure all lists are per-swimlane
*/
async ensurePerSwimlaneLists(boardId) {
- const lists = ReactiveCache.getLists({ boardId });
- const swimlanes = ReactiveCache.getSwimlanes({ boardId });
+ const lists = await ReactiveCache.getLists({ boardId });
+ const swimlanes = await ReactiveCache.getSwimlanes({ boardId });
const defaultSwimlane = swimlanes.find(s => s.title === 'Default') || swimlanes[0];
let listsProcessed = 0;
@@ -501,8 +501,8 @@ class ComprehensiveBoardMigration {
* Step 5: Cleanup empty lists (lists with no cards)
*/
async cleanupEmptyLists(boardId) {
- const lists = ReactiveCache.getLists({ boardId });
- const cards = ReactiveCache.getCards({ boardId });
+ const lists = await ReactiveCache.getLists({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
let listsRemoved = 0;
@@ -527,9 +527,9 @@ class ComprehensiveBoardMigration {
* Step 6: Validate migration
*/
async validateMigration(boardId) {
- const issues = this.detectMigrationIssues(boardId);
- const cards = ReactiveCache.getCards({ boardId });
- const lists = ReactiveCache.getLists({ boardId });
+ const issues = await this.detectMigrationIssues(boardId);
+ const cards = await ReactiveCache.getCards({ boardId });
+ const lists = await ReactiveCache.getLists({ boardId });
// Check that all cards have valid swimlaneId and listId
const validCards = cards.filter(card => card.swimlaneId && card.listId);
@@ -555,7 +555,7 @@ class ComprehensiveBoardMigration {
* Step 7: Fix avatar URLs (remove problematic auth parameters and fix URL formats)
*/
async fixAvatarUrls() {
- const users = ReactiveCache.getUsers({});
+ const users = await ReactiveCache.getUsers({});
let avatarsFixed = 0;
for (const user of users) {
@@ -610,7 +610,7 @@ class ComprehensiveBoardMigration {
* Step 8: Fix attachment URLs (remove problematic auth parameters and fix URL formats)
*/
async fixAttachmentUrls() {
- const attachments = ReactiveCache.getAttachments({});
+ const attachments = await ReactiveCache.getAttachments({});
let attachmentsFixed = 0;
for (const attachment of attachments) {
@@ -669,9 +669,9 @@ class ComprehensiveBoardMigration {
/**
* Get migration status for a board
*/
- getMigrationStatus(boardId) {
+ async getMigrationStatus(boardId) {
try {
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
return { status: 'board_not_found' };
}
@@ -684,8 +684,8 @@ class ComprehensiveBoardMigration {
};
}
- const needsMigration = this.needsMigration(boardId);
- const issues = this.detectMigrationIssues(boardId);
+ const needsMigration = await this.needsMigration(boardId);
+ const issues = await this.detectMigrationIssues(boardId);
return {
status: needsMigration ? 'needed' : 'not_needed',
@@ -705,25 +705,25 @@ export const comprehensiveBoardMigration = new ComprehensiveBoardMigration();
// Meteor methods
Meteor.methods({
- 'comprehensiveBoardMigration.check'(boardId) {
+ async 'comprehensiveBoardMigration.check'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- return comprehensiveBoardMigration.getMigrationStatus(boardId);
+ return await comprehensiveBoardMigration.getMigrationStatus(boardId);
},
- 'comprehensiveBoardMigration.execute'(boardId) {
+ async 'comprehensiveBoardMigration.execute'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- const user = ReactiveCache.getUser(this.userId);
- const board = ReactiveCache.getBoard(boardId);
+ const user = await ReactiveCache.getUser(this.userId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found');
}
@@ -735,52 +735,52 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'You must be a board admin or instance admin to perform this action.');
}
- return comprehensiveBoardMigration.executeMigration(boardId);
+ return await comprehensiveBoardMigration.executeMigration(boardId);
},
- 'comprehensiveBoardMigration.needsMigration'(boardId) {
+ async 'comprehensiveBoardMigration.needsMigration'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- return comprehensiveBoardMigration.needsMigration(boardId);
+ return await comprehensiveBoardMigration.needsMigration(boardId);
},
- 'comprehensiveBoardMigration.detectIssues'(boardId) {
+ async 'comprehensiveBoardMigration.detectIssues'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- return comprehensiveBoardMigration.detectMigrationIssues(boardId);
+ return await comprehensiveBoardMigration.detectMigrationIssues(boardId);
},
- 'comprehensiveBoardMigration.fixAvatarUrls'() {
+ async 'comprehensiveBoardMigration.fixAvatarUrls'() {
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Only instance admins can perform this action.');
}
- return comprehensiveBoardMigration.fixAvatarUrls();
+ return await comprehensiveBoardMigration.fixAvatarUrls();
},
- 'comprehensiveBoardMigration.fixAttachmentUrls'() {
+ async 'comprehensiveBoardMigration.fixAttachmentUrls'() {
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user || !user.isAdmin) {
throw new Meteor.Error('not-authorized', 'Only instance admins can perform this action.');
}
- return comprehensiveBoardMigration.fixAttachmentUrls();
+ return await comprehensiveBoardMigration.fixAttachmentUrls();
}
});
diff --git a/server/migrations/deleteDuplicateEmptyLists.js b/server/migrations/deleteDuplicateEmptyLists.js
index 0f590dacc..f6b7fa91b 100644
--- a/server/migrations/deleteDuplicateEmptyLists.js
+++ b/server/migrations/deleteDuplicateEmptyLists.js
@@ -26,10 +26,10 @@ class DeleteDuplicateEmptyListsMigration {
/**
* Check if migration is needed for a board
*/
- needsMigration(boardId) {
+ async needsMigration(boardId) {
try {
- const lists = ReactiveCache.getLists({ boardId });
- const cards = ReactiveCache.getCards({ boardId });
+ const lists = await ReactiveCache.getLists({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
// Check if there are any empty lists that have a duplicate with the same title containing cards
for (const list of lists) {
@@ -104,10 +104,10 @@ class DeleteDuplicateEmptyListsMigration {
* Convert shared lists (lists without swimlaneId) to per-swimlane lists
*/
async convertSharedListsToPerSwimlane(boardId) {
- const lists = ReactiveCache.getLists({ boardId });
- const swimlanes = ReactiveCache.getSwimlanes({ boardId, archived: false });
- const cards = ReactiveCache.getCards({ boardId });
-
+ const lists = await ReactiveCache.getLists({ boardId });
+ const swimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: false });
+ const cards = await ReactiveCache.getCards({ boardId });
+
let listsConverted = 0;
// Find shared lists (lists without swimlaneId)
@@ -206,9 +206,9 @@ class DeleteDuplicateEmptyListsMigration {
* 3. Have a duplicate list with the same title on the same board that contains cards
*/
async deleteEmptyPerSwimlaneLists(boardId) {
- const lists = ReactiveCache.getLists({ boardId });
- const cards = ReactiveCache.getCards({ boardId });
-
+ const lists = await ReactiveCache.getLists({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
+
let listsDeleted = 0;
for (const list of lists) {
@@ -268,8 +268,8 @@ class DeleteDuplicateEmptyListsMigration {
* Get detailed status of empty lists
*/
async getStatus(boardId) {
- const lists = ReactiveCache.getLists({ boardId });
- const cards = ReactiveCache.getCards({ boardId });
+ const lists = await ReactiveCache.getLists({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
const sharedLists = [];
const emptyPerSwimlaneLists = [];
@@ -319,17 +319,17 @@ const deleteDuplicateEmptyListsMigration = new DeleteDuplicateEmptyListsMigratio
// Register Meteor methods
Meteor.methods({
- 'deleteDuplicateEmptyLists.needsMigration'(boardId) {
+ async 'deleteDuplicateEmptyLists.needsMigration'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in');
}
- return deleteDuplicateEmptyListsMigration.needsMigration(boardId);
+ return await deleteDuplicateEmptyListsMigration.needsMigration(boardId);
},
- 'deleteDuplicateEmptyLists.execute'(boardId) {
+ async 'deleteDuplicateEmptyLists.execute'(boardId) {
check(boardId, String);
if (!this.userId) {
@@ -337,12 +337,12 @@ Meteor.methods({
}
// Check if user is board admin
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user) {
throw new Meteor.Error('user-not-found', 'User not found');
}
@@ -356,17 +356,17 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations');
}
- return deleteDuplicateEmptyListsMigration.executeMigration(boardId);
+ return await deleteDuplicateEmptyListsMigration.executeMigration(boardId);
},
- 'deleteDuplicateEmptyLists.getStatus'(boardId) {
+ async 'deleteDuplicateEmptyLists.getStatus'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in');
}
- return deleteDuplicateEmptyListsMigration.getStatus(boardId);
+ return await deleteDuplicateEmptyListsMigration.getStatus(boardId);
}
});
diff --git a/server/migrations/fixAllFileUrls.js b/server/migrations/fixAllFileUrls.js
index c18a34dbb..f577125ec 100644
--- a/server/migrations/fixAllFileUrls.js
+++ b/server/migrations/fixAllFileUrls.js
@@ -22,9 +22,9 @@ class FixAllFileUrlsMigration {
/**
* Check if migration is needed for a board
*/
- needsMigration(boardId) {
+ async needsMigration(boardId) {
// Get all users who are members of this board
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.members) {
return false;
}
@@ -32,7 +32,7 @@ class FixAllFileUrlsMigration {
const memberIds = board.members.map(m => m.userId);
// Check for problematic avatar URLs for board members
- const users = ReactiveCache.getUsers({ _id: { $in: memberIds } });
+ const users = await ReactiveCache.getUsers({ _id: { $in: memberIds } });
for (const user of users) {
if (user.profile && user.profile.avatarUrl) {
const avatarUrl = user.profile.avatarUrl;
@@ -43,10 +43,10 @@ class FixAllFileUrlsMigration {
}
// Check for problematic attachment URLs on this board
- const cards = ReactiveCache.getCards({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
const cardIds = cards.map(c => c._id);
- const attachments = ReactiveCache.getAttachments({ cardId: { $in: cardIds } });
-
+ const attachments = await ReactiveCache.getAttachments({ cardId: { $in: cardIds } });
+
for (const attachment of attachments) {
if (attachment.url && this.hasProblematicUrl(attachment.url)) {
return true;
@@ -133,13 +133,13 @@ class FixAllFileUrlsMigration {
* Fix avatar URLs in user profiles for board members
*/
async fixAvatarUrls(boardId) {
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.members) {
return 0;
}
const memberIds = board.members.map(m => m.userId);
- const users = ReactiveCache.getUsers({ _id: { $in: memberIds } });
+ const users = await ReactiveCache.getUsers({ _id: { $in: memberIds } });
let avatarsFixed = 0;
for (const user of users) {
@@ -189,9 +189,9 @@ class FixAllFileUrlsMigration {
* Fix attachment URLs in attachment records for this board
*/
async fixAttachmentUrls(boardId) {
- const cards = ReactiveCache.getCards({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
const cardIds = cards.map(c => c._id);
- const attachments = ReactiveCache.getAttachments({ cardId: { $in: cardIds } });
+ const attachments = await ReactiveCache.getAttachments({ cardId: { $in: cardIds } });
let attachmentsFixed = 0;
for (const attachment of attachments) {
@@ -229,9 +229,9 @@ class FixAllFileUrlsMigration {
* Fix attachment URLs in the Attachments collection for this board
*/
async fixCardAttachmentUrls(boardId) {
- const cards = ReactiveCache.getCards({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
const cardIds = cards.map(c => c._id);
- const attachments = ReactiveCache.getAttachments({ cardId: { $in: cardIds } });
+ const attachments = await ReactiveCache.getAttachments({ cardId: { $in: cardIds } });
let attachmentsFixed = 0;
for (const attachment of attachments) {
@@ -270,7 +270,7 @@ export const fixAllFileUrlsMigration = new FixAllFileUrlsMigration();
// Meteor methods
Meteor.methods({
- 'fixAllFileUrls.execute'(boardId) {
+ async 'fixAllFileUrls.execute'(boardId) {
check(boardId, String);
if (!this.userId) {
@@ -278,12 +278,12 @@ Meteor.methods({
}
// Check if user is board admin
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user) {
throw new Meteor.Error('user-not-found', 'User not found');
}
@@ -297,16 +297,16 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations');
}
- return fixAllFileUrlsMigration.execute(boardId);
+ return await fixAllFileUrlsMigration.execute(boardId);
},
- 'fixAllFileUrls.needsMigration'(boardId) {
+ async 'fixAllFileUrls.needsMigration'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in');
}
- return fixAllFileUrlsMigration.needsMigration(boardId);
+ return await fixAllFileUrlsMigration.needsMigration(boardId);
}
});
diff --git a/server/migrations/fixAvatarUrls.js b/server/migrations/fixAvatarUrls.js
index a5b01571d..445dec945 100644
--- a/server/migrations/fixAvatarUrls.js
+++ b/server/migrations/fixAvatarUrls.js
@@ -19,16 +19,16 @@ class FixAvatarUrlsMigration {
/**
* Check if migration is needed for a board
*/
- needsMigration(boardId) {
+ async needsMigration(boardId) {
// Get all users who are members of this board
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.members) {
return false;
}
const memberIds = board.members.map(m => m.userId);
- const users = ReactiveCache.getUsers({ _id: { $in: memberIds } });
-
+ const users = await ReactiveCache.getUsers({ _id: { $in: memberIds } });
+
for (const user of users) {
if (user.profile && user.profile.avatarUrl) {
const avatarUrl = user.profile.avatarUrl;
@@ -46,7 +46,7 @@ class FixAvatarUrlsMigration {
*/
async execute(boardId) {
// Get all users who are members of this board
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.members) {
return {
success: false,
@@ -55,7 +55,7 @@ class FixAvatarUrlsMigration {
}
const memberIds = board.members.map(m => m.userId);
- const users = ReactiveCache.getUsers({ _id: { $in: memberIds } });
+ const users = await ReactiveCache.getUsers({ _id: { $in: memberIds } });
let avatarsFixed = 0;
console.log(`Starting avatar URL fix migration for board ${boardId}...`);
@@ -131,7 +131,7 @@ export const fixAvatarUrlsMigration = new FixAvatarUrlsMigration();
// Meteor method
Meteor.methods({
- 'fixAvatarUrls.execute'(boardId) {
+ async 'fixAvatarUrls.execute'(boardId) {
check(boardId, String);
if (!this.userId) {
@@ -139,12 +139,12 @@ Meteor.methods({
}
// Check if user is board admin
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user) {
throw new Meteor.Error('user-not-found', 'User not found');
}
@@ -158,16 +158,16 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations');
}
- return fixAvatarUrlsMigration.execute(boardId);
+ return await fixAvatarUrlsMigration.execute(boardId);
},
- 'fixAvatarUrls.needsMigration'(boardId) {
+ async 'fixAvatarUrls.needsMigration'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in');
}
- return fixAvatarUrlsMigration.needsMigration(boardId);
+ return await fixAvatarUrlsMigration.needsMigration(boardId);
}
});
diff --git a/server/migrations/fixMissingListsMigration.js b/server/migrations/fixMissingListsMigration.js
index 2994d70ac..dd2af5dbd 100644
--- a/server/migrations/fixMissingListsMigration.js
+++ b/server/migrations/fixMissingListsMigration.js
@@ -31,9 +31,9 @@ class FixMissingListsMigration {
/**
* Check if migration is needed for a board
*/
- needsMigration(boardId) {
+ async needsMigration(boardId) {
try {
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) return false;
// Check if board has already been processed
@@ -42,9 +42,9 @@ class FixMissingListsMigration {
}
// Check if there are cards with mismatched listId/swimlaneId
- const cards = ReactiveCache.getCards({ boardId });
- const lists = ReactiveCache.getLists({ boardId });
-
+ const cards = await ReactiveCache.getCards({ boardId });
+ const lists = await ReactiveCache.getLists({ boardId });
+
// Create a map of listId -> swimlaneId for existing lists
const listSwimlaneMap = new Map();
lists.forEach(list => {
@@ -78,14 +78,14 @@ class FixMissingListsMigration {
console.log(`Starting fix missing lists migration for board ${boardId}`);
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Error(`Board ${boardId} not found`);
}
- const cards = ReactiveCache.getCards({ boardId });
- const lists = ReactiveCache.getLists({ boardId });
- const swimlanes = ReactiveCache.getSwimlanes({ boardId });
+ const cards = await ReactiveCache.getCards({ boardId });
+ const lists = await ReactiveCache.getLists({ boardId });
+ const swimlanes = await ReactiveCache.getSwimlanes({ boardId });
// Create maps for efficient lookup
const listSwimlaneMap = new Map();
@@ -214,9 +214,9 @@ class FixMissingListsMigration {
/**
* Get migration status for a board
*/
- getMigrationStatus(boardId) {
+ async getMigrationStatus(boardId) {
try {
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
return { status: 'board_not_found' };
}
@@ -228,7 +228,7 @@ class FixMissingListsMigration {
};
}
- const needsMigration = this.needsMigration(boardId);
+ const needsMigration = await this.needsMigration(boardId);
return {
status: needsMigration ? 'needed' : 'not_needed'
};
@@ -245,33 +245,33 @@ export const fixMissingListsMigration = new FixMissingListsMigration();
// Meteor methods
Meteor.methods({
- 'fixMissingListsMigration.check'(boardId) {
+ async 'fixMissingListsMigration.check'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- return fixMissingListsMigration.getMigrationStatus(boardId);
+ return await fixMissingListsMigration.getMigrationStatus(boardId);
},
- 'fixMissingListsMigration.execute'(boardId) {
+ async 'fixMissingListsMigration.execute'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- return fixMissingListsMigration.executeMigration(boardId);
+ return await fixMissingListsMigration.executeMigration(boardId);
},
- 'fixMissingListsMigration.needsMigration'(boardId) {
+ async 'fixMissingListsMigration.needsMigration'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized');
}
- return fixMissingListsMigration.needsMigration(boardId);
+ return await fixMissingListsMigration.needsMigration(boardId);
}
});
diff --git a/server/migrations/migrateAttachments.js b/server/migrations/migrateAttachments.js
index 55ffdb0c7..5ff8dcce5 100644
--- a/server/migrations/migrateAttachments.js
+++ b/server/migrations/migrateAttachments.js
@@ -14,7 +14,7 @@ if (Meteor.isServer) {
* @param {string} attachmentId - The old attachment ID
* @returns {Object} - Migration result
*/
- migrateAttachment(attachmentId) {
+ async migrateAttachment(attachmentId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
@@ -27,7 +27,7 @@ if (Meteor.isServer) {
}
// Check if already migrated
- const existingAttachment = ReactiveCache.getAttachment(attachmentId);
+ const existingAttachment = await ReactiveCache.getAttachment(attachmentId);
if (existingAttachment) {
return { success: true, message: 'Already migrated', attachmentId };
}
@@ -72,7 +72,7 @@ if (Meteor.isServer) {
* @param {string} cardId - The card ID
* @returns {Object} - Migration results
*/
- migrateCardAttachments(cardId) {
+ async migrateCardAttachments(cardId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
@@ -85,7 +85,7 @@ if (Meteor.isServer) {
try {
// Get all old attachments for this card
- const oldAttachments = ReactiveCache.getAttachments({ 'meta.cardId': cardId });
+ const oldAttachments = await ReactiveCache.getAttachments({ 'meta.cardId': cardId });
for (const attachment of oldAttachments) {
const result = Meteor.call('migrateAttachment', attachment._id);
@@ -113,14 +113,14 @@ if (Meteor.isServer) {
* @param {string} cardId - The card ID (optional)
* @returns {Object} - Migration status
*/
- getAttachmentMigrationStatus(cardId) {
+ async getAttachmentMigrationStatus(cardId) {
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'Must be logged in');
}
try {
const selector = cardId ? { 'meta.cardId': cardId } : {};
- const allAttachments = ReactiveCache.getAttachments(selector);
+ const allAttachments = await ReactiveCache.getAttachments(selector);
const status = {
total: allAttachments.length,
diff --git a/server/migrations/restoreAllArchived.js b/server/migrations/restoreAllArchived.js
index 177b16947..a3b703618 100644
--- a/server/migrations/restoreAllArchived.js
+++ b/server/migrations/restoreAllArchived.js
@@ -24,11 +24,11 @@ class RestoreAllArchivedMigration {
/**
* Check if migration is needed for a board
*/
- needsMigration(boardId) {
+ async needsMigration(boardId) {
try {
- const archivedSwimlanes = ReactiveCache.getSwimlanes({ boardId, archived: true });
- const archivedLists = ReactiveCache.getLists({ boardId, archived: true });
- const archivedCards = ReactiveCache.getCards({ boardId, archived: true });
+ const archivedSwimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: true });
+ const archivedLists = await ReactiveCache.getLists({ boardId, archived: true });
+ const archivedCards = await ReactiveCache.getCards({ boardId, archived: true });
return archivedSwimlanes.length > 0 || archivedLists.length > 0 || archivedCards.length > 0;
} catch (error) {
@@ -50,19 +50,19 @@ class RestoreAllArchivedMigration {
errors: []
};
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Error('Board not found');
}
// Get archived items
- const archivedSwimlanes = ReactiveCache.getSwimlanes({ boardId, archived: true });
- const archivedLists = ReactiveCache.getLists({ boardId, archived: true });
- const archivedCards = ReactiveCache.getCards({ boardId, archived: true });
+ const archivedSwimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: true });
+ const archivedLists = await ReactiveCache.getLists({ boardId, archived: true });
+ const archivedCards = await ReactiveCache.getCards({ boardId, archived: true });
// Get active items for reference
- const activeSwimlanes = ReactiveCache.getSwimlanes({ boardId, archived: false });
- const activeLists = ReactiveCache.getLists({ boardId, archived: false });
+ const activeSwimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: false });
+ const activeLists = await ReactiveCache.getLists({ boardId, archived: false });
// Restore all archived swimlanes
for (const swimlane of archivedSwimlanes) {
@@ -101,7 +101,7 @@ class RestoreAllArchivedMigration {
updatedAt: new Date(),
archived: false
});
- targetSwimlane = ReactiveCache.getSwimlane(swimlaneId);
+ targetSwimlane = await ReactiveCache.getSwimlane(swimlaneId);
}
updateFields.swimlaneId = targetSwimlane._id;
@@ -123,8 +123,8 @@ class RestoreAllArchivedMigration {
}
// Refresh lists after restoration
- const allLists = ReactiveCache.getLists({ boardId, archived: false });
- const allSwimlanes = ReactiveCache.getSwimlanes({ boardId, archived: false });
+ const allLists = await ReactiveCache.getLists({ boardId, archived: false });
+ const allSwimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: false });
// Restore all archived cards and fix missing IDs
for (const card of archivedCards) {
@@ -153,7 +153,7 @@ class RestoreAllArchivedMigration {
updatedAt: new Date(),
archived: false
});
- targetList = ReactiveCache.getList(listId);
+ targetList = await ReactiveCache.getList(listId);
}
updateFields.listId = targetList._id;
@@ -222,17 +222,17 @@ const restoreAllArchivedMigration = new RestoreAllArchivedMigration();
// Register Meteor methods
Meteor.methods({
- 'restoreAllArchived.needsMigration'(boardId) {
+ async 'restoreAllArchived.needsMigration'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in');
}
- return restoreAllArchivedMigration.needsMigration(boardId);
+ return await restoreAllArchivedMigration.needsMigration(boardId);
},
- 'restoreAllArchived.execute'(boardId) {
+ async 'restoreAllArchived.execute'(boardId) {
check(boardId, String);
if (!this.userId) {
@@ -240,12 +240,12 @@ Meteor.methods({
}
// Check if user is board admin
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user) {
throw new Meteor.Error('user-not-found', 'User not found');
}
@@ -259,7 +259,7 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations');
}
- return restoreAllArchivedMigration.executeMigration(boardId);
+ return await restoreAllArchivedMigration.executeMigration(boardId);
}
});
diff --git a/server/migrations/restoreLostCards.js b/server/migrations/restoreLostCards.js
index 027469809..d6cc835a1 100644
--- a/server/migrations/restoreLostCards.js
+++ b/server/migrations/restoreLostCards.js
@@ -24,10 +24,10 @@ class RestoreLostCardsMigration {
/**
* Check if migration is needed for a board
*/
- needsMigration(boardId) {
+ async needsMigration(boardId) {
try {
- const cards = ReactiveCache.getCards({ boardId, archived: false });
- const lists = ReactiveCache.getLists({ boardId, archived: false });
+ const cards = await ReactiveCache.getCards({ boardId, archived: false });
+ const lists = await ReactiveCache.getLists({ boardId, archived: false });
// Check for cards missing swimlaneId or listId
const lostCards = cards.filter(card => !card.swimlaneId || !card.listId);
@@ -70,15 +70,15 @@ class RestoreLostCardsMigration {
errors: []
};
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Error('Board not found');
}
// Get all non-archived items
- const cards = ReactiveCache.getCards({ boardId, archived: false });
- const lists = ReactiveCache.getLists({ boardId, archived: false });
- const swimlanes = ReactiveCache.getSwimlanes({ boardId, archived: false });
+ const cards = await ReactiveCache.getCards({ boardId, archived: false });
+ const lists = await ReactiveCache.getLists({ boardId, archived: false });
+ const swimlanes = await ReactiveCache.getSwimlanes({ boardId, archived: false });
// Detect items to restore BEFORE creating anything
const lostLists = lists.filter(list => !list.swimlaneId);
@@ -116,7 +116,7 @@ class RestoreLostCardsMigration {
updatedAt: new Date(),
archived: false
});
- lostCardsSwimlane = ReactiveCache.getSwimlane(swimlaneId);
+ lostCardsSwimlane = await ReactiveCache.getSwimlane(swimlaneId);
results.lostCardsSwimlaneCreated = true;
if (process.env.DEBUG === 'true') {
console.log(`Created "Lost Cards" swimlane for board ${boardId}`);
@@ -156,7 +156,7 @@ class RestoreLostCardsMigration {
updatedAt: new Date(),
archived: false
});
- defaultList = ReactiveCache.getList(listId);
+ defaultList = await ReactiveCache.getList(listId);
if (process.env.DEBUG === 'true') {
console.log(`Created default list in Lost Cards swimlane`);
}
@@ -215,17 +215,17 @@ const restoreLostCardsMigration = new RestoreLostCardsMigration();
// Register Meteor methods
Meteor.methods({
- 'restoreLostCards.needsMigration'(boardId) {
+ async 'restoreLostCards.needsMigration'(boardId) {
check(boardId, String);
if (!this.userId) {
throw new Meteor.Error('not-authorized', 'You must be logged in');
}
- return restoreLostCardsMigration.needsMigration(boardId);
+ return await restoreLostCardsMigration.needsMigration(boardId);
},
- 'restoreLostCards.execute'(boardId) {
+ async 'restoreLostCards.execute'(boardId) {
check(boardId, String);
if (!this.userId) {
@@ -233,12 +233,12 @@ Meteor.methods({
}
// Check if user is board admin
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
throw new Meteor.Error('board-not-found', 'Board not found');
}
- const user = ReactiveCache.getUser(this.userId);
+ const user = await ReactiveCache.getUser(this.userId);
if (!user) {
throw new Meteor.Error('user-not-found', 'User not found');
}
@@ -252,7 +252,7 @@ Meteor.methods({
throw new Meteor.Error('not-authorized', 'Only board administrators can run migrations');
}
- return restoreLostCardsMigration.executeMigration(boardId);
+ return await restoreLostCardsMigration.executeMigration(boardId);
}
});
diff --git a/server/notifications/email.js b/server/notifications/email.js
index 7b1bf9a42..3b9706402 100644
--- a/server/notifications/email.js
+++ b/server/notifications/email.js
@@ -33,8 +33,8 @@ Meteor.startup(() => {
// Meteor.setTimeout(func, delay) does not accept args :-(
// so we pass userId with closure
const userId = user._id;
- Meteor.setTimeout(() => {
- const user = ReactiveCache.getUser(userId);
+ Meteor.setTimeout(async () => {
+ const user = await ReactiveCache.getUser(userId);
// for each user, in the timed period, only the first call will get the cached content,
// other calls will get nothing
diff --git a/server/notifications/notifications.js b/server/notifications/notifications.js
index 59a98066d..0be96f25f 100644
--- a/server/notifications/notifications.js
+++ b/server/notifications/notifications.js
@@ -19,12 +19,12 @@ Notifications = {
delete notifyServices[serviceName];
},
- getUsers: watchers => {
+ getUsers: async watchers => {
const users = [];
- watchers.forEach(userId => {
- const user = ReactiveCache.getUser(userId);
+ for (const userId of watchers) {
+ const user = await ReactiveCache.getUser(userId);
if (user && user._id) users.push(user);
- });
+ }
return users;
},
diff --git a/server/notifications/outgoing.js b/server/notifications/outgoing.js
index ff5123946..1703b1ead 100644
--- a/server/notifications/outgoing.js
+++ b/server/notifications/outgoing.js
@@ -70,20 +70,20 @@ if (Meteor.isServer) {
'label',
'attachmentId',
];
- const responseFunc = data => {
+ const responseFunc = async data => {
const paramCommentId = data.commentId;
const paramCardId = data.cardId;
const paramBoardId = data.boardId;
const newComment = data.comment;
if (paramCardId && paramBoardId && newComment) {
// only process data with the cardid, boardid and comment text, TODO can expand other functions here to react on returned data
- const comment = ReactiveCache.getCardComment({
+ const comment = await ReactiveCache.getCardComment({
_id: paramCommentId,
cardId: paramCardId,
boardId: paramBoardId,
});
- const board = ReactiveCache.getBoard(paramBoardId);
- const card = ReactiveCache.getCard(paramCardId);
+ const board = await ReactiveCache.getBoard(paramBoardId);
+ const card = await ReactiveCache.getCard(paramCardId);
if (board && card) {
if (comment) {
Lock.set(comment._id, newComment);
@@ -108,8 +108,8 @@ if (Meteor.isServer) {
}
};
Meteor.methods({
- outgoingWebhooks(integration, description, params) {
- if (ReactiveCache.getCurrentUser()) {
+ async outgoingWebhooks(integration, description, params) {
+ if (await ReactiveCache.getCurrentUser()) {
check(integration, Object);
check(description, String);
check(params, Object);
@@ -137,7 +137,7 @@ if (Meteor.isServer) {
});
const userId = params.userId ? params.userId : integrations[0].userId;
- const user = ReactiveCache.getUser(userId);
+ const user = await ReactiveCache.getUser(userId);
const descriptionText = TAPi18n.__(
description,
quoteParams,
@@ -171,7 +171,7 @@ if (Meteor.isServer) {
data: is2way ? { description, ...clonedParams } : value,
};
- if (!ReactiveCache.getIntegration({ url: integration.url })) return;
+ if (!(await ReactiveCache.getIntegration({ url: integration.url }))) return;
const url = integration.url;
@@ -198,7 +198,7 @@ if (Meteor.isServer) {
const data = response.data; // only an JSON encoded response will be actioned
if (data) {
try {
- responseFunc(data);
+ await responseFunc(data);
} catch (e) {
throw new Meteor.Error('error-process-data');
}
diff --git a/server/notifications/watch.js b/server/notifications/watch.js
index 3b4907220..cb9cf5029 100644
--- a/server/notifications/watch.js
+++ b/server/notifications/watch.js
@@ -11,17 +11,17 @@ Meteor.methods({
let watchableObj = null;
let board = null;
if (watchableType === 'board') {
- watchableObj = ReactiveCache.getBoard(id);
+ watchableObj = await ReactiveCache.getBoard(id);
if (!watchableObj) throw new Meteor.Error('error-board-doesNotExist');
board = watchableObj;
} else if (watchableType === 'list') {
- watchableObj = ReactiveCache.getList(id);
+ watchableObj = await ReactiveCache.getList(id);
if (!watchableObj) throw new Meteor.Error('error-list-doesNotExist');
- board = watchableObj.board();
+ board = await watchableObj.board();
} else if (watchableType === 'card') {
- watchableObj = ReactiveCache.getCard(id);
+ watchableObj = await ReactiveCache.getCard(id);
if (!watchableObj) throw new Meteor.Error('error-card-doesNotExist');
- board = watchableObj.board();
+ board = await watchableObj.board();
} else {
throw new Meteor.Error('error-json-schema');
}
diff --git a/server/publications/activities.js b/server/publications/activities.js
index 8c3e17d69..ffb545fa0 100644
--- a/server/publications/activities.js
+++ b/server/publications/activities.js
@@ -5,7 +5,7 @@ import { ReactiveCache } from '/imports/reactiveCache';
// 2. The card activity tab
// We use this publication to paginate for these two publications.
-Meteor.publish('activities', function(kind, id, limit, showActivities) {
+Meteor.publish('activities', async function(kind, id, limit, showActivities) {
check(
kind,
Match.Where(x => {
@@ -29,27 +29,28 @@ Meteor.publish('activities', function(kind, id, limit, showActivities) {
let board;
if (kind === 'board') {
- board = ReactiveCache.getBoard(id);
+ board = await ReactiveCache.getBoard(id);
if (!board || !board.isVisibleBy(this.userId)) {
return this.ready();
}
// Get linked boards, but only those visible to the user
- ReactiveCache.getCards({
+ const linkedCards = await ReactiveCache.getCards({
"type": "cardType-linkedBoard",
"boardId": id
- }).forEach(card => {
- const linkedBoard = ReactiveCache.getBoard(card.linkedId);
+ });
+ for (const card of linkedCards) {
+ const linkedBoard = await ReactiveCache.getBoard(card.linkedId);
if (linkedBoard && linkedBoard.isVisibleBy(this.userId)) {
linkedElmtId.push(card.linkedId);
}
- });
+ }
} else if (kind === 'card') {
- const card = ReactiveCache.getCard(id);
+ const card = await ReactiveCache.getCard(id);
if (!card) {
return this.ready();
}
- board = ReactiveCache.getBoard(card.boardId);
+ board = await ReactiveCache.getBoard(card.boardId);
if (!board || !board.isVisibleBy(this.userId)) {
return this.ready();
}
@@ -58,7 +59,7 @@ Meteor.publish('activities', function(kind, id, limit, showActivities) {
const selector = showActivities
? { [`${kind}Id`]: { $in: linkedElmtId } }
: { $and: [{ activityType: 'addComment' }, { [`${kind}Id`]: { $in: linkedElmtId } }] };
- const ret = ReactiveCache.getActivities(selector,
+ const ret = await ReactiveCache.getActivities(selector,
{
limit,
sort: { createdAt: -1 },
diff --git a/server/publications/attachments.js b/server/publications/attachments.js
index d618012f8..a25c82487 100644
--- a/server/publications/attachments.js
+++ b/server/publications/attachments.js
@@ -1,16 +1,16 @@
import Attachments from '/models/attachments';
import { ObjectID } from 'bson';
-Meteor.publish('attachmentsList', function(limit) {
+Meteor.publish('attachmentsList', async function(limit) {
const userId = this.userId;
// Get boards the user has access to
- const userBoards = ReactiveCache.getBoards({
+ const userBoards = (await ReactiveCache.getBoards({
$or: [
{ permission: 'public' },
{ members: { $elemMatch: { userId, isActive: true } } }
]
- }).map(board => board._id);
+ })).map(board => board._id);
if (userBoards.length === 0) {
// User has no access to any boards, return empty cursor
@@ -18,10 +18,10 @@ Meteor.publish('attachmentsList', function(limit) {
}
// Get cards from those boards
- const userCards = ReactiveCache.getCards({
+ const userCards = (await ReactiveCache.getCards({
boardId: { $in: userBoards },
archived: false
- }).map(card => card._id);
+ })).map(card => card._id);
if (userCards.length === 0) {
// No cards found, return empty cursor
@@ -29,7 +29,7 @@ Meteor.publish('attachmentsList', function(limit) {
}
// Only return attachments for cards the user has access to
- const ret = ReactiveCache.getAttachments(
+ const ret = (await ReactiveCache.getAttachments(
{ 'meta.cardId': { $in: userCards } },
{
fields: {
@@ -47,6 +47,6 @@ Meteor.publish('attachmentsList', function(limit) {
limit: limit,
},
true,
- ).cursor;
+ )).cursor;
return ret;
});
diff --git a/server/publications/avatars.js b/server/publications/avatars.js
index 47f9f0bdd..d7ca862f4 100644
--- a/server/publications/avatars.js
+++ b/server/publications/avatars.js
@@ -1,5 +1,5 @@
import Avatars from '../../models/avatars';
-Meteor.publish('my-avatars', function() {
- const ret = ReactiveCache.getAvatars({ userId: this.userId }, {}, true).cursor;
+Meteor.publish('my-avatars', async function() {
+ const ret = (await ReactiveCache.getAvatars({ userId: this.userId }, {}, true)).cursor;
return ret;
});
diff --git a/server/publications/boards.js b/server/publications/boards.js
index c070949ba..0eaa42db0 100644
--- a/server/publications/boards.js
+++ b/server/publications/boards.js
@@ -18,11 +18,11 @@ publishComposite('boards', function() {
}
return {
- find() {
- return ReactiveCache.getBoards(
+ async find() {
+ return await ReactiveCache.getBoards(
{
archived: false,
- _id: { $in: Boards.userBoardIds(userId, false) },
+ _id: { $in: await Boards.userBoardIds(userId, false) },
},
{
sort: { sort: 1 /* boards default sorting */ },
@@ -32,10 +32,10 @@ publishComposite('boards', function() {
},
children: [
{
- find(board) {
+ async find(board) {
// Publish lists with extended fields for proper sync
// Including swimlaneId, modifiedAt, and _updatedAt for list order changes
- return ReactiveCache.getLists(
+ return await ReactiveCache.getLists(
{ boardId: board._id, archived: false },
{
fields: {
@@ -54,8 +54,8 @@ publishComposite('boards', function() {
}
},
{
- find(board) {
- return ReactiveCache.getCards(
+ async find(board) {
+ return await ReactiveCache.getCards(
{ boardId: board._id, archived: false },
{
fields: {
@@ -74,15 +74,15 @@ publishComposite('boards', function() {
};
});
-Meteor.publish('boardsReport', function() {
+Meteor.publish('boardsReport', async function() {
const userId = this.userId;
// Ensure that the user is connected. If it is not, we need to return an empty
// array to tell the client to remove the previously published docs.
if (!Match.test(userId, String) || !userId) return [];
- const boards = ReactiveCache.getBoards(
+ const boards = await ReactiveCache.getBoards(
{
- _id: { $in: Boards.userBoardIds(userId, null) },
+ _id: { $in: await Boards.userBoardIds(userId, null) },
},
{
fields: {
@@ -129,20 +129,20 @@ Meteor.publish('boardsReport', function() {
const ret = [
boards,
- ReactiveCache.getUsers({ _id: { $in: userIds } }, { fields: Users.safeFields }, true),
- ReactiveCache.getTeams({ _id: { $in: teamIds } }, {}, true),
- ReactiveCache.getOrgs({ _id: { $in: orgIds } }, {}, true),
+ await ReactiveCache.getUsers({ _id: { $in: userIds } }, { fields: Users.safeFields }, true),
+ await ReactiveCache.getTeams({ _id: { $in: teamIds } }, {}, true),
+ await ReactiveCache.getOrgs({ _id: { $in: orgIds } }, {}, true),
]
return ret;
});
-Meteor.publish('archivedBoards', function() {
+Meteor.publish('archivedBoards', async function() {
const userId = this.userId;
if (!Match.test(userId, String)) return [];
- const ret = ReactiveCache.getBoards(
+ const ret = await ReactiveCache.getBoards(
{
- _id: { $in: Boards.userBoardIds(userId, true)},
+ _id: { $in: await Boards.userBoardIds(userId, true)},
archived: true,
members: {
$elemMatch: {
@@ -170,14 +170,14 @@ Meteor.publish('archivedBoards', function() {
// If isArchived = false, this will only return board elements which are not archived.
// If isArchived = true, this will only return board elements which are archived.
-publishComposite('board', function(boardId, isArchived) {
+publishComposite('board', async function(boardId, isArchived) {
check(boardId, String);
check(isArchived, Boolean);
const thisUserId = this.userId;
const $or = [{ permission: 'public' }];
- let currUser = (!Match.test(thisUserId, String) || !thisUserId) ? 'undefined' : ReactiveCache.getUser(thisUserId);
+ let currUser = (!Match.test(thisUserId, String) || !thisUserId) ? 'undefined' : await ReactiveCache.getUser(thisUserId);
let orgIdsUserBelongs = currUser !== 'undefined' && currUser.teams !== 'undefined' ? currUser.orgIdsUserBelongs() : '';
let teamIdsUserBelongs = currUser !== 'undefined' && currUser.teams !== 'undefined' ? currUser.teamIdsUserBelongs() : '';
let orgsIds = [];
@@ -197,8 +197,8 @@ publishComposite('board', function(boardId, isArchived) {
}
return {
- find() {
- return ReactiveCache.getBoards(
+ async find() {
+ return await ReactiveCache.getBoards(
{
_id: boardId,
archived: false,
@@ -212,32 +212,32 @@ publishComposite('board', function(boardId, isArchived) {
children: [
// Lists
{
- find(board) {
- return ReactiveCache.getLists({ boardId: board._id, archived: isArchived }, {}, true);
+ async find(board) {
+ return await ReactiveCache.getLists({ boardId: board._id, archived: isArchived }, {}, true);
}
},
// Swimlanes
{
- find(board) {
- return ReactiveCache.getSwimlanes({ boardId: board._id, archived: isArchived }, {}, true);
+ async find(board) {
+ return await ReactiveCache.getSwimlanes({ boardId: board._id, archived: isArchived }, {}, true);
}
},
// Integrations
{
- find(board) {
- return ReactiveCache.getIntegrations({ boardId: board._id }, {}, true);
+ async find(board) {
+ return await ReactiveCache.getIntegrations({ boardId: board._id }, {}, true);
}
},
// CardCommentReactions at board level
{
- find(board) {
- return ReactiveCache.getCardCommentReactions({ boardId: board._id }, {}, true);
+ async find(board) {
+ return await ReactiveCache.getCardCommentReactions({ boardId: board._id }, {}, true);
}
},
// CustomFields
{
- find(board) {
- return ReactiveCache.getCustomFields(
+ async find(board) {
+ return await ReactiveCache.getCustomFields(
{ boardIds: { $in: [board._id] } },
{ sort: { name: 1 } },
true,
@@ -246,7 +246,7 @@ publishComposite('board', function(boardId, isArchived) {
},
// Cards and their related data
{
- find(board) {
+ async find(board) {
const cardSelector = {
boardId: { $in: [board._id, board.subtasksDefaultBoardId] },
archived: isArchived,
@@ -261,7 +261,7 @@ publishComposite('board', function(boardId, isArchived) {
}
}
- return ReactiveCache.getCards(cardSelector, {}, true);
+ return await ReactiveCache.getCards(cardSelector, {}, true);
},
children: [
// CardComments for each card
@@ -366,7 +366,7 @@ publishComposite('board', function(boardId, isArchived) {
},
// Board members/Users
{
- find(board) {
+ async find(board) {
if (board.members) {
// Board members. This publication also includes former board members that
// aren't members anymore but may have some activities attached to them in
@@ -376,7 +376,7 @@ publishComposite('board', function(boardId, isArchived) {
// We omit the current user because the client should already have that data,
// and sending it triggers a subtle bug:
// https://github.com/wefork/wekan/issues/15
- return ReactiveCache.getUsers(
+ return await ReactiveCache.getUsers(
{
_id: { $in: _.without(memberIds, thisUserId) },
},
@@ -399,12 +399,12 @@ publishComposite('board', function(boardId, isArchived) {
});
Meteor.methods({
- copyBoard(boardId, properties) {
+ async copyBoard(boardId, properties) {
check(boardId, String);
check(properties, Object);
let ret = null;
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (board) {
for (const key in properties) {
board[key] = properties[key];
diff --git a/server/publications/cards.js b/server/publications/cards.js
index 1a259d7d2..59ee2ee01 100644
--- a/server/publications/cards.js
+++ b/server/publications/cards.js
@@ -74,17 +74,17 @@ import { CARD_TYPES } from '../../config/const';
import Org from "../../models/org";
import Team from "../../models/team";
-Meteor.publish('card', cardId => {
+Meteor.publish('card', async cardId => {
check(cardId, String);
const userId = Meteor.userId();
- const card = ReactiveCache.getCard({ _id: cardId });
+ const card = await ReactiveCache.getCard({ _id: cardId });
if (!card || !card.boardId) {
return [];
}
- const board = ReactiveCache.getBoard({ _id: card.boardId });
+ const board = await ReactiveCache.getBoard({ _id: card.boardId });
if (!board || !board.isVisibleBy(userId)) {
return [];
}
@@ -100,7 +100,7 @@ Meteor.publish('card', cardId => {
}
}
- const ret = ReactiveCache.getCards(
+ const ret = await ReactiveCache.getCards(
{ _id: cardId },
{},
true,
@@ -111,17 +111,17 @@ Meteor.publish('card', cardId => {
/** publish all data which is necessary to display card details as popup
* @returns array of cursors
*/
-publishComposite('popupCardData', function(cardId) {
+publishComposite('popupCardData', async function(cardId) {
check(cardId, String);
const userId = this.userId;
- const card = ReactiveCache.getCard({ _id: cardId });
+ const card = await ReactiveCache.getCard({ _id: cardId });
if (!card || !card.boardId) {
return [];
}
- const board = ReactiveCache.getBoard({ _id: card.boardId });
+ const board = await ReactiveCache.getBoard({ _id: card.boardId });
if (!board || !board.isVisibleBy(userId)) {
return [];
}
@@ -138,29 +138,29 @@ publishComposite('popupCardData', function(cardId) {
}
return {
- find() {
- return ReactiveCache.getCards({ _id: cardId }, {}, true);
+ async find() {
+ return await ReactiveCache.getCards({ _id: cardId }, {}, true);
},
children: [
{
- find(card) {
- return ReactiveCache.getBoards({ _id: card.boardId }, {}, true);
+ async find(card) {
+ return await ReactiveCache.getBoards({ _id: card.boardId }, {}, true);
}
},
{
- find(card) {
- return ReactiveCache.getLists({ boardId: card.boardId }, {}, true);
+ async find(card) {
+ return await ReactiveCache.getLists({ boardId: card.boardId }, {}, true);
}
}
]
};
});
-Meteor.publish('myCards', function(sessionId) {
+Meteor.publish('myCards', async function(sessionId) {
check(sessionId, String);
const queryParams = new QueryParams();
- queryParams.addPredicate(OPERATOR_USER, ReactiveCache.getCurrentUser().username);
+ queryParams.addPredicate(OPERATOR_USER, (await ReactiveCache.getCurrentUser()).username);
queryParams.setPredicate(OPERATOR_LIMIT, 200);
const query = buildQuery(queryParams);
@@ -175,7 +175,7 @@ Meteor.publish('myCards', function(sessionId) {
});
// Optimized due cards publication for better performance
-Meteor.publish('dueCards', function(allUsers = false) {
+Meteor.publish('dueCards', async function(allUsers = false) {
check(allUsers, Boolean);
const userId = this.userId;
@@ -188,12 +188,12 @@ Meteor.publish('dueCards', function(allUsers = false) {
}
// Get user's board memberships for efficient filtering
- const userBoards = ReactiveCache.getBoards({
+ const userBoards = (await ReactiveCache.getBoards({
$or: [
{ permission: 'public' },
{ members: { $elemMatch: { userId, isActive: true } } }
]
- }).map(board => board._id);
+ })).map(board => board._id);
if (process.env.DEBUG === 'true') {
console.log('dueCards userBoards:', userBoards);
@@ -273,7 +273,7 @@ Meteor.publish('dueCards', function(allUsers = false) {
return result;
});
-Meteor.publish('globalSearch', function(sessionId, params, text) {
+Meteor.publish('globalSearch', async function(sessionId, params, text) {
check(sessionId, String);
check(params, Object);
check(text, String);
@@ -282,7 +282,7 @@ Meteor.publish('globalSearch', function(sessionId, params, text) {
console.log('globalSearch publication called with:', { sessionId, params, text });
}
- const ret = findCards(sessionId, buildQuery(new QueryParams(params, text)));
+ const ret = findCards(sessionId, await buildQuery(new QueryParams(params, text)));
if (process.env.DEBUG === 'true') {
console.log('globalSearch publication returning:', ret);
}
@@ -303,7 +303,7 @@ Meteor.publish('sessionData', function(sessionId) {
return cursor;
});
-function buildSelector(queryParams) {
+async function buildSelector(queryParams) {
const userId = Meteor.userId();
const errors = new QueryErrors();
@@ -336,8 +336,8 @@ function buildSelector(queryParams) {
if (queryParams.hasOperator(OPERATOR_ORG)) {
const orgs = [];
- queryParams.getPredicates(OPERATOR_ORG).forEach(name => {
- const org = ReactiveCache.getOrg({
+ for (const name of queryParams.getPredicates(OPERATOR_ORG)) {
+ const org = await ReactiveCache.getOrg({
$or: [
{ orgDisplayName: name },
{ orgShortName: name }
@@ -348,7 +348,7 @@ function buildSelector(queryParams) {
} else {
errors.addNotFound(OPERATOR_ORG, name);
}
- });
+ }
if (orgs.length) {
boardsSelector.orgs = {
$elemMatch: { orgId: { $in: orgs }, isActive: true }
@@ -358,8 +358,8 @@ function buildSelector(queryParams) {
if (queryParams.hasOperator(OPERATOR_TEAM)) {
const teams = [];
- queryParams.getPredicates(OPERATOR_TEAM).forEach(name => {
- const team = ReactiveCache.getTeam({
+ for (const name of queryParams.getPredicates(OPERATOR_TEAM)) {
+ const team = await ReactiveCache.getTeam({
$or: [
{ teamDisplayName: name },
{ teamShortName: name }
@@ -370,7 +370,7 @@ function buildSelector(queryParams) {
} else {
errors.addNotFound(OPERATOR_TEAM, name);
}
- });
+ }
if (teams.length) {
boardsSelector.teams = {
$elemMatch: { teamId: { $in: teams }, isActive: true }
@@ -387,13 +387,13 @@ function buildSelector(queryParams) {
if (archived !== null) {
if (archived) {
selector.boardId = {
- $in: Boards.userBoardIds(userId, null, boardsSelector),
+ $in: await Boards.userBoardIds(userId, null, boardsSelector),
};
selector.$and.push({
$or: [
{
boardId: {
- $in: Boards.userBoardIds(userId, archived, boardsSelector),
+ $in: await Boards.userBoardIds(userId, archived, boardsSelector),
},
},
{ swimlaneId: { $in: Swimlanes.userArchivedSwimlaneIds(userId) } },
@@ -403,14 +403,14 @@ function buildSelector(queryParams) {
});
} else {
selector.boardId = {
- $in: Boards.userBoardIds(userId, false, boardsSelector),
+ $in: await Boards.userBoardIds(userId, false, boardsSelector),
};
selector.swimlaneId = { $nin: Swimlanes.archivedSwimlaneIds() };
selector.listId = { $nin: Lists.archivedListIds() };
selector.archived = false;
}
} else {
- const userBoardIds = Boards.userBoardIds(userId, null, boardsSelector);
+ const userBoardIds = await Boards.userBoardIds(userId, null, boardsSelector);
if (process.env.DEBUG === 'true') {
console.log('buildSelector - userBoardIds:', userBoardIds);
}
@@ -424,8 +424,8 @@ function buildSelector(queryParams) {
if (queryParams.hasOperator(OPERATOR_BOARD)) {
const queryBoards = [];
- queryParams.getPredicates(OPERATOR_BOARD).forEach(query => {
- const boards = Boards.userSearch(userId, {
+ for (const query of queryParams.getPredicates(OPERATOR_BOARD)) {
+ const boards = await Boards.userSearch(userId, {
title: new RegExp(escapeForRegex(query), 'i'),
});
if (boards.length) {
@@ -435,15 +435,15 @@ function buildSelector(queryParams) {
} else {
errors.addNotFound(OPERATOR_BOARD, query);
}
- });
+ }
selector.boardId.$in = queryBoards;
}
if (queryParams.hasOperator(OPERATOR_SWIMLANE)) {
const querySwimlanes = [];
- queryParams.getPredicates(OPERATOR_SWIMLANE).forEach(query => {
- const swimlanes = ReactiveCache.getSwimlanes({
+ for (const query of queryParams.getPredicates(OPERATOR_SWIMLANE)) {
+ const swimlanes = await ReactiveCache.getSwimlanes({
title: new RegExp(escapeForRegex(query), 'i'),
});
if (swimlanes.length) {
@@ -453,7 +453,7 @@ function buildSelector(queryParams) {
} else {
errors.addNotFound(OPERATOR_SWIMLANE, query);
}
- });
+ }
// eslint-disable-next-line no-prototype-builtins
if (!selector.swimlaneId.hasOwnProperty('swimlaneId')) {
@@ -464,8 +464,8 @@ function buildSelector(queryParams) {
if (queryParams.hasOperator(OPERATOR_LIST)) {
const queryLists = [];
- queryParams.getPredicates(OPERATOR_LIST).forEach(query => {
- const lists = ReactiveCache.getLists({
+ for (const query of queryParams.getPredicates(OPERATOR_LIST)) {
+ const lists = await ReactiveCache.getLists({
title: new RegExp(escapeForRegex(query), 'i'),
});
if (lists.length) {
@@ -475,7 +475,7 @@ function buildSelector(queryParams) {
} else {
errors.addNotFound(OPERATOR_LIST, query);
}
- });
+ }
// eslint-disable-next-line no-prototype-builtins
if (!selector.hasOwnProperty('listId')) {
@@ -516,14 +516,14 @@ function buildSelector(queryParams) {
if (queryParams.hasOperator(OPERATOR_USER)) {
const users = [];
- queryParams.getPredicates(OPERATOR_USER).forEach(username => {
- const user = ReactiveCache.getUser({ username });
+ for (const username of queryParams.getPredicates(OPERATOR_USER)) {
+ const user = await ReactiveCache.getUser({ username });
if (user) {
users.push(user._id);
} else {
errors.addNotFound(OPERATOR_USER, username);
}
- });
+ }
if (users.length) {
selector.$and.push({
$or: [{ members: { $in: users } }, { assignees: { $in: users } }],
@@ -531,36 +531,32 @@ function buildSelector(queryParams) {
}
}
- [OPERATOR_MEMBER, OPERATOR_ASSIGNEE, OPERATOR_CREATOR].forEach(key => {
+ for (const key of [OPERATOR_MEMBER, OPERATOR_ASSIGNEE, OPERATOR_CREATOR]) {
if (queryParams.hasOperator(key)) {
const users = [];
- queryParams.getPredicates(key).forEach(username => {
- const user = ReactiveCache.getUser({ username });
+ for (const username of queryParams.getPredicates(key)) {
+ const user = await ReactiveCache.getUser({ username });
if (user) {
users.push(user._id);
} else {
errors.addNotFound(key, username);
}
- });
+ }
if (users.length) {
selector[key] = { $in: users };
}
}
- });
+ }
if (queryParams.hasOperator(OPERATOR_LABEL)) {
const queryLabels = [];
- queryParams.getPredicates(OPERATOR_LABEL).forEach(label => {
- let boards = Boards.userBoards(userId, null, {
+ for (const label of queryParams.getPredicates(OPERATOR_LABEL)) {
+ let boards = await Boards.userBoards(userId, null, {
labels: { $elemMatch: { color: label.toLowerCase() } },
});
if (boards.length) {
boards.forEach(board => {
- // eslint-disable-next-line no-console
- // console.log('board:', board);
- // eslint-disable-next-line no-console
- // console.log('board.labels:', board.labels);
board.labels
.filter(boardLabel => {
return boardLabel.color === label.toLowerCase();
@@ -570,12 +566,8 @@ function buildSelector(queryParams) {
});
});
} else {
- // eslint-disable-next-line no-console
- // console.log('label:', label);
const reLabel = new RegExp(escapeForRegex(label), 'i');
- // eslint-disable-next-line no-console
- // console.log('reLabel:', reLabel);
- boards = Boards.userBoards(userId, null, {
+ boards = await Boards.userBoards(userId, null, {
labels: { $elemMatch: { name: reLabel } },
});
@@ -596,7 +588,7 @@ function buildSelector(queryParams) {
errors.addNotFound(OPERATOR_LABEL, label);
}
}
- });
+ }
if (queryLabels.length) {
// eslint-disable-next-line no-console
// console.log('queryLabels:', queryLabels);
@@ -605,12 +597,12 @@ function buildSelector(queryParams) {
}
if (queryParams.hasOperator(OPERATOR_HAS)) {
- queryParams.getPredicates(OPERATOR_HAS).forEach(has => {
+ for (const has of queryParams.getPredicates(OPERATOR_HAS)) {
switch (has.field) {
case PREDICATE_ATTACHMENT:
selector.$and.push({
_id: {
- $in: ReactiveCache.getAttachments({}, { fields: { cardId: 1 } }).map(
+ $in: (await ReactiveCache.getAttachments({}, { fields: { cardId: 1 } })).map(
a => a.cardId,
),
},
@@ -619,7 +611,7 @@ function buildSelector(queryParams) {
case PREDICATE_CHECKLIST:
selector.$and.push({
_id: {
- $in: ReactiveCache.getChecklists({}, { fields: { cardId: 1 } }).map(
+ $in: (await ReactiveCache.getChecklists({}, { fields: { cardId: 1 } })).map(
a => a.cardId,
),
},
@@ -644,17 +636,17 @@ function buildSelector(queryParams) {
}
break;
}
- });
+ }
}
if (queryParams.text) {
const regex = new RegExp(escapeForRegex(queryParams.text), 'i');
- const items = ReactiveCache.getChecklistItems(
+ const items = await ReactiveCache.getChecklistItems(
{ title: regex },
{ fields: { cardId: 1, checklistId: 1 } },
);
- const checklists = ReactiveCache.getChecklists(
+ const checklists = await ReactiveCache.getChecklists(
{
$or: [
{ title: regex },
@@ -664,9 +656,9 @@ function buildSelector(queryParams) {
{ fields: { cardId: 1 } },
);
- const attachments = ReactiveCache.getAttachments({ 'original.name': regex });
+ const attachments = await ReactiveCache.getAttachments({ 'original.name': regex });
- const comments = ReactiveCache.getCardComments(
+ const comments = await ReactiveCache.getCardComments(
{ text: regex },
{ fields: { cardId: 1 } },
);
@@ -806,18 +798,18 @@ function buildProjection(query) {
return query;
}
-function buildQuery(queryParams) {
- const query = buildSelector(queryParams);
+async function buildQuery(queryParams) {
+ const query = await buildSelector(queryParams);
return buildProjection(query);
}
-Meteor.publish('brokenCards', function(sessionId) {
+Meteor.publish('brokenCards', async function(sessionId) {
check(sessionId, String);
const params = new QueryParams();
params.addPredicate(OPERATOR_STATUS, PREDICATE_ALL);
- const query = buildQuery(params);
+ const query = await buildQuery(params);
query.selector.$or = [
{ boardId: { $in: [null, ''] } },
{ swimlaneId: { $in: [null, ''] } },
@@ -830,10 +822,10 @@ Meteor.publish('brokenCards', function(sessionId) {
return ret;
});
-Meteor.publish('nextPage', function(sessionId) {
+Meteor.publish('nextPage', async function(sessionId) {
check(sessionId, String);
- const session = ReactiveCache.getSessionData({ sessionId });
+ const session = await ReactiveCache.getSessionData({ sessionId });
const projection = session.getProjection();
projection.skip = session.lastHit;
@@ -841,10 +833,10 @@ Meteor.publish('nextPage', function(sessionId) {
return ret;
});
-Meteor.publish('previousPage', function(sessionId) {
+Meteor.publish('previousPage', async function(sessionId) {
check(sessionId, String);
- const session = ReactiveCache.getSessionData({ sessionId });
+ const session = await ReactiveCache.getSessionData({ sessionId });
const projection = session.getProjection();
projection.skip = session.lastHit - session.resultsCount - projection.limit;
@@ -852,7 +844,7 @@ Meteor.publish('previousPage', function(sessionId) {
return ret;
});
-function findCards(sessionId, query) {
+async function findCards(sessionId, query) {
const userId = Meteor.userId();
// eslint-disable-next-line no-console
@@ -863,7 +855,7 @@ function findCards(sessionId, query) {
console.log('findCards - projection:', query.projection);
}
- const cards = ReactiveCache.getCards(query.selector, query.projection, true);
+ const cards = await ReactiveCache.getCards(query.selector, query.projection, true);
if (process.env.DEBUG === 'true') {
console.log('findCards - cards count:', cards ? cards.count() : 0);
}
@@ -977,23 +969,23 @@ function findCards(sessionId, query) {
return [
cards,
- ReactiveCache.getBoards(
+ await ReactiveCache.getBoards(
{ _id: { $in: boards } },
{ fields: { ...fields, labels: 1, color: 1 } },
true,
),
- ReactiveCache.getSwimlanes(
+ await ReactiveCache.getSwimlanes(
{ _id: { $in: swimlanes } },
{ fields: { ...fields, color: 1 } },
true,
),
- ReactiveCache.getLists({ _id: { $in: lists } }, { fields }, true),
- ReactiveCache.getCustomFields({ _id: { $in: customFieldIds } }, {}, true),
- ReactiveCache.getUsers({ _id: { $in: users } }, { fields: Users.safeFields }, true),
- ReactiveCache.getChecklists({ cardId: { $in: cards.map(c => c._id) } }, {}, true),
- ReactiveCache.getChecklistItems({ cardId: { $in: cards.map(c => c._id) } }, {}, true),
- ReactiveCache.getAttachments({ 'meta.cardId': { $in: cards.map(c => c._id) } }, {}, true).cursor,
- ReactiveCache.getCardComments({ cardId: { $in: cards.map(c => c._id) } }, {}, true),
+ await ReactiveCache.getLists({ _id: { $in: lists } }, { fields }, true),
+ await ReactiveCache.getCustomFields({ _id: { $in: customFieldIds } }, {}, true),
+ await ReactiveCache.getUsers({ _id: { $in: users } }, { fields: Users.safeFields }, true),
+ await ReactiveCache.getChecklists({ cardId: { $in: cards.map(c => c._id) } }, {}, true),
+ await ReactiveCache.getChecklistItems({ cardId: { $in: cards.map(c => c._id) } }, {}, true),
+ (await ReactiveCache.getAttachments({ 'meta.cardId': { $in: cards.map(c => c._id) } }, {}, true)).cursor,
+ await ReactiveCache.getCardComments({ cardId: { $in: cards.map(c => c._id) } }, {}, true),
sessionDataCursor,
];
}
diff --git a/server/publications/notifications.js b/server/publications/notifications.js
index 1d9db198e..bc6998a4c 100644
--- a/server/publications/notifications.js
+++ b/server/publications/notifications.js
@@ -3,33 +3,33 @@ import { ReactiveCache } from '/imports/reactiveCache';
// We use these when displaying notifications in the notificationsDrawer
// gets all activities associated with the current user
-Meteor.publish('notificationActivities', () => {
- const ret = activities();
+Meteor.publish('notificationActivities', async () => {
+ const ret = await activities();
return ret;
});
// gets all attachments associated with activities associated with the current user
-Meteor.publish('notificationAttachments', function() {
- const ret = ReactiveCache.getAttachments(
+Meteor.publish('notificationAttachments', async function() {
+ const ret = (await ReactiveCache.getAttachments(
{
_id: {
- $in: activities()
+ $in: (await activities())
.map(v => v.attachmentId)
.filter(v => !!v),
},
},
{},
true,
- ).cursor;
+ )).cursor;
return ret;
});
// gets all cards associated with activities associated with the current user
-Meteor.publish('notificationCards', function() {
- const ret = ReactiveCache.getCards(
+Meteor.publish('notificationCards', async function() {
+ const ret = await ReactiveCache.getCards(
{
_id: {
- $in: activities()
+ $in: (await activities())
.map(v => v.cardId)
.filter(v => !!v),
},
@@ -41,11 +41,11 @@ Meteor.publish('notificationCards', function() {
});
// gets all checklistItems associated with activities associated with the current user
-Meteor.publish('notificationChecklistItems', function() {
- const ret = ReactiveCache.getChecklistItems(
+Meteor.publish('notificationChecklistItems', async function() {
+ const ret = await ReactiveCache.getChecklistItems(
{
_id: {
- $in: activities()
+ $in: (await activities())
.map(v => v.checklistItemId)
.filter(v => !!v),
},
@@ -57,11 +57,11 @@ Meteor.publish('notificationChecklistItems', function() {
});
// gets all checklists associated with activities associated with the current user
-Meteor.publish('notificationChecklists', function() {
- const ret = ReactiveCache.getChecklists(
+Meteor.publish('notificationChecklists', async function() {
+ const ret = await ReactiveCache.getChecklists(
{
_id: {
- $in: activities()
+ $in: (await activities())
.map(v => v.checklistId)
.filter(v => !!v),
},
@@ -73,11 +73,11 @@ Meteor.publish('notificationChecklists', function() {
});
// gets all comments associated with activities associated with the current user
-Meteor.publish('notificationComments', function() {
- const ret = ReactiveCache.getCardComments(
+Meteor.publish('notificationComments', async function() {
+ const ret = await ReactiveCache.getCardComments(
{
_id: {
- $in: activities()
+ $in: (await activities())
.map(v => v.commentId)
.filter(v => !!v),
},
@@ -89,11 +89,11 @@ Meteor.publish('notificationComments', function() {
});
// gets all lists associated with activities associated with the current user
-Meteor.publish('notificationLists', function() {
- const ret = ReactiveCache.getLists(
+Meteor.publish('notificationLists', async function() {
+ const ret = await ReactiveCache.getLists(
{
_id: {
- $in: activities()
+ $in: (await activities())
.map(v => v.listId)
.filter(v => !!v),
},
@@ -105,11 +105,11 @@ Meteor.publish('notificationLists', function() {
});
// gets all swimlanes associated with activities associated with the current user
-Meteor.publish('notificationSwimlanes', function() {
- const ret = ReactiveCache.getSwimlanes(
+Meteor.publish('notificationSwimlanes', async function() {
+ const ret = await ReactiveCache.getSwimlanes(
{
_id: {
- $in: activities()
+ $in: (await activities())
.map(v => v.swimlaneId)
.filter(v => !!v),
},
@@ -121,11 +121,11 @@ Meteor.publish('notificationSwimlanes', function() {
});
// gets all users associated with activities associated with the current user
-Meteor.publish('notificationUsers', function() {
- const ret = ReactiveCache.getUsers(
+Meteor.publish('notificationUsers', async function() {
+ const ret = await ReactiveCache.getUsers(
{
_id: {
- $in: activities()
+ $in: (await activities())
.map(v => v.userId)
.filter(v => !!v),
},
@@ -136,11 +136,11 @@ Meteor.publish('notificationUsers', function() {
return ret;
});
-function activities() {
- const activityIds = ReactiveCache.getCurrentUser()?.profile?.notifications?.map(v => v.activity) || [];
+async function activities() {
+ const activityIds = (await ReactiveCache.getCurrentUser())?.profile?.notifications?.map(v => v.activity) || [];
let ret = [];
if (activityIds.length > 0) {
- ret = ReactiveCache.getActivities(
+ ret = await ReactiveCache.getActivities(
{
_id: { $in: activityIds },
},
diff --git a/server/publications/org.js b/server/publications/org.js
index adf7f33d0..e7569a396 100644
--- a/server/publications/org.js
+++ b/server/publications/org.js
@@ -1,14 +1,14 @@
import { ReactiveCache } from '/imports/reactiveCache';
-Meteor.publish('org', function(query, limit) {
+Meteor.publish('org', async function(query, limit) {
check(query, Match.OneOf(Object, null));
check(limit, Number);
let ret = [];
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
if (user && user.isAdmin) {
- ret = ReactiveCache.getOrgs(query,
+ ret = await ReactiveCache.getOrgs(query,
{
limit,
sort: { createdAt: -1 },
diff --git a/server/publications/people.js b/server/publications/people.js
index 7135cb820..57602757f 100644
--- a/server/publications/people.js
+++ b/server/publications/people.js
@@ -1,14 +1,14 @@
import { ReactiveCache } from '/imports/reactiveCache';
-Meteor.publish('people', function(query, limit) {
+Meteor.publish('people', async function(query, limit) {
check(query, Match.OneOf(Object, null));
check(limit, Number);
let ret = [];
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
if (user && user.isAdmin) {
- ret = ReactiveCache.getUsers(query, {
+ ret = await ReactiveCache.getUsers(query, {
limit,
sort: { createdAt: -1 },
fields: {
diff --git a/server/publications/rules.js b/server/publications/rules.js
index 45d949e7b..78c2f5896 100644
--- a/server/publications/rules.js
+++ b/server/publications/rules.js
@@ -4,24 +4,24 @@ import Triggers from '/models/triggers';
import Rules from '/models/rules';
import { ReactiveCache } from '/imports/reactiveCache';
-Meteor.publish('rules', function(ruleId) {
+Meteor.publish('rules', async function(ruleId) {
check(ruleId, String);
if (!this.userId) {
return this.ready();
}
- const rule = ReactiveCache.getRule(ruleId);
+ const rule = await ReactiveCache.getRule(ruleId);
if (!rule) {
return this.ready();
}
- const board = ReactiveCache.getBoard(rule.boardId);
+ const board = await ReactiveCache.getBoard(rule.boardId);
if (!board || !board.isVisibleBy(this.userId)) {
return this.ready();
}
- const ret = ReactiveCache.getRules(
+ const ret = await ReactiveCache.getRules(
{
_id: ruleId,
},
@@ -31,39 +31,39 @@ Meteor.publish('rules', function(ruleId) {
return ret;
});
-Meteor.publish('allRules', function() {
- if (!this.userId || !ReactiveCache.getUser(this.userId).isAdmin) {
+Meteor.publish('allRules', async function() {
+ if (!this.userId || !(await ReactiveCache.getUser(this.userId)).isAdmin) {
return this.ready();
}
- const ret = ReactiveCache.getRules({}, {}, true);
+ const ret = await ReactiveCache.getRules({}, {}, true);
return ret;
});
-Meteor.publish('allTriggers', function() {
- if (!this.userId || !ReactiveCache.getUser(this.userId).isAdmin) {
+Meteor.publish('allTriggers', async function() {
+ if (!this.userId || !(await ReactiveCache.getUser(this.userId)).isAdmin) {
return this.ready();
}
- const ret = ReactiveCache.getTriggers({}, {}, true);
+ const ret = await ReactiveCache.getTriggers({}, {}, true);
return ret;
});
-Meteor.publish('allActions', function() {
- if (!this.userId || !ReactiveCache.getUser(this.userId).isAdmin) {
+Meteor.publish('allActions', async function() {
+ if (!this.userId || !(await ReactiveCache.getUser(this.userId)).isAdmin) {
return this.ready();
}
- const ret = ReactiveCache.getActions({}, {}, true);
+ const ret = await ReactiveCache.getActions({}, {}, true);
return ret;
});
-Meteor.publish('rulesReport', function() {
- if (!this.userId || !ReactiveCache.getUser(this.userId).isAdmin) {
+Meteor.publish('rulesReport', async function() {
+ if (!this.userId || !(await ReactiveCache.getUser(this.userId)).isAdmin) {
return this.ready();
}
- const rules = ReactiveCache.getRules({}, {}, true);
+ const rules = await ReactiveCache.getRules({}, {}, true);
const actionIds = [];
const triggerIds = [];
const boardIds = [];
@@ -76,9 +76,9 @@ Meteor.publish('rulesReport', function() {
const ret = [
rules,
- ReactiveCache.getActions({ _id: { $in: actionIds } }, {}, true),
- ReactiveCache.getTriggers({ _id: { $in: triggerIds } }, {}, true),
- ReactiveCache.getBoards({ _id: { $in: boardIds } }, { fields: { title: 1 } }, true),
+ await ReactiveCache.getActions({ _id: { $in: actionIds } }, {}, true),
+ await ReactiveCache.getTriggers({ _id: { $in: triggerIds } }, {}, true),
+ await ReactiveCache.getBoards({ _id: { $in: boardIds } }, { fields: { title: 1 } }, true),
];
return ret;
});
diff --git a/server/publications/settings.js b/server/publications/settings.js
index 0f546454b..531ee10c5 100644
--- a/server/publications/settings.js
+++ b/server/publications/settings.js
@@ -1,8 +1,8 @@
import { ReactiveCache } from '/imports/reactiveCache';
-Meteor.publish('globalwebhooks', () => {
+Meteor.publish('globalwebhooks', async () => {
const boardId = Integrations.Const.GLOBAL_WEBHOOK_ID;
- const ret = ReactiveCache.getIntegrations(
+ const ret = await ReactiveCache.getIntegrations(
{
boardId,
},
@@ -54,8 +54,8 @@ Meteor.publish('setting', () => {
return ret;
});
-Meteor.publish('mailServer', function() {
- const user = ReactiveCache.getCurrentUser();
+Meteor.publish('mailServer', async function() {
+ const user = await ReactiveCache.getCurrentUser();
let ret = []
if (user && user.isAdmin) {
diff --git a/server/publications/swimlanes.js b/server/publications/swimlanes.js
index fec6958c0..dc8f295bf 100644
--- a/server/publications/swimlanes.js
+++ b/server/publications/swimlanes.js
@@ -1,12 +1,12 @@
import { ReactiveCache } from '/imports/reactiveCache';
Meteor.methods({
- copySwimlane(swimlaneId, toBoardId) {
+ async copySwimlane(swimlaneId, toBoardId) {
check(swimlaneId, String);
check(toBoardId, String);
- const swimlane = ReactiveCache.getSwimlane(swimlaneId);
- const toBoard = ReactiveCache.getBoard(toBoardId);
+ const swimlane = await ReactiveCache.getSwimlane(swimlaneId);
+ const toBoard = await ReactiveCache.getBoard(toBoardId);
let ret = false;
if (swimlane && toBoard) {
@@ -21,8 +21,8 @@ Meteor.methods({
check(swimlaneId, String);
check(toBoardId, String);
- const swimlane = ReactiveCache.getSwimlane(swimlaneId);
- const toBoard = ReactiveCache.getBoard(toBoardId);
+ const swimlane = await ReactiveCache.getSwimlane(swimlaneId);
+ const toBoard = await ReactiveCache.getBoard(toBoardId);
let ret = false;
if (swimlane && toBoard) {
diff --git a/server/publications/team.js b/server/publications/team.js
index 37a161793..6a8437c02 100644
--- a/server/publications/team.js
+++ b/server/publications/team.js
@@ -1,14 +1,14 @@
import { ReactiveCache } from '/imports/reactiveCache';
-Meteor.publish('team', function(query, limit) {
+Meteor.publish('team', async function(query, limit) {
check(query, Match.OneOf(Object, null));
check(limit, Number);
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
let ret = [];
if (user && user.isAdmin) {
- ret = ReactiveCache.getTeams(query,
+ ret = await ReactiveCache.getTeams(query,
{
limit,
sort: { createdAt: -1 },
diff --git a/server/publications/translation.js b/server/publications/translation.js
index 2868329f0..09b79fb5d 100644
--- a/server/publications/translation.js
+++ b/server/publications/translation.js
@@ -1,14 +1,14 @@
import { ReactiveCache } from '/imports/reactiveCache';
-Meteor.publish('translation', function(query, limit) {
+Meteor.publish('translation', async function(query, limit) {
check(query, Match.OneOf(Object, null));
check(limit, Number);
let ret = [];
- const user = ReactiveCache.getCurrentUser();
+ const user = await ReactiveCache.getCurrentUser();
if (user && user.isAdmin) {
- ret = ReactiveCache.getTranslations(query,
+ ret = await ReactiveCache.getTranslations(query,
{
limit,
sort: { modifiedAt: -1 },
diff --git a/server/publications/users.js b/server/publications/users.js
index 4730a3d35..f929c5db1 100644
--- a/server/publications/users.js
+++ b/server/publications/users.js
@@ -1,9 +1,9 @@
-Meteor.publish('user-miniprofile', function (usernames) {
+Meteor.publish('user-miniprofile', async function (usernames) {
check(usernames, Array);
// eslint-disable-next-line no-console
// console.log('usernames:', usernames);
- const ret = ReactiveCache.getUsers(
+ const ret = await ReactiveCache.getUsers(
{
$or: [
{ username: { $in: usernames } },
@@ -33,9 +33,9 @@ Meteor.publish('user-admin', function () {
return ret;
});
-Meteor.publish('user-authenticationMethod', function (match) {
+Meteor.publish('user-authenticationMethod', async function (match) {
check(match, String);
- const ret = ReactiveCache.getUsers(
+ const ret = await ReactiveCache.getUsers(
{ $or: [{ _id: match }, { email: match }, { username: match }] },
{
fields: {
@@ -50,7 +50,7 @@ Meteor.publish('user-authenticationMethod', function (match) {
});
// Secure user search publication for board sharing
-Meteor.publish('user-search', function (searchTerm) {
+Meteor.publish('user-search', async function (searchTerm) {
check(searchTerm, String);
// Only allow logged-in users to search for other users
@@ -62,7 +62,7 @@ Meteor.publish('user-search', function (searchTerm) {
const searchRegex = new RegExp(searchTerm, 'i');
// Search for users by username, fullname, or email
- const ret = ReactiveCache.getUsers(
+ const ret = await ReactiveCache.getUsers(
{
$or: [
{ username: searchRegex },
diff --git a/server/routes/attachmentApi.js b/server/routes/attachmentApi.js
index 30aded02f..309878e20 100644
--- a/server/routes/attachmentApi.js
+++ b/server/routes/attachmentApi.js
@@ -48,7 +48,7 @@ if (Meteor.isServer) {
}
// Upload attachment endpoint
- WebApp.connectHandlers.use('/api/attachment/upload', (req, res, next) => {
+ WebApp.connectHandlers.use('/api/attachment/upload', async (req, res, next) => {
if (req.method !== 'POST') {
return next();
}
@@ -75,7 +75,7 @@ if (Meteor.isServer) {
}
});
- req.on('end', () => {
+ req.on('end', async () => {
if (bodyComplete) return; // Already processed
bodyComplete = true;
clearTimeout(timeout);
@@ -90,12 +90,12 @@ if (Meteor.isServer) {
}
// Check if user has permission to modify the card
- const card = ReactiveCache.getCard(cardId);
+ const card = await ReactiveCache.getCard(cardId);
if (!card) {
return sendErrorResponse(res, 404, 'Card not found');
}
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board) {
return sendErrorResponse(res, 404, 'Board not found');
}
@@ -207,7 +207,7 @@ if (Meteor.isServer) {
});
// Download attachment endpoint
- WebApp.connectHandlers.use('/api/attachment/download/([^/]+)', (req, res, next) => {
+ WebApp.connectHandlers.use('/api/attachment/download/([^/]+)', async (req, res, next) => {
if (req.method !== 'GET') {
return next();
}
@@ -217,13 +217,13 @@ if (Meteor.isServer) {
const attachmentId = req.params[0];
// Get attachment
- const attachment = ReactiveCache.getAttachment(attachmentId);
+ const attachment = await ReactiveCache.getAttachment(attachmentId);
if (!attachment) {
return sendErrorResponse(res, 404, 'Attachment not found');
}
// Check permissions
- const board = ReactiveCache.getBoard(attachment.meta.boardId);
+ const board = await ReactiveCache.getBoard(attachment.meta.boardId);
if (!board || !board.isBoardMember(userId)) {
return sendErrorResponse(res, 403, 'You do not have permission to access this attachment');
}
@@ -267,7 +267,7 @@ if (Meteor.isServer) {
});
// List attachments endpoint
- WebApp.connectHandlers.use('/api/attachment/list/([^/]+)/([^/]+)/([^/]+)/([^/]+)', (req, res, next) => {
+ WebApp.connectHandlers.use('/api/attachment/list/([^/]+)/([^/]+)/([^/]+)/([^/]+)', async (req, res, next) => {
if (req.method !== 'GET') {
return next();
}
@@ -280,14 +280,14 @@ if (Meteor.isServer) {
const cardId = req.params[3];
// Check permissions
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (!board || !board.isBoardMember(userId)) {
return sendErrorResponse(res, 403, 'You do not have permission to access this board');
}
// If cardId is provided, verify it belongs to the board
if (cardId && cardId !== 'null') {
- const card = ReactiveCache.getCard(cardId);
+ const card = await ReactiveCache.getCard(cardId);
if (!card || card.boardId !== boardId) {
return sendErrorResponse(res, 404, 'Card not found or does not belong to the specified board');
}
@@ -307,8 +307,8 @@ if (Meteor.isServer) {
query['meta.cardId'] = cardId;
}
- const attachments = ReactiveCache.getAttachments(query);
-
+ const attachments = await ReactiveCache.getAttachments(query);
+
const attachmentList = attachments.map(attachment => {
const strategy = fileStoreStrategyFactory.getFileStrategy(attachment, 'original');
return {
@@ -337,7 +337,7 @@ if (Meteor.isServer) {
});
// Copy attachment endpoint
- WebApp.connectHandlers.use('/api/attachment/copy', (req, res, next) => {
+ WebApp.connectHandlers.use('/api/attachment/copy', async (req, res, next) => {
if (req.method !== 'POST') {
return next();
}
@@ -362,7 +362,7 @@ if (Meteor.isServer) {
}
});
- req.on('end', () => {
+ req.on('end', async () => {
if (bodyComplete) return;
bodyComplete = true;
clearTimeout(timeout);
@@ -372,25 +372,25 @@ if (Meteor.isServer) {
const { attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId } = data;
// Get source attachment
- const sourceAttachment = ReactiveCache.getAttachment(attachmentId);
+ const sourceAttachment = await ReactiveCache.getAttachment(attachmentId);
if (!sourceAttachment) {
return sendErrorResponse(res, 404, 'Source attachment not found');
}
// Check source permissions
- const sourceBoard = ReactiveCache.getBoard(sourceAttachment.meta.boardId);
+ const sourceBoard = await ReactiveCache.getBoard(sourceAttachment.meta.boardId);
if (!sourceBoard || !sourceBoard.isBoardMember(userId)) {
return sendErrorResponse(res, 403, 'You do not have permission to access the source attachment');
}
// Check target permissions
- const targetBoard = ReactiveCache.getBoard(targetBoardId);
+ const targetBoard = await ReactiveCache.getBoard(targetBoardId);
if (!targetBoard || !targetBoard.isBoardMember(userId)) {
return sendErrorResponse(res, 403, 'You do not have permission to modify the target card');
}
// Verify that the target card belongs to the target board
- const targetCard = ReactiveCache.getCard(targetCardId);
+ const targetCard = await ReactiveCache.getCard(targetCardId);
if (!targetCard) {
return sendErrorResponse(res, 404, 'Target card not found');
}
@@ -493,7 +493,7 @@ if (Meteor.isServer) {
});
// Move attachment endpoint
- WebApp.connectHandlers.use('/api/attachment/move', (req, res, next) => {
+ WebApp.connectHandlers.use('/api/attachment/move', async (req, res, next) => {
if (req.method !== 'POST') {
return next();
}
@@ -518,7 +518,7 @@ if (Meteor.isServer) {
}
});
- req.on('end', () => {
+ req.on('end', async () => {
if (bodyComplete) return;
bodyComplete = true;
clearTimeout(timeout);
@@ -528,25 +528,25 @@ if (Meteor.isServer) {
const { attachmentId, targetBoardId, targetSwimlaneId, targetListId, targetCardId } = data;
// Get source attachment
- const sourceAttachment = ReactiveCache.getAttachment(attachmentId);
+ const sourceAttachment = await ReactiveCache.getAttachment(attachmentId);
if (!sourceAttachment) {
return sendErrorResponse(res, 404, 'Source attachment not found');
}
// Check source permissions
- const sourceBoard = ReactiveCache.getBoard(sourceAttachment.meta.boardId);
+ const sourceBoard = await ReactiveCache.getBoard(sourceAttachment.meta.boardId);
if (!sourceBoard || !sourceBoard.isBoardMember(userId)) {
return sendErrorResponse(res, 403, 'You do not have permission to access the source attachment');
}
// Check target permissions
- const targetBoard = ReactiveCache.getBoard(targetBoardId);
+ const targetBoard = await ReactiveCache.getBoard(targetBoardId);
if (!targetBoard || !targetBoard.isBoardMember(userId)) {
return sendErrorResponse(res, 403, 'You do not have permission to modify the target card');
}
// Verify that the target card belongs to the target board
- const targetCard = ReactiveCache.getCard(targetCardId);
+ const targetCard = await ReactiveCache.getCard(targetCardId);
if (!targetCard) {
return sendErrorResponse(res, 404, 'Target card not found');
}
@@ -610,7 +610,7 @@ if (Meteor.isServer) {
});
// Delete attachment endpoint
- WebApp.connectHandlers.use('/api/attachment/delete/([^/]+)', (req, res, next) => {
+ WebApp.connectHandlers.use('/api/attachment/delete/([^/]+)', async (req, res, next) => {
if (req.method !== 'DELETE') {
return next();
}
@@ -620,13 +620,13 @@ if (Meteor.isServer) {
const attachmentId = req.params[0];
// Get attachment
- const attachment = ReactiveCache.getAttachment(attachmentId);
+ const attachment = await ReactiveCache.getAttachment(attachmentId);
if (!attachment) {
return sendErrorResponse(res, 404, 'Attachment not found');
}
// Check permissions
- const board = ReactiveCache.getBoard(attachment.meta.boardId);
+ const board = await ReactiveCache.getBoard(attachment.meta.boardId);
if (!board || !board.isBoardMember(userId)) {
return sendErrorResponse(res, 403, 'You do not have permission to delete this attachment');
}
@@ -646,7 +646,7 @@ if (Meteor.isServer) {
});
// Get attachment info endpoint
- WebApp.connectHandlers.use('/api/attachment/info/([^/]+)', (req, res, next) => {
+ WebApp.connectHandlers.use('/api/attachment/info/([^/]+)', async (req, res, next) => {
if (req.method !== 'GET') {
return next();
}
@@ -656,13 +656,13 @@ if (Meteor.isServer) {
const attachmentId = req.params[0];
// Get attachment
- const attachment = ReactiveCache.getAttachment(attachmentId);
+ const attachment = await ReactiveCache.getAttachment(attachmentId);
if (!attachment) {
return sendErrorResponse(res, 404, 'Attachment not found');
}
// Check permissions
- const board = ReactiveCache.getBoard(attachment.meta.boardId);
+ const board = await ReactiveCache.getBoard(attachment.meta.boardId);
if (!board || !board.isBoardMember(userId)) {
return sendErrorResponse(res, 403, 'You do not have permission to access this attachment');
}
diff --git a/server/routes/avatarServer.js b/server/routes/avatarServer.js
index 4ce221ecb..e5c29baee 100644
--- a/server/routes/avatarServer.js
+++ b/server/routes/avatarServer.js
@@ -13,7 +13,7 @@ import path from 'path';
if (Meteor.isServer) {
// Handle avatar file downloads
- WebApp.connectHandlers.use('/cdn/storage/avatars/([^/]+)', (req, res, next) => {
+ WebApp.connectHandlers.use('/cdn/storage/avatars/([^/]+)', async (req, res, next) => {
if (req.method !== 'GET') {
return next();
}
@@ -37,7 +37,7 @@ if (Meteor.isServer) {
}
// Get avatar file from database
- const avatar = ReactiveCache.getAvatar(fileId);
+ const avatar = await ReactiveCache.getAvatar(fileId);
if (!avatar) {
res.writeHead(404);
res.end('Avatar not found');
diff --git a/server/routes/legacyAttachments.js b/server/routes/legacyAttachments.js
index 5e9f8b570..9b8d9e502 100644
--- a/server/routes/legacyAttachments.js
+++ b/server/routes/legacyAttachments.js
@@ -58,7 +58,7 @@ function buildContentDispositionHeader(disposition, sanitizedFilename) {
if (Meteor.isServer) {
// Handle legacy attachment downloads
- WebApp.connectHandlers.use('/cfs/files/attachments', (req, res, next) => {
+ WebApp.connectHandlers.use('/cfs/files/attachments', async (req, res, next) => {
const attachmentId = req.url.split('/').pop();
if (!attachmentId) {
@@ -78,7 +78,7 @@ if (Meteor.isServer) {
}
// Check permissions
- const board = ReactiveCache.getBoard(attachment.meta.boardId);
+ const board = await ReactiveCache.getBoard(attachment.meta.boardId);
if (!board) {
res.writeHead(404);
res.end('Board not found');
diff --git a/server/routes/universalFileServer.js b/server/routes/universalFileServer.js
index 018c7cbf7..0036b75d4 100644
--- a/server/routes/universalFileServer.js
+++ b/server/routes/universalFileServer.js
@@ -172,7 +172,7 @@ if (Meteor.isServer) {
* - Else if avatar's owner belongs to at least one public board -> allow
* - Otherwise -> deny
*/
- function isAuthorizedForAvatar(req, avatar) {
+ async function isAuthorizedForAvatar(req, avatar) {
try {
if (!avatar) return false;
@@ -180,7 +180,7 @@ if (Meteor.isServer) {
const q = parseQuery(req);
const boardId = q.boardId || q.board || q.b;
if (boardId) {
- const board = ReactiveCache.getBoard(boardId);
+ const board = await ReactiveCache.getBoard(boardId);
if (board && board.isPublic && board.isPublic()) return true;
// If private board is specified, require membership of requester
@@ -297,7 +297,7 @@ if (Meteor.isServer) {
* - Public boards: allow
* - Private boards: require valid user who is a member
*/
- function isAuthorizedForBoard(req, board) {
+ async function isAuthorizedForBoard(req, board) {
try {
if (!board) return false;
if (board.isPublic && board.isPublic()) return true;
@@ -389,7 +389,7 @@ if (Meteor.isServer) {
* Serve attachments from new Meteor-Files structure
* Route: /cdn/storage/attachments/{fileId} or /cdn/storage/attachments/{fileId}/original/{filename}
*/
- WebApp.connectHandlers.use('/cdn/storage/attachments', (req, res, next) => {
+ WebApp.connectHandlers.use('/cdn/storage/attachments', async (req, res, next) => {
if (req.method !== 'GET') {
return next();
}
@@ -412,7 +412,7 @@ if (Meteor.isServer) {
}
// Check permissions
- const board = ReactiveCache.getBoard(attachment.meta.boardId);
+ const board = await ReactiveCache.getBoard(attachment.meta.boardId);
if (!board) {
res.writeHead(404);
res.end('Board not found');
@@ -420,7 +420,7 @@ if (Meteor.isServer) {
}
// Enforce cookie/header/query-based auth for private boards
- if (!isAuthorizedForBoard(req, board)) {
+ if (!(await isAuthorizedForBoard(req, board))) {
res.writeHead(403);
res.end('Access denied');
return;
@@ -476,7 +476,7 @@ if (Meteor.isServer) {
* Serve avatars from new Meteor-Files structure
* Route: /cdn/storage/avatars/{fileId} or /cdn/storage/avatars/{fileId}/original/{filename}
*/
- WebApp.connectHandlers.use('/cdn/storage/avatars', (req, res, next) => {
+ WebApp.connectHandlers.use('/cdn/storage/avatars', async (req, res, next) => {
if (req.method !== 'GET') {
return next();
}
@@ -491,7 +491,7 @@ if (Meteor.isServer) {
}
// Get avatar from database
- const avatar = ReactiveCache.getAvatar(fileId);
+ const avatar = await ReactiveCache.getAvatar(fileId);
if (!avatar) {
res.writeHead(404);
res.end('Avatar not found');
@@ -499,7 +499,7 @@ if (Meteor.isServer) {
}
// Enforce visibility: avatars are public only in the context of public boards
- if (!isAuthorizedForAvatar(req, avatar)) {
+ if (!(await isAuthorizedForAvatar(req, avatar))) {
res.writeHead(403);
res.end('Access denied');
return;
@@ -541,7 +541,7 @@ if (Meteor.isServer) {
* Serve legacy attachments from CollectionFS structure
* Route: /cfs/files/attachments/{attachmentId}
*/
- WebApp.connectHandlers.use('/cfs/files/attachments', (req, res, next) => {
+ WebApp.connectHandlers.use('/cfs/files/attachments', async (req, res, next) => {
if (req.method !== 'GET') {
return next();
}
@@ -564,7 +564,7 @@ if (Meteor.isServer) {
}
// Check permissions
- const board = ReactiveCache.getBoard(attachment.meta.boardId);
+ const board = await ReactiveCache.getBoard(attachment.meta.boardId);
if (!board) {
res.writeHead(404);
res.end('Board not found');
@@ -572,7 +572,7 @@ if (Meteor.isServer) {
}
// Enforce cookie/header/query-based auth for private boards
- if (!isAuthorizedForBoard(req, board)) {
+ if (!(await isAuthorizedForBoard(req, board))) {
res.writeHead(403);
res.end('Access denied');
return;
@@ -617,7 +617,7 @@ if (Meteor.isServer) {
* Serve legacy avatars from CollectionFS structure
* Route: /cfs/files/avatars/{avatarId}
*/
- WebApp.connectHandlers.use('/cfs/files/avatars', (req, res, next) => {
+ WebApp.connectHandlers.use('/cfs/files/avatars', async (req, res, next) => {
if (req.method !== 'GET') {
return next();
}
@@ -632,7 +632,7 @@ if (Meteor.isServer) {
}
// Try to get avatar from database (new structure first)
- let avatar = ReactiveCache.getAvatar(avatarId);
+ let avatar = await ReactiveCache.getAvatar(avatarId);
// If not found in new structure, try to handle legacy format
if (!avatar) {
@@ -644,7 +644,7 @@ if (Meteor.isServer) {
}
// Enforce visibility for legacy avatars as well
- if (!isAuthorizedForAvatar(req, avatar)) {
+ if (!(await isAuthorizedForAvatar(req, avatar))) {
res.writeHead(403);
res.end('Access denied');
return;
diff --git a/server/rulesHelper.js b/server/rulesHelper.js
index b783b407d..0df49dff1 100644
--- a/server/rulesHelper.js
+++ b/server/rulesHelper.js
@@ -2,47 +2,47 @@ import { ReactiveCache } from '/imports/reactiveCache';
RulesHelper = {
async executeRules(activity) {
- const matchingRules = this.findMatchingRules(activity);
+ const matchingRules = await this.findMatchingRules(activity);
for (let i = 0; i < matchingRules.length; i++) {
- const action = matchingRules[i].getAction();
+ const action = await matchingRules[i].getAction();
if (action !== undefined) {
await this.performAction(activity, action);
}
}
},
- findMatchingRules(activity) {
+ async findMatchingRules(activity) {
const activityType = activity.activityType;
if (TriggersDef[activityType] === undefined) {
return [];
}
const matchingFields = TriggersDef[activityType].matchingFields;
- const matchingMap = this.buildMatchingFieldsMap(activity, matchingFields);
- const matchingTriggers = ReactiveCache.getTriggers(matchingMap);
+ const matchingMap = await this.buildMatchingFieldsMap(activity, matchingFields);
+ const matchingTriggers = await ReactiveCache.getTriggers(matchingMap);
const matchingRules = [];
- matchingTriggers.forEach(function(trigger) {
- const rule = trigger.getRule();
+ for (const trigger of matchingTriggers) {
+ const rule = await trigger.getRule();
// Check that for some unknown reason there are some leftover triggers
// not connected to any rules
if (rule !== undefined) {
- matchingRules.push(trigger.getRule());
+ matchingRules.push(rule);
}
- });
+ }
return matchingRules;
},
- buildMatchingFieldsMap(activity, matchingFields) {
+ async buildMatchingFieldsMap(activity, matchingFields) {
const matchingMap = { activityType: activity.activityType };
- matchingFields.forEach(field => {
+ for (const field of matchingFields) {
// Creating a matching map with the actual field of the activity
// and with the wildcard (for example: trigger when a card is added
// in any [*] board
let value = activity[field];
if (field === 'oldListName') {
- const oldList = ReactiveCache.getList(activity.oldListId);
+ const oldList = await ReactiveCache.getList(activity.oldListId);
if (oldList) {
value = oldList.title;
}
} else if (field === 'oldSwimlaneName') {
- const oldSwimlane = ReactiveCache.getSwimlane(activity.oldSwimlaneId);
+ const oldSwimlane = await ReactiveCache.getSwimlane(activity.oldSwimlaneId);
if (oldSwimlane) {
value = oldSwimlane.title;
}
@@ -54,11 +54,11 @@ RulesHelper = {
matchingMap[field] = {
$in: matchesList,
};
- });
+ }
return matchingMap;
},
async performAction(activity, action) {
- const card = ReactiveCache.getCard(activity.cardId);
+ const card = await ReactiveCache.getCard(activity.cardId);
const boardId = activity.boardId;
if (
action.actionType === 'moveCardToTop' ||
@@ -67,12 +67,12 @@ RulesHelper = {
let list;
let listId;
if (action.listName === '*') {
- list = card.list();
+ list = await card.list();
if (boardId !== action.boardId) {
- list = ReactiveCache.getList({ title: list.title, boardId: action.boardId });
+ list = await ReactiveCache.getList({ title: list.title, boardId: action.boardId });
}
} else {
- list = ReactiveCache.getList({
+ list = await ReactiveCache.getList({
title: action.listName,
boardId: action.boardId,
});
@@ -86,36 +86,36 @@ RulesHelper = {
let swimlane;
let swimlaneId;
if (action.swimlaneName === '*') {
- swimlane = ReactiveCache.getSwimlane(card.swimlaneId);
+ swimlane = await ReactiveCache.getSwimlane(card.swimlaneId);
if (boardId !== action.boardId) {
- swimlane = ReactiveCache.getSwimlane({
+ swimlane = await ReactiveCache.getSwimlane({
title: swimlane.title,
boardId: action.boardId,
});
}
} else {
- swimlane = ReactiveCache.getSwimlane({
+ swimlane = await ReactiveCache.getSwimlane({
title: action.swimlaneName,
boardId: action.boardId,
});
}
if (swimlane === undefined) {
- swimlaneId = ReactiveCache.getSwimlane({
+ swimlaneId = (await ReactiveCache.getSwimlane({
title: 'Default',
boardId: action.boardId,
- })._id;
+ }))._id;
} else {
swimlaneId = swimlane._id;
}
if (action.actionType === 'moveCardToTop') {
const minOrder = _.min(
- list.cardsUnfiltered(swimlaneId).map(c => c.sort),
+ (await list.cardsUnfiltered(swimlaneId)).map(c => c.sort),
);
await card.move(action.boardId, swimlaneId, listId, minOrder - 1);
} else {
const maxOrder = _.max(
- list.cardsUnfiltered(swimlaneId).map(c => c.sort),
+ (await list.cardsUnfiltered(swimlaneId)).map(c => c.sort),
);
await card.move(action.boardId, swimlaneId, listId, maxOrder + 1);
}
@@ -132,7 +132,7 @@ RulesHelper = {
// Check if recipient is a Wekan user to get their language
if (to && to.includes('@')) {
- recipientUser = ReactiveCache.getUser({ 'emails.address': to.toLowerCase() });
+ recipientUser = await ReactiveCache.getUser({ 'emails.address': to.toLowerCase() });
if (recipientUser && typeof recipientUser.getLanguage === 'function') {
recipientLang = recipientUser.getLanguage();
}
@@ -262,7 +262,7 @@ RulesHelper = {
card.removeLabel(action.labelId);
}
if (action.actionType === 'addMember') {
- const memberId = ReactiveCache.getUser({ username: action.username })._id;
+ const memberId = (await ReactiveCache.getUser({ username: action.username }))._id;
card.assignMember(memberId);
}
if (action.actionType === 'removeMember') {
@@ -272,41 +272,41 @@ RulesHelper = {
card.unassignMember(members[i]);
}
} else {
- const memberId = ReactiveCache.getUser({ username: action.username })._id;
+ const memberId = (await ReactiveCache.getUser({ username: action.username }))._id;
card.unassignMember(memberId);
}
}
if (action.actionType === 'checkAll') {
- const checkList = ReactiveCache.getChecklist({
+ const checkList = await ReactiveCache.getChecklist({
title: action.checklistName,
cardId: card._id,
});
await checkList.checkAllItems();
}
if (action.actionType === 'uncheckAll') {
- const checkList = ReactiveCache.getChecklist({
+ const checkList = await ReactiveCache.getChecklist({
title: action.checklistName,
cardId: card._id,
});
await checkList.uncheckAllItems();
}
if (action.actionType === 'checkItem') {
- const checkList = ReactiveCache.getChecklist({
+ const checkList = await ReactiveCache.getChecklist({
title: action.checklistName,
cardId: card._id,
});
- const checkItem = ReactiveCache.getChecklistItem({
+ const checkItem = await ReactiveCache.getChecklistItem({
title: action.checkItemName,
checkListId: checkList._id,
});
await checkItem.check();
}
if (action.actionType === 'uncheckItem') {
- const checkList = ReactiveCache.getChecklist({
+ const checkList = await ReactiveCache.getChecklist({
title: action.checklistName,
cardId: card._id,
});
- const checkItem = ReactiveCache.getChecklistItem({
+ const checkItem = await ReactiveCache.getChecklistItem({
title: action.checkItemName,
checkListId: checkList._id,
});
@@ -340,21 +340,22 @@ RulesHelper = {
sort: 0,
});
const itemsArray = action.checklistItems.split(',');
- const checkList = ReactiveCache.getChecklist(checkListId);
+ const existingItems = await ReactiveCache.getChecklistItems({ checklistId: checkListId });
+ const sortBase = existingItems.length;
for (let i = 0; i < itemsArray.length; i++) {
ChecklistItems.insert({
title: itemsArray[i],
checklistId: checkListId,
cardId: card._id,
- sort: checkList.itemCount(),
+ sort: sortBase + i,
});
}
}
if (action.actionType === 'createCard') {
- const list = ReactiveCache.getList({ title: action.listName, boardId });
+ const list = await ReactiveCache.getList({ title: action.listName, boardId });
let listId = '';
let swimlaneId = '';
- const swimlane = ReactiveCache.getSwimlane({
+ const swimlane = await ReactiveCache.getSwimlane({
title: action.swimlaneName,
boardId,
});
@@ -364,7 +365,7 @@ RulesHelper = {
listId = list._id;
}
if (swimlane === undefined) {
- swimlaneId = ReactiveCache.getSwimlane({ title: 'Default', boardId })._id;
+ swimlaneId = (await ReactiveCache.getSwimlane({ title: 'Default', boardId }))._id;
} else {
swimlaneId = swimlane._id;
}
@@ -377,11 +378,11 @@ RulesHelper = {
});
}
if (action.actionType === 'linkCard') {
- const list = ReactiveCache.getList({ title: action.listName, boardId: action.boardId });
- const card = ReactiveCache.getCard(activity.cardId);
+ const list = await ReactiveCache.getList({ title: action.listName, boardId: action.boardId });
+ const card = await ReactiveCache.getCard(activity.cardId);
let listId = '';
let swimlaneId = '';
- const swimlane = ReactiveCache.getSwimlane({
+ const swimlane = await ReactiveCache.getSwimlane({
title: action.swimlaneName,
boardId: action.boardId,
});
@@ -391,7 +392,7 @@ RulesHelper = {
listId = list._id;
}
if (swimlane === undefined) {
- swimlaneId = ReactiveCache.getSwimlane({ title: 'Default', boardId: action.boardId })._id;
+ swimlaneId = (await ReactiveCache.getSwimlane({ title: 'Default', boardId: action.boardId }))._id;
} else {
swimlaneId = swimlane._id;
}