WIP! A BB-style forum, on the ATmosphere! We're still working... we'll be back soon when we have something to show off!
node typescript hono htmx atproto

refactor: replace firehose handler boilerplate with factory method (#22)

Replace 18 one-liner handler property declarations and the wrapHandler
method with a single createWrappedHandler factory that generates
circuit-breaker-wrapped handlers from Indexer method names.

https://claude.ai/code/session_01YL7ZhPgh8CZ9QzKH5A2exT

Co-authored-by: Claude <noreply@anthropic.com>

authored by

Malpercio
Claude
and committed by
GitHub
b758cd90 832b1f86

+31 -72
+31 -72
apps/appview/src/lib/firehose.ts
··· 67 67 } 68 68 69 69 /** 70 + * Factory method that creates a wrapped handler for a given Indexer method. 71 + * The returned handler delegates to the indexer method with circuit breaker protection. 72 + */ 73 + private createWrappedHandler<M extends keyof Indexer>(methodName: M) { 74 + return async (event: any) => { 75 + await this.circuitBreaker.execute( 76 + () => (this.indexer[methodName] as any).call(this.indexer, event), 77 + methodName as string 78 + ); 79 + }; 80 + } 81 + 82 + /** 70 83 * Create and configure the event handler registry 71 84 */ 72 85 private createHandlerRegistry(): EventHandlerRegistry { 73 86 return new EventHandlerRegistry() 74 87 .register({ 75 88 collection: "space.atbb.post", 76 - onCreate: this.handlePostCreate, 77 - onUpdate: this.handlePostUpdate, 78 - onDelete: this.handlePostDelete, 89 + onCreate: this.createWrappedHandler("handlePostCreate"), 90 + onUpdate: this.createWrappedHandler("handlePostUpdate"), 91 + onDelete: this.createWrappedHandler("handlePostDelete"), 79 92 }) 80 93 .register({ 81 94 collection: "space.atbb.forum.forum", 82 - onCreate: this.handleForumCreate, 83 - onUpdate: this.handleForumUpdate, 84 - onDelete: this.handleForumDelete, 95 + onCreate: this.createWrappedHandler("handleForumCreate"), 96 + onUpdate: this.createWrappedHandler("handleForumUpdate"), 97 + onDelete: this.createWrappedHandler("handleForumDelete"), 85 98 }) 86 99 .register({ 87 100 collection: "space.atbb.forum.category", 88 - onCreate: this.handleCategoryCreate, 89 - onUpdate: this.handleCategoryUpdate, 90 - onDelete: this.handleCategoryDelete, 101 + onCreate: this.createWrappedHandler("handleCategoryCreate"), 102 + onUpdate: this.createWrappedHandler("handleCategoryUpdate"), 103 + onDelete: this.createWrappedHandler("handleCategoryDelete"), 91 104 }) 92 105 .register({ 93 106 collection: "space.atbb.membership", 94 - onCreate: this.handleMembershipCreate, 95 - onUpdate: this.handleMembershipUpdate, 96 - onDelete: this.handleMembershipDelete, 107 + onCreate: this.createWrappedHandler("handleMembershipCreate"), 108 + onUpdate: this.createWrappedHandler("handleMembershipUpdate"), 109 + onDelete: this.createWrappedHandler("handleMembershipDelete"), 97 110 }) 98 111 .register({ 99 112 collection: "space.atbb.modAction", 100 - onCreate: this.handleModActionCreate, 101 - onUpdate: this.handleModActionUpdate, 102 - onDelete: this.handleModActionDelete, 113 + onCreate: this.createWrappedHandler("handleModActionCreate"), 114 + onUpdate: this.createWrappedHandler("handleModActionUpdate"), 115 + onDelete: this.createWrappedHandler("handleModActionDelete"), 103 116 }) 104 117 .register({ 105 118 collection: "space.atbb.reaction", 106 - onCreate: this.handleReactionCreate, 107 - onUpdate: this.handleReactionUpdate, 108 - onDelete: this.handleReactionDelete, 119 + onCreate: this.createWrappedHandler("handleReactionCreate"), 120 + onUpdate: this.createWrappedHandler("handleReactionUpdate"), 121 + onDelete: this.createWrappedHandler("handleReactionDelete"), 109 122 }); 110 123 } 111 124 ··· 218 231 } 219 232 } 220 233 221 - /** 222 - * Wrap handler to track failures and stop firehose on excessive errors 223 - */ 224 - private async wrapHandler<T>( 225 - handler: (event: T) => Promise<void>, 226 - event: T, 227 - handlerName: string 228 - ): Promise<void> { 229 - await this.circuitBreaker.execute(() => handler(event), handlerName); 230 - } 231 - 232 - // ── Event Handlers ────────────────────────────────────── 233 - 234 - private handlePostCreate = async (event: Parameters<Indexer['handlePostCreate']>[0]) => 235 - this.wrapHandler(this.indexer.handlePostCreate.bind(this.indexer), event, "handlePostCreate"); 236 - private handlePostUpdate = async (event: Parameters<Indexer['handlePostUpdate']>[0]) => 237 - this.wrapHandler(this.indexer.handlePostUpdate.bind(this.indexer), event, "handlePostUpdate"); 238 - private handlePostDelete = async (event: Parameters<Indexer['handlePostDelete']>[0]) => 239 - this.wrapHandler(this.indexer.handlePostDelete.bind(this.indexer), event, "handlePostDelete"); 240 - 241 - private handleForumCreate = async (event: Parameters<Indexer['handleForumCreate']>[0]) => 242 - this.wrapHandler(this.indexer.handleForumCreate.bind(this.indexer), event, "handleForumCreate"); 243 - private handleForumUpdate = async (event: Parameters<Indexer['handleForumUpdate']>[0]) => 244 - this.wrapHandler(this.indexer.handleForumUpdate.bind(this.indexer), event, "handleForumUpdate"); 245 - private handleForumDelete = async (event: Parameters<Indexer['handleForumDelete']>[0]) => 246 - this.wrapHandler(this.indexer.handleForumDelete.bind(this.indexer), event, "handleForumDelete"); 247 - 248 - private handleCategoryCreate = async (event: Parameters<Indexer['handleCategoryCreate']>[0]) => 249 - this.wrapHandler(this.indexer.handleCategoryCreate.bind(this.indexer), event, "handleCategoryCreate"); 250 - private handleCategoryUpdate = async (event: Parameters<Indexer['handleCategoryUpdate']>[0]) => 251 - this.wrapHandler(this.indexer.handleCategoryUpdate.bind(this.indexer), event, "handleCategoryUpdate"); 252 - private handleCategoryDelete = async (event: Parameters<Indexer['handleCategoryDelete']>[0]) => 253 - this.wrapHandler(this.indexer.handleCategoryDelete.bind(this.indexer), event, "handleCategoryDelete"); 254 - 255 - private handleMembershipCreate = async (event: Parameters<Indexer['handleMembershipCreate']>[0]) => 256 - this.wrapHandler(this.indexer.handleMembershipCreate.bind(this.indexer), event, "handleMembershipCreate"); 257 - private handleMembershipUpdate = async (event: Parameters<Indexer['handleMembershipUpdate']>[0]) => 258 - this.wrapHandler(this.indexer.handleMembershipUpdate.bind(this.indexer), event, "handleMembershipUpdate"); 259 - private handleMembershipDelete = async (event: Parameters<Indexer['handleMembershipDelete']>[0]) => 260 - this.wrapHandler(this.indexer.handleMembershipDelete.bind(this.indexer), event, "handleMembershipDelete"); 261 - 262 - private handleModActionCreate = async (event: Parameters<Indexer['handleModActionCreate']>[0]) => 263 - this.wrapHandler(this.indexer.handleModActionCreate.bind(this.indexer), event, "handleModActionCreate"); 264 - private handleModActionUpdate = async (event: Parameters<Indexer['handleModActionUpdate']>[0]) => 265 - this.wrapHandler(this.indexer.handleModActionUpdate.bind(this.indexer), event, "handleModActionUpdate"); 266 - private handleModActionDelete = async (event: Parameters<Indexer['handleModActionDelete']>[0]) => 267 - this.wrapHandler(this.indexer.handleModActionDelete.bind(this.indexer), event, "handleModActionDelete"); 268 - 269 - private handleReactionCreate = async (event: Parameters<Indexer['handleReactionCreate']>[0]) => 270 - this.wrapHandler(this.indexer.handleReactionCreate.bind(this.indexer), event, "handleReactionCreate"); 271 - private handleReactionUpdate = async (event: Parameters<Indexer['handleReactionUpdate']>[0]) => 272 - this.wrapHandler(this.indexer.handleReactionUpdate.bind(this.indexer), event, "handleReactionUpdate"); 273 - private handleReactionDelete = async (event: Parameters<Indexer['handleReactionDelete']>[0]) => 274 - this.wrapHandler(this.indexer.handleReactionDelete.bind(this.indexer), event, "handleReactionDelete"); 275 234 }