···1414export interface ConnectionConfig {
1515 /**
1616 * a unique identifier for the connection.
1717- *
1717+ *
1818 * if not specified, a random id will be generated.
1919 */
2020 id?: string,
···2929 label?: string,
3030 /**
3131 * the URL of the IRC server to connect to.
3232- *
3232+ *
3333 * the protocol used varies depending on the type of connection, but
3434- * it's usually either `ws[s]://` for websocket connections or
3434+ * it's usually either `ws[s]://` for websocket connections or
3535 * `irc[s]://` for tcp connections.
3636 */
3737 url: string | URL,
···4040 */
4141 nickname: string,
4242 /**
4343- * the user's username (or ident).
4343+ * the user's username (or ident).
4444 * defaults to the same value as {@link nickname}.
4545 */
4646 username?: string,
4747 /**
4848- * the user's "real name" or "GECOS" field. contains general information
4848+ * the user's "real name" or "GECOS" field. contains general information
4949 * about the user.
5050 */
5151 realname: string,
5252 /**
5353- * automatically connect when this connection is constructed.
5353+ * automatically connect when this connection is constructed.
5454 * @default false
5555 */
5656 autoconnect?: boolean,
···9292 */
9393 Connected,
9494 /**
9595- * something went horribly wrong and the connection was severed.
9595+ * something went horribly wrong and the connection was severed.
9696 * the reason is typically stored in {@link Connection.$error}
9797 */
9898 Failed,
···131131 nickname: string;
132132 username!: string;
133133 realname!: string;
134134- adapter_id?: string;
134134+ adapter_id?: string;
135135 hostname?: string;
136136 motd: string | null = null;
137137 system_msgs = <string[]>[]
···143143 capabilities: Map<string, string | null> = new Map();
144144 /**
145145 * Map of every capability the server advertised to us.
146146- *
147147- * These may or may not have been negotaited, check {@link capabilities}
146146+ *
147147+ * These may or may not have been negotaited, check {@link capabilities}
148148 * if you want to know what capabilities the client can actually use.
149149 */
150150 available_capabilities: Map<string, string | null> = new Map();
···154154 $error: Signal<[code: ConnectionErrorCode, reason?: IrcMessage] | null> = signal(null);
155155156156 abstract connect(): Promise<void>;
157157+157158 abstract disconnect(state?: ConnectionState): Promise<void>;
159159+158160 abstract send_raw(message: string): void;
159161160162 on_connect?: (conn: Connection) => void;
···170172 expect(...params: Parameters<TaskQueue["expect"]>) {
171173 return this.queue.expect(...params);
172174 }
175175+173176 collect(...params: Parameters<TaskQueue["collect"]>) {
174177 return this.queue.collect(...params);
175178 }
179179+176180 collect_batch(...params: Parameters<TaskQueue["collect_batch"]>) {
177181 return this.queue.collect_batch(...params);
178182 }
···198202199203 parsed.timestamp = new Date(server_time ?? Date.now());
200204201201- if (this.debug) console.debug(`${this.id} → RECEIVED: ${message}`, parsed);
205205+ if (this.debug) console.debug(`${this.id} → RECEIVED: ${message}`, parsed);
202206203207 // resolve pending tasks
204208 this.queue.resolve_tasks(parsed, { batch });
···210214211215 server_does_the_pinging = false;
212216 #ping_token = "tubes";
217217+213218 /**
214219 * begin pinging the server every {@link ping_interval} milliseconds.
215215- *
220220+ *
216221 * this is only used if the server doesn't bother pinging the client itself.
217222 */
218223 protected start_pinging() {
219224 const token = crypto.randomUUID();
220225 this.#ping_token = token;
221221-226226+222227 setTimeout(() => {
223228 if (this.server_does_the_pinging) {
224229 return;
···244249 * Dynamically update the connection's config whilst it's running.
245250 */
246251 #apply_config(config: ConnectionConfig) {
247247- this.id = config.id ?? nanoid();
252252+ this.id = config.id ?? nanoid();
248253 this.adapter_id = config.adapter_id;
249254 this.url = config.url instanceof URL ? config.url : new URL(config.url);
250255 this.label = config.label ?? this.url.hostname;
···279284 * @param new_nick The new nickname
280285 * @returns A promise that resolves when the change is acknowledged by the
281286 * server
282282- * @throws If the change is rejected by the server for whatever reason
287287+ * @throws If the change is rejected by the server for whatever reason
283288 * (e.g., the nickname is already in use).
284289 */
285290 async update_nick(new_nick: string) {
···298303 return this.nickname;
299304 }
300305301301- retry_interval = 0;
306306+ $retry_interval = signal(0);
302307 retry_count = 0;
308308+ interval_incr_amount = 1000;
303309 $recovering = signal(false);
304310305311 protected async recover_connection() {
306312 console.log("Recovering connection");
307313 this.$recovering.value = true;
314314+ if (this.$state.value == ConnectionState.Connected) {
315315+ return;
316316+ }
317317+308318 try {
309309- await this.connect().catch(x => {});
310310- } catch {}
311311-312312- if (this.retry_count > 5
319319+ await this.connect().catch(() => {
320320+ });
321321+ } catch {
322322+ }
323323+324324+ if (this.retry_count > 5
325325+ // @ts-ignore
313326 || this.$state.value == ConnectionState.Connected
314327 ) {
315328 this.$recovering.value = false;
316329 this.retry_count = 0;
317317- this.retry_interval = 0;
330330+ this.$retry_interval.value = 0;
331331+ this.interval_incr_amount = 1000;
318332 } else {
319319- this.retry_interval += 10000;
333333+ this.$retry_interval.value += this.interval_incr_amount;
334334+ this.interval_incr_amount *= 2;
320335 this.retry_count += 1;
321321-322322- console.log(`Reconnection failed, trying again in ${this.retry_interval}ms`)
323323-324324- setTimeout(() => this.recover_connection(), this.retry_interval);
336336+337337+ console.log(`Reconnection failed, trying again in ${this.$retry_interval.value}ms`)
338338+339339+ setTimeout(() => this.recover_connection(), this.$retry_interval.value);
325340 }
326341 }
327342328343 /**
329344 * join an IRC Channel
330330- *
345345+ *
331346 * if the channel is already in the list of joined channels, this will return it.
332332- *
333333- * @param channel The name of the channel.
334334- * @throws if the channel name doesn't match the network's supported channel prefixes
335335- * (see {@link ISupport.CHANTYPES}), is too long (see {@link ISupport.CHANNELLEN}),
347347+ *
348348+ * @param channel The name of the channel.
349349+ * @throws if the channel name doesn't match the network's supported channel prefixes
350350+ * (see {@link ISupport.CHANTYPES}), is too long (see {@link ISupport.CHANNELLEN}),
336351 * or the server forbids it (e.g., if you're banned).
337352 * @returns A promise that resolves when the channel been joined.
338353 */
···370385371386 /**
372387 * retrieve a channel from the connection's buffer list.
373373- *
388388+ *
374389 * @param name the name of the channel
375390 * @throws if the channel is actually a direct message
376391 * @returns the channel, or nothing
···401416 * @param target The channel or nick to retrieve history from.
402417 * @param range The range to retrieve history from. Can be an object with a
403418 * 'after' key or a 'before' key, or the string "latest".
404404- * @param limit The maximum number of messages to retrieve.
419419+ * @param limit The maximum number of messages to retrieve.
405420 */
406421 fetch_history?(...params: FetchHistoryParams): Promise<IrcMessage[]>;
407422···415430 sasl: () => this.capabilities.has("sasl"),
416431 batches: () => this.capabilities.has("batch"),
417432 }
433433+434434+ __debug_explode: (() => void) | undefined;
418435}
+4-1
core/ws/connection.ts
···100100 }
101101102102103103-104103 // do this /after/ getting the motd to avoid it being missing
105104 // when the connection opens.
106105 // this feels incorrect, but who gives a shit
···120119 this.$state.value = ConnectionState.Disconnected;
121120 }
122121 }
122122+123123+ __debug_explode = () => {
124124+ this.socket_error()
125125+ };
123126124127 private socket_error() {
125128 console.log("Socket Error Moment!!!!")
+4-1
neo/src/bits/buttons.tsx
···22import { FunctionalComponent } from "preact";
33import { HTMLProps } from "preact/compat";
4455-type Button = FunctionalComponent<HTMLProps<HTMLButtonElement>>;
55+type Button = FunctionalComponent<HTMLProps<HTMLButtonElement>
66+ // webstorm doesn't like it if you don't make this explicit.
77+ // don't ask me why
88+ & { type?: "submit" | "reset" | "button" | undefined }>;
69710export const IconButton: Button = ({ children, ...rest }) =>
811 <button {...rest} class={`icon-button ${rest["class"] ?? ""}`}>