diff --git a/src/client/webapp/auto-verifier-with-args.ts b/src/client/webapp/auto-verifier-with-args.ts index 1c0d746..9ac2cb7 100644 --- a/src/client/webapp/auto-verifier-with-args.ts +++ b/src/client/webapp/auto-verifier-with-args.ts @@ -26,13 +26,13 @@ export class AutoVerifierWithArg { private tokenizer: (query: K) => string, // must be one-to-one mapping private primaryFunc: (query: K) => Promise, private trustedFunc: (query: K) => Promise, - private verifyFunc: (query: K, primaryResult: T | null, trustedResult: T | null) => Promise + private verifyFunc: (query: K, primaryResult: T | null, trustedResult: T | null) => Promise ) {} static createStandardPartialMessageListAutoVerifier & { id: string }>( primaryFunc: (query: PartialMessageListQuery) => Promise, trustedFunc: (query: PartialMessageListQuery) => Promise, - changesFunc: (query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes) => Promise + changesFunc: (query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes) => Promise ) { return new AutoVerifierWithArg( query => `ch#${query.channelId} m#${query.messageId}->${query.number}`, @@ -51,7 +51,7 @@ export class AutoVerifierWithArg { const changes = AutoVerifier.getChanges(primaryResult, trustedResult); //LOG.debug('changes:', { changes }); const changesType = AutoVerifier.getListChangesType(primaryResult, trustedResult, changes); - await changesFunc(query, changesType, changes); + return await changesFunc(query, changesType, changes); } ); } @@ -59,7 +59,7 @@ export class AutoVerifierWithArg { static createStandardIDQueriedSingleAutoVerifier & { id: string }>( primaryFunc: (query: IDQuery) => Promise, trustedFunc: (query: IDQuery) => Promise, - changesFunc: (query: IDQuery, changesType: AutoVerifierChangesType, primaryResult: T | null, trustedResult: T | null) => Promise + changesFunc: (query: IDQuery, changesType: AutoVerifierChangesType, primaryResult: T | null, trustedResult: T | null) => Promise ) { return new AutoVerifierWithArg( query => `id#${query.id}`, @@ -67,7 +67,7 @@ export class AutoVerifierWithArg { query => trustedFunc(query), async (query: IDQuery, primaryResult: T | null, trustedResult: T | null) => { const changesType = AutoVerifier.getSingleChangesType(primaryResult, trustedResult); - await changesFunc(query, changesType, primaryResult, trustedResult); + return await changesFunc(query, changesType, primaryResult, trustedResult); } ); } diff --git a/src/client/webapp/auto-verifier.ts b/src/client/webapp/auto-verifier.ts index a4d6e37..a9ff803 100644 --- a/src/client/webapp/auto-verifier.ts +++ b/src/client/webapp/auto-verifier.ts @@ -2,6 +2,7 @@ // const electronConsole = electronRemote.getGlobal('console') as Console; // import Logger from '../../logger/logger'; // const LOG = Logger.create(__filename, electronConsole); + import { Changes, WithEquals } from './data-types'; export enum AutoVerifierChangesType { @@ -29,12 +30,12 @@ export class AutoVerifier { * Allows a trusted function to verify the primary function * @param primaryFunc The primary function * @param trustedFunc The trusted function that will verify the results of the primary function (one time) - * @param verifyFunc The verification function that is called when the primary function's results need to be verified against the trusted function's results + * @param verifyFunc The verification function that is called when the primary function's results need to be verified against the trusted function's results. Must return true if the structure was updated, false if not. */ constructor( private primaryFunc: () => Promise, private trustedFunc: () => Promise, - private verifyFunc: (primaryResult: T | null, trustedResult: T | null) => Promise + private verifyFunc: (primaryResult: T | null, trustedResult: T | null) => Promise ) {} static getChanges & { id: string }>(primaryResult: T[] | null, trustedResult: T[] | null): Changes { @@ -104,7 +105,7 @@ export class AutoVerifier { static createStandardListAutoVerifier & { id: string }>( primaryFunc: () => Promise, trustedFunc: () => Promise, - changesFunc: (changesType: AutoVerifierChangesType, changes: Changes) => Promise + changesFunc: (changesType: AutoVerifierChangesType, changes: Changes) => Promise ) { return new AutoVerifier( primaryFunc, @@ -112,7 +113,7 @@ export class AutoVerifier { async (primaryResult: T[] | null, trustedResult: T[] | null) => { const changes = AutoVerifier.getChanges(primaryResult, trustedResult); const changesType = AutoVerifier.getListChangesType(primaryResult, trustedResult, changes); - await changesFunc(changesType, changes); + return await changesFunc(changesType, changes); } ); } @@ -120,14 +121,14 @@ export class AutoVerifier { static createStandardSingleAutoVerifier>( primaryFunc: () => Promise, trustedFunc: () => Promise, - changesFunc: (changesType: AutoVerifierChangesType, primaryResult: T | null, trustedResult: T | null) => Promise + changesFunc: (changesType: AutoVerifierChangesType, primaryResult: T | null, trustedResult: T | null) => Promise ) { return new AutoVerifier( primaryFunc, trustedFunc, async (primaryResult: T | null, trustedResult: T | null) => { const changesType = AutoVerifier.getSingleChangesType(primaryResult, trustedResult); - await changesFunc(changesType, primaryResult, trustedResult); + return await changesFunc(changesType, primaryResult, trustedResult); } ); } @@ -148,7 +149,7 @@ export class AutoVerifier { // Fetches the result of the primary fetchable // If the primary fetchable returns null but has not been verified yet, this will return the result of the trusted fetchable // If the trusted fetchable has not been used to verify the primary fetchable yet, this queries the trusted fetchable and calls verify - // @param lazyVerify: set to true to only verify if primaryResult returns null + // @param lazyVerify: set to true to only verify if primaryResult returns null (useful for fetching resources since they can never change) async fetchAndVerifyIfNeeded(lazyVerify = false): Promise { // eslint-disable-next-line no-async-promise-executor return await new Promise(async (resolve: (result: T | null) => void, reject: (error: Error) => void) => { @@ -167,6 +168,7 @@ export class AutoVerifier { const primaryResult = await this.primaryPromise; if (this.primaryPromise === origPrimaryPromise) { + // Reset the primary promise so we create a new one next time this.primaryPromise = null; } @@ -206,10 +208,17 @@ export class AutoVerifier { // Make sure to verify BEFORE potentially resolving // This way the conflicts can be resolved before the result is returned - await this.verifyFunc(primaryResult, trustedResult); + const primaryUpToDate = await this.verifyFunc(primaryResult, trustedResult); if (this.trustedPromise === origTrustedPromise) { - this.trustedStatus = 'verified'; + if (trustedResult !== null && primaryUpToDate) { + // We got a good trusted result and the primary data has been updated + // to reflect the trusted data (or already reflects it). + this.trustedStatus = 'verified'; + } else { + // We have to re-fetch the trusted promise again next fetch + this.trustedStatus = 'none'; + } } else { // this actually could be quite common //console.warn('RARE ALERT: we got unverified during verification!'); diff --git a/src/client/webapp/fetchable-pair-verifier.ts b/src/client/webapp/fetchable-pair-verifier.ts index 3c63ac2..e9a4217 100644 --- a/src/client/webapp/fetchable-pair-verifier.ts +++ b/src/client/webapp/fetchable-pair-verifier.ts @@ -57,6 +57,8 @@ export default class PairVerifierFetchable extends EventEmitter im ); this.fetchTokensVerifier = AutoVerifier.createStandardListAutoVerifier( + // async () => { LOG.debug('fetching primary tokens for ' + this.primary.constructor.name); return await this.primary.fetchTokens() }, + // async () => { LOG.debug('fetching primary tokens for ' + this.trusted.constructor.name); return await this.trusted.fetchTokens() }, async () => await this.primary.fetchTokens(), async () => await this.trusted.fetchTokens(), this.handleTokensConflict.bind(this) @@ -87,7 +89,7 @@ export default class PairVerifierFetchable extends EventEmitter im ); } - async handleMetadataConflict(changesType: AutoVerifierChangesType, primaryMetadata: GuildMetadata | null, trustedMetadata: GuildMetadata | null): Promise { + async handleMetadataConflict(changesType: AutoVerifierChangesType, primaryMetadata: GuildMetadata | null, trustedMetadata: GuildMetadata | null): Promise { // LOG.debug('metadata conflict', { // primaryClass: this.primary.constructor.name, // trustedClass: this.trusted.constructor.name, @@ -95,63 +97,75 @@ export default class PairVerifierFetchable extends EventEmitter im // primaryMetadata: primaryMetadata, // trustedMetadata: trustedMetadata, // }); + let success = true; if (changesType === AutoVerifierChangesType.TRUSTED_ONLY) { - await this.primary.handleMetadataChanged(trustedMetadata as GuildMetadata); + success = success && await this.primary.handleMetadataChanged(trustedMetadata as GuildMetadata); } else if (changesType === AutoVerifierChangesType.CONFLICT) { - await this.primary.handleMetadataChanged(trustedMetadata as GuildMetadata); + success = success && await this.primary.handleMetadataChanged(trustedMetadata as GuildMetadata); this.emit('conflict-metadata', changesType, primaryMetadata as GuildMetadata, trustedMetadata as GuildMetadata); } + return success; } - async handleMembersConflict(changesType: AutoVerifierChangesType, changes: Changes): Promise{ - if (changes.added.length > 0) await this.primary.handleMembersAdded(changes.added); - if (changes.updated.length > 0) await this.primary.handleMembersChanged(changes.updated.map(change => change.newDataPoint)); - if (changes.deleted.length > 0) await this.primary.handleMembersDeleted(changes.deleted); + async handleMembersConflict(changesType: AutoVerifierChangesType, changes: Changes): Promise{ + let success = true; + if (changes.added.length > 0) success = success && await this.primary.handleMembersAdded(changes.added); + if (changes.updated.length > 0) success = success && await this.primary.handleMembersChanged(changes.updated.map(change => change.newDataPoint)); + if (changes.deleted.length > 0) success = success && await this.primary.handleMembersDeleted(changes.deleted); if (changesType === AutoVerifierChangesType.CONFLICT) { this.emit('conflict-members', changesType, changes); } + return success; } - async handleChannelsConflict(changesType: AutoVerifierChangesType, changes: Changes): Promise { - if (changes.added.length > 0) await this.primary.handleChannelsAdded(changes.added); - if (changes.updated.length > 0) await this.primary.handleChannelsChanged(changes.updated.map(change => change.newDataPoint)); - if (changes.deleted.length > 0) await this.primary.handleChannelsDeleted(changes.deleted); + async handleChannelsConflict(changesType: AutoVerifierChangesType, changes: Changes): Promise { + let success = true; + if (changes.added.length > 0) success = success && await this.primary.handleChannelsAdded(changes.added); + if (changes.updated.length > 0) success = success && await this.primary.handleChannelsChanged(changes.updated.map(change => change.newDataPoint)); + if (changes.deleted.length > 0) success = success && await this.primary.handleChannelsDeleted(changes.deleted); if (changesType === AutoVerifierChangesType.CONFLICT) { this.emit('conflict-channels', changesType, changes); } + return success; } - async handleTokensConflict(changesType: AutoVerifierChangesType, changes: Changes): Promise { - if (changes.added.length > 0) await this.primary.handleTokensAdded(changes.added); - if (changes.updated.length > 0) await this.primary.handleTokensChanged(changes.updated.map(change => change.newDataPoint)); - if (changes.deleted.length > 0) await this.primary.handleTokensDeleted(changes.deleted); + async handleTokensConflict(changesType: AutoVerifierChangesType, changes: Changes): Promise { + let success = true; + if (changes.added.length > 0) success = success && await this.primary.handleTokensAdded(changes.added); + if (changes.updated.length > 0) success = success && await this.primary.handleTokensChanged(changes.updated.map(change => change.newDataPoint)); + if (changes.deleted.length > 0) success = success && await this.primary.handleTokensDeleted(changes.deleted); if (changesType === AutoVerifierChangesType.CONFLICT) { this.emit('conflict-tokens', changesType, changes); } + return success; } - async handleResourceConflict(query: IDQuery, changesType: AutoVerifierChangesType, primaryResource: Resource | null, trustedResource: Resource | null): Promise { + async handleResourceConflict(query: IDQuery, changesType: AutoVerifierChangesType, primaryResource: Resource | null, trustedResource: Resource | null): Promise { + let success = true; if (changesType === AutoVerifierChangesType.PRIMARY_ONLY) { - await this.primary.handleResourceDeleted(trustedResource as Resource); + success = success && await this.primary.handleResourceDeleted(trustedResource as Resource); } else if (changesType === AutoVerifierChangesType.TRUSTED_ONLY) { - await this.primary.handleResourceAdded(trustedResource as Resource); + success = success && await this.primary.handleResourceAdded(trustedResource as Resource); } else if (changesType === AutoVerifierChangesType.CONFLICT) { - await this.primary.handleResourceChanged(trustedResource as Resource); + success = success && await this.primary.handleResourceChanged(trustedResource as Resource); this.emit('conflict-resource', query, changesType, primaryResource as Resource, trustedResource as Resource); } + return success; } - async handleMessagesConflict(query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes): Promise { - if (changes.added.length > 0) await this.primary.handleMessagesAdded(changes.added); - if (changes.updated.length > 0) await this.primary.handleMessagesChanged(changes.updated.map(change => change.newDataPoint)); - if (changes.deleted.length > 0) await this.primary.handleMessagesDeleted(changes.deleted); + async handleMessagesConflict(query: PartialMessageListQuery, changesType: AutoVerifierChangesType, changes: Changes): Promise { + let success = true; + if (changes.added.length > 0) success = success && await this.primary.handleMessagesAdded(changes.added); + if (changes.updated.length > 0) success = success && await this.primary.handleMessagesChanged(changes.updated.map(change => change.newDataPoint)); + if (changes.deleted.length > 0) success = success && await this.primary.handleMessagesDeleted(changes.deleted); if (changesType === AutoVerifierChangesType.CONFLICT) { this.emit('conflict-messages', query, changesType, changes); } + return success; } unverify() { diff --git a/src/client/webapp/guild-personal-db.ts b/src/client/webapp/guild-personal-db.ts index 30bcdbb..94a9219 100644 --- a/src/client/webapp/guild-personal-db.ts +++ b/src/client/webapp/guild-personal-db.ts @@ -55,70 +55,84 @@ export default class PersonalDBGuild implements AsyncFetchable, AsyncLackable { // Lacking Methods (resolving differences) - async handleMetadataChanged(changedMetaData: GuildMetadata): Promise { - (async () => { await this.db.updateGuildName(this.guildId, changedMetaData.name); })(); - (async () => { await this.db.updateGuildIcon(this.guildId, changedMetaData.iconResourceId); })(); + async handleMetadataChanged(changedMetaData: GuildMetadata): Promise { + // TODO: These could be done in parallel + await this.db.updateGuildName(this.guildId, changedMetaData.name); + await this.db.updateGuildIcon(this.guildId, changedMetaData.iconResourceId); + return true; } - async handleMembersAdded(addedMembers: Member[]): Promise { + async handleMembersAdded(addedMembers: Member[]): Promise { //LOG.debug(addedMembers.length + ' personal members added'); await this.db.addMembers(this.guildId, addedMembers); + return true; } - async handleMembersChanged(changedMembers: Member[]): Promise { + async handleMembersChanged(changedMembers: Member[]): Promise { //LOG.debug(changedMembers.length + ' personal members changed'); await this.db.updateMembers(this.guildId, changedMembers); + return true; } - async handleMembersDeleted(deletedMembers: Member[]): Promise { + async handleMembersDeleted(deletedMembers: Member[]): Promise { //LOG.debug(deletedMembers.length + ' personal members deleted'); await this.db.deleteMembers(this.guildId, deletedMembers); + return true; } - async handleChannelsAdded(addedChannels: Channel[]): Promise { + async handleChannelsAdded(addedChannels: Channel[]): Promise { //LOG.debug(addedChannels.length + ' personal channels added'); await this.db.addChannels(this.guildId, addedChannels); + return true; } - async handleChannelsChanged(changedChannels: Channel[]): Promise { + async handleChannelsChanged(changedChannels: Channel[]): Promise { //LOG.debug(changedChannels.length + ' personal channels changed'); await this.db.updateChannels(this.guildId, changedChannels); + return true; } - async handleChannelsDeleted(deletedChannels: Channel[]): Promise { + async handleChannelsDeleted(deletedChannels: Channel[]): Promise { //LOG.debug(deletedChannels.length + ' personal channels deleted'); await this.db.deleteChannels(this.guildId, deletedChannels); + return true; } - async handleMessagesAdded(addedMessages: Message[]): Promise { + async handleMessagesAdded(addedMessages: Message[]): Promise { //LOG.debug(addedMessages.length + ' personal messages added'); await this.db.addMessages(this.guildId, addedMessages); + return true; } - async handleMessagesChanged(changedMessages: Message[]): Promise { + async handleMessagesChanged(changedMessages: Message[]): Promise { //LOG.debug(changedMessages.length + ' personal messages changed'); await this.db.updateMessages(this.guildId, changedMessages); + return true; } - async handleMessagesDeleted(deletedMessages: Message[]): Promise { + async handleMessagesDeleted(deletedMessages: Message[]): Promise { //LOG.debug(deletedMessages.length + ' personal messages deleted'); await this.db.deleteMessages(this.guildId, deletedMessages); + return true; } - async handleResourceAdded(addedResource: Resource): Promise { + async handleResourceAdded(addedResource: Resource): Promise { //LOG.debug('personal resoprces added', { addedResource }); await this.db.addResources(this.guildId, [ addedResource ]); + return true; } - async handleResourceChanged(changedResource: Resource): Promise { + async handleResourceChanged(changedResource: Resource): Promise { //LOG.debug('personal resoprces changed', { changedResource }); await this.db.updateResources(this.guildId, [ changedResource ]); + return true; } - async handleResourceDeleted(deletedResource: Resource): Promise { + async handleResourceDeleted(deletedResource: Resource): Promise { //LOG.debug('personal resoprces deleted', { deletedResource }); await this.db.deleteResources(this.guildId, [ deletedResource ]); + return true; } - async handleTokensAdded(addedTokens: Token[]): Promise { - return; // cache currently does not handle tokens + async handleTokensAdded(addedTokens: Token[]): Promise { + return false; // cache currently does not handle tokens } - async handleTokensChanged(changedTokens: Token[]): Promise { - return; // cache currently does not handle tokens + async handleTokensChanged(changedTokens: Token[]): Promise { + return false; // cache currently does not handle tokens } - async handleTokensDeleted(deletedTokens: Token[]): Promise { - return; // cache currently does not handle tokens + async handleTokensDeleted(deletedTokens: Token[]): Promise { + return false; // cache currently does not handle tokens } } diff --git a/src/client/webapp/guild-ram.ts b/src/client/webapp/guild-ram.ts index cb1671c..68294fb 100644 --- a/src/client/webapp/guild-ram.ts +++ b/src/client/webapp/guild-ram.ts @@ -51,46 +51,53 @@ export default class RAMGuild implements SyncFetchable, SyncLackable { return null; // TODO: token ram cache } - handleMetadataChanged(changedMetaData: GuildMetadata): void { + handleMetadataChanged(changedMetaData: GuildMetadata): boolean { this.metadata = changedMetaData; + return true; } - handleMembersAdded(addedMembers: Member[]): void { + handleMembersAdded(addedMembers: Member[]): boolean { // LOG.debug('RAM members added', { addedMembers }); for (const member of addedMembers) { this.members.set(member.id, member); } + return true; } - handleMembersChanged(changedMembers: Member[]): void { + handleMembersChanged(changedMembers: Member[]): boolean { // LOG.debug('RAM members changed', { changedMembers }); for (const member of changedMembers) { if (this.members.has(member.id)) this.members.set(member.id, member); } + return true; } - handleMembersDeleted(deletedMembers: Member[]): void { + handleMembersDeleted(deletedMembers: Member[]): boolean { // LOG.debug('RAM members deleted', { deletedMembers }); for (const member of deletedMembers) { this.members.delete(member.id); } + return true; } - handleChannelsAdded(addedChannels: Channel[]): void { + handleChannelsAdded(addedChannels: Channel[]): boolean { for (const channel of addedChannels) { this.channels.set(channel.id, channel); } + return true; } - handleChannelsChanged(changedChannels: Channel[]): void { + handleChannelsChanged(changedChannels: Channel[]): boolean { for (const channel of changedChannels) { if (this.channels.has(channel.id)) this.channels.set(channel.id, channel); } + return true; } - handleChannelsDeleted(deletedChannels: Channel[]): void { + handleChannelsDeleted(deletedChannels: Channel[]): boolean { for (const channel of deletedChannels) { this.channels.delete(channel.id); } + return true; } - handleMessagesAdded(addedMessages: Message[]): void { + handleMessagesAdded(addedMessages: Message[]): boolean { const byChannel = new Map(); for (const message of addedMessages) { if (!byChannel.has(message.channel.id)) { @@ -102,8 +109,9 @@ export default class RAMGuild implements SyncFetchable, SyncLackable { for (const [ channelId, messages ] of byChannel.entries()) { this.recentMessages.upsertMessages(this.guildId, channelId, messages); } + return true; } - handleMessagesChanged(changedMessages: Message[]): void { + handleMessagesChanged(changedMessages: Message[]): boolean { const byChannel = new Map(); for (const message of changedMessages) { if (!byChannel.has(message.channel.id)) { @@ -115,8 +123,9 @@ export default class RAMGuild implements SyncFetchable, SyncLackable { for (const [ channelId, messages ] of byChannel.entries()) { this.recentMessages.upsertMessages(this.guildId, channelId, messages); } + return true; } - handleMessagesDeleted(deletedMessages: Message[]): void { + handleMessagesDeleted(deletedMessages: Message[]): boolean { const byChannel = new Map(); for (const message of deletedMessages) { if (!byChannel.has(message.channel.id)) { @@ -128,28 +137,31 @@ export default class RAMGuild implements SyncFetchable, SyncLackable { for (const [ channelId, messages ] of byChannel.entries()) { this.recentMessages.deleteMessages(this.guildId, channelId, messages); } + return true; } - handleResourceAdded(addedResource: Resource): void { + handleResourceAdded(addedResource: Resource): boolean { this.resources.putResource(this.guildId, addedResource); + return true; } - handleResourceChanged(changedResource: Resource): void { + handleResourceChanged(changedResource: Resource): boolean { // This should be rare/never happen this.resources.putResource(this.guildId, changedResource); + return true; } - handleResourceDeleted(deletedResource: Resource): void { - // do nothing, the cache will garbage collect itself :) + handleResourceDeleted(deletedResource: Resource): boolean { + // do nothing, the cache will garbage collect itself and this is probably fine to leave as verified :) // also resource deletion should be pretty rare, especially in a cache - return; + return true; } - handleTokensAdded(addedTokens: Token[]): void { - return; // TODO: token ram cache + handleTokensAdded(addedTokens: Token[]): boolean { + return false; // TODO: token ram cache } - handleTokensChanged(changedTokens: Token[]): void { - return; // TODO: token ram cache + handleTokensChanged(changedTokens: Token[]): boolean { + return false; // TODO: token ram cache } - handleTokensDeleted(deletedTokens: Token[]): void { - return; // TODO: token ram cache + handleTokensDeleted(deletedTokens: Token[]): boolean { + return false; // TODO: token ram cache } } \ No newline at end of file diff --git a/src/client/webapp/guild-socket.ts b/src/client/webapp/guild-socket.ts index 7ba73a8..7d0b674 100644 --- a/src/client/webapp/guild-socket.ts +++ b/src/client/webapp/guild-socket.ts @@ -116,9 +116,12 @@ export default class SocketGuild extends EventEmitter implements As let dedup = this.queryDedups.get(id); if (!dedup) { dedup = new DedupAwaiter(async () => { - const result = await this.queryOnceVerified(timeout, endpoint, ...args); - this.queryDedups.delete(id); - return result; + try { + const result = await this.queryOnceVerified(timeout, endpoint, ...args); + return result; + } finally { + this.queryDedups.delete(id); + } }); this.queryDedups.set(id, dedup); } diff --git a/src/client/webapp/guild-types.ts b/src/client/webapp/guild-types.ts index 7bd9ce9..126dd51 100644 --- a/src/client/webapp/guild-types.ts +++ b/src/client/webapp/guild-types.ts @@ -58,53 +58,55 @@ export type Requestable = AsyncRequestable; // Lackable // A connectable that could be lacking data (or doesn't always have the most up-to-date data) // This connectable has functions to manage what to do when the data it was lacking is found +// handlers return a boolean. If true, their new data is up to date and they will mirror the trusted +// data. If false, it is not up to date and the trusted data should be fetched again. export interface AsyncLackable { - handleMetadataChanged(changedMetaData: GuildMetadata): Promise; + handleMetadataChanged(changedMetaData: GuildMetadata): Promise; - handleMembersAdded(addedMembers: Member[]): Promise; - handleMembersChanged(changedMembers: Member[]): Promise; - handleMembersDeleted(deletedMembers: Member[]): Promise; + handleMembersAdded(addedMembers: Member[]): Promise; + handleMembersChanged(changedMembers: Member[]): Promise; + handleMembersDeleted(deletedMembers: Member[]): Promise; - handleChannelsAdded(addedChannels: Channel[]): Promise; - handleChannelsChanged(changedChannels: Channel[]): Promise; - handleChannelsDeleted(deletedChannels: Channel[]): Promise; + handleChannelsAdded(addedChannels: Channel[]): Promise; + handleChannelsChanged(changedChannels: Channel[]): Promise; + handleChannelsDeleted(deletedChannels: Channel[]): Promise; - handleMessagesAdded(addedMessages: Message[]): Promise; - handleMessagesChanged(changedMessages: Message[]): Promise; - handleMessagesDeleted(deletedMessages: Message[]): Promise; + handleMessagesAdded(addedMessages: Message[]): Promise; + handleMessagesChanged(changedMessages: Message[]): Promise; + handleMessagesDeleted(deletedMessages: Message[]): Promise; - handleResourceAdded(addedResource: Resource): Promise; - handleResourceChanged(changedResource: Resource): Promise; - handleResourceDeleted(deletedResource: Resource): Promise; + handleResourceAdded(addedResource: Resource): Promise; + handleResourceChanged(changedResource: Resource): Promise; + handleResourceDeleted(deletedResource: Resource): Promise; - handleTokensAdded(addedTokens: Token[]): Promise; - handleTokensChanged(changedTokens: Token[]): Promise; - handleTokensDeleted(deletedTokens: Token[]): Promise; + handleTokensAdded(addedTokens: Token[]): Promise; + handleTokensChanged(changedTokens: Token[]): Promise; + handleTokensDeleted(deletedTokens: Token[]): Promise; } export interface SyncLackable { - handleMetadataChanged(changedMetaData: GuildMetadata): void; + handleMetadataChanged(changedMetaData: GuildMetadata): boolean; - handleMembersAdded(addedMembers: Member[]): void; - handleMembersChanged(changedMembers: Member[]): void; - handleMembersDeleted(deletedMembers: Member[]): void; + handleMembersAdded(addedMembers: Member[]): boolean; + handleMembersChanged(changedMembers: Member[]): boolean; + handleMembersDeleted(deletedMembers: Member[]): boolean; - handleChannelsAdded(addedChannels: Channel[]): void; - handleChannelsChanged(changedChannels: Channel[]): void; - handleChannelsDeleted(deletedChannels: Channel[]): void; + handleChannelsAdded(addedChannels: Channel[]): boolean; + handleChannelsChanged(changedChannels: Channel[]): boolean; + handleChannelsDeleted(deletedChannels: Channel[]): boolean; - handleMessagesAdded(addedMessages: Message[]): void; - handleMessagesChanged(changedMessages: Message[]): void; - handleMessagesDeleted(deletedMessages: Message[]): void; + handleMessagesAdded(addedMessages: Message[]): boolean; + handleMessagesChanged(changedMessages: Message[]): boolean; + handleMessagesDeleted(deletedMessages: Message[]): boolean; - handleResourceAdded(addedResource: Resource): void; - handleResourceChanged(changedResource: Resource): void; - handleResourceDeleted(deletedResource: Resource): void; + handleResourceAdded(addedResource: Resource): boolean; + handleResourceChanged(changedResource: Resource): boolean; + handleResourceDeleted(deletedResource: Resource): boolean; - handleTokensAdded(addedTokens: Token[]): void; - handleTokensChanged(changedTokens: Token[]): void; - handleTokensDeleted(deletedTokens: Token[]): void; + handleTokensAdded(addedTokens: Token[]): boolean; + handleTokensChanged(changedTokens: Token[]): boolean; + handleTokensDeleted(deletedTokens: Token[]): boolean; } export type Lackable = AsyncLackable | SyncLackable;