Snippets

  • Conventional Commits Cheatsheet

    # Conventional Commit Messages <sub><img src="https://img.icons8.com/dusk/1600/commit-git.png" height="64" /></sub>
    
    See how [a minor change](#examples) to your commit message style can make a difference. 
    
    > [!TIP]
    > Have a look at **[git-conventional-commits](https://github.com/qoomon/git-conventional-commits)** , a CLI util to ensure these conventions, determine version and generate changelogs
    
    ## Commit Message Formats
    
    ### Default
    <pre>
    <b><a href="#types">&lt;type&gt;</a></b></font>(<b><a href="#scopes">&lt;optional scope&gt;</a></b>): <b><a href="#description">&lt;description&gt;</a></b>
    <sub>empty separator line</sub>
    <b><a href="#body">&lt;optional body&gt;</a></b>
    <sub>empty separator line</sub>
    <b><a href="#footer">&lt;optional footer&gt;</a></b>
    </pre>
    
    ### Merge Commit
    <pre>
    Merge branch '<b>&lt;branch name&gt;</b>'
    </pre>
    <sup>Follows default git merge message</sup>
    
    ### Revert Commit
    <pre>
    Revert "<b>&lt;reverted commit subject line&gt;</b>"
    </pre>
    <sup>Follows default git revert message</sup>
    
    ### Inital Commit 
    ```
    chore: init
    ```
    
    ### Types
    * API relevant changes
        * `feat` Commits, that adds or remove a new feature
        * `fix` Commits, that fixes a bug
    * `refactor` Commits, that rewrite/restructure your code, however does not change any API behaviour
        * `perf` Commits are special `refactor` commits, that improve performance
    * `style` Commits, that do not affect the meaning (white-space, formatting, missing semi-colons, etc)
    * `test` Commits, that add missing tests or correcting existing tests
    * `docs` Commits, that affect documentation only
    * `build` Commits, that affect build components like build tool, ci pipeline, dependencies, project version, ...
    * `ops` Commits, that affect operational components like infrastructure, deployment, backup, recovery, ...
    * `chore` Miscellaneous commits e.g. modifying `.gitignore`
    
    ### Scopes
    The `scope` provides additional contextual information.
    * Is an **optional** part of the format
    * Allowed Scopes depends on the specific project
    * Don't use issue identifiers as scopes
    
    ### Breaking Changes Indicator
    Breaking changes should be indicated by an `!` before the `:` in the subject line e.g. `feat(api)!: remove status endpoint`
    * Is an **optional** part of the format
    
    ### Description
    The `description` contains a concise description of the change.
    * Is a **mandatory** part of the format
    * Use the imperative, present tense: "change" not "changed" nor "changes"
      * Think of `This commit will...` or `This commit should...`
    * Don't capitalize the first letter
    * No dot (`.`) at the end
    
    ### Body
    The `body` should include the motivation for the change and contrast this with previous behavior.
    * Is an **optional** part of the format
    * Use the imperative, present tense: "change" not "changed" nor "changes"
    * This is the place to mention issue identifiers and their relations
    
    ### Footer
    The `footer` should contain any information about **Breaking Changes** and is also the place to **reference Issues** that this commit refers to.
    * Is an **optional** part of the format
    * **optionally** reference an issue by its id.
    * **Breaking Changes** should start with the word `BREAKING CHANGES:` followed by space or two newlines. The rest of the commit message is then used for this.
    
    
    ### Examples
    * ```
      feat: add email notifications on new direct messages
      ```
    * ```
      feat(shopping cart): add the amazing button
      ```
    * ```
      feat!: remove ticket list endpoint
    
      refers to JIRA-1337
    
      BREAKING CHANGES: ticket enpoints no longer supports list all entites.
      ```
    * ```
      fix(shopping-cart): prevent order an empty shopping cart
      ```
    * ```
      fix(api): fix wrong calculation of request body checksum
      ```
    * ```
      fix: add missing parameter to service call
    
      The error occurred because of <reasons>.
      ```
    * ```
      perf: decrease memory footprint for determine uniqe visitors by using HyperLogLog
      ```
    * ```
      build: update dependencies
      ```
    * ```
      build(release): bump version to 1.0.0
      ```
    * ```
      refactor: implement fibonacci number calculation as recursion
      ```
    * ```
      style: remove empty line
      ```
    
    ---
      
    ## Git Hook Scripts to ensure commit message header format
    <details>
    <summary>Click to expand</summary>
       
    ### commit-msg Hook (local)
    * Create a commit-msg hook using [git-conventional-commits cli](https://github.com/qoomon/git-conventional-commits?tab=readme-ov-file#automatically-validate-commit-message-convention-before-commit)
    
    ### pre-receive Hook (server side)
    * create following file in your repository folder `.git/hooks/pre-receive`
      ```shell
      #!/usr/bin/env bash
    
      # Pre-receive hook that will block commits with messges that do not follow regex rule
    
      commit_msg_type_regex='feat|fix|refactor|style|test|docs|build'
      commit_msg_scope_regex='.{1,20}'
      commit_msg_description_regex='.{1,100}'
      commit_msg_regex="^(${commit_msg_type_regex})(\(${commit_msg_scope_regex}\))?: (${commit_msg_description_regex})\$"
      merge_msg_regex="^Merge branch '.+'\$"
    
      zero_commit="0000000000000000000000000000000000000000"
    
      # Do not traverse over commits that are already in the repository
      excludeExisting="--not --all"
    
      error=""
      while read oldrev newrev refname; do
        # branch or tag get deleted
        if [ "$newrev" = "$zero_commit" ]; then
          continue
        fi
    
        # Check for new branch or tag
        if [ "$oldrev" = "$zero_commit" ]; then
          rev_span=`git rev-list $newrev $excludeExisting`
        else
          rev_span=`git rev-list $oldrev..$newrev $excludeExisting`
        fi
    
        for commit in $rev_span; do
          commit_msg_header=$(git show -s --format=%s $commit)
          if ! [[ "$commit_msg_header" =~ (${commit_msg_regex})|(${merge_msg_regex}) ]]; then
            echo "$commit" >&2
            echo "ERROR: Invalid commit message format" >&2
            echo "$commit_msg_header" >&2
            error="true"
          fi
        done
      done
    
      if [ -n "$error" ]; then
        exit 1
      fi
      ```
    * ⚠ make `.git/hooks/pre-receive` executable (unix: `chmod +x '.git/hooks/pre-receive'`)
    
    </details>
    
    -----
    ## References
    * https://www.conventionalcommits.org/
    * https://github.com/angular/angular/blob/master/CONTRIBUTING.md
    * http://karma-runner.github.io/1.0/dev/git-commit-msg.html
    <br>
    
    * https://github.com/github/platform-samples/tree/master/pre-receive-hooks  
    * https://github.community/t5/GitHub-Enterprise-Best-Practices/Using-pre-receive-hooks-in-GitHub-Enterprise/ba-p/13863
    
    
  • ANSI Escape Codes

    # ANSI Escape Sequences
    
    Standard escape codes are prefixed with `Escape`:
    
    - Ctrl-Key: `^[`
    - Octal: `\033`
    - Unicode: `\u001b`
    - Hexadecimal: `\x1B`
    - Decimal: `27`
    
    Followed by the command, somtimes delimited by opening square bracket (`[`), known as a Control Sequence Introducer (CSI), optionally followed by arguments and the command itself.
    
    Arguments are delimeted by semi colon (`;`).
    
    For example:
    
    ```sh
    \x1b[1;31m  # Set style to bold, red foreground.
    ```
    
    ## Sequences
    
    - `ESC` - sequence starting with `ESC` (`\x1B`)
    - `CSI` - Control Sequence Introducer: sequence starting with `ESC [` or CSI (`\x9B`)
    - `DCS` - Device Control String: sequence starting with `ESC P` or DCS (`\x90`)
    - `OSC` - Operating System Command: sequence starting with `ESC ]` or OSC (`\x9D`)
    
    Any whitespaces between sequences and arguments should be ignored. They are present for improved readability.
    
    ## General ASCII Codes
    
    | Name  | decimal | octal | hex  | C-escape | Ctrl-Key | Description                    |
    | ----- | ------- | ----- | ---- | -------- | -------- | ------------------------------ |
    | `BEL` | 7       | 007   | 0x07 | `\a`     | `^G`     | Terminal bell                  |
    | `BS`  | 8       | 010   | 0x08 | `\b`     | `^H`     | Backspace                      |
    | `HT`  | 9       | 011   | 0x09 | `\t`     | `^I`     | Horizontal TAB                 |
    | `LF`  | 10      | 012   | 0x0A | `\n`     | `^J`     | Linefeed (newline)             |
    | `VT`  | 11      | 013   | 0x0B | `\v`     | `^K`     | Vertical TAB                   |
    | `FF`  | 12      | 014   | 0x0C | `\f`     | `^L`     | Formfeed (also: New page `NP`) |
    | `CR`  | 13      | 015   | 0x0D | `\r`     | `^M`     | Carriage return                |
    | `ESC` | 27      | 033   | 0x1B | `\e`[*](#escape) | `^[` | Escape character           |
    | `DEL` | 127     | 177   | 0x7F | `<none>` | `<none>` | Delete character               |
    
    <div id="escape"></div>
    
    > **Note:** Some control escape sequences, like `\e` for `ESC`, are not guaranteed to work in all languages and compilers. It is recommended to use the decimal, octal or hex representation as escape code.
    
      
    
    > **Note:** The **Ctrl-Key** representation is simply associating the non-printable characters from ASCII code 1 with the printable (letter) characters from ASCII code 65 ("A"). ASCII code 1 would be `^A` (Ctrl-A), while ASCII code 7 (BEL) would be `^G` (Ctrl-G). This is a common representation (and input method) and historically comes from one of the VT series of terminals.
    
    ## Cursor Controls
    
    | ESC Code Sequence                                  | Description                                              |
    | :------------------------------------------------- | :------------------------------------------------------- |
    | `ESC[H`                                            | moves cursor to home position (0, 0)                     |
    | `ESC[{line};{column}H` <br> `ESC[{line};{column}f` | moves cursor to line #, column #                         |
    | `ESC[#A`                                           | moves cursor up # lines                                  |
    | `ESC[#B`                                           | moves cursor down # lines                                |
    | `ESC[#C`                                           | moves cursor right # columns                             |
    | `ESC[#D`                                           | moves cursor left # columns                              |
    | `ESC[#E`                                           | moves cursor to beginning of next line, # lines down     |
    | `ESC[#F`                                           | moves cursor to beginning of previous line, # lines up   |
    | `ESC[#G`                                           | moves cursor to column #                                 |
    | `ESC[6n`                                           | request cursor position (reports as `ESC[#;#R`)          |
    | `ESC M`                                            | moves cursor one line up, scrolling if needed            |
    | `ESC 7`                                            | save cursor position (DEC)                               |
    | `ESC 8`                                            | restores the cursor to the last saved position (DEC)     |
    | `ESC[s`                                            | save cursor position (SCO)                               |
    | `ESC[u`                                            | restores the cursor to the last saved position (SCO)     |
    
    > **Note:** Some sequences, like saving and restoring cursors, are private sequences and are not standardized. While some terminal emulators (i.e. xterm and derived) support both SCO and DEC sequences, they are likely to have different functionality. It is therefore recommended to use DEC sequences.
    
    ## Erase Functions
    
    | ESC Code Sequence | Description                               |
    | :---------------- | :---------------------------------------- |
    | `ESC[J`           | erase in display (same as `ESC[0J`)       |
    | `ESC[0J`          | erase from cursor until end of screen     |
    | `ESC[1J`          | erase from cursor to beginning of screen  |
    | `ESC[2J`          | erase entire screen                       |
    | `ESC[3J`          | erase saved lines                         |
    | `ESC[K`           | erase in line (same as `ESC[0K`)          |
    | `ESC[0K`          | erase from cursor to end of line          |
    | `ESC[1K`          | erase start of line to the cursor         |
    | `ESC[2K`          | erase the entire line                     |
    
    > Note: Erasing the line won't move the cursor, meaning that the cursor will stay at the last position it was at before the line was erased. You can use `\r` after erasing the line, to return the cursor to the start of the current line.
    
    ## Colors / Graphics Mode
    
    | ESC Code Sequence | Reset Sequence | Description                                                |
    | :---------------- | :------------- | :--------------------------------------------------------- |
    | `ESC[1;34;{...}m` |                | Set graphics modes for cell, separated by semicolon (`;`). |
    | `ESC[0m`          |                | reset all modes (styles and colors)                        |
    | `ESC[1m`          | `ESC[22m`      | set bold mode.                                             |
    | `ESC[2m`          | `ESC[22m`      | set dim/faint mode.                                        |
    | `ESC[3m`          | `ESC[23m`      | set italic mode.                                           |
    | `ESC[4m`          | `ESC[24m`      | set underline mode.                                        |
    | `ESC[5m`          | `ESC[25m`      | set blinking mode                                          |
    | `ESC[7m`          | `ESC[27m`      | set inverse/reverse mode                                   |
    | `ESC[8m`          | `ESC[28m`      | set hidden/invisible mode                                  |
    | `ESC[9m`          | `ESC[29m`      | set strikethrough mode.                                    |
    
    > **Note:** Some terminals may not support some of the graphic mode sequences listed above.
    
    > **Note:** Both dim and bold modes are reset with the `ESC[22m` sequence. The `ESC[21m` sequence is a non-specified sequence for double underline mode and only work in some terminals and is reset with `ESC[24m`.
    
    ### Color codes
    
    Most terminals support 8 and 16 colors, as well as 256 (8-bit) colors. These colors are set by the user, but have commonly defined meanings.
    
    #### 8-16 Colors
    
    | Color Name | Foreground Color Code | Background Color Code |
    | :--------- | :-------------------- | :-------------------- |
    | Black      | `30`                  | `40`                  |
    | Red        | `31`                  | `41`                  |
    | Green      | `32`                  | `42`                  |
    | Yellow     | `33`                  | `43`                  |
    | Blue       | `34`                  | `44`                  |
    | Magenta    | `35`                  | `45`                  |
    | Cyan       | `36`                  | `46`                  |
    | White      | `37`                  | `47`                  |
    | Default    | `39`                  | `49`                  |
    | Reset      | `0`                   | `0`                   |
    
    > **Note:** the _Reset_ color is the reset code that resets _all_ colors and text effects, Use _Default_ color to reset colors only.
    
    Most terminals, apart from the basic set of 8 colors, also support the "bright" or "bold" colors. These have their own set of codes, mirroring the normal colors, but with an additional `;1` in their codes:
    
    ```sh
    # Set style to bold, red foreground.
    \x1b[1;31mHello
    # Set style to dimmed white foreground with red background.
    \x1b[2;37;41mWorld
    ```
    
    Terminals that support the [aixterm specification](https://sites.ualberta.ca/dept/chemeng/AIX-43/share/man/info/C/a_doc_lib/cmds/aixcmds1/aixterm.htm) provides bright versions of the ISO colors, without the need to use the bold modifier:
    
    | Color Name     | Foreground Color Code | Background Color Code |
    | :------------- | :-------------------- | :-------------------- |
    | Bright Black   | `90`                  | `100`                 |
    | Bright Red     | `91`                  | `101`                 |
    | Bright Green   | `92`                  | `102`                 |
    | Bright Yellow  | `93`                  | `103`                 |
    | Bright Blue    | `94`                  | `104`                 |
    | Bright Magenta | `95`                  | `105`                 |
    | Bright Cyan    | `96`                  | `106`                 |
    | Bright White   | `97`                  | `107`                 |
    
    #### 256 Colors
    
    The following escape codes tells the terminal to use the given color ID:
    
    | ESC Code Sequence | Description           |
    | :---------------- | :-------------------- |
    | `ESC[38;5;{ID}m` | Set foreground color. |
    | `ESC[48;5;{ID}m` | Set background color. |
    
    Where `{ID}` should be replaced with the color index from 0 to 255 of the following color table:
    
    ![256 Color table](https://user-images.githubusercontent.com/995050/47952855-ecb12480-df75-11e8-89d4-ac26c50e80b9.png)
    
    The table starts with the original 16 colors (0-15).
    
    The proceeding 216 colors (16-231) or formed by a 3bpc RGB value offset by 16, packed into a single value.
    
    The final 24 colors (232-255) are grayscale starting from a shade slighly lighter than black, ranging up to shade slightly darker than white.
    
    Some emulators interpret these steps as linear increments (`256 / 24`) on all three channels, although some emulators may explicitly define these values.
    
    #### RGB Colors
    
    More modern terminals supports [Truecolor](https://en.wikipedia.org/wiki/Color_depth#True_color_.2824-bit.29) (24-bit RGB), which allows you to set foreground and background colors using RGB.
    
    These escape sequences are usually not well documented.
    
    | ESC Code Sequence       | Description                  |
    | :---------------------- | :--------------------------- |
    | `ESC[38;2;{r};{g};{b}m` | Set foreground color as RGB. |
    | `ESC[48;2;{r};{g};{b}m` | Set background color as RGB. |
    
    > Note that `;38` and `;48` corresponds to the 16 color sequence and is interpreted by the terminal to set the foreground and background color respectively. Where as `;2` and `;5` sets the color format.
    
    ## Screen Modes
    
    ### Set Mode
    
    | ESC Code Sequence | Description                                                                                                                                                           |
    | :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `ESC[={value}h`   | Changes the screen width or type to the mode specified by value.                                                                                                      |
    | `ESC[=0h`         | 40 x 25 monochrome (text)                                                                                                                                             |
    | `ESC[=1h`         | 40 x 25 color (text)                                                                                                                                                  |
    | `ESC[=2h`         | 80 x 25 monochrome (text)                                                                                                                                             |
    | `ESC[=3h`         | 80 x 25 color (text)                                                                                                                                                  |
    | `ESC[=4h`         | 320 x 200 4-color (graphics)                                                                                                                                          |
    | `ESC[=5h`         | 320 x 200 monochrome (graphics)                                                                                                                                       |
    | `ESC[=6h`         | 640 x 200 monochrome (graphics)                                                                                                                                       |
    | `ESC[=7h`         | Enables line wrapping                                                                                                                                                 |
    | `ESC[=13h`        | 320 x 200 color (graphics)                                                                                                                                            |
    | `ESC[=14h`        | 640 x 200 color (16-color graphics)                                                                                                                                   |
    | `ESC[=15h`        | 640 x 350 monochrome (2-color graphics)                                                                                                                               |
    | `ESC[=16h`        | 640 x 350 color (16-color graphics)                                                                                                                                   |
    | `ESC[=17h`        | 640 x 480 monochrome (2-color graphics)                                                                                                                               |
    | `ESC[=18h`        | 640 x 480 color (16-color graphics)                                                                                                                                   |
    | `ESC[=19h`        | 320 x 200 color (256-color graphics)                                                                                                                                  |
    | `ESC[={value}l`   | Resets the mode by using the same values that Set Mode uses, except for 7, which disables line wrapping. The last character in this escape sequence is a lowercase L. |
    
    ### Common Private Modes
    
    These are some examples of private modes, which are not defined by the specification, but are implemented in most terminals.
    
    | ESC Code Sequence | Description                     |
    | :---------------- | :------------------------------ |
    | `ESC[?25l`        | make cursor invisible           |
    | `ESC[?25h`        | make cursor visible             |
    | `ESC[?47l`        | restore screen                  |
    | `ESC[?47h`        | save screen                     |
    | `ESC[?1049h`      | enables the alternative buffer  |
    | `ESC[?1049l`      | disables the alternative buffer |
    
    Refer to the [XTerm Control Sequences](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html) for a more in-depth list of private modes defined by XTerm.
    
    > Note: While these modes may be supported by the most terminals, some may not work in multiplexers like tmux.
    
    ### Keyboard Strings
    
    ```sh
    ESC[{code};{string};{...}p
    ```
    
    Redefines a keyboard key to a specified string.
    
    The parameters for this escape sequence are defined as follows:
    
    - `code` is one or more of the values listed in the following table. These values represent keyboard keys and key combinations. When using these values in a command, you must type the semicolons shown in this table in addition to the semicolons required by the escape sequence. The codes in parentheses are not available on some keyboards. `ANSI.SYS` will not interpret the codes in parentheses for those keyboards unless you specify the `/X` switch in the `DEVICE` command for `ANSI.SYS`.
    
    - `string` is either the ASCII code for a single character or a string contained in quotation marks. For example, both 65 and "A" can be used to represent an uppercase A.
    
    > **IMPORTANT:** Some of the values in the following table are not valid for all computers. Check your computer's documentation for values that are different.
    
    #### List of keyboard strings
    
    | Key                      | Code     | SHIFT+code | CTRL+code | ALT+code  |
    | ------------------------ | -------- | ---------- | --------- | --------- |
    | F1                       | 0;59     | 0;84       | 0;94      | 0;104     |
    | F2                       | 0;60     | 0;85       | 0;95      | 0;105     |
    | F3                       | 0;61     | 0;86       | 0;96      | 0;106     |
    | F4                       | 0;62     | 0;87       | 0;97      | 0;107     |
    | F5                       | 0;63     | 0;88       | 0;98      | 0;108     |
    | F6                       | 0;64     | 0;89       | 0;99      | 0;109     |
    | F7                       | 0;65     | 0;90       | 0;100     | 0;110     |
    | F8                       | 0;66     | 0;91       | 0;101     | 0;111     |
    | F9                       | 0;67     | 0;92       | 0;102     | 0;112     |
    | F10                      | 0;68     | 0;93       | 0;103     | 0;113     |
    | F11                      | 0;133    | 0;135      | 0;137     | 0;139     |
    | F12                      | 0;134    | 0;136      | 0;138     | 0;140     |
    | HOME (num keypad)        | 0;71     | 55         | 0;119     | \--       |
    | UP ARROW (num keypad)    | 0;72     | 56         | (0;141)   | \--       |
    | PAGE UP (num keypad)     | 0;73     | 57         | 0;132     | \--       |
    | LEFT ARROW (num keypad)  | 0;75     | 52         | 0;115     | \--       |
    | RIGHT ARROW (num keypad) | 0;77     | 54         | 0;116     | \--       |
    | END (num keypad)         | 0;79     | 49         | 0;117     | \--       |
    | DOWN ARROW (num keypad)  | 0;80     | 50         | (0;145)   | \--       |
    | PAGE DOWN (num keypad)   | 0;81     | 51         | 0;118     | \--       |
    | INSERT (num keypad)      | 0;82     | 48         | (0;146)   | \--       |
    | DELETE (num keypad)      | 0;83     | 46         | (0;147)   | \--       |
    | HOME                     | (224;71) | (224;71)   | (224;119) | (224;151) |
    | UP ARROW                 | (224;72) | (224;72)   | (224;141) | (224;152) |
    | PAGE UP                  | (224;73) | (224;73)   | (224;132) | (224;153) |
    | LEFT ARROW               | (224;75) | (224;75)   | (224;115) | (224;155) |
    | RIGHT ARROW              | (224;77) | (224;77)   | (224;116) | (224;157) |
    | END                      | (224;79) | (224;79)   | (224;117) | (224;159) |
    | DOWN ARROW               | (224;80) | (224;80)   | (224;145) | (224;154) |
    | PAGE DOWN                | (224;81) | (224;81)   | (224;118) | (224;161) |
    | INSERT                   | (224;82) | (224;82)   | (224;146) | (224;162) |
    | DELETE                   | (224;83) | (224;83)   | (224;147) | (224;163) |
    | PRINT SCREEN             | \--      | \--        | 0;114     | \--       |
    | PAUSE/BREAK              | \--      | \--        | 0;0       | \--       |
    | BACKSPACE                | 8        | 8          | 127       | (0)       |
    | ENTER                    | 13       | \--        | 10        | (0        |
    | TAB                      | 9        | 0;15       | (0;148)   | (0;165)   |
    | NULL                     | 0;3      | \--        | \--       | \--       |
    | A                        | 97       | 65         | 1         | 0;30      |
    | B                        | 98       | 66         | 2         | 0;48      |
    | C                        | 99       | 66         | 3         | 0;46      |
    | D                        | 100      | 68         | 4         | 0;32      |
    | E                        | 101      | 69         | 5         | 0;18      |
    | F                        | 102      | 70         | 6         | 0;33      |
    | G                        | 103      | 71         | 7         | 0;34      |
    | H                        | 104      | 72         | 8         | 0;35      |
    | I                        | 105      | 73         | 9         | 0;23      |
    | J                        | 106      | 74         | 10        | 0;36      |
    | K                        | 107      | 75         | 11        | 0;37      |
    | L                        | 108      | 76         | 12        | 0;38      |
    | M                        | 109      | 77         | 13        | 0;50      |
    | N                        | 110      | 78         | 14        | 0;49      |
    | O                        | 111      | 79         | 15        | 0;24      |
    | P                        | 112      | 80         | 16        | 0;25      |
    | Q                        | 113      | 81         | 17        | 0;16      |
    | R                        | 114      | 82         | 18        | 0;19      |
    | S                        | 115      | 83         | 19        | 0;31      |
    | T                        | 116      | 84         | 20        | 0;20      |
    | U                        | 117      | 85         | 21        | 0;22      |
    | V                        | 118      | 86         | 22        | 0;47      |
    | W                        | 119      | 87         | 23        | 0;17      |
    | X                        | 120      | 88         | 24        | 0;45      |
    | Y                        | 121      | 89         | 25        | 0;21      |
    | Z                        | 122      | 90         | 26        | 0;44      |
    | 1                        | 49       | 33         | \--       | 0;120     |
    | 2                        | 50       | 64         | 0         | 0;121     |
    | 3                        | 51       | 35         | \--       | 0;122     |
    | 4                        | 52       | 36         | \--       | 0;123     |
    | 5                        | 53       | 37         | \--       | 0;124     |
    | 6                        | 54       | 94         | 30        | 0;125     |
    | 7                        | 55       | 38         | \--       | 0;126     |
    | 8                        | 56       | 42         | \--       | 0;126     |
    | 9                        | 57       | 40         | \--       | 0;127     |
    | 0                        | 48       | 41         | \--       | 0;129     |
    | \-                       | 45       | 95         | 31        | 0;130     |
    | \=                       | 61       | 43         | \---      | 0;131     |
    | \[                       | 91       | 123        | 27        | 0;26      |
    | \]                       | 93       | 125        | 29        | 0;27      |
    |                          | 92       | 124        | 28        | 0;43      |
    | ;                        | 59       | 58         | \--       | 0;39      |
    | '                        | 39       | 34         | \--       | 0;40      |
    | ,                        | 44       | 60         | \--       | 0;51      |
    | .                        | 46       | 62         | \--       | 0;52      |
    | /                        | 47       | 63         | \--       | 0;53      |
    | \`                       | 96       | 126        | \--       | (0;41)    |
    | ENTER (keypad)           | 13       | \--        | 10        | (0;166)   |
    | / (keypad)               | 47       | 47         | (0;142)   | (0;74)    |
    | \* (keypad)              | 42       | (0;144)    | (0;78)    | \--       |
    | \- (keypad)              | 45       | 45         | (0;149)   | (0;164)   |
    | \+ (keypad)              | 43       | 43         | (0;150)   | (0;55)    |
    | 5 (keypad)               | (0;76)   | 53         | (0;143)   | \--       |
    
    ## Resources
    
    - [Wikipedia: ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code)
    - [Build your own Command Line with ANSI escape codes](http://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html)
    - [ascii-table: ANSI Escape sequences](http://ascii-table.com/ansi-escape-sequences.php)
    - [bluesock: ansi codes](https://bluesock.org/~willkg/dev/ansi.html)
    - [bash-hackers: Terminal Codes (ANSI/VT100) introduction](http://wiki.bash-hackers.org/scripting/terminalcodes)
    - [XTerm Control Sequences](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
    - [VT100 – Various terminal manuals](https://vt100.net/)
    - [xterm.js – Supported Terminal Sequences](https://xtermjs.org/docs/api/vtfeatures/)
    
  • Merge objects in Typescript.

    type Primitive = null | undefined | string | number | boolean | symbol | bigint;
    
    function isObject(item: unknown): item is Record<string, unknown> {
      return typeof item === 'object' && item !== null && !Array.isArray(item);
    }
      
     /**
      * Merge objects, second objects overwrites the keys of the first objects
      */
    function deepMerge<T extends Record<string, unknown>, U extends Record<string, unknown>>(target: T, source: U): T & U {
      const output = { ...target } as T & U;
    
      Object.keys(source).forEach((key) => {
        const sourceValue = source[key];
    
        if (isObject(sourceValue)) {
          const targetValue = (target as Record<string, unknown>)[key];
    
          if (isObject(targetValue)) {
            (output as Record<string, unknown>)[key] = deepMerge(targetValue, sourceValue);
          } else {
            (output as Record<string, unknown>)[key] = sourceValue;
          }
        } else {
          (output as Record<string, unknown>)[key] = sourceValue;
        }
      });
    
      return output;
    }
  • Git

    | Command                                          | Description                                                                                   |
    |--------------------------------------------------|-----------------------------------------------------------------------------------------------|
    | `git blame -L 28,43 path/to/file`                | Shows who modified each line in a specific range of a file.                                   |
    | `git blame -L :'class LocalFile' filepath`       | Blames a block of code in a file matching a specific pattern.                                 |
    | `git log -L28,43:filepath`                       | Lists commits that last touched a specified region of a file.                                 |
    | `git blame -w`                                   | Ignores whitespace changes in `git blame`.                                                    |
    | `git blame -C`                                   | Follows code movement across files in `git blame`. Can be used up to three times.              |
    | `git diff --word-diff`                           | Shows word-level differences in `git diff` output.                                            |
    | `git config --global rerere.enabled true`        | Enables reuse of recorded resolution of conflicts.                                            |
    | `git config --global rerere.autoUpdate true`     | Automatically updates the index with the result of rerere resolution.                         |
    | `git config --global branch.sort -committerdate` | Sorts branches by last commit date in descending order.                                       |
    | `git push --force-with-lease`                    | Safely force-pushes, ensuring no updates were pushed to the branch since your last fetch.     |
    | `git maintenance start`                          | Starts Git maintenance tasks to optimize repository performance.                              |
    
  • Usefull bash commands

    # Delete node_modules using fd (github.com/sharkdp/fd)
    fd -HI node_modules -X rm -rf
  • Typescript tips and tricks. This is update whenever I come across something cool

    // Credits to Matt Pocock
    
    type CreateAPIMethod = <
      TInput extends Record<string, string>, // The input 
      TOutput // The output
    >(opts: {
          url: string;
          method: "GET" | "POST"
          }) = (input: TInput) = Promise<TOutput>;
        declare const createAPIMethod: CreateAPIMethod;
    /**
    * You can reuse this function as many times as you
    * like to create all your API methods!
    */
    const getUser = createAPIMethod<
          { id: string }, 
          { name: string }
    >({
      method: "GET", url: "/user",
    })
    
    getUser({ id: 123 }); //
    // Loose autocomplete -> after 5.3 not needed anymore 'string' is sufficient
    type IconSize =
      | "small"
      | "medium"
      | "large"
      | (string & {});
    
    // Remove the prefix property
    type RemovePrefix<T, Prefix extends string> = {
        [K in keyof T as K extends `${Prefix}_${infer _}` ? never : K]: T[K]
    };
    /**
     * Throttle a function call, allowing it to be called at most once every `wait` milliseconds.
     * @param fn - The function to be throttled.
     * @param wait - The time in milliseconds to wait before allowing the next invocation.
     * @returns A throttled version of the input function.
     */
    function throttle<T extends (...args: any[]) => any>(fn: T, wait: number): (...funcArgs: Parameters<T>) => void {
      let isThrottling: boolean = false;
      let lastArgs: Parameters<T> | null = null;
      let lastThis: ThisParameterType<T> | null = null;
    
      const call = () => {
        if (lastArgs === null || lastThis === null) {
          isThrottling = false;
          return;
        }
        fn.apply(lastThis, lastArgs);
        lastArgs = null;
        lastThis = null;
        setTimeout(call, wait);
      };
    
      return function(this: ThisParameterType<T>, ...args: Parameters<T>): void {
        if (isThrottling) {
          lastArgs = args;
          lastThis = this;
          return;
        }
    
        fn.apply(this, args);
        isThrottling = true;
        setTimeout(() => {
          isThrottling = false;
          call();
        }, wait);
      };
    }
  • The CSS property to prevent scrollbar pushing the layout

    /* The scrollbar-gutter CSS property allows authors to reserve space for the scrollbar, preventing unwanted layout changes as the content grows while also avoiding unnecessary visuals when scrolling isn't needed. */
    /* https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-gutter */
    
    .scrollbar {
      scrollbar-gutter: stable;
    }
  • A simple HTTP service to wrap fetch services with abstraction to handle errors

    /**
     * A strategy for fetching data from the server.
     *
     * @callback FetchStrategy
     * @param {string} url - The URL to fetch data from.
     * @param {RequestInit} [options] - Additional options for the fetch request.
     * @returns {Promise<Response>} A promise that resolves to the fetch response.
     */
    export type FetchStrategy = (
      url: string,
      options?: RequestInit
    ) => Promise<Response>;
    
    /**
     * A service for sending HTTP requests.
     * @class HttpService
     * @param {FetchStrategy} [fetchStrategy=fetch] - A strategy for fetching data from the server.
     * @returns {HttpService} An instance of the HttpService class.
     * @example
     * const httpService = new HttpService(customFetch);
     * const data = await httpService.get("https://example.com");
     * console.log(data);
     */
    export default class HttpService {
      private fetchStrategy: FetchStrategy;
    
      constructor(fetchStrategy: FetchStrategy = fetch) {
        this.fetchStrategy = fetchStrategy;
      }
    
      async fetchHandler<T = unknown>(
        url: string,
        options?: RequestInit
      ): Promise<T> {
        try {
          const response = await this.fetchStrategy(url, options);
    
          if (!response.ok) {
            throw new HttpError(
              `HTTP Error: ${response.status} - ${response.statusText}`,
              response.status
            );
          }
    
          const data = (await response.json()) as T;
    
          return data;
        } catch (error) {
          throw new RequestError(`Request failed: ${error.message}`);
        }
      }
    
      /**
       * Sends an HTTP GET request.
       *
       * @template T
       * @param {string} url - The URL to send the GET request to.
       * @param {RequestInit} [options] - Additional options for the request.
       * @returns {Promise<T>} A promise that resolves to the parsed response data.
       */
      async get<T = unknown>(url: string, options?: RequestInit): Promise<T> {
        return this.fetchHandler<T>(url, options);
      }
    
      /**
       * Sends an HTTP POST request.
       *
       * @template T
       * @param {string} url - The URL to send the POST request to.
       * @param {Record<string, unknown> | BodyInit | null} [body=null] - The request body.
       * @param {Omit<RequestInit, "method" | "body">} [options] - Additional options for the request.
       * @returns {Promise<T>} A promise that resolves to the parsed response data.
       */
      async post<T = unknown>(
        url: string,
        body: Record<string, unknown> | BodyInit | null = null,
        options?: Omit<RequestInit, "method" | "body">
      ): Promise<T> {
        return this.fetchHandler<T>(url, {
          method: "POST",
          body: body ? JSON.stringify(body) : null,
          ...options
        });
      }
    }
    
    class HttpError extends Error {
      constructor(message: string, public statusCode: number) {
        super(message);
        this.name = "HttpError";
      }
    }
    
    class RequestError extends Error {
      constructor(message: string) {
        super(message);
        this.name = "RequestError";
      }
    }
    
    /**
     * A service for sending HTTP requests.
     * We use globalThis.fetch and rebind the context to the globalThis object because
     * `fetch` is reliant on this being referenced to the window
     *
     * @type {HttpService}
     * @example
     * const data = await FetchService.get("https://example.com");
     * console.log(data);
     */
    export const FetchService: HttpService = new HttpService(
      globalThis.fetch.bind(globalThis)
    );
    
       
    // Or a custom strategy with different options
    const authFetch: FetchStrategy = async (url, options) => {
      return fetch(`${baseUrl}${url}`, {
        credentials: "include",
        headers: {
          Accept: "application/json",
          "Content-Type": "application/json",
          ...headers
        },
        ...options
      });
    };
    
    export const AuthService = new HttpService(authFetch);
    
  • A simple way for error handling in Typescript

    enum ResultKind {
      OK = "Ok",
      ERR = "Err",
    }
    
    export type Result<T, E> = Ok<T> | Err<E>;
    
    interface ResultBase<A, E> {
      kind: ResultKind;
      map<B>(fn: (_: A) => B): Result<B, E>;
      bind<B>(fn: (_: A) => Result<B, E>): Result<B, E>;
      match<B>(obj: { ok: (_: A) => B; err: (_: E) => B }): B;
    }
    
    export type Ok<A> = Readonly<ResultBase<A, never> & { kind: ResultKind.OK; value: A }>;
    
    export function ok<A>(a: A): Ok<A> {
      return {
        kind: ResultKind.OK,
        value: a,
        map(fn) {
          return ok(fn(this.value));
        },
        bind(fn) {
          return fn(this.value);
        },
        match({ ok }) {
          return ok(this.value);
        },
      };
    }
    
    export type Err<E> = Readonly<ResultBase<never, E> & { kind: ResultKind.ERR; error: E }>;
    
    export function err<E>(e: E): Err<E> {
      return {
        kind: ResultKind.ERR,
        error: e,
        map() {
          return this;
        },
        bind() {
          return this;
        },
        match({ err }) {
          return err(this.error);
        },
      };
    }
    
  • ZOD

    import { zodResolver } from "@hookform/resolvers/zod";
    import { type UseFormProps, useForm } from "react-hook-form";
    import { type z } from "zod";
    
    /**
     * A wrapper around `useForm` that uses `zodResolver` as the resolver.
     *
     * @example
     *
     * const schema = z.object({
     *  email: z.string().email(),
     *  password: z.string().min(8),
     * })
     * const form = useZodForm({ schema })
     *
     *
     */
    export default function useZodForm<TSchema extends z.ZodType>(
      props: Omit<UseFormProps<TSchema["_input"]>, "resolver"> & {
        schema: TSchema;
      },
    ) {
      const form = useForm<TSchema["_input"]>({
        ...props,
        resolver: zodResolver(props.schema, undefined, {
          // This makes it so we can use `.transform()`s on the schema without same transform getting applied again when it reaches the server
          raw: true,
        }),
      });
    
      return form;
    }
    
    /**
     * @description
     * This function is used to create a safe loader function.
     * It takes in a loader function and a zod schema.
     * The loader function is expected to return an object.
     * The zod schema is used to validate the output of the loader function.
     * If the output of the loader function is not valid, an error is thrown.
     * If the output of the loader function is valid, it is returned.
     * @param outputValidation - a zod schema used to validate the output of the loader function
     * @param loader - a function that returns an object
     * @returns a function that returns the output of the loader function if it is validate
     * @example
     * const getUsers = safeLoader({
     *  outputValidation: z.object({
     *    name: z.string(),
     *  }),
     *  loader: async () => {
     *    return {
     *      name: "John Doe",
     *    };
     *  },
     * })
     */
    export function safeLoader<
      LoaderInputs extends any[],
      OutputValidation extends ZodTypeAny,
    >({
      outputValidation,
      loader,
    }: {
      outputValidation: OutputValidation;
      loader: (...argsList: LoaderInputs) => any;
    }) {
      return async function (
        ...args: LoaderInputs
      ): Promise<z.infer<OutputValidation>> {
        const outputs = await loader.apply(null, args);
        const parsedOutput = outputValidation.parse(outputs);
        return parsedOutput;
      };
    }